[UCRT] Import Microsoft.Windows.SDK.CRTSource version 10.0.22621.3

Imported from https://www.nuget.org/packages/Microsoft.Windows.SDK.CRTSource/10.0.22621.3
License: MIT
This commit is contained in:
Timo Kreuzer 2024-05-11 07:03:12 +02:00
parent f1b60c66f0
commit 04e0dc4a7a
568 changed files with 115483 additions and 0 deletions

View file

@ -0,0 +1,336 @@
//
// environment_initialization.cpp
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Defines functions for initializing and uninitializing the global environments
// and for constructing and destroying environments. The logic for manipulating
// the environment data structures is split across this file and the setenv.cpp
// file.
//
#include <corecrt_internal_traits.h>
#include <stdlib.h>
#include <string.h>
// The global environment data. The initial environments store the pointer to
// the environment that is passed to main or wmain. This is used only to
// ensure that we do not modify that environment block after we pass it to
// user code. The _environ_table and _wenviron_table hold the current CRT environment.
// Their names cannot change; they are publicly documented.
extern "C"
{
char** __dcrt_initial_narrow_environment = nullptr;
wchar_t** __dcrt_initial_wide_environment = nullptr;
__crt_state_management::dual_state_global<char**> _environ_table;
__crt_state_management::dual_state_global<wchar_t**> _wenviron_table;
char*** __cdecl __p__environ() { return &_environ_table.value(); }
wchar_t*** __cdecl __p__wenviron() { return &_wenviron_table.value(); }
}
_Ret_opt_z_
static char**& get_environment_nolock(char) throw() { return _environ_table.value(); }
_Ret_opt_z_
static wchar_t**& get_environment_nolock(wchar_t) throw() { return _wenviron_table.value(); }
_Ret_opt_z_
static char**& __cdecl get_initial_environment(char) throw() { return __dcrt_initial_narrow_environment; }
_Ret_opt_z_
static wchar_t**& __cdecl get_initial_environment(wchar_t) throw() { return __dcrt_initial_wide_environment; }
static __crt_state_management::dual_state_global<char**>& get_dual_state_environment_nolock(char) throw() { return _environ_table; }
static __crt_state_management::dual_state_global<wchar_t**>& get_dual_state_environment_nolock(wchar_t) throw() { return _wenviron_table; }
// Counts the number of environment variables in the provided 'environment_block',
// excluding those that start with '=' (these are drive letter settings).
template <typename Character>
static size_t const count_variables_in_environment_block(Character* const environment_block) throw()
{
typedef __crt_char_traits<Character> traits;
// Count the number of variables in the environment block, ignoring drive
// letter settings, which begin with '=':
size_t count = 0;
Character* it = environment_block;
while (*it != '\0')
{
if (*it != '=')
++count;
// This advances the iterator to the next string:
it += traits::tcslen(it) + 1;
}
return count;
}
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Environment Create and Free (These do not modify any global data)
//
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// Frees the environment pointed-to by 'environment'. This function ensures that
// each of the environment strings is freed and that the array itself is freed.
// This function requires that 'environment' is either nullptr or that it points
// to a valid environment (i.e., one composed of a sequence of zero or more non-
// null pointers terminated by a null pointer).
template <typename Character>
static void free_environment(Character** const environment) throw()
{
if (!environment)
return;
for (Character** it = environment; *it; ++it)
_free_crt(*it);
_free_crt(environment);
}
// Creates a new environment, populating it with the strings from the provided
// 'environment_block', which must be a double-null-terminated sequence of
// environment variable strings of the form "name=value". Variables beginning
// with '=' are ignored (these are drive letter settings). Returns the newly
// created environment on succes; returns nullptr on failure.
template <typename Character>
static Character** const create_environment(Character* const environment_block) throw()
{
typedef __crt_char_traits<Character> traits;
size_t const variable_count = count_variables_in_environment_block(environment_block);
__crt_unique_heap_ptr<Character*> environment(_calloc_crt_t(Character*, variable_count + 1));
if (!environment)
return nullptr;
Character* source_it = environment_block;
Character** result_it = environment.get();
while (*source_it != '\0')
{
size_t const required_count = traits::tcslen(source_it) + 1;
// Don't copy drive letter settings, which start with '=':
if (*source_it != '=')
{
__crt_unique_heap_ptr<Character> variable(_calloc_crt_t(Character, required_count));
if (!variable)
{
free_environment(environment.detach());
return nullptr;
}
_ERRCHECK(traits::tcscpy_s(variable.get(), required_count, source_it));
*result_it++ = variable.detach();
}
// This advances the iterator to the next string:
source_it += required_count;
}
// The sequence of pointers is already null-terminated; return it:
return environment.detach();
}
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Environment Initialize and Uninitialize
//
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// In the initialize function below, we need to ensure that we've initialized
// the mbc table before we start performing character transformations.
static void pre_initialize(char) throw() { __acrt_initialize_multibyte(); }
static void pre_initialize(wchar_t) throw() { /* no-op */ }
// Gets the current environment from the operating system and initializes the
// CRT environment from that environment. Returns 0 on success; -1 on failure.
// If this function returns successfully, the global environment pointer for
// the requested environment will be non-null and valid.
template <typename Character>
static int __cdecl common_initialize_environment_nolock() throw()
{
typedef __crt_char_traits<Character> traits;
// We only initialize the environment once. Once the environment has been
// initialized, all updates and modifications go through the other functions
// that manipulate the environment.
if (get_environment_nolock(Character()))
return 0;
pre_initialize(Character());
__crt_unique_heap_ptr<Character> const os_environment(traits::get_environment_from_os());
if (!os_environment)
return -1;
__crt_unique_heap_ptr<Character*> crt_environment(create_environment(os_environment.get()));
if (!crt_environment)
return -1;
get_initial_environment(Character()) = crt_environment.get();
get_dual_state_environment_nolock(Character()).initialize(crt_environment.detach());
return 0;
}
extern "C" int __cdecl _initialize_narrow_environment()
{
return common_initialize_environment_nolock<char>();
}
extern "C" int __cdecl _initialize_wide_environment()
{
return common_initialize_environment_nolock<wchar_t>();
}
// Frees the global wide and narrow environments and returns.
template <typename Character>
static void __cdecl uninitialize_environment_internal(Character**& environment) throw()
{
if (environment == get_initial_environment(Character()))
{
return;
}
free_environment(environment);
}
extern "C" void __cdecl __dcrt_uninitialize_environments_nolock()
{
_environ_table .uninitialize(uninitialize_environment_internal<char>);
_wenviron_table.uninitialize(uninitialize_environment_internal<wchar_t>);
free_environment(__dcrt_initial_narrow_environment);
free_environment(__dcrt_initial_wide_environment);
}
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Environment Get-Or-Create
//
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// These functions help with synchronization between the narrow and wide
// environments. If the requested environment has not yet been initialized but
// the other environment has been initialized, the other environment is cloned
// to create the requested environment. Note that if the other environment has
// not yet been initialized, these functions do not do anything.
// Gets the other environment and copies each of the environment variables from
// it into the requested environment. Returns 0 on success; -1 on failure.
template <typename Character>
static int __cdecl initialize_environment_by_cloning_nolock() throw()
{
typedef __crt_char_traits<Character> traits;
typedef typename traits::other_char_type other_char_type;
other_char_type** const other_environment = get_environment_nolock(other_char_type());
if (!other_environment)
return -1;
for (other_char_type** it = other_environment; *it; ++it)
{
size_t const required_count = __crt_compute_required_transform_buffer_count(CP_ACP, *it);
if (required_count == 0)
return -1;
__crt_unique_heap_ptr<Character> buffer(_calloc_crt_t(Character, required_count));
if (!buffer)
return -1;
size_t const actual_count = __crt_transform_string(CP_ACP, *it, buffer.get(), required_count);
if (actual_count == 0)
return -1;
// Ignore a failed attempt to set a variable; continue with the rest...
traits::set_variable_in_environment_nolock(buffer.detach(), 0);
}
return 0;
}
// If the requested environment exists, this function returns it unmodified. If
// the requested environment does not exist but the other environment does, the
// other environment is cloned to create the requested environment, and the new
// requested environment is returned. Otherwise, nullptr is returned.
template <typename Character>
_Deref_ret_opt_z_
static Character** __cdecl common_get_or_create_environment_nolock() throw()
{
typedef __crt_char_traits<Character> traits;
typedef typename traits::other_char_type other_char_type;
// Check to see if the required environment already exists:
Character** const existing_environment = get_environment_nolock(Character());
if (existing_environment)
return existing_environment;
// Check to see if the other environment exists. We will only initialize
// the environment here if the other environment was already initialized.
other_char_type** const other_environment = get_environment_nolock(other_char_type());
if (!other_environment)
return nullptr;
if (common_initialize_environment_nolock<Character>() != 0)
{
if (initialize_environment_by_cloning_nolock<Character>() != 0)
{
return nullptr;
}
}
return get_environment_nolock(Character());
}
extern "C" char** __cdecl __dcrt_get_or_create_narrow_environment_nolock()
{
return common_get_or_create_environment_nolock<char>();
}
extern "C" wchar_t** __cdecl __dcrt_get_or_create_wide_environment_nolock()
{
return common_get_or_create_environment_nolock<wchar_t>();
}
template <typename Character>
static Character** __cdecl common_get_initial_environment() throw()
{
Character**& initial_environment = get_initial_environment(Character());
if (!initial_environment)
{
initial_environment = common_get_or_create_environment_nolock<Character>();
}
return initial_environment;
}
extern "C" char** __cdecl _get_initial_narrow_environment()
{
return common_get_initial_environment<char>();
}
extern "C" wchar_t** __cdecl _get_initial_wide_environment()
{
return common_get_initial_environment<wchar_t>();
}

