Simple Directmedia Layer
at main 16 kB view raw
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22 23#include "SDL_getenv_c.h" 24 25#if defined(SDL_PLATFORM_WINDOWS) 26#include "../core/windows/SDL_windows.h" 27#endif 28 29#ifdef SDL_PLATFORM_ANDROID 30#include "../core/android/SDL_android.h" 31#endif 32 33#if defined(SDL_PLATFORM_WINDOWS) 34#define HAVE_WIN32_ENVIRONMENT 35#elif defined(HAVE_GETENV) && \ 36 (defined(HAVE_SETENV) || defined(HAVE_PUTENV)) && \ 37 (defined(HAVE_UNSETENV) || defined(HAVE_PUTENV)) 38#define HAVE_LIBC_ENVIRONMENT 39#if defined(SDL_PLATFORM_MACOS) 40#include <crt_externs.h> 41#define environ (*_NSGetEnviron()) 42#elif defined(SDL_PLATFORM_FREEBSD) 43#include <dlfcn.h> 44#define environ ((char **)dlsym(RTLD_DEFAULT, "environ")) 45#else 46extern char **environ; 47#endif 48#else 49#define HAVE_LOCAL_ENVIRONMENT 50static char **environ; 51#endif 52 53 54struct SDL_Environment 55{ 56 SDL_Mutex *lock; 57 SDL_HashTable *strings; 58}; 59static SDL_Environment *SDL_environment; 60 61SDL_Environment *SDL_GetEnvironment(void) 62{ 63 if (!SDL_environment) { 64 SDL_environment = SDL_CreateEnvironment(true); 65 } 66 return SDL_environment; 67} 68 69bool SDL_InitEnvironment(void) 70{ 71 return (SDL_GetEnvironment() != NULL); 72} 73 74void SDL_QuitEnvironment(void) 75{ 76 SDL_Environment *env = SDL_environment; 77 78 if (env) { 79 SDL_environment = NULL; 80 SDL_DestroyEnvironment(env); 81 } 82} 83 84SDL_Environment *SDL_CreateEnvironment(bool populated) 85{ 86 SDL_Environment *env = SDL_calloc(1, sizeof(*env)); 87 if (!env) { 88 return NULL; 89 } 90 91 env->strings = SDL_CreateHashTable(NULL, 16, SDL_HashString, SDL_KeyMatchString, SDL_NukeFreeKey, false, false); 92 if (!env->strings) { 93 SDL_free(env); 94 return NULL; 95 } 96 97 // Don't fail if we can't create a mutex (e.g. on a single-thread environment) 98 env->lock = SDL_CreateMutex(); 99 100 if (populated) { 101#ifdef SDL_PLATFORM_WINDOWS 102 LPWCH strings = GetEnvironmentStringsW(); 103 if (strings) { 104 for (LPWCH string = strings; *string; string += SDL_wcslen(string) + 1) { 105 char *variable = WIN_StringToUTF8W(string); 106 if (!variable) { 107 continue; 108 } 109 110 char *value = SDL_strchr(variable, '='); 111 if (!value || value == variable) { 112 SDL_free(variable); 113 continue; 114 } 115 *value++ = '\0'; 116 117 SDL_InsertIntoHashTable(env->strings, variable, value); 118 } 119 FreeEnvironmentStringsW(strings); 120 } 121#else 122#ifdef SDL_PLATFORM_ANDROID 123 // Make sure variables from the application manifest are available 124 Android_JNI_GetManifestEnvironmentVariables(); 125#endif 126 char **strings = environ; 127 if (strings) { 128 for (int i = 0; strings[i]; ++i) { 129 char *variable = SDL_strdup(strings[i]); 130 if (!variable) { 131 continue; 132 } 133 134 char *value = SDL_strchr(variable, '='); 135 if (!value || value == variable) { 136 SDL_free(variable); 137 continue; 138 } 139 *value++ = '\0'; 140 141 SDL_InsertIntoHashTable(env->strings, variable, value); 142 } 143 } 144#endif // SDL_PLATFORM_WINDOWS 145 } 146 147 return env; 148} 149 150const char *SDL_GetEnvironmentVariable(SDL_Environment *env, const char *name) 151{ 152 const char *result = NULL; 153 154 if (!env) { 155 return NULL; 156 } else if (!name || *name == '\0') { 157 return NULL; 158 } 159 160 SDL_LockMutex(env->lock); 161 { 162 const char *value; 163 164 if (SDL_FindInHashTable(env->strings, name, (const void **)&value)) { 165 result = SDL_GetPersistentString(value); 166 } 167 } 168 SDL_UnlockMutex(env->lock); 169 170 return result; 171} 172 173char **SDL_GetEnvironmentVariables(SDL_Environment *env) 174{ 175 char **result = NULL; 176 177 if (!env) { 178 SDL_InvalidParamError("env"); 179 return NULL; 180 } 181 182 SDL_LockMutex(env->lock); 183 { 184 size_t count, length = 0; 185 void *iter; 186 const char *key, *value; 187 188 // First pass, get the size we need for all the strings 189 count = 0; 190 iter = NULL; 191 while (SDL_IterateHashTable(env->strings, (const void **)&key, (const void **)&value, &iter)) { 192 length += SDL_strlen(key) + 1 + SDL_strlen(value) + 1; 193 ++count; 194 } 195 196 // Allocate memory for the strings 197 result = (char **)SDL_malloc((count + 1) * sizeof(*result) + length); 198 char *string = (char *)(result + count + 1); 199 200 // Second pass, copy the strings 201 count = 0; 202 iter = NULL; 203 while (SDL_IterateHashTable(env->strings, (const void **)&key, (const void **)&value, &iter)) { 204 size_t len; 205 206 result[count] = string; 207 len = SDL_strlen(key); 208 SDL_memcpy(string, key, len); 209 string += len; 210 *string++ = '='; 211 len = SDL_strlen(value); 212 SDL_memcpy(string, value, len); 213 string += len; 214 *string++ = '\0'; 215 ++count; 216 } 217 result[count] = NULL; 218 } 219 SDL_UnlockMutex(env->lock); 220 221 return result; 222} 223 224bool SDL_SetEnvironmentVariable(SDL_Environment *env, const char *name, const char *value, bool overwrite) 225{ 226 bool result = false; 227 228 if (!env) { 229 return SDL_InvalidParamError("env"); 230 } else if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) { 231 return SDL_InvalidParamError("name"); 232 } else if (!value) { 233 return SDL_InvalidParamError("value"); 234 } 235 236 SDL_LockMutex(env->lock); 237 { 238 const void *existing_value; 239 bool insert = true; 240 241 if (SDL_FindInHashTable(env->strings, name, &existing_value)) { 242 if (!overwrite) { 243 result = true; 244 insert = false; 245 } else { 246 SDL_RemoveFromHashTable(env->strings, name); 247 } 248 } 249 250 if (insert) { 251 char *string = NULL; 252 if (SDL_asprintf(&string, "%s=%s", name, value) > 0) { 253 size_t len = SDL_strlen(name); 254 string[len] = '\0'; 255 name = string; 256 value = string + len + 1; 257 result = SDL_InsertIntoHashTable(env->strings, name, value); 258 } 259 } 260 } 261 SDL_UnlockMutex(env->lock); 262 263 return result; 264} 265 266bool SDL_UnsetEnvironmentVariable(SDL_Environment *env, const char *name) 267{ 268 bool result = false; 269 270 if (!env) { 271 return SDL_InvalidParamError("env"); 272 } else if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) { 273 return SDL_InvalidParamError("name"); 274 } 275 276 SDL_LockMutex(env->lock); 277 { 278 const void *value; 279 if (SDL_FindInHashTable(env->strings, name, &value)) { 280 result = SDL_RemoveFromHashTable(env->strings, name); 281 } else { 282 result = true; 283 } 284 } 285 SDL_UnlockMutex(env->lock); 286 287 return result; 288} 289 290void SDL_DestroyEnvironment(SDL_Environment *env) 291{ 292 if (!env || env == SDL_environment) { 293 return; 294 } 295 296 SDL_DestroyMutex(env->lock); 297 SDL_DestroyHashTable(env->strings); 298 SDL_free(env); 299} 300 301// Put a variable into the environment 302// Note: Name may not contain a '=' character. (Reference: http://www.unix.com/man-page/Linux/3/setenv/) 303#ifdef HAVE_LIBC_ENVIRONMENT 304#if defined(HAVE_SETENV) 305int SDL_setenv_unsafe(const char *name, const char *value, int overwrite) 306{ 307 // Input validation 308 if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL || !value) { 309 return -1; 310 } 311 312 SDL_SetEnvironmentVariable(SDL_GetEnvironment(), name, value, (overwrite != 0)); 313 314 return setenv(name, value, overwrite); 315} 316// We have a real environment table, but no real setenv? Fake it w/ putenv. 317#else 318int SDL_setenv_unsafe(const char *name, const char *value, int overwrite) 319{ 320 char *new_variable; 321 322 // Input validation 323 if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL || !value) { 324 return -1; 325 } 326 327 SDL_SetEnvironmentVariable(SDL_GetEnvironment(), name, value, (overwrite != 0)); 328 329 if (getenv(name) != NULL) { 330 if (!overwrite) { 331 return 0; // leave the existing one there. 332 } 333 } 334 335 // This leaks. Sorry. Get a better OS so we don't have to do this. 336 SDL_asprintf(&new_variable, "%s=%s", name, value); 337 if (!new_variable) { 338 return -1; 339 } 340 return putenv(new_variable); 341} 342#endif 343#elif defined(HAVE_WIN32_ENVIRONMENT) 344int SDL_setenv_unsafe(const char *name, const char *value, int overwrite) 345{ 346 // Input validation 347 if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL || !value) { 348 return -1; 349 } 350 351 SDL_SetEnvironmentVariable(SDL_GetEnvironment(), name, value, (overwrite != 0)); 352 353 if (!overwrite) { 354 if (GetEnvironmentVariableA(name, NULL, 0) > 0) { 355 return 0; // asked not to overwrite existing value. 356 } 357 } 358 if (!SetEnvironmentVariableA(name, value)) { 359 return -1; 360 } 361 return 0; 362} 363#else // roll our own 364 365int SDL_setenv_unsafe(const char *name, const char *value, int overwrite) 366{ 367 int added; 368 size_t len, i; 369 char **new_env; 370 char *new_variable; 371 372 // Input validation 373 if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL || !value) { 374 return -1; 375 } 376 377 // See if it already exists 378 if (!overwrite && SDL_getenv_unsafe(name)) { 379 return 0; 380 } 381 382 SDL_SetEnvironmentVariable(SDL_GetEnvironment(), name, value, (overwrite != 0)); 383 384 // Allocate memory for the variable 385 len = SDL_strlen(name) + SDL_strlen(value) + 2; 386 new_variable = (char *)SDL_malloc(len); 387 if (!new_variable) { 388 return -1; 389 } 390 391 SDL_snprintf(new_variable, len, "%s=%s", name, value); 392 value = new_variable + SDL_strlen(name) + 1; 393 name = new_variable; 394 395 // Actually put it into the environment 396 added = 0; 397 i = 0; 398 if (environ) { 399 // Check to see if it's already there... 400 len = (value - name); 401 for (; environ[i]; ++i) { 402 if (SDL_strncmp(environ[i], name, len) == 0) { 403 // If we found it, just replace the entry 404 SDL_free(environ[i]); 405 environ[i] = new_variable; 406 added = 1; 407 break; 408 } 409 } 410 } 411 412 // Didn't find it in the environment, expand and add 413 if (!added) { 414 new_env = SDL_realloc(environ, (i + 2) * sizeof(char *)); 415 if (new_env) { 416 environ = new_env; 417 environ[i++] = new_variable; 418 environ[i++] = (char *)0; 419 added = 1; 420 } else { 421 SDL_free(new_variable); 422 } 423 } 424 return added ? 0 : -1; 425} 426#endif // HAVE_LIBC_ENVIRONMENT 427 428#ifdef HAVE_LIBC_ENVIRONMENT 429#if defined(HAVE_UNSETENV) 430int SDL_unsetenv_unsafe(const char *name) 431{ 432 // Input validation 433 if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) { 434 return -1; 435 } 436 437 SDL_UnsetEnvironmentVariable(SDL_GetEnvironment(), name); 438 439 return unsetenv(name); 440} 441// We have a real environment table, but no unsetenv? Fake it w/ putenv. 442#else 443int SDL_unsetenv_unsafe(const char *name) 444{ 445 // Input validation 446 if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) { 447 return -1; 448 } 449 450 SDL_UnsetEnvironmentVariable(SDL_GetEnvironment(), name); 451 452 // Hope this environment uses the non-standard extension of removing the environment variable if it has no '=' 453 return putenv(name); 454} 455#endif 456#elif defined(HAVE_WIN32_ENVIRONMENT) 457int SDL_unsetenv_unsafe(const char *name) 458{ 459 // Input validation 460 if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) { 461 return -1; 462 } 463 464 SDL_UnsetEnvironmentVariable(SDL_GetEnvironment(), name); 465 466 if (!SetEnvironmentVariableA(name, NULL)) { 467 return -1; 468 } 469 return 0; 470} 471#else 472int SDL_unsetenv_unsafe(const char *name) 473{ 474 size_t len, i; 475 476 // Input validation 477 if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) { 478 return -1; 479 } 480 481 SDL_UnsetEnvironmentVariable(SDL_GetEnvironment(), name); 482 483 if (environ) { 484 len = SDL_strlen(name); 485 for (i = 0; environ[i]; ++i) { 486 if ((SDL_strncmp(environ[i], name, len) == 0) && 487 (environ[i][len] == '=')) { 488 // Just clear out this entry for now 489 *environ[i] = '\0'; 490 break; 491 } 492 } 493 } 494 return 0; 495} 496#endif // HAVE_LIBC_ENVIRONMENT 497 498// Retrieve a variable named "name" from the environment 499#ifdef HAVE_LIBC_ENVIRONMENT 500const char *SDL_getenv_unsafe(const char *name) 501{ 502#ifdef SDL_PLATFORM_ANDROID 503 // Make sure variables from the application manifest are available 504 Android_JNI_GetManifestEnvironmentVariables(); 505#endif 506 507 // Input validation 508 if (!name || *name == '\0') { 509 return NULL; 510 } 511 512 return getenv(name); 513} 514#elif defined(HAVE_WIN32_ENVIRONMENT) 515const char *SDL_getenv_unsafe(const char *name) 516{ 517 DWORD length, maxlen = 0; 518 char *string = NULL; 519 const char *result = NULL; 520 521 // Input validation 522 if (!name || *name == '\0') { 523 return NULL; 524 } 525 526 for ( ; ; ) { 527 SetLastError(ERROR_SUCCESS); 528 length = GetEnvironmentVariableA(name, string, maxlen); 529 530 if (length > maxlen) { 531 char *temp = (char *)SDL_realloc(string, length); 532 if (!temp) { 533 return NULL; 534 } 535 string = temp; 536 maxlen = length; 537 } else { 538 if (GetLastError() != ERROR_SUCCESS) { 539 if (string) { 540 SDL_free(string); 541 } 542 return NULL; 543 } 544 break; 545 } 546 } 547 if (string) { 548 result = SDL_GetPersistentString(string); 549 SDL_free(string); 550 } 551 return result; 552} 553#else 554const char *SDL_getenv_unsafe(const char *name) 555{ 556 size_t len, i; 557 const char *value = NULL; 558 559 // Input validation 560 if (!name || *name == '\0') { 561 return NULL; 562 } 563 564 if (environ) { 565 len = SDL_strlen(name); 566 for (i = 0; environ[i]; ++i) { 567 if ((SDL_strncmp(environ[i], name, len) == 0) && 568 (environ[i][len] == '=')) { 569 value = &environ[i][len + 1]; 570 break; 571 } 572 } 573 } 574 return value; 575} 576#endif // HAVE_LIBC_ENVIRONMENT 577 578const char *SDL_getenv(const char *name) 579{ 580 return SDL_GetEnvironmentVariable(SDL_GetEnvironment(), name); 581}