sdl2-compat: env: Handle cleared variables during env sync

From 27995954768fe2dda8261a9ba3f654693fea3c7b Mon Sep 17 00:00:00 2001
From: Cameron Gutman <[EMAIL REDACTED]>
Date: Sun, 16 Feb 2025 16:20:08 -0600
Subject: [PATCH] env: Handle cleared variables during env sync

---
 src/sdl2_compat.c | 50 +++++++++++++++++++++++++++++++++++++----------
 src/sdl3_syms.h   |  2 ++
 2 files changed, 42 insertions(+), 10 deletions(-)

diff --git a/src/sdl2_compat.c b/src/sdl2_compat.c
index 7b2ce95..99b5598 100644
--- a/src/sdl2_compat.c
+++ b/src/sdl2_compat.c
@@ -5725,13 +5725,16 @@ static struct {
 #endif
 };
 
-/* TODO: Handle variables that were set previously and later cleared */
-static void SDL2COMPAT_SetEnvironmentVariable(SDL_Environment *env, const char *name, const char *value)
+static void SDL2COMPAT_SetEnvironmentVariable(SDL_Environment *env, const char *name, const char *value, bool overwrite)
 {
     unsigned int i;
 
     /* Propagate the original variable name and value, so SDL_getenv() works as expected */
-    SDL3_SetEnvironmentVariable(env, name, value, true);
+    if (value) {
+        SDL3_SetEnvironmentVariable(env, name, value, overwrite);
+    } else {
+        SDL3_UnsetEnvironmentVariable(env, name);
+    }
 
     /* Handle environment variables that became hints in SDL3  */
     for (i = 0; i < SDL_arraysize(envvars_to_hints); ++i) {
@@ -5750,7 +5753,11 @@ static void SDL2COMPAT_SetEnvironmentVariable(SDL_Environment *env, const char *
                 break;
             }
 
-            SDL3_SetEnvironmentVariable(env, envvars_to_hints[i].new_hint, value3, true);
+            if (value3) {
+                SDL3_SetEnvironmentVariable(env, envvars_to_hints[i].new_hint, value3, overwrite);
+            } else {
+                SDL3_UnsetEnvironmentVariable(env, envvars_to_hints[i].new_hint);
+            }
         }
     }
 
@@ -5759,7 +5766,13 @@ static void SDL2COMPAT_SetEnvironmentVariable(SDL_Environment *env, const char *
         if (SDL3_strcmp(name, renamed_hints[i].old_hint) == 0) {
             bool free_value = false;
             const char *value3 = SDL2_to_SDL3_hint_value(name, value, &free_value);
-            SDL3_SetEnvironmentVariable(env, renamed_hints[i].new_hint, value3, true);
+
+            if (value3) {
+                SDL3_SetEnvironmentVariable(env, renamed_hints[i].new_hint, value3, overwrite);
+            } else {
+                SDL3_UnsetEnvironmentVariable(env, renamed_hints[i].new_hint);
+            }
+
             if (free_value) {
                 SDL3_free((void *)value3);
             }
@@ -5771,15 +5784,32 @@ static void SynchronizeEnvironmentVariables()
 {
     SDL_Environment *env = SDL3_GetEnvironment();
     SDL_Environment *fresh_env = SDL3_CreateEnvironment(true);
+    char **envp;
+    char **fresh_envp;
+    int i;
+
+    /* Clear variables that are no longer set in the environment */
+    envp = SDL3_GetEnvironmentVariables(env);
+    if (envp) {
+        for (i = 0; envp[i]; ++i) {
+            char *sep = SDL3_strchr(envp[i], '=');
+            *sep = '\0';
+
+            if (SDL3_GetEnvironmentVariable(fresh_env, envp[i]) == NULL) {
+                SDL2COMPAT_SetEnvironmentVariable(env, envp[i], NULL, true);
+            }
+        }
+
+        SDL3_free(envp);
+    }
 
-    /* Sync any changes to the C environment into SDL3's cached copy */
-    char **fresh_envp = SDL3_GetEnvironmentVariables(fresh_env);
+    /* Set variables that are present in the environment */
+    fresh_envp = SDL3_GetEnvironmentVariables(fresh_env);
     if (fresh_envp) {
-        int i = 0;
-        for (; fresh_envp[i]; ++i) {
+        for (i = 0; fresh_envp[i]; ++i) {
             char *sep = SDL3_strchr(fresh_envp[i], '=');
             *sep = '\0';
-            SDL2COMPAT_SetEnvironmentVariable(env, fresh_envp[i], sep + 1);
+            SDL2COMPAT_SetEnvironmentVariable(env, fresh_envp[i], sep + 1, true);
         }
 
         SDL3_free(fresh_envp);
diff --git a/src/sdl3_syms.h b/src/sdl3_syms.h
index b129320..dca3710 100644
--- a/src/sdl3_syms.h
+++ b/src/sdl3_syms.h
@@ -277,6 +277,7 @@ SDL3_SYM(const char*,GetDisplayName,(SDL_DisplayID a),(a),return)
 SDL3_SYM(bool,GetDisplayUsableBounds,(SDL_DisplayID a, SDL_Rect *b),(a,b),return)
 SDL3_SYM(SDL_DisplayID*,GetDisplays,(int *a),(a),return)
 SDL3_SYM(SDL_Environment*,GetEnvironment,(void),(),return)
+SDL3_SYM(const char*,GetEnvironmentVariable,(SDL_Environment *a, const char *b),(a,b),return)
 SDL3_SYM(char**,GetEnvironmentVariables,(SDL_Environment *a),(a),return)
 SDL3_SYM_PASSTHROUGH(const char*,GetError,(void),(),return)
 SDL3_SYM(bool,GetEventFilter,(SDL_EventFilter *a, void **b),(a,b),)
@@ -700,6 +701,7 @@ SDL3_SYM(bool,UnlockAudioStream,(SDL_AudioStream *a),(a),return)
 SDL3_SYM(void,UnlockMutex,(SDL_Mutex *a),(a),)
 SDL3_SYM(void,UnlockSurface,(SDL_Surface *a),(a),)
 SDL3_SYM_PASSTHROUGH(void,UnlockTexture,(SDL_Texture *a),(a),)
+SDL3_SYM(bool,UnsetEnvironmentVariable,(SDL_Environment *a, const char *b),(a,b),return)
 SDL3_SYM(bool,UpdateHapticEffect,(SDL_Haptic *a, int b, const SDL_HapticEffect *c),(a,b,c),return)
 SDL3_SYM_PASSTHROUGH_RETCODE(bool,UpdateNVTexture,(SDL_Texture *a, const SDL_Rect *b, const Uint8 *c, int d, const Uint8 *e, int f),(a,b,c,d,e,f),return)
 SDL3_SYM_PASSTHROUGH_RETCODE(bool,UpdateTexture,(SDL_Texture *a, const SDL_Rect *b, const void *c, int d),(a,b,c,d),return)