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 = 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