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