View file

@ -0,0 +1,121 @@
//
// get_environment_from_os.cpp
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Defines a pair of functions to get the environment strings from the operating
// system. These functions copy the environment strings into a CRT-allocated
// buffer and return a pointer to that buffer. The caller is responsible for
// freeing the returned buffer (via _crt_free).
//
#include <corecrt_internal.h>
#include <corecrt_internal_traits.h>
#include <stdlib.h>
namespace
{
struct environment_strings_traits
{
typedef wchar_t* type;
static bool close(_In_ type p) throw()
{
FreeEnvironmentStringsW(p);
return true;
}
static type get_invalid_value() throw()
{
return nullptr;
}
};
typedef __crt_unique_handle_t<environment_strings_traits> environment_strings_handle;
}
static wchar_t const* __cdecl find_end_of_double_null_terminated_sequence(wchar_t const* const first) throw()
{
wchar_t const* last = first;
for (; *last != '\0'; last += wcslen(last) + 1)
{
}
return last + 1; // Return the pointer one-past-the-end of the double null terminator
}
extern "C" wchar_t* __cdecl __dcrt_get_wide_environment_from_os() throw()
{
environment_strings_handle const environment(GetEnvironmentStringsW());
if (!environment)
return nullptr;
// Find the
wchar_t const* const first = environment.get();
wchar_t const* const last = find_end_of_double_null_terminated_sequence(first);
size_t const required_count = last - first;
__crt_unique_heap_ptr<wchar_t> buffer(_malloc_crt_t(wchar_t, required_count));
if (!buffer)
return nullptr;
// Note that the multiplication here cannot overflow:
memcpy(buffer.get(), environment.get(), required_count * sizeof(wchar_t));
return buffer.detach();
}
extern "C" char* __cdecl __dcrt_get_narrow_environment_from_os() throw()
{
// Note that we call GetEnvironmentStringsW and convert to multibyte. The
// GetEnvironmentStringsA function returns the environment in the OEM code
// page, but we need the strings in ANSI.
environment_strings_handle const environment(GetEnvironmentStringsW());
if (!environment.is_valid())
return nullptr;
wchar_t const* const first = environment.get();
wchar_t const* const last = find_end_of_double_null_terminated_sequence(first);
size_t const required_wide_count = last - first;
#pragma warning(suppress:__WARNING_W2A_BEST_FIT) // 38021 Prefast recommends WC_NO_BEST_FIT_CHARS.
size_t const required_narrow_count = static_cast<size_t>(__acrt_WideCharToMultiByte(
CP_ACP,
0,
environment.get(),
static_cast<int>(required_wide_count),
nullptr,
0,
nullptr,
nullptr));
if (required_narrow_count == 0)
return nullptr;
__crt_unique_heap_ptr<char> buffer(_malloc_crt_t(char, required_narrow_count));
if (!buffer)
return nullptr;
#pragma warning(suppress:__WARNING_W2A_BEST_FIT) // 38021 Prefast recommends WC_NO_BEST_FIT_CHARS.
int const conversion_result = __acrt_WideCharToMultiByte(
CP_ACP,
0,
environment.get(),
static_cast<int>(required_wide_count),
buffer.get(),
static_cast<int>(required_narrow_count),
nullptr,
nullptr);
if (conversion_result == 0)
return nullptr;
return buffer.detach();
}

