xref: /reactos/sdk/lib/ucrt/env/putenv.cpp (revision e98e9000)
1 //
2 // putenv.cpp
3 //
4 //      Copyright (c) Microsoft Corporation.  All rights reserved.
5 //
6 // Defines the putenv() family of functions, which add, replace, or remove an
7 // environment variable from the current process environment.
8 //
9 #include <corecrt_internal.h>
10 #include <corecrt_internal_traits.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 
15 
16 // These functions test to see if the other environment exists
other_environment_exists(wchar_t)17 static bool other_environment_exists(wchar_t) throw() { return _environ_table.value() != nullptr; }
other_environment_exists(char)18 static bool other_environment_exists(char)    throw() { return _wenviron_table.value() != nullptr; }
19 
20 
21 
22 // This function computes the required size of the buffer that will store a
23 // "name=value" string in the other environment.
24 template <typename Character>
compute_required_transform_buffer_count(Character const * const name,Character const * const value)25 static size_t compute_required_transform_buffer_count(
26     Character const* const name,
27     Character const* const value
28     ) throw()
29 {
30     // Compute the amount of space required for the transformation:
31     size_t const name_count_required = __crt_compute_required_transform_buffer_count(CP_ACP, name);
32     _VALIDATE_RETURN_NOEXC(name_count_required != 0, EILSEQ, false);
33 
34     if (!value)
35         return name_count_required;
36 
37     size_t const value_count_required = __crt_compute_required_transform_buffer_count(CP_ACP, value);
38     _VALIDATE_RETURN_NOEXC(value_count_required != 0, EILSEQ, false);
39 
40     // Note that each count includes space a the null terminator.  Since we'll
41     // only be storing one terminator in the buffer, the space for the other
42     // terminator will be used to store the '=' between the name and the value.
43     return name_count_required + value_count_required;
44 }
45 
46 
47 
48 // Constructs an environment string of the form "name=value" from a {name, value}
49 // pair.  Returns a pointer to the resulting string.  The string is dynamically
50 // allocated and must be freed by the caller (via the CRT free).
51 template <typename Character>
create_environment_string(Character const * const name,Character const * const value)52 static Character* create_environment_string(
53     Character const* const name,
54     Character const* const value
55     ) throw()
56 {
57     typedef __crt_char_traits<Character> traits;
58 
59     if (value)
60     {
61         size_t const name_length  = traits::tcsnlen(name,  _MAX_ENV);
62         size_t const value_length = traits::tcsnlen(value, _MAX_ENV);
63 
64         _VALIDATE_RETURN(name_length  < _MAX_ENV, EINVAL, nullptr);
65         _VALIDATE_RETURN(value_length < _MAX_ENV, EINVAL, nullptr);
66 
67         // We add two to the length:  one for the '=' and one for the terminator
68         size_t const buffer_count = name_length + 1 + value_length + 1;
69 
70         __crt_unique_heap_ptr<Character> buffer(_calloc_crt_t(Character, buffer_count));
71         if (!buffer)
72             return nullptr;
73 
74         traits::tcscpy_s(buffer.get(), buffer_count, name);
75         buffer.get()[name_length] = '=';
76         traits::tcscpy_s(buffer.get() + name_length + 1, value_length + 1, value);
77 
78         return buffer.detach();
79     }
80     else
81     {
82         Character const* const equal_sign_it = traits::tcschr(name, '=');
83         if (equal_sign_it)
84         {
85             // Validate the length of both the name and the value:
86             _VALIDATE_RETURN(equal_sign_it - name < _MAX_ENV,                         EINVAL, nullptr);
87             _VALIDATE_RETURN(traits::tcsnlen(equal_sign_it + 1, _MAX_ENV) < _MAX_ENV, EINVAL, nullptr);
88         }
89 
90         size_t const buffer_count = traits::tcslen(name) + 1;
91 
92         __crt_unique_heap_ptr<Character> buffer(_calloc_crt_t(Character, buffer_count));
93         if (!buffer)
94             return nullptr;
95 
96         traits::tcscpy_s(buffer.get(), buffer_count, name);
97 
98         return buffer.detach();
99     }
100 }
101 
102 
103 
104 // Converts the {name, value} pair to the other kind of string (wchar_t => char,
105 // char => wchar_t) and updates the other environment.
106 template <typename Character>
set_variable_in_other_environment(Character const * const name,Character const * const value)107 static bool __cdecl set_variable_in_other_environment(
108     Character const* const name,
109     Character const* const value
110     ) throw()
111 {
112     typedef __crt_char_traits<Character>       traits;
113     typedef typename traits::other_char_type   other_char_type;
114     typedef __crt_char_traits<other_char_type> other_traits;
115 
116     size_t const buffer_count = compute_required_transform_buffer_count(name, value);
117 
118     __crt_unique_heap_ptr<other_char_type> buffer(_calloc_crt_t(other_char_type, buffer_count));
119     if (!buffer)
120         return false;
121 
122     size_t const name_written_count = __crt_transform_string(CP_ACP, name, buffer.get(), buffer_count);
123     _VALIDATE_RETURN_NOEXC(name_written_count != 0, EILSEQ, false);
124 
125     if (value)
126     {
127         // Overwrite the null terminator with an '=':
128         buffer.get()[name_written_count - 1] = '=';
129 
130         size_t const value_written_count = __crt_transform_string(
131             CP_ACP,
132             value,
133             buffer.get() + name_written_count,
134             buffer_count - name_written_count);
135         _VALIDATE_RETURN_NOEXC(value_written_count != 0, EILSEQ, false);
136     }
137 
138     return other_traits::set_variable_in_environment_nolock(buffer.detach(), 0) == 0;
139 }
140 
141 
142 
143 // Adds, replaces, or removes a variable in the current environment.  For the
144 // functions that take a {name, value} pair, the name is the name of the variable
145 // and the value is the value it is to be given.  For the functions that just
146 // take and option, the option is of the form "name=value".
147 //
148 // If the value is an empty string and the name names an existing environment
149 // variable, that variable is removed from the environment.  If the value is
150 // a nonempty string and the name names an existing environment variable, the
151 // variable is updated to have the new value.  If the value is a nonempty string
152 // and the name does not name an existing environment variable, a new variable
153 // is added to the environment.
154 //
155 // If the required environment does not yet exist, it is created from the other
156 // environment.  If both environments exist, the modifications are made to both
157 // of them so that they are kept in sync.
158 //
159 // Returns 0 on success; -1 on failure.
160 template <typename Character>
common_putenv_nolock(Character const * const name,Character const * const value)161 static int __cdecl common_putenv_nolock(
162     Character const* const name,
163     Character const* const value
164     ) throw()
165 {
166     typedef __crt_char_traits<Character> traits;
167 
168     // Ensure that the environment is initialized:
169     if (!_environ_table.value() && !_wenviron_table.value())
170         return -1;
171 
172     // At startup, we obtain the "native" flavor of environment strings from the
173     // operating system.  So, a "main" program has _environ set and a "wmain"
174     // program has _wenviron set.  Only when the user gets or puts the "other"
175     // flavor do we convert it.
176     _VALIDATE_RETURN(name != nullptr, EINVAL, -1);
177 
178     __crt_unique_heap_ptr<Character> new_option(create_environment_string(name, value));
179     if (!new_option)
180         return -1;
181 
182     if (traits::set_variable_in_environment_nolock(new_option.detach(), 1) != 0)
183         return -1;
184 
185     // See if the "other" environment type exists; if it doesn't, we're done.
186     // Otherwise, put the new option into the other environment as well.
187     if (!other_environment_exists(Character()))
188         return 0;
189 
190     if (!set_variable_in_other_environment(name, value))
191         return -1;
192 
193     return 0;
194 }
195 
196 
197 
198 template <typename Character>
common_putenv(Character const * const name,Character const * const value)199 static int __cdecl common_putenv(
200     Character const* const name,
201     Character const* const value
202     ) throw()
203 {
204     int status = 0;
205 
206     __acrt_lock(__acrt_environment_lock);
207     __try
208     {
209         status = common_putenv_nolock(name, value);
210     }
211     __finally
212     {
213         __acrt_unlock(__acrt_environment_lock);
214     }
215     __endtry
216 
217     return status;
218 }
219 
220 
221 
_putenv(char const * const option)222 extern "C" int __cdecl _putenv(char const* const option)
223 {
224     return common_putenv(option, static_cast<char const*>(nullptr));
225 }
226 
_wputenv(wchar_t const * const option)227 extern "C" int __cdecl _wputenv(wchar_t const* const option)
228 {
229     return common_putenv(option, static_cast<wchar_t const*>(nullptr));
230 }
231 
232 
233 
_putenv_s(char const * const name,char const * const value)234 extern "C" errno_t __cdecl _putenv_s(char const* const name, char const* const value)
235 {
236     _VALIDATE_RETURN_ERRCODE(value != nullptr, EINVAL);
237     return common_putenv(name, value) == 0 ? 0 : errno;
238 }
239 
_wputenv_s(wchar_t const * const name,wchar_t const * const value)240 extern "C" errno_t __cdecl _wputenv_s(wchar_t const* const name, wchar_t const* const value)
241 {
242     _VALIDATE_RETURN_ERRCODE(value != nullptr, EINVAL);
243     return common_putenv(name, value) == 0 ? 0 : errno;
244 }
245