Re-added SDL_getenv() as a thread-safe getenv() implementation

This commit is contained in:
Sam Lantinga
2024-09-15 09:16:30 -07:00
parent 718845a33d
commit 28b94c4758
28 changed files with 374 additions and 359 deletions

View File

@@ -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,272 +45,10 @@
#else
extern char **environ;
#endif
#endif // !SDL_PLATFORM_WINDOWS
// 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;
}
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;
}
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);
}
#define HAVE_LOCAL_ENVIRONMENT
static char **environ;
#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;
}
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
// 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;
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;
}
// 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 (SDL_env) {
// 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) {
// If we found it, just replace the entry
SDL_free(SDL_env[i]);
SDL_env[i] = new_variable;
added = 1;
break;
}
}
}
// Didn't find it in the environment, expand and add
if (!added) {
new_env = SDL_realloc(SDL_env, (i + 2) * sizeof(char *));
if (new_env) {
SDL_env = new_env;
SDL_env[i++] = new_variable;
SDL_env[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;
}
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;
}
// 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;
}
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;
}
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_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;
char *value;
// Input validation
if (!name || *name == '\0') {
return NULL;
}
value = (char *)0;
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] == '=')) {
value = &SDL_env[i][len + 1];
break;
}
}
}
return value;
}
#endif // HAVE_LIBC_ENVIRONMENT
struct SDL_Environment
@@ -387,20 +119,22 @@ SDL_Environment *SDL_CreateEnvironment(SDL_bool populated)
Android_JNI_GetManifestEnvironmentVariables();
#endif
char **strings = environ;
for (int i = 0; strings[i]; ++i) {
char *variable = SDL_strdup(strings[i]);
if (!variable) {
continue;
}
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';
char *value = SDL_strchr(variable, '=');
if (!value || value == variable) {
SDL_free(variable);
continue;
}
*value++ = '\0';
SDL_InsertIntoHashTable(env->strings, variable, value);
SDL_InsertIntoHashTable(env->strings, variable, value);
}
}
#endif // SDL_PLATFORM_WINDOWS
}
@@ -558,3 +292,285 @@ void SDL_DestroyEnvironment(SDL_Environment *env)
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);
}