1 // 2 // putenv.cpp 3 // 4 // Copyright (c) Microsoft Corporation. All rights reserved. 5 // 6 // Defines the putenv() family of functions, which add, replace, or remove an 7 // environment variable from the current process environment. 8 // 9 #include <corecrt_internal.h> 10 #include <corecrt_internal_traits.h> 11 #include <stdlib.h> 12 #include <string.h> 13 14 15 16 // These functions test to see if the other environment exists 17 static bool other_environment_exists(wchar_t) throw() { return _environ_table.value() != nullptr; } 18 static bool other_environment_exists(char) throw() { return _wenviron_table.value() != nullptr; } 19 20 21 22 // This function computes the required size of the buffer that will store a 23 // "name=value" string in the other environment. 24 template <typename Character> 25 static size_t compute_required_transform_buffer_count( 26 Character const* const name, 27 Character const* const value 28 ) throw() 29 { 30 // Compute the amount of space required for the transformation: 31 size_t const name_count_required = __crt_compute_required_transform_buffer_count(CP_ACP, name); 32 _VALIDATE_RETURN_NOEXC(name_count_required != 0, EILSEQ, false); 33 34 if (!value) 35 return name_count_required; 36 37 size_t const value_count_required = __crt_compute_required_transform_buffer_count(CP_ACP, value); 38 _VALIDATE_RETURN_NOEXC(value_count_required != 0, EILSEQ, false); 39 40 // Note that each count includes space a the null terminator. Since we'll 41 // only be storing one terminator in the buffer, the space for the other 42 // terminator will be used to store the '=' between the name and the value. 43 return name_count_required + value_count_required; 44 } 45 46 47 48 // Constructs an environment string of the form "name=value" from a {name, value} 49 // pair. Returns a pointer to the resulting string. The string is dynamically 50 // allocated and must be freed by the caller (via the CRT free). 51 template <typename Character> 52 static Character* create_environment_string( 53 Character const* const name, 54 Character const* const value 55 ) throw() 56 { 57 typedef __crt_char_traits<Character> traits; 58 59 if (value) 60 { 61 size_t const name_length = traits::tcsnlen(name, _MAX_ENV); 62 size_t const value_length = traits::tcsnlen(value, _MAX_ENV); 63 64 _VALIDATE_RETURN(name_length < _MAX_ENV, EINVAL, nullptr); 65 _VALIDATE_RETURN(value_length < _MAX_ENV, EINVAL, nullptr); 66 67 // We add two to the length: one for the '=' and one for the terminator 68 size_t const buffer_count = name_length + 1 + value_length + 1; 69 70 __crt_unique_heap_ptr<Character> buffer(_calloc_crt_t(Character, buffer_count)); 71 if (!buffer) 72 return nullptr; 73 74 traits::tcscpy_s(buffer.get(), buffer_count, name); 75 buffer.get()[name_length] = '='; 76 traits::tcscpy_s(buffer.get() + name_length + 1, value_length + 1, value); 77 78 return buffer.detach(); 79 } 80 else 81 { 82 Character const* const equal_sign_it = traits::tcschr(name, '='); 83 if (equal_sign_it) 84 { 85 // Validate the length of both the name and the value: 86 _VALIDATE_RETURN(equal_sign_it - name < _MAX_ENV, EINVAL, nullptr); 87 _VALIDATE_RETURN(traits::tcsnlen(equal_sign_it + 1, _MAX_ENV) < _MAX_ENV, EINVAL, nullptr); 88 } 89 90 size_t const buffer_count = traits::tcslen(name) + 1; 91 92 __crt_unique_heap_ptr<Character> buffer(_calloc_crt_t(Character, buffer_count)); 93 if (!buffer) 94 return nullptr; 95 96 traits::tcscpy_s(buffer.get(), buffer_count, name); 97 98 return buffer.detach(); 99 } 100 } 101 102 103 104 // Converts the {name, value} pair to the other kind of string (wchar_t => char, 105 // char => wchar_t) and updates the other environment. 106 template <typename Character> 107 static bool __cdecl set_variable_in_other_environment( 108 Character const* const name, 109 Character const* const value 110 ) throw() 111 { 112 typedef __crt_char_traits<Character> traits; 113 typedef typename traits::other_char_type other_char_type; 114 typedef __crt_char_traits<other_char_type> other_traits; 115 116 size_t const buffer_count = compute_required_transform_buffer_count(name, value); 117 118 __crt_unique_heap_ptr<other_char_type> buffer(_calloc_crt_t(other_char_type, buffer_count)); 119 if (!buffer) 120 return false; 121 122 size_t const name_written_count = __crt_transform_string(CP_ACP, name, buffer.get(), buffer_count); 123 _VALIDATE_RETURN_NOEXC(name_written_count != 0, EILSEQ, false); 124 125 if (value) 126 { 127 // Overwrite the null terminator with an '=': 128 buffer.get()[name_written_count - 1] = '='; 129 130 size_t const value_written_count = __crt_transform_string( 131 CP_ACP, 132 value, 133 buffer.get() + name_written_count, 134 buffer_count - name_written_count); 135 _VALIDATE_RETURN_NOEXC(value_written_count != 0, EILSEQ, false); 136 } 137 138 return other_traits::set_variable_in_environment_nolock(buffer.detach(), 0) == 0; 139 } 140 141 142 143 // Adds, replaces, or removes a variable in the current environment. For the 144 // functions that take a {name, value} pair, the name is the name of the variable 145 // and the value is the value it is to be given. For the functions that just 146 // take and option, the option is of the form "name=value". 147 // 148 // If the value is an empty string and the name names an existing environment 149 // variable, that variable is removed from the environment. If the value is 150 // a nonempty string and the name names an existing environment variable, the 151 // variable is updated to have the new value. If the value is a nonempty string 152 // and the name does not name an existing environment variable, a new variable 153 // is added to the environment. 154 // 155 // If the required environment does not yet exist, it is created from the other 156 // environment. If both environments exist, the modifications are made to both 157 // of them so that they are kept in sync. 158 // 159 // Returns 0 on success; -1 on failure. 160 template <typename Character> 161 static int __cdecl common_putenv_nolock( 162 Character const* const name, 163 Character const* const value 164 ) throw() 165 { 166 typedef __crt_char_traits<Character> traits; 167 168 // Ensure that the environment is initialized: 169 if (!_environ_table.value() && !_wenviron_table.value()) 170 return -1; 171 172 // At startup, we obtain the "native" flavor of environment strings from the 173 // operating system. So, a "main" program has _environ set and a "wmain" 174 // program has _wenviron set. Only when the user gets or puts the "other" 175 // flavor do we convert it. 176 _VALIDATE_RETURN(name != nullptr, EINVAL, -1); 177 178 __crt_unique_heap_ptr<Character> new_option(create_environment_string(name, value)); 179 if (!new_option) 180 return -1; 181 182 if (traits::set_variable_in_environment_nolock(new_option.detach(), 1) != 0) 183 return -1; 184 185 // See if the "other" environment type exists; if it doesn't, we're done. 186 // Otherwise, put the new option into the other environment as well. 187 if (!other_environment_exists(Character())) 188 return 0; 189 190 if (!set_variable_in_other_environment(name, value)) 191 return -1; 192 193 return 0; 194 } 195 196 197 198 template <typename Character> 199 static int __cdecl common_putenv( 200 Character const* const name, 201 Character const* const value 202 ) throw() 203 { 204 int status = 0; 205 206 __acrt_lock(__acrt_environment_lock); 207 __try 208 { 209 status = common_putenv_nolock(name, value); 210 } 211 __finally 212 { 213 __acrt_unlock(__acrt_environment_lock); 214 } 215 __endtry 216 217 return status; 218 } 219 220 221 222 extern "C" int __cdecl _putenv(char const* const option) 223 { 224 return common_putenv(option, static_cast<char const*>(nullptr)); 225 } 226 227 extern "C" int __cdecl _wputenv(wchar_t const* const option) 228 { 229 return common_putenv(option, static_cast<wchar_t const*>(nullptr)); 230 } 231 232 233 234 extern "C" errno_t __cdecl _putenv_s(char const* const name, char const* const value) 235 { 236 _VALIDATE_RETURN_ERRCODE(value != nullptr, EINVAL); 237 return common_putenv(name, value) == 0 ? 0 : errno; 238 } 239 240 extern "C" errno_t __cdecl _wputenv_s(wchar_t const* const name, wchar_t const* const value) 241 { 242 _VALIDATE_RETURN_ERRCODE(value != nullptr, EINVAL); 243 return common_putenv(name, value) == 0 ? 0 : errno; 244 } 245