ReactOS 0.4.16-dev-959-g2ec3a19
setenv.cpp
Go to the documentation of this file.
1//
2// setenv.cpp
3//
4// Copyright (c) Microsoft Corporation. All rights reserved.
5//
6// Internal functions for setting or removing variables from an environment. The
7// logic for manipulating the environment data structures is split across this
8// file and environment_initialization.cpp.
9//
10#include <corecrt_internal.h>
12#include <limits.h>
13#include <stdlib.h>
14
15
16
17static char**& __cdecl get_environment(char) throw() { return _environ_table.value(); }
18static wchar_t**& __cdecl get_environment(wchar_t) throw() { return _wenviron_table.value(); }
19
20static wchar_t**& __cdecl get_other_environment(char) throw() { return _wenviron_table.value(); }
21static char**& __cdecl get_other_environment(wchar_t) throw() { return _environ_table.value(); }
22
24static wchar_t**& __cdecl get_initial_environment(wchar_t) throw() { return __dcrt_initial_wide_environment; }
25
26
27
28// Makes a copy of the provided environment and returns the copy. The caller is
29// responsible for freeing the returned array (using the CRT free). Returns
30// nullptr on failure; terminates the process on allocation failure.
31template <typename Character>
32static Character** __cdecl copy_environment(Character** const old_environment) throw()
33{
34 typedef __crt_char_traits<Character> traits;
35
36 if (!old_environment)
37 {
38 return nullptr;
39 }
40
41 // Count the number of environment variables:
42 size_t entry_count = 0;
43 for (Character** it = old_environment; *it; ++it)
44 {
45 ++entry_count;
46 }
47
48 // We need one pointer for each string, plus one null pointer at the end:
49 __crt_unique_heap_ptr<Character*> new_environment(_calloc_crt_t(Character*, entry_count + 1));
50 if (!new_environment)
51 {
52 abort();
53 }
54
55 Character** old_it = old_environment;
56 Character** new_it = new_environment.get();
57 for (; *old_it; ++old_it, ++new_it)
58 {
59 size_t const required_count = traits::tcslen(*old_it) + 1;
60 *new_it = _calloc_crt_t(Character, required_count).detach();
61 if (!*new_it)
62 {
63 abort();
64 }
65
66 _ERRCHECK(traits::tcscpy_s(*new_it, required_count, *old_it));
67 }
68
69 return new_environment.detach();
70}
71
72
73
74// If the current environment is the initial environment, this function clones
75// the current environment so that it is not the initial environment. This
76// should be called any time that we are about to modify the current environment
77// but we do not know whether the current environment is the initial environment.
78template <typename Character>
80{
81 if (get_environment(Character()) == get_initial_environment(Character()))
82 {
83 get_environment(Character()) = copy_environment(get_environment(Character()));
84 }
85}
86
87
88
89// Finds an environment variable in the specified environment. If a variable
90// with the given name is found, its index in the environment is returned. If
91// no such environment is found, the total number of environment variables is
92// returned, multiplied by -1. Note that a return value of 0 may indicate
93// either that the variable was found at index 0 or there are zero variables
94// in the environment. Be sure to check for this case.
95template <typename Character>
97 Character const* const name,
98 size_t const length
99 ) throw()
100{
101 typedef __crt_char_traits<Character> traits;
102
103 Character** const environment = get_environment(Character());
104
105 Character** it = nullptr;
106 for (it = environment; *it; ++it)
107 {
108 // See if the first 'length' characters match:
109 if (traits::tcsnicoll(name, *it, length) != 0)
110 {
111 continue;
112 }
113
114 // Ensure that the next character of the environment is an '=' or '\0':
115 if ((*it)[length] != '=' && (*it)[length] != '\0')
116 {
117 continue;
118 }
119
120 // Otherwise, this entry matched; return its index in the environment:
121 return static_cast<ptrdiff_t>(it - environment);
122 }
123
124 // No entry matched; return the total number of strings, multiplied by -1:
125 return -static_cast<ptrdiff_t>(it - environment);
126}
127
128
129/***
130*int __dcrt_set_variable_in_narrow_environment(option) - add/replace/remove variable in environment
131*
132*Purpose:
133* option should be of the form "option=value". If a string with the
134* given option part already exists, it is replaced with the given
135* string; otherwise the given string is added to the environment.
136* If the string is of the form "option=", then the string is
137* removed from the environment, if it exists. If the string has
138* no equals sign, error is returned.
139*
140*Entry:
141* TCHAR **poption - pointer to option string to set in the environment list.
142* should be of the form "option=value".
143* This function takes ownership of this pointer in the success case.
144* int primary - Only the primary call to _crt[w]setenv needs to
145* create new copies or set the OS environment.
146* 1 indicates that this is the primary call.
147*
148*Exit:
149* returns 0 if OK, -1 if fails.
150* If *poption is non-null on exit, we did not free it, and the caller should
151* If *poption is null on exit, we did free it, and the caller should not.
152*
153*Exceptions:
154*
155*Warnings:
156* This code will not work if variables are removed from the environment
157* by deleting them from environ[]. Use _putenv("option=") to remove a
158* variable.
159*
160* The option argument will be taken ownership of by this code and may be freed!
161*
162*******************************************************************************/
163template <typename Character>
165 Character* const option,
166 int const is_top_level_call
167 ) throw()
168{
169 typedef __crt_char_traits<Character> traits;
170
171 // Check that the option string is valid first. Find the '=' and verify
172 // that '=' is not the first character in the string:
173 _VALIDATE_RETURN_NOEXC(option != nullptr, EINVAL, -1);
174 __crt_unique_heap_ptr<Character> owned_option(option);
175
176 Character* const equal_sign = traits::tcschr(option, '=');
177 _VALIDATE_RETURN_NOEXC(equal_sign != nullptr && equal_sign != option, EINVAL, -1);
178
179 // Internal consistency check: The environment string should never use
180 // buffers larger than _MAX_ENV. See also the SetEnvironmentVariable SDK
181 // function.
182 _ASSERTE(equal_sign - option < _MAX_ENV);
183 _ASSERTE(traits::tcsnlen(equal_sign + 1, _MAX_ENV) < _MAX_ENV);
184
185 // If the character following '=' is the terminator, we are removing the
186 // environment variable. Otherwise, we are adding or updating the variable:
187 bool const is_removal = *(equal_sign + 1) == '\0';
188
189 // At program startup, the initial environment (__dcrt_initial_narrow_environment), which is passed
190 // to main(), is backed by the same environment arrays as the global
191 // environment used by getenv, setenv, et al. We cannot modify thie initial
192 // environment, so we make a copy of it the first time we need to make any
193 // modifications to the global environment:
194 ensure_current_environment_is_not_initial_environment_nolock<Character>();
195
196 // If the required environment does not exist, see if the other environment
197 // exists; if it does, convert it to create the required environment. These
198 // functions will reenter this function once for each environment variable;
199 // we use the top-level call flag to stop recursion.
200 if (!get_environment(Character()))
201 {
202 if (is_top_level_call && get_other_environment(Character()))
203 {
204 _VALIDATE_RETURN_NOEXC(traits::get_or_create_environment_nolock() != nullptr, EINVAL, -1);
205
206 // The call to get_or_create_environment() may have initialized the
207 // current environment to the same environment that is the initial
208 // environment. Re-check and make a new copy of the environment to
209 // modify if necessary.
210 ensure_current_environment_is_not_initial_environment_nolock<Character>();
211 }
212 else
213 {
214 // If the environment doesn't exist and the requested operation is a
215 // removal, there is nothing to do (there is nothing to remove):
216 if (is_removal)
217 {
218 return 0;
219 }
220
221 // Create a new environment for each environment that does not exist.
222 // Just start each off as an empty environment:
223 if (!_environ_table.value())
224 {
225 _environ_table.value() = _calloc_crt_t(char*, 1).detach();
226 }
227
228 if (!_environ_table.value())
229 {
230 return -1;
231 }
232
233 if (!_wenviron_table.value())
234 {
235 _wenviron_table.value() = _calloc_crt_t(wchar_t*, 1).detach();
236 }
237
238 if (!_wenviron_table.value())
239 {
240 return -1;
241 }
242 }
243 }
244
245 // At this point, either [1] only one environment exists, or [2] both of the
246 // environments exist and are in-sync. The only way they can get out of sync
247 // is if there are conversion problems. For example, if the user sets two
248 // Unicode environment variables, FOO1 and FOO2, and the conversion of these
249 // to multibyte yields FOO? and FOO?, then these environment blocks will
250 // differ.
251 Character** const environment = get_environment(Character());
252 if (!environment)
253 {
254 _ASSERTE(("CRT logic error in setenv", 0));
255 return -1;
256 }
257
258 // Try to find the option in the environment...
259 ptrdiff_t const option_index = find_in_environment_nolock(option, equal_sign - option);
260
261 // ... if the string is already in the environment, we free up the original
262 // string, then install the new string or shrink the environment:
263 if (option_index >= 0 && environment[0])
264 {
265 _free_crt(environment[option_index]);
266
267 // If this is a removal, shrink the environment:
268 if (is_removal)
269 {
270 // Shift all of the entries down by one element:
271 size_t i = static_cast<size_t>(option_index);
272 for (; environment[i]; ++i)
273 {
274 environment[i] = environment[i + 1];
275 }
276
277 // Shrink the environment memory block. At this point, i is the
278 // number of elements remaining in the environment. This realloc
279 // should never fail, since we are shrinking the block, but it is
280 // best to be careful. If it does fail, it doesn't matter.
281 Character** new_environment = _recalloc_crt_t(Character*, environment, i).detach();
282 if (new_environment)
283 {
284 get_environment(Character()) = new_environment;
285 }
286 }
287 // If this is a replacement, replace the variable:
288 else
289 {
290 environment[option_index] = owned_option.detach();
291 }
292 }
293 // Otherwise, the string is not in the environment:
294 else
295 {
296 // If this is a removal, it is a no-op: the variable does not exist.
297 if (is_removal)
298 {
299 return 0;
300 }
301 // Otherwise, we need to append the string to the environment table, and
302 // we must grow the table to do this:
303 else
304 {
305 size_t const environment_count = static_cast<size_t>(-option_index);
306 if (environment_count + 2 < environment_count)
307 {
308 return -1;
309 }
310
311 if (environment_count + 2 >= SIZE_MAX / sizeof(Character*))
312 {
313 return -1;
314 }
315
316 Character** const new_environment = _recalloc_crt_t(Character*, environment, environment_count + 2).detach();
317 if (!new_environment)
318 {
319 return -1;
320 }
321
322 new_environment[environment_count] = owned_option.detach();
323 new_environment[environment_count + 1] = nullptr;
324
325 get_environment(Character()) = new_environment;
326 }
327 }
328
329 // Update the operating system environment. Do not give an error if this
330 // fails since the failure will not affect the user code unless it is making
331 // direct calls to the operating system. We only need to do this for one of
332 // the environments; the operating system synchronizes with the other
333 // environment automatically.
334 if (is_top_level_call)
335 {
336 size_t const count = traits::tcslen(option) + 2;
337 __crt_unique_heap_ptr<Character> const buffer(_calloc_crt_t(Character, count));
338 if (!buffer)
339 {
340 return 0;
341 }
342
343 Character* const name = buffer.get();
344 _ERRCHECK(traits::tcscpy_s(name, count, option));
345
346 Character* const value = name + (equal_sign - option) + 1;
347 *(value - 1) = '\0'; // Overwrite the '=' with a null terminator
348
349 if (traits::set_environment_variable(name, is_removal ? nullptr : value) == 0)
350 {
351 errno = EILSEQ;
352 return -1;
353 }
354 }
355
356 return 0;
357}
358
360 char* const option,
361 int const is_top_level_call
362 )
363{
364 return common_set_variable_in_environment_nolock(option, is_top_level_call);
365}
366
368 wchar_t* const option,
369 int const is_top_level_call
370 )
371{
372 return common_set_variable_in_environment_nolock(option, is_top_level_call);
373}
#define EINVAL
Definition: acclib.h:90
#define __cdecl
Definition: accygwin.h:79
#define _ERRCHECK(e)
#define _ASSERTE(expr)
Definition: crtdbg.h:114
result_buffer_count char *const _In_ int const _In_ bool const _In_ unsigned const _In_ STRFLT const _In_ bool const _Inout_ __crt_cached_ptd_host &ptd throw()
Definition: cvt.cpp:119
__kernel_ptrdiff_t ptrdiff_t
Definition: linux.h:247
__crt_state_management::dual_state_global< wchar_t ** > _wenviron_table
__crt_state_management::dual_state_global< char ** > _environ_table
wchar_t ** __dcrt_initial_wide_environment
char ** __dcrt_initial_narrow_environment
GLuint GLuint GLsizei count
Definition: gl.h:1545
GLuint buffer
Definition: glext.h:5915
GLuint GLsizei GLsizei * length
Definition: glext.h:6040
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
#define abort()
Definition: i386-dis.c:34
#define _MAX_ENV
Definition: stdlib.h:118
#define _free_crt
#define _VALIDATE_RETURN_NOEXC(expr, errorcode, retexpr)
#define errno
Definition: errno.h:18
#define EILSEQ
Definition: errno.h:109
#define SIZE_MAX
Definition: compat.h:66
static void __cdecl ensure_current_environment_is_not_initial_environment_nolock()
Definition: setenv.cpp:79
static char **&__cdecl get_initial_environment(char)
Definition: setenv.cpp:23
int __cdecl __dcrt_set_variable_in_narrow_environment_nolock(char *const option, int const is_top_level_call)
Definition: setenv.cpp:359
static int __cdecl common_set_variable_in_environment_nolock(Character *const option, int const is_top_level_call)
Definition: setenv.cpp:164
static wchar_t **&__cdecl get_other_environment(char)
Definition: setenv.cpp:20
static ptrdiff_t __cdecl find_in_environment_nolock(Character const *const name, size_t const length)
Definition: setenv.cpp:96
int __cdecl __dcrt_set_variable_in_wide_environment_nolock(wchar_t *const option, int const is_top_level_call)
Definition: setenv.cpp:367
static char **&__cdecl get_environment(char)
Definition: setenv.cpp:17
static Character **__cdecl copy_environment(Character **const old_environment)
Definition: setenv.cpp:32
Definition: name.c:39
Definition: getopt.h:109
Definition: pdh_main.c:96