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.

872 lines
25 KiB

  1. /*++
  2. Copyright (c) 1988-1999 Microsoft Corporation
  3. Module Name:
  4. tree.c
  5. Abstract:
  6. Tree walking
  7. --*/
  8. #include "cmd.h"
  9. extern TCHAR CurDrvDir[] ;
  10. extern TCHAR *SaveDir ;
  11. extern DWORD DosErr ;
  12. extern BOOL CtrlCSeen;
  13. PTCHAR SetWildCards( PTCHAR, BOOLEAN );
  14. BOOLEAN IsFATDrive( PTCHAR );
  15. VOID SortFileList( PFS, PSORTDESC, ULONG);
  16. BOOLEAN FindFirstNt( PTCHAR, PWIN32_FIND_DATA, PHANDLE );
  17. BOOLEAN FindNextNt ( PWIN32_FIND_DATA, HANDLE );
  18. STATUS
  19. BuildFSFromPatterns (
  20. IN PDRP pdpr,
  21. IN BOOLEAN fPrintErrors,
  22. IN BOOLEAN fAddWild,
  23. OUT PFS * ppfs
  24. )
  25. {
  26. PCPYINFO pcisFile;
  27. TCHAR szCurDir[MAX_PATH + 2];
  28. TCHAR szFilePattern[MAX_PATH + 2];
  29. PTCHAR pszPatternCur;
  30. PPATDSC ppatdscCur;
  31. PFS pfsFirst;
  32. PFS pfsCur;
  33. ULONG cbPath;
  34. BOOLEAN fFatDrive;
  35. ULONG i;
  36. PTCHAR pszT;
  37. //
  38. // determine FAT drive from original pattern.
  39. // Used in several places to control name format etc.
  40. //
  41. DosErr = 0;
  42. //
  43. // Run through each pattern making all sorts of FAT etc. specific
  44. // changes to it and creating the directory list for it. Then
  45. // combine groups of patterns into common directories and recurse
  46. // for each directory group.
  47. //
  48. *ppfs = pfsFirst = (PFS)gmkstr(sizeof(FS));
  49. pfsFirst->pfsNext = NULL;
  50. pfsFirst->pszDir = NULL;
  51. pfsCur = pfsFirst;
  52. pfsCur->cpatdsc = 1;
  53. for(i = 1, ppatdscCur = &(pdpr->patdscFirst);
  54. i <= pdpr->cpatdsc;
  55. i++, ppatdscCur = ppatdscCur->ppatdscNext) {
  56. pszPatternCur = ppatdscCur->pszPattern;
  57. if (!(fFatDrive = IsFATDrive(pszPatternCur)) && DosErr) {
  58. //
  59. // Error in determining file system type so get out.
  60. //
  61. if (fPrintErrors) PutStdErr(DosErr, NOARGS);
  62. return( FAILURE );
  63. }
  64. ppatdscCur->fIsFat = fFatDrive;
  65. //
  66. // Do any alterations that require wild cards for searching
  67. // such as change .xxx to *.xxx for FAT file system requests
  68. //
  69. // Note that if the return values is a different buffer then
  70. // the input the input will be freed when we are done with the
  71. // Dir command.
  72. //
  73. //
  74. // Note that though SetWildCards will allocate heap for the
  75. // modified pattern this will get freed when FreeStack is
  76. // called at the end of the Dir call.
  77. //
  78. // An out of memory is the only reason to fail and we would not
  79. // return from that but go through the abort call in gmstr
  80. //
  81. if (fAddWild) {
  82. pszT = SetWildCards(pszPatternCur, fFatDrive);
  83. FreeStr(pszPatternCur);
  84. pszPatternCur = pszT;
  85. }
  86. //
  87. // Convert the current pattern into a path and file part
  88. //
  89. // Save the current directory in SaveDir, change to new directory
  90. // and parse pattern into a copy information structure. This also
  91. // converts pszPatternCur into the current directory which also produces
  92. // a fully qualified name.
  93. //
  94. DosErr = 0;
  95. DEBUG((ICGRP, DILVL, "PrintPattern pattern `%ws'", pszPatternCur));
  96. if ((pcisFile = SetFsSetSaveDir(pszPatternCur)) == (PCPYINFO) FAILURE) {
  97. //
  98. // DosErr is set in SetFs.. from GetLastError
  99. //
  100. if (fPrintErrors)
  101. PutStdErr(DosErr, NOARGS);
  102. return( FAILURE );
  103. }
  104. DEBUG((ICGRP, DILVL, "PrintPattern fullname `%ws'", pcisFile->fnptr));
  105. //
  106. // CurDrvDir ends in '\' (old code also and a DOT but I do not
  107. // understand where this would come from I will leave it in for now.
  108. // Remove the final '\' from a copy of the current directory and
  109. // print that version out.
  110. //
  111. mystrcpy(szCurDir,CurDrvDir);
  112. //
  113. // SetFsSetSaveDir changes directories as a side effect. Since all
  114. // work will be in fully qualified paths we do not need this. Also
  115. // since we will change directories for each pattern that is examined
  116. // we will force the directory back to the original each time.
  117. //
  118. // This can not be done until after all use of the current directory
  119. // is made.
  120. //
  121. RestoreSavedDirectory( );
  122. DEBUG((ICGRP, DILVL, "PrintPattern Current Drive `%ws'", szCurDir));
  123. cbPath = mystrlen(szCurDir);
  124. if (cbPath > 3) {
  125. if (fFatDrive && *penulc(szCurDir) == DOT) {
  126. szCurDir[cbPath-2] = NULLC;
  127. } else {
  128. szCurDir[cbPath-1] = NULLC;
  129. }
  130. }
  131. //
  132. // If no room for filename then return
  133. //
  134. if (cbPath >= MAX_PATH -1) {
  135. if (fPrintErrors) PutStdErr( ERROR_FILE_NOT_FOUND, NOARGS );
  136. return(FAILURE);
  137. }
  138. //
  139. // Add filename and possibly ext to szSearchPath
  140. // if no filename or ext, use "*"
  141. //
  142. // If pattern was just extension the SetWildCard had already
  143. // added * to front of extension.
  144. //
  145. if (*(pcisFile->fnptr) == NULLC) {
  146. mystrcpy(szFilePattern, TEXT("*"));
  147. } else {
  148. mystrcpy(szFilePattern, pcisFile->fnptr);
  149. }
  150. DEBUG((ICGRP, DILVL, "DIR:PrintPattern Pattern to search for `%ws'", szFilePattern));
  151. //
  152. // Is name too long
  153. //
  154. if ((cbPath + mystrlen(szFilePattern) + 1) > MAX_PATH ) {
  155. if (fPrintErrors) PutStdErr(ERROR_BUFFER_OVERFLOW, NOARGS);
  156. return( FAILURE );
  157. } else {
  158. //
  159. // If this is a FAT drive and there was a filename with
  160. // no extension then add '.*' (and there is room)
  161. //
  162. if (*pcisFile->fnptr && (!pcisFile->extptr || !*pcisFile->extptr) &&
  163. ((mystrlen(szFilePattern) + 2) < MAX_PATH) && fFatDrive && fAddWild) {
  164. mystrcat(szFilePattern, TEXT(".*")) ;
  165. }
  166. }
  167. //
  168. // ppatdscCur->pszPattern will be freed at end of command when everything
  169. // else is freed.
  170. //
  171. ppatdscCur->pszPattern = (PTCHAR)gmkstr(_tcslen(szFilePattern)*sizeof(TCHAR) + sizeof(TCHAR));
  172. mystrcpy(ppatdscCur->pszPattern, szFilePattern);
  173. ppatdscCur->pszDir = (PTCHAR)gmkstr(_tcslen(szCurDir)*sizeof(TCHAR) + sizeof(TCHAR));
  174. mystrcpy(ppatdscCur->pszDir, szCurDir);
  175. if (pfsCur->pszDir) {
  176. //
  177. // changing directories so change directory grouping.
  178. //
  179. if (_tcsicmp(pfsCur->pszDir, ppatdscCur->pszDir)) {
  180. pfsCur->pfsNext = (PFS)gmkstr(sizeof(FS));
  181. pfsCur = pfsCur->pfsNext;
  182. pfsCur->pszDir = (PTCHAR)gmkstr(_tcslen(ppatdscCur->pszDir)*sizeof(TCHAR) + sizeof(TCHAR));
  183. mystrcpy(pfsCur->pszDir, ppatdscCur->pszDir);
  184. pfsCur->pfsNext = NULL;
  185. pfsCur->fIsFat = ppatdscCur->fIsFat;
  186. pfsCur->ppatdsc = ppatdscCur;
  187. pfsCur->cpatdsc = 1;
  188. } else {
  189. pfsCur->cpatdsc++;
  190. }
  191. } else {
  192. //
  193. // Have not filled in current fs descriptor yet.
  194. //
  195. pfsCur->pszDir = (PTCHAR)gmkstr(_tcslen(ppatdscCur->pszDir)*sizeof(TCHAR) + 2*sizeof(TCHAR));
  196. mystrcpy(pfsCur->pszDir, ppatdscCur->pszDir);
  197. pfsCur->fIsFat = ppatdscCur->fIsFat;
  198. pfsCur->ppatdsc = ppatdscCur;
  199. }
  200. } // while for running through pattern list
  201. return( SUCCESS );
  202. }
  203. STATUS
  204. AppendPath(
  205. OUT PTCHAR Buffer,
  206. IN ULONG BufferCount,
  207. IN PTCHAR Prefix,
  208. IN PTCHAR Suffix
  209. )
  210. {
  211. if (mystrlen( Prefix ) + 1 + mystrlen( Suffix ) + 1 > BufferCount) {
  212. return(ERROR_BUFFER_OVERFLOW);
  213. }
  214. mystrcpy( Buffer, Prefix );
  215. //
  216. // Append a \ if there isn't one already at the end of the prefix
  217. //
  218. if (*lastc( Buffer ) != TEXT('\\')) {
  219. mystrcat( Buffer, TEXT( "\\" ));
  220. }
  221. mystrcat( Buffer, Suffix );
  222. return( SUCCESS );
  223. }
  224. STATUS
  225. ExpandAndApplyToFS(
  226. IN PFS FileSpec,
  227. IN PSCREEN pscr,
  228. IN ULONG AttribMask,
  229. IN ULONG AttribValues,
  230. IN PVOID Data OPTIONAL,
  231. IN VOID (*ErrorFunction) (STATUS, PTCHAR, PVOID) OPTIONAL,
  232. IN STATUS (*PreScanFunction) (PFS, PSCREEN, PVOID) OPTIONAL,
  233. IN STATUS (*ScanFunction) (PFS, PFF, PSCREEN, PVOID) OPTIONAL,
  234. IN STATUS (*PostScanFunction) (PFS, PSCREEN, PVOID) OPTIONAL
  235. )
  236. /*++
  237. Routine Description:
  238. Expand a given FS and apply the dispatch functions to it. The pff field is
  239. set to point to the packed set of Win32 find records and the array of pointers
  240. is set up as well.
  241. Arguments:
  242. FileSpec - FS pointer to expand.
  243. AttribMask - mask for attributes we care about when matching
  244. AttribValues - attributes that must match to satisfy the enumeration
  245. Data - pointer to caller data passed to functions
  246. ErrorFunction - routine to call on pattern matching errors
  247. PreScanFunction - routine to call before enumeration begins
  248. ScanFunction - routine to call during enumeration
  249. PostScanFunction - routine to call after enumeration is complete
  250. Return Value:
  251. If any of the applied functions returns a non-SUCCESS status, we return that.
  252. If no matching file is ever found then ERROR_FILE_NOT_FOUND.
  253. Otherwise SUCCESS.
  254. --*/
  255. {
  256. PFF CurrentFF; // Pointer to FF begin built
  257. HANDLE FindHandle; // Find handle
  258. ULONG MaxFFSize = CBFILEINC; // Current max size of FF buffer
  259. ULONG CurrentFFSize = 0; // Bytes in use in FF
  260. PPATDSC CurrentPattern; // Current pattern being expanded
  261. #define SEARCHBUFFERLENGTH (MAX_PATH + 2)
  262. TCHAR szSearchPath[SEARCHBUFFERLENGTH];
  263. ULONG i; // loop count for patterns
  264. PTCHAR p;
  265. STATUS Status;
  266. BOOL FoundAnyFile = FALSE;
  267. DosErr = SUCCESS;
  268. //
  269. // Initialize the FS structure:
  270. // Allocate default size FF array
  271. // Indicate no files stored
  272. //
  273. FileSpec->pff = CurrentFF = (PFF)gmkstr( MaxFFSize );
  274. FileSpec->cff = 0;
  275. FileSpec->FileCount = 0;
  276. FileSpec->DirectoryCount = 0;
  277. FileSpec->cbFileTotal.QuadPart = 0;
  278. for(i = 1, CurrentPattern = FileSpec->ppatdsc;
  279. i <= FileSpec->cpatdsc;
  280. i++, CurrentPattern = CurrentPattern->ppatdscNext ) {
  281. //
  282. // Check immediately if a control-c was hit before
  283. // doing file I/O (which may take a long time on a slow link)
  284. //
  285. if (CtrlCSeen) {
  286. return FAILURE;
  287. }
  288. //
  289. // Build enumeration path. Handle buffer overflow.
  290. //
  291. if (AppendPath( szSearchPath, SEARCHBUFFERLENGTH,
  292. FileSpec->pszDir, CurrentPattern->pszPattern) != SUCCESS) {
  293. return ERROR_BUFFER_OVERFLOW;
  294. }
  295. if (PreScanFunction != 0) {
  296. Status = PreScanFunction( FileSpec, pscr, Data );
  297. if (Status != SUCCESS) {
  298. return Status;
  299. }
  300. }
  301. //
  302. // Fetch all files since we may looking for a file with a attribute that
  303. // is not set (a non-directory etc.
  304. //
  305. if (!FindFirstNt(szSearchPath, &(CurrentFF->data), &FindHandle)) {
  306. if (DosErr) {
  307. //
  308. // Map NO_MORE_FILES/ACCESS_DENIED into FILE_NOT_FOUND
  309. //
  310. if (DosErr == ERROR_NO_MORE_FILES
  311. || DosErr == ERROR_ACCESS_DENIED) {
  312. DosErr = ERROR_FILE_NOT_FOUND;
  313. }
  314. if (DosErr == ERROR_FILE_NOT_FOUND || DosErr == ERROR_PATH_NOT_FOUND) {
  315. if (ErrorFunction != NULL) {
  316. ErrorFunction( DosErr, szSearchPath, Data );
  317. }
  318. }
  319. }
  320. } else {
  321. do {
  322. //
  323. // Check immediately if a control-c was hit before
  324. // doing file I/O (which may take a long time on a slow link)
  325. //
  326. if (CtrlCSeen) {
  327. findclose( FindHandle );
  328. return(FAILURE);
  329. }
  330. //
  331. // Before allowing this entry to be put in the list check it
  332. // for the proper attribs
  333. //
  334. // AttribMask is a bit mask of attribs we care to look at
  335. // AttribValues is the state these selected bits must be in
  336. // for them to be selected.
  337. //
  338. // IMPORTANT: both of these must be in the same bit order
  339. //
  340. DEBUG((ICGRP, DILVL, " found %ws", CurrentFF->data.cFileName)) ;
  341. DEBUG((ICGRP, DILVL, " attribs %x", CurrentFF->data.dwFileAttributes)) ;
  342. if ((CurrentFF->data.dwFileAttributes & AttribMask) !=
  343. (AttribValues & AttribMask) ) {
  344. continue;
  345. }
  346. //
  347. // We have an entry that matches. Set up FF
  348. //
  349. // Compute the true size of the ff entry and don't forget the zero
  350. // and the DWORD alignment factor.
  351. // Note that CurrentFF->cb is a USHORT to save space. The
  352. // assumption is that MAX_PATH is quite a bit less then 32k
  353. //
  354. // To compute remove the size of the filename field since it is at MAX_PATH.
  355. // also take out the size of the alternative name field
  356. // then add back in the actual size of the field plus 1 byte termination
  357. //
  358. FoundAnyFile = TRUE;
  359. p = (PTCHAR)CurrentFF->data.cFileName;
  360. p += mystrlen( p ) + 1;
  361. mystrcpy( p, CurrentFF->data.cAlternateFileName );
  362. if (*p == TEXT('\0')) {
  363. CurrentFF->obAlternate = 0;
  364. } else {
  365. CurrentFF->obAlternate = (USHORT)(p - CurrentFF->data.cFileName);
  366. }
  367. p += mystrlen( p ) + 1;
  368. CurrentFF->cb = (USHORT)((PBYTE)p - (PBYTE)CurrentFF);
  369. //
  370. // Adjust count to align on DWORD boundaries for mips and risc
  371. // machines
  372. //
  373. CurrentFF->cb = (CurrentFF->cb + sizeof( DWORD ) - 1) & (-(int)sizeof( DWORD ));
  374. if ((CurrentFF->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
  375. FileSpec->FileCount++;
  376. } else {
  377. FileSpec->DirectoryCount++;
  378. }
  379. //
  380. // The FF is built. Call the enumeration function
  381. //
  382. if (ScanFunction != NULL) {
  383. Status = ScanFunction( FileSpec, CurrentFF, pscr, Data );
  384. if (Status != SUCCESS) {
  385. findclose( FindHandle );
  386. return Status;
  387. }
  388. }
  389. //
  390. // If there's no scanning function, save the results
  391. //
  392. if (ScanFunction == NULL) {
  393. FileSpec->cff ++;
  394. //
  395. // Update the accounting information for file buffer info.
  396. //
  397. CurrentFFSize += CurrentFF->cb;
  398. CurrentFF = (PFF) ((PBYTE)CurrentFF + CurrentFF->cb);
  399. //
  400. // Make sure we can handle a max sized entry. If not, resize the buffer.
  401. //
  402. if (CurrentFFSize + sizeof( FF ) >= MaxFFSize ) {
  403. MaxFFSize += 64 * 1024;
  404. DEBUG((ICGRP, DILVL, "\t size of new pff %d", MaxFFSize ));
  405. FileSpec->pff = (PFF)resize( FileSpec->pff, MaxFFSize );
  406. if (FileSpec->pff == NULL) {
  407. DEBUG((ICGRP, DILVL, "\t Could not resize pff" ));
  408. return MSG_NO_MEMORY;
  409. }
  410. CurrentFF = (PFF)((PBYTE)FileSpec->pff + CurrentFFSize);
  411. DEBUG((ICGRP, DILVL, "\t resized CurrentFF new value %lx", CurrentFF)) ;
  412. }
  413. }
  414. } while (FindNextNt(&CurrentFF->data, FindHandle));
  415. findclose( FindHandle );
  416. }
  417. //
  418. // We have an error left over from the FindNext. If it is NO_MORE_FILES then we
  419. // continue only if we have more than one directory.
  420. //
  421. if (DosErr != SUCCESS && DosErr != ERROR_NO_MORE_FILES) {
  422. //
  423. // If not doing multiple file list then error
  424. // If multiple have failed but still have files from previous pattern
  425. //
  426. if (FileSpec->cpatdsc <= 1) {
  427. return DosErr ;
  428. }
  429. }
  430. }
  431. //
  432. // If we did no scan processing, then we must create the pointers since
  433. // SOMEONE is interested in this data.
  434. //
  435. if (ScanFunction == NULL && FileSpec->cff != 0) {
  436. FileSpec->prgpff = (PPFF)gmkstr( sizeof(PFF) * FileSpec->cff );
  437. CurrentFF = FileSpec->pff;
  438. for (i = 0; i < FileSpec->cff; i++) {
  439. FileSpec->prgpff[i] = CurrentFF;
  440. CurrentFF = (PFF) ((PBYTE)CurrentFF + CurrentFF->cb);
  441. }
  442. }
  443. //
  444. // Perform post processing
  445. //
  446. Status = SUCCESS;
  447. if (PostScanFunction != NULL) {
  448. Status = PostScanFunction( FileSpec, pscr, Data );
  449. }
  450. if (Status == SUCCESS && !FoundAnyFile) {
  451. return ERROR_FILE_NOT_FOUND;
  452. } else {
  453. return Status;
  454. }
  455. }
  456. STATUS
  457. WalkTree(
  458. IN PFS FileSpec,
  459. IN PSCREEN pscr,
  460. IN ULONG AttribMask,
  461. IN ULONG AttribValues,
  462. IN BOOL Recurse,
  463. IN PVOID Data OPTIONAL,
  464. IN VOID (*ErrorFunction) (STATUS, PTCHAR, PVOID) OPTIONAL,
  465. IN STATUS (*PreScanFunction) (PFS, PSCREEN, PVOID) OPTIONAL,
  466. IN STATUS (*ScanFunction) (PFS, PFF, PSCREEN, PVOID) OPTIONAL,
  467. IN STATUS (*PostScanFunction) (PFS, PSCREEN, PVOID) OPTIONAL
  468. )
  469. /*++
  470. Routine Description:
  471. Expand a given FS and apply the dispatch functions to it. The pff field is
  472. set to point to the packed set of Win32 find records and the array of pointers
  473. is set up as well. Recurse if necessary.
  474. Arguments:
  475. FileSpec - FS pointer to expand.
  476. pscr - screen for output
  477. AttribMask - mask for attributes we care about when matching
  478. AttribValues - attributes that must match to satisfy the enumeration
  479. Recurse - TRUE => perform the operation in a directory and then descend to
  480. children
  481. Data - pointer to caller data passed to functions
  482. ErrorFunction - routine to call on pattern matching errors
  483. PreScanFunction - routine to call before enumeration begins
  484. ScanFunction - routine to call during enumeration
  485. PostScanFunction - routine to call after enumeration is complete
  486. Return Value:
  487. If any of the applied functions returns a non-SUCCESS status, we return that.
  488. If no matching file is ever found then ERROR_FILE_NOT_FOUND.
  489. Otherwise SUCCESS.
  490. --*/
  491. {
  492. STATUS Status;
  493. FS DirectorySpec;
  494. FS ChildFileSpec;
  495. ULONG i;
  496. BOOL FoundAnyFile = FALSE;
  497. //
  498. // Check for ^C often
  499. //
  500. if (CtrlCSeen) {
  501. return FAILURE;
  502. }
  503. Status = ExpandAndApplyToFS( FileSpec,
  504. pscr,
  505. AttribMask,
  506. AttribValues,
  507. Data,
  508. ErrorFunction,
  509. PreScanFunction,
  510. ScanFunction,
  511. PostScanFunction );
  512. //
  513. // If we succeeded, remember that we did some work
  514. //
  515. if (Status == SUCCESS) {
  516. FoundAnyFile = TRUE;
  517. //
  518. // If we got an unknown error, or we got FILE_NOT_FOUND and we're not
  519. // recursing, return that error
  520. //
  521. } else if ((Status != ERROR_FILE_NOT_FOUND && Status != ERROR_PATH_NOT_FOUND)
  522. || !Recurse) {
  523. return Status;
  524. }
  525. //
  526. // Free up buffer holding files since we no longer need these.
  527. // Move on to determine if we needed to go to another directory
  528. //
  529. FreeStr((PTCHAR)(FileSpec->pff));
  530. FileSpec->pff = NULL;
  531. if (CtrlCSeen) {
  532. return FAILURE;
  533. }
  534. if (!Recurse)
  535. return SUCCESS;
  536. //
  537. // Build up a copy of the FileSpec and build a list of all
  538. // immediate child directories
  539. //
  540. DirectorySpec.pszDir = (PTCHAR)gmkstr( (_tcslen( FileSpec->pszDir ) + 1 ) * sizeof( TCHAR ));
  541. mystrcpy( DirectorySpec.pszDir, FileSpec->pszDir );
  542. DirectorySpec.ppatdsc = (PPATDSC)gmkstr( sizeof( PATDSC ) );
  543. DirectorySpec.cpatdsc = 1;
  544. DirectorySpec.fIsFat = FileSpec->fIsFat;
  545. DirectorySpec.pfsNext = NULL;
  546. if (FileSpec->fIsFat) {
  547. DirectorySpec.ppatdsc->pszPattern = TEXT("*.*");
  548. } else {
  549. DirectorySpec.ppatdsc->pszPattern = TEXT("*");
  550. }
  551. DirectorySpec.ppatdsc->pszDir = (PTCHAR)gmkstr( (_tcslen( FileSpec->pszDir ) + 1) * sizeof(TCHAR));
  552. mystrcpy( DirectorySpec.ppatdsc->pszDir, DirectorySpec.pszDir );
  553. DirectorySpec.ppatdsc->ppatdscNext = NULL;
  554. Status = ExpandAndApplyToFS( &DirectorySpec,
  555. pscr,
  556. FILE_ATTRIBUTE_DIRECTORY,
  557. FILE_ATTRIBUTE_DIRECTORY,
  558. NULL,
  559. NULL,
  560. NULL,
  561. NULL,
  562. NULL );
  563. //
  564. // If we got an error enumerating the directories, pretend that
  565. // we just didn't find any at all.
  566. //
  567. if (Status != SUCCESS) {
  568. DirectorySpec.cff = 0;
  569. Status = SUCCESS;
  570. }
  571. //
  572. // Check for CtrlC again after calling GetFS because
  573. // GetFS may have returned failure because CtrlC was hit
  574. // inside the GetFS function call
  575. //
  576. if (CtrlCSeen) {
  577. return( FAILURE );
  578. }
  579. //
  580. // Walk the list of found directories, processing each one
  581. //
  582. for (i = 0; i < DirectorySpec.cff; i++) {
  583. PTCHAR DirectoryName;
  584. ULONG NameLength;
  585. //
  586. // Skip recursing on . and ..
  587. //
  588. DirectoryName = DirectorySpec.prgpff[i]->data.cFileName;
  589. if (!_tcscmp( DirectoryName, TEXT(".") )
  590. || !_tcscmp( DirectoryName, TEXT("..") )) {
  591. continue;
  592. }
  593. //
  594. // Form the new name we will descend into
  595. //
  596. NameLength = _tcslen( FileSpec->pszDir ) + 1 +
  597. _tcslen( DirectoryName ) + 1;
  598. if (NameLength > MAX_PATH) {
  599. PutStdErr( MSG_DIR_TOO_LONG, TWOARGS, FileSpec->pszDir, DirectoryName );
  600. return ERROR_BUFFER_OVERFLOW;
  601. }
  602. memset( &ChildFileSpec, 0, sizeof( ChildFileSpec ));
  603. ChildFileSpec.pszDir = (PTCHAR)gmkstr( NameLength * sizeof( TCHAR ));
  604. AppendPath( ChildFileSpec.pszDir, NameLength, FileSpec->pszDir, DirectoryName );
  605. ChildFileSpec.ppatdsc = FileSpec->ppatdsc;
  606. ChildFileSpec.cpatdsc = FileSpec->cpatdsc;
  607. ChildFileSpec.fIsFat = FileSpec->fIsFat;
  608. Status = WalkTree( &ChildFileSpec,
  609. pscr,
  610. AttribMask,
  611. AttribValues,
  612. Recurse,
  613. Data,
  614. ErrorFunction,
  615. PreScanFunction,
  616. ScanFunction,
  617. PostScanFunction );
  618. FreeStr( (PTCHAR) ChildFileSpec.pff );
  619. ChildFileSpec.pff = NULL;
  620. FreeStr( (PTCHAR) ChildFileSpec.prgpff );
  621. ChildFileSpec.prgpff = NULL;
  622. FreeStr( ChildFileSpec.pszDir );
  623. ChildFileSpec.pszDir = NULL;
  624. //
  625. // If we succeeded, then remember that we actually did something
  626. //
  627. if (Status == SUCCESS) {
  628. FoundAnyFile = TRUE;
  629. //
  630. // If we just couldn't find what we wanted, keep on working
  631. //
  632. } else if (Status == ERROR_BUFFER_OVERFLOW
  633. || Status == ERROR_FILE_NOT_FOUND
  634. || Status == ERROR_PATH_NOT_FOUND) {
  635. Status = SUCCESS;
  636. } else if ((DirectorySpec.prgpff[i]->data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
  637. Status = SUCCESS;
  638. } else {
  639. break;
  640. }
  641. }
  642. //
  643. // At bottom of directory tree, free buffer holding
  644. // list of directories.
  645. //
  646. FreeStr( (PTCHAR)DirectorySpec.pszDir );
  647. FreeStr( (PTCHAR)DirectorySpec.pff );
  648. FreeStr( (PTCHAR)DirectorySpec.prgpff );
  649. if (Status == SUCCESS && !FoundAnyFile) {
  650. return ERROR_FILE_NOT_FOUND;
  651. } else {
  652. return Status;
  653. }
  654. }