ReactOS 0.4.16-dev-852-gcfcc8d8
cenvarg.cpp
Go to the documentation of this file.
1//
2// cenvarg.cpp
3//
4// Copyright (c) Microsoft Corporation. All rights reserved.
5//
6// Defines the _cenvarg() and _capture_argv functions, which transform argument
7// vectors and environments for use by the _exec() and _spawn() functions.
8//
9#include <corecrt_internal.h>
10#include <errno.h>
12#include <stdarg.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16
17#pragma warning(disable:__WARNING_POSTCONDITION_NULLTERMINATION_VIOLATION) // 26036
18
19
20// Converts a main()-style argv arguments vector into a command line. On success,
21// returns a pointer to the newly constructed arguments block; the caller is
22// responsible for freeing the string. On failure, returns null and sets errno.
23template <typename Character>
25 Character const* const* const argv,
26 Character** const command_line_result
27 ) throw()
28{
29 typedef __crt_char_traits<Character> traits;
30
31 *command_line_result = nullptr;
32
33 // Compute the number of bytes required to store the arguments in argv in a
34 // command line string (including spaces between arguments and a terminator):
35 size_t const command_line_count = [&]
36 {
37 size_t n = 0;
38 for (Character const* const* it = argv; *it; n += traits::tcslen(*it++) + 1) { }
39
40 // If there were no arguments, return 1 so that we can return an empty
41 // string:
42 return __max(n, 1);
43 }();
44
45 __crt_unique_heap_ptr<Character> command_line(_calloc_crt_t(Character, command_line_count));
46 if (!command_line)
47 {
49 return errno = ENOMEM;
50 }
51
52 Character const* const* source_it = argv;
53 Character* result_it = command_line.get();
54
55 // If there are no arguments, just return the empty string:
56 if (*source_it == nullptr)
57 {
58 *command_line_result = command_line.detach();
59 return 0;
60 }
61
62 // Copy the arguments, separated by spaces:
63 while (*source_it != nullptr)
64 {
65 _ERRCHECK(traits::tcscpy_s(result_it, command_line_count - (result_it - command_line.get()), *source_it));
66 result_it += traits::tcslen(*source_it);
67 *result_it++ = ' ';
68 ++source_it;
69 }
70
71 // Replace the last space with a terminator:
72 result_it[-1] = '\0';
73
74 *command_line_result = command_line.detach();
75 return 0;
76}
77
78
79
80// Converts a main()-style envp environment vector into an environment block in
81// the form required by the CreateProcess API. On success, returns a pointer to
82// the newly constructed environment block; the caller is responsible for freeing
83// the block. On failure, returns null and sets errno.
84template <typename Character>
86 _In_opt_z_ Character const* const* const envp,
87 _Outptr_result_maybenull_ Character** const environment_block_result
88 ) throw()
89{
90 typedef __crt_char_traits<Character> traits;
91
92 *environment_block_result = nullptr;
93
94 // If envp is null, we will use the current environment of this process as
95 // the environment for the new process. No action is required in this case
96 // because simply passing a null environment pointer to CreateProcess will
97 // do the right thing.
98 if (envp == nullptr)
99 return 0;
100
101 // Get the value of the SystemRoot environment variable, if it is defined,
102 // and compute the number of characters required to store it in the
103 // envrionment block:
104 Character const system_root_name[] = { 'S', 'y', 's', 't', 'e', 'm', 'R', 'o', 'o', 't', '\0' };
105
106 __crt_unique_heap_ptr<Character> system_root_value;
107 if (_ERRCHECK_EINVAL(traits::tdupenv_s_crt(system_root_value.get_address_of(), nullptr, system_root_name)) != 0)
108 return errno;
109
110 size_t const system_root_value_count = system_root_value
111 ? traits::tcslen(system_root_value.get()) + 1
112 : 0;
113
114 size_t const system_root_count = _countof(system_root_name) + system_root_value_count;
115
116 // Compute the number of characters required to hold the environment
117 // strings provided by the user:
118 size_t const envp_count = [&]
119 {
120 size_t n = 2; // Account for double null terminator
121 for (auto it = envp; *it != nullptr; n += traits::tcslen(*it++) + 1) { }
122 return n;
123 }();
124
125 // Get the current environment from the OS so that we can get the current
126 // directory strings (those starting with '=') and append them to the user-
127 // provided environment.
128 __crt_unique_heap_ptr<Character> const os_environment(traits::get_environment_from_os());
129 if (!os_environment)
130 return EINVAL;
131
132 // Find the first shell environment variable:
133 Character* const first_cwd = [&]
134 {
135 Character* it = os_environment.get();
136 while (*it != '=')
137 it += traits::tcslen(it) + 1;
138 return it;
139 }();
140
141 // Find the end of the shell environment variables (assume they are contiguous):
142 Character* const last_cwd = [&]
143 {
144 Character* it = first_cwd;
145 while (it[0] == '=' && it[1] != '\0' && it[2] == ':' && it[3] == '=')
146 it += 4 + traits::tcslen(it + 4) + 1;
147 return it;
148 }();
149
150 size_t const cwd_count = last_cwd - first_cwd;
151
152
153 // Check to see if the SystemRoot is already defined in the environment:
154 bool const system_root_defined_in_environment = [&]
155 {
156 for (auto it = envp; *it != nullptr; ++it)
157 {
158 if (traits::tcsnicmp(*it, system_root_name, traits::tcslen(system_root_name)) == 0)
159 return true;
160 }
161
162 return false;
163 }();
164
165 // Allocate storage for the new environment:
166 size_t const environment_block_count = system_root_defined_in_environment
167 ? envp_count + cwd_count
168 : envp_count + cwd_count + system_root_count;
169
170 __crt_unique_heap_ptr<Character> environment_block(_calloc_crt_t(Character, environment_block_count));
171 if (!environment_block)
172 {
174 return errno = ENOMEM;
175 }
176
177 // Build the environment block by concatenating the environment strings with
178 // null characters between them, and with a double null terminator.
179 Character* result_it = environment_block.get();
180 size_t remaining_characters = environment_block_count;
181
182 // Copy the cwd strings into the new environment:
183 if (cwd_count != 0)
184 {
185 memcpy(result_it, first_cwd, cwd_count * sizeof(Character));
186 result_it += cwd_count;
187 remaining_characters -= cwd_count;
188 }
189
190 // Copy the environment strings from envp into the new environment:
191 for (auto it = envp; *it != nullptr; ++it)
192 {
193 _ERRCHECK(traits::tcscpy_s(result_it, remaining_characters, *it));
194
195 size_t const count_copied = traits::tcslen(*it) + 1;
196 result_it += count_copied;
197 remaining_characters -= count_copied;
198 }
199
200 // Copy the SystemRoot into the new environment:
201 if (!system_root_defined_in_environment)
202 {
203 static Character const equal_sign[] = { '=', '\0' };
204
205 _ERRCHECK(traits::tcscpy_s(result_it, system_root_count, system_root_name));
206 _ERRCHECK(traits::tcscat_s(result_it, system_root_count, equal_sign));
207 if (system_root_value)
208 {
209 _ERRCHECK(traits::tcscat_s(result_it, system_root_count, system_root_value.get()));
210 }
211 result_it += system_root_count;
212 }
213
214 // Null-terminate the environment block and return it. If the environment
215 // block is empty, it requires two null terminators:
216 if (result_it == environment_block.get())
217 *result_it++ = '\0';
218
219 *result_it = '\0';
220
221 *environment_block_result = environment_block.detach();
222 return 0;
223}
224
225
226
227// Converts a main()-style argv arguments vector and envp environment vector into
228// a command line and an environment block, for use in the _exec and _spawn
229// functions. On success, returns 0 and sets the two result argumetns to point
230// to the newly created command line and environment block. The caller is
231// responsible for freeing these blocks. On failure, returns -1 and sets errno.
232template <typename Character>
233_Success_(return == 0)
234_Ret_range_(-1, 0)
236 _In_z_ Character const* const* const argv,
237 _In_opt_z_ Character const* const* const envp,
238 _Outptr_result_maybenull_ Character** const command_line_result,
239 _Outptr_result_maybenull_ Character** const environment_block_result
240 ) throw()
241{
242 typedef __crt_char_traits<Character> traits;
243
244 __crt_unique_heap_ptr<Character> command_line;
245 if (construct_command_line(argv, command_line.get_address_of()) != 0)
246 return -1;
247
248 __crt_unique_heap_ptr<Character> environment_block;
249 if (construct_environment_block(envp, environment_block.get_address_of()) != 0)
250 return -1;
251
252 *command_line_result = command_line.detach();
253 *environment_block_result = environment_block.detach();
254 return 0;
255}
256
258 char const* const* const argv,
259 char const* const* const envp,
260 char** const command_line_result,
261 char** const environment_block_result
262 )
263{
264 return common_pack_argv_and_envp(argv, envp, command_line_result, environment_block_result);
265}
266
268 wchar_t const* const* const argv,
269 wchar_t const* const* const envp,
270 wchar_t** const command_line_result,
271 wchar_t** const environment_block_result
272 )
273{
274 return common_pack_argv_and_envp(argv, envp, command_line_result, environment_block_result);
275}
276
277
278
279// Creates an argv array for the _exec and _spawn functions. This function walks
280// the provided varargs list, copying the char* or wchar_t* pointers into an
281// array. The caller_array is used first; if it is too small to fit all of the
282// arguments, an array is dynamically allocated. A pointer to the argv array is
283// returned to the caller. If the returned pointer is not 'caller_array', the
284// caller must free the array. On failure, nullptr is returned and errno is set.
285template <typename Character>
286_Success_(return != 0)
287static Character** __cdecl common_capture_argv(
290 _When_(return == caller_array, _Post_z_)
291 _Out_writes_(caller_array_count) Character** const caller_array,
294{
295 Character** argv = caller_array;
296 size_t argv_count = caller_array_count;
297
298 __crt_unique_heap_ptr<Character*> local_array;
299
300 size_t i = 0;
301 Character* next_argument = const_cast<Character*>(first_argument);
302 for (;;)
303 {
304 if (i >= argv_count)
305 {
306 _VALIDATE_RETURN_NOEXC(SIZE_MAX / 2 > argv_count, ENOMEM, nullptr);
307
308 // If we have run out of room in the caller-provided array, allocate
309 // an array on the heap and copy the contents of the caller-provided
310 // array:
311 if (argv == caller_array)
312 {
313 local_array = _calloc_crt_t(Character*, argv_count * 2);
314 _VALIDATE_RETURN_NOEXC(local_array.get() != nullptr, ENOMEM, nullptr);
315
316 _ERRCHECK(memcpy_s(local_array.get(), argv_count * 2, caller_array, caller_array_count));
317
318 argv = local_array.get();
319 }
320 // Otherwise, we have run out of room in a dynamically allocated
321 // array. We need to reallocate:
322 else
323 {
324 __crt_unique_heap_ptr<Character*> new_array(_recalloc_crt_t(Character*, local_array.get(), argv_count * 2));
325 _VALIDATE_RETURN_NOEXC(new_array.get() != nullptr, ENOMEM, nullptr);
326
327 local_array.detach();
328 local_array.attach(new_array.detach());
329
330 argv = local_array.get();
331 }
332
333 argv_count *= 2;
334 }
335
336 argv[i++] = next_argument;
337 if (!next_argument)
338 break;
339
340#pragma warning(suppress:__WARNING_INCORRECT_ANNOTATION) // 26007 Possibly incorrect single element annotation on arglist
341 next_argument = va_arg(*arglist, Character*);
342 }
343
344 // At this point, we have succeeded; either local_array is null, or argv is
345 // local_array. In either case, we detach so that we can transfer ownership
346 // to the caller:
347 local_array.detach();
348 return argv;
349}
350
352 va_list* const arglist,
353 char const* const first_argument,
354 char** const caller_array,
355 size_t const caller_array_count
356 )
357{
358 return common_capture_argv(arglist, first_argument, caller_array, caller_array_count);
359}
360
361extern "C" wchar_t** __acrt_capture_wide_argv(
362 va_list* const arglist,
363 wchar_t const* const first_argument,
364 wchar_t** const caller_array,
365 size_t const caller_array_count
366 )
367{
368 return common_capture_argv(arglist, first_argument, caller_array, caller_array_count);
369}
#define EINVAL
Definition: acclib.h:90
#define ENOMEM
Definition: acclib.h:84
#define __cdecl
Definition: accygwin.h:79
char * va_list
Definition: acmsvcex.h:78
#define va_arg(ap, T)
Definition: acmsvcex.h:89
return
Definition: dirsup.c:529
int __cdecl __acrt_pack_narrow_command_line_and_environment(char const *const *const argv, char const *const *const envp, char **const command_line_result, char **const environment_block_result)
Definition: cenvarg.cpp:257
_In_z_ Character const *const first_argument
Definition: cenvarg.cpp:289
static errno_t __cdecl construct_command_line(Character const *const *const argv, Character **const command_line_result)
Definition: cenvarg.cpp:24
static errno_t __cdecl construct_environment_block(_In_opt_z_ Character const *const *const envp, _Outptr_result_maybenull_ Character **const environment_block_result)
Definition: cenvarg.cpp:85
int __cdecl __acrt_pack_wide_command_line_and_environment(wchar_t const *const *const argv, wchar_t const *const *const envp, wchar_t **const command_line_result, wchar_t **const environment_block_result)
Definition: cenvarg.cpp:267
wchar_t ** __acrt_capture_wide_argv(va_list *const arglist, wchar_t const *const first_argument, wchar_t **const caller_array, size_t const caller_array_count)
Definition: cenvarg.cpp:361
_In_z_ Character const *const _In_ size_t const caller_array_count throw()
Definition: cenvarg.cpp:293
static int __cdecl common_pack_argv_and_envp(_In_z_ Character const *const *const argv, _In_opt_z_ Character const *const *const envp, _Outptr_result_maybenull_ Character **const command_line_result, _Outptr_result_maybenull_ Character **const environment_block_result)
Definition: cenvarg.cpp:235
char ** __acrt_capture_narrow_argv(va_list *const arglist, char const *const first_argument, char **const caller_array, size_t const caller_array_count)
Definition: cenvarg.cpp:351
_Ret_z_ _In_z_ char const _In_ size_t caller_array_count
#define _ERRCHECK(e)
void __cdecl __acrt_errno_map_os_error(unsigned long)
Definition: errno.cpp:91
#define _ERRCHECK_EINVAL(e)
#define ERROR_NOT_ENOUGH_MEMORY
Definition: dderror.h:7
#define ERROR_OUTOFMEMORY
Definition: deptool.c:13
GLdouble n
Definition: glext.h:7729
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 __max(a, b)
Definition: stdlib.h:101
#define _VALIDATE_RETURN_NOEXC(expr, errorcode, retexpr)
#define memcpy(s1, s2, n)
Definition: mkisofs.h:878
#define argv
Definition: mplay32.c:18
#define _Success_(c)
Definition: no_sal2.h:84
#define _Outptr_result_maybenull_
Definition: no_sal2.h:266
#define _In_z_
Definition: no_sal2.h:164
#define _In_opt_z_
Definition: no_sal2.h:218
#define _Ret_range_(l, h)
Definition: no_sal2.h:372
#define _Post_z_
Definition: no_sal2.h:508
#define _Out_writes_(s)
Definition: no_sal2.h:176
#define _In_
Definition: no_sal2.h:158
#define _When_(c, a)
Definition: no_sal2.h:38
#define errno
Definition: errno.h:18
#define SIZE_MAX
Definition: compat.h:66
int CDECL memcpy_s(void *dest, size_t numberOfElements, const void *src, size_t count)
Definition: heap.c:800
va_lists_t arglist[FMT_ARGMAX+1]
Definition: format.c:284
#define _countof(array)
Definition: sndvol32.h:70
int errno_t
Definition: corecrt.h:615
#define const
Definition: zconf.h:233