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: 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 47 bool SetVar(const char* variable_name, 48 const std::string& new_value) override { 49 return SetVarImpl(variable_name, new_value); 50 } 51 52 bool UnSetVar(const char* variable_name) override { 53 return UnSetVarImpl(variable_name); 54 } 55 56 private: 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 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 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. 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 134 Environment::~Environment() {} 135 136 // static 137 Environment* Environment::Create() { 138 return new EnvironmentImpl(); 139 } 140 141 bool Environment::HasVar(const char* variable_name) { 142 return GetVar(variable_name, NULL); 143 } 144 145 #if defined(OS_WIN) 146 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 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