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