From 5a079a2d114f96d4847d1ee305d5b7c16eeec50e Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Sat, 27 Dec 2025 12:03:39 -0800 Subject: Initial commit --- contrib/SDL-3.2.8/src/stdlib/SDL_getenv.c | 601 ++++++++++++++++++++++++++++++ 1 file changed, 601 insertions(+) create mode 100644 contrib/SDL-3.2.8/src/stdlib/SDL_getenv.c (limited to 'contrib/SDL-3.2.8/src/stdlib/SDL_getenv.c') diff --git a/contrib/SDL-3.2.8/src/stdlib/SDL_getenv.c b/contrib/SDL-3.2.8/src/stdlib/SDL_getenv.c new file mode 100644 index 0000000..b4a1922 --- /dev/null +++ b/contrib/SDL-3.2.8/src/stdlib/SDL_getenv.c @@ -0,0 +1,601 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#include "SDL_getenv_c.h" + +#if defined(SDL_PLATFORM_WINDOWS) +#include "../core/windows/SDL_windows.h" +#endif + +#ifdef SDL_PLATFORM_ANDROID +#include "../core/android/SDL_android.h" +#endif + +#if defined(SDL_PLATFORM_WINDOWS) +#define HAVE_WIN32_ENVIRONMENT +#elif defined(HAVE_GETENV) && \ + (defined(HAVE_SETENV) || defined(HAVE_PUTENV)) && \ + (defined(HAVE_UNSETENV) || defined(HAVE_PUTENV)) +#define HAVE_LIBC_ENVIRONMENT +#if defined(SDL_PLATFORM_MACOS) +#include +#define environ (*_NSGetEnviron()) +#elif defined(SDL_PLATFORM_FREEBSD) +#include +#define environ ((char **)dlsym(RTLD_DEFAULT, "environ")) +#else +extern char **environ; +#endif +#else +#define HAVE_LOCAL_ENVIRONMENT +static char **environ; +#endif + + +struct SDL_Environment +{ + SDL_Mutex *lock; // !!! FIXME: reuse SDL_HashTable's lock. + SDL_HashTable *strings; +}; +static SDL_Environment *SDL_environment; + +SDL_Environment *SDL_GetEnvironment(void) +{ + if (!SDL_environment) { + SDL_environment = SDL_CreateEnvironment(true); + } + return SDL_environment; +} + +bool SDL_InitEnvironment(void) +{ + return (SDL_GetEnvironment() != NULL); +} + +void SDL_QuitEnvironment(void) +{ + SDL_Environment *env = SDL_environment; + + if (env) { + SDL_environment = NULL; + SDL_DestroyEnvironment(env); + } +} + +SDL_Environment *SDL_CreateEnvironment(bool populated) +{ + SDL_Environment *env = SDL_calloc(1, sizeof(*env)); + if (!env) { + return NULL; + } + + env->strings = SDL_CreateHashTable(0, false, SDL_HashString, SDL_KeyMatchString, SDL_DestroyHashKey, NULL); + if (!env->strings) { + SDL_free(env); + return NULL; + } + + // Don't fail if we can't create a mutex (e.g. on a single-thread environment) // !!! FIXME: single-threaded environments should still return a non-NULL, do-nothing object here. Check for failure! + env->lock = SDL_CreateMutex(); + + if (populated) { +#ifdef SDL_PLATFORM_WINDOWS + LPWCH strings = GetEnvironmentStringsW(); + if (strings) { + for (LPWCH string = strings; *string; string += SDL_wcslen(string) + 1) { + char *variable = WIN_StringToUTF8W(string); + if (!variable) { + continue; + } + + char *value = SDL_strchr(variable, '='); + if (!value || value == variable) { + SDL_free(variable); + continue; + } + *value++ = '\0'; + + SDL_InsertIntoHashTable(env->strings, variable, value, true); + } + FreeEnvironmentStringsW(strings); + } +#else +#ifdef SDL_PLATFORM_ANDROID + // Make sure variables from the application manifest are available + Android_JNI_GetManifestEnvironmentVariables(); +#endif + char **strings = environ; + if (strings) { + for (int i = 0; strings[i]; ++i) { + char *variable = SDL_strdup(strings[i]); + if (!variable) { + continue; + } + + char *value = SDL_strchr(variable, '='); + if (!value || value == variable) { + SDL_free(variable); + continue; + } + *value++ = '\0'; + + SDL_InsertIntoHashTable(env->strings, variable, value, true); + } + } +#endif // SDL_PLATFORM_WINDOWS + } + + return env; +} + +const char *SDL_GetEnvironmentVariable(SDL_Environment *env, const char *name) +{ + const char *result = NULL; + + if (!env) { + return NULL; + } else if (!name || *name == '\0') { + return NULL; + } + + SDL_LockMutex(env->lock); + { + const char *value; + + if (SDL_FindInHashTable(env->strings, name, (const void **)&value)) { + result = SDL_GetPersistentString(value); + } + } + SDL_UnlockMutex(env->lock); + + return result; +} + +typedef struct CountEnvStringsData +{ + size_t count; + size_t length; +} CountEnvStringsData; + +static bool SDLCALL CountEnvStrings(void *userdata, const SDL_HashTable *table, const void *key, const void *value) +{ + CountEnvStringsData *data = (CountEnvStringsData *) userdata; + data->length += SDL_strlen((const char *) key) + 1 + SDL_strlen((const char *) value) + 1; + data->count++; + return true; // keep iterating. +} + +typedef struct CopyEnvStringsData +{ + char **result; + char *string; + size_t count; +} CopyEnvStringsData; + +static bool SDLCALL CopyEnvStrings(void *userdata, const SDL_HashTable *table, const void *vkey, const void *vvalue) +{ + CopyEnvStringsData *data = (CopyEnvStringsData *) userdata; + const char *key = (const char *) vkey; + const char *value = (const char *) vvalue; + size_t len; + + len = SDL_strlen(key); + data->result[data->count] = data->string; + SDL_memcpy(data->string, key, len); + data->string += len; + *(data->string++) = '='; + + len = SDL_strlen(value); + SDL_memcpy(data->string, value, len); + data->string += len; + *(data->string++) = '\0'; + data->count++; + + return true; // keep iterating. +} + +char **SDL_GetEnvironmentVariables(SDL_Environment *env) +{ + char **result = NULL; + + if (!env) { + SDL_InvalidParamError("env"); + return NULL; + } + + SDL_LockMutex(env->lock); + { + // First pass, get the size we need for all the strings + CountEnvStringsData countdata = { 0, 0 }; + SDL_IterateHashTable(env->strings, CountEnvStrings, &countdata); + + // Allocate memory for the strings + result = (char **)SDL_malloc((countdata.count + 1) * sizeof(*result) + countdata.length); + if (result) { + // Second pass, copy the strings + char *string = (char *)(result + countdata.count + 1); + CopyEnvStringsData cpydata = { result, string, 0 }; + SDL_IterateHashTable(env->strings, CopyEnvStrings, &cpydata); + SDL_assert(countdata.count == cpydata.count); + result[cpydata.count] = NULL; + } + } + SDL_UnlockMutex(env->lock); + + return result; +} + +bool SDL_SetEnvironmentVariable(SDL_Environment *env, const char *name, const char *value, bool overwrite) +{ + bool result = false; + + if (!env) { + return SDL_InvalidParamError("env"); + } else if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) { + return SDL_InvalidParamError("name"); + } else if (!value) { + return SDL_InvalidParamError("value"); + } + + SDL_LockMutex(env->lock); + { + char *string = NULL; + if (SDL_asprintf(&string, "%s=%s", name, value) > 0) { + const size_t len = SDL_strlen(name); + string[len] = '\0'; + const char *origname = name; + name = string; + value = string + len + 1; + result = SDL_InsertIntoHashTable(env->strings, name, value, overwrite); + if (!result) { + SDL_free(string); + if (!overwrite) { + const void *existing_value = NULL; + // !!! FIXME: InsertIntoHashTable does this lookup too, maybe we should have a means to report that, to avoid duplicate work? + if (SDL_FindInHashTable(env->strings, origname, &existing_value)) { + result = true; // it already existed, and we refused to overwrite it. Call it success. + } + } + } + } + } + SDL_UnlockMutex(env->lock); + + return result; +} + +bool SDL_UnsetEnvironmentVariable(SDL_Environment *env, const char *name) +{ + bool result = false; + + if (!env) { + return SDL_InvalidParamError("env"); + } else if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) { + return SDL_InvalidParamError("name"); + } + + SDL_LockMutex(env->lock); + { + const void *value; + if (SDL_FindInHashTable(env->strings, name, &value)) { + result = SDL_RemoveFromHashTable(env->strings, name); + } else { + result = true; + } + } + SDL_UnlockMutex(env->lock); + + return result; +} + +void SDL_DestroyEnvironment(SDL_Environment *env) +{ + if (!env || env == SDL_environment) { + return; + } + + SDL_DestroyMutex(env->lock); + SDL_DestroyHashTable(env->strings); + SDL_free(env); +} + +// Put a variable into the environment +// Note: Name may not contain a '=' character. (Reference: http://www.unix.com/man-page/Linux/3/setenv/) +#ifdef HAVE_LIBC_ENVIRONMENT +#if defined(HAVE_SETENV) +int SDL_setenv_unsafe(const char *name, const char *value, int overwrite) +{ + // Input validation + if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL || !value) { + return -1; + } + + SDL_SetEnvironmentVariable(SDL_GetEnvironment(), name, value, (overwrite != 0)); + + return setenv(name, value, overwrite); +} +// We have a real environment table, but no real setenv? Fake it w/ putenv. +#else +int SDL_setenv_unsafe(const char *name, const char *value, int overwrite) +{ + char *new_variable; + + // Input validation + if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL || !value) { + return -1; + } + + SDL_SetEnvironmentVariable(SDL_GetEnvironment(), name, value, (overwrite != 0)); + + if (getenv(name) != NULL) { + if (!overwrite) { + return 0; // leave the existing one there. + } + } + + // This leaks. Sorry. Get a better OS so we don't have to do this. + SDL_asprintf(&new_variable, "%s=%s", name, value); + if (!new_variable) { + return -1; + } + return putenv(new_variable); +} +#endif +#elif defined(HAVE_WIN32_ENVIRONMENT) +int SDL_setenv_unsafe(const char *name, const char *value, int overwrite) +{ + // Input validation + if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL || !value) { + return -1; + } + + SDL_SetEnvironmentVariable(SDL_GetEnvironment(), name, value, (overwrite != 0)); + + if (!overwrite) { + if (GetEnvironmentVariableA(name, NULL, 0) > 0) { + return 0; // asked not to overwrite existing value. + } + } + if (!SetEnvironmentVariableA(name, value)) { + return -1; + } + return 0; +} +#else // roll our own + +int SDL_setenv_unsafe(const char *name, const char *value, int overwrite) +{ + int added; + size_t len, i; + char **new_env; + char *new_variable; + + // Input validation + if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL || !value) { + return -1; + } + + // See if it already exists + if (!overwrite && SDL_getenv_unsafe(name)) { + return 0; + } + + SDL_SetEnvironmentVariable(SDL_GetEnvironment(), name, value, (overwrite != 0)); + + // Allocate memory for the variable + len = SDL_strlen(name) + SDL_strlen(value) + 2; + new_variable = (char *)SDL_malloc(len); + if (!new_variable) { + return -1; + } + + SDL_snprintf(new_variable, len, "%s=%s", name, value); + value = new_variable + SDL_strlen(name) + 1; + name = new_variable; + + // Actually put it into the environment + added = 0; + i = 0; + if (environ) { + // Check to see if it's already there... + len = (value - name); + for (; environ[i]; ++i) { + if (SDL_strncmp(environ[i], name, len) == 0) { + // If we found it, just replace the entry + SDL_free(environ[i]); + environ[i] = new_variable; + added = 1; + break; + } + } + } + + // Didn't find it in the environment, expand and add + if (!added) { + new_env = SDL_realloc(environ, (i + 2) * sizeof(char *)); + if (new_env) { + environ = new_env; + environ[i++] = new_variable; + environ[i++] = (char *)0; + added = 1; + } else { + SDL_free(new_variable); + } + } + return added ? 0 : -1; +} +#endif // HAVE_LIBC_ENVIRONMENT + +#ifdef HAVE_LIBC_ENVIRONMENT +#if defined(HAVE_UNSETENV) +int SDL_unsetenv_unsafe(const char *name) +{ + // Input validation + if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) { + return -1; + } + + SDL_UnsetEnvironmentVariable(SDL_GetEnvironment(), name); + + return unsetenv(name); +} +// We have a real environment table, but no unsetenv? Fake it w/ putenv. +#else +int SDL_unsetenv_unsafe(const char *name) +{ + // Input validation + if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) { + return -1; + } + + SDL_UnsetEnvironmentVariable(SDL_GetEnvironment(), name); + + // Hope this environment uses the non-standard extension of removing the environment variable if it has no '=' + return putenv(name); +} +#endif +#elif defined(HAVE_WIN32_ENVIRONMENT) +int SDL_unsetenv_unsafe(const char *name) +{ + // Input validation + if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) { + return -1; + } + + SDL_UnsetEnvironmentVariable(SDL_GetEnvironment(), name); + + if (!SetEnvironmentVariableA(name, NULL)) { + return -1; + } + return 0; +} +#else +int SDL_unsetenv_unsafe(const char *name) +{ + size_t len, i; + + // Input validation + if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) { + return -1; + } + + SDL_UnsetEnvironmentVariable(SDL_GetEnvironment(), name); + + if (environ) { + len = SDL_strlen(name); + for (i = 0; environ[i]; ++i) { + if ((SDL_strncmp(environ[i], name, len) == 0) && + (environ[i][len] == '=')) { + // Just clear out this entry for now + *environ[i] = '\0'; + break; + } + } + } + return 0; +} +#endif // HAVE_LIBC_ENVIRONMENT + +// Retrieve a variable named "name" from the environment +#ifdef HAVE_LIBC_ENVIRONMENT +const char *SDL_getenv_unsafe(const char *name) +{ +#ifdef SDL_PLATFORM_ANDROID + // Make sure variables from the application manifest are available + Android_JNI_GetManifestEnvironmentVariables(); +#endif + + // Input validation + if (!name || *name == '\0') { + return NULL; + } + + return getenv(name); +} +#elif defined(HAVE_WIN32_ENVIRONMENT) +const char *SDL_getenv_unsafe(const char *name) +{ + DWORD length, maxlen = 0; + char *string = NULL; + const char *result = NULL; + + // Input validation + if (!name || *name == '\0') { + return NULL; + } + + for ( ; ; ) { + SetLastError(ERROR_SUCCESS); + length = GetEnvironmentVariableA(name, string, maxlen); + + if (length > maxlen) { + char *temp = (char *)SDL_realloc(string, length); + if (!temp) { + return NULL; + } + string = temp; + maxlen = length; + } else { + if (GetLastError() != ERROR_SUCCESS) { + if (string) { + SDL_free(string); + } + return NULL; + } + break; + } + } + if (string) { + result = SDL_GetPersistentString(string); + SDL_free(string); + } + return result; +} +#else +const char *SDL_getenv_unsafe(const char *name) +{ + size_t len, i; + const char *value = NULL; + + // Input validation + if (!name || *name == '\0') { + return NULL; + } + + if (environ) { + len = SDL_strlen(name); + for (i = 0; environ[i]; ++i) { + if ((SDL_strncmp(environ[i], name, len) == 0) && + (environ[i][len] == '=')) { + value = &environ[i][len + 1]; + break; + } + } + } + return value; +} +#endif // HAVE_LIBC_ENVIRONMENT + +const char *SDL_getenv(const char *name) +{ + return SDL_GetEnvironmentVariable(SDL_GetEnvironment(), name); +} -- cgit v1.2.3