xref: /aosp_15_r20/external/cronet/base/process/environment_internal.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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