1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
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/environment.h"
6 
7 #include <stddef.h>
8 
9 #include <vector>
10 
11 #include "base/strings/string_piece.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "build/build_config.h"
15 
16 #if defined(OS_POSIX)
17 #include <stdlib.h>
18 #elif defined(OS_WIN)
19 #include <windows.h>
20 #endif
21 
22 namespace base {
23 
24 namespace {
25 
26 class EnvironmentImpl : public Environment {
27  public:
GetVar(const char * variable_name,std::string * result)28   bool GetVar(const char* variable_name, std::string* result) override {
29     if (GetVarImpl(variable_name, result))
30       return true;
31 
32     // Some commonly used variable names are uppercase while others
33     // are lowercase, which is inconsistent. Let's try to be helpful
34     // and look for a variable name with the reverse case.
35     // I.e. HTTP_PROXY may be http_proxy for some users/systems.
36     char first_char = variable_name[0];
37     std::string alternate_case_var;
38     if (first_char >= 'a' && first_char <= 'z')
39       alternate_case_var = ToUpperASCII(variable_name);
40     else if (first_char >= 'A' && first_char <= 'Z')
41       alternate_case_var = ToLowerASCII(variable_name);
42     else
43       return false;
44     return GetVarImpl(alternate_case_var.c_str(), result);
45   }
46 
SetVar(const char * variable_name,const std::string & new_value)47   bool SetVar(const char* variable_name,
48               const std::string& new_value) override {
49     return SetVarImpl(variable_name, new_value);
50   }
51 
UnSetVar(const char * variable_name)52   bool UnSetVar(const char* variable_name) override {
53     return UnSetVarImpl(variable_name);
54   }
55 
56  private:
GetVarImpl(const char * variable_name,std::string * result)57   bool GetVarImpl(const char* variable_name, std::string* result) {
58 #if defined(OS_POSIX)
59     const char* env_value = getenv(variable_name);
60     if (!env_value)
61       return false;
62     // Note that the variable may be defined but empty.
63     if (result)
64       *result = env_value;
65     return true;
66 #elif defined(OS_WIN)
67     DWORD value_length = ::GetEnvironmentVariable(
68         UTF8ToWide(variable_name).c_str(), NULL, 0);
69     if (value_length == 0)
70       return false;
71     if (result) {
72       scoped_ptr<wchar_t[]> value(new wchar_t[value_length]);
73       ::GetEnvironmentVariable(UTF8ToWide(variable_name).c_str(), value.get(),
74                                value_length);
75       *result = WideToUTF8(value.get());
76     }
77     return true;
78 #else
79 #error need to port
80 #endif
81   }
82 
SetVarImpl(const char * variable_name,const std::string & new_value)83   bool SetVarImpl(const char* variable_name, const std::string& new_value) {
84 #if defined(OS_POSIX)
85     // On success, zero is returned.
86     return !setenv(variable_name, new_value.c_str(), 1);
87 #elif defined(OS_WIN)
88     // On success, a nonzero value is returned.
89     return !!SetEnvironmentVariable(UTF8ToWide(variable_name).c_str(),
90                                     UTF8ToWide(new_value).c_str());
91 #endif
92   }
93 
UnSetVarImpl(const char * variable_name)94   bool UnSetVarImpl(const char* variable_name) {
95 #if defined(OS_POSIX)
96     // On success, zero is returned.
97     return !unsetenv(variable_name);
98 #elif defined(OS_WIN)
99     // On success, a nonzero value is returned.
100     return !!SetEnvironmentVariable(UTF8ToWide(variable_name).c_str(), NULL);
101 #endif
102   }
103 };
104 
105 // Parses a null-terminated input string of an environment block. The key is
106 // placed into the given string, and the total length of the line, including
107 // the terminating null, is returned.
ParseEnvLine(const NativeEnvironmentString::value_type * input,NativeEnvironmentString * key)108 size_t ParseEnvLine(const NativeEnvironmentString::value_type* input,
109                     NativeEnvironmentString* key) {
110   // Skip to the equals or end of the string, this is the key.
111   size_t cur = 0;
112   while (input[cur] && input[cur] != '=')
113     cur++;
114   *key = NativeEnvironmentString(&input[0], cur);
115 
116   // Now just skip to the end of the string.
117   while (input[cur])
118     cur++;
119   return cur + 1;
120 }
121 
122 }  // namespace
123 
124 namespace env_vars {
125 
126 #if defined(OS_POSIX)
127 // On Posix systems, this variable contains the location of the user's home
128 // directory. (e.g, /home/username/).
129 const char kHome[] = "HOME";
130 #endif
131 
132 }  // namespace env_vars
133 
~Environment()134 Environment::~Environment() {}
135 
136 // static
Create()137 Environment* Environment::Create() {
138   return new EnvironmentImpl();
139 }
140 
HasVar(const char * variable_name)141 bool Environment::HasVar(const char* variable_name) {
142   return GetVar(variable_name, NULL);
143 }
144 
145 #if defined(OS_WIN)
146 
AlterEnvironment(const wchar_t * env,const EnvironmentMap & changes)147 string16 AlterEnvironment(const wchar_t* env,
148                           const EnvironmentMap& changes) {
149   string16 result;
150 
151   // First copy all unmodified values to the output.
152   size_t cur_env = 0;
153   string16 key;
154   while (env[cur_env]) {
155     const wchar_t* line = &env[cur_env];
156     size_t line_length = ParseEnvLine(line, &key);
157 
158     // Keep only values not specified in the change vector.
159     EnvironmentMap::const_iterator found_change = changes.find(key);
160     if (found_change == changes.end())
161       result.append(line, line_length);
162 
163     cur_env += line_length;
164   }
165 
166   // Now append all modified and new values.
167   for (EnvironmentMap::const_iterator i = changes.begin();
168        i != changes.end(); ++i) {
169     if (!i->second.empty()) {
170       result.append(i->first);
171       result.push_back('=');
172       result.append(i->second);
173       result.push_back(0);
174     }
175   }
176 
177   // An additional null marks the end of the list. We always need a double-null
178   // in case nothing was added above.
179   if (result.empty())
180     result.push_back(0);
181   result.push_back(0);
182   return result;
183 }
184 
185 #elif defined(OS_POSIX)
186 
AlterEnvironment(const char * const * const env,const EnvironmentMap & changes)187 scoped_ptr<char*[]> AlterEnvironment(const char* const* const env,
188                                      const EnvironmentMap& changes) {
189   std::string value_storage;  // Holds concatenated null-terminated strings.
190   std::vector<size_t> result_indices;  // Line indices into value_storage.
191 
192   // First build up all of the unchanged environment strings. These are
193   // null-terminated of the form "key=value".
194   std::string key;
195   for (size_t i = 0; env[i]; i++) {
196     size_t line_length = ParseEnvLine(env[i], &key);
197 
198     // Keep only values not specified in the change vector.
199     EnvironmentMap::const_iterator found_change = changes.find(key);
200     if (found_change == changes.end()) {
201       result_indices.push_back(value_storage.size());
202       value_storage.append(env[i], line_length);
203     }
204   }
205 
206   // Now append all modified and new values.
207   for (EnvironmentMap::const_iterator i = changes.begin();
208        i != changes.end(); ++i) {
209     if (!i->second.empty()) {
210       result_indices.push_back(value_storage.size());
211       value_storage.append(i->first);
212       value_storage.push_back('=');
213       value_storage.append(i->second);
214       value_storage.push_back(0);
215     }
216   }
217 
218   size_t pointer_count_required =
219       result_indices.size() + 1 +  // Null-terminated array of pointers.
220       (value_storage.size() + sizeof(char*) - 1) / sizeof(char*);  // Buffer.
221   scoped_ptr<char*[]> result(new char*[pointer_count_required]);
222 
223   // The string storage goes after the array of pointers.
224   char* storage_data = reinterpret_cast<char*>(
225       &result.get()[result_indices.size() + 1]);
226   if (!value_storage.empty())
227     memcpy(storage_data, value_storage.data(), value_storage.size());
228 
229   // Fill array of pointers at the beginning of the result.
230   for (size_t i = 0; i < result_indices.size(); i++)
231     result[i] = &storage_data[result_indices[i]];
232   result[result_indices.size()] = 0;  // Null terminator.
233 
234   return result;
235 }
236 
237 #endif  // OS_POSIX
238 
239 }  // namespace base
240