329
sdk/lib/ucrt/env/getenv.cpp vendored Normal file
View file

@ -0,0 +1,329 @@
//
// 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 <corecrt_internal_traits.h>
#include <stdlib.h>
#include <string.h>
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// 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 <typename Character>
static Character* __cdecl common_getenv_nolock(Character const* const name) throw()
{
typedef __crt_char_traits<Character> 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 <typename Character>
static Character* __cdecl common_getenv(Character const* const name) throw()
{
typedef __crt_char_traits<Character> 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);
}
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 <typename Character>
_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<Character> 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 <typename Character>
_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);
}
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 <typename Character>
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<Character> 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<Character*>(_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 <typename Character>
_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);
}
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

159
sdk/lib/ucrt/env/getpath.cpp vendored Normal file
View file

@ -0,0 +1,159 @@
/***
*getpath.c - extract a pathname from an environment variable
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
* Extract pathnames from a string of semicolon delimited pathnames
* (generally the value of an environment variable such as PATH).
*
*******************************************************************************/
#include <corecrt_internal.h>
#include <stddef.h>
/***
*_getpath() - extract a pathname from a semicolon-delimited list of pathnames
*
*Purpose:
* To extract the next pathname from a semicolon-delimited list of
* pathnames (usually the value on an environment variable) and copy
* it to a caller-specified buffer. No check is done to see if the path
* is valid. The maximum number of characters copied to the buffer is
* maxlen - 1 (and then a '\0' is appended).
*
*ifdef _HPFS_
* If we hit a quoted string, then allow any characters inside.
* For example, to put a semi-colon in a path, the user could have
* an environment variable that looks like:
*
* PATH=C:\BIN;"D:\CRT\TOOLS;B1";C:\BINP
*endif
*
* NOTE: Semi-colons in sequence are skipped over; pointers to 0-length
* pathnames are NOT returned (this includes leading semi-colons).
*
* NOTE: If this routine is made user-callable, the near attribute
* must be replaced by _LOAD_DS and the prototype moved from INTERNAL.H
* to STDLIB.H. The source files MISC\SEARCHEN.C and EXEC\SPAWNVPE.C
* will need to be recompiled, but should not require any changes.
*
*Entry:
* src - Pointer to a string of 0 or more path specificiations,
* delimited by semicolons (';'), and terminated by a null
* character
* dst - Pointer to the buffer where the next path specification is to
* be copied
* maxlen - Maximum number of characters to be copied, counting the
* terminating null character. Note that a value of 0 is treated
* as UINT_MAX + 1.
*
*Exit:
* If a pathname is successfully extracted and copied, a pointer to the
* first character of next pathname is returned (intervening semicolons
* are skipped over). If the pathname is too long, as much as possible
* is copied to the user-specified buffer and nullptr is returned.
*
* Note that the no check is made of the validity of the copied pathname.
*
*Exceptions:
*
*******************************************************************************/
template <typename Character>
_Success_(return != 0)
static Character* __cdecl common_getpath(
_In_z_ Character const* const delimited_paths,
_Out_writes_z_(result_count) Character* const result,
size_t const result_count
) throw()
{
_VALIDATE_RETURN_NOEXC(result != nullptr, EINVAL, nullptr);
if (result_count > 0)
{
result[0] = '\0';
}
_VALIDATE_RETURN_NOEXC(result_count > 1, EINVAL, nullptr);
Character const* source_it = delimited_paths;
// Skip past any leading semicolons:
while (*source_it == ';')
{
++source_it;
}
Character const* const source_first = source_it;
Character* result_it = result;
Character* const result_last = result + result_count - 1; // Leave room for \0
#pragma warning(suppress:__WARNING_POTENTIAL_BUFFER_OVERFLOW_NULLTERMINATED) // 26018 Prefast is confused.
while (*source_it != '\0' && *source_it != ';')
{
if (*source_it == '"')
{
// We found a quote; copy all characters until we reach the matching
// end quote or the end of the string:
++source_it; // Advance past opening quote
while (*source_it != '\0' && *source_it != '"')
{
#pragma warning(suppress:__WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY) // 26015 Prefast is confused.
*result_it++ = *source_it++;
if (result_it == result_last)
{
*result_it = '\0';
errno = ERANGE;
return nullptr;
}
}
if (*source_it != '\0')
{
++source_it; // Advance past closing quote
}
}
else
{
*result_it++ = *source_it++;
if (result_it == result_last)
{
*result_it = '\0';
errno = ERANGE;
return nullptr;
}
}
}
// If we copied something and stopped because we reached a semicolon, skip
// any semicolons before returning:
while (*source_it == ';')
{
++source_it;
}
*result_it = '\0';
return source_it == source_first
? nullptr
: const_cast<Character*>(source_it);
}
extern "C" char* __cdecl __acrt_getpath(
char const* const delimited_paths,
char* const result,
size_t const result_count
)
{
return common_getpath(delimited_paths, result, result_count);
}
extern "C" wchar_t* __cdecl __acrt_wgetpath(
wchar_t const* const delimited_paths,
wchar_t* const result,
size_t const result_count
)
{
return common_getpath(delimited_paths, result, result_count);
}

