1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5/*++
6
7
8
9Module Name:
10
11 environ.cpp
12
13Abstract:
14
15 Implementation of functions manipulating environment variables.
16
17Revision History:
18
19
20
21--*/
22
23#include "pal/palinternal.h"
24#include "pal/critsect.h"
25#include "pal/dbgmsg.h"
26#include "pal/environ.h"
27#include "pal/malloc.hpp"
28
29#if HAVE_CRT_EXTERNS_H
30#include <crt_externs.h>
31#endif
32
33#include <stdlib.h>
34
35using namespace CorUnix;
36
37SET_DEFAULT_DEBUG_CHANNEL(MISC);
38
39char **palEnvironment = nullptr;
40int palEnvironmentCount = 0;
41int palEnvironmentCapacity = 0;
42
43CRITICAL_SECTION gcsEnvironment;
44
45/*++
46Function:
47 GetEnvironmentVariableA
48
49The GetEnvironmentVariable function retrieves the value of the
50specified variable from the environment block of the calling
51process. The value is in the form of a null-terminated string of
52characters.
53
54Parameters
55
56lpName
57 [in] Pointer to a null-terminated string that specifies the environment variable.
58lpBuffer
59 [out] Pointer to a buffer to receive the value of the specified environment variable.
60nSize
61 [in] Specifies the size, in TCHARs, of the buffer pointed to by the lpBuffer parameter.
62
63Return Values
64
65If the function succeeds, the return value is the number of TCHARs
66stored into the buffer pointed to by lpBuffer, not including the
67terminating null character.
68
69If the specified environment variable name was not found in the
70environment block for the current process, the return value is zero.
71
72If the buffer pointed to by lpBuffer is not large enough, the return
73value is the buffer size, in TCHARs, required to hold the value string
74and its terminating null character.
75
76--*/
77DWORD
78PALAPI
79GetEnvironmentVariableA(
80 IN LPCSTR lpName,
81 OUT LPSTR lpBuffer,
82 IN DWORD nSize)
83{
84 char *value;
85 DWORD dwRet = 0;
86
87 PERF_ENTRY(GetEnvironmentVariableA);
88 ENTRY("GetEnvironmentVariableA(lpName=%p (%s), lpBuffer=%p, nSize=%u)\n",
89 lpName ? lpName : "NULL",
90 lpName ? lpName : "NULL", lpBuffer, nSize);
91
92 CPalThread * pthrCurrent = InternalGetCurrentThread();
93
94 if (lpName == nullptr)
95 {
96 ERROR("lpName is null\n");
97 SetLastError(ERROR_INVALID_PARAMETER);
98 goto done;
99 }
100
101 if (lpName[0] == 0)
102 {
103 TRACE("lpName is an empty string\n", lpName);
104 SetLastError(ERROR_ENVVAR_NOT_FOUND);
105 goto done;
106 }
107
108 if (strchr(lpName, '=') != nullptr)
109 {
110 // GetEnvironmentVariable doesn't permit '=' in variable names.
111 value = nullptr;
112 }
113 else
114 {
115 // Enter the environment critical section so that we can safely get
116 // the environment variable value without EnvironGetenv making an
117 // intermediate copy. We will just copy the string to the output
118 // buffer anyway, so just stay in the critical section until then.
119 InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment);
120
121 value = EnvironGetenv(lpName, /* copyValue */ FALSE);
122
123 if (value != nullptr)
124 {
125 DWORD valueLength = strlen(value);
126 if (valueLength < nSize)
127 {
128 strcpy_s(lpBuffer, nSize, value);
129 dwRet = valueLength;
130 }
131 else
132 {
133 dwRet = valueLength + 1;
134 }
135
136 SetLastError(ERROR_SUCCESS);
137 }
138
139 InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment);
140 }
141
142 if (value == nullptr)
143 {
144 TRACE("%s is not found\n", lpName);
145 SetLastError(ERROR_ENVVAR_NOT_FOUND);
146 }
147
148done:
149
150 LOGEXIT("GetEnvironmentVariableA returns DWORD 0x%x\n", dwRet);
151 PERF_EXIT(GetEnvironmentVariableA);
152 return dwRet;
153}
154
155/*++
156Function:
157 GetEnvironmentVariableW
158
159See MSDN doc.
160--*/
161DWORD
162PALAPI
163GetEnvironmentVariableW(
164 IN LPCWSTR lpName,
165 OUT LPWSTR lpBuffer,
166 IN DWORD nSize)
167{
168 CHAR *inBuff = nullptr;
169 CHAR *outBuff = nullptr;
170 INT inBuffSize;
171 DWORD size = 0;
172
173 PERF_ENTRY(GetEnvironmentVariableW);
174 ENTRY("GetEnvironmentVariableW(lpName=%p (%S), lpBuffer=%p, nSize=%u)\n",
175 lpName ? lpName : W16_NULLSTRING,
176 lpName ? lpName : W16_NULLSTRING, lpBuffer, nSize);
177
178 inBuffSize = WideCharToMultiByte(CP_ACP, 0, lpName, -1,
179 inBuff, 0, nullptr, nullptr);
180 if (0 == inBuffSize)
181 {
182 ERROR("lpName has to be a valid parameter\n");
183 SetLastError(ERROR_INVALID_PARAMETER);
184 goto done;
185 }
186
187 inBuff = (CHAR *)PAL_malloc(inBuffSize);
188 if (inBuff == nullptr)
189 {
190 ERROR("malloc failed\n");
191 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
192 goto done;
193 }
194
195 if (nSize)
196 {
197 outBuff = (CHAR *)PAL_malloc(nSize*2);
198 if (outBuff == nullptr)
199 {
200 ERROR("malloc failed\n");
201 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
202 goto done;
203 }
204 }
205
206 if (0 == WideCharToMultiByte(CP_ACP, 0, lpName, -1, inBuff,
207 inBuffSize, nullptr, nullptr))
208 {
209 ASSERT("WideCharToMultiByte failed!\n");
210 SetLastError(ERROR_INTERNAL_ERROR);
211 goto done;
212 }
213
214 size = GetEnvironmentVariableA(inBuff, outBuff, nSize);
215 if (size > nSize)
216 {
217 TRACE("Insufficient buffer\n");
218 }
219 else if (size == 0)
220 {
221 // If size is 0, it either means GetEnvironmentVariableA failed, or that
222 // it succeeded and the value of the variable is empty. Check GetLastError
223 // to determine which. If the call failed, we won't touch the buffer.
224 if (GetLastError() == ERROR_SUCCESS)
225 {
226 *lpBuffer = '\0';
227 }
228 }
229 else
230 {
231 size = MultiByteToWideChar(CP_ACP, 0, outBuff, -1, lpBuffer, nSize);
232 if (0 != size)
233 {
234 // -1 for the null.
235 size--;
236 }
237 else
238 {
239 ASSERT("MultiByteToWideChar failed!\n");
240 SetLastError(ERROR_INTERNAL_ERROR);
241 size = 0;
242 *lpBuffer = '\0';
243 }
244 }
245
246done:
247 PAL_free(outBuff);
248 PAL_free(inBuff);
249
250 LOGEXIT("GetEnvironmentVariableW returns DWORD 0x%x\n", size);
251 PERF_EXIT(GetEnvironmentVariableW);
252
253 return size;
254}
255
256/*++
257Function:
258 SetEnvironmentVariableW
259
260The SetEnvironmentVariable function sets the value of an environment
261variable for the current process.
262
263Parameters
264
265lpName
266 [in] Pointer to a null-terminated string that specifies the
267 environment variable whose value is being set. The operating
268 system creates the environment variable if it does not exist
269 and lpValue is not null.
270lpValue
271 [in] Pointer to a null-terminated string containing the new
272 value of the specified environment variable. If this parameter
273 is null, the variable is deleted from the current process's
274 environment.
275
276Return Values
277
278If the function succeeds, the return value is nonzero.
279
280If the function fails, the return value is zero. To get extended error
281information, call GetLastError.
282
283Remarks
284
285This function has no effect on the system environment variables or the
286environment variables of other processes.
287
288--*/
289BOOL
290PALAPI
291SetEnvironmentVariableW(
292 IN LPCWSTR lpName,
293 IN LPCWSTR lpValue)
294{
295 PCHAR name = nullptr;
296 PCHAR value = nullptr;
297 INT nameSize = 0;
298 INT valueSize = 0;
299 BOOL bRet = FALSE;
300
301 PERF_ENTRY(SetEnvironmentVariableW);
302 ENTRY("SetEnvironmentVariableW(lpName=%p (%S), lpValue=%p (%S))\n",
303 lpName?lpName:W16_NULLSTRING,
304 lpName?lpName:W16_NULLSTRING, lpValue?lpValue:W16_NULLSTRING, lpValue?lpValue:W16_NULLSTRING);
305
306 if ((nameSize = WideCharToMultiByte(CP_ACP, 0, lpName, -1, name, 0,
307 nullptr, nullptr)) == 0)
308 {
309 ERROR("WideCharToMultiByte failed\n");
310 SetLastError(ERROR_INVALID_PARAMETER);
311 goto done;
312 }
313
314 name = (PCHAR)PAL_malloc(sizeof(CHAR)* nameSize);
315 if (name == nullptr)
316 {
317 ERROR("malloc failed\n");
318 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
319 goto done;
320 }
321
322 if (0 == WideCharToMultiByte(CP_ACP, 0, lpName, -1,
323 name, nameSize, nullptr, nullptr))
324 {
325 ASSERT("WideCharToMultiByte returned 0\n");
326 SetLastError(ERROR_INTERNAL_ERROR);
327 goto done;
328 }
329
330 if (lpValue != nullptr)
331 {
332 if ((valueSize = WideCharToMultiByte(CP_ACP, 0, lpValue, -1, value,
333 0, nullptr, nullptr)) == 0)
334 {
335 ERROR("WideCharToMultiByte failed\n");
336 SetLastError(ERROR_INVALID_PARAMETER);
337 goto done;
338 }
339
340 value = (PCHAR)PAL_malloc(sizeof(CHAR)*valueSize);
341
342 if (value == nullptr)
343 {
344 ERROR("malloc failed\n");
345 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
346 goto done;
347 }
348
349 if (0 == WideCharToMultiByte(CP_ACP, 0, lpValue, -1,
350 value, valueSize, nullptr, nullptr))
351 {
352 ASSERT("WideCharToMultiByte failed\n");
353 SetLastError( ERROR_INTERNAL_ERROR );
354 goto done;
355 }
356 }
357
358 bRet = SetEnvironmentVariableA(name, value);
359done:
360 PAL_free(value);
361 PAL_free(name);
362
363 LOGEXIT("SetEnvironmentVariableW returning BOOL %d\n", bRet);
364 PERF_EXIT(SetEnvironmentVariableW);
365 return bRet;
366}
367
368/*++
369Function:
370 GetEnvironmentStringsW
371
372The GetEnvironmentStrings function retrieves the environment block for
373the current process.
374
375Parameters
376
377This function has no parameters.
378
379Return Values
380
381The return value is a pointer to an environment block for the current process.
382
383Remarks
384
385The GetEnvironmentStrings function returns a pointer to the
386environment block of the calling process. This should be treated as a
387read-only block; do not modify it directly. Instead, use the
388GetEnvironmentVariable and SetEnvironmentVariable functions to
389retrieve or change the environment variables within this block. When
390the block is no longer needed, it should be freed by calling
391FreeEnvironmentStrings.
392
393--*/
394LPWSTR
395PALAPI
396GetEnvironmentStringsW(
397 VOID)
398{
399 WCHAR *wenviron = nullptr, *tempEnviron;
400 int i, len, envNum;
401
402 PERF_ENTRY(GetEnvironmentStringsW);
403 ENTRY("GetEnvironmentStringsW()\n");
404
405 CPalThread * pthrCurrent = InternalGetCurrentThread();
406 InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment);
407
408 envNum = 0;
409 len = 0;
410
411 /* get total length of the bytes that we need to allocate */
412 for (i = 0; palEnvironment[i] != 0; i++)
413 {
414 len = MultiByteToWideChar(CP_ACP, 0, palEnvironment[i], -1, wenviron, 0);
415 envNum += len;
416 }
417
418 wenviron = (WCHAR *)PAL_malloc(sizeof(WCHAR)* (envNum + 1));
419 if (wenviron == nullptr)
420 {
421 ERROR("malloc failed\n");
422 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
423 goto EXIT;
424 }
425
426 len = 0;
427 tempEnviron = wenviron;
428 for (i = 0; palEnvironment[i] != 0; i++)
429 {
430 len = MultiByteToWideChar(CP_ACP, 0, palEnvironment[i], -1, tempEnviron, envNum);
431 tempEnviron += len;
432 envNum -= len;
433 }
434
435 *tempEnviron = 0; /* Put an extra null at the end */
436
437 EXIT:
438 InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment);
439
440 LOGEXIT("GetEnvironmentStringsW returning %p\n", wenviron);
441 PERF_EXIT(GetEnvironmentStringsW);
442 return wenviron;
443}
444
445/*++
446Function:
447 GetEnvironmentStringsA
448
449See GetEnvironmentStringsW.
450
451--*/
452LPSTR
453PALAPI
454GetEnvironmentStringsA(
455 VOID)
456{
457 char *environ = nullptr, *tempEnviron;
458 int i, len, envNum;
459
460 PERF_ENTRY(GetEnvironmentStringsA);
461 ENTRY("GetEnvironmentStringsA()\n");
462
463 CPalThread * pthrCurrent = InternalGetCurrentThread();
464 InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment);
465
466 envNum = 0;
467 len = 0;
468
469 /* get total length of the bytes that we need to allocate */
470 for (i = 0; palEnvironment[i] != 0; i++)
471 {
472 len = strlen(palEnvironment[i]) + 1;
473 envNum += len;
474 }
475
476 environ = (char *)PAL_malloc(envNum + 1);
477 if (environ == nullptr)
478 {
479 ERROR("malloc failed\n");
480 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
481 goto EXIT;
482 }
483
484 len = 0;
485 tempEnviron = environ;
486 for (i = 0; palEnvironment[i] != 0; i++)
487 {
488 len = strlen(palEnvironment[i]) + 1;
489 memcpy(tempEnviron, palEnvironment[i], len);
490 tempEnviron += len;
491 envNum -= len;
492 }
493
494 *tempEnviron = 0; /* Put an extra null at the end */
495
496 EXIT:
497 InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment);
498
499 LOGEXIT("GetEnvironmentStringsA returning %p\n", environ);
500 PERF_EXIT(GetEnvironmentStringsA);
501 return environ;
502}
503
504/*++
505Function:
506 FreeEnvironmentStringsW
507
508The FreeEnvironmentStrings function frees a block of environment strings.
509
510Parameters
511
512lpszEnvironmentBlock [in] Pointer to a block of environment strings. The pointer to
513 the block must be obtained by calling the
514 GetEnvironmentStrings function.
515
516Return Values
517
518If the function succeeds, the return value is nonzero. If the
519function fails, the return value is zero. To get extended error
520information, call GetLastError.
521
522Remarks
523
524When GetEnvironmentStrings is called, it allocates memory for a block
525of environment strings. When the block is no longer needed, it should
526be freed by calling FreeEnvironmentStrings.
527
528--*/
529BOOL
530PALAPI
531FreeEnvironmentStringsW(
532 IN LPWSTR lpValue)
533{
534 PERF_ENTRY(FreeEnvironmentStringsW);
535 ENTRY("FreeEnvironmentStringsW(lpValue=%p (%S))\n", lpValue ? lpValue : W16_NULLSTRING, lpValue ? lpValue : W16_NULLSTRING);
536
537 if (lpValue != nullptr)
538 {
539 PAL_free(lpValue);
540 }
541
542 LOGEXIT("FreeEnvironmentStringW returning BOOL TRUE\n");
543 PERF_EXIT(FreeEnvironmentStringsW);
544 return TRUE;
545}
546
547/*++
548Function:
549 FreeEnvironmentStringsA
550
551See FreeEnvironmentStringsW.
552
553--*/
554BOOL
555PALAPI
556FreeEnvironmentStringsA(
557 IN LPSTR lpValue)
558{
559 PERF_ENTRY(FreeEnvironmentStringsA);
560 ENTRY("FreeEnvironmentStringsA(lpValue=%p (%s))\n", lpValue ? lpValue : "NULL", lpValue ? lpValue : "NULL");
561
562 if (lpValue != nullptr)
563 {
564 PAL_free(lpValue);
565 }
566
567 LOGEXIT("FreeEnvironmentStringA returning BOOL TRUE\n");
568 PERF_EXIT(FreeEnvironmentStringsA);
569 return TRUE;
570}
571
572/*++
573Function:
574 SetEnvironmentVariableA
575
576The SetEnvironmentVariable function sets the value of an environment
577variable for the current process.
578
579Parameters
580
581lpName
582 [in] Pointer to a null-terminated string that specifies the
583 environment variable whose value is being set. The operating
584 system creates the environment variable if it does not exist
585 and lpValue is not null.
586lpValue
587 [in] Pointer to a null-terminated string containing the new
588 value of the specified environment variable. If this parameter
589 is null, the variable is deleted from the current process's
590 environment.
591
592Return Values
593
594If the function succeeds, the return value is nonzero.
595
596If the function fails, the return value is zero. To get extended error
597information, call GetLastError.
598
599Remarks
600
601This function has no effect on the system environment variables or the
602environment variables of other processes.
603
604--*/
605BOOL
606PALAPI
607SetEnvironmentVariableA(
608 IN LPCSTR lpName,
609 IN LPCSTR lpValue)
610{
611
612 BOOL bRet = FALSE;
613 int nResult =0;
614 PERF_ENTRY(SetEnvironmentVariableA);
615 ENTRY("SetEnvironmentVariableA(lpName=%p (%s), lpValue=%p (%s))\n",
616 lpName ? lpName : "NULL", lpName ? lpName : "NULL",
617 lpValue ? lpValue : "NULL", lpValue ? lpValue : "NULL");
618
619 // exit if the input variable name is null
620 if ((lpName == nullptr) || (lpName[0] == 0))
621 {
622 ERROR("lpName is null\n");
623 goto done;
624 }
625
626 /* check if the input value is null and if so
627 * check if the input name is valid and delete
628 * the variable name from process environment */
629 if (lpValue == nullptr)
630 {
631 // We tell EnvironGetenv not to bother with making a copy of the
632 // value since we're not going to use it for anything interesting
633 // apart from checking whether it's null.
634 if ((lpValue = EnvironGetenv(lpName, /* copyValue */ FALSE)) == nullptr)
635 {
636 ERROR("Couldn't find environment variable (%s)\n", lpName);
637 SetLastError(ERROR_ENVVAR_NOT_FOUND);
638 goto done;
639 }
640
641 EnvironUnsetenv(lpName);
642 }
643 else
644 {
645 // All the conditions are met. Set the variable.
646 int iLen = strlen(lpName) + strlen(lpValue) + 2;
647 LPSTR string = (LPSTR) PAL_malloc(iLen);
648 if (string == nullptr)
649 {
650 bRet = FALSE;
651 ERROR("Unable to allocate memory\n");
652 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
653 goto done;
654 }
655
656 sprintf_s(string, iLen, "%s=%s", lpName, lpValue);
657 nResult = EnvironPutenv(string, FALSE) ? 0 : -1;
658
659 PAL_free(string);
660 string = nullptr;
661
662 // If EnvironPutenv returns FALSE, it almost certainly failed to allocate memory.
663 if (nResult == -1)
664 {
665 bRet = FALSE;
666 ERROR("Unable to allocate memory\n");
667 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
668 goto done;
669 }
670 }
671
672 bRet = TRUE;
673
674done:
675 LOGEXIT("SetEnvironmentVariableA returning BOOL %d\n", bRet);
676 PERF_EXIT(SetEnvironmentVariableA);
677 return bRet;
678}
679
680/*++
681Function:
682 ResizeEnvironment
683
684Resizes the PAL environment buffer.
685
686Parameters
687
688 newSize
689 [in] New size of palEnvironment
690
691Return Values
692
693 TRUE on success, FALSE otherwise
694
695--*/
696BOOL ResizeEnvironment(int newSize)
697{
698 CPalThread * pthrCurrent = InternalGetCurrentThread();
699 InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment);
700
701 BOOL ret = FALSE;
702 if (newSize >= palEnvironmentCount)
703 {
704 // If palEnvironment is null, realloc acts like malloc.
705 char **newEnvironment = (char**)realloc(palEnvironment, newSize * sizeof(char *));
706 if (newEnvironment != nullptr)
707 {
708 // realloc succeeded, so set palEnvironment to what it returned.
709 palEnvironment = newEnvironment;
710 palEnvironmentCapacity = newSize;
711 ret = TRUE;
712 }
713 }
714 else
715 {
716 ASSERT("ResizeEnvironment: newSize < current palEnvironmentCount!\n");
717 }
718
719 InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment);
720 return ret;
721}
722
723/*++
724Function:
725 EnvironUnsetenv
726
727Remove the environment variable with the given name from the PAL version
728of the environment if it exists.
729
730Parameters
731
732 name
733 [in] Name of variable to unset.
734
735--*/
736void EnvironUnsetenv(const char *name)
737{
738 int nameLength = strlen(name);
739
740 CPalThread * pthrCurrent = InternalGetCurrentThread();
741 InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment);
742
743 for (int i = 0; palEnvironment[i] != nullptr; ++i)
744 {
745 const char *equalsSignPosition = strchr(palEnvironment[i], '=');
746 if (equalsSignPosition == nullptr)
747 {
748 equalsSignPosition = palEnvironment[i] + strlen(palEnvironment[i]);
749 }
750
751 // Check whether the name of this variable has the same length as the one
752 // we're looking for before proceeding to compare them.
753 if (equalsSignPosition - palEnvironment[i] == nameLength)
754 {
755 if (memcmp(name, palEnvironment[i], nameLength) == 0)
756 {
757 // Free the string we're removing.
758 free(palEnvironment[i]);
759
760 // Move the last environment variable pointer here.
761 palEnvironment[i] = palEnvironment[palEnvironmentCount - 1];
762 palEnvironment[palEnvironmentCount - 1] = nullptr;
763
764 palEnvironmentCount--;
765 }
766 }
767 }
768
769 InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment);
770}
771
772/*++
773Function:
774 EnvironPutenv
775
776Add the environment variable string provided to the PAL version
777of the environment.
778
779Parameters
780
781 entry
782 [in] The variable string to add. Should be in the format
783 "name=value", where value might be empty (see below).
784 deleteIfEmpty
785 [in] If this is TRUE, "name=" will unset the 'name' variable.
786
787Return Values
788
789 TRUE on success, FALSE otherwise
790
791--*/
792BOOL EnvironPutenv(const char* entry, BOOL deleteIfEmpty)
793{
794 BOOL result = FALSE;
795
796 bool fOwningCS = false;
797
798 CPalThread * pthrCurrent = InternalGetCurrentThread();
799
800 const char *equalsSignPosition = strchr(entry, '=');
801 if (equalsSignPosition == entry || equalsSignPosition == nullptr)
802 {
803 // "=foo" and "foo" have no meaning
804 return FALSE;
805 }
806
807 char* copy = strdup(entry);
808 if (copy == nullptr)
809 {
810 return FALSE;
811 }
812
813 int nameLength = equalsSignPosition - entry;
814
815 if (equalsSignPosition[1] == '\0' && deleteIfEmpty)
816 {
817 // "foo=" removes foo from the environment in _putenv() on Windows.
818 // The same string can result from a call to SetEnvironmentVariable()
819 // with the empty string as the value, but in that case we want to
820 // set the variable's value to "". deleteIfEmpty will be FALSE in
821 // that case.
822
823 // Change '=' to '\0'
824 copy[nameLength] = '\0';
825
826 EnvironUnsetenv(copy);
827 free(copy);
828
829 result = TRUE;
830 }
831 else
832 {
833 // See if we are replacing an item or adding one.
834
835 InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment);
836 fOwningCS = true;
837
838 int i;
839 for (i = 0; palEnvironment[i] != nullptr; i++)
840 {
841 const char *existingEquals = strchr(palEnvironment[i], '=');
842 if (existingEquals == nullptr)
843 {
844 // The PAL screens out malformed strings, but the strings which
845 // came from the system during initialization might not have the
846 // equals sign. We treat the entire string as a name in that case.
847 existingEquals = palEnvironment[i] + strlen(palEnvironment[i]);
848 }
849
850 if (existingEquals - palEnvironment[i] == nameLength)
851 {
852 if (memcmp(entry, palEnvironment[i], nameLength) == 0)
853 {
854 free(palEnvironment[i]);
855 palEnvironment[i] = copy;
856
857 result = TRUE;
858 break;
859 }
860 }
861 }
862
863 if (palEnvironment[i] == nullptr)
864 {
865 _ASSERTE(i < palEnvironmentCapacity);
866 if (i == (palEnvironmentCapacity - 1))
867 {
868 // We found the first null, but it's the last element in our environment
869 // block. We need more space in our environment, so let's double its size.
870 int resizeRet = ResizeEnvironment(palEnvironmentCapacity * 2);
871 if (resizeRet != TRUE)
872 {
873 free(copy);
874 goto done;
875 }
876 }
877
878 _ASSERTE(copy != nullptr);
879 palEnvironment[i] = copy;
880 palEnvironment[i + 1] = nullptr;
881 palEnvironmentCount++;
882
883 result = TRUE;
884 }
885 }
886done:
887
888 if (fOwningCS)
889 {
890 InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment);
891 }
892
893 return result;
894}
895
896/*++
897Function:
898 EnvironGetenv
899
900Get the value of environment variable with the given name.
901
902Parameters
903
904 name
905 [in] The name of the environment variable to get.
906 copyValue
907 [in] If this is TRUE, the function will make a copy of the
908 value and return a pointer to that. Otherwise, it will
909 return a pointer to the value in the PAL environment
910 directly. Calling this function with copyValue set to
911 FALSE is therefore unsafe without taking special pre-
912 cautions since the pointer may point to garbage later.
913
914Return Value
915
916 A pointer to the value of the environment variable if it exists,
917 or nullptr otherwise.
918
919--*/
920char* EnvironGetenv(const char* name, BOOL copyValue)
921{
922 char *retValue = nullptr;
923
924 CPalThread * pthrCurrent = InternalGetCurrentThread();
925 InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment);
926
927 int nameLength = strlen(name);
928 for (int i = 0; palEnvironment[i] != nullptr; ++i)
929 {
930 if (strlen(palEnvironment[i]) < nameLength)
931 {
932 continue;
933 }
934
935 if (memcmp(palEnvironment[i], name, nameLength) == 0)
936 {
937 char *equalsSignPosition = palEnvironment[i] + nameLength;
938
939 // If this is one of the variables which has no equals sign, we
940 // treat the whole thing as name, so the value is an empty string.
941 if (*equalsSignPosition == '\0')
942 {
943 retValue = (char *)"";
944 break;
945 }
946 else if (*equalsSignPosition == '=')
947 {
948 retValue = equalsSignPosition + 1;
949 break;
950 }
951 }
952 }
953
954 if ((retValue != nullptr) && copyValue)
955 {
956 retValue = strdup(retValue);
957 }
958
959 InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment);
960 return retValue;
961}
962
963/*++
964Function:
965 EnvironGetSystemEnvironment
966
967Get a pointer to the array of pointers representing the process's
968environment.
969
970See 'man environ' for details.
971
972Return Value
973
974 A pointer to the environment.
975
976--*/
977char** EnvironGetSystemEnvironment()
978{
979 char** sysEnviron;
980
981#if HAVE__NSGETENVIRON
982 sysEnviron = *(_NSGetEnviron());
983#else // HAVE__NSGETENVIRON
984 extern char **environ;
985 sysEnviron = environ;
986#endif // HAVE__NSGETENVIRON
987
988 return sysEnviron;
989}
990
991/*++
992Function:
993 EnvironInitialize
994
995Initialization function called from PAL_Initialize.
996
997Note: This is called before debug channels are initialized, so it
998 cannot use debug tracing calls.
999--*/
1000BOOL
1001EnvironInitialize(void)
1002{
1003 BOOL ret = FALSE;
1004
1005 InternalInitializeCriticalSection(&gcsEnvironment);
1006
1007 CPalThread * pthrCurrent = InternalGetCurrentThread();
1008 InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment);
1009
1010 char** sourceEnviron = EnvironGetSystemEnvironment();
1011
1012 int variableCount = 0;
1013 while (sourceEnviron[variableCount] != nullptr)
1014 variableCount++;
1015
1016 palEnvironmentCount = 0;
1017
1018 // We need to decide how much space to allocate. Since we need enough
1019 // space for all of the 'n' current environment variables, but we don't
1020 // know how many more there will be, we will initially make room for
1021 // '2n' variables. If even more are added, we will resize again.
1022 // If there are no variables, we will still make room for 1 entry to
1023 // store a nullptr there.
1024 int initialSize = (variableCount == 0) ? 1 : variableCount * 2;
1025
1026 ret = ResizeEnvironment(initialSize);
1027 if (ret == TRUE)
1028 {
1029 _ASSERTE(palEnvironment != nullptr);
1030 for (int i = 0; i < variableCount; ++i)
1031 {
1032 palEnvironment[i] = strdup(sourceEnviron[i]);
1033 palEnvironmentCount++;
1034 }
1035
1036 // Set the entry after the last variable to null to indicate the end.
1037 palEnvironment[variableCount] = nullptr;
1038 }
1039
1040 InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment);
1041 return ret;
1042}
1043
1044/*++
1045
1046Function : _putenv.
1047
1048See MSDN for more details.
1049
1050Note: The BSD implementation can cause
1051 memory leaks. See man pages for more details.
1052--*/
1053int
1054__cdecl
1055_putenv( const char * envstring )
1056{
1057 int ret = -1;
1058
1059 PERF_ENTRY(_putenv);
1060 ENTRY( "_putenv( %p (%s) )\n", envstring ? envstring : "NULL", envstring ? envstring : "NULL") ;
1061
1062 if (envstring != nullptr)
1063 {
1064 ret = EnvironPutenv(envstring, TRUE) ? 0 : -1;
1065 }
1066 else
1067 {
1068 ERROR( "_putenv() called with NULL envstring!\n");
1069 }
1070
1071 LOGEXIT( "_putenv returning %d\n", ret);
1072 PERF_EXIT(_putenv);
1073 return ret;
1074}
1075
1076/*++
1077
1078Function : PAL_getenv
1079
1080See MSDN for more details.
1081--*/
1082char * __cdecl PAL_getenv(const char *varname)
1083{
1084 char *retval;
1085
1086 PERF_ENTRY(getenv);
1087 ENTRY("getenv (%p (%s))\n", varname ? varname : "NULL", varname ? varname : "NULL");
1088
1089 if (strcmp(varname, "") == 0)
1090 {
1091 ERROR("getenv called with a empty variable name\n");
1092 LOGEXIT("getenv returning NULL\n");
1093 PERF_EXIT(getenv);
1094 return(NULL);
1095 }
1096
1097 retval = EnvironGetenv(varname);
1098
1099 LOGEXIT("getenv returning %p\n", retval);
1100 PERF_EXIT(getenv);
1101 return(retval);
1102}
1103