1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/process/environment_internal.h"
6
7 #include <stddef.h>
8
9 #include <vector>
10
11 #include "build/build_config.h"
12
13 #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
14 #include <string.h>
15 #endif
16
17 #if BUILDFLAG(IS_WIN)
18 #include "base/check_op.h"
19 #endif
20
21 namespace base {
22 namespace internal {
23
24 namespace {
25
26 #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_WIN)
27 // Parses a null-terminated input string of an environment block. The key is
28 // placed into the given string, and the total length of the line, including
29 // the terminating null, is returned.
ParseEnvLine(const NativeEnvironmentString::value_type * input,NativeEnvironmentString * key)30 size_t ParseEnvLine(const NativeEnvironmentString::value_type* input,
31 NativeEnvironmentString* key) {
32 // Skip to the equals or end of the string, this is the key.
33 size_t cur = 0;
34 while (input[cur] && input[cur] != '=')
35 cur++;
36 *key = NativeEnvironmentString(&input[0], cur);
37
38 // Now just skip to the end of the string.
39 while (input[cur])
40 cur++;
41 return cur + 1;
42 }
43 #endif
44
45 } // namespace
46
47 #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
48
AlterEnvironment(const char * const * const env,const EnvironmentMap & changes)49 std::unique_ptr<char* []> AlterEnvironment(const char* const* const env,
50 const EnvironmentMap& changes) {
51 std::string value_storage; // Holds concatenated null-terminated strings.
52 std::vector<size_t> result_indices; // Line indices into value_storage.
53
54 // First build up all of the unchanged environment strings. These are
55 // null-terminated of the form "key=value".
56 std::string key;
57 for (size_t i = 0; env[i]; i++) {
58 size_t line_length = ParseEnvLine(env[i], &key);
59
60 // Keep only values not specified in the change vector.
61 auto found_change = changes.find(key);
62 if (found_change == changes.end()) {
63 result_indices.push_back(value_storage.size());
64 value_storage.append(env[i], line_length);
65 }
66 }
67
68 // Now append all modified and new values.
69 for (const auto& i : changes) {
70 if (!i.second.empty()) {
71 result_indices.push_back(value_storage.size());
72 value_storage.append(i.first);
73 value_storage.push_back('=');
74 value_storage.append(i.second);
75 value_storage.push_back(0);
76 }
77 }
78
79 size_t pointer_count_required =
80 result_indices.size() + 1 + // Null-terminated array of pointers.
81 (value_storage.size() + sizeof(char*) - 1) / sizeof(char*); // Buffer.
82 std::unique_ptr<char*[]> result(new char*[pointer_count_required]);
83
84 // The string storage goes after the array of pointers.
85 char* storage_data =
86 reinterpret_cast<char*>(&result.get()[result_indices.size() + 1]);
87 if (!value_storage.empty())
88 memcpy(storage_data, value_storage.data(), value_storage.size());
89
90 // Fill array of pointers at the beginning of the result.
91 for (size_t i = 0; i < result_indices.size(); i++)
92 result[i] = &storage_data[result_indices[i]];
93 result[result_indices.size()] = 0; // Null terminator.
94
95 return result;
96 }
97
98 #elif BUILDFLAG(IS_WIN)
99
AlterEnvironment(const wchar_t * env,const EnvironmentMap & changes)100 NativeEnvironmentString AlterEnvironment(const wchar_t* env,
101 const EnvironmentMap& changes) {
102 NativeEnvironmentString result;
103
104 // First build up all of the unchanged environment strings.
105 const wchar_t* ptr = env;
106 while (*ptr) {
107 std::wstring key;
108 size_t line_length = ParseEnvLine(ptr, &key);
109
110 // Keep only values not specified in the change vector.
111 if (changes.find(key) == changes.end()) {
112 result.append(ptr, line_length);
113 }
114 ptr += line_length;
115 }
116
117 // Now append all modified and new values.
118 for (const auto& i : changes) {
119 // Windows environment blocks cannot handle keys or values with NULs.
120 CHECK_EQ(std::wstring::npos, i.first.find(L'\0'));
121 CHECK_EQ(std::wstring::npos, i.second.find(L'\0'));
122 if (!i.second.empty()) {
123 result += i.first;
124 result.push_back('=');
125 result += i.second;
126 result.push_back('\0');
127 }
128 }
129
130 // Add the terminating NUL.
131 result.push_back('\0');
132 return result;
133 }
134
135 #endif // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
136
137 } // namespace internal
138 } // namespace base
139