mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-09-21 02:38:14 +00:00
Added thread-safe environment functions
Also marked the existing functions as unsafe, as they can cause crashes if used in multi-threaded applications. As a bonus, since the new functions are hashtable based, hint environment lookups are much faster.
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include "SDL_getenv_c.h"
|
||||
#include "../SDL_hashtable.h"
|
||||
|
||||
#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
|
||||
#include "../core/windows/SDL_windows.h"
|
||||
@@ -40,11 +41,23 @@
|
||||
#define HAVE_LOCAL_ENVIRONMENT
|
||||
#endif
|
||||
|
||||
#if !defined(SDL_PLATFORM_WINDOWS)
|
||||
#if defined(SDL_PLATFORM_MACOS)
|
||||
#include <crt_externs.h>
|
||||
#define environ (*_NSGetEnviron())
|
||||
#elif defined(SDL_PLATFORM_FREEBSD)
|
||||
#include <dlfcn.h>
|
||||
#define environ ((char **)dlsym(RTLD_DEFAULT, "environ"))
|
||||
#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(const char *name, const char *value, int overwrite)
|
||||
int SDL_setenv_unsafe(const char *name, const char *value, int overwrite)
|
||||
{
|
||||
// Input validation
|
||||
if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL || !value) {
|
||||
@@ -55,7 +68,7 @@ int SDL_setenv(const char *name, const char *value, int overwrite)
|
||||
}
|
||||
// We have a real environment table, but no real setenv? Fake it w/ putenv.
|
||||
#else
|
||||
int SDL_setenv(const char *name, const char *value, int overwrite)
|
||||
int SDL_setenv_unsafe(const char *name, const char *value, int overwrite)
|
||||
{
|
||||
char *new_variable;
|
||||
|
||||
@@ -79,7 +92,7 @@ int SDL_setenv(const char *name, const char *value, int overwrite)
|
||||
}
|
||||
#endif
|
||||
#elif defined(HAVE_WIN32_ENVIRONMENT)
|
||||
int SDL_setenv(const char *name, const char *value, int overwrite)
|
||||
int SDL_setenv_unsafe(const char *name, const char *value, int overwrite)
|
||||
{
|
||||
// Input validation
|
||||
if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL || !value) {
|
||||
@@ -101,7 +114,7 @@ int SDL_setenv(const char *name, const char *value, int overwrite)
|
||||
// We'll leak this, as environment variables are intended to persist past SDL_Quit()
|
||||
static char **SDL_env;
|
||||
|
||||
int SDL_setenv(const char *name, const char *value, int overwrite)
|
||||
int SDL_setenv_unsafe(const char *name, const char *value, int overwrite)
|
||||
{
|
||||
int added;
|
||||
size_t len, i;
|
||||
@@ -114,7 +127,7 @@ int SDL_setenv(const char *name, const char *value, int overwrite)
|
||||
}
|
||||
|
||||
// See if it already exists
|
||||
if (!overwrite && SDL_getenv(name)) {
|
||||
if (!overwrite && SDL_getenv_unsafe(name)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -164,7 +177,7 @@ int SDL_setenv(const char *name, const char *value, int overwrite)
|
||||
|
||||
#ifdef HAVE_LIBC_ENVIRONMENT
|
||||
#if defined(HAVE_UNSETENV)
|
||||
int SDL_unsetenv(const char *name)
|
||||
int SDL_unsetenv_unsafe(const char *name)
|
||||
{
|
||||
// Input validation
|
||||
if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) {
|
||||
@@ -175,7 +188,7 @@ int SDL_unsetenv(const char *name)
|
||||
}
|
||||
// We have a real environment table, but no unsetenv? Fake it w/ putenv.
|
||||
#else
|
||||
int SDL_unsetenv(const char *name)
|
||||
int SDL_unsetenv_unsafe(const char *name)
|
||||
{
|
||||
// Input validation
|
||||
if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) {
|
||||
@@ -187,7 +200,7 @@ int SDL_unsetenv(const char *name)
|
||||
}
|
||||
#endif
|
||||
#elif defined(HAVE_WIN32_ENVIRONMENT)
|
||||
int SDL_unsetenv(const char *name)
|
||||
int SDL_unsetenv_unsafe(const char *name)
|
||||
{
|
||||
// Input validation
|
||||
if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) {
|
||||
@@ -200,7 +213,7 @@ int SDL_unsetenv(const char *name)
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int SDL_unsetenv(const char *name)
|
||||
int SDL_unsetenv_unsafe(const char *name)
|
||||
{
|
||||
size_t len, i;
|
||||
|
||||
@@ -226,7 +239,7 @@ int SDL_unsetenv(const char *name)
|
||||
|
||||
// Retrieve a variable named "name" from the environment
|
||||
#ifdef HAVE_LIBC_ENVIRONMENT
|
||||
const char *SDL_getenv(const char *name)
|
||||
const char *SDL_getenv_unsafe(const char *name)
|
||||
{
|
||||
#ifdef SDL_PLATFORM_ANDROID
|
||||
// Make sure variables from the application manifest are available
|
||||
@@ -241,7 +254,7 @@ const char *SDL_getenv(const char *name)
|
||||
return getenv(name);
|
||||
}
|
||||
#elif defined(HAVE_WIN32_ENVIRONMENT)
|
||||
const char *SDL_getenv(const char *name)
|
||||
const char *SDL_getenv_unsafe(const char *name)
|
||||
{
|
||||
DWORD length, maxlen = 0;
|
||||
char *string = NULL;
|
||||
@@ -280,7 +293,7 @@ const char *SDL_getenv(const char *name)
|
||||
return result;
|
||||
}
|
||||
#else
|
||||
const char *SDL_getenv(const char *name)
|
||||
const char *SDL_getenv_unsafe(const char *name)
|
||||
{
|
||||
size_t len, i;
|
||||
char *value;
|
||||
@@ -304,3 +317,244 @@ const char *SDL_getenv(const char *name)
|
||||
return value;
|
||||
}
|
||||
#endif // HAVE_LIBC_ENVIRONMENT
|
||||
|
||||
|
||||
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(false);
|
||||
}
|
||||
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 empty)
|
||||
{
|
||||
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 (!empty) {
|
||||
#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;
|
||||
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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user