Browse Source

Added SDL_unsetenv()

Sam Lantinga 8 months ago
parent
commit
473feab2a4

+ 1 - 0
include/SDL3/SDL_stdinc.h

@@ -665,6 +665,7 @@ extern SDL_DECLSPEC int SDLCALL SDL_GetNumAllocations(void);
 
 extern SDL_DECLSPEC const char * SDLCALL SDL_getenv(const char *name);
 extern SDL_DECLSPEC int SDLCALL SDL_setenv(const char *name, const char *value, int overwrite);
+extern SDL_DECLSPEC int SDLCALL SDL_unsetenv(const char *name);
 
 typedef int (SDLCALL *SDL_CompareCallback)(const void *a, const void *b);
 extern SDL_DECLSPEC void SDLCALL SDL_qsort(void *base, size_t nmemb, size_t size, SDL_CompareCallback compare);

+ 0 - 2
include/build_config/SDL_build_config.h.cmake

@@ -75,12 +75,10 @@
 #cmakedefine HAVE_CALLOC 1
 #cmakedefine HAVE_REALLOC 1
 #cmakedefine HAVE_FREE 1
-#ifndef SDL_PLATFORM_WIN32 /* Don't use C runtime versions of these on Windows */
 #cmakedefine HAVE_GETENV 1
 #cmakedefine HAVE_SETENV 1
 #cmakedefine HAVE_PUTENV 1
 #cmakedefine HAVE_UNSETENV 1
-#endif
 #cmakedefine HAVE_ABS 1
 #cmakedefine HAVE_BCOPY 1
 #cmakedefine HAVE_MEMSET 1

+ 0 - 1
include/build_config/SDL_build_config_android.h

@@ -58,7 +58,6 @@
 #define HAVE_REALLOC    1
 #define HAVE_FREE   1
 #define HAVE_GETENV 1
-#define HAVE_SETENV 1
 #define HAVE_PUTENV 1
 #define HAVE_SETENV 1
 #define HAVE_UNSETENV   1

+ 0 - 1
include/build_config/SDL_build_config_ios.h

@@ -50,7 +50,6 @@
 #define HAVE_REALLOC    1
 #define HAVE_FREE   1
 #define HAVE_GETENV 1
-#define HAVE_SETENV 1
 #define HAVE_PUTENV 1
 #define HAVE_SETENV 1
 #define HAVE_UNSETENV   1

+ 1 - 0
src/dynapi/SDL_dynapi.sym

@@ -1038,6 +1038,7 @@ SDL3_0.0.0 {
     SDL_uitoa;
     SDL_ulltoa;
     SDL_ultoa;
+    SDL_unsetenv;
     SDL_utf8strlcpy;
     SDL_utf8strlen;
     SDL_utf8strnlen;

+ 1 - 0
src/dynapi/SDL_dynapi_overrides.h

@@ -1063,6 +1063,7 @@
 #define SDL_uitoa SDL_uitoa_REAL
 #define SDL_ulltoa SDL_ulltoa_REAL
 #define SDL_ultoa SDL_ultoa_REAL
+#define SDL_unsetenv SDL_unsetenv_REAL
 #define SDL_utf8strlcpy SDL_utf8strlcpy_REAL
 #define SDL_utf8strlen SDL_utf8strlen_REAL
 #define SDL_utf8strnlen SDL_utf8strnlen_REAL

+ 1 - 0
src/dynapi/SDL_dynapi_procs.h

@@ -1069,6 +1069,7 @@ SDL_DYNAPI_PROC(float,SDL_truncf,(float a),(a),return)
 SDL_DYNAPI_PROC(char*,SDL_uitoa,(unsigned int a, char *b, int c),(a,b,c),return)
 SDL_DYNAPI_PROC(char*,SDL_ulltoa,(Uint64 a, char *b, int c),(a,b,c),return)
 SDL_DYNAPI_PROC(char*,SDL_ultoa,(unsigned long a, char *b, int c),(a,b,c),return)
+SDL_DYNAPI_PROC(int,SDL_unsetenv,(const char *a),(a),return)
 SDL_DYNAPI_PROC(size_t,SDL_utf8strlcpy,(SDL_OUT_Z_CAP(c) char *a, const char *b, size_t c),(a,b,c),return)
 SDL_DYNAPI_PROC(size_t,SDL_utf8strlen,(const char *a),(a),return)
 SDL_DYNAPI_PROC(size_t,SDL_utf8strnlen,(const char *a, size_t b),(a,b),return)

+ 83 - 16
src/stdlib/SDL_getenv.c

@@ -30,11 +30,12 @@
 #include "../core/android/SDL_android.h"
 #endif
 
-#if (defined(HAVE_GETENV) && defined(HAVE_SETENV)) || \
-    (defined(HAVE_GETENV) && defined(HAVE_PUTENV) && defined(HAVE_UNSETENV))
-#define HAVE_LIBC_ENVIRONMENT
-#elif defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
+#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
 #define HAVE_WIN32_ENVIRONMENT
+#elif defined(HAVE_GETENV) && \
+      (defined(HAVE_SETENV) || defined(HAVE_PUTENV)) && \
+      (defined(HAVE_UNSETENV) || defined(HAVE_PUTENV))
+#define HAVE_LIBC_ENVIRONMENT
 #else
 #define HAVE_LOCAL_ENVIRONMENT
 #endif
@@ -64,15 +65,13 @@ int SDL_setenv(const char *name, const char *value, int overwrite)
     }
 
     if (getenv(name) != NULL) {
-        if (overwrite) {
-            unsetenv(name);
-        } else {
+        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_aprintf(&new_variable, "%s=%s", name, value);
+    SDL_asprintf(&new_variable, "%s=%s", name, value);
     if (!new_variable) {
         return -1;
     }
@@ -92,7 +91,7 @@ int SDL_setenv(const char *name, const char *value, int overwrite)
             return 0; /* asked not to overwrite existing value. */
         }
     }