243
sdk/lib/ucrt/env/putenv.cpp vendored Normal file
View file

@ -0,0 +1,243 @@
//
// putenv.cpp
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Defines the putenv() family of functions, which add, replace, or remove an
// environment variable from the current process environment.
//
#include <corecrt_internal.h>
#include <corecrt_internal_traits.h>
#include <stdlib.h>
#include <string.h>
// These functions test to see if the other environment exists
static bool other_environment_exists(wchar_t) throw() { return _environ_table.value() != nullptr; }
static bool other_environment_exists(char) throw() { return _wenviron_table.value() != nullptr; }
// This function computes the required size of the buffer that will store a
// "name=value" string in the other environment.
template <typename Character>
static size_t compute_required_transform_buffer_count(
Character const* const name,
Character const* const value
) throw()
{
// Compute the amount of space required for the transformation:
size_t const name_count_required = __crt_compute_required_transform_buffer_count(CP_ACP, name);
_VALIDATE_RETURN_NOEXC(name_count_required != 0, EILSEQ, false);
if (!value)
return name_count_required;
size_t const value_count_required = __crt_compute_required_transform_buffer_count(CP_ACP, value);
_VALIDATE_RETURN_NOEXC(value_count_required != 0, EILSEQ, false);
// Note that each count includes space a the null terminator. Since we'll
// only be storing one terminator in the buffer, the space for the other
// terminator will be used to store the '=' between the name and the value.
return name_count_required + value_count_required;
}
// Constructs an environment string of the form "name=value" from a {name, value}
// pair. Returns a pointer to the resulting string. The string is dynamically
// allocated and must be freed by the caller (via the CRT free).
template <typename Character>
static Character* create_environment_string(
Character const* const name,
Character const* const value
) throw()
{
typedef __crt_char_traits<Character> traits;
if (value)
{
size_t const name_length = traits::tcsnlen(name, _MAX_ENV);
size_t const value_length = traits::tcsnlen(value, _MAX_ENV);
_VALIDATE_RETURN(name_length < _MAX_ENV, EINVAL, nullptr);
_VALIDATE_RETURN(value_length < _MAX_ENV, EINVAL, nullptr);
// We add two to the length: one for the '=' and one for the terminator
size_t const buffer_count = name_length + 1 + value_length + 1;
__crt_unique_heap_ptr<Character> buffer(_calloc_crt_t(Character, buffer_count));
if (!buffer)
return nullptr;
traits::tcscpy_s(buffer.get(), buffer_count, name);
buffer.get()[name_length] = '=';
traits::tcscpy_s(buffer.get() + name_length + 1, value_length + 1, value);
return buffer.detach();
}
else
{
Character const* const equal_sign_it = traits::tcschr(name, '=');
if (equal_sign_it)
{
// Validate the length of both the name and the value:
_VALIDATE_RETURN(equal_sign_it - name < _MAX_ENV, EINVAL, nullptr);
_VALIDATE_RETURN(traits::tcsnlen(equal_sign_it + 1, _MAX_ENV) < _MAX_ENV, EINVAL, nullptr);
}
size_t const buffer_count = traits::tcslen(name) + 1;
__crt_unique_heap_ptr<Character> buffer(_calloc_crt_t(Character, buffer_count));
if (!buffer)
return nullptr;
traits::tcscpy_s(buffer.get(), buffer_count, name);
return buffer.detach();
}
}
// Converts the {name, value} pair to the other kind of string (wchar_t => char,
// char => wchar_t) and updates the other environment.
template <typename Character>
static bool __cdecl set_variable_in_other_environment(
Character const* const name,
Character const* const value
) throw()
{
typedef __crt_char_traits<Character> traits;
typedef typename traits::other_char_type other_char_type;
typedef __crt_char_traits<other_char_type> other_traits;
size_t const buffer_count = compute_required_transform_buffer_count(name, value);
__crt_unique_heap_ptr<other_char_type> buffer(_calloc_crt_t(other_char_type, buffer_count));
if (!buffer)
return false;
size_t const name_written_count = __crt_transform_string(CP_ACP, name, buffer.get(), buffer_count);
_VALIDATE_RETURN_NOEXC(name_written_count != 0, EILSEQ, false);
if (value)
{
// Overwrite the null terminator with an '=':
buffer.get()[name_written_count - 1] = '=';
size_t const value_written_count = __crt_transform_string(
CP_ACP,
value,
buffer.get() + name_written_count,
buffer_count - name_written_count);
_VALIDATE_RETURN_NOEXC(value_written_count != 0, EILSEQ, false);
}
return other_traits::set_variable_in_environment_nolock(buffer.detach(), 0) == 0;
}
// Adds, replaces, or removes a variable in the current environment. For the
// functions that take a {name, value} pair, the name is the name of the variable
// and the value is the value it is to be given. For the functions that just
// take and option, the option is of the form "name=value".
//
// If the value is an empty string and the name names an existing environment
// variable, that variable is removed from the environment. If the value is
// a nonempty string and the name names an existing environment variable, the
// variable is updated to have the new value. If the value is a nonempty string
// and the name does not name an existing environment variable, a new variable
// is added to the environment.
//
// If the required environment does not yet exist, it is created from the other
// environment. If both environments exist, the modifications are made to both
// of them so that they are kept in sync.
//
// Returns 0 on success; -1 on failure.
template <typename Character>
static int __cdecl common_putenv_nolock(
Character const* const name,
Character const* const value
) throw()
{
typedef __crt_char_traits<Character> traits;
// Ensure that the environment is initialized:
if (!_environ_table.value() && !_wenviron_table.value())
return -1;
// At startup, we obtain the "native" flavor of environment strings from the
// operating system. So, a "main" program has _environ set and a "wmain"
// program has _wenviron set. Only when the user gets or puts the "other"
// flavor do we convert it.
_VALIDATE_RETURN(name != nullptr, EINVAL, -1);
__crt_unique_heap_ptr<Character> new_option(create_environment_string(name, value));
if (!new_option)
return -1;
if (traits::set_variable_in_environment_nolock(new_option.detach(), 1) != 0)
return -1;
// See if the "other" environment type exists; if it doesn't, we're done.
// Otherwise, put the new option into the other environment as well.
if (!other_environment_exists(Character()))
return 0;
if (!set_variable_in_other_environment(name, value))
return -1;
return 0;
}
template <typename Character>
static int __cdecl common_putenv(
Character const* const name,
Character const* const value
) throw()
{
int status = 0;
__acrt_lock(__acrt_environment_lock);
__try
{
status = common_putenv_nolock(name, value);
}
__finally
{
__acrt_unlock(__acrt_environment_lock);
}
return status;
}
extern "C" int __cdecl _putenv(char const* const option)
{
return common_putenv(option, static_cast<char const*>(nullptr));
}
extern "C" int __cdecl _wputenv(wchar_t const* const option)
{
return common_putenv(option, static_cast<wchar_t const*>(nullptr));
}
extern "C" errno_t __cdecl _putenv_s(char const* const name, char const* const value)
{
_VALIDATE_RETURN_ERRCODE(value != nullptr, EINVAL);
return common_putenv(name, value) == 0 ? 0 : errno;
}
extern "C" errno_t __cdecl _wputenv_s(wchar_t const* const name, wchar_t const* const value)
{
_VALIDATE_RETURN_ERRCODE(value != nullptr, EINVAL);
return common_putenv(name, value) == 0 ? 0 : errno;
}

