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