-    if (!SetEnvironmentVariableA(name, *value ? value : NULL)) {
+    if (!SetEnvironmentVariableA(name, value)) {
         return -1;
     }
     return 0;
@@ -138,15 +137,13 @@ int SDL_setenv(const char *name, const char *value, int overwrite)
         len = (value - name);
         for (; SDL_env[i]; ++i) {
             if (SDL_strncmp(SDL_env[i], name, len) == 0) {
+                /* If we found it, just replace the entry */
+                SDL_free(SDL_env[i]);
+                SDL_env[i] = new_variable;
+                added = 1;
                 break;
             }
         }
-        /* If we found it, just replace the entry */
-        if (SDL_env[i]) {
-            SDL_free(SDL_env[i]);
-            SDL_env[i] = new_variable;
-            added = 1;
-        }
     }
 
     /* Didn't find it in the environment, expand and add */
@@ -165,6 +162,68 @@ int SDL_setenv(const char *name, const char *value, int overwrite)
 }
 #endif // HAVE_LIBC_ENVIRONMENT
 
+#ifdef HAVE_LIBC_ENVIRONMENT
+#if defined(HAVE_UNSETENV)
+int SDL_unsetenv(const char *name)
+{
+    /* Input validation */
+    if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) {
+        return -1;
+    }
+
+    return unsetenv(name);
+}
+/* We have a real environment table, but no unsetenv? Fake it w/ putenv. */
+#else
+int SDL_unsetenv(const char *name)
+{
+    /* Input validation */
+    if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) {
+        return -1;
+    }
+
+    // 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(const char *name)
+{
+    /* Input validation */
+    if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) {
+        return -1;
+    }
+
+    if (!SetEnvironmentVariableA(name, NULL)) {
+        return -1;
+    }
+    return 0;
+}
+#else
+int SDL_unsetenv(const char *name)
+{
+    size_t len, i;
+
+    /* Input validation */
+    if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) {
+        return -1;
+    }
+
+    if (SDL_env) {
+        len = SDL_strlen(name);
+        for (i = 0; SDL_env[i]; ++i) {
+            if ((SDL_strncmp(SDL_env[i], name, len) == 0) &&
+                (SDL_env[i][len] == '=')) {
+                /* Just clear out this entry for now */
+                *SDL_env[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(const char *name)
@@ -194,6 +253,7 @@ const char *SDL_getenv(const char *name)
     }
 
     for ( ; ; ) {
+        SetLastError(ERROR_SUCCESS);
         length = GetEnvironmentVariableA(name, string, maxlen);
 
         if (length > maxlen) {
@@ -204,6 +264,12 @@ const char *SDL_getenv(const char *name)
             string = temp;
             maxlen = length;
         } else {
+            if (GetLastError() != ERROR_SUCCESS) {
+                if (string) {
+                    SDL_free(string);
+                }
+                return NULL;
+            }
             break;
         }
     }
@@ -227,10 +293,11 @@ const char *SDL_getenv(const char *name)
     value = (char *)0;
     if (SDL_env) {
         len = SDL_strlen(name);
-        for (i = 0; SDL_env[i] && !value; ++i) {
+        for (i = 0; SDL_env[i]; ++i) {
             if ((SDL_strncmp(SDL_env[i], name, len) == 0) &&
                 (SDL_env[i][len] == '=')) {
                 value = &SDL_env[i][len + 1];
+                break;
             }
         }
     }

+ 46 - 17
test/testautomation_stdlib.c

@@ -624,7 +624,7 @@ static int stdlib_getsetenv(void *arg)
             text);
     }
 
-    /* Set value 1 without overwrite */
+    /* Set value 1 with overwrite */
     overwrite = 1;
     expected = value1;
     result = SDL_setenv(name, value1, overwrite);
@@ -643,6 +643,35 @@ static int stdlib_getsetenv(void *arg)
             text);
     }
 
