Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

702 lines
19 KiB

  1. // Copyright (c) 1998-1999 Microsoft Corporation
  2. /******************************************************************************
  3. *
  4. * EXPAND.C
  5. *
  6. *
  7. ******************************************************************************/
  8. #include <windows.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include "utilsubres.h" // resources refrenced in this file.
  13. void ErrorOutFromResource(UINT uiStringResource, ...);
  14. #define INCL_DOSPROCESS
  15. #define INCL_DOSFILEMGR
  16. #define INCL_DOSERRORS
  17. #define INCL_ERRORS
  18. #ifdef DOS
  19. #define INCL_NOXLATE_DOS16
  20. #endif
  21. #include "expand.h"
  22. #define TRUE 1
  23. #define FALSE 0
  24. #define SUCCESS 0 /* function call successful */
  25. #define FAILURE (-1) /* function call had a failure */
  26. #define READ_ONLY 0x0001 /* file is read only */
  27. #define HIDDEN 0x0002 /* file is hidden */
  28. #define SYSTEM 0x0004 /* file is a system file */
  29. #define VOLUME 0x0008 /* file is a volume label */
  30. #define SUBDIR 0x0010 /* file is a subdirectory */
  31. #define ARCHIVE 0x0020 /* file has archive bit on */
  32. #define uint unsigned int
  33. #define ulong unsigned long
  34. #define ushort unsigned short
  35. /*
  36. * struct search_rec is used to form a linked list of path specifications
  37. * that are still left to be searched.
  38. */
  39. struct search_rec {
  40. struct search_rec *next;
  41. WCHAR *dir_spec; /* path spec up until component w/ wildcard */
  42. WCHAR *wild_spec; /* component containing wildcard char(s) */
  43. WCHAR *remain; /* remainder of name after wildcard component */
  44. ushort attr;
  45. };
  46. /*
  47. * global variables
  48. */
  49. static struct search_rec *search_head = NULL;
  50. /*
  51. * prototypes of functions referenced
  52. */
  53. split_path(WCHAR *, WCHAR *, WCHAR *, WCHAR *);
  54. add_search_list(WCHAR *, WCHAR *, WCHAR *, ushort);
  55. add_arg_to_list(WCHAR *, ARGS *);
  56. do_tree(struct search_rec *, ushort, ARGS *);
  57. file_exists(WCHAR *);
  58. /******************************************************************************
  59. *
  60. * args_init()
  61. *
  62. * Initialize the ARGS struct passed as an argument.
  63. *
  64. * ENTRY:
  65. * argp = pointer to ARGS struct
  66. * maxargs = max number of args expected
  67. *
  68. * EXIT:
  69. *
  70. ******************************************************************************/
  71. void
  72. args_init( ARGS *argp,
  73. int maxargs )
  74. {
  75. argp->argc = 0;
  76. argp->argv = argp->argvp = NULL;
  77. argp->maxargc = argp->maxargs = maxargs;
  78. argp->buf = argp->bufptr = argp->bufend = NULL;
  79. }
  80. /******************************************************************************
  81. * args_trunc()
  82. *
  83. * Truncate the memory used by the ARGS struct
  84. * so that unused memory is freed.
  85. *
  86. * ENTRY:
  87. * argp = pointer to ARGS struct
  88. *
  89. * EXIT:
  90. *
  91. ******************************************************************************/
  92. void
  93. args_trunc( ARGS *argp )
  94. {
  95. /*
  96. * call realloc to shrink size of argv array, set maxargc = argc
  97. * to indicate no more room in argv array.
  98. */
  99. realloc(argp->argv, (argp->argc + 1) * sizeof(WCHAR*));
  100. argp->maxargc = argp->argc;
  101. /*
  102. * call realloc to shrink size of argument string buffer, set bufend
  103. * pointer to current buf pointer to indicate buf is full.
  104. */
  105. realloc(argp->buf, (size_t)(argp->bufptr - argp->buf));
  106. argp->bufend = argp->bufptr - 1;
  107. }
  108. /******************************************************************************
  109. *
  110. * args_reset()
  111. *
  112. * Re-initialize the ARGS struct passed as an argument,
  113. * free memory if possible.
  114. *
  115. * ENTRY:
  116. * argp = pointer to ARGS struct
  117. *
  118. * EXIT:
  119. *
  120. ******************************************************************************/
  121. void
  122. args_reset( ARGS *argp )
  123. {
  124. /*
  125. * if there is an argv array, but it has been truncated, then free
  126. * the array so a new one will be allocated later.
  127. */
  128. if (argp->argv && argp->maxargc != argp->maxargs) {
  129. free(argp->argv);
  130. argp->argv = NULL;
  131. }
  132. argp->argc = 0;
  133. argp->argvp = argp->argv;
  134. argp->maxargc = argp->maxargs;
  135. /*
  136. * if there is an argument buffer, but it has been truncated, then
  137. * free the buffer so a new one will be allocated later.
  138. */
  139. if (argp->buf && argp->bufend != argp->buf + MAX_ARG_ALLOC - 1) {
  140. free(argp->buf);
  141. argp->buf = argp->bufend = NULL;
  142. }
  143. argp->bufptr = argp->buf;
  144. }
  145. /******************************************************************************
  146. *
  147. * args_free()
  148. *
  149. * Will free the memory allocated for
  150. * argument storage by all preceeding calls to expand_path().
  151. * Args_init() must be called before reusing this ARGS structure.
  152. *
  153. * ENTRY:
  154. * argp = pointer to ARGSW struct
  155. *
  156. * EXIT:
  157. *
  158. ******************************************************************************/
  159. void
  160. args_free( ARGS *argp )
  161. {
  162. if (argp->argv != NULL)
  163. free(argp->argv);
  164. argp->argv = argp->argvp = NULL;
  165. if (argp->buf != NULL)
  166. free(argp->buf);
  167. argp->buf = argp->bufptr = argp->bufend = NULL;
  168. }
  169. /******************************************************************************
  170. *
  171. * expand_path()
  172. *
  173. * This routine will expand the specified path string into pathnames
  174. * that match. The matching pathnames will be added to the specified
  175. * argv array and the specified argc count will be incremented to
  176. * reflect the number of pathnames added.
  177. *
  178. * This routine will expand filename arguments in Unix fashion
  179. * (i.e. '[..]' is supported, '?' and '*' are allowed anywhere in the
  180. * pathname, even in the directory part of the name, and the
  181. * name/extension separator '.' is not treated special but is just
  182. * considered part of the filename).
  183. *
  184. * Storage for the pathname strings will be obtained via malloc.
  185. * This space may later be free'd with a call to args_free();
  186. *
  187. * ENTRY:
  188. * path Pathname string to be expanded.
  189. * attr Attribute bits of files to include
  190. * (regular, directory, hidden, system).
  191. * -1 = return the specified pathname string unmodified
  192. * in the argv array.
  193. * argp Pointer to an ARGSW struct containing fields to be used/
  194. * updated by expand_path. The ARGS struct must be initialized
  195. * by calling args_init() before calling expand_path().
  196. *
  197. * EXIT:
  198. * TRUE -- indicates at least 1 pathname was found matching
  199. * the pathname string specified.
  200. * FALSE -- indicates no matching pathnames were found. The specified
  201. * pathname string is returned unmodified in the argv array.
  202. *
  203. ******************************************************************************/
  204. int
  205. expand_path( WCHAR *path,
  206. ushort attr,
  207. ARGS *argp )
  208. {
  209. int argc, add_count, rc, i, j, k;
  210. WCHAR **argv;
  211. WCHAR dirname[128], wild[128], remain[128];
  212. struct search_rec *save, *q;
  213. #ifdef DEBUG
  214. printf("expand_path: path=%s attr=%d\n", path, attr);
  215. #endif
  216. argc = argp->argc;
  217. argv = argp->argvp;
  218. if ( attr != -1 && split_path(path, dirname, wild, remain)) {
  219. add_search_list(dirname, wild, remain, attr);
  220. while (search_head) {
  221. /*
  222. * save the next portion and allow new directories to be
  223. * added to the head.
  224. */
  225. save = search_head->next;
  226. search_head->next = NULL;
  227. /*
  228. * perform the do_tree operation on the current path
  229. */
  230. rc = do_tree(search_head, attr, argp);
  231. /*
  232. * restore the saved list at the end of the head list
  233. */
  234. if ( save ) {
  235. q = search_head;
  236. while ( q->next ) {
  237. q = q->next;
  238. }
  239. q->next = save;
  240. }
  241. /*
  242. * move to the next path in the list and free the memory used
  243. * by the link we are done with
  244. */
  245. do {
  246. q = search_head;
  247. search_head = search_head->next;
  248. free( q->dir_spec );
  249. free( q->wild_spec );
  250. free( q->remain );
  251. free( q );
  252. } while (rc==FAILURE && search_head);
  253. }
  254. }
  255. /*
  256. * If no filenames were expanded, just put the original name
  257. * into the buffer and indicate no names were expanded.
  258. */
  259. if (argc == argp->argc) {
  260. add_arg_to_list(path, argp);
  261. return(FALSE);
  262. }
  263. /*
  264. * Sort the names just added
  265. */
  266. if ( argv == NULL )
  267. argv = argp->argv;
  268. add_count = argp->argc - argc;
  269. for (i=add_count-1; i>0; --i) {
  270. uint swap = FALSE;
  271. for (j=0; j<i; ++j) {
  272. if (!argv[j] || !argv[j+1]) {
  273. ErrorOutFromResource(IDS_INTERNAL_ERROR_1);
  274. //fprintf(stderr,"internal error 1\n");
  275. }
  276. for (k=0; k<128; ++k) {
  277. if (argv[j][k] < argv[j+1][k]) {
  278. break;
  279. } else if (argv[j][k] > argv[j+1][k]) {
  280. WCHAR *temp;
  281. swap = TRUE;
  282. temp = argv[j];
  283. argv[j] = argv[j+1];
  284. argv[j+1] = temp;
  285. break;
  286. }
  287. }
  288. if (k>125) {
  289. ErrorOutFromResource(IDS_INTERNAL_ERROR_2);
  290. // fprintf(stderr,"internal error 2\n");
  291. }
  292. }
  293. if (!swap) {
  294. break;
  295. }
  296. }
  297. return(TRUE);
  298. }
  299. /******************************************************************************
  300. *
  301. * add_search_list()
  302. *
  303. * Adds a record to the global search list, search_head.
  304. *
  305. ******************************************************************************/
  306. static
  307. add_search_list(
  308. WCHAR *dir_spec, /* the dir to be added to the list */
  309. WCHAR *wild_spec, /* the file to be added to the list */
  310. WCHAR *remain_spec, /* remaining portion of pathname */
  311. ushort attr )
  312. {
  313. struct search_rec *new, /* pointer to the new link */
  314. *q; /* used to traverse the linked list */
  315. #ifdef DEBUG
  316. wprintf(L"add_search_list: dir=%s: file=%s: rem=%s:\n", dir_spec, wild_spec, remain_spec);
  317. #endif
  318. /*
  319. * allocate the new link. make sure that it is initialized to zeros.
  320. */
  321. new = malloc(sizeof(struct search_rec));
  322. if (!new) {
  323. ErrorOutFromResource(IDS_ADD_SRCH_LIST_NO_MEMORY_MALLOC);
  324. // fprintf(stderr, "add_search_list: not enough memory (malloc)");
  325. return FAILURE;
  326. }
  327. memset(new, 0, sizeof(struct search_rec));
  328. /*
  329. * allocate memory for and copy the dir spec and file spec.
  330. */
  331. if (dir_spec)
  332. {
  333. new->dir_spec = _wcsdup(dir_spec);
  334. if( new->dir_spec == NULL )
  335. {
  336. ErrorOutFromResource(IDS_ADD_SRCH_LIST_NO_MEMORY_STRDUP1);
  337. // fprintf(stderr, "add_search_list: not enough memory (strdup1)");
  338. return FAILURE;
  339. }
  340. _wcslwr( new->dir_spec );
  341. }
  342. if (wild_spec)
  343. {
  344. new->wild_spec = _wcsdup(wild_spec);
  345. if (new->wild_spec == NULL )
  346. {
  347. ErrorOutFromResource(IDS_ADD_SRCH_LIST_NO_MEMORY_STRDUP2);
  348. // fprintf(stderr, "add_search_list: not enough memory (strdup2)");
  349. return FAILURE;
  350. }
  351. _wcslwr( new->wild_spec );
  352. }
  353. if (remain_spec)
  354. {
  355. new->remain = _wcsdup(remain_spec);
  356. if( new->remain == NULL )
  357. {
  358. ErrorOutFromResource(IDS_ADD_SRCH_LIST_NO_MEMORY_STRDUP3);
  359. // fprintf(stderr, "add_search_list: not enough memory (strdup3)");
  360. return FAILURE;
  361. }
  362. _wcslwr( new->remain );
  363. }
  364. /*
  365. * store file attributes
  366. */
  367. if (remain_spec)
  368. new->attr = attr | SUBDIR;
  369. else
  370. new->attr = attr;
  371. /*
  372. * add the new link at the end of the list
  373. */
  374. if (!search_head) {
  375. search_head = new;
  376. } else {
  377. q = search_head;
  378. while (q->next) {
  379. q = q->next;
  380. }
  381. q->next = new;
  382. }
  383. return SUCCESS;
  384. }
  385. /******************************************************************************
  386. *
  387. * add_arg_to_list()
  388. *
  389. * This routine adds the specified argument string to the argv array,
  390. * and increments the argv pointer and argc counter.
  391. * If necessary, memory for the argument string is allocated.
  392. *
  393. * EXIT:
  394. * SUCCESS -- if argument added successfully
  395. * FAILURE -- if argument could not be added
  396. * (indicates too many args or out of memory for argument string)
  397. *
  398. ******************************************************************************/
  399. static int
  400. add_arg_to_list( WCHAR *arg_string,
  401. ARGS *argp )
  402. {
  403. int len;
  404. #ifdef DEBUG
  405. wprintf(L"add_arg_to_list: arg_string=%s:, argc=%d, argvp=%x, maxargs=%d\n",
  406. arg_string,argp->argc,argp->argvp,argp->maxargc);
  407. #endif
  408. if (argp->argc >= argp->maxargc) {
  409. ErrorOutFromResource(IDS_TOO_MANY_ARGUMENTS);
  410. // fprintf(stderr,"add_arg_to_list: too many arguments\n");
  411. return FAILURE;
  412. }
  413. if (!argp->argv) {
  414. argp->argv = malloc(sizeof(WCHAR *) * (argp->maxargs+1));
  415. if (argp->argv) {
  416. argp->argc = 0;
  417. argp->argvp = argp->argv;
  418. argp->maxargc = argp->maxargs;
  419. } else {
  420. ErrorOutFromResource(IDS_ARGS_TO_LIST_NOT_ENOUGH_MEMORY);
  421. // fprintf(stderr,"add_arg_to_list: not enough memory\n");
  422. return FAILURE;
  423. }
  424. }
  425. if (!argp->buf) {
  426. argp->buf = malloc(MAX_ARG_ALLOC);
  427. if (argp->buf) {
  428. argp->bufptr = argp->buf;
  429. argp->bufend = argp->buf + MAX_ARG_ALLOC - 1;
  430. } else {
  431. ErrorOutFromResource(IDS_ARGS_TO_LIST_NOT_ENOUGH_MEMORY);
  432. // fprintf(stderr,"add_arg_to_list: not enough memory\n");
  433. return FAILURE;
  434. }
  435. }
  436. len = wcslen(arg_string) + 1;
  437. if (argp->bufptr + len > argp->bufend) {
  438. ErrorOutFromResource(IDS_ARGS_TO_LIST_ARG_BUFFER_SMALL);
  439. // fprintf(stderr,"add_arg_to_list: argument buffer too small\n");
  440. return FAILURE;
  441. }
  442. wcscpy(argp->bufptr, arg_string);
  443. *(argp->argvp) = argp->bufptr;
  444. argp->bufptr += len;
  445. ++argp->argc;
  446. ++argp->argvp;
  447. *(argp->argvp) = NULL;
  448. return SUCCESS;
  449. }
  450. /******************************************************************************
  451. *
  452. * do_tree()
  453. *
  454. ******************************************************************************/
  455. static
  456. do_tree( struct search_rec *searchp,
  457. ushort attr,
  458. ARGS *argp )
  459. {
  460. int rc; /* return code from Dos calls */
  461. WIN32_FIND_DATA result; /* the structure returned from FindFirst/Next */
  462. ushort count = 1; /* number of files to look for at one time */
  463. HANDLE handle; /* the dir handle used by FindFirst/Next */
  464. WCHAR full_path[128]; /* used to hold the path/file combination */
  465. WCHAR dirname[128], wild[128], remain[128];
  466. WCHAR *fptr; /* pointer to file portion of full_path */
  467. ULONG Status;
  468. #ifdef DEBUG
  469. wprintf(L"do_tree: dirname=%s:\n", searchp->dir_spec);
  470. #endif
  471. /*
  472. * build up directory part of path and save a pointer to the file portion
  473. */
  474. wcscpy(full_path, searchp->dir_spec);
  475. fptr = full_path + wcslen(searchp->dir_spec);
  476. wcscpy(fptr, L"*.*");
  477. handle = FindFirstFile ( full_path, /* files to find */
  478. &result
  479. );
  480. if(handle == INVALID_HANDLE_VALUE){
  481. Status = GetLastError();
  482. if(Status == ERROR_NO_MORE_FILES) {
  483. // no files match
  484. return(SUCCESS);
  485. }
  486. return(FAILURE);
  487. }
  488. rc = TRUE;
  489. while (rc) {
  490. /*
  491. * do not do anything for the "." and ".." entries
  492. */
  493. if (wcscmp(result.cFileName, L".") == 0 ||
  494. wcscmp(result.cFileName, L"..") == 0) {
  495. rc = FindNextFile( handle, &result );
  496. continue;
  497. }
  498. /*
  499. * fully qualify the found file
  500. */
  501. wcscpy(fptr, _wcslwr(result.cFileName));
  502. if (searchp->remain)
  503. wcscat(full_path, searchp->remain);
  504. /*
  505. * see if current wild_spec matches FindFirst/Next file
  506. */
  507. if (unix_match(searchp->wild_spec, result.cFileName)) {
  508. if (searchp->remain && split_path(full_path, dirname, wild, remain)) {
  509. if (result.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
  510. file_exists(dirname))
  511. add_search_list(dirname, wild, remain, attr);
  512. } else if (file_exists(full_path)) {
  513. rc = add_arg_to_list(full_path, argp);
  514. if (rc != SUCCESS)
  515. break;
  516. }
  517. }
  518. /*
  519. * find the next file
  520. */
  521. rc = FindNextFile( handle, &result );
  522. }
  523. /*
  524. * if no more files to find then reset the error code back to successful.
  525. */
  526. if(!rc) {
  527. Status = GetLastError();
  528. if(Status == ERROR_NO_MORE_FILES)
  529. rc = SUCCESS;
  530. }
  531. return rc;
  532. }
  533. /******************************************************************************
  534. *
  535. * split_path()
  536. *
  537. * This routine splits the specified pathname into 3 parts, any of which
  538. * may be null; 1) the pathname from the beginning up to but not including
  539. * the first component containing a wildcard character, 2) the component
  540. * containing the wildcard, and 3) the remainder of the path string after
  541. * the component containing the wildcard.
  542. *
  543. * Examples:
  544. * Original path dir file remain
  545. * "c:\mydir\dir??\*.c" "c:\mydir\" "dir??" "\*.c"
  546. * "*\abc.def" "" "*" "\abc.def"
  547. * "mydir\*.c" "mydir\" "*.c" ""
  548. *
  549. * EXIT:
  550. * TRUE -- if the pathname could be split
  551. * FALSE -- otherwise (i.e. pathname did not contain any wildcards)
  552. *
  553. ******************************************************************************/
  554. static int
  555. split_path( WCHAR *path,
  556. WCHAR *dir,
  557. WCHAR *file,
  558. WCHAR *remain )
  559. {
  560. WCHAR *cp, *end_dir, *end_wild = NULL;
  561. #ifdef DEBUG
  562. wprintf("split_path: path=%s:\n", path);
  563. #endif
  564. for (cp=end_dir=path; *cp!=L'\0'; ) {
  565. if (*cp==L'\\' || *cp==L'/' || *cp==L':') {
  566. ++cp;
  567. while (*cp==L'\\' || *cp==L'/' ) ++cp;
  568. end_dir = cp;
  569. } else if (*cp==L'*' || *cp==L'?' || *cp==L'[') {
  570. ++cp;
  571. while (*cp!=L'\\' && *cp!=L'/' && *cp!=L'\0') ++cp;
  572. end_wild = cp;
  573. break;
  574. } else {
  575. ++cp;
  576. }
  577. }
  578. if (!end_wild)
  579. return(FALSE);
  580. for (cp=path; cp<end_dir; ++cp, ++dir)
  581. *dir = *cp;
  582. *dir = L'\0';
  583. for (cp=end_dir; cp<end_wild; ++cp, ++file)
  584. *file = *cp;
  585. *file = L'\0';
  586. wcscpy(remain, cp);
  587. #ifdef DEBUG
  588. wprintf("split_path: dir=%s: file=%s: remain=%s:\n", dir, file, remain);
  589. #endif
  590. return(TRUE);
  591. }
  592. /******************************************************************************
  593. *
  594. * file_existsW()
  595. *
  596. * Returns TRUE if specified file exists, otherwise returns FALSE.
  597. *
  598. ******************************************************************************/
  599. static int
  600. file_exists( WCHAR *path )
  601. {
  602. int len;
  603. WCHAR path2[128];
  604. WCHAR ch;
  605. ULONG Result;
  606. wcscpy(path2, path);
  607. len = wcslen(path2);
  608. while ((ch=path2[--len]) == L'\\' || ch == L'/' ) path2[len] = L'\0';
  609. Result = GetFileAttributes(path2);
  610. if(Result == 0xFFFFFFFF) {
  611. return(FALSE);
  612. }
  613. return(TRUE);
  614. }