1 /**
2  * Functions for modifying environment variables.
3  *
4  * Copyright:   Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
5  * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
6  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/env.d, env.d)
7  * Documentation:  https://dlang.org/phobos/dmd_root_env.html
8  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/env.d
9  */
10 
11 module dmd.root.env;
12 
13 import core.stdc.string;
14 import core.sys.posix.stdlib;
15 import dmd.root.array;
16 import dmd.root.rmem;
17 import dmd.root.string;
18 
19 version (Windows)
20     private extern (C) int putenv(const char*) nothrow;
21 
22 nothrow:
23 
24 /**
25 Construct a variable from `name` and `value` and put it in the environment while saving
26 the previous value of the environment variable into a global list so it can be restored later.
27 Params:
28     name = the name of the variable
29     value = the value of the variable
30 Returns:
31     true on error, false on success
32 */
33 bool putenvRestorable(const(char)[] name, const(char)[] value) nothrow
34 {
35     saveEnvVar(name);
36     const nameValue = allocNameValue(name, value);
37     const result = putenv(cast(char*)nameValue.ptr);
38     version (Windows)
39         mem.xfree(cast(void*)nameValue.ptr);
40     else
41     {
42         if (result)
43             mem.xfree(cast(void*)nameValue.ptr);
44     }
45     return result ? true : false;
46 }
47 
48 /**
49 Allocate a new variable via xmalloc that can be added to the global environment. The
50 resulting string will be null-terminated immediately after the end of the array.
51 Params:
52     name = name of the variable
53     value = value of the variable
54 Returns:
55     a newly allocated variable that can be added to the global environment
56 */
57 string allocNameValue(const(char)[] name, const(char)[] value) nothrow
58 {
59     const length = name.length + 1 + value.length;
60     auto str = (cast(char*)mem.xmalloc(length + 1))[0 .. length];
61     str[0 .. name.length] = name[];
62     str[name.length] = '=';
63     str[name.length + 1 .. length] = value[];
64     str.ptr[length] = '\0';
65     return cast(string)str;
66 }
67 
68 /// Holds the original values of environment variables when they are overwritten.
69 private __gshared string[string] envNameValues;
70 
71 /// Restore the original environment.
72 void restoreEnvVars() nothrow
73 {
74     foreach (var; envNameValues.values)
75     {
76         if (putenv(cast(char*)var.ptr))
77             assert(0);
78     }
79 }
80 
81 /// Save the environment variable `name` if not saved already.
82 void saveEnvVar(const(char)[] name) nothrow
83 {
84     if (!(name in envNameValues))
85     {
86         envNameValues[name.idup] = allocNameValue(name, name.toCStringThen!(n => getenv(n.ptr)).toDString);
87     }
88 }