+    /* Verify setenv() with empty string vs unsetenv() */
+    result = SDL_setenv("FOO", "1", 1);
+    SDLTest_AssertPass("Call to SDL_setenv('FOO','1', 1)");
+    SDLTest_AssertCheck(result == 0, "Check result, expected: 0, got: %i", result);
+    expected = "1";
+    text = SDL_getenv("FOO");
+    SDLTest_AssertPass("Call to SDL_getenv('FOO')");
+    SDLTest_AssertCheck(text && SDL_strcmp(text, expected) == 0, "Verify returned text, expected: %s, got: %s", expected, text);
+    result = SDL_setenv("FOO", "", 1);
+    SDLTest_AssertPass("Call to SDL_setenv('FOO','', 1)");
+    SDLTest_AssertCheck(result == 0, "Check result, expected: 0, got: %i", result);
+    expected = "";
+    text = SDL_getenv("FOO");
+    SDLTest_AssertPass("Call to SDL_getenv('FOO')");
+    SDLTest_AssertCheck(text && SDL_strcmp(text, expected) == 0, "Verify returned text, expected: '%s', got: '%s'", expected, text);
+    result = SDL_unsetenv("FOO");
+    SDLTest_AssertPass("Call to SDL_unsetenv('FOO')");
+    SDLTest_AssertCheck(result == 0, "Check result, expected: 0, got: %i", result);
+    text = SDL_getenv("FOO");
+    SDLTest_AssertPass("Call to SDL_getenv('FOO')");
+    SDLTest_AssertCheck(text == NULL, "Verify returned text, expected: (null), got: %s", text);
+    result = SDL_setenv("FOO", "0", 0);
+    SDLTest_AssertPass("Call to SDL_setenv('FOO','0', 0)");
+    SDLTest_AssertCheck(result == 0, "Check result, expected: 0, got: %i", result);
+    expected = "0";
+    text = SDL_getenv("FOO");
+    SDLTest_AssertPass("Call to SDL_getenv('FOO')");
+    SDLTest_AssertCheck(text && SDL_strcmp(text, expected) == 0, "Verify returned text, expected: %s, got: %s", expected, text);
+
     /* Negative cases */
     for (overwrite = 0; overwrite <= 1; overwrite++) {
         result = SDL_setenv(NULL, value1, overwrite);
@@ -1026,35 +1055,35 @@ stdlib_overflow(void *arg)
 /* ================= Test References ================== */
 
 /* Standard C routine test cases */
-static const SDLTest_TestCaseReference stdlibTest1 = {
+static const SDLTest_TestCaseReference stdlibTest_strnlen = {
     stdlib_strnlen, "stdlib_strnlen", "Call to SDL_strnlen", TEST_ENABLED
 };
 
-static const SDLTest_TestCaseReference stdlibTest2 = {
+static const SDLTest_TestCaseReference stdlibTest_strlcpy = {
     stdlib_strlcpy, "stdlib_strlcpy", "Call to SDL_strlcpy", TEST_ENABLED
 };
 
-static const SDLTest_TestCaseReference stdlibTest3 = {
+static const SDLTest_TestCaseReference stdlibTest_strstr = {
     stdlib_strstr, "stdlib_strstr", "Call to SDL_strstr", TEST_ENABLED
 };
 
-static const SDLTest_TestCaseReference stdlibTest4 = {
+static const SDLTest_TestCaseReference stdlibTest_snprintf = {
     stdlib_snprintf, "stdlib_snprintf", "Call to SDL_snprintf", TEST_ENABLED
 };
 
-static const SDLTest_TestCaseReference stdlibTest5 = {
+static const SDLTest_TestCaseReference stdlibTest_swprintf = {
     stdlib_swprintf, "stdlib_swprintf", "Call to SDL_swprintf", TEST_ENABLED
 };
 
-static const SDLTest_TestCaseReference stdlibTest6 = {
+static const SDLTest_TestCaseReference stdlibTest_getsetenv = {
     stdlib_getsetenv, "stdlib_getsetenv", "Call to SDL_getenv and SDL_setenv", TEST_ENABLED
 };
 
-static const SDLTest_TestCaseReference stdlibTest7 = {
+static const SDLTest_TestCaseReference stdlibTest_sscanf = {
     stdlib_sscanf, "stdlib_sscanf", "Call to SDL_sscanf", TEST_ENABLED
 };
 
-static const SDLTest_TestCaseReference stdlibTest8 = {
+static const SDLTest_TestCaseReference stdlibTest_aligned_alloc = {
     stdlib_aligned_alloc, "stdlib_aligned_alloc", "Call to SDL_aligned_alloc", TEST_ENABLED
 };
 
@@ -1064,14 +1093,14 @@ static const SDLTest_TestCaseReference stdlibTestOverflow = {
 
 /* Sequence of Standard C routine test cases */
 static const SDLTest_TestCaseReference *stdlibTests[] = {
-    &stdlibTest1,
-    &stdlibTest2,
-    &stdlibTest3,
-    &stdlibTest4,
-    &stdlibTest5,
-    &stdlibTest6,
-    &stdlibTest7,
-    &stdlibTest8,
+    &stdlibTest_strnlen,
+    &stdlibTest_strlcpy,
+    &stdlibTest_strstr,
+    &stdlibTest_snprintf,
+    &stdlibTest_swprintf,
+    &stdlibTest_getsetenv,
+    &stdlibTest_sscanf,
+    &stdlibTest_aligned_alloc,
     &stdlibTestOverflow,
     NULL
 };