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.

1711 lines
42 KiB

  1. /*++
  2. Copyright (c) 1988-1999 Microsoft Corporation
  3. Module Name:
  4. dir.c
  5. Abstract:
  6. Directory command
  7. --*/
  8. #include "cmd.h"
  9. /*
  10. Usage:
  11. ------
  12. DIR <filespec> /n /d /w /p /b /s /l /o<sortorder> /a<attriblist>
  13. DIR /?
  14. <filespec> may include any or none of: drive; directory path;
  15. wildcarded filename. If drive or directory path are
  16. omitted, the current defaults are used. If the
  17. file name or extension is omitted, wildcards are
  18. assumed.
  19. /n Normal display form FAT drives is name followed by
  20. file information, for non-FAT drives it is file
  21. information followed by name. This switch will use
  22. the non-FAT format independent of the filesystem.
  23. /w Wide listing format. Files are displayed in compressed
  24. 'name.ext' format. Subdirectory files are enclosed in
  25. brackets, '[dirname]'.
  26. /d Same as /w but display sort by columns instead of by
  27. rows.
  28. /p Paged, or prompted listing. A screenful is displayed
  29. at a time. The name of the directory being listed appears
  30. at the top of each page.
  31. /b Bare listing format. Turns off /w or /p. Files are
  32. listed in compressed 'name.ext' format, one per line,
  33. without additional information. Good for making batch
  34. files or for piping. When used with /s, complete
  35. pathnames are listed.
  36. /s Descend subdirectory tree. Performs command on current
  37. or specified directory, then for each subdirectory below
  38. that directory. Directory header and footer is displayed
  39. for each directory where matching files are found, unless
  40. used with /b. /b suppresses headers and footers.
  41. Tree is explored depth first, alphabetically within the
  42. same level.
  43. /l Display file names, extensions and paths in lowercase. ;M010
  44. /o Sort order. /o alone sorts by default order (dirs-first, name,
  45. extension). A sort order may be specified after /o. Any of
  46. the following characters may be used: nedsg (name, extension,
  47. date/time, size, group-dirs-first). Placing a '-' before any
  48. letter causes a downward sort on that field. E.g., /oe-d
  49. means sort first by extension in alphabetical order, then
  50. within each extension sort by date and time in reverse chronological
  51. order.
  52. /a Attribute selection. Without /a, hidden and system files
  53. are suppressed from the listing. With /a alone, all files
  54. are listed. An attribute list may follow /a, consisting of
  55. any of the following characters: hsdar (hidden, system,
  56. directory, archive, read-only). A '-' before any letter
  57. means 'not' that attribute. E.g., /ar-d means files that
  58. are marked read-only and are not directory files. Note
  59. that hidden or system files may be included in the listing.
  60. They are suppressed without /a but are treated like any other
  61. attribute with /a.
  62. /t Which time stamp to use.
  63. /t:a - last access
  64. /t:c - create
  65. /t:w - last write
  66. /, Show thousand separators in output display.
  67. /4 Show 4 digit years
  68. DIRCMD An environment variable named DIRCMD is parsed before the
  69. DIR command line. Any command line options may be specified
  70. in DIRCMD, and become defaults. /? will be ignored in DIRCMD.
  71. A filespec may be specified in DIRCMD and will be used unless
  72. a filespec is specified on the command line. Any switch
  73. specified in DIRCMD may be overridden on the command line.
  74. If the original DIR default action is desired for a particular
  75. switch, the switch letter may be preceded by a '-' on the
  76. command line. E.g.,
  77. /-w use long listing format
  78. /-p don't page the listing
  79. /-b don't use bare format
  80. /-s don't descend subdirectory tree
  81. /-o display files in disk order
  82. /-a suppress hidden and system files
  83. */
  84. extern TCHAR SwitChar, PathChar;
  85. extern TCHAR CurDrvDir[] ;
  86. extern ULONG DCount ;
  87. extern DWORD DosErr ;
  88. extern BOOL CtrlCSeen;
  89. HANDLE OpenConsole();
  90. STATUS PrintPatterns( PDRP );
  91. PTCHAR SetWildCards( PTCHAR, BOOLEAN );
  92. BOOLEAN GetDrive( PTCHAR , PTCHAR );
  93. BOOLEAN IsFATDrive( PTCHAR );
  94. PTCHAR GetNewDir(PTCHAR, PFF);
  95. PTCHAR BuildSearchPath( PTCHAR );
  96. VOID SortFileList( PFS, PSORTDESC, ULONG);
  97. STATUS SetSortDesc( PTCHAR, PDRP );
  98. STATUS
  99. NewDisplayFileListHeader(
  100. IN PFS FileSpec,
  101. IN PSCREEN pscr,
  102. IN PVOID Data
  103. );
  104. STATUS
  105. NewDisplayFile(
  106. IN PFS FileSpec,
  107. IN PFF CurrentFF,
  108. IN PSCREEN pscr,
  109. IN PVOID Data
  110. );
  111. STATUS
  112. NewDisplayFileList(
  113. IN PFS FileSpec,
  114. IN PSCREEN pscr,
  115. IN PVOID Data
  116. );
  117. //
  118. // This global is set in SortFileList and is used by the sort routine called
  119. // within qsort. This array contains pointers to compare functions. Our sort
  120. // does not just sort against 1 criteria but all of the criteria in the sort
  121. // description array built from the command line.
  122. PSORTDESC prgsrtdsc;
  123. //
  124. // dwTimeType is also globally set in SortFileList and is used to control
  125. // which time field is used for sorting.
  126. //
  127. ULONG dwTimeType;
  128. /*++
  129. Routine Description:
  130. Prints out a catalog of a specified directory.
  131. Arguments:
  132. pszCmdLine - Command line (see comment above)
  133. Return Value:
  134. Return: SUCCESS - no completion.
  135. FAILURE - failed to complete entire catalog.
  136. --*/
  137. int
  138. Dir (
  139. TCHAR *pszCmdLine
  140. ) {
  141. //
  142. // drp - structure holding current set of parameters. It is initialized
  143. // in ParseDirParms function. It is also modified later when
  144. // parameters are examined to determine if some turn others on.
  145. //
  146. DRP drpCur = {0, 0, 0, 0,
  147. {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}},
  148. 0, 0, NULL, 0, 0, 0, 0} ;
  149. //
  150. // szEnvVar - pointer to value of the DIRCMD environmental variable.
  151. // This should be a form of the command line that is
  152. // used to alter DIR default behavior.
  153. TCHAR szEnvVar[MAX_PATH + 2];
  154. //
  155. // szCurDrv - Hold current drive letter
  156. //
  157. TCHAR szCurDrv[MAX_PATH + 2];
  158. //
  159. // OldDCount - Holds the level number of the heap. It is used to
  160. // free entries off the stack that might not have been
  161. // freed due to error processing (ctrl-c etc.)
  162. ULONG OldDCount;
  163. STATUS rc;
  164. OldDCount = DCount;
  165. //
  166. // Setup defaults
  167. //
  168. //
  169. // Display everything but system and hidden files
  170. // rgfAttribs set the attribute bits to that are of interest and
  171. // rgfAttribsOnOff says either the attributes should be present
  172. // or not (i.e. On or Off)
  173. //
  174. drpCur.rgfAttribs = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
  175. drpCur.rgfAttribsOnOff = 0;
  176. drpCur.rgfSwitches = THOUSANDSEPSWITCH;
  177. //
  178. // Number of patterns present. A pattern is a string that may have
  179. // wild cards. It is used to match against files present in the directory
  180. // 0 patterns will show all files (i.e. mapped to *.*)
  181. //
  182. drpCur.cpatdsc = 0;
  183. DEBUG((ICGRP, DILVL, "DIR:\t arg = `%ws'", (UINT_PTR)pszCmdLine)) ;
  184. //
  185. // default time is LAST_WRITE_TIME.
  186. //
  187. drpCur.dwTimeType = LAST_WRITE_TIME;
  188. //
  189. // DIRCMD holds a copy of default parameters to Dir
  190. // parse these into drpCur (list of parameters to dir) and use as
  191. // default into parsing parameters on command line.
  192. //
  193. if (GetEnvironmentVariable(TEXT("DIRCMD"), szEnvVar, MAX_PATH + 2)) {
  194. DEBUG((ICGRP, DILVL, "DIR: DIRCMD `%ws'", (UINT_PTR)szEnvVar)) ;
  195. if (ParseDirParms(szEnvVar, &drpCur) == FAILURE) {
  196. //
  197. // Error in parsing environment variable
  198. //
  199. // DOS 5.0 continues with command even if the
  200. // environmental variable is wrong
  201. //
  202. PutStdErr(MSG_ERROR_IN_DIRCMD, NOARGS);
  203. }
  204. }
  205. //
  206. // Override environment variable with command line options
  207. //
  208. if (ParseDirParms(pszCmdLine, &drpCur) == FAILURE) {
  209. return( FAILURE );
  210. }
  211. //
  212. // If bare format then turn off the other formats
  213. // bare format will have no addition information on the line so
  214. // make sure that options set from the DIRCMD variable etc. to
  215. // not combine with the bare switch
  216. //
  217. if (drpCur.rgfSwitches & BAREFORMATSWITCH) {
  218. drpCur.rgfSwitches &= ~WIDEFORMATSWITCH;
  219. drpCur.rgfSwitches &= ~SORTDOWNFORMATSWITCH;
  220. drpCur.rgfSwitches &= ~SHORTFORMATSWITCH;
  221. drpCur.rgfSwitches &= ~THOUSANDSEPSWITCH;
  222. drpCur.rgfSwitches &= ~DISPLAYOWNER;
  223. }
  224. //
  225. // If short form (short file names) turn off others
  226. //
  227. if (drpCur.rgfSwitches & SHORTFORMATSWITCH) {
  228. drpCur.rgfSwitches &= ~WIDEFORMATSWITCH;
  229. drpCur.rgfSwitches &= ~SORTDOWNFORMATSWITCH;
  230. drpCur.rgfSwitches &= ~BAREFORMATSWITCH;
  231. }
  232. //
  233. // If no patterns on the command line use the default which
  234. // would be the current directory
  235. //
  236. GetDir((PTCHAR)szCurDrv, GD_DEFAULT);
  237. if (drpCur.cpatdsc == 0) {
  238. drpCur.cpatdsc++;
  239. drpCur.patdscFirst.pszPattern = gmkstr( mystrlen( szCurDrv ) * sizeof( TCHAR ) + sizeof( TCHAR ));
  240. mystrcpy( drpCur.patdscFirst.pszPattern, szCurDrv );
  241. drpCur.patdscFirst.fIsFat = TRUE;
  242. drpCur.patdscFirst.pszDir = NULL;
  243. drpCur.patdscFirst.ppatdscNext = NULL;
  244. }
  245. DEBUG((ICGRP, DILVL, "Dir: Parameters")) ;
  246. DEBUG((ICGRP, DILVL, "\t rgfSwitches %x", drpCur.rgfSwitches)) ;
  247. DEBUG((ICGRP, DILVL, "\t rgfAttribs %x", drpCur.rgfAttribs)) ;
  248. DEBUG((ICGRP, DILVL, "\t rgfAttribsOnOff %x", drpCur.rgfAttribsOnOff)) ;
  249. DEBUG((ICGRP, DILVL, "\t csrtdsc %d", drpCur.csrtdsc)) ;
  250. //
  251. // Print out this particular pattern. If the recursion switch
  252. // is set then this will desend down the tree.
  253. //
  254. rc = PrintPatterns(&drpCur);
  255. mystrcpy(CurDrvDir, szCurDrv);
  256. //
  257. // Free unneeded memory
  258. //
  259. FreeStack( OldDCount );
  260. #ifdef _CRTHEAP_
  261. //
  262. // Force the crt to release heap we may have taken on recursion
  263. //
  264. if (drpCur.rgfSwitches & RECURSESWITCH) {
  265. _heapmin();
  266. }
  267. #endif
  268. return( (int)rc );
  269. }
  270. STATUS
  271. SetTimeType(
  272. IN PTCHAR pszTok,
  273. OUT PDRP pdrp
  274. )
  275. /*++
  276. Routine Description:
  277. Parses the 'time' string
  278. Arguments:
  279. pszTok -
  280. Return Value:
  281. pdrp - where to place the time type
  282. Return: TRUE - recognized all parameters
  283. FALSE - syntax error.
  284. An error is printed if incountered.
  285. --*/
  286. {
  287. ULONG irgch;
  288. //
  289. // Move over optional ':'
  290. //
  291. if (*pszTok == COLON) {
  292. pszTok++;
  293. }
  294. for( irgch = 0; pszTok[irgch]; irgch++ ) {
  295. switch (_totupper(pszTok[irgch])) {
  296. case TEXT('C'):
  297. pdrp->dwTimeType = CREATE_TIME;
  298. break;
  299. case TEXT('A'):
  300. pdrp->dwTimeType = LAST_ACCESS_TIME;
  301. break;
  302. case TEXT('W'):
  303. pdrp->dwTimeType = LAST_WRITE_TIME;
  304. break;
  305. default:
  306. PutStdErr( MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG, pszTok + irgch );
  307. return( FAILURE );
  308. } // switch
  309. } // for
  310. return( SUCCESS );
  311. }
  312. STATUS
  313. SetAttribs(
  314. IN PTCHAR pszTok,
  315. OUT PDRP pdrp
  316. )
  317. /*++
  318. Routine Description:
  319. Parses the 'attribute' string
  320. Arguments:
  321. pszTok - list of attributes
  322. Return Value:
  323. pdrp - where to place the attributes recognized.
  324. this is the parameter structure.
  325. Return: TRUE - recognized all parameters
  326. FALSE - syntax error.
  327. An error is printed if incountered.
  328. --*/
  329. {
  330. ULONG irgch;
  331. BOOLEAN fOff;
  332. ULONG rgfAttribs, rgfAttribsOnOff;
  333. // rgfAttributes hold 1 bit per recognized attribute. If the bit is
  334. // on then do something with this attribute. Either select the file
  335. // with this attribute or select the file without this attribute.
  336. //
  337. // rgfAttribsOnOff controls wither to select for the attribute or
  338. // select without the attribute.
  339. //
  340. // /a triggers selection of all files by default
  341. // so override the default
  342. //
  343. pdrp->rgfAttribs = rgfAttribs = 0;
  344. pdrp->rgfAttribsOnOff = rgfAttribsOnOff = 0;
  345. //
  346. // Move over optional ':'
  347. //
  348. if (*pszTok == COLON) {
  349. pszTok++;
  350. }
  351. //
  352. // rgfAttribs and rgfAttribsOnOff must be maintained in the
  353. // same bit order.
  354. //
  355. for( irgch = 0, fOff = FALSE; pszTok[irgch]; irgch++ ) {
  356. switch (_totupper(pszTok[irgch])) {
  357. #define AddAttribute(a) \
  358. { \
  359. rgfAttribs |= (a); \
  360. if (fOff) { \
  361. rgfAttribsOnOff &= ~(a); \
  362. fOff = FALSE; \
  363. } else { \
  364. rgfAttribsOnOff |= (a); \
  365. } \
  366. }
  367. case TEXT('L'): AddAttribute( FILE_ATTRIBUTE_REPARSE_POINT ); break;
  368. case TEXT('H'): AddAttribute( FILE_ATTRIBUTE_HIDDEN ); break;
  369. case TEXT('S'): AddAttribute( FILE_ATTRIBUTE_SYSTEM ); break;
  370. case TEXT('D'): AddAttribute( FILE_ATTRIBUTE_DIRECTORY ); break;
  371. case TEXT('A'): AddAttribute( FILE_ATTRIBUTE_ARCHIVE ); break;
  372. case TEXT('R'): AddAttribute( FILE_ATTRIBUTE_READONLY ); break;
  373. case MINUS:
  374. if (fOff) {
  375. PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG, pszTok + irgch );
  376. return( FAILURE );
  377. }
  378. fOff = TRUE;
  379. break;
  380. default:
  381. PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG, pszTok + irgch );
  382. return( FAILURE );
  383. } // switch
  384. } // for
  385. pdrp->rgfAttribs = rgfAttribs;
  386. pdrp->rgfAttribsOnOff = rgfAttribsOnOff;
  387. return( SUCCESS );
  388. }
  389. STATUS
  390. SetSortDesc(
  391. IN PTCHAR pszTok,
  392. OUT PDRP pdrp
  393. )
  394. /*++
  395. Routine Description:
  396. Parses the 'attribute' string
  397. Arguments:
  398. pszTok - list of sort orders
  399. Return Value:
  400. pdrp - where to place the sort orderings recognized.
  401. this is the parameter structure.
  402. Return: TRUE - recognized all parameters
  403. FALSE - syntax error.
  404. An error is printed if incountered.
  405. --*/
  406. {
  407. ULONG irgch, irgsrtdsc;
  408. DEBUG((ICGRP, DILVL, "SetSortDesc for `%ws'", pszTok));
  409. //
  410. // Move over optional ':'
  411. //
  412. if (*pszTok == COLON) {
  413. pszTok++;
  414. }
  415. //
  416. // Sorting order is based upon the order of entries in rgsrtdsc.
  417. // srtdsc contains a pointer to a compare function and a flag
  418. // wither to sort up or down.
  419. //
  420. for( irgch = 0, irgsrtdsc = pdrp->csrtdsc ;
  421. pszTok[irgch] && irgsrtdsc < MAXSORTDESC ;
  422. irgch++, irgsrtdsc++) {
  423. switch (_totupper(pszTok[irgch])) {
  424. case TEXT('N'):
  425. pdrp->rgsrtdsc[irgsrtdsc].fctCmp = CmpName;
  426. break;
  427. case TEXT('E'):
  428. pdrp->rgsrtdsc[irgsrtdsc].fctCmp = CmpExt;
  429. break;
  430. case TEXT('D'):
  431. pdrp->rgsrtdsc[irgsrtdsc].fctCmp = CmpTime;
  432. break;
  433. case TEXT('S'):
  434. pdrp->rgsrtdsc[irgsrtdsc].fctCmp = CmpSize;
  435. break;
  436. case TEXT('G'):
  437. pdrp->rgsrtdsc[irgsrtdsc].fctCmp = CmpType;
  438. break;
  439. case MINUS:
  440. //
  441. // Check that there are not 2 -- in a row
  442. //
  443. if (pszTok[irgch+1] == MINUS) {
  444. PutStdErr( MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG, pszTok + irgch );
  445. return( FAILURE );
  446. }
  447. pdrp->rgsrtdsc[irgsrtdsc].Order = DESCENDING;
  448. irgsrtdsc--;
  449. break;
  450. default:
  451. PutStdErr( MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG, pszTok + irgch );
  452. return( FAILURE );
  453. } // switch
  454. } // for
  455. //
  456. // Was there any specific sort order (something besides /O
  457. //
  458. if (irgsrtdsc == 0) {
  459. //
  460. // Setup default sorting
  461. //
  462. pdrp->rgsrtdsc[0].fctCmp = CmpType;
  463. pdrp->rgsrtdsc[1].fctCmp = CmpName;
  464. irgsrtdsc = 2;
  465. }
  466. DEBUG((ICGRP, DILVL, "SetSortDesc count %d", irgsrtdsc));
  467. pdrp->csrtdsc = irgsrtdsc;
  468. pdrp->rgsrtdsc[irgsrtdsc].fctCmp = NULL;
  469. return( SUCCESS );
  470. }
  471. STATUS
  472. ParseDirParms (
  473. IN PTCHAR pszCmdLine,
  474. OUT PDRP pdrp
  475. )
  476. /*++
  477. Routine Description:
  478. Parse the command line translating the tokens into values
  479. placed in the parameter structure. The values are or'd into
  480. the parameter structure since this routine is called repeatedly
  481. to build up values (once for the environment variable DIRCMD
  482. and once for the actual command line).
  483. Arguments:
  484. pszCmdLine - pointer to command line user typed
  485. Return Value:
  486. pdrp - parameter data structure
  487. Return: TRUE - if valid command line.
  488. FALSE - if not.
  489. --*/
  490. {
  491. PTCHAR pszTok;
  492. TCHAR szT[10] ;
  493. USHORT irgchTok;
  494. BOOLEAN fToggle;
  495. PPATDSC ppatdscCur;
  496. DEBUG((ICGRP, DILVL, "DIR:ParseParms for `%ws'", pszCmdLine));
  497. //
  498. // Tokensize the command line (special delimeters are tokens)
  499. //
  500. szT[0] = SwitChar ;
  501. szT[1] = NULLC ;
  502. pszTok = TokStr(pszCmdLine, szT, TS_SDTOKENS) ;
  503. ppatdscCur = &(pdrp->patdscFirst);
  504. //
  505. // If there was a pattern put in place from the environment.
  506. // just add any new patterns on. So move to the end of the
  507. // current list.
  508. //
  509. if (pdrp->cpatdsc) {
  510. while (ppatdscCur->ppatdscNext) {
  511. ppatdscCur = ppatdscCur->ppatdscNext;
  512. }
  513. }
  514. pdrp->csrtdsc = 0;
  515. //
  516. // At this state pszTok will be a series of zero terminated strings.
  517. // "/o foo" wil be /0o0foo0
  518. //
  519. for ( irgchTok = 0; *pszTok ; pszTok += mystrlen(pszTok)+1, irgchTok = 0) {
  520. DEBUG((ICGRP, DILVL, "PRIVSW: pszTok = %ws", (UINT_PTR)pszTok)) ;
  521. //
  522. // fToggle control whether to turn off a switch that was set
  523. // in the DIRCMD environment variable.
  524. //
  525. fToggle = FALSE;
  526. if (pszTok[irgchTok] == (TCHAR)SwitChar) {
  527. if (pszTok[irgchTok + 2] == MINUS) {
  528. //
  529. // disable the previously enabled the switch
  530. //
  531. fToggle = TRUE;
  532. irgchTok++;
  533. }
  534. switch (_totupper(pszTok[irgchTok + 2])) {
  535. //
  536. // New Format is the os/2 default HPFS format. The main
  537. // difference is the filename is at the end of a long display
  538. // instead of at the beginning
  539. //
  540. case TEXT('N'):
  541. fToggle ? (pdrp->rgfSwitches |= OLDFORMATSWITCH) : (pdrp->rgfSwitches |= NEWFORMATSWITCH);
  542. if (pszTok[irgchTok + 3]) {
  543. PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
  544. (UINT_PTR)(&(pszTok[irgchTok + 2])) );
  545. return( FAILURE );
  546. }
  547. break;
  548. case TEXT('W'):
  549. fToggle ? (pdrp->rgfSwitches ^= WIDEFORMATSWITCH) : (pdrp->rgfSwitches |= WIDEFORMATSWITCH);
  550. if (pszTok[irgchTok + 3]) {
  551. PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
  552. (UINT_PTR)(&(pszTok[irgchTok + 2])) );
  553. return( FAILURE );
  554. }
  555. break;
  556. case TEXT('D'):
  557. fToggle ? (pdrp->rgfSwitches ^= SORTDOWNFORMATSWITCH) : (pdrp->rgfSwitches |= SORTDOWNFORMATSWITCH);
  558. if (pszTok[irgchTok + 3]) {
  559. PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
  560. (UINT_PTR)(&(pszTok[irgchTok + 2])) );
  561. return( FAILURE );
  562. }
  563. break;
  564. case TEXT('P'):
  565. fToggle ? (pdrp->rgfSwitches ^= PAGEDOUTPUTSWITCH) : (pdrp->rgfSwitches |= PAGEDOUTPUTSWITCH);
  566. if (pszTok[irgchTok + 3]) {
  567. PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
  568. (UINT_PTR)(&(pszTok[irgchTok + 2])) );
  569. return( FAILURE );
  570. }
  571. break;
  572. case TEXT('4'):
  573. fToggle ? (pdrp->rgfSwitches ^= YEAR2000) : (pdrp->rgfSwitches |= YEAR2000);
  574. if (pszTok[irgchTok + 3]) {
  575. PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
  576. (UINT_PTR)(&(pszTok[irgchTok + 2])) );
  577. return( FAILURE );
  578. }
  579. break;
  580. case TEXT('B'):
  581. fToggle ? (pdrp->rgfSwitches ^= BAREFORMATSWITCH) : (pdrp->rgfSwitches |= BAREFORMATSWITCH);
  582. if (pszTok[irgchTok + 3]) {
  583. PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
  584. (UINT_PTR)(&(pszTok[irgchTok + 2])) );
  585. return( FAILURE );
  586. }
  587. break;
  588. case TEXT('L'):
  589. fToggle ? (pdrp->rgfSwitches ^= LOWERCASEFORMATSWITCH) : (pdrp->rgfSwitches |= LOWERCASEFORMATSWITCH);
  590. if (pszTok[irgchTok + 3]) {
  591. PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
  592. (UINT_PTR)(&(pszTok[irgchTok + 2])) );
  593. return( FAILURE );
  594. }
  595. break;
  596. #ifndef WIN95_CMD
  597. case TEXT('Q'):
  598. fToggle ? (pdrp->rgfSwitches ^= DISPLAYOWNER) : (pdrp->rgfSwitches |= DISPLAYOWNER);
  599. if (pszTok[irgchTok + 3]) {
  600. PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
  601. (UINT_PTR)(&(pszTok[irgchTok + 2])) );
  602. return( FAILURE );
  603. }
  604. break;
  605. #endif
  606. case TEXT('S'):
  607. fToggle ? (pdrp->rgfSwitches ^= RECURSESWITCH) : (pdrp->rgfSwitches |= RECURSESWITCH);
  608. if (pszTok[irgchTok + 3]) {
  609. PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
  610. (UINT_PTR)(&(pszTok[irgchTok + 2])) );
  611. return( FAILURE );
  612. }
  613. break;
  614. case TEXT('C'):
  615. fToggle ? (pdrp->rgfSwitches ^= THOUSANDSEPSWITCH) : (pdrp->rgfSwitches |= THOUSANDSEPSWITCH);
  616. if (pszTok[irgchTok + 3]) {
  617. PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
  618. (UINT_PTR)(&(pszTok[irgchTok + 2])) );
  619. return( FAILURE );
  620. }
  621. break;
  622. case TEXT('X'):
  623. pdrp->rgfSwitches |= SHORTFORMATSWITCH;
  624. pdrp->rgfSwitches |= NEWFORMATSWITCH;
  625. if (pszTok[irgchTok + 3]) {
  626. PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
  627. (UINT_PTR)(&(pszTok[irgchTok + 2])) );
  628. return( FAILURE );
  629. }
  630. break;
  631. case MINUS:
  632. PutStdOut(MSG_HELP_DIR, NOARGS);
  633. return( FAILURE );
  634. break;
  635. case TEXT('O'):
  636. fToggle ? (pdrp->rgfSwitches ^= SORTSWITCH) : (pdrp->rgfSwitches |= SORTSWITCH);
  637. if (fToggle) {
  638. if ( _tcslen( &(pszTok[irgchTok + 2]) ) > 1) {
  639. PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
  640. (UINT_PTR)(&(pszTok[irgchTok + 2])) );
  641. return( FAILURE );
  642. }
  643. pdrp->csrtdsc = 0;
  644. pdrp->rgsrtdsc[0].fctCmp = NULL;
  645. break;
  646. }
  647. if (SetSortDesc( &(pszTok[irgchTok+3]), pdrp)) {
  648. return( FAILURE );
  649. }
  650. break;
  651. case TEXT('A'):
  652. if (fToggle) {
  653. if ( _tcslen( &(pszTok[irgchTok + 2]) ) > 1) {
  654. PutStdErr(MSG_PARAMETER_FORMAT_NOT_CORRECT, ONEARG,
  655. (UINT_PTR)(&(pszTok[irgchTok + 2])) );
  656. return( FAILURE );
  657. }
  658. pdrp->rgfAttribs = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
  659. pdrp->rgfAttribsOnOff = 0;
  660. break;
  661. }
  662. if (SetAttribs(&(pszTok[irgchTok + 3]), pdrp) ) {
  663. return( FAILURE );
  664. }
  665. break;
  666. case TEXT('T'):
  667. if (fToggle) {
  668. //
  669. // revert to default
  670. //
  671. pdrp->dwTimeType = LAST_WRITE_TIME;
  672. break;
  673. }
  674. if (SetTimeType(&(pszTok[irgchTok + 3]), pdrp) ) {
  675. return( FAILURE );
  676. }
  677. break;
  678. default:
  679. szT[0] = SwitChar;
  680. szT[1] = pszTok[2];
  681. szT[2] = NULLC;
  682. PutStdErr(MSG_INVALID_SWITCH,
  683. ONEARG,
  684. (UINT_PTR)(&(pszTok[irgchTok + 2])) );
  685. return( FAILURE );
  686. } // switch
  687. //
  688. // TokStr parses /N as /0N0 so we need to move over the
  689. // switchar in or to move past the actual switch value
  690. // in for loop.
  691. //
  692. pszTok += 2;
  693. } else {
  694. //
  695. // If there already is a list the extend it else put info
  696. // directly into structure.
  697. //
  698. if (pdrp->cpatdsc) {
  699. ppatdscCur->ppatdscNext = (PPATDSC)gmkstr(sizeof(PATDSC));
  700. ppatdscCur = ppatdscCur->ppatdscNext;
  701. ppatdscCur->ppatdscNext = NULL;
  702. }
  703. pdrp->cpatdsc++;
  704. ppatdscCur->pszPattern = (PTCHAR)gmkstr(_tcslen(pszTok)*sizeof(TCHAR) + sizeof(TCHAR));
  705. mystrcpy(ppatdscCur->pszPattern, StripQuotes(pszTok) );
  706. ppatdscCur->fIsFat = TRUE;
  707. }
  708. } // for
  709. return( SUCCESS );
  710. }
  711. //
  712. // return a pointer to the a new pattern with wild cards inserted.
  713. // If no change has occured the passed in pattern is returned.
  714. //
  715. // NULL is returned if error.
  716. //
  717. /*++
  718. Routine Description:
  719. This routine determines if any modification of to the current.
  720. NOTE that pszInPattern is freed!
  721. Arguments:
  722. Return Value:
  723. Return:
  724. --*/
  725. PTCHAR
  726. SetWildCards (
  727. IN PTCHAR pszInPattern,
  728. IN BOOLEAN fFatDrive
  729. )
  730. {
  731. PTCHAR pszNewPattern = NULL;
  732. PTCHAR pszT;
  733. USHORT cb;
  734. DWORD l;
  735. DEBUG((ICGRP, DILVL, "DIR:SetWildCards"));
  736. DEBUG((ICGRP, DILVL, "\t fFatDrive = %x",fFatDrive));
  737. //
  738. // failure to allocate will not return but go through an
  739. // abort call in gmkstr
  740. //
  741. l = max(mystrlen(pszInPattern)+2, MAX_PATH+2) * sizeof(TCHAR);
  742. pszNewPattern = (PTCHAR)gmkstr(l);
  743. mystrcpy(pszNewPattern, pszInPattern);
  744. //
  745. // On FAT the default for .xxx is *.xxx while for HPFS .xxx is
  746. // just a file name.
  747. //
  748. // If .xxx or \xxx\.xxx then tranform into *.xxx or \xxx\*.xxx
  749. //
  750. // Likewise for no extension the default would be foo.*
  751. //
  752. if (fFatDrive) {
  753. pszT = mystrrchr(pszInPattern, PathChar);
  754. //
  755. // If there is no slash then check if pattern begining with
  756. // a .xxx (making sure not to confuse it with just a . or .. at
  757. // start of pattern)
  758. // If there a slash then check for \xxx\.xxx again making sure
  759. // it is not \xxx\.. or \xxx\.
  760. //
  761. if ((!pszT && *pszInPattern == DOT &&
  762. *(pszInPattern + 1) != NULLC &&
  763. *(pszInPattern + 1) != DOT ) ||
  764. (pszT && *(pszT + 1) == DOT &&
  765. *(pszT + 2) != NULLC &&
  766. *(pszT + 2) != DOT ) ) {
  767. if (pszT) {
  768. cb = (USHORT)(pszT - pszInPattern + 1);
  769. _tcsncpy(pszNewPattern, pszInPattern, cb);
  770. *(pszNewPattern + cb) = NULLC;
  771. } else {
  772. *pszNewPattern = NULLC;
  773. cb = 0;
  774. }
  775. mystrcat(pszNewPattern, TEXT("*"));
  776. mystrcat(pszNewPattern, pszInPattern + cb);
  777. // FreeStr( pszInPattern );
  778. return( pszNewPattern );
  779. }
  780. }
  781. return( pszNewPattern );
  782. }
  783. /*++
  784. Routine Description:
  785. Arguments:
  786. Return Value:
  787. Return:
  788. --*/
  789. BOOLEAN
  790. IsFATDrive (
  791. IN PTCHAR pszPath
  792. )
  793. {
  794. DWORD cbComponentMax;
  795. TCHAR szFileSystemName[MAX_PATH + 2];
  796. TCHAR szDrivePath[ MAX_PATH + 2 ];
  797. TCHAR szDrive[MAX_PATH + 2];
  798. DosErr = 0;
  799. if (GetDrive(pszPath, (PTCHAR)szDrive)) {
  800. DEBUG((ICGRP, DILVL, "DIR:IsFatDrive `%ws'", szDrive));
  801. mystrcpy( szDrivePath, szDrive );
  802. mystrcat( szDrivePath, TEXT("\\") );
  803. //
  804. // We return that the file system in question is a FAT file system
  805. // if the component length is more than 12 bytes.
  806. //
  807. if (GetVolumeInformation( szDrivePath,
  808. NULL,
  809. 0,
  810. NULL,
  811. &cbComponentMax,
  812. NULL,
  813. szFileSystemName,
  814. MAX_PATH + 2
  815. )
  816. ) {
  817. if (!_tcsicmp(szFileSystemName, TEXT("FAT")) && cbComponentMax == 12) {
  818. return(TRUE);
  819. } else {
  820. return(FALSE);
  821. }
  822. } else {
  823. DosErr = GetLastError();
  824. // if GetVolumeInformation failed because we're a substed drive
  825. // or a down-level server, don't fail.
  826. if (DosErr == ERROR_DIR_NOT_ROOT) {
  827. DosErr = 0;
  828. }
  829. return(FALSE);
  830. }
  831. } else {
  832. //
  833. // If we could not get the drive then assume it is not FAT.
  834. // If it is not accessable etc. then that will be caught
  835. // later.
  836. //
  837. return( FALSE );
  838. }
  839. }
  840. /*++
  841. Routine Description:
  842. Arguments:
  843. Return Value:
  844. Return:
  845. --*/
  846. BOOLEAN
  847. GetDrive(
  848. IN PTCHAR pszPattern,
  849. OUT PTCHAR szDrive
  850. )
  851. {
  852. TCHAR szCurDrv[MAX_PATH + 2];
  853. PTCHAR pszT;
  854. TCHAR ch = NULLC;
  855. if (pszPattern == NULL) {
  856. return( FALSE );
  857. }
  858. //
  859. // assume we have the default case with no drive
  860. // letter specified
  861. //
  862. GetDir((PTCHAR)szCurDrv,GD_DEFAULT);
  863. szDrive[0] = szCurDrv[0];
  864. //
  865. // If we have a UNC name do not return a drive. No
  866. // drive operation would be allowed
  867. // For everything else a some drive operation would
  868. // be valid
  869. //
  870. // handle UNC names with drive letter (allowed in DOS)
  871. if ((pszPattern[1] == COLON) && (pszPattern[2] == BSLASH) &&
  872. (pszPattern[3] == BSLASH)) {
  873. mystrcpy(&pszPattern[0],&pszPattern[2]);
  874. }
  875. if ((pszPattern[0] == BSLASH) && (pszPattern[1] == BSLASH)) {
  876. pszT = mystrchr(&(pszPattern[2]), BSLASH);
  877. if (pszT == NULL) {
  878. //
  879. // badly formed unc name
  880. //
  881. return( FALSE );
  882. } else {
  883. //
  884. // look for '\\foo\bar\xxx'
  885. //
  886. pszT = mystrchr(pszT + 1, BSLASH);
  887. //
  888. // pszPattern contains more then just share point
  889. //
  890. if (pszT != NULL) {
  891. ch = *pszT;
  892. *pszT = NULLC;
  893. }
  894. mystrcpy(szDrive, pszPattern);
  895. if (ch != NULLC) {
  896. *pszT = ch;
  897. }
  898. return ( TRUE );
  899. }
  900. }
  901. //
  902. // Must be a drive letter
  903. //
  904. if ((pszPattern[0]) && (pszPattern[1] == COLON)) {
  905. szDrive[0] = (TCHAR)_totupper(*pszPattern);
  906. }
  907. szDrive[1] = COLON;
  908. szDrive[2] = NULLC;
  909. return( TRUE );
  910. }
  911. /*++
  912. Routine Description:
  913. Arguments:
  914. Return Value:
  915. Return:
  916. --*/
  917. STATUS
  918. PrintPatterns (
  919. IN PDRP pdpr
  920. )
  921. {
  922. TCHAR szDriveCur[MAX_PATH + 2];
  923. TCHAR szDrivePrev[MAX_PATH + 2];
  924. TCHAR szDriveNext[MAX_PATH + 2];
  925. TCHAR szPathForFreeSpace[MAX_PATH + 2];
  926. PPATDSC ppatdscCur;
  927. PPATDSC ppatdscX;
  928. PFS pfsFirst;
  929. PFS pfsCur;
  930. PFS pfsPrev;
  931. ULONG i;
  932. STATUS rc;
  933. PSCREEN pscr;
  934. //
  935. // Creating the console output is done early since error message
  936. // should go through the console. If PrintPattern is called
  937. // many times in the future this will be required since the
  938. // error message should need to be under pause control
  939. //
  940. if (OpenScreen( &pscr) == FAILURE) {
  941. return( FAILURE );
  942. }
  943. //
  944. // This will be NULL if for any reason we STDOUT is not a valid
  945. // console handle, such as file redirection or redirection to a
  946. // non-console device. In that case we turn off any paged output.
  947. //
  948. if (!(pscr->hndScreen)) {
  949. pdpr->rgfSwitches &= ~PAGEDOUTPUTSWITCH;
  950. }
  951. //
  952. // Default will be the size of the screen - 1
  953. // subtract 1 to account for the current line
  954. //
  955. if (pdpr->rgfSwitches & PAGEDOUTPUTSWITCH) {
  956. SetPause( pscr, pscr->crowMax - 1 );
  957. }
  958. //
  959. // Sortdown => wide format but a different display order
  960. //
  961. if (pdpr->rgfSwitches & SORTDOWNFORMATSWITCH) {
  962. pdpr->rgfSwitches |= WIDEFORMATSWITCH;
  963. }
  964. //
  965. // determine FAT drive from original pattern.
  966. // Used in several places to control name format etc.
  967. //
  968. DosErr = 0;
  969. if (BuildFSFromPatterns(pdpr, TRUE, TRUE, &pfsFirst) == FAILURE) {
  970. return( FAILURE );
  971. }
  972. pfsPrev = NULL;
  973. mystrcpy( szPathForFreeSpace, TEXT("") );
  974. mystrcpy( szDriveCur, TEXT("") );
  975. for( pfsCur = pfsFirst; pfsCur; pfsCur = pfsCur->pfsNext) {
  976. mystrcpy( szPathForFreeSpace, pfsCur->pszDir );
  977. //
  978. // Set up flags based on type of drive. FAT drives get
  979. // FAT format and are unable to display anything except
  980. // LAST_WRITE_TIME
  981. //
  982. if (pfsCur->fIsFat) {
  983. pdpr->rgfSwitches |= FATFORMAT;
  984. if (pdpr->dwTimeType != LAST_WRITE_TIME) {
  985. PutStdErr(MSG_TIME_NOT_SUPPORTED, NOARGS);
  986. return( FAILURE );
  987. }
  988. } else {
  989. //
  990. // If it is not fat then print out in new format that
  991. // puts names to the right to allow for extra long names
  992. //
  993. if (!(pdpr->rgfSwitches & OLDFORMATSWITCH)) {
  994. pdpr->rgfSwitches |= NEWFORMATSWITCH;
  995. }
  996. }
  997. //
  998. // If we're not in bare mode, print out header if this
  999. // is the first time or if the drive letter changes.
  1000. //
  1001. if ((pdpr->rgfSwitches & BAREFORMATSWITCH) == 0) {
  1002. mystrcpy( szDrivePrev, szDriveCur );
  1003. GetDrive(pfsCur->pszDir, szDriveCur);
  1004. if (_tcsicmp( szDriveCur, szDrivePrev ) != 0) {
  1005. if ((pfsPrev != NULL && WriteEol( pscr ) != SUCCESS) ||
  1006. DisplayVolInfo( pscr, pfsCur->pszDir ) != SUCCESS) {
  1007. return FAILURE;
  1008. }
  1009. }
  1010. }
  1011. //
  1012. // Walk down the tree printing each directory or just return
  1013. // after specificied directory.
  1014. //
  1015. pdpr->FileCount = pdpr->DirectoryCount = 0;
  1016. pdpr->TotalBytes.QuadPart = 0i64;
  1017. rc = WalkTree( pfsCur,
  1018. pscr,
  1019. pdpr->rgfAttribs,
  1020. pdpr->rgfAttribsOnOff,
  1021. pdpr->rgfSwitches & RECURSESWITCH,
  1022. pdpr, // Data for display functions
  1023. NULL, // Error
  1024. NewDisplayFileListHeader, // PreScan
  1025. (pdpr->rgfSwitches & (WIDEFORMATSWITCH | SORTSWITCH))
  1026. ? NULL : NewDisplayFile, // Scan
  1027. NewDisplayFileList // PostScan
  1028. );
  1029. //
  1030. // If we enumerated everything and we printed some files and the next
  1031. // file spec is on a different drive, display the free space
  1032. //
  1033. if (rc == SUCCESS && pdpr->FileCount + pdpr->DirectoryCount != 0) {
  1034. if (!(pdpr->rgfSwitches & BAREFORMATSWITCH )) {
  1035. mystrcpy( szDriveNext, TEXT("") );
  1036. if (pfsCur->pfsNext) {
  1037. GetDrive( pfsCur->pfsNext->pszDir, szDriveNext );
  1038. }
  1039. if (_tcsicmp( szDriveNext, szDriveCur )) {
  1040. if ((pdpr->rgfSwitches & RECURSESWITCH) != 0) {
  1041. CHECKSTATUS ( WriteEol( pscr ));
  1042. CHECKSTATUS( DisplayTotals( pscr, pdpr->FileCount, &pdpr->TotalBytes, pdpr->rgfSwitches ));
  1043. }
  1044. CHECKSTATUS( DisplayDiskFreeSpace( pscr, szPathForFreeSpace, pdpr->rgfSwitches, pdpr->DirectoryCount ));
  1045. }
  1046. }
  1047. } else if (!CtrlCSeen) {
  1048. if (rc == ERROR_ACCESS_DENIED) {
  1049. PutStdErr( rc, NOARGS );
  1050. } else if (pdpr->FileCount + pdpr->DirectoryCount == 0) {
  1051. WriteFlush( pscr );
  1052. PutStdErr( MSG_FILE_NOT_FOUND, NOARGS );
  1053. rc = 1;
  1054. }
  1055. }
  1056. FreeStr(pfsCur->pszDir);
  1057. for(i = 1, ppatdscCur = pfsCur->ppatdsc;
  1058. i <= pfsCur->cpatdsc;
  1059. i++, ppatdscCur = ppatdscX) {
  1060. ppatdscX = ppatdscCur->ppatdscNext;
  1061. FreeStr(ppatdscCur->pszPattern);
  1062. FreeStr(ppatdscCur->pszDir);
  1063. FreeStr((PTCHAR)ppatdscCur);
  1064. }
  1065. if (pfsPrev) {
  1066. FreeStr((PTCHAR)pfsPrev);
  1067. }
  1068. pfsPrev = pfsCur;
  1069. }
  1070. WriteFlush( pscr );
  1071. return(rc);
  1072. }
  1073. /*++
  1074. Routine Description:
  1075. Arguments:
  1076. Return Value:
  1077. Return:
  1078. --*/
  1079. int
  1080. _cdecl
  1081. CmpName(
  1082. const void *elem1,
  1083. const void *elem2
  1084. )
  1085. {
  1086. int result;
  1087. result = lstrcmpi( ((PFF)(* (PPFF)elem1))->data.cFileName, ((PFF)(* (PPFF)elem2))->data.cFileName);
  1088. return result;
  1089. }
  1090. /*++
  1091. Routine Description:
  1092. Arguments:
  1093. Return Value:
  1094. Return:
  1095. --*/
  1096. int
  1097. _cdecl
  1098. CmpExt(
  1099. const void *pszElem1,
  1100. const void *pszElem2
  1101. )
  1102. {
  1103. PTCHAR pszElem1T, pszElem2T;
  1104. int rc;
  1105. //
  1106. // Move pointer to name to make it all easier to read
  1107. //
  1108. pszElem1 = &(((PFF)(* (PPFF)pszElem1))->data.cFileName);
  1109. pszElem2 = &(((PFF)(* (PPFF)pszElem2))->data.cFileName);
  1110. //
  1111. // Locate the extensions if any
  1112. //
  1113. if (((pszElem1T = mystrrchr( pszElem1, DOT)) == NULL ) ||
  1114. (!_tcscmp(TEXT(".."),pszElem1) || !_tcscmp(TEXT("."),pszElem1)) ) {
  1115. //
  1116. // If no extension then point to end of string
  1117. //
  1118. pszElem1T = ((PTCHAR)pszElem1) + mystrlen(pszElem1 );
  1119. }
  1120. if (((pszElem2T = mystrrchr( pszElem2, DOT)) == NULL ) ||
  1121. (!_tcscmp(TEXT(".."),pszElem2) || !_tcscmp(TEXT("."),pszElem2)) ) {
  1122. //
  1123. // If no extension then point to end of string
  1124. //
  1125. pszElem2T = ((PTCHAR)pszElem2) + mystrlen(pszElem2 );
  1126. }
  1127. rc = lstrcmpi( pszElem1T, pszElem2T );
  1128. return rc;
  1129. }
  1130. /*++
  1131. Routine Description:
  1132. Arguments:
  1133. Return Value:
  1134. Return:
  1135. --*/
  1136. int
  1137. _cdecl
  1138. CmpTime(
  1139. const void *pszElem1,
  1140. const void *pszElem2
  1141. )
  1142. {
  1143. LPFILETIME pft1, pft2;
  1144. switch (dwTimeType) {
  1145. case LAST_ACCESS_TIME:
  1146. pft1 = & ((* (PPFF)pszElem1)->data.ftLastAccessTime);
  1147. pft2 = & ((* (PPFF)pszElem2)->data.ftLastAccessTime);
  1148. break;
  1149. case LAST_WRITE_TIME:
  1150. pft1 = & ((* (PPFF)pszElem1)->data.ftLastWriteTime);
  1151. pft2 = & ((* (PPFF)pszElem2)->data.ftLastWriteTime);
  1152. break;
  1153. case CREATE_TIME:
  1154. pft1 = & ((* (PPFF)pszElem1)->data.ftCreationTime);
  1155. pft2 = & ((* (PPFF)pszElem2)->data.ftCreationTime);
  1156. break;
  1157. }
  1158. return(CompareFileTime( pft1, pft2 ) );
  1159. }
  1160. /*++
  1161. Routine Description:
  1162. Arguments:
  1163. Return Value:
  1164. Return:
  1165. --*/
  1166. int
  1167. _cdecl
  1168. CmpSize(
  1169. const void * pszElem1,
  1170. const void * pszElem2
  1171. )
  1172. {
  1173. ULARGE_INTEGER ul1, ul2;
  1174. ul1.HighPart = (* (PPFF)pszElem1)->data.nFileSizeHigh;
  1175. ul2.HighPart = (* (PPFF)pszElem2)->data.nFileSizeHigh;
  1176. ul1.LowPart = (* (PPFF)pszElem1)->data.nFileSizeLow;
  1177. ul2.LowPart = (* (PPFF)pszElem2)->data.nFileSizeLow;
  1178. if (ul1.QuadPart < ul2.QuadPart)
  1179. return -1;
  1180. else
  1181. if (ul1.QuadPart > ul2.QuadPart)
  1182. return 1;
  1183. else
  1184. return 0;
  1185. }
  1186. /*++
  1187. Routine Description:
  1188. Arguments:
  1189. Return Value:
  1190. Return:
  1191. --*/
  1192. int
  1193. _cdecl
  1194. CmpType(
  1195. const void *pszElem1,
  1196. const void *pszElem2
  1197. )
  1198. {
  1199. //
  1200. // This dependents upon FILE_ATTRIBUTE_DIRECTORY not being the high bit.
  1201. //
  1202. return( (( (* (PPFF)pszElem2)->data.dwFileAttributes) & FILE_ATTRIBUTE_DIRECTORY) -
  1203. (( (* (PPFF)pszElem1)->data.dwFileAttributes) & FILE_ATTRIBUTE_DIRECTORY) );
  1204. }
  1205. /*++
  1206. Routine Description:
  1207. Arguments:
  1208. Return Value:
  1209. Return:
  1210. --*/
  1211. int
  1212. _cdecl
  1213. SortCompare(
  1214. IN const void * elem1,
  1215. IN const void * elem2
  1216. )
  1217. {
  1218. ULONG irgsrt;
  1219. int rc;
  1220. //
  1221. // prgsrtdsc is set in SortFileList
  1222. //
  1223. for (irgsrt = 0; prgsrtdsc[irgsrt].fctCmp; irgsrt++) {
  1224. if (prgsrtdsc[irgsrt].Order == DESCENDING) {
  1225. if (rc = prgsrtdsc[irgsrt].fctCmp(elem2, elem1)) {
  1226. return( rc );
  1227. }
  1228. } else {
  1229. if (rc = prgsrtdsc[irgsrt].fctCmp(elem1, elem2)) {
  1230. return( rc );
  1231. }
  1232. }
  1233. }
  1234. return( 0 );
  1235. }
  1236. /*++
  1237. Routine Description:
  1238. Arguments:
  1239. Return Value:
  1240. Return:
  1241. --*/
  1242. VOID
  1243. SortFileList(
  1244. IN PFS pfsFiles,
  1245. IN PSORTDESC prgsrtdscLocal,
  1246. IN ULONG dwTimeTypeLocal
  1247. )
  1248. {
  1249. //
  1250. // Set these globally to handle fixed parameters list for qsort
  1251. //
  1252. dwTimeType = dwTimeTypeLocal;
  1253. prgsrtdsc = prgsrtdscLocal;
  1254. //
  1255. // Make sure there is something to sort
  1256. //
  1257. if (pfsFiles->cff) {
  1258. if (prgsrtdsc[0].fctCmp) {
  1259. qsort(pfsFiles->prgpff,
  1260. pfsFiles->cff,
  1261. sizeof(PTCHAR),
  1262. SortCompare);
  1263. }
  1264. }
  1265. }