Leaked source code of windows server 2003
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.

671 lines
18 KiB

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