Reactos
at master 336 lines 12 kB view raw
1// 2// environment_initialization.cpp 3// 4// Copyright (c) Microsoft Corporation. All rights reserved. 5// 6// Defines functions for initializing and uninitializing the global environments 7// and for constructing and destroying environments. The logic for manipulating 8// the environment data structures is split across this file and the setenv.cpp 9// file. 10// 11#include <corecrt_internal_traits.h> 12#include <stdlib.h> 13#include <string.h> 14 15 16 17// The global environment data. The initial environments store the pointer to 18// the environment that is passed to main or wmain. This is used only to 19// ensure that we do not modify that environment block after we pass it to 20// user code. The _environ_table and _wenviron_table hold the current CRT environment. 21// Their names cannot change; they are publicly documented. 22extern "C" 23{ 24 char** __dcrt_initial_narrow_environment = nullptr; 25 wchar_t** __dcrt_initial_wide_environment = nullptr; 26 27 __crt_state_management::dual_state_global<char**> _environ_table; 28 __crt_state_management::dual_state_global<wchar_t**> _wenviron_table; 29 30 char*** __cdecl __p__environ() { return &_environ_table.value(); } 31 wchar_t*** __cdecl __p__wenviron() { return &_wenviron_table.value(); } 32} 33 34 35 36_Ret_opt_z_ 37static char**& get_environment_nolock(char) throw() { return _environ_table.value(); } 38 39_Ret_opt_z_ 40static wchar_t**& get_environment_nolock(wchar_t) throw() { return _wenviron_table.value(); } 41 42_Ret_opt_z_ 43static char**& __cdecl get_initial_environment(char) throw() { return __dcrt_initial_narrow_environment; } 44 45_Ret_opt_z_ 46static wchar_t**& __cdecl get_initial_environment(wchar_t) throw() { return __dcrt_initial_wide_environment; } 47 48static __crt_state_management::dual_state_global<char**>& get_dual_state_environment_nolock(char) throw() { return _environ_table; } 49static __crt_state_management::dual_state_global<wchar_t**>& get_dual_state_environment_nolock(wchar_t) throw() { return _wenviron_table; } 50 51 52 53// Counts the number of environment variables in the provided 'environment_block', 54// excluding those that start with '=' (these are drive letter settings). 55template <typename Character> 56static size_t const count_variables_in_environment_block(Character* const environment_block) throw() 57{ 58 typedef __crt_char_traits<Character> traits; 59 60 // Count the number of variables in the environment block, ignoring drive 61 // letter settings, which begin with '=': 62 size_t count = 0; 63 64 Character* it = environment_block; 65 while (*it != '\0') 66 { 67 if (*it != '=') 68 ++count; 69 70 // This advances the iterator to the next string: 71 it += traits::tcslen(it) + 1; 72 } 73 74 return count; 75} 76 77 78 79//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 80// 81// Environment Create and Free (These do not modify any global data) 82// 83//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 84// Frees the environment pointed-to by 'environment'. This function ensures that 85// each of the environment strings is freed and that the array itself is freed. 86// This function requires that 'environment' is either nullptr or that it points 87// to a valid environment (i.e., one composed of a sequence of zero or more non- 88// null pointers terminated by a null pointer). 89template <typename Character> 90static void free_environment(Character** const environment) throw() 91{ 92 if (!environment) 93 return; 94 95 for (Character** it = environment; *it; ++it) 96 _free_crt(*it); 97 98 _free_crt(environment); 99} 100 101 102 103// Creates a new environment, populating it with the strings from the provided 104// 'environment_block', which must be a double-null-terminated sequence of 105// environment variable strings of the form "name=value". Variables beginning 106// with '=' are ignored (these are drive letter settings). Returns the newly 107// created environment on succes; returns nullptr on failure. 108template <typename Character> 109static Character** const create_environment(Character* const environment_block) throw() 110{ 111 typedef __crt_char_traits<Character> traits; 112 113 size_t const variable_count = count_variables_in_environment_block(environment_block); 114 115 __crt_unique_heap_ptr<Character*> environment(_calloc_crt_t(Character*, variable_count + 1)); 116 if (!environment) 117 return nullptr; 118 119 Character* source_it = environment_block; 120 Character** result_it = environment.get(); 121 122 while (*source_it != '\0') 123 { 124 size_t const required_count = traits::tcslen(source_it) + 1; 125 126 // Don't copy drive letter settings, which start with '=': 127 if (*source_it != '=') 128 { 129 __crt_unique_heap_ptr<Character> variable(_calloc_crt_t(Character, required_count)); 130 if (!variable) 131 { 132 free_environment(environment.detach()); 133 return nullptr; 134 } 135 136 _ERRCHECK(traits::tcscpy_s(variable.get(), required_count, source_it)); 137 *result_it++ = variable.detach(); 138 } 139 140 // This advances the iterator to the next string: 141 source_it += required_count; 142 } 143 144 // The sequence of pointers is already null-terminated; return it: 145 return environment.detach(); 146} 147 148 149 150//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 151// 152// Environment Initialize and Uninitialize 153// 154//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 155// In the initialize function below, we need to ensure that we've initialized 156// the mbc table before we start performing character transformations. 157static void pre_initialize(char) throw() { __acrt_initialize_multibyte(); } 158static void pre_initialize(wchar_t) throw() { /* no-op */ } 159 160 161 162// Gets the current environment from the operating system and initializes the 163// CRT environment from that environment. Returns 0 on success; -1 on failure. 164// If this function returns successfully, the global environment pointer for 165// the requested environment will be non-null and valid. 166template <typename Character> 167static int __cdecl common_initialize_environment_nolock() throw() 168{ 169 typedef __crt_char_traits<Character> traits; 170 171 // We only initialize the environment once. Once the environment has been 172 // initialized, all updates and modifications go through the other functions 173 // that manipulate the environment. 174 if (get_environment_nolock(Character())) 175 return 0; 176 177 pre_initialize(Character()); 178 179 __crt_unique_heap_ptr<Character> const os_environment(traits::get_environment_from_os()); 180 if (!os_environment) 181 return -1; 182 183 __crt_unique_heap_ptr<Character*> crt_environment(create_environment(os_environment.get())); 184 if (!crt_environment) 185 return -1; 186 187 get_initial_environment(Character()) = crt_environment.get(); 188 get_dual_state_environment_nolock(Character()).initialize(crt_environment.detach()); 189 return 0; 190} 191 192extern "C" int __cdecl _initialize_narrow_environment() 193{ 194 return common_initialize_environment_nolock<char>(); 195} 196 197extern "C" int __cdecl _initialize_wide_environment() 198{ 199 return common_initialize_environment_nolock<wchar_t>(); 200} 201 202 203 204// Frees the global wide and narrow environments and returns. 205template <typename Character> 206static void __cdecl uninitialize_environment_internal(Character**& environment) throw() 207{ 208 if (environment == get_initial_environment(Character())) 209 { 210 return; 211 } 212 213 free_environment(environment); 214} 215 216extern "C" void __cdecl __dcrt_uninitialize_environments_nolock() 217{ 218 _environ_table .uninitialize(uninitialize_environment_internal<char>); 219 _wenviron_table.uninitialize(uninitialize_environment_internal<wchar_t>); 220 221 free_environment(__dcrt_initial_narrow_environment); 222 free_environment(__dcrt_initial_wide_environment); 223} 224 225 226 227//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 228// 229// Environment Get-Or-Create 230// 231//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 232// These functions help with synchronization between the narrow and wide 233// environments. If the requested environment has not yet been initialized but 234// the other environment has been initialized, the other environment is cloned 235// to create the requested environment. Note that if the other environment has 236// not yet been initialized, these functions do not do anything. 237 238// Gets the other environment and copies each of the environment variables from 239// it into the requested environment. Returns 0 on success; -1 on failure. 240template <typename Character> 241static int __cdecl initialize_environment_by_cloning_nolock() throw() 242{ 243 typedef __crt_char_traits<Character> traits; 244 typedef typename traits::other_char_type other_char_type; 245 246 other_char_type** const other_environment = get_environment_nolock(other_char_type()); 247 if (!other_environment) 248 return -1; 249 250 for (other_char_type** it = other_environment; *it; ++it) 251 { 252 size_t const required_count = __crt_compute_required_transform_buffer_count(CP_ACP, *it); 253 if (required_count == 0) 254 return -1; 255 256 __crt_unique_heap_ptr<Character> buffer(_calloc_crt_t(Character, required_count)); 257 if (!buffer) 258 return -1; 259 260 size_t const actual_count = __crt_transform_string(CP_ACP, *it, buffer.get(), required_count); 261 if (actual_count == 0) 262 return -1; 263 264 // Ignore a failed attempt to set a variable; continue with the rest... 265 traits::set_variable_in_environment_nolock(buffer.detach(), 0); 266 } 267 268 return 0; 269} 270 271 272 273// If the requested environment exists, this function returns it unmodified. If 274// the requested environment does not exist but the other environment does, the 275// other environment is cloned to create the requested environment, and the new 276// requested environment is returned. Otherwise, nullptr is returned. 277template <typename Character> 278_Deref_ret_opt_z_ 279static Character** __cdecl common_get_or_create_environment_nolock() throw() 280{ 281 typedef __crt_char_traits<Character> traits; 282 typedef typename traits::other_char_type other_char_type; 283 284 // Check to see if the required environment already exists: 285 Character** const existing_environment = get_environment_nolock(Character()); 286 if (existing_environment) 287 return existing_environment; 288 289 // Check to see if the other environment exists. We will only initialize 290 // the environment here if the other environment was already initialized. 291 other_char_type** const other_environment = get_environment_nolock(other_char_type()); 292 if (!other_environment) 293 return nullptr; 294 295 if (common_initialize_environment_nolock<Character>() != 0) 296 { 297 if (initialize_environment_by_cloning_nolock<Character>() != 0) 298 { 299 return nullptr; 300 } 301 } 302 303 return get_environment_nolock(Character()); 304} 305 306extern "C" char** __cdecl __dcrt_get_or_create_narrow_environment_nolock() 307{ 308 return common_get_or_create_environment_nolock<char>(); 309} 310 311extern "C" wchar_t** __cdecl __dcrt_get_or_create_wide_environment_nolock() 312{ 313 return common_get_or_create_environment_nolock<wchar_t>(); 314} 315 316template <typename Character> 317static Character** __cdecl common_get_initial_environment() throw() 318{ 319 Character**& initial_environment = get_initial_environment(Character()); 320 if (!initial_environment) 321 { 322 initial_environment = common_get_or_create_environment_nolock<Character>(); 323 } 324 325 return initial_environment; 326} 327 328extern "C" char** __cdecl _get_initial_narrow_environment() 329{ 330 return common_get_initial_environment<char>(); 331} 332 333extern "C" wchar_t** __cdecl _get_initial_wide_environment() 334{ 335 return common_get_initial_environment<wchar_t>(); 336}