Reactos
at master 391 lines 12 kB view raw
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}