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.
5 #include "base/environment.h"
9 #include "base/strings/string_piece.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/utf_string_conversions.h"
23 class EnvironmentImpl
: public Environment
{
25 bool GetVar(const char* variable_name
, std::string
* result
) override
{
26 if (GetVarImpl(variable_name
, result
))
29 // Some commonly used variable names are uppercase while others
30 // are lowercase, which is inconsistent. Let's try to be helpful
31 // and look for a variable name with the reverse case.
32 // I.e. HTTP_PROXY may be http_proxy for some users/systems.
33 char first_char
= variable_name
[0];
34 std::string alternate_case_var
;
35 if (first_char
>= 'a' && first_char
<= 'z')
36 alternate_case_var
= ToUpperASCII(variable_name
);
37 else if (first_char
>= 'A' && first_char
<= 'Z')
38 alternate_case_var
= ToLowerASCII(variable_name
);
41 return GetVarImpl(alternate_case_var
.c_str(), result
);
44 bool SetVar(const char* variable_name
,
45 const std::string
& new_value
) override
{
46 return SetVarImpl(variable_name
, new_value
);
49 bool UnSetVar(const char* variable_name
) override
{
50 return UnSetVarImpl(variable_name
);
54 bool GetVarImpl(const char* variable_name
, std::string
* result
) {
56 const char* env_value
= getenv(variable_name
);
59 // Note that the variable may be defined but empty.
64 DWORD value_length
= ::GetEnvironmentVariable(
65 UTF8ToWide(variable_name
).c_str(), NULL
, 0);
66 if (value_length
== 0)
69 scoped_ptr
<wchar_t[]> value(new wchar_t[value_length
]);
70 ::GetEnvironmentVariable(UTF8ToWide(variable_name
).c_str(), value
.get(),
72 *result
= WideToUTF8(value
.get());
80 bool SetVarImpl(const char* variable_name
, const std::string
& new_value
) {
82 // On success, zero is returned.
83 return !setenv(variable_name
, new_value
.c_str(), 1);
85 // On success, a nonzero value is returned.
86 return !!SetEnvironmentVariable(UTF8ToWide(variable_name
).c_str(),
87 UTF8ToWide(new_value
).c_str());
91 bool UnSetVarImpl(const char* variable_name
) {
93 // On success, zero is returned.
94 return !unsetenv(variable_name
);
96 // On success, a nonzero value is returned.
97 return !!SetEnvironmentVariable(UTF8ToWide(variable_name
).c_str(), NULL
);
102 // Parses a null-terminated input string of an environment block. The key is
103 // placed into the given string, and the total length of the line, including
104 // the terminating null, is returned.
105 size_t ParseEnvLine(const NativeEnvironmentString::value_type
* input
,
106 NativeEnvironmentString
* key
) {
107 // Skip to the equals or end of the string, this is the key.
109 while (input
[cur
] && input
[cur
] != '=')
111 *key
= NativeEnvironmentString(&input
[0], cur
);
113 // Now just skip to the end of the string.
123 #if defined(OS_POSIX)
124 // On Posix systems, this variable contains the location of the user's home
125 // directory. (e.g, /home/username/).
126 const char kHome
[] = "HOME";
129 } // namespace env_vars
131 Environment::~Environment() {}
134 Environment
* Environment::Create() {
135 return new EnvironmentImpl();
138 bool Environment::HasVar(const char* variable_name
) {
139 return GetVar(variable_name
, NULL
);
144 string16
AlterEnvironment(const wchar_t* env
,
145 const EnvironmentMap
& changes
) {
148 // First copy all unmodified values to the output.
151 while (env
[cur_env
]) {
152 const wchar_t* line
= &env
[cur_env
];
153 size_t line_length
= ParseEnvLine(line
, &key
);
155 // Keep only values not specified in the change vector.
156 EnvironmentMap::const_iterator found_change
= changes
.find(key
);
157 if (found_change
== changes
.end())
158 result
.append(line
, line_length
);
160 cur_env
+= line_length
;
163 // Now append all modified and new values.
164 for (EnvironmentMap::const_iterator i
= changes
.begin();
165 i
!= changes
.end(); ++i
) {
166 if (!i
->second
.empty()) {
167 result
.append(i
->first
);
168 result
.push_back('=');
169 result
.append(i
->second
);
174 // An additional null marks the end of the list. We always need a double-null
175 // in case nothing was added above.
182 #elif defined(OS_POSIX)
184 scoped_ptr
<char*[]> AlterEnvironment(const char* const* const env
,
185 const EnvironmentMap
& changes
) {
186 std::string value_storage
; // Holds concatenated null-terminated strings.
187 std::vector
<size_t> result_indices
; // Line indices into value_storage.
189 // First build up all of the unchanged environment strings. These are
190 // null-terminated of the form "key=value".
192 for (size_t i
= 0; env
[i
]; i
++) {
193 size_t line_length
= ParseEnvLine(env
[i
], &key
);
195 // Keep only values not specified in the change vector.
196 EnvironmentMap::const_iterator found_change
= changes
.find(key
);
197 if (found_change
== changes
.end()) {
198 result_indices
.push_back(value_storage
.size());
199 value_storage
.append(env
[i
], line_length
);
203 // Now append all modified and new values.
204 for (EnvironmentMap::const_iterator i
= changes
.begin();
205 i
!= changes
.end(); ++i
) {
206 if (!i
->second
.empty()) {
207 result_indices
.push_back(value_storage
.size());
208 value_storage
.append(i
->first
);
209 value_storage
.push_back('=');
210 value_storage
.append(i
->second
);
211 value_storage
.push_back(0);
215 size_t pointer_count_required
=
216 result_indices
.size() + 1 + // Null-terminated array of pointers.
217 (value_storage
.size() + sizeof(char*) - 1) / sizeof(char*); // Buffer.
218 scoped_ptr
<char*[]> result(new char*[pointer_count_required
]);
220 // The string storage goes after the array of pointers.
221 char* storage_data
= reinterpret_cast<char*>(
222 &result
.get()[result_indices
.size() + 1]);
223 if (!value_storage
.empty())
224 memcpy(storage_data
, value_storage
.data(), value_storage
.size());
226 // Fill array of pointers at the beginning of the result.
227 for (size_t i
= 0; i
< result_indices
.size(); i
++)
228 result
[i
] = &storage_data
[result_indices
[i
]];
229 result
[result_indices
.size()] = 0; // Null terminator.
231 return result
.Pass();