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