215
sdk/lib/ucrt/env/searchenv.cpp vendored Normal file
View file

@ -0,0 +1,215 @@
//
// searchenv.cpp
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Defines the searchenv() family of functions, which search for a file in the
// paths contained in an environment variable (like %PATH%).
//
#include <direct.h>
#include <corecrt_internal_traits.h>
#include <io.h>
#include <stdlib.h>
#include <string.h>
#pragma warning(disable:__WARNING_POSTCONDITION_NULLTERMINATION_VIOLATION) // 26036 Prefast doesn't understand perfect forwarding so annotations are lost.
#pragma warning(disable:__WARNING_MISSING_ZERO_TERMINATION2) // 6054 Prefast doesn't understand perfect forwarding so annotations are lost.
// These functions search for a file in the paths in an environment variable.
// The environment variable named 'environment_variable' is retrieved from the
// environment. It is expected to contain a semicolon-delimited sequence of
// directories, similar to %PATH%. The file name is concatenated onto the end
// of each directory string in sequence and its existence is tested for. When
// a file is found, the search stops and the buffer is filled with the full path
// to the file. If the file is not found for whatever reason, a zero-length
// string is placed in the buffer and an error code is returned.
template <typename Character>
static errno_t __cdecl common_searchenv_s(
_In_z_ Character const* const file_name,
_In_z_ Character const* const environment_variable,
_Out_writes_z_(result_count) Character* const result_buffer,
_In_ size_t const result_count
) throw()
{
typedef __crt_char_traits<Character> traits;
_VALIDATE_RETURN_ERRCODE(result_buffer != nullptr, EINVAL);
_VALIDATE_RETURN_ERRCODE(result_count > 0, EINVAL);
if (!file_name)
{
result_buffer[0] = '\0';
_VALIDATE_RETURN_ERRCODE(file_name != nullptr, EINVAL);
}
// Special case: If the file name is an empty string, we'll just return an
// empty result_buffer and set errno:
if (file_name[0] == '\0')
{
result_buffer[0] = '\0';
return errno = ENOENT;
}
errno_t saved_errno = errno;
int const access_result = traits::taccess_s(file_name, 0);
errno = saved_errno;
// If the file name exists, convert it to a fully qualified result_buffer name:
if (access_result == 0)
{
if (traits::tfullpath(result_buffer, file_name, result_count) == nullptr)
{
result_buffer[0] = '\0';
return errno; // fullpath will set errno
}
return 0;
}
Character* path_string = nullptr;
if (_ERRCHECK_EINVAL(traits::tdupenv_s_crt(&path_string, nullptr, environment_variable)) != 0 || path_string == nullptr)
{
result_buffer[0] = '\0';
return errno = ENOENT; // The environment variable doesn't exist
}
__crt_unique_heap_ptr<Character> const path_string_cleanup(path_string);
size_t const file_name_length = traits::tcslen(file_name);
size_t const local_path_count = _MAX_PATH + 4;
Character local_path_buffer[local_path_count];
size_t path_count = local_path_count;
Character* path_buffer = local_path_buffer;
if (file_name_length >= result_count)
{
// The local buffer is not large enough; dynamically allocate a new
// buffer. We add two to the size to account for a trailing slash
// that we may need to add and for the null terminator:
path_count = traits::tcslen(path_string) + file_name_length + 2;
path_buffer = _calloc_crt_t(Character, path_count).detach(); // We'll retake ownership below
if (!path_buffer)
{
result_buffer[0] = '\0';
return errno = ENOMEM;
}
}
__crt_unique_heap_ptr<Character> path_buffer_cleanup(path_buffer == local_path_buffer
? nullptr
: path_buffer);
saved_errno = errno;
while (path_string)
{
Character* const previous_path_string = path_string;
path_string = traits::tgetpath(path_string, path_buffer, path_count - file_name_length - 1);
if (!path_string && path_buffer == local_path_buffer && errno == ERANGE)
{
// If the getpath operation failed because the buffer was not large
// enough, try allocating a larger buffer:
size_t const required_count = traits::tcslen(previous_path_string) + file_name_length + 2;
path_buffer_cleanup = _calloc_crt_t(Character, required_count);
if (!path_buffer_cleanup)
{
result_buffer[0] = '\0';
return errno = ENOMEM;
}
path_count = required_count;
path_buffer = path_buffer_cleanup.get();
path_string = traits::tgetpath(previous_path_string, path_buffer, path_count - file_name_length);
}
if (!path_string || path_buffer[0] == '\0')
{
result_buffer[0] = '\0';
return errno = ENOENT;
}
// The result_buffer now holds a non-empty path name from path_string
// and we know that the buffer is large enough to hold the concatenation
// of the path with the file name (if not, the call to getpath would
// have failed. So, we concatenate the path and file names:
size_t path_length = traits::tcslen(path_buffer);
Character* path_it = path_buffer + path_length;
// Add a trailing '\' if one is required:
Character const last_character = *(path_it - 1);
if (last_character != '/' && last_character != '\\' && last_character != ':')
{
*path_it++ = '\\';
++path_length;
}
// The path_it now points to the character following the trailing '\',
// '/', or ':'; this is where we copy the file name:
_ERRCHECK(traits::tcscpy_s(path_it, path_count - path_length, file_name));
// If we can't access the file at this path, it isn't a match:
if (traits::taccess_s(path_buffer, 0) != 0)
continue;
// Otherwise, we can access the file, and we copy the full path into the
// caller-provided buffer:
if (path_length + file_name_length + 1 > result_count)
{
result_buffer[0] = '\0';
return errno = ERANGE;
}
errno = saved_errno;
_ERRCHECK(traits::tcscpy_s(result_buffer, result_count, path_buffer));
return 0;
}
// If we get here, we must have tried every path in the environment and not
// found the file name:
result_buffer[0] = '\0';
return errno = ENOENT;
}
extern "C" errno_t __cdecl _searchenv_s(
char const* const file_name,
char const* const environment_variable,
char* const result_buffer,
size_t const result_count
)
{
return common_searchenv_s(file_name, environment_variable, result_buffer, result_count);
}
extern "C" errno_t __cdecl _wsearchenv_s(
wchar_t const* const file_name,
wchar_t const* const environment_variable,
wchar_t* const result_buffer,
size_t const result_count
)
{
return common_searchenv_s(file_name, environment_variable, result_buffer, result_count);
}
extern "C" void __cdecl _searchenv(
char const* const file_name,
char const* const environment_variable,
char* const result_buffer
)
{
common_searchenv_s(file_name, environment_variable, result_buffer, _MAX_PATH);
}
extern "C" void __cdecl _wsearchenv(
wchar_t const* const file_name,
wchar_t const* const environment_variable,
wchar_t* const result_buffer
)
{
common_searchenv_s(file_name, environment_variable, result_buffer, _MAX_PATH);
}

