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