Atlas - SDL_getenv.c
Home / ext / SDL / src / stdlib Lines: 1 | Size: 16997 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 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> 44static char **get_environ_rtld(void) 45{ 46 char ***environ_rtld = (char ***)dlsym(RTLD_DEFAULT, "environ"); 47 return environ_rtld ? *environ_rtld : NULL; 48} 49#define environ (get_environ_rtld()) 50#else 51extern char **environ; 52#endif 53#else 54#define HAVE_LOCAL_ENVIRONMENT 55static char **environ; 56#endif 57 58 59struct SDL_Environment 60{ 61 SDL_Mutex *lock; // !!! FIXME: reuse SDL_HashTable's lock. 62 SDL_HashTable *strings; 63}; 64static SDL_Environment *SDL_environment; 65 66SDL_Environment *SDL_GetEnvironment(void) 67{ 68 if (!SDL_environment) { 69 SDL_environment = SDL_CreateEnvironment(true); 70 } 71 return SDL_environment; 72} 73 74bool SDL_InitEnvironment(void) 75{ 76 return (SDL_GetEnvironment() != NULL); 77} 78 79void SDL_QuitEnvironment(void) 80{ 81 SDL_Environment *env = SDL_environment; 82 83 if (env) { 84 SDL_environment = NULL; 85 SDL_DestroyEnvironment(env); 86 } 87} 88 89SDL_Environment *SDL_CreateEnvironment(bool populated) 90{ 91 SDL_Environment *env = SDL_calloc(1, sizeof(*env)); 92 if (!env) { 93 return NULL; 94 } 95 96 env->strings = SDL_CreateHashTable(0, false, SDL_HashString, SDL_KeyMatchString, SDL_DestroyHashKey, NULL); 97 if (!env->strings) { 98 SDL_free(env); 99 return NULL; 100 } 101 102 // Don't fail if we can't create a mutex (e.g. on a single-thread environment) // !!! FIXME: single-threaded environments should still return a non-NULL, do-nothing object here. Check for failure! 103 env->lock = SDL_CreateMutex(); 104 105 if (populated) { 106#ifdef SDL_PLATFORM_WINDOWS 107 LPWCH strings = GetEnvironmentStringsW(); 108 if (strings) { 109 for (LPWCH string = strings; *string; string += SDL_wcslen(string) + 1) { 110 char *variable = WIN_StringToUTF8W(string); 111 if (!variable) { 112 continue; 113 } 114 115 char *value = SDL_strchr(variable, '='); 116 if (!value || value == variable) { 117 SDL_free(variable); 118 continue; 119 } 120 *value++ = '\0'; 121 122 SDL_InsertIntoHashTable(env->strings, variable, value, true); 123 } 124 FreeEnvironmentStringsW(strings); 125 } 126#else 127#ifdef SDL_PLATFORM_ANDROID 128 // Make sure variables from the application manifest are available 129 Android_JNI_GetManifestEnvironmentVariables(); 130#endif 131 char **strings = environ; 132 if (strings) { 133 for (int i = 0; strings[i]; ++i) { 134 char *variable = SDL_strdup(strings[i]); 135 if (!variable) { 136 continue; 137 } 138 139 char *value = SDL_strchr(variable, '='); 140 if (!value || value == variable) { 141 SDL_free(variable); 142 continue; 143 } 144 *value++ = '\0'; 145 146 SDL_InsertIntoHashTable(env->strings, variable, value, true); 147 } 148 } 149#endif // SDL_PLATFORM_WINDOWS 150 } 151 152 return env; 153} 154 155const char *SDL_GetEnvironmentVariable(SDL_Environment *env, const char *name) 156{ 157 const char *result = NULL; 158 159 if (!env) { 160 return NULL; 161 } else if (!name || *name == '\0') { 162 return NULL; 163 } 164 165 SDL_LockMutex(env->lock); 166 { 167 const char *value; 168 169 if (SDL_FindInHashTable(env->strings, name, (const void **)&value)) { 170 result = SDL_GetPersistentString(value); 171 } 172 } 173 SDL_UnlockMutex(env->lock); 174 175 return result; 176} 177 178typedef struct CountEnvStringsData 179{ 180 size_t count; 181 size_t length; 182} CountEnvStringsData; 183 184static bool SDLCALL CountEnvStrings(void *userdata, const SDL_HashTable *table, const void *key, const void *value) 185{ 186 CountEnvStringsData *data = (CountEnvStringsData *) userdata; 187 data->length += SDL_strlen((const char *) key) + 1 + SDL_strlen((const char *) value) + 1; 188 data->count++; 189 return true; // keep iterating. 190} 191 192typedef struct CopyEnvStringsData 193{ 194 char **result; 195 char *string; 196 size_t count; 197} CopyEnvStringsData; 198 199static bool SDLCALL CopyEnvStrings(void *userdata, const SDL_HashTable *table, const void *vkey, const void *vvalue) 200{ 201 CopyEnvStringsData *data = (CopyEnvStringsData *) userdata; 202 const char *key = (const char *) vkey; 203 const char *value = (const char *) vvalue; 204 size_t len; 205 206 len = SDL_strlen(key); 207 data->result[data->count] = data->string; 208 SDL_memcpy(data->string, key, len); 209 data->string += len; 210 *(data->string++) = '='; 211 212 len = SDL_strlen(value); 213 SDL_memcpy(data->string, value, len); 214 data->string += len; 215 *(data->string++) = '\0'; 216 data->count++; 217 218 return true; // keep iterating. 219} 220 221char **SDL_GetEnvironmentVariables(SDL_Environment *env) 222{ 223 char **result = NULL; 224 225 CHECK_PARAM(!env) { 226 SDL_InvalidParamError("env"); 227 return NULL; 228 } 229 230 SDL_LockMutex(env->lock); 231 { 232 // First pass, get the size we need for all the strings 233 CountEnvStringsData countdata = { 0, 0 }; 234 SDL_IterateHashTable(env->strings, CountEnvStrings, &countdata); 235 236 // Allocate memory for the strings 237 result = (char **)SDL_malloc((countdata.count + 1) * sizeof(*result) + countdata.length); 238 if (result) { 239 // Second pass, copy the strings 240 char *string = (char *)(result + countdata.count + 1); 241 CopyEnvStringsData cpydata = { result, string, 0 }; 242 SDL_IterateHashTable(env->strings, CopyEnvStrings, &cpydata); 243 SDL_assert(countdata.count == cpydata.count); 244 result[cpydata.count] = NULL; 245 } 246 } 247 SDL_UnlockMutex(env->lock); 248 249 return result; 250} 251 252bool SDL_SetEnvironmentVariable(SDL_Environment *env, const char *name, const char *value, bool overwrite) 253{ 254 bool result = false; 255 256 CHECK_PARAM(!env) { 257 return SDL_InvalidParamError("env"); 258 } 259 CHECK_PARAM(!name || *name == '\0' || SDL_strchr(name, '=') != NULL) { 260 return SDL_InvalidParamError("name"); 261 } 262 CHECK_PARAM(!value) { 263 return SDL_InvalidParamError("value"); 264 } 265 266 SDL_LockMutex(env->lock); 267 { 268 char *string = NULL; 269 if (SDL_asprintf(&string, "%s=%s", name, value) > 0) { 270 const size_t len = SDL_strlen(name); 271 string[len] = '\0'; 272 const char *origname = name; 273 name = string; 274 value = string + len + 1; 275 result = SDL_InsertIntoHashTable(env->strings, name, value, overwrite); 276 if (!result) { 277 SDL_free(string); 278 if (!overwrite) { 279 const void *existing_value = NULL; 280 // !!! FIXME: InsertIntoHashTable does this lookup too, maybe we should have a means to report that, to avoid duplicate work? 281 if (SDL_FindInHashTable(env->strings, origname, &existing_value)) { 282 result = true; // it already existed, and we refused to overwrite it. Call it success. 283 } 284 } 285 } 286 } 287 } 288 SDL_UnlockMutex(env->lock); 289 290 return result; 291} 292 293bool SDL_UnsetEnvironmentVariable(SDL_Environment *env, const char *name) 294{ 295 bool result = false; 296 297 CHECK_PARAM(!env) { 298 return SDL_InvalidParamError("env"); 299 } 300 CHECK_PARAM(!name || *name == '\0' || SDL_strchr(name, '=') != NULL) { 301 return SDL_InvalidParamError("name"); 302 } 303 304 SDL_LockMutex(env->lock); 305 { 306 const void *value; 307 if (SDL_FindInHashTable(env->strings, name, &value)) { 308 result = SDL_RemoveFromHashTable(env->strings, name); 309 } else { 310 result = true; 311 } 312 } 313 SDL_UnlockMutex(env->lock); 314 315 return result; 316} 317 318void SDL_DestroyEnvironment(SDL_Environment *env) 319{ 320 if (!env || env == SDL_environment) { 321 return; 322 } 323 324 SDL_DestroyMutex(env->lock); 325 SDL_DestroyHashTable(env->strings); 326 SDL_free(env); 327} 328 329// Put a variable into the environment 330// Note: Name may not contain a '=' character. (Reference: http://www.unix.com/man-page/Linux/3/setenv/) 331#ifdef HAVE_LIBC_ENVIRONMENT 332#if defined(HAVE_SETENV) 333int SDL_setenv_unsafe(const char *name, const char *value, int overwrite) 334{ 335 // Input validation 336 if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL || !value) { 337 return -1; 338 } 339 340 if (SDL_environment) { 341 SDL_SetEnvironmentVariable(SDL_environment, name, value, (overwrite != 0)); 342 } 343 344 return setenv(name, value, overwrite); 345} 346// We have a real environment table, but no real setenv? Fake it w/ putenv. 347#else 348int SDL_setenv_unsafe(const char *name, const char *value, int overwrite) 349{ 350 char *new_variable; 351 352 // Input validation 353 if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL || !value) { 354 return -1; 355 } 356 357 if (SDL_environment) { 358 SDL_SetEnvironmentVariable(SDL_environment, name, value, (overwrite != 0)); 359 } 360 361 if (getenv(name) != NULL) { 362 if (!overwrite) { 363 return 0; // leave the existing one there. 364 } 365 } 366 367 // This leaks. Sorry. Get a better OS so we don't have to do this. 368 SDL_asprintf(&new_variable, "%s=%s", name, value); 369 if (!new_variable) { 370 return -1; 371 } 372 return putenv(new_variable); 373} 374#endif 375#elif defined(HAVE_WIN32_ENVIRONMENT) 376int SDL_setenv_unsafe(const char *name, const char *value, int overwrite) 377{ 378 // Input validation 379 if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL || !value) { 380 return -1; 381 } 382 383 if (SDL_environment) { 384 SDL_SetEnvironmentVariable(SDL_environment, name, value, (overwrite != 0)); 385 } 386 387 if (!overwrite) { 388 if (GetEnvironmentVariableA(name, NULL, 0) > 0) { 389 return 0; // asked not to overwrite existing value. 390 } 391 } 392 if (!SetEnvironmentVariableA(name, value)) { 393 return -1; 394 } 395 return 0; 396} 397#else // roll our own 398 399int SDL_setenv_unsafe(const char *name, const char *value, int overwrite) 400{ 401 int added; 402 size_t len, i; 403 char **new_env; 404 char *new_variable; 405 406 // Input validation 407 if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL || !value) { 408 return -1; 409 } 410 411 // See if it already exists 412 if (!overwrite && SDL_getenv_unsafe(name)) { 413 return 0; 414 } 415 416 if (SDL_environment) { 417 SDL_SetEnvironmentVariable(SDL_environment, name, value, (overwrite != 0)); 418 } 419 420 // Allocate memory for the variable 421 len = SDL_strlen(name) + SDL_strlen(value) + 2; 422 new_variable = (char *)SDL_malloc(len); 423 if (!new_variable) { 424 return -1; 425 } 426 427 SDL_snprintf(new_variable, len, "%s=%s", name, value); 428 value = new_variable + SDL_strlen(name) + 1; 429 name = new_variable; 430 431 // Actually put it into the environment 432 added = 0; 433 i = 0; 434 if (environ) { 435 // Check to see if it's already there... 436 len = (value - name); 437 for (; environ[i]; ++i) { 438 if (SDL_strncmp(environ[i], name, len) == 0) { 439 // If we found it, just replace the entry 440 SDL_free(environ[i]); 441 environ[i] = new_variable; 442 added = 1; 443 break; 444 } 445 } 446 } 447 448 // Didn't find it in the environment, expand and add 449 if (!added) { 450 new_env = SDL_realloc(environ, (i + 2) * sizeof(char *)); 451 if (new_env) { 452 environ = new_env; 453 environ[i++] = new_variable; 454 environ[i++] = (char *)0; 455 added = 1; 456 } else { 457 SDL_free(new_variable); 458 } 459 } 460 return added ? 0 : -1; 461} 462#endif // HAVE_LIBC_ENVIRONMENT 463 464#ifdef HAVE_LIBC_ENVIRONMENT 465#if defined(HAVE_UNSETENV) 466int SDL_unsetenv_unsafe(const char *name) 467{ 468 // Input validation 469 if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) { 470 return -1; 471 } 472 473 if (SDL_environment) { 474 SDL_UnsetEnvironmentVariable(SDL_environment, name); 475 } 476 477 return unsetenv(name); 478} 479// We have a real environment table, but no unsetenv? Fake it w/ putenv. 480#else 481int SDL_unsetenv_unsafe(const char *name) 482{ 483 // Input validation 484 if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) { 485 return -1; 486 } 487 488 if (SDL_environment) { 489 SDL_UnsetEnvironmentVariable(SDL_environment, name); 490 } 491 492 // Hope this environment uses the non-standard extension of removing the environment variable if it has no '=' 493 return putenv(name); 494} 495#endif 496#elif defined(HAVE_WIN32_ENVIRONMENT) 497int SDL_unsetenv_unsafe(const char *name) 498{ 499 // Input validation 500 if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) { 501 return -1; 502 } 503 504 if (SDL_environment) { 505 SDL_UnsetEnvironmentVariable(SDL_environment, name); 506 } 507 508 if (!SetEnvironmentVariableA(name, NULL)) { 509 return -1; 510 } 511 return 0; 512} 513#else 514int SDL_unsetenv_unsafe(const char *name) 515{ 516 size_t len, i; 517 518 // Input validation 519 if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) { 520 return -1; 521 } 522 523 if (SDL_environment) { 524 SDL_UnsetEnvironmentVariable(SDL_environment, name); 525 } 526 527 if (environ) { 528 len = SDL_strlen(name); 529 for (i = 0; environ[i]; ++i) { 530 if ((SDL_strncmp(environ[i], name, len) == 0) && 531 (environ[i][len] == '=')) { 532 // Just clear out this entry for now 533 *environ[i] = '\0'; 534 break; 535 } 536 } 537 } 538 return 0; 539} 540#endif // HAVE_LIBC_ENVIRONMENT 541 542// Retrieve a variable named "name" from the environment 543#ifdef HAVE_LIBC_ENVIRONMENT 544const char *SDL_getenv_unsafe(const char *name) 545{ 546#ifdef SDL_PLATFORM_ANDROID 547 // Make sure variables from the application manifest are available 548 Android_JNI_GetManifestEnvironmentVariables(); 549#endif 550 551 // Input validation 552 if (!name || *name == '\0') { 553 return NULL; 554 } 555 556 return getenv(name); 557} 558#elif defined(HAVE_WIN32_ENVIRONMENT) 559const char *SDL_getenv_unsafe(const char *name) 560{ 561 DWORD length, maxlen = 0; 562 char *string = NULL; 563 const char *result = NULL; 564 565 // Input validation 566 if (!name || *name == '\0') { 567 return NULL; 568 } 569 570 for ( ; ; ) { 571 SetLastError(ERROR_SUCCESS); 572 length = GetEnvironmentVariableA(name, string, maxlen); 573 574 if (length > maxlen) { 575 char *temp = (char *)SDL_realloc(string, length); 576 if (!temp) { 577 return NULL; 578 } 579 string = temp; 580 maxlen = length; 581 } else { 582 if (GetLastError() != ERROR_SUCCESS) { 583 SDL_free(string); 584 return NULL; 585 } 586 break; 587 } 588 } 589 if (string) { 590 result = SDL_GetPersistentString(string); 591 SDL_free(string); 592 } 593 return result; 594} 595#else 596const char *SDL_getenv_unsafe(const char *name) 597{ 598 size_t len, i; 599 const char *value = NULL; 600 601 // Input validation 602 if (!name || *name == '\0') { 603 return NULL; 604 } 605 606 if (environ) { 607 len = SDL_strlen(name); 608 for (i = 0; environ[i]; ++i) { 609 if ((SDL_strncmp(environ[i], name, len) == 0) && 610 (environ[i][len] == '=')) { 611 value = &environ[i][len + 1]; 612 break; 613 } 614 } 615 } 616 return value; 617} 618#endif // HAVE_LIBC_ENVIRONMENT 619 620const char *SDL_getenv(const char *name) 621{ 622 return SDL_GetEnvironmentVariable(SDL_GetEnvironment(), name); 623} 624[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.