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 19 namespace base { 20 21 namespace { 22 23 class 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. 106 size_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 122 namespace 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/). 127 const char kHome[] = "HOME"; 128 #endif 129 130 } // namespace env_vars 131 132 Environment::~Environment() {} 133 134 // static 135 Environment* Environment::Create() { 136 return new EnvironmentImpl(); 137 } 138 139 bool Environment::HasVar(const char* variable_name) { 140 return GetVar(variable_name, NULL); 141 } 142 143 #if defined(OS_WIN) 144 145 string16 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 185 scoped_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