373
sdk/lib/ucrt/env/setenv.cpp vendored Normal file
View file

@ -0,0 +1,373 @@
//
// setenv.cpp
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Internal functions for setting or removing variables from an environment. The
// logic for manipulating the environment data structures is split across this
// file and environment_initialization.cpp.
//
#include <corecrt_internal.h>
#include <corecrt_internal_traits.h>
#include <limits.h>
#include <stdlib.h>
static char**& __cdecl get_environment(char) throw() { return _environ_table.value(); }
static wchar_t**& __cdecl get_environment(wchar_t) throw() { return _wenviron_table.value(); }
static wchar_t**& __cdecl get_other_environment(char) throw() { return _wenviron_table.value(); }
static char**& __cdecl get_other_environment(wchar_t) throw() { return _environ_table.value(); }
static char**& __cdecl get_initial_environment(char) throw() { return __dcrt_initial_narrow_environment; }
static wchar_t**& __cdecl get_initial_environment(wchar_t) throw() { return __dcrt_initial_wide_environment; }
// Makes a copy of the provided environment and returns the copy. The caller is
// responsible for freeing the returned array (using the CRT free). Returns
// nullptr on failure; terminates the process on allocation failure.
template <typename Character>
static Character** __cdecl copy_environment(Character** const old_environment) throw()
{
typedef __crt_char_traits<Character> traits;
if (!old_environment)
{
return nullptr;
}
// Count the number of environment variables:
size_t entry_count = 0;
for (Character** it = old_environment; *it; ++it)
{
++entry_count;
}
// We need one pointer for each string, plus one null pointer at the end:
__crt_unique_heap_ptr<Character*> new_environment(_calloc_crt_t(Character*, entry_count + 1));
if (!new_environment)
{
abort();
}
Character** old_it = old_environment;
Character** new_it = new_environment.get();
for (; *old_it; ++old_it, ++new_it)
{
size_t const required_count = traits::tcslen(*old_it) + 1;
*new_it = _calloc_crt_t(Character, required_count).detach();
if (!*new_it)
{
abort();
}
_ERRCHECK(traits::tcscpy_s(*new_it, required_count, *old_it));
}
return new_environment.detach();
}
// If the current environment is the initial environment, this function clones
// the current environment so that it is not the initial environment. This
// should be called any time that we are about to modify the current environment
// but we do not know whether the current environment is the initial environment.
template <typename Character>
static void __cdecl ensure_current_environment_is_not_initial_environment_nolock() throw()
{
if (get_environment(Character()) == get_initial_environment(Character()))
{
get_environment(Character()) = copy_environment(get_environment(Character()));
}
}
// Finds an environment variable in the specified environment. If a variable
// with the given name is found, its index in the environment is returned. If
// no such environment is found, the total number of environment variables is
// returned, multiplied by -1. Note that a return value of 0 may indicate
// either that the variable was found at index 0 or there are zero variables
// in the environment. Be sure to check for this case.
template <typename Character>
static ptrdiff_t __cdecl find_in_environment_nolock(
Character const* const name,
size_t const length
) throw()
{
typedef __crt_char_traits<Character> traits;
Character** const environment = get_environment(Character());
Character** it = nullptr;
for (it = environment; *it; ++it)
{
// See if the first 'length' characters match:
if (traits::tcsnicoll(name, *it, length) != 0)
{
continue;
}
// Ensure that the next character of the environment is an '=' or '\0':
if ((*it)[length] != '=' && (*it)[length] != '\0')
{
continue;
}
// Otherwise, this entry matched; return its index in the environment:
return static_cast<ptrdiff_t>(it - environment);
}
// No entry matched; return the total number of strings, multiplied by -1:
return -static_cast<ptrdiff_t>(it - environment);
}
/***
*int __dcrt_set_variable_in_narrow_environment(option) - add/replace/remove variable in environment
*
*Purpose:
* option should be of the form "option=value". If a string with the
* given option part already exists, it is replaced with the given
* string; otherwise the given string is added to the environment.
* If the string is of the form "option=", then the string is
* removed from the environment, if it exists. If the string has
* no equals sign, error is returned.
*
*Entry:
* TCHAR **poption - pointer to option string to set in the environment list.
* should be of the form "option=value".
* This function takes ownership of this pointer in the success case.
* int primary - Only the primary call to _crt[w]setenv needs to
* create new copies or set the OS environment.
* 1 indicates that this is the primary call.
*
*Exit:
* returns 0 if OK, -1 if fails.
* If *poption is non-null on exit, we did not free it, and the caller should
* If *poption is null on exit, we did free it, and the caller should not.
*
*Exceptions:
*
*Warnings:
* This code will not work if variables are removed from the environment
* by deleting them from environ[]. Use _putenv("option=") to remove a
* variable.
*
* The option argument will be taken ownership of by this code and may be freed!
*
*******************************************************************************/
template <typename Character>
static int __cdecl common_set_variable_in_environment_nolock(
Character* const option,
int const is_top_level_call
) throw()
{
typedef __crt_char_traits<Character> traits;
// Check that the option string is valid first. Find the '=' and verify
// that '=' is not the first character in the string:
_VALIDATE_RETURN_NOEXC(option != nullptr, EINVAL, -1);
__crt_unique_heap_ptr<Character> owned_option(option);
Character* const equal_sign = traits::tcschr(option, '=');
_VALIDATE_RETURN_NOEXC(equal_sign != nullptr && equal_sign != option, EINVAL, -1);
// Internal consistency check: The environment string should never use
// buffers larger than _MAX_ENV. See also the SetEnvironmentVariable SDK
// function.
_ASSERTE(equal_sign - option < _MAX_ENV);
_ASSERTE(traits::tcsnlen(equal_sign + 1, _MAX_ENV) < _MAX_ENV);
// If the character following '=' is the terminator, we are removing the
// environment variable. Otherwise, we are adding or updating the variable:
bool const is_removal = *(equal_sign + 1) == '\0';
// At program startup, the initial environment (__dcrt_initial_narrow_environment), which is passed
// to main(), is backed by the same environment arrays as the global
// environment used by getenv, setenv, et al. We cannot modify thie initial
// environment, so we make a copy of it the first time we need to make any
// modifications to the global environment:
ensure_current_environment_is_not_initial_environment_nolock<Character>();
// If the required environment does not exist, see if the other environment
// exists; if it does, convert it to create the required environment. These
// functions will reenter this function once for each environment variable;
// we use the top-level call flag to stop recursion.
if (!get_environment(Character()))
{
if (is_top_level_call && get_other_environment(Character()))
{
_VALIDATE_RETURN_NOEXC(traits::get_or_create_environment_nolock() != nullptr, EINVAL, -1);
// The call to get_or_create_environment() may have initialized the
// current environment to the same environment that is the initial
// environment. Re-check and make a new copy of the environment to
// modify if necessary.
ensure_current_environment_is_not_initial_environment_nolock<Character>();
}
else
{
// If the environment doesn't exist and the requested operation is a
// removal, there is nothing to do (there is nothing to remove):
if (is_removal)
{
return 0;
}
// Create a new environment for each environment that does not exist.
// Just start each off as an empty environment:
if (!_environ_table.value())
{
_environ_table.value() = _calloc_crt_t(char*, 1).detach();
}
if (!_environ_table.value())
{
return -1;
}
if (!_wenviron_table.value())
{
_wenviron_table.value() = _calloc_crt_t(wchar_t*, 1).detach();
}
if (!_wenviron_table.value())
{
return -1;
}
}
}
// At this point, either [1] only one environment exists, or [2] both of the
// environments exist and are in-sync. The only way they can get out of sync
// is if there are conversion problems. For example, if the user sets two
// Unicode environment variables, FOO1 and FOO2, and the conversion of these
// to multibyte yields FOO? and FOO?, then these environment blocks will
// differ.
Character** const environment = get_environment(Character());
if (!environment)
{
_ASSERTE(("CRT logic error in setenv", 0));
return -1;
}
// Try to find the option in the environment...
ptrdiff_t const option_index = find_in_environment_nolock(option, equal_sign - option);
// ... if the string is already in the environment, we free up the original
// string, then install the new string or shrink the environment:
if (option_index >= 0 && environment[0])
{
_free_crt(environment[option_index]);
// If this is a removal, shrink the environment:
if (is_removal)
{
// Shift all of the entries down by one element:
size_t i = static_cast<size_t>(option_index);
for (; environment[i]; ++i)
{
environment[i] = environment[i + 1];
}
// Shrink the environment memory block. At this point, i is the
// number of elements remaining in the environment. This realloc
// should never fail, since we are shrinking the block, but it is
// best to be careful. If it does fail, it doesn't matter.
Character** new_environment = _recalloc_crt_t(Character*, environment, i).detach();
if (new_environment)
{
get_environment(Character()) = new_environment;
}
}
// If this is a replacement, replace the variable:
else
{
environment[option_index] = owned_option.detach();
}
}
// Otherwise, the string is not in the environment:
else
{
// If this is a removal, it is a no-op: the variable does not exist.
if (is_removal)
{
return 0;
}
// Otherwise, we need to append the string to the environment table, and
// we must grow the table to do this:
else
{
size_t const environment_count = static_cast<size_t>(-option_index);
if (environment_count + 2 < environment_count)
{
return -1;
}
if (environment_count + 2 >= SIZE_MAX / sizeof(Character*))
{
return -1;
}
Character** const new_environment = _recalloc_crt_t(Character*, environment, environment_count + 2).detach();
if (!new_environment)
{
return -1;
}
new_environment[environment_count] = owned_option.detach();
new_environment[environment_count + 1] = nullptr;
get_environment(Character()) = new_environment;
}
}
// Update the operating system environment. Do not give an error if this
// fails since the failure will not affect the user code unless it is making
// direct calls to the operating system. We only need to do this for one of
// the environments; the operating system synchronizes with the other
// environment automatically.
if (is_top_level_call)
{
size_t const count = traits::tcslen(option) + 2;
__crt_unique_heap_ptr<Character> const buffer(_calloc_crt_t(Character, count));
if (!buffer)
{
return 0;
}
Character* const name = buffer.get();
_ERRCHECK(traits::tcscpy_s(name, count, option));
Character* const value = name + (equal_sign - option) + 1;
*(value - 1) = '\0'; // Overwrite the '=' with a null terminator
if (traits::set_environment_variable(name, is_removal ? nullptr : value) == 0)
{
errno = EILSEQ;
return -1;
}
}
return 0;
}
extern "C" int __cdecl __dcrt_set_variable_in_narrow_environment_nolock(
char* const option,
int const is_top_level_call
)
{
return common_set_variable_in_environment_nolock(option, is_top_level_call);
}
extern "C" int __cdecl __dcrt_set_variable_in_wide_environment_nolock(
wchar_t* const option,
int const is_top_level_call
)
{
return common_set_variable_in_environment_nolock(option, is_top_level_call);
}