From 28b94c4758563eee6a7193d22e0380cc85f7b5bf Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 15 Sep 2024 09:16:30 -0700
Subject: [PATCH] Re-added SDL_getenv() as a thread-safe getenv()
implementation
---
docs/README-migration.md | 2 +
include/SDL3/SDL_stdinc.h | 21 +-
src/SDL_hints.c | 8 +-
src/audio/SDL_audiodev.c | 2 +-
src/core/linux/SDL_ibus.c | 10 +-
src/core/linux/SDL_ime.c | 4 +-
src/core/linux/SDL_sandbox.c | 6 +-
src/dynapi/SDL_dynapi.sym | 3 +-
src/dynapi/SDL_dynapi_overrides.h | 3 +-
src/dynapi/SDL_dynapi_procs.h | 3 +-
src/filesystem/cocoa/SDL_sysfilesystem.m | 2 +-
src/filesystem/emscripten/SDL_sysfilesystem.c | 2 +-
src/filesystem/haiku/SDL_sysfilesystem.cc | 4 +-
src/filesystem/unix/SDL_sysfilesystem.c | 16 +-
src/haptic/SDL_haptic.c | 2 +-
src/locale/unix/SDL_syslocale.c | 4 +-
src/stdlib/SDL_getenv.c | 554 +++++++++---------
src/stdlib/SDL_getenv_c.h | 23 -
src/stdlib/SDL_iconv.c | 8 +-
src/video/SDL_egl.c | 4 +-
src/video/wayland/SDL_waylandevents.c | 6 +-
src/video/wayland/SDL_waylandmessagebox.c | 4 +-
src/video/wayland/SDL_waylandmouse.c | 4 +-
src/video/wayland/SDL_waylandshmbuffer.c | 2 +-
src/video/wayland/SDL_waylandvideo.c | 4 +-
src/video/wayland/SDL_waylandwindow.c | 2 +-
src/video/x11/SDL_x11keyboard.c | 2 +-
src/video/x11/SDL_x11modes.c | 2 +-
28 files changed, 361 insertions(+), 346 deletions(-)
delete mode 100644 src/stdlib/SDL_getenv_c.h
diff --git a/docs/README-migration.md b/docs/README-migration.md
index 171823a014ccc..daa521af42a13 100644
--- a/docs/README-migration.md
+++ b/docs/README-migration.md
@@ -1743,6 +1743,8 @@ Please note that the case-folding technique used by SDL3 will not produce correc
SDL_strtoll(), SDL_strtoull(), SDL_lltoa(), and SDL_ulltoa() use long long values instead of 64-bit values, to match their C runtime counterparts.
+SDL_setenv() is not thread-safe and has been renamed SDL_setenv_unsafe().
+
The following macros have been removed:
* SDL_TABLESIZE() - use SDL_arraysize() instead
diff --git a/include/SDL3/SDL_stdinc.h b/include/SDL3/SDL_stdinc.h
index e7ca1cc7cda36..924c5399b47e5 100644
--- a/include/SDL3/SDL_stdinc.h
+++ b/include/SDL3/SDL_stdinc.h
@@ -1156,16 +1156,33 @@ extern SDL_DECLSPEC void SDLCALL SDL_DestroyEnvironment(SDL_Environment *env);
/**
* Get the value of a variable in the environment.
*
+ * This function uses SDL's cached copy of the environment and is thread-safe.
+ *
+ * \param name the name of the variable to get.
+ * \returns a pointer to the value of the variable or NULL if it can't be
+ * found.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern SDL_DECLSPEC const char * SDLCALL SDL_getenv(const char *name);
+
+/**
+ * Get the value of a variable in the environment.
+ *
+ * This function bypasses SDL's cached copy of the environment and is not thread-safe.
+ *
* \param name the name of the variable to get.
* \returns a pointer to the value of the variable or NULL if it can't be
* found.
*
* \threadsafety This function is not thread safe, consider using
- * SDL_GetEnvironmentVariable() instead.
+ * SDL_getenv() instead.
*
* \since This function is available since SDL 3.0.0.
*
- * \sa SDL_GetEnvironmentVariable
+ * \sa SDL_getenv
*/
extern SDL_DECLSPEC const char * SDLCALL SDL_getenv_unsafe(const char *name);
diff --git a/src/SDL_hints.c b/src/SDL_hints.c
index a91877bbaad38..8dd9c551ccb09 100644
--- a/src/SDL_hints.c
+++ b/src/SDL_hints.c
@@ -72,7 +72,7 @@ SDL_bool SDL_SetHintWithPriority(const char *name, const char *value, SDL_HintPr
return SDL_InvalidParamError("name");
}
- const char *env = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), name);
+ const char *env = SDL_getenv(name);
if (env && (priority < SDL_HINT_OVERRIDE)) {
return SDL_SetError("An environment variable is taking priority");
}
@@ -126,7 +126,7 @@ SDL_bool SDL_ResetHint(const char *name)
return SDL_InvalidParamError("name");
}
- const char *env = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), name);
+ const char *env = SDL_getenv(name);
const SDL_PropertiesID hints = GetHintProperties(false);
if (!hints) {
@@ -165,7 +165,7 @@ static void SDLCALL ResetHintsCallback(void *userdata, SDL_PropertiesID hints, c
return; // uh...okay.
}
- const char *env = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), name);
+ const char *env = SDL_getenv(name);
if ((!env && hint->value) || (env && !hint->value) || (env && SDL_strcmp(env, hint->value) != 0)) {
SDL_HintWatch *entry = hint->callbacks;
while (entry) {
@@ -196,7 +196,7 @@ const char *SDL_GetHint(const char *name)
return NULL;
}
- const char *result = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), name);
+ const char *result = SDL_getenv(name);
const SDL_PropertiesID hints = GetHintProperties(false);
if (hints) {
diff --git a/src/audio/SDL_audiodev.c b/src/audio/SDL_audiodev.c
index f5dc4b20e95f3..f48a5b25d0bb5 100644
--- a/src/audio/SDL_audiodev.c
+++ b/src/audio/SDL_audiodev.c
@@ -87,7 +87,7 @@ static void SDL_EnumUnixAudioDevices_Internal(const bool recording, const bool c
}
// Figure out what our audio device is
- audiodev = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "AUDIODEV");
+ audiodev = SDL_getenv("AUDIODEV");
if (!audiodev) {
if (classic) {
audiodev = SDL_PATH_DEV_AUDIO;
diff --git a/src/core/linux/SDL_ibus.c b/src/core/linux/SDL_ibus.c
index 62daaf3a5fb0f..dbbc3e1740e41 100644
--- a/src/core/linux/SDL_ibus.c
+++ b/src/core/linux/SDL_ibus.c
@@ -331,14 +331,14 @@ static char *IBus_GetDBusAddressFilename(void)
}
// Use this environment variable if it exists.
- addr = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "IBUS_ADDRESS");
+ addr = SDL_getenv("IBUS_ADDRESS");
if (addr && *addr) {
return SDL_strdup(addr);
}
/* Otherwise, we have to get the hostname, display, machine id, config dir
and look up the address from a filepath using all those bits, eek. */
- disp_env = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "DISPLAY");
+ disp_env = SDL_getenv("DISPLAY");
if (!disp_env || !*disp_env) {
display = SDL_strdup(":0.0");
@@ -363,7 +363,7 @@ static char *IBus_GetDBusAddressFilename(void)
}
if (!*host) {
- const char *session = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "XDG_SESSION_TYPE");
+ const char *session = SDL_getenv("XDG_SESSION_TYPE");
if (session && SDL_strcmp(session, "wayland") == 0) {
host = "unix-wayland";
} else {
@@ -373,11 +373,11 @@ static char *IBus_GetDBusAddressFilename(void)
SDL_memset(config_dir, 0, sizeof(config_dir));
- conf_env = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "XDG_CONFIG_HOME");
+ conf_env = SDL_getenv("XDG_CONFIG_HOME");
if (conf_env && *conf_env) {
SDL_strlcpy(config_dir, conf_env, sizeof(config_dir));
} else {
- const char *home_env = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "HOME");
+ const char *home_env = SDL_getenv("HOME");
if (!home_env || !*home_env) {
SDL_free(display);
return NULL;
diff --git a/src/core/linux/SDL_ime.c b/src/core/linux/SDL_ime.c
index a371e658e1ca6..a853618bb41c7 100644
--- a/src/core/linux/SDL_ime.c
+++ b/src/core/linux/SDL_ime.c
@@ -44,8 +44,8 @@ static void InitIME(void)
{
static bool inited = false;
#ifdef HAVE_FCITX
- const char *im_module = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "SDL_IM_MODULE");
- const char *xmodifiers = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "XMODIFIERS");
+ const char *im_module = SDL_getenv("SDL_IM_MODULE");
+ const char *xmodifiers = SDL_getenv("XMODIFIERS");
#endif
if (inited == true) {
diff --git a/src/core/linux/SDL_sandbox.c b/src/core/linux/SDL_sandbox.c
index 797d36c1ef95e..5a8df84871459 100644
--- a/src/core/linux/SDL_sandbox.c
+++ b/src/core/linux/SDL_sandbox.c
@@ -33,9 +33,9 @@ SDL_Sandbox SDL_DetectSandbox(void)
/* For Snap, we check multiple variables because they might be set for
* unrelated reasons. This is the same thing WebKitGTK does. */
- if (SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "SNAP") != NULL &&
- SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "SNAP_NAME") != NULL &&
- SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "SNAP_REVISION") != NULL) {
+ if (SDL_getenv("SNAP") != NULL &&
+ SDL_getenv("SNAP_NAME") != NULL &&
+ SDL_getenv("SNAP_REVISION") != NULL) {
return SDL_SANDBOX_SNAP;
}
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index 6b382567539eb..01f6f506d0602 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -239,7 +239,6 @@ SDL3_0.0.0 {
SDL_GetBasePath;
SDL_GetBooleanProperty;
SDL_GetCPUCacheLineSize;
- SDL_GetNumLogicalCPUCores;
SDL_GetCameraDriver;
SDL_GetCameraFormat;
SDL_GetCameraID;
@@ -414,6 +413,7 @@ SDL3_0.0.0 {
SDL_GetNumJoystickBalls;
SDL_GetNumJoystickButtons;
SDL_GetNumJoystickHats;
+ SDL_GetNumLogicalCPUCores;
SDL_GetNumRenderDrivers;
SDL_GetNumVideoDrivers;
SDL_GetNumberProperty;
@@ -1029,6 +1029,7 @@ SDL3_0.0.0 {
SDL_fmod;
SDL_fmodf;
SDL_free;
+ SDL_getenv;
SDL_getenv_unsafe;
SDL_hid_ble_scan;
SDL_hid_close;
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 3221271800163..e0d1be09216d5 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -264,7 +264,6 @@
#define SDL_GetBasePath SDL_GetBasePath_REAL
#define SDL_GetBooleanProperty SDL_GetBooleanProperty_REAL
#define SDL_GetCPUCacheLineSize SDL_GetCPUCacheLineSize_REAL
-#define SDL_GetNumLogicalCPUCores SDL_GetNumLogicalCPUCores_REAL
#define SDL_GetCameraDriver SDL_GetCameraDriver_REAL
#define SDL_GetCameraFormat SDL_GetCameraFormat_REAL
#define SDL_GetCameraID SDL_GetCameraID_REAL
@@ -439,6 +438,7 @@
#define SDL_GetNumJoystickBalls SDL_GetNumJoystickBalls_REAL
#define SDL_GetNumJoystickButtons SDL_GetNumJoystickButtons_REAL
#define SDL_GetNumJoystickHats SDL_GetNumJoystickHats_REAL
+#define SDL_GetNumLogicalCPUCores SDL_GetNumLogicalCPUCores_REAL
#define SDL_GetNumRenderDrivers SDL_GetNumRenderDrivers_REAL
#define SDL_GetNumVideoDrivers SDL_GetNumVideoDrivers_REAL
#define SDL_GetNumberProperty SDL_GetNumberProperty_REAL
@@ -1054,6 +1054,7 @@
#define SDL_fmod SDL_fmod_REAL
#define SDL_fmodf SDL_fmodf_REAL
#define SDL_free SDL_free_REAL
+#define SDL_getenv SDL_getenv_REAL
#define SDL_getenv_unsafe SDL_getenv_unsafe_REAL
#define SDL_hid_ble_scan SDL_hid_ble_scan_REAL
#define SDL_hid_close SDL_hid_close_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index ab406b4f587ee..8c3c10a05a0bf 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -284,7 +284,6 @@ SDL_DYNAPI_PROC(int,SDL_GetAudioStreamQueued,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(const char*,SDL_GetBasePath,(void),(),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_GetBooleanProperty,(SDL_PropertiesID a, const char *b, SDL_bool c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_GetCPUCacheLineSize,(void),(),return)
-SDL_DYNAPI_PROC(int,SDL_GetNumLogicalCPUCores,(void),(),return)
SDL_DYNAPI_PROC(const char*,SDL_GetCameraDriver,(int a),(a),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_GetCameraFormat,(SDL_Camera *a, SDL_CameraSpec *b),(a,b),return)
SDL_DYNAPI_PROC(SDL_CameraID,SDL_GetCameraID,(SDL_Camera *a),(a),return)
@@ -459,6 +458,7 @@ SDL_DYNAPI_PROC(int,SDL_GetNumJoystickAxes,(SDL_Joystick *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetNumJoystickBalls,(SDL_Joystick *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetNumJoystickButtons,(SDL_Joystick *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetNumJoystickHats,(SDL_Joystick *a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_GetNumLogicalCPUCores,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_GetNumRenderDrivers,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_GetNumVideoDrivers,(void),(),return)
SDL_DYNAPI_PROC(Sint64,SDL_GetNumberProperty,(SDL_PropertiesID a, const char *b, Sint64 c),(a,b,c),return)
@@ -1063,6 +1063,7 @@ SDL_DYNAPI_PROC(float,SDL_floorf,(float a),(a),return)
SDL_DYNAPI_PROC(double,SDL_fmod,(double a, double b),(a,b),return)
SDL_DYNAPI_PROC(float,SDL_fmodf,(float a, float b),(a,b),return)
SDL_DYNAPI_PROC(void,SDL_free,(void *a),(a),)
+SDL_DYNAPI_PROC(const char*,SDL_getenv,(const char *a),(a),return)
SDL_DYNAPI_PROC(const char*,SDL_getenv_unsafe,(const char *a),(a),return)
SDL_DYNAPI_PROC(void,SDL_hid_ble_scan,(SDL_bool a),(a),)
SDL_DYNAPI_PROC(int,SDL_hid_close,(SDL_hid_device *a),(a),return)
diff --git a/src/filesystem/cocoa/SDL_sysfilesystem.m b/src/filesystem/cocoa/SDL_sysfilesystem.m
index c88ad1c1ade7d..07a75ff74e4e6 100644
--- a/src/filesystem/cocoa/SDL_sysfilesystem.m
+++ b/src/filesystem/cocoa/SDL_sysfilesystem.m
@@ -144,7 +144,7 @@
switch (folder) {
case SDL_FOLDER_HOME:
- base = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "HOME");
+ base = SDL_getenv("HOME");
if (!base) {
SDL_SetError("No $HOME environment variable available");
diff --git a/src/filesystem/emscripten/SDL_sysfilesystem.c b/src/filesystem/emscripten/SDL_sysfilesystem.c
index 8e228203c4c88..e10081c775193 100644
--- a/src/filesystem/emscripten/SDL_sysfilesystem.c
+++ b/src/filesystem/emscripten/SDL_sysfilesystem.c
@@ -93,7 +93,7 @@ char *SDL_SYS_GetUserFolder(SDL_Folder folder)
return NULL;
}
- home = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "HOME");
+ home = SDL_getenv("HOME");
if (!home) {
SDL_SetError("No $HOME environment variable available");
return NULL;
diff --git a/src/filesystem/haiku/SDL_sysfilesystem.cc b/src/filesystem/haiku/SDL_sysfilesystem.cc
index e8f70715591e1..60e7d5b09de5e 100644
--- a/src/filesystem/haiku/SDL_sysfilesystem.cc
+++ b/src/filesystem/haiku/SDL_sysfilesystem.cc
@@ -68,7 +68,7 @@ char *SDL_SYS_GetBasePath(void)
char *SDL_SYS_GetPrefPath(const char *org, const char *app)
{
// !!! FIXME: is there a better way to do this?
- const char *home = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "HOME");
+ const char *home = SDL_getenv("HOME");
const char *append = "/config/settings/";
size_t len = SDL_strlen(home);
@@ -102,7 +102,7 @@ char *SDL_SYS_GetUserFolder(SDL_Folder folder)
const char *home = NULL;
char *result;
- home = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "HOME");
+ home = SDL_getenv("HOME");
if (!home) {
SDL_SetError("No $HOME environment variable available");
return NULL;
diff --git a/src/filesystem/unix/SDL_sysfilesystem.c b/src/filesystem/unix/SDL_sysfilesystem.c
index 80938c47c5f07..6f46a5b3fdd52 100644
--- a/src/filesystem/unix/SDL_sysfilesystem.c
+++ b/src/filesystem/unix/SDL_sysfilesystem.c
@@ -74,7 +74,7 @@ static char *readSymLink(const char *path)
#ifdef SDL_PLATFORM_OPENBSD
static char *search_path_for_binary(const char *bin)
{
- const char *envr_real = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "PATH");
+ const char *envr_real = SDL_getenv("PATH");
char *envr;
size_t alloc_size;
char *exe = NULL;
@@ -163,7 +163,7 @@ char *SDL_SYS_GetBasePath(void)
exe = search_path_for_binary(cmdline[0]);
} else {
if (exe && *exe == '.') {
- const char *pwd = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "PWD");
+ const char *pwd = SDL_getenv("PWD");
if (pwd && *pwd) {
SDL_asprintf(&pwddst, "%s/%s", pwd, exe);
}
@@ -265,7 +265,7 @@ char *SDL_SYS_GetPrefPath(const char *org, const char *app)
*
* http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
*/
- const char *envr = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "XDG_DATA_HOME");
+ const char *envr = SDL_getenv("XDG_DATA_HOME");
const char *append;
char *result = NULL;
char *ptr = NULL;
@@ -281,7 +281,7 @@ char *SDL_SYS_GetPrefPath(const char *org, const char *app)
if (!envr) {
// You end up with "$HOME/.local/share/Game Name 2"
- envr = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "HOME");
+ envr = SDL_getenv("HOME");
if (!envr) {
// we could take heroic measures with /etc/passwd, but oh well.
SDL_SetError("neither XDG_DATA_HOME nor HOME environment is set");
@@ -368,12 +368,12 @@ static char *xdg_user_dir_lookup_with_fallback (const char *type, const char *fa
int relative;
size_t l;
- home_dir = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "HOME");
+ home_dir = SDL_getenv("HOME");
if (!home_dir)
goto error;
- config_home = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "XDG_CONFIG_HOME");
+ config_home = SDL_getenv("XDG_CONFIG_HOME");
if (!config_home || config_home[0] == 0)
{
l = SDL_strlen (home_dir) + SDL_strlen ("/.config/user-dirs.dirs") + 1;
@@ -495,7 +495,7 @@ static char *xdg_user_dir_lookup (const char *type)
if (dir)
return dir;
- home_dir = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "HOME");
+ home_dir = SDL_getenv("HOME");
if (!home_dir)
return NULL;
@@ -533,7 +533,7 @@ char *SDL_SYS_GetUserFolder(SDL_Folder folder)
*/
switch(folder) {
case SDL_FOLDER_HOME:
- param = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "HOME");
+ param = SDL_getenv("HOME");
if (!param) {
SDL_SetError("No $HOME environment variable available");
diff --git a/src/haptic/SDL_haptic.c b/src/haptic/SDL_haptic.c
index c40afacbf28f2..2f6ef78ea129e 100644
--- a/src/haptic/SDL_haptic.c
+++ b/src/haptic/SDL_haptic.c
@@ -537,7 +537,7 @@ SDL_bool SDL_SetHapticGain(SDL_Haptic *haptic, int gain)
}
// The user can use an environment variable to override the max gain.
- env = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "SDL_HAPTIC_GAIN_MAX");
+ env = SDL_getenv("SDL_HAPTIC_GAIN_MAX");
if (env) {
max_gain = SDL_atoi(env);
diff --git a/src/locale/unix/SDL_syslocale.c b/src/locale/unix/SDL_syslocale.c
index ac4301dc1a933..e945da5c86da6 100644
--- a/src/locale/unix/SDL_syslocale.c
+++ b/src/locale/unix/SDL_syslocale.c
@@ -78,13 +78,13 @@ bool SDL_SYS_GetPreferredLocales(char *buf, size_t buflen)
*tmp = '\0';
// LANG is the primary locale (maybe)
- envr = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "LANG");
+ envr = SDL_getenv("LANG");
if (envr) {
SDL_strlcpy(tmp, envr, buflen);
}
// fallback languages
- envr = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "LANGUAGE");
+ envr = SDL_getenv("LANGUAGE");
if (envr) {
if (*tmp) {
SDL_strlcat(tmp, ":", buflen);
diff --git a/src/stdlib/SDL_getenv.c b/src/stdlib/SDL_getenv.c
index c29fc3149cffe..0243b9bf92446 100644
--- a/src/stdlib/SDL_getenv.c
+++ b/src/stdlib/SDL_getenv.c
@@ -20,7 +20,6 @@
*/
#include "SDL_internal.h"
-#include "SDL_getenv_c.h"
#include "../SDL_hashtable.h"
#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
@@ -37,11 +36,6 @@
(defined(HAVE_SETENV) || defined(HAVE_PUTENV)) && \
(defined(HAVE_UNSETENV) || defined(HAVE_PUTENV))
#define HAVE_LIBC_ENVIRONMENT
-#else
-#define HAVE_LOCAL_ENVIRONMENT
-#endif
-
-#if !defined(SDL_PLATFORM_WINDOWS)
#if defined(SDL_PLATFORM_MACOS)
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
@@ -51,7 +45,253 @@
#else
extern char **environ;
#endif
-#endif // !SDL_PLATFORM_WINDOWS
+#else
+#define HAVE_LOCAL_ENVIRONMENT
+static char **environ;
+#endif
+
+
+struct SDL_Environment
+{
+ SDL_Mutex *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;
+}
+
+void SDL_CleanupEnvironment(void)
+{
+ SDL_Environment *env = SDL_environment;
+
+ if (env) {
+ SDL_environment = NULL;
+ SDL_DestroyEnvironment(env);
+ }
+}
+
+SDL_Environment *SDL_CreateEnvironment(SDL_bool populated)
+{
+ SDL_Environment *env = SDL_calloc(1, sizeof(*env));
+ if (!env) {
+ return NULL;
+ }
+
+ env->strings = SDL_CreateHashTable(NULL, 16, SDL_HashString, SDL_KeyMatchString, SDL_NukeFreeKey, false);
+ 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)
+ 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);
+ }
+ 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);
+ }
+ }
+#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;
+}
+
+char **SDL_GetEnvironmentVariables(SDL_Environment *env)
+{
+ char **result = NULL;
+
+ if (!env) {
+ SDL_InvalidParamError("env");
+ return NULL;
+ }
+
+ SDL_LockMutex(env->lock);
+ {
+ size_t count, length = 0;
+ void *iter;
+ const char *key, *value;
+
+ // First pass, get the size we need for all the strings
+ count = 0;
+ iter = NULL;
+ while (SDL_IterateHashTable(env->strings, (const void **)&key, (const void **)&value, &iter)) {
+ length += SDL_strlen(key) + 1 + SDL_strlen(value) + 1;
+ ++count;
+ }
+
+ // Allocate memory for the strings
+ result = (char **)SDL_malloc((count + 1) * sizeof(*result) + length);
+ char *string = (char *)(result + count + 1);
+
+ // Second pass, copy the strings
+ count = 0;
+ iter = NULL;
+ while (SDL_IterateHashTable(env->strings, (const void **)&key, (const void **)&value, &iter)) {
+ size_t len;
+
+ result[count] = string;
+ len = SDL_strlen(key);
+ SDL_memcpy(string, key, len);
+ string += len;
+ *string++ = '=';
+ len = SDL_strlen(value);
+ SDL_memcpy(string, value, len);
+ string += len;
+ *string++ = '\0';
+ ++count;
+ }
+ result[count] = NULL;
+ }
+ SDL_UnlockMutex(env->lock);
+
+ return result;
+}
+
+SDL_bool SDL_SetEnvironmentVariable(SDL_Environment *env, const char *name, const char *value, SDL_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);
+ {
+ const void *existing_value;
+ bool insert = true;
+
+ if (SDL_FindInHashTable(env->strings, name, &existing_value)) {
+ if (!overwrite) {
+ result = true;
+ insert = false;
+ } else {
+ SDL_RemoveFromHashTable(env->strings, name);
+ }
+ }
+
+ if (insert) {
+ char *string = NULL;
+ if (SDL_asprintf(&string, "%s=%s", name, value) > 0) {
+ size_t len = SDL_strlen(name);
+ string[len] = '\0';
+ name = string;
+ value = string + len + 1;
+ result = SDL_InsertIntoHashTable(env->strings, name, value);
+ }
+ }
+ }
+ SDL_UnlockMutex(env->lock);
+
+ return result;
+}
+
+SDL_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/)
@@ -64,6 +304,8 @@ int SDL_setenv_unsafe(const char *name, const char *value, int overwrite)
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.
@@ -77,6 +319,8 @@ int SDL_setenv_unsafe(const char *name, const char *value, int overwrite)
return -1;
}
+ SDL_SetEnvironmentVariable(SDL_GetEnvironment(), name, value, (overwrite != 0));
+
if (getenv(name) != NULL) {
if (!overwrite) {
return 0; // leave the existing one there.
@@ -99,6 +343,8 @@ int SDL_setenv_unsafe(const char *name, const char *value, int overwrite)
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.
@@ -111,9 +357,6 @@ int SDL_setenv_unsafe(const char *name, const char *value, int overwrite)
}
#else // roll our own
-// We'll leak this, as environment variables are intended to persist past SDL_Quit()
-static char **SDL_env;
-
int SDL_setenv_unsafe(const char *name, const char *value, int overwrite)
{
int added;
@@ -131,6 +374,8 @@ int SDL_setenv_unsafe(const char *name, const char *value, int overwrite)
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);
@@ -145,14 +390,14 @@ int SDL_setenv_unsafe(const char *name, const char *value, int overwrite)
// Actually put it into the environment
added = 0;
i = 0;
- if (SDL_env) {
+ if (environ) {
// Check to see if it's already there...
len = (value - name);
- for (; SDL_env[i]; ++i) {
- if (SDL_strncmp(SDL_env[i], name, len) == 0) {
+ for (; environ[i]; ++i) {
+ if (SDL_strncmp(environ[i], name, len) == 0) {
// If we found it, just replace the entry
- SDL_free(SDL_env[i]);
- SDL_env[i] = new_variable;
+ SDL_free(environ[i]);
+ environ[i] = new_variable;
added = 1;
break;
}
@@ -161,11 +406,11 @@ int SDL_setenv_unsafe(const char *name, const char *value, int overwrite)
// Didn't find it in the environment, expand and add
if (!added) {
- new_env = SDL_realloc(SDL_env, (i + 2) * sizeof(char *));
+ new_env = SDL_realloc(environ, (i + 2) * sizeof(char *));
if (new_env) {
- SDL_env = new_env;
- SDL_env[i++] = new_variable;
- SDL_env[i++] = (char *)0;
+ environ = new_env;
+ environ[i++] = new_variable;
+ environ[i++] = (char *)0;
added = 1;
} else {
SDL_free(new_variable);
@@ -184,6 +429,8 @@ int SDL_unsetenv_unsafe(const char *name)
return -1;
}
+ SDL_UnsetEnvironmentVariable(SDL_GetEnvironment(), name);
+
return unsetenv(name);
}
// We have a real environment table, but no unsetenv? Fake it w/ putenv.
@@ -195,6 +442,8 @@ int SDL_unsetenv_unsafe(const char *name)
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);
}
@@ -207,6 +456,8 @@ int SDL_unsetenv_unsafe(const char *name)
return -1;
}
+ SDL_UnsetEnvironmentVariable(SDL_GetEnvironment(), name);
+
if (!SetEnvironmentVariableA(name, NULL)) {
return -1;
}
@@ -222,13 +473,15 @@ int SDL_unsetenv_unsafe(const char *name)
return -1;
}
- if (SDL_env) {
+ SDL_UnsetEnvironmentVariable(SDL_GetEnvironment(), name);
+
+ if (environ) {
len = SDL_strlen(name);
- for (i = 0; SDL_env[i]; ++i) {
- if ((SDL_strncmp(SDL_env[i], name, len) == 0) &&
- (SDL_env[i][len] == '=')) {
+ for (i = 0; environ[i]; ++i) {
+ if ((SDL_strncmp(environ[i], name, len) == 0) &&
+ (environ[i][len] == '=')
(Patch may be truncated, please check the link at the top of this post.)