Reactos
1/***
2*wild.c - wildcard expander
3*
4* Copyright (c) Microsoft Corporation. All rights reserved.
5*
6*Purpose:
7* expands wildcards in argv
8*
9* handles '*' (none or more of any char) and '?' (exactly one char)
10*
11*
12*******************************************************************************/
13
14#include <corecrt_internal.h>
15#include <ctype.h>
16#include <corecrt_internal_traits.h>
17#include <limits.h>
18#include <mbstring.h>
19#include <stddef.h>
20#include <stdlib.h>
21#include <string.h>
22#include <wrl/wrappers/corewrappers.h>
23
24
25
26namespace
27{
28 template <typename Character>
29 class argument_list
30 {
31 public:
32
33 argument_list() throw() : _first(nullptr), _last(nullptr), _end(nullptr) { }
34
35 size_t size() const throw() { return _last - _first; }
36 Character** begin() const throw() { return _first; }
37 Character** end() const throw() { return _last; }
38
39 errno_t append(Character* const element) throw()
40 {
41 errno_t const expand_status = expand_if_necessary();
42 if (expand_status != 0)
43 {
44 _free_crt(element);
45 return expand_status;
46 }
47
48 *_last++ = element;
49 return 0;
50 }
51
52 Character** detach() throw()
53 {
54 _last = nullptr;
55 _end = nullptr;
56
57 Character** const first = _first;
58 _first = nullptr;
59 return first;
60 }
61
62 ~argument_list() throw()
63 {
64 for (auto it = _first; it != _last; ++it)
65 _free_crt(*it);
66
67 _free_crt(_first);
68 }
69
70 private:
71
72 argument_list(argument_list const&) throw(); // not implemented
73 argument_list& operator=(argument_list const&) throw(); // not implemented
74
75 errno_t expand_if_necessary() throw()
76 {
77 // If there is already room for more elements, just return:
78 if (_last != _end)
79 {
80 return 0;
81 }
82 // If the list has not yet had an array allocated for it, allocate one:
83 if (!_first)
84 {
85 size_t const initial_count = 4;
86
87 _first = _calloc_crt_t(Character*, initial_count).detach();
88 if (!_first)
89 return ENOMEM;
90
91 _last = _first;
92 _end = _first + initial_count;
93 return 0;
94 }
95 // Otherwise, double the size of the array:
96 else
97 {
98 size_t const old_count = _end - _first;
99 if (old_count > SIZE_MAX / 2)
100 return ENOMEM;
101
102 size_t const new_count = old_count * 2;
103 __crt_unique_heap_ptr<Character*> new_array(_recalloc_crt_t(Character*, _first, new_count));
104 if (!new_array)
105 return ENOMEM;
106
107 _first = new_array.detach();
108 _last = _first + old_count;
109 _end = _first + new_count;
110 return 0;
111 }
112 }
113
114 Character** _first;
115 Character** _last;
116 Character** _end;
117 };
118}
119
120_Check_return_
121static char*
122previous_character(_In_reads_z_(current - first + 1) char* const first,
123 _In_z_ char* const current) throw()
124{
125 return reinterpret_cast<char*>(_mbsdec(
126 reinterpret_cast<unsigned char*>(first),
127 reinterpret_cast<unsigned char*>(current)));
128}
129
130static wchar_t* previous_character(_In_reads_(0) wchar_t*, _In_reads_(0) wchar_t* const current) throw()
131{
132 return current - 1;
133}
134
135
136
137template <typename Character>
138static errno_t copy_and_add_argument_to_buffer(
139 _In_z_ Character const* const file_name,
140 _In_z_ Character const* const directory,
141 size_t const directory_length,
142 argument_list<Character>& buffer
143 ) throw()
144{
145 typedef __crt_char_traits<Character> traits;
146
147 size_t const file_name_count = traits::tcslen(file_name) + 1;
148 if (file_name_count > SIZE_MAX - directory_length)
149 return ENOMEM;
150
151 size_t const required_count = directory_length + file_name_count + 1;
152 __crt_unique_heap_ptr<Character> argument_buffer(_calloc_crt_t(Character, required_count));
153
154 if (directory_length > 0)
155 {
156 _ERRCHECK(traits::tcsncpy_s(argument_buffer.get(), required_count, directory, directory_length));
157 }
158
159 _ERRCHECK(traits::tcsncpy_s(
160 argument_buffer.get() + directory_length,
161 required_count - directory_length,
162 file_name,
163 file_name_count));
164
165 return buffer.append(argument_buffer.detach());
166}
167
168
169static wchar_t * get_wide(__crt_internal_win32_buffer<wchar_t> * const dest, char * const source)
170{
171 errno_t const cvt1 = __acrt_mbs_to_wcs_cp(
172 source,
173 *dest,
174 __acrt_get_utf8_acp_compatibility_codepage()
175 );
176
177 if (cvt1 != 0)
178 {
179 return nullptr;
180 }
181
182 return dest->data();
183};
184
185static wchar_t * get_wide(__crt_internal_win32_buffer<wchar_t> *, wchar_t * const source)
186{
187 return source;
188}
189
190static char * get_file_name(__crt_internal_win32_buffer<char> * const dest, wchar_t * const source)
191{
192 errno_t const cvt = __acrt_wcs_to_mbs_cp(
193 source,
194 *dest,
195 __acrt_get_utf8_acp_compatibility_codepage()
196 );
197
198 if (cvt != 0)
199 {
200 return nullptr;
201 }
202
203 return dest->data();
204}
205
206static wchar_t * get_file_name(__crt_internal_win32_buffer<wchar_t> *, wchar_t * const source)
207{
208 return source;
209}
210
211template <typename Character>
212static errno_t expand_argument_wildcards(
213 Character* const argument,
214 Character* const wildcard,
215 argument_list<Character>& buffer
216 ) throw()
217{
218 typedef __crt_char_traits<Character> traits;
219 typedef typename traits::win32_find_data_type find_data_type;
220
221 auto const is_directory_separator = [](Character const c) { return c == '/' || c == '\\' || c == ':'; };
222
223 // Find the first slash or colon before the wildcard:
224 Character* it = wildcard;
225 while (it != argument && !is_directory_separator(*it))
226 {
227 it = previous_character(argument, it);
228 }
229
230 // If we found a colon that can't form a drive name (e.g. it can't be 'D:'),
231 // then just add the argument as-is (we don't know how to expand it):
232 if (*it == ':' && it != argument + 1)
233 {
234 return copy_and_add_argument_to_buffer(argument, static_cast<Character*>(nullptr), 0, buffer);
235 }
236
237 size_t const directory_length = is_directory_separator(*it)
238 ? it - argument + 1 // it points to the separator, so add 1 to include it.
239 : 0;
240
241 // Try to begin the find operation:
242 WIN32_FIND_DATAW findFileDataW;
243 __crt_internal_win32_buffer<wchar_t> wide_file_name;
244
245 __crt_findfile_handle const find_handle(::FindFirstFileExW(
246 get_wide(&wide_file_name, argument),
247 FindExInfoStandard,
248 &findFileDataW,
249 FindExSearchNameMatch,
250 nullptr,
251 0));
252
253 // If the find operation failed, there was no match, so just add the argument:
254 if (find_handle.get() == INVALID_HANDLE_VALUE)
255 {
256 return copy_and_add_argument_to_buffer(argument, static_cast<Character*>(nullptr), 0, buffer);
257 }
258
259 size_t const old_argument_count = buffer.size();
260
261 do
262 {
263 __crt_internal_win32_buffer<Character> character_buffer;
264 Character* const file_name = get_file_name(&character_buffer, findFileDataW.cFileName);
265 // Skip . and ..:
266 if (file_name[0] == '.' && file_name[1] == '\0')
267 {
268 continue;
269 }
270
271 if (file_name[0] == '.' && file_name[1] == '.' && file_name[2] == '\0')
272 {
273 continue;
274 }
275
276 errno_t const add_status = copy_and_add_argument_to_buffer(file_name, argument, directory_length, buffer);
277 if (add_status != 0)
278 {
279 return add_status;
280 }
281 }
282 while (::FindNextFileW(find_handle.get(), &findFileDataW));
283
284 // If we didn't add any arguments to the buffer, then we're done:
285 size_t const new_argument_count = buffer.size();
286 if (old_argument_count == new_argument_count)
287 {
288 return 0;
289 }
290
291 // If we did add new arguments, let's helpfully sort them:
292 qsort(
293 buffer.begin() + old_argument_count,
294 new_argument_count - old_argument_count,
295 sizeof(Character*),
296 [](void const* lhs, void const* rhs) -> int
297 {
298 if (lhs < rhs) { return -1; }
299 if (lhs > rhs) { return 1; }
300 return 0;
301 });
302
303 return 0;
304}
305
306
307template <typename Character>
308static errno_t common_expand_argv_wildcards(Character** const argv, Character*** const result) throw()
309{
310 typedef __crt_char_traits<Character> traits;
311
312 _VALIDATE_RETURN_ERRCODE(result != nullptr, EINVAL);
313 *result = nullptr;
314
315 argument_list<Character> expansion_buffer;
316 for (Character** it = argv; *it != nullptr; ++it)
317 {
318 Character const wildcard_characters[] = { '*', '?', '\0' };
319 Character* const wildcard = traits::tcspbrk(*it, wildcard_characters);
320
321 // If no wildcard characters were found in the argument string, just
322 // append it to the list and continue on. Otherwise, do the expansion:
323 if (!wildcard)
324 {
325 errno_t const append_status = copy_and_add_argument_to_buffer(
326 *it,
327 static_cast<Character*>(nullptr),
328 0,
329 expansion_buffer);
330
331 if (append_status != 0)
332 return append_status;
333 }
334 else
335 {
336 errno_t const expand_status = expand_argument_wildcards(*it, wildcard, expansion_buffer);
337 if (expand_status != 0)
338 return expand_status;
339 }
340 }
341
342 // Now that we've accumulated the expanded arguments into the expansion
343 // buffer, we want to re-pack them in the form used by the argv parser,
344 // in a single array, with everything "concatenated" together.
345 size_t const argument_count = expansion_buffer.size() + 1;
346 size_t character_count = 0;
347 for (auto it = expansion_buffer.begin(); it != expansion_buffer.end(); ++it)
348 character_count += traits::tcslen(*it) + 1;
349
350 __crt_unique_heap_ptr<unsigned char> expanded_argv(__acrt_allocate_buffer_for_argv(
351 argument_count,
352 character_count,
353 sizeof(Character)));
354
355 if (!expanded_argv)
356 return -1;
357
358 Character** const argument_first = reinterpret_cast<Character**>(expanded_argv.get());
359 Character* const character_first = reinterpret_cast<Character*>(
360 expanded_argv.get() +
361 argument_count * sizeof(Character*));
362
363 Character** argument_it = argument_first;
364 Character* character_it = character_first;
365 for (auto it = expansion_buffer.begin(); it != expansion_buffer.end(); ++it)
366 {
367 size_t const count = traits::tcslen(*it) + 1;
368
369 _ERRCHECK(traits::tcsncpy_s(
370 character_it,
371 character_count - (character_it - character_first),
372 *it,
373 count));
374
375 *argument_it++ = character_it;
376 character_it += count;
377 }
378
379 *result = reinterpret_cast<Character**>(expanded_argv.detach());
380 return 0;
381}
382
383extern "C" errno_t __acrt_expand_narrow_argv_wildcards(char** const argv, char*** const result)
384{
385 return common_expand_argv_wildcards(argv, result);
386}
387
388extern "C" errno_t __acrt_expand_wide_argv_wildcards(wchar_t** const argv, wchar_t*** const result)
389{
390 return common_expand_argv_wildcards(argv, result);
391}