diff --git a/include/SDL3/SDL_stdinc.h b/include/SDL3/SDL_stdinc.h index 96cc643a26..83411d3e20 100644 --- a/include/SDL3/SDL_stdinc.h +++ b/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); diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake index 0e63c1d645..3f01525e9a 100644 --- a/include/build_config/SDL_build_config.h.cmake +++ b/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 diff --git a/include/build_config/SDL_build_config_android.h b/include/build_config/SDL_build_config_android.h index 098e2f5052..3716c1550f 100644 --- a/include/build_config/SDL_build_config_android.h +++ b/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 diff --git a/include/build_config/SDL_build_config_ios.h b/include/build_config/SDL_build_config_ios.h index 93bf3abea8..430a984173 100644 --- a/include/build_config/SDL_build_config_ios.h +++ b/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 diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index 0d518a4127..8d789ac741 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/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; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 5b87ee5ca6..8a17b98052 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/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 diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 341c70c38f..b93fe03451 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/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) diff --git a/src/stdlib/SDL_getenv.c b/src/stdlib/SDL_getenv.c index c37568ab87..3deea8c681 100644 --- a/src/stdlib/SDL_getenv.c +++ b/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; } } } diff --git a/test/testautomation_stdlib.c b/test/testautomation_stdlib.c index c489c58463..d7072f03c6 100644 --- a/test/testautomation_stdlib.c +++ b/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 };