// // getenv.cpp // // Copyright (c) Microsoft Corporation. All rights reserved. // // Defines the getenv family of functions, which search the environment for a // variable and return its value. // #include #include #include //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // getenv() and _wgetenv() // //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // These functions search the environment for a variable with the given name. // If such a variable is found, a pointer to its value is returned. Otherwise, // nullptr is returned. Note that if the environment is access and manipulated // from multiple threads, this function cannot be safely used: the returned // pointer may not be valid when the function returns. template static Character* __cdecl common_getenv_nolock(Character const* const name) throw() { typedef __crt_char_traits traits; Character** const environment = traits::get_or_create_environment_nolock(); if (environment == nullptr || name == nullptr) return nullptr; size_t const name_length = traits::tcslen(name); for (Character** current = environment; *current; ++current) { if (traits::tcslen(*current) <= name_length) continue; if (*(*current + name_length) != '=') continue; if (traits::tcsnicoll(*current, name, name_length) != 0) continue; // Internal consistency check: The environment string should never use // a bigger buffer than _MAX_ENV. See also the SetEnvironmentVariable // SDK function. _ASSERTE(traits::tcsnlen(*current + name_length + 1, _MAX_ENV) < _MAX_ENV); return *current + name_length + 1; } return nullptr; } template static Character* __cdecl common_getenv(Character const* const name) throw() { typedef __crt_char_traits traits; _VALIDATE_RETURN(name != nullptr, EINVAL, nullptr); _VALIDATE_RETURN(traits::tcsnlen(name, _MAX_ENV) < _MAX_ENV, EINVAL, nullptr); Character* result = 0; __acrt_lock(__acrt_environment_lock); __try { result = common_getenv_nolock(name); } __finally { __acrt_unlock(__acrt_environment_lock); } __endtry return result; } extern "C" char* __cdecl getenv(char const* const name) { return common_getenv(name); } extern "C" wchar_t* __cdecl _wgetenv(wchar_t const* const name) { return common_getenv(name); } //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // getenv_s() and _wgetenv_s() // //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // These functions search the environment for a variable with the given name. // If such a variable is found, its value is copied into the provided buffer. // The number of characters in the value (including the null terminator) is // stored in '*required_count'. Returns 0 on success; returns ERANGE if the // provided buffer is too small; otherwise returns an error code on failure. template _Success_(return == 0) static errno_t __cdecl common_getenv_s_nolock( size_t* const required_count, _Out_writes_z_(buffer_count) Character* const buffer, size_t const buffer_count, Character const* const name ) throw() { typedef __crt_char_traits traits; _VALIDATE_RETURN_ERRCODE(required_count != nullptr, EINVAL); *required_count = 0; _VALIDATE_RETURN_ERRCODE( (buffer != nullptr && buffer_count > 0) || (buffer == nullptr && buffer_count == 0), EINVAL); if (buffer) buffer[0] = '\0'; Character const* const value = common_getenv_nolock(name); if (!value) return 0; *required_count = traits::tcslen(value) + 1; if (buffer_count == 0) return 0; // The buffer is too small; we return an error code and the caller can have // the opportunity to try again with a larger buffer: if (*required_count > buffer_count) return ERANGE; _ERRCHECK(traits::tcscpy_s(buffer, buffer_count, value)); return 0; } template _Success_(return == 0) static errno_t __cdecl common_getenv_s( size_t* const required_count, _Out_writes_z_(buffer_count) Character* const buffer, size_t const buffer_count, Character const* const name ) throw() { errno_t status = 0; __acrt_lock(__acrt_environment_lock); __try { status = common_getenv_s_nolock(required_count, buffer, buffer_count, name); } __finally { __acrt_unlock(__acrt_environment_lock); } __endtry return status; } extern "C" errno_t __cdecl getenv_s( size_t* const required_count, char* const buffer, size_t const buffer_count, char const* const name ) { return common_getenv_s(required_count, buffer, buffer_count, name); } extern "C" errno_t __cdecl _wgetenv_s( size_t* const required_count, wchar_t* const buffer, size_t const buffer_count, wchar_t const* const name ) { return common_getenv_s(required_count, buffer, buffer_count, name); } //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // _dupenv_s() and _wdupenv_s() // //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // These functions search the environment for a variable with the given name. // If a variable is found, a buffer is allocated using the public malloc to hold // the value of the variable. The value is copied into the buffer and the buffer // is returned to the caller via 'buffer_pointer' and 'buffer_count'. The caller // is responsible for freeing the buffer. // // Returns zero on success; returns an error code on failure. Note that if a // variable with the specified name is not found, that is still considered a // success; in this case, the 'value' is an empty string ('*buffer_count' will // be zero and '*buffer_pointer' will be nullptr). template static errno_t __cdecl common_dupenv_s_nolock( _Outptr_result_buffer_maybenull_(*buffer_count) _Outptr_result_maybenull_z_ Character** const buffer_pointer, _Out_opt_ size_t* const buffer_count, Character const* const name, int const block_use, char const* const file_name, int const line_number ) throw() { // These are referenced only in the Debug CRT build UNREFERENCED_PARAMETER(block_use); UNREFERENCED_PARAMETER(file_name); UNREFERENCED_PARAMETER(line_number); typedef __crt_char_traits traits; _VALIDATE_RETURN_ERRCODE(buffer_pointer != nullptr, EINVAL); *buffer_pointer = nullptr; if (buffer_count != nullptr) *buffer_count = 0; _VALIDATE_RETURN_ERRCODE(name != nullptr, EINVAL); Character const* const value = common_getenv_nolock(name); if (value == nullptr) return 0; size_t const value_count = traits::tcslen(value) + 1; *buffer_pointer = static_cast(_calloc_dbg( value_count, sizeof(Character), block_use, file_name, line_number)); _VALIDATE_RETURN_ERRCODE_NOEXC(*buffer_pointer != nullptr, ENOMEM); _ERRCHECK(traits::tcscpy_s(*buffer_pointer, value_count, value)); if (buffer_count != nullptr) *buffer_count = value_count; return 0; } template _Success_(return != 0) static errno_t __cdecl common_dupenv_s( _Outptr_result_buffer_maybenull_(*buffer_count) _Outptr_result_maybenull_z_ Character** const buffer_pointer, _Out_opt_ size_t* const buffer_count, Character const* const name, int const block_use, char const* const file_name, int const line_number ) throw() { errno_t status = 0; __acrt_lock(__acrt_environment_lock); __try { status = common_dupenv_s_nolock( buffer_pointer, buffer_count, name, block_use, file_name, line_number); } __finally { __acrt_unlock(__acrt_environment_lock); } __endtry return status; } extern "C" errno_t __cdecl _dupenv_s( char** const buffer_pointer, size_t* const buffer_count, char const* const name ) { return common_dupenv_s(buffer_pointer, buffer_count, name, _NORMAL_BLOCK, nullptr, 0); } extern "C" errno_t __cdecl _wdupenv_s( wchar_t** const buffer_pointer, size_t* const buffer_count, wchar_t const* const name ) { return common_dupenv_s(buffer_pointer, buffer_count, name, _NORMAL_BLOCK, nullptr, 0); } #ifdef _DEBUG #undef _dupenv_s_dbg #undef _wdupenv_s_dbg extern "C" errno_t __cdecl _dupenv_s_dbg( char** const buffer_pointer, size_t* const buffer_count, char const* const name, int const block_use, char const* const file_name, int const line_number ) { return common_dupenv_s(buffer_pointer, buffer_count, name, block_use, file_name, line_number); } extern "C" errno_t __cdecl _wdupenv_s_dbg( wchar_t** const buffer_pointer, size_t* const buffer_count, wchar_t const* const name, int const block_use, char const* const file_name, int const line_number ) { return common_dupenv_s(buffer_pointer, buffer_count, name, block_use, file_name, line_number); } #endif // _DEBUG