diff options
Diffstat (limited to 'src/pal/src/misc/environ.cpp')
-rw-r--r-- | src/pal/src/misc/environ.cpp | 1096 |
1 files changed, 1096 insertions, 0 deletions
diff --git a/src/pal/src/misc/environ.cpp b/src/pal/src/misc/environ.cpp new file mode 100644 index 0000000000..fed7b69f38 --- /dev/null +++ b/src/pal/src/misc/environ.cpp @@ -0,0 +1,1096 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*++ + + + +Module Name: + + environ.cpp + +Abstract: + + Implementation of functions manipulating environment variables. + +Revision History: + + + +--*/ + +#include "pal/palinternal.h" +#include "pal/critsect.h" +#include "pal/dbgmsg.h" +#include "pal/environ.h" +#include "pal/malloc.hpp" + +#if HAVE_CRT_EXTERNS_H +#include <crt_externs.h> +#endif + +#include <stdlib.h> + +using namespace CorUnix; + +SET_DEFAULT_DEBUG_CHANNEL(MISC); + +char **palEnvironment = nullptr; +int palEnvironmentCount = 0; +int palEnvironmentCapacity = 0; + +CRITICAL_SECTION gcsEnvironment; + +/*++ +Function: + GetEnvironmentVariableA + +The GetEnvironmentVariable function retrieves the value of the +specified variable from the environment block of the calling +process. The value is in the form of a null-terminated string of +characters. + +Parameters + +lpName + [in] Pointer to a null-terminated string that specifies the environment variable. +lpBuffer + [out] Pointer to a buffer to receive the value of the specified environment variable. +nSize + [in] Specifies the size, in TCHARs, of the buffer pointed to by the lpBuffer parameter. + +Return Values + +If the function succeeds, the return value is the number of TCHARs +stored into the buffer pointed to by lpBuffer, not including the +terminating null character. + +If the specified environment variable name was not found in the +environment block for the current process, the return value is zero. + +If the buffer pointed to by lpBuffer is not large enough, the return +value is the buffer size, in TCHARs, required to hold the value string +and its terminating null character. + +--*/ +DWORD +PALAPI +GetEnvironmentVariableA( + IN LPCSTR lpName, + OUT LPSTR lpBuffer, + IN DWORD nSize) +{ + char *value; + DWORD dwRet = 0; + + PERF_ENTRY(GetEnvironmentVariableA); + ENTRY("GetEnvironmentVariableA(lpName=%p (%s), lpBuffer=%p, nSize=%u)\n", + lpName ? lpName : "NULL", + lpName ? lpName : "NULL", lpBuffer, nSize); + + CPalThread * pthrCurrent = InternalGetCurrentThread(); + + if (lpName == nullptr) + { + ERROR("lpName is null\n"); + SetLastError(ERROR_INVALID_PARAMETER); + goto done; + } + + if (lpName[0] == 0) + { + TRACE("lpName is an empty string\n", lpName); + SetLastError(ERROR_ENVVAR_NOT_FOUND); + goto done; + } + + if (strchr(lpName, '=') != nullptr) + { + // GetEnvironmentVariable doesn't permit '=' in variable names. + value = nullptr; + } + else + { + // Enter the environment critical section so that we can safely get + // the environment variable value without EnvironGetenv making an + // intermediate copy. We will just copy the string to the output + // buffer anyway, so just stay in the critical section until then. + InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); + + value = EnvironGetenv(lpName, /* copyValue */ FALSE); + + if (value != nullptr) + { + DWORD valueLength = strlen(value); + if (valueLength < nSize) + { + strcpy_s(lpBuffer, nSize, value); + dwRet = valueLength; + } + else + { + dwRet = valueLength + 1; + } + + SetLastError(ERROR_SUCCESS); + } + + InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); + } + + if (value == nullptr) + { + TRACE("%s is not found\n", lpName); + SetLastError(ERROR_ENVVAR_NOT_FOUND); + } + +done: + + LOGEXIT("GetEnvironmentVariableA returns DWORD 0x%x\n", dwRet); + PERF_EXIT(GetEnvironmentVariableA); + return dwRet; +} + +/*++ +Function: + GetEnvironmentVariableW + +See MSDN doc. +--*/ +DWORD +PALAPI +GetEnvironmentVariableW( + IN LPCWSTR lpName, + OUT LPWSTR lpBuffer, + IN DWORD nSize) +{ + CHAR *inBuff = nullptr; + CHAR *outBuff = nullptr; + INT inBuffSize; + DWORD size = 0; + + PERF_ENTRY(GetEnvironmentVariableW); + ENTRY("GetEnvironmentVariableW(lpName=%p (%S), lpBuffer=%p, nSize=%u)\n", + lpName ? lpName : W16_NULLSTRING, + lpName ? lpName : W16_NULLSTRING, lpBuffer, nSize); + + inBuffSize = WideCharToMultiByte(CP_ACP, 0, lpName, -1, + inBuff, 0, nullptr, nullptr); + if (0 == inBuffSize) + { + ERROR("lpName has to be a valid parameter\n"); + SetLastError(ERROR_INVALID_PARAMETER); + goto done; + } + + inBuff = (CHAR *)PAL_malloc(inBuffSize); + if (inBuff == nullptr) + { + ERROR("malloc failed\n"); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto done; + } + + if (nSize) + { + outBuff = (CHAR *)PAL_malloc(nSize*2); + if (outBuff == nullptr) + { + ERROR("malloc failed\n"); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto done; + } + } + + if (0 == WideCharToMultiByte(CP_ACP, 0, lpName, -1, inBuff, + inBuffSize, nullptr, nullptr)) + { + ASSERT("WideCharToMultiByte failed!\n"); + SetLastError(ERROR_INTERNAL_ERROR); + goto done; + } + + size = GetEnvironmentVariableA(inBuff, outBuff, nSize); + if (size > nSize) + { + TRACE("Insufficient buffer\n"); + } + else if (size == 0) + { + // handle error in GetEnvironmentVariableA + } + else + { + size = MultiByteToWideChar(CP_ACP, 0, outBuff, -1, lpBuffer, nSize); + if (0 != size) + { + // -1 for the null. + size--; + } + else + { + ASSERT("MultiByteToWideChar failed!\n"); + SetLastError(ERROR_INTERNAL_ERROR); + size = 0; + *lpBuffer = '\0'; + } + } + +done: + PAL_free(outBuff); + PAL_free(inBuff); + + LOGEXIT("GetEnvironmentVariableW returns DWORD 0x%x\n", size); + PERF_EXIT(GetEnvironmentVariableW); + + return size; +} + +/*++ +Function: + SetEnvironmentVariableW + +The SetEnvironmentVariable function sets the value of an environment +variable for the current process. + +Parameters + +lpName + [in] Pointer to a null-terminated string that specifies the + environment variable whose value is being set. The operating + system creates the environment variable if it does not exist + and lpValue is not null. +lpValue + [in] Pointer to a null-terminated string containing the new + value of the specified environment variable. If this parameter + is null, the variable is deleted from the current process's + environment. + +Return Values + +If the function succeeds, the return value is nonzero. + +If the function fails, the return value is zero. To get extended error +information, call GetLastError. + +Remarks + +This function has no effect on the system environment variables or the +environment variables of other processes. + +--*/ +BOOL +PALAPI +SetEnvironmentVariableW( + IN LPCWSTR lpName, + IN LPCWSTR lpValue) +{ + PCHAR name = nullptr; + PCHAR value = nullptr; + INT nameSize = 0; + INT valueSize = 0; + BOOL bRet = FALSE; + + PERF_ENTRY(SetEnvironmentVariableW); + ENTRY("SetEnvironmentVariableW(lpName=%p (%S), lpValue=%p (%S))\n", + lpName?lpName:W16_NULLSTRING, + lpName?lpName:W16_NULLSTRING, lpValue?lpValue:W16_NULLSTRING, lpValue?lpValue:W16_NULLSTRING); + + if ((nameSize = WideCharToMultiByte(CP_ACP, 0, lpName, -1, name, 0, + nullptr, nullptr)) == 0) + { + ERROR("WideCharToMultiByte failed\n"); + SetLastError(ERROR_INVALID_PARAMETER); + goto done; + } + + name = (PCHAR)PAL_malloc(sizeof(CHAR)* nameSize); + if (name == nullptr) + { + ERROR("malloc failed\n"); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto done; + } + + if (0 == WideCharToMultiByte(CP_ACP, 0, lpName, -1, + name, nameSize, nullptr, nullptr)) + { + ASSERT("WideCharToMultiByte returned 0\n"); + SetLastError(ERROR_INTERNAL_ERROR); + goto done; + } + + if (lpValue != nullptr) + { + if ((valueSize = WideCharToMultiByte(CP_ACP, 0, lpValue, -1, value, + 0, nullptr, nullptr)) == 0) + { + ERROR("WideCharToMultiByte failed\n"); + SetLastError(ERROR_INVALID_PARAMETER); + goto done; + } + + value = (PCHAR)PAL_malloc(sizeof(CHAR)*valueSize); + + if (value == nullptr) + { + ERROR("malloc failed\n"); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto done; + } + + if (0 == WideCharToMultiByte(CP_ACP, 0, lpValue, -1, + value, valueSize, nullptr, nullptr)) + { + ASSERT("WideCharToMultiByte failed\n"); + SetLastError( ERROR_INTERNAL_ERROR ); + goto done; + } + } + + bRet = SetEnvironmentVariableA(name, value); +done: + PAL_free(value); + PAL_free(name); + + LOGEXIT("SetEnvironmentVariableW returning BOOL %d\n", bRet); + PERF_EXIT(SetEnvironmentVariableW); + return bRet; +} + +/*++ +Function: + GetEnvironmentStringsW + +The GetEnvironmentStrings function retrieves the environment block for +the current process. + +Parameters + +This function has no parameters. + +Return Values + +The return value is a pointer to an environment block for the current process. + +Remarks + +The GetEnvironmentStrings function returns a pointer to the +environment block of the calling process. This should be treated as a +read-only block; do not modify it directly. Instead, use the +GetEnvironmentVariable and SetEnvironmentVariable functions to +retrieve or change the environment variables within this block. When +the block is no longer needed, it should be freed by calling +FreeEnvironmentStrings. + +--*/ +LPWSTR +PALAPI +GetEnvironmentStringsW( + VOID) +{ + WCHAR *wenviron = nullptr, *tempEnviron; + int i, len, envNum; + + PERF_ENTRY(GetEnvironmentStringsW); + ENTRY("GetEnvironmentStringsW()\n"); + + CPalThread * pthrCurrent = InternalGetCurrentThread(); + InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); + + envNum = 0; + len = 0; + + /* get total length of the bytes that we need to allocate */ + for (i = 0; palEnvironment[i] != 0; i++) + { + len = MultiByteToWideChar(CP_ACP, 0, palEnvironment[i], -1, wenviron, 0); + envNum += len; + } + + wenviron = (WCHAR *)PAL_malloc(sizeof(WCHAR)* (envNum + 1)); + if (wenviron == nullptr) + { + ERROR("malloc failed\n"); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto EXIT; + } + + len = 0; + tempEnviron = wenviron; + for (i = 0; palEnvironment[i] != 0; i++) + { + len = MultiByteToWideChar(CP_ACP, 0, palEnvironment[i], -1, tempEnviron, envNum); + tempEnviron += len; + envNum -= len; + } + + *tempEnviron = 0; /* Put an extra null at the end */ + + EXIT: + InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); + + LOGEXIT("GetEnvironmentStringsW returning %p\n", wenviron); + PERF_EXIT(GetEnvironmentStringsW); + return wenviron; +} + +/*++ +Function: + GetEnvironmentStringsA + +See GetEnvironmentStringsW. + +--*/ +LPSTR +PALAPI +GetEnvironmentStringsA( + VOID) +{ + char *environ = nullptr, *tempEnviron; + int i, len, envNum; + + PERF_ENTRY(GetEnvironmentStringsA); + ENTRY("GetEnvironmentStringsA()\n"); + + CPalThread * pthrCurrent = InternalGetCurrentThread(); + InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); + + envNum = 0; + len = 0; + + /* get total length of the bytes that we need to allocate */ + for (i = 0; palEnvironment[i] != 0; i++) + { + len = strlen(palEnvironment[i]) + 1; + envNum += len; + } + + environ = (char *)PAL_malloc(envNum + 1); + if (environ == nullptr) + { + ERROR("malloc failed\n"); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto EXIT; + } + + len = 0; + tempEnviron = environ; + for (i = 0; palEnvironment[i] != 0; i++) + { + len = strlen(palEnvironment[i]) + 1; + memcpy(tempEnviron, palEnvironment[i], len); + tempEnviron += len; + envNum -= len; + } + + *tempEnviron = 0; /* Put an extra null at the end */ + + EXIT: + InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); + + LOGEXIT("GetEnvironmentStringsA returning %p\n", environ); + PERF_EXIT(GetEnvironmentStringsA); + return environ; +} + +/*++ +Function: + FreeEnvironmentStringsW + +The FreeEnvironmentStrings function frees a block of environment strings. + +Parameters + +lpszEnvironmentBlock [in] Pointer to a block of environment strings. The pointer to + the block must be obtained by calling the + GetEnvironmentStrings function. + +Return Values + +If the function succeeds, the return value is nonzero. If the +function fails, the return value is zero. To get extended error +information, call GetLastError. + +Remarks + +When GetEnvironmentStrings is called, it allocates memory for a block +of environment strings. When the block is no longer needed, it should +be freed by calling FreeEnvironmentStrings. + +--*/ +BOOL +PALAPI +FreeEnvironmentStringsW( + IN LPWSTR lpValue) +{ + PERF_ENTRY(FreeEnvironmentStringsW); + ENTRY("FreeEnvironmentStringsW(lpValue=%p (%S))\n", lpValue ? lpValue : W16_NULLSTRING, lpValue ? lpValue : W16_NULLSTRING); + + if (lpValue != nullptr) + { + PAL_free(lpValue); + } + + LOGEXIT("FreeEnvironmentStringW returning BOOL TRUE\n"); + PERF_EXIT(FreeEnvironmentStringsW); + return TRUE; +} + +/*++ +Function: + FreeEnvironmentStringsA + +See FreeEnvironmentStringsW. + +--*/ +BOOL +PALAPI +FreeEnvironmentStringsA( + IN LPSTR lpValue) +{ + PERF_ENTRY(FreeEnvironmentStringsA); + ENTRY("FreeEnvironmentStringsA(lpValue=%p (%s))\n", lpValue ? lpValue : "NULL", lpValue ? lpValue : "NULL"); + + if (lpValue != nullptr) + { + PAL_free(lpValue); + } + + LOGEXIT("FreeEnvironmentStringA returning BOOL TRUE\n"); + PERF_EXIT(FreeEnvironmentStringsA); + return TRUE; +} + +/*++ +Function: + SetEnvironmentVariableA + +The SetEnvironmentVariable function sets the value of an environment +variable for the current process. + +Parameters + +lpName + [in] Pointer to a null-terminated string that specifies the + environment variable whose value is being set. The operating + system creates the environment variable if it does not exist + and lpValue is not null. +lpValue + [in] Pointer to a null-terminated string containing the new + value of the specified environment variable. If this parameter + is null, the variable is deleted from the current process's + environment. + +Return Values + +If the function succeeds, the return value is nonzero. + +If the function fails, the return value is zero. To get extended error +information, call GetLastError. + +Remarks + +This function has no effect on the system environment variables or the +environment variables of other processes. + +--*/ +BOOL +PALAPI +SetEnvironmentVariableA( + IN LPCSTR lpName, + IN LPCSTR lpValue) +{ + + BOOL bRet = FALSE; + int nResult =0; + PERF_ENTRY(SetEnvironmentVariableA); + ENTRY("SetEnvironmentVariableA(lpName=%p (%s), lpValue=%p (%s))\n", + lpName ? lpName : "NULL", lpName ? lpName : "NULL", + lpValue ? lpValue : "NULL", lpValue ? lpValue : "NULL"); + + // exit if the input variable name is null + if ((lpName == nullptr) || (lpName[0] == 0)) + { + ERROR("lpName is null\n"); + goto done; + } + + /* check if the input value is null and if so + * check if the input name is valid and delete + * the variable name from process environment */ + if (lpValue == nullptr) + { + // We tell EnvironGetenv not to bother with making a copy of the + // value since we're not going to use it for anything interesting + // apart from checking whether it's null. + if ((lpValue = EnvironGetenv(lpName, /* copyValue */ FALSE)) == nullptr) + { + ERROR("Couldn't find environment variable (%s)\n", lpName); + SetLastError(ERROR_ENVVAR_NOT_FOUND); + goto done; + } + + EnvironUnsetenv(lpName); + } + else + { + // All the conditions are met. Set the variable. + int iLen = strlen(lpName) + strlen(lpValue) + 2; + LPSTR string = (LPSTR) PAL_malloc(iLen); + if (string == nullptr) + { + bRet = FALSE; + ERROR("Unable to allocate memory\n"); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto done; + } + + sprintf_s(string, iLen, "%s=%s", lpName, lpValue); + nResult = EnvironPutenv(string, FALSE) ? 0 : -1; + + PAL_free(string); + string = nullptr; + + // If EnvironPutenv returns FALSE, it almost certainly failed to allocate memory. + if (nResult == -1) + { + bRet = FALSE; + ERROR("Unable to allocate memory\n"); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto done; + } + } + + bRet = TRUE; + +done: + LOGEXIT("SetEnvironmentVariableA returning BOOL %d\n", bRet); + PERF_EXIT(SetEnvironmentVariableA); + return bRet; +} + +/*++ +Function: + ResizeEnvironment + +Resizes the PAL environment buffer. + +Parameters + + newSize + [in] New size of palEnvironment + +Return Values + + TRUE on success, FALSE otherwise + +--*/ +BOOL ResizeEnvironment(int newSize) +{ + CPalThread * pthrCurrent = InternalGetCurrentThread(); + InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); + + BOOL ret = FALSE; + if (newSize >= palEnvironmentCount) + { + // If palEnvironment is null, realloc acts like malloc. + char **newEnvironment = (char**)realloc(palEnvironment, newSize * sizeof(char *)); + if (newEnvironment != nullptr) + { + // realloc succeeded, so set palEnvironment to what it returned. + palEnvironment = newEnvironment; + palEnvironmentCapacity = newSize; + ret = TRUE; + } + } + else + { + ASSERT("ResizeEnvironment: newSize < current palEnvironmentCount!\n"); + } + + InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); + return ret; +} + +/*++ +Function: + EnvironUnsetenv + +Remove the environment variable with the given name from the PAL version +of the environment if it exists. + +Parameters + + name + [in] Name of variable to unset. + +--*/ +void EnvironUnsetenv(const char *name) +{ + int nameLength = strlen(name); + + CPalThread * pthrCurrent = InternalGetCurrentThread(); + InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); + + for (int i = 0; palEnvironment[i] != nullptr; ++i) + { + const char *equalsSignPosition = strchr(palEnvironment[i], '='); + if (equalsSignPosition == nullptr) + { + equalsSignPosition = palEnvironment[i] + strlen(palEnvironment[i]); + } + + // Check whether the name of this variable has the same length as the one + // we're looking for before proceeding to compare them. + if (equalsSignPosition - palEnvironment[i] == nameLength) + { + if (memcmp(name, palEnvironment[i], nameLength) == 0) + { + // Free the string we're removing. + free(palEnvironment[i]); + + // Move the last environment variable pointer here. + palEnvironment[i] = palEnvironment[palEnvironmentCount - 1]; + palEnvironment[palEnvironmentCount - 1] = nullptr; + + palEnvironmentCount--; + } + } + } + + InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); +} + +/*++ +Function: + EnvironPutenv + +Add the environment variable string provided to the PAL version +of the environment. + +Parameters + + entry + [in] The variable string to add. Should be in the format + "name=value", where value might be empty (see below). + deleteIfEmpty + [in] If this is TRUE, "name=" will unset the 'name' variable. + +Return Values + + TRUE on success, FALSE otherwise + +--*/ +BOOL EnvironPutenv(const char* entry, BOOL deleteIfEmpty) +{ + BOOL result = FALSE; + + bool fOwningCS = false; + + CPalThread * pthrCurrent = InternalGetCurrentThread(); + + const char *equalsSignPosition = strchr(entry, '='); + if (equalsSignPosition == entry || equalsSignPosition == nullptr) + { + // "=foo" and "foo" have no meaning + return FALSE; + } + + char* copy = strdup(entry); + if (copy == nullptr) + { + return FALSE; + } + + int nameLength = equalsSignPosition - entry; + + if (equalsSignPosition[1] == '\0' && deleteIfEmpty) + { + // "foo=" removes foo from the environment in _putenv() on Windows. + // The same string can result from a call to SetEnvironmentVariable() + // with the empty string as the value, but in that case we want to + // set the variable's value to "". deleteIfEmpty will be FALSE in + // that case. + + // Change '=' to '\0' + copy[nameLength] = '\0'; + + EnvironUnsetenv(copy); + free(copy); + + result = TRUE; + } + else + { + // See if we are replacing an item or adding one. + + InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); + fOwningCS = true; + + int i; + for (i = 0; palEnvironment[i] != nullptr; i++) + { + const char *existingEquals = strchr(palEnvironment[i], '='); + if (existingEquals == nullptr) + { + // The PAL screens out malformed strings, but the strings which + // came from the system during initialization might not have the + // equals sign. We treat the entire string as a name in that case. + existingEquals = palEnvironment[i] + strlen(palEnvironment[i]); + } + + if (existingEquals - palEnvironment[i] == nameLength) + { + if (memcmp(entry, palEnvironment[i], nameLength) == 0) + { + free(palEnvironment[i]); + palEnvironment[i] = copy; + + result = TRUE; + break; + } + } + } + + if (palEnvironment[i] == nullptr) + { + _ASSERTE(i < palEnvironmentCapacity); + if (i == (palEnvironmentCapacity - 1)) + { + // We found the first null, but it's the last element in our environment + // block. We need more space in our environment, so let's double its size. + int resizeRet = ResizeEnvironment(palEnvironmentCapacity * 2); + if (resizeRet != TRUE) + { + free(copy); + goto done; + } + } + + _ASSERTE(copy != nullptr); + palEnvironment[i] = copy; + palEnvironment[i + 1] = nullptr; + palEnvironmentCount++; + + result = TRUE; + } + } +done: + + if (fOwningCS) + { + InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); + } + + return result; +} + +/*++ +Function: + EnvironGetenv + +Get the value of environment variable with the given name. + +Parameters + + name + [in] The name of the environment variable to get. + copyValue + [in] If this is TRUE, the function will make a copy of the + value and return a pointer to that. Otherwise, it will + return a pointer to the value in the PAL environment + directly. Calling this function with copyValue set to + FALSE is therefore unsafe without taking special pre- + cautions since the pointer may point to garbage later. + +Return Value + + A pointer to the value of the environment variable if it exists, + or nullptr otherwise. + +--*/ +char* EnvironGetenv(const char* name, BOOL copyValue) +{ + char *retValue = nullptr; + + CPalThread * pthrCurrent = InternalGetCurrentThread(); + InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); + + int nameLength = strlen(name); + for (int i = 0; palEnvironment[i] != nullptr; ++i) + { + if (strlen(palEnvironment[i]) < nameLength) + { + continue; + } + + if (memcmp(palEnvironment[i], name, nameLength) == 0) + { + char *equalsSignPosition = palEnvironment[i] + nameLength; + + // If this is one of the variables which has no equals sign, we + // treat the whole thing as name, so the value is an empty string. + if (*equalsSignPosition == '\0') + { + retValue = (char *)""; + break; + } + else if (*equalsSignPosition == '=') + { + retValue = equalsSignPosition + 1; + break; + } + } + } + + if ((retValue != nullptr) && copyValue) + { + retValue = strdup(retValue); + } + + InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); + return retValue; +} + +/*++ +Function: + EnvironGetSystemEnvironment + +Get a pointer to the array of pointers representing the process's +environment. + +See 'man environ' for details. + +Return Value + + A pointer to the environment. + +--*/ +char** EnvironGetSystemEnvironment() +{ + char** sysEnviron; + +#if HAVE__NSGETENVIRON + sysEnviron = *(_NSGetEnviron()); +#else // HAVE__NSGETENVIRON + extern char **environ; + sysEnviron = environ; +#endif // HAVE__NSGETENVIRON + + return sysEnviron; +} + +/*++ +Function: + EnvironInitialize + +Initialization function called from PAL_Initialize. + +Note: This is called before debug channels are initialized, so it + cannot use debug tracing calls. +--*/ +BOOL +EnvironInitialize(void) +{ + BOOL ret = FALSE; + + InternalInitializeCriticalSection(&gcsEnvironment); + + CPalThread * pthrCurrent = InternalGetCurrentThread(); + InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); + + char** sourceEnviron = EnvironGetSystemEnvironment(); + + int variableCount = 0; + while (sourceEnviron[variableCount] != nullptr) + variableCount++; + + palEnvironmentCount = 0; + + // We need to decide how much space to allocate. Since we need enough + // space for all of the 'n' current environment variables, but we don't + // know how many more there will be, we will initially make room for + // '2n' variables. If even more are added, we will resize again. + // If there are no variables, we will still make room for 1 entry to + // store a nullptr there. + int initialSize = (variableCount == 0) ? 1 : variableCount * 2; + + ret = ResizeEnvironment(initialSize); + if (ret == TRUE) + { + _ASSERTE(palEnvironment != nullptr); + for (int i = 0; i < variableCount; ++i) + { + palEnvironment[i] = strdup(sourceEnviron[i]); + palEnvironmentCount++; + } + + // Set the entry after the last variable to null to indicate the end. + palEnvironment[variableCount] = nullptr; + } + + InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); + return ret; +} + +/*++ + +Function : _putenv. + +See MSDN for more details. + +Note: The BSD implementation can cause + memory leaks. See man pages for more details. +--*/ +int +__cdecl +_putenv( const char * envstring ) +{ + int ret = -1; + + PERF_ENTRY(_putenv); + ENTRY( "_putenv( %p (%s) )\n", envstring ? envstring : "NULL", envstring ? envstring : "NULL") ; + + if (envstring != nullptr) + { + ret = EnvironPutenv(envstring, TRUE) ? 0 : -1; + } + else + { + ERROR( "_putenv() called with NULL envstring!\n"); + } + + LOGEXIT( "_putenv returning %d\n", ret); + PERF_EXIT(_putenv); + return ret; +} + +/*++ + +Function : PAL_getenv + +See MSDN for more details. +--*/ +char * __cdecl PAL_getenv(const char *varname) +{ + char *retval; + + PERF_ENTRY(getenv); + ENTRY("getenv (%p (%s))\n", varname ? varname : "NULL", varname ? varname : "NULL"); + + if (strcmp(varname, "") == 0) + { + ERROR("getenv called with a empty variable name\n"); + LOGEXIT("getenv returning NULL\n"); + PERF_EXIT(getenv); + return(NULL); + } + + retval = EnvironGetenv(varname); + + LOGEXIT("getenv returning %p\n", retval); + PERF_EXIT(getenv); + return(retval); +} |