ReactOS 0.4.15-dev-6054-gbddd8b0
dir.c
Go to the documentation of this file.
1/*
2 * DIR.C - dir internal command.
3 *
4 *
5 * History:
6 *
7 * 01/29/97 (Tim Norman)
8 * started.
9 *
10 * 06/13/97 (Tim Norman)
11 * Fixed code.
12 *
13 * 07/12/97 (Tim Norman)
14 * Fixed bug that caused the root directory to be unlistable
15 *
16 * 07/12/97 (Marc Desrochers)
17 * Changed to use maxx, maxy instead of findxy()
18 *
19 * 06/08/98 (Rob Lake)
20 * Added compatibility for /w in dir
21 *
22 * 06/09/98 (Rob Lake)
23 * Compatibility for dir/s started
24 * Tested that program finds directories off root fine
25 *
26 * 06/10/98 (Rob Lake)
27 * do_recurse saves the cwd and also stores it in Root
28 * build_tree adds the cwd to the beginning of its' entries
29 * Program runs fine, added print_tree -- works fine.. as EXE,
30 * program won't work properly as COM.
31 *
32 * 06/11/98 (Rob Lake)
33 * Found problem that caused COM not to work
34 *
35 * 06/12/98 (Rob Lake)
36 * debugged...
37 * added free mem routine
38 *
39 * 06/13/98 (Rob Lake)
40 * debugged the free mem routine
41 * debugged whole thing some more
42 * Notes:
43 * ReadDir stores Root name and _Read_Dir does the hard work
44 * PrintDir prints Root and _Print_Dir does the hard work
45 * KillDir kills Root _after_ _Kill_Dir does the hard work
46 * Integrated program into DIR.C(this file) and made some same
47 * changes throughout
48 *
49 * 06/14/98 (Rob Lake)
50 * Cleaned up code a bit, added comments
51 *
52 * 06/16/98 (Rob Lake)
53 * Added error checking to my previously added routines
54 *
55 * 06/17/98 (Rob Lake)
56 * Rewrote recursive functions, again! Most other recursive
57 * functions are now obsolete -- ReadDir, PrintDir, _Print_Dir,
58 * KillDir and _Kill_Dir. do_recurse does what PrintDir did
59 * and _Read_Dir did what it did before along with what _Print_Dir
60 * did. Makes /s a lot faster!
61 * Reports 2 more files/dirs that MS-DOS actually reports
62 * when used in root directory(is this because dir defaults
63 * to look for read only files?)
64 * Added support for /b, /a and /l
65 * Made error message similar to DOS error messages
66 * Added help screen
67 *
68 * 06/20/98 (Rob Lake)
69 * Added check for /-(switch) to turn off previously defined
70 * switches.
71 * Added ability to check for DIRCMD in environment and
72 * process it
73 *
74 * 06/21/98 (Rob Lake)
75 * Fixed up /B
76 * Now can dir *.ext/X, no spaces!
77 *
78 * 06/29/98 (Rob Lake)
79 * error message now found in command.h
80 *
81 * 07/08/1998 (John P. Price)
82 * removed extra returns; closer to MSDOS
83 * fixed wide display so that an extra return is not displayed
84 * when there is five filenames in the last line.
85 *
86 * 07/12/98 (Rob Lake)
87 * Changed error messages
88 *
89 * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
90 * added config.h include
91 *
92 *
93 * 04-Dec-1998 (Eric Kohl)
94 * Converted source code to Win32, except recursive dir ("dir /s").
95 *
96 * 10-Dec-1998 (Eric Kohl)
97 * Fixed recursive dir ("dir /s").
98 *
99 * 14-Dec-1998 (Eric Kohl)
100 * Converted to Win32 directory functions and
101 * fixed some output bugs. There are still some more ;)
102 *
103 * 10-Jan-1999 (Eric Kohl)
104 * Added "/N" and "/4" options, "/O" is a dummy.
105 * Added locale support.
106 *
107 * 20-Jan-1999 (Eric Kohl)
108 * Redirection safe!
109 *
110 * 01-Mar-1999 (Eric Kohl)
111 * Replaced all runtime io functions by their Win32 counterparts.
112 *
113 * 23-Feb-2001 (Carl Nettelblad <cnettel@hem.passagen.se>)
114 * dir /s now works in deeper trees
115 *
116 * 28-Jan-2004 (Michael Fritscher <michael@fritscher.net>)
117 * Fix for /p, so it is working under Windows in GUI-mode, too.
118 *
119 * 30-Apr-2004 (Filip Navara <xnavara@volny.cz>)
120 * Fix /w to print long names.
121 *
122 * 27-Feb-2005 (Konstantinos Paliouras <squarious@gmail.com>)
123 * Implemented all the switches that were missing, and made
124 * the ROS dir very similar to windows dir. Major part of
125 * the code is rewritten. /p is removed, to be rewritten in
126 * the main cmd code.
127 *
128 * 1-Jul-2004 (Brandon Turner <turnerb7@msu.edu>)
129 * Added /p back in using ConOutPrintfPaging
130 *
131 * 3-Feb-2007 (Paolo Devoti devotip at gmail)
132 * Removed variables formerly in use to handle pagination
133 * Pagination belongs to ConOutPrintfPaging
134 * Removed already commented out code of old pagination
135 *
136 * 25-Aug-2015 (Pierre Schweitzer)
137 * Implemented /R switch
138 *
139 * 6-Aug-2018 (Hermes Belusca-Maito and Katayama Hirofumi MZ)
140 * Fix handling of patterns containing trailing dots.
141 */
142
143#include "precomp.h"
144
145#ifdef INCLUDE_CMD_DIR
146
147/* Time Field enumeration */
149{
154
155/* Ordered by enumeration */
157{
162 ORDER_TIME = 4
164
165/* The struct for holding the switches */
166typedef struct _DirSwitchesFlags
167{
168 BOOL bBareFormat; /* Bare Format */
169 BOOL bTSeparator; /* Thousands separator */
170 BOOL bWideList; /* Wide list format */
171 BOOL bWideListColSort; /* Wide list format but sorted by column */
172 BOOL bLowerCase; /* Uses lower case */
173 BOOL bNewLongList; /* New long list */
174 BOOL bPause; /* Pause per page */
175 BOOL bUser; /* Displays the owner of file */
176 BOOL bRecursive; /* Displays files in specified directory and all sub */
177 BOOL bShortName; /* Displays the sort name of files if exist */
178 BOOL b4Digit; /* Four digit year */
179 BOOL bDataStreams; /* Displays alternate data streams */
180 struct
181 {
182 DWORD dwAttribVal; /* The desired state of attribute */
183 DWORD dwAttribMask; /* Which attributes to check */
184 } stAttribs; /* Displays files with this attributes only */
185 struct
186 {
187 enum EOrderBy eCriteria[3]; /* Criterias used to order by */
188 BOOL bCriteriaRev[3]; /* If the criteria is in reversed order */
189 short sCriteriaCount; /* The quantity of criterias */
190 } stOrderBy; /* Ordered by criterias */
191 struct
192 {
193 enum ETimeField eTimeField; /* The time field that will be used for */
194 } stTimeField; /* The time field to display or use for sorting */
196
197typedef struct _DIRFINDSTREAMNODE
198{
199 WIN32_FIND_STREAM_DATA stStreamInfo;
202
203typedef struct _DIRFINDINFO
204{
208
209typedef struct _DIRFINDLISTNODE
210{
214
215typedef BOOL
217
218/* Globally save the # of dirs, files and bytes,
219 * probably later pass them to functions. Rob Lake */
223
224/*
225 * help
226 *
227 * displays help screen for dir
228 * Rob Lake
229 */
230static VOID
232{
234}
235
236/* Check whether this is a dot-directory "." or "..", speed-optimized */
238BOOL
240 IN LPCTSTR pszPath)
241{
242 return ( pszPath[0] == _T('.') &&
243 ( pszPath[1] == 0 || /* pszPath[1] == _T('\\') || */
244 (pszPath[1] == _T('.') && (pszPath[2] == 0 /* || pszPath[2] == _T('\\') */))
245 ) );
246}
247
249BOOL
251 IN const TCHAR* pPath,
253{
254 return ((Length == 1 && pPath[0] == _T('.')) ||
255 (Length == 2 && pPath[0] == _T('.') && pPath[1] == _T('.')));
256}
257
258/*
259 * DirReadParameters
260 *
261 * Parse the parameters and switches of the command line and exports them
262 */
263static BOOL
264DirReadParam(LPTSTR Line, /* [IN] The line with the parameters & switches */
265 LPTSTR** params, /* [OUT] The parameters after parsing */
266 LPINT entries, /* [OUT] The number of parameters after parsing */
267 LPDIRSWITCHFLAGS lpFlags) /* [IN/OUT] The flags after calculating switches */
268{
269 TCHAR cCurSwitch; /* The current switch */
270 TCHAR cCurChar; /* Current examined character */
271 TCHAR cCurUChar; /* Current upper examined character */
272 BOOL bNegative; /* Negative switch */
273 BOOL bPNegative; /* Negative switch parameter */
274 BOOL bIntoQuotes; /* A flag showing if we are in quotes (") */
275 LPTSTR ptrStart; /* A pointer to the first character of a parameter */
276 LPTSTR ptrEnd; /* A pointer to the last character of a parameter */
277 BOOL bOrderByNoPar; /* A flag to indicate /O with no switch parameter */
278 LPTSTR temp;
279
280 /* Initialize parameter array */
281 *params = NULL;
282 *entries = 0;
283
284 /* Initialize variables; */
285 cCurSwitch = _T(' ');
286 bNegative = FALSE;
287 bPNegative = FALSE;
288
289 /* We suppose that switch parameters
290 were given to avoid setting them to default
291 if the switch was not given */
292 bOrderByNoPar = FALSE;
293
294 /* Main Loop (see README_DIR.txt) */
295 /* scan the command line char per char, and we process its char */
296 while (*Line)
297 {
298 /* we save current character as it is and its upper case */
299 cCurChar = *Line;
300 cCurUChar = _totupper(*Line);
301
302 /* 1st section (see README_DIR.txt) */
303 /* When a switch is expecting */
304 if (cCurSwitch == _T('/'))
305 {
306 while (_istspace(*Line))
307 Line++;
308
309 bNegative = (*Line == _T('-'));
310 Line += bNegative;
311
312 cCurChar = *Line;
313 cCurUChar = _totupper(*Line);
314
315 if ((cCurUChar == _T('A')) ||(cCurUChar == _T('T')) || (cCurUChar == _T('O')))
316 {
317 /* If positive, prepare for parameters... if negative, reset to defaults */
318 switch (cCurUChar)
319 {
320 case _T('A'):
321 lpFlags->stAttribs.dwAttribVal = 0L;
322 lpFlags->stAttribs.dwAttribMask = 0L;
323 if (bNegative)
324 lpFlags->stAttribs.dwAttribMask = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM;
325 break;
326 case _T('T'):
327 if (bNegative)
328 lpFlags->stTimeField.eTimeField = TF_MODIFIEDDATE;
329 break;
330 case _T('O'):
331 bOrderByNoPar = !bNegative;
332 lpFlags->stOrderBy.sCriteriaCount = 0;
333 break;
334 }
335
336 if (!bNegative)
337 {
338 /* Positive switch, so it can take parameters. */
339 cCurSwitch = cCurUChar;
340 Line++;
341 /* Skip optional leading colon */
342 if (*Line == _T(':'))
343 Line++;
344 continue;
345 }
346 }
347 else if (cCurUChar == _T('L'))
348 lpFlags->bLowerCase = ! bNegative;
349 else if (cCurUChar == _T('B'))
350 lpFlags->bBareFormat = ! bNegative;
351 else if (cCurUChar == _T('C'))
352 lpFlags->bTSeparator = ! bNegative;
353 else if (cCurUChar == _T('W'))
354 lpFlags->bWideList = ! bNegative;
355 else if (cCurUChar == _T('D'))
356 lpFlags->bWideListColSort = ! bNegative;
357 else if (cCurUChar == _T('N'))
358 lpFlags->bNewLongList = ! bNegative;
359 else if (cCurUChar == _T('P'))
360 lpFlags->bPause = ! bNegative;
361 else if (cCurUChar == _T('Q'))
362 lpFlags->bUser = ! bNegative;
363 else if (cCurUChar == _T('S'))
364 lpFlags->bRecursive = ! bNegative;
365 else if (cCurUChar == _T('X'))
366 lpFlags->bShortName = ! bNegative;
367 else if (cCurUChar == _T('R'))
368 lpFlags->bDataStreams = ! bNegative;
369 else if (cCurChar == _T('4'))
370 lpFlags->b4Digit = ! bNegative;
371 else if (cCurChar == _T('?'))
372 {
373 DirHelp();
374 return FALSE;
375 }
376 else
377 {
379 return FALSE;
380 }
381
382 /* Make sure there's no extra characters at the end of the switch */
383 if (Line[1] && Line[1] != _T('/') && !_istspace(Line[1]))
384 {
386 return FALSE;
387 }
388
389 cCurSwitch = _T(' ');
390 }
391 else if (cCurSwitch == _T(' '))
392 {
393 /* 2nd section (see README_DIR.txt) */
394 /* We are expecting parameter or the unknown */
395
396 if (cCurChar == _T('/'))
397 cCurSwitch = _T('/');
398 else if (_istspace(cCurChar))
399 /* do nothing */;
400 else
401 {
402 /* This is a file/directory name parameter. Find its end */
403 ptrStart = Line;
404 bIntoQuotes = FALSE;
405 while (*Line)
406 {
407 if (!bIntoQuotes && (*Line == _T('/') || _istspace(*Line)))
408 break;
409 bIntoQuotes ^= (*Line == _T('"'));
410 Line++;
411 }
412 ptrEnd = Line;
413
414 /* Copy it to the entries list */
415 temp = cmd_alloc((ptrEnd - ptrStart + 1) * sizeof(TCHAR));
416 if (!temp)
417 return FALSE;
418 memcpy(temp, ptrStart, (ptrEnd - ptrStart) * sizeof(TCHAR));
419 temp[ptrEnd - ptrStart] = _T('\0');
421 if (!add_entry(entries, params, temp))
422 {
423 cmd_free(temp);
424 freep(*params);
425 return FALSE;
426 }
427
428 cmd_free(temp);
429 continue;
430 }
431 }
432 else
433 {
434 /* 3rd section (see README_DIR.txt) */
435 /* We are waiting for switch parameters */
436
437 /* Check if there are no more switch parameters */
438 if ((cCurChar == _T('/')) || _istspace(cCurChar))
439 {
440 /* Wrong decision path, reprocess current character */
441 cCurSwitch = _T(' ');
442 continue;
443 }
444 /* Process parameter switch */
445 switch (cCurSwitch)
446 {
447 case _T('A'): /* Switch parameters for /A (attributes filter) */
448 if (cCurChar == _T('-'))
449 bPNegative = TRUE;
450 else if (cCurUChar == _T('D'))
451 {
452 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_DIRECTORY;
453 if (bPNegative)
454 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_DIRECTORY;
455 else
456 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_DIRECTORY;
457 }
458 else if (cCurUChar == _T('R'))
459 {
460 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_READONLY;
461 if (bPNegative)
462 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_READONLY;
463 else
464 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_READONLY;
465 }
466 else if (cCurUChar == _T('H'))
467 {
468 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_HIDDEN;
469 if (bPNegative)
470 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_HIDDEN;
471 else
472 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_HIDDEN;
473 }
474 else if (cCurUChar == _T('A'))
475 {
476 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_ARCHIVE;
477 if (bPNegative)
478 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_ARCHIVE;
479 else
480 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_ARCHIVE;
481 }
482 else if (cCurUChar == _T('S'))
483 {
484 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_SYSTEM;
485 if (bPNegative)
486 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_SYSTEM;
487 else
488 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_SYSTEM;
489 }
490 else
491 {
493 return FALSE;
494 }
495 break;
496 case _T('T'): /* Switch parameters for /T (time field) */
497 if (cCurUChar == _T('C'))
498 lpFlags->stTimeField.eTimeField= TF_CREATIONDATE ;
499 else if (cCurUChar == _T('A'))
500 lpFlags->stTimeField.eTimeField= TF_LASTACCESSEDDATE ;
501 else if (cCurUChar == _T('W'))
502 lpFlags->stTimeField.eTimeField= TF_MODIFIEDDATE ;
503 else
504 {
506 return FALSE;
507 }
508 break;
509 case _T('O'): /* Switch parameters for /O (order) */
510 /* Ok a switch parameter was given */
511 bOrderByNoPar = FALSE;
512
513 if (cCurChar == _T('-'))
514 bPNegative = TRUE;
515 else if (cCurUChar == _T('N'))
516 {
517 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++;
518 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative;
519 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_NAME;
520 }
521 else if (cCurUChar == _T('S'))
522 {
523 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++;
524 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative;
525 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_SIZE;
526 }
527 else if (cCurUChar == _T('G'))
528 {
529 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++;
530 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative;
531 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_DIRECTORY;
532 }
533 else if (cCurUChar == _T('E'))
534 {
535 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++;
536 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative;
537 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_EXTENSION;
538 }
539 else if (cCurUChar == _T('D'))
540 {
541 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++;
542 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative;
543 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_TIME;
544 }
545
546 else
547 {
549 return FALSE;
550 }
551
552
553 }
554 /* We check if we calculated the negative value and release the flag */
555 if ((cCurChar != _T('-')) && bPNegative)
556 bPNegative = FALSE;
557 }
558
559 Line++;
560 }
561
562 /* /O with no switch parameters acts like /O:GN */
563 if (bOrderByNoPar)
564 {
565 lpFlags->stOrderBy.sCriteriaCount = 2;
566 lpFlags->stOrderBy.eCriteria[0] = ORDER_DIRECTORY;
567 lpFlags->stOrderBy.bCriteriaRev[0] = FALSE;
568 lpFlags->stOrderBy.eCriteria[1] = ORDER_NAME;
569 lpFlags->stOrderBy.bCriteriaRev[1] = FALSE;
570 }
571
572 return TRUE;
573}
574
575/* Print either with or without paging, depending on /P switch */
576static BOOL
577DirPrintf(LPDIRSWITCHFLAGS lpFlags, LPTSTR szFormat, ...)
578{
579 BOOL Done = TRUE;
580 va_list arg_ptr;
581 va_start(arg_ptr, szFormat);
582 if (lpFlags->bPause)
583 Done = ConPrintfVPaging(&StdOutPager, FALSE, szFormat, arg_ptr);
584 else
585 ConPrintfV(StdOut, szFormat, arg_ptr);
586 va_end(arg_ptr);
587 return Done;
588}
589
590
591/*
592 * PrintDirectoryHeader
593 *
594 * print the header for the dir command
595 */
596static BOOL
598{
600 LPCTSTR szFullDir;
601 TCHAR szRootName[MAX_PATH];
602 TCHAR szVolName[80];
603 DWORD dwSerialNr;
604
605 if (lpFlags->bBareFormat)
606 return TRUE;
607
608 szFullDir = szPath;
609
610 /* Get the media ID of the drive */
611 if (!GetVolumePathName(szFullDir, szRootName, ARRAYSIZE(szRootName)) ||
612 !GetVolumeInformation(szRootName, szVolName, ARRAYSIZE(szVolName),
613 &dwSerialNr, NULL, NULL, NULL, 0))
614 {
615 return TRUE;
616 }
617
618 /* Print drive info */
619 if (szVolName[0] != _T('\0'))
620 {
622 DirPrintf(lpFlags, szMsg, _totupper(szRootName[0]), szVolName);
623 }
624 else
625 {
627 DirPrintf(lpFlags, szMsg, _totupper(szRootName[0]));
628 }
629
630 /* Print the volume serial number if the return was successful */
632 DirPrintf(lpFlags, szMsg, HIWORD(dwSerialNr), LOWORD(dwSerialNr));
633
634 return TRUE;
635}
636
637
638static VOID
640 TCHAR *lpTime,
641 LPWIN32_FIND_DATA lpFile,
642 LPDIRSWITCHFLAGS lpFlags)
643{
644 FILETIME ft;
645 SYSTEMTIME dt;
646
647 /* Select the right time field */
648 switch (lpFlags->stTimeField.eTimeField)
649 {
650 case TF_CREATIONDATE:
651 if (!FileTimeToLocalFileTime(&lpFile->ftCreationTime, &ft))
652 return;
653 FileTimeToSystemTime(&ft, &dt);
654 break;
655
657 if (!FileTimeToLocalFileTime(&lpFile->ftLastAccessTime, &ft))
658 return;
659 FileTimeToSystemTime(&ft, &dt);
660 break;
661
662 case TF_MODIFIEDDATE:
663 if (!FileTimeToLocalFileTime(&lpFile->ftLastWriteTime, &ft))
664 return;
665 FileTimeToSystemTime(&ft, &dt);
666 break;
667 }
668
669 FormatDate(lpDate, &dt, lpFlags->b4Digit);
670 FormatTime(lpTime, &dt);
671}
672
673INT
674FormatDate(TCHAR *lpDate, LPSYSTEMTIME dt, BOOL b4Digit)
675{
676 /* Format date */
677 WORD wYear = b4Digit ? dt->wYear : dt->wYear%100;
678 switch (nDateFormat)
679 {
680 case 0: /* mmddyy */
681 default:
682 return _stprintf(lpDate, _T("%02d%c%02d%c%0*d"),
684 dt->wDay, cDateSeparator,
685 b4Digit?4:2, wYear);
686 break;
687
688 case 1: /* ddmmyy */
689 return _stprintf(lpDate, _T("%02d%c%02d%c%0*d"),
690 dt->wDay, cDateSeparator, dt->wMonth,
691 cDateSeparator, b4Digit?4:2, wYear);
692 break;
693
694 case 2: /* yymmdd */
695 return _stprintf(lpDate, _T("%0*d%c%02d%c%02d"),
696 b4Digit?4:2, wYear, cDateSeparator,
697 dt->wMonth, cDateSeparator, dt->wDay);
698 break;
699 }
700}
701
702INT
704{
705 /* Format Time */
706 switch (nTimeFormat)
707 {
708 case 0: /* 12 hour format */
709 default:
710 return _stprintf(lpTime,_T("%02d%c%02u %cM"),
711 (dt->wHour == 0 ? 12 : (dt->wHour <= 12 ? dt->wHour : dt->wHour - 12)),
713 dt->wMinute, (dt->wHour <= 11 ? _T('A') : _T('P')));
714 break;
715
716 case 1: /* 24 hour format */
717 return _stprintf(lpTime, _T("%02d%c%02u"),
718 dt->wHour, cTimeSeparator, dt->wMinute);
719 break;
720 }
721}
722
723
724static VOID
726 PULARGE_INTEGER lpFreeSpace)
727{
728 PGETFREEDISKSPACEEX pGetFreeDiskSpaceEx;
730 DWORD dwSecPerCl;
731 DWORD dwBytPerSec;
732 DWORD dwFreeCl;
733 DWORD dwTotCl;
734 ULARGE_INTEGER TotalNumberOfBytes, TotalNumberOfFreeBytes;
735
736 lpFreeSpace->QuadPart = 0;
737
738 hInstance = GetModuleHandle(_T("KERNEL32"));
739 if (hInstance != NULL)
740 {
741 pGetFreeDiskSpaceEx = (PGETFREEDISKSPACEEX)GetProcAddress(hInstance,
742#ifdef _UNICODE
743 "GetDiskFreeSpaceExW");
744#else
745 "GetDiskFreeSpaceExA");
746#endif
747 if (pGetFreeDiskSpaceEx != NULL)
748 {
749 if (pGetFreeDiskSpaceEx(lpRoot, lpFreeSpace, &TotalNumberOfBytes, &TotalNumberOfFreeBytes) != FALSE)
750 return;
751 }
752 }
753
754 GetDiskFreeSpace(lpRoot,
755 &dwSecPerCl,
756 &dwBytPerSec,
757 &dwFreeCl,
758 &dwTotCl);
759
760 lpFreeSpace->QuadPart = dwSecPerCl * dwBytPerSec * dwFreeCl;
761}
762
763
764/*
765 * print_summary: prints dir summary
766 * Added by Rob Lake 06/17/98 to compact code
767 * Just copied Tim's Code and patched it a bit
768 */
769static INT
771 ULONG ulFiles,
772 ULONG ulDirs,
773 ULONGLONG u64Bytes,
774 LPDIRSWITCHFLAGS lpFlags,
775 BOOL TotalSummary)
776{
778 TCHAR szBuffer[64];
779 ULARGE_INTEGER uliFree;
780
781 /* Here we check if we didn't find anything */
782 if (!(ulFiles + ulDirs))
783 {
784 if (!lpFlags->bRecursive || (TotalSummary && lpFlags->bRecursive))
786 return 1;
787 }
788
789 /* In bare format we don't print results */
790 if (lpFlags->bBareFormat)
791 return 0;
792
793 /* Print recursive specific results */
794
795 /* Take this code offline to fix /S does not print double info */
796 if (TotalSummary && lpFlags->bRecursive)
797 {
798 ConvertULargeInteger(u64Bytes, szBuffer, ARRAYSIZE(szBuffer), lpFlags->bTSeparator);
800 DirPrintf(lpFlags, szMsg, ulFiles, szBuffer);
801 }
802 else
803 {
804 /* Print File Summary */
805 /* Condition to print summary is:
806 If we are not in bare format and if we have results! */
807 ConvertULargeInteger(u64Bytes, szBuffer, ARRAYSIZE(szBuffer), lpFlags->bTSeparator);
809 DirPrintf(lpFlags, szMsg, ulFiles, szBuffer);
810 }
811
812 /* Print total directories and free space */
813 if (!lpFlags->bRecursive || TotalSummary)
814 {
815 GetUserDiskFreeSpace(szPath, &uliFree);
816 ConvertULargeInteger(uliFree.QuadPart, szBuffer, ARRAYSIZE(szBuffer), lpFlags->bTSeparator);
818 DirPrintf(lpFlags, szMsg, ulDirs, szBuffer);
819 }
820
821 return 0;
822}
823
824/*
825 * getExt
826 *
827 * Get the extension of a filename
828 */
830{
831 static TCHAR *NoExt = _T("");
832 TCHAR* lastdot = _tcsrchr(file, _T('.'));
833 return (lastdot != NULL ? lastdot + 1 : NoExt);
834}
835
836/*
837 * getName
838 *
839 * Get the name of the file without extension
840 */
841static LPTSTR
843{
844 INT_PTR iLen;
845 LPTSTR end;
846
847 /* Check for dot-directories "." and ".." */
848 if (IsDotDirectory(file))
849 {
850 _tcscpy(dest, file);
851 return dest;
852 }
853
854 end = _tcsrchr(file, _T('.'));
855 if (!end)
856 iLen = _tcslen(file);
857 else
858 iLen = (end - file);
859
860 _tcsncpy(dest, file, iLen);
861 *(dest + iLen) = _T('\0');
862
863 return dest;
864}
865
866
867/*
868 * DirPrintNewList
869 *
870 * The function that prints in new style
871 */
872static VOID
873DirPrintNewList(PDIRFINDINFO ptrFiles[], /* [IN]Files' Info */
874 DWORD dwCount, /* [IN] The quantity of files */
875 LPCTSTR szCurPath, /* [IN] Full path of current directory */
876 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */
877{
878 DWORD i;
879 TCHAR szSize[30];
880 TCHAR szShortName[15];
881 TCHAR szDate[20];
882 TCHAR szTime[20];
883 INT iSizeFormat;
884 ULARGE_INTEGER u64FileSize;
885 PDIRFINDSTREAMNODE ptrCurStream;
886
887 for (i = 0; i < dwCount && !CheckCtrlBreak(BREAK_INPUT); i++)
888 {
889 /* Calculate size */
890 if (ptrFiles[i]->stFindInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
891 {
892 /* Junction */
893 iSizeFormat = -14;
894 _tcscpy(szSize, _T("<JUNCTION>"));
895 }
896 else if (ptrFiles[i]->stFindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
897 {
898 /* Directory */
899 iSizeFormat = -14;
900 _tcscpy(szSize, _T("<DIR>"));
901 }
902 else
903 {
904 /* File */
905 iSizeFormat = 14;
906 u64FileSize.HighPart = ptrFiles[i]->stFindInfo.nFileSizeHigh;
907 u64FileSize.LowPart = ptrFiles[i]->stFindInfo.nFileSizeLow;
908 ConvertULargeInteger(u64FileSize.QuadPart, szSize, 20, lpFlags->bTSeparator);
909 }
910
911 /* Calculate short name */
912 szShortName[0] = _T('\0');
913 if (lpFlags->bShortName)
914 _stprintf(szShortName, _T(" %-12s"), ptrFiles[i]->stFindInfo.cAlternateFileName);
915
916 /* Format date and time */
917 DirPrintFileDateTime(szDate, szTime, &ptrFiles[i]->stFindInfo, lpFlags);
918
919 /* Print the line */
920 DirPrintf(lpFlags, _T("%10s %-6s %*s%s %s\n"),
921 szDate,
922 szTime,
923 iSizeFormat,
924 szSize,
925 szShortName,
926 ptrFiles[i]->stFindInfo.cFileName);
927
928 /* Now, loop on the streams */
929 ptrCurStream = ptrFiles[i]->ptrHead;
930 while (ptrCurStream)
931 {
932 ConvertULargeInteger(ptrCurStream->stStreamInfo.StreamSize.QuadPart, szSize, 20, lpFlags->bTSeparator);
933
934 /* Print the line */
935 DirPrintf(lpFlags, _T("%10s %-6s %*s%s %s%s\n"),
936 L"",
937 L"",
938 16,
939 szSize,
940 L"",
941 ptrFiles[i]->stFindInfo.cFileName,
942 ptrCurStream->stStreamInfo.cStreamName);
943 ptrCurStream = ptrCurStream->ptrNext;
944 }
945 }
946}
947
948
949/*
950 * DirPrintWideList
951 *
952 * The function that prints in wide list
953 */
954static VOID
955DirPrintWideList(PDIRFINDINFO ptrFiles[], /* [IN] Files' Info */
956 DWORD dwCount, /* [IN] The quantity of files */
957 LPCTSTR szCurPath, /* [IN] Full path of current directory */
958 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */
959{
960 SHORT iScreenWidth;
961 USHORT iColumns;
962 USHORT iLines;
963 UINT_PTR iLongestName;
964 TCHAR szTempFname[MAX_PATH];
965 DWORD i;
966 DWORD j;
967 DWORD temp;
968
969 /* Calculate longest name */
970 iLongestName = 1;
971 for (i = 0; i < dwCount; i++)
972 {
973 if (ptrFiles[i]->stFindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
974 {
975 /* Directories need 2 additional characters for brackets */
976 if ((_tcslen(ptrFiles[i]->stFindInfo.cFileName) + 2) > iLongestName)
977 iLongestName = _tcslen(ptrFiles[i]->stFindInfo.cFileName) + 2;
978 }
979 else
980 {
981 if (_tcslen(ptrFiles[i]->stFindInfo.cFileName) > iLongestName)
982 iLongestName = _tcslen(ptrFiles[i]->stFindInfo.cFileName);
983 }
984 }
985
986 /* Count the highest number of columns */
987 GetScreenSize(&iScreenWidth, NULL);
988 iColumns = (USHORT)(iScreenWidth / iLongestName);
989
990 /* Check if there is enough space for spaces between names */
991 if (((iLongestName * iColumns) + iColumns) >= (UINT)iScreenWidth)
992 iColumns --;
993
994 /* A last check at iColumns to avoid division by zero */
995 if (!iColumns) iColumns = 1;
996
997 /* Calculate the lines that will be printed */
998 iLines = (USHORT)((dwCount + iColumns - 1) / iColumns);
999
1000 for (i = 0; i < iLines && !CheckCtrlBreak(BREAK_INPUT); i++)
1001 {
1002 for (j = 0; j < iColumns; j++)
1003 {
1004 if (lpFlags->bWideListColSort)
1005 {
1006 /* Print Column sorted */
1007 temp = (j * iLines) + i;
1008 }
1009 else
1010 {
1011 /* Print Line sorted */
1012 temp = (i * iColumns) + j;
1013 }
1014
1015 if (temp >= dwCount) break;
1016
1017 if (ptrFiles[temp]->stFindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1018 _stprintf(szTempFname, _T("[%s]"), ptrFiles[temp]->stFindInfo.cFileName);
1019 else
1020 _stprintf(szTempFname, _T("%s"), ptrFiles[temp]->stFindInfo.cFileName);
1021
1022 DirPrintf(lpFlags, _T("%-*s"), iLongestName + 1, szTempFname);
1023 }
1024
1025 /* Add a new line after the last item in the column */
1026 DirPrintf(lpFlags, _T("\n"));
1027 }
1028}
1029
1030
1031/*
1032 * DirPrintOldList
1033 *
1034 * The function that prints in old style
1035 */
1036static VOID
1037DirPrintOldList(PDIRFINDINFO ptrFiles[], /* [IN] Files' Info */
1038 DWORD dwCount, /* [IN] The quantity of files */
1039 LPCTSTR szCurPath, /* [IN] Full path of current directory */
1040 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */
1041{
1042 DWORD i; /* An indexer for "for"s */
1043 TCHAR szName[10]; /* The name of file */
1044 TCHAR szExt[5]; /* The extension of file */
1045 TCHAR szDate[30],szTime[30]; /* Used to format time and date */
1046 TCHAR szSize[30]; /* The size of file */
1047 int iSizeFormat; /* The format of size field */
1048 ULARGE_INTEGER u64FileSize; /* The file size */
1049
1050 for (i = 0; i < dwCount && !CheckCtrlBreak(BREAK_INPUT); i++)
1051 {
1052 /* Broke 8.3 format */
1053 if (*ptrFiles[i]->stFindInfo.cAlternateFileName )
1054 {
1055 /* If the file is long named then we read the alter name */
1056 getName( ptrFiles[i]->stFindInfo.cAlternateFileName, szName);
1057 _tcscpy(szExt, getExt( ptrFiles[i]->stFindInfo.cAlternateFileName));
1058 }
1059 else
1060 {
1061 /* If the file is not long name we read its original name */
1062 getName( ptrFiles[i]->stFindInfo.cFileName, szName);
1063 _tcscpy(szExt, getExt( ptrFiles[i]->stFindInfo.cFileName));
1064 }
1065
1066 /* Calculate size */
1067 if (ptrFiles[i]->stFindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1068 {
1069 /* Directory, no size it's a directory */
1070 iSizeFormat = -17;
1071 _tcscpy(szSize, _T("<DIR>"));
1072 }
1073 else
1074 {
1075 /* File */
1076 iSizeFormat = 17;
1077 u64FileSize.HighPart = ptrFiles[i]->stFindInfo.nFileSizeHigh;
1078 u64FileSize.LowPart = ptrFiles[i]->stFindInfo.nFileSizeLow;
1079 ConvertULargeInteger(u64FileSize.QuadPart, szSize, 20, lpFlags->bTSeparator);
1080 }
1081
1082 /* Format date and time */
1083 DirPrintFileDateTime(szDate,szTime,&ptrFiles[i]->stFindInfo,lpFlags);
1084
1085 /* Print the line */
1086 DirPrintf(lpFlags, _T("%-8s %-3s %*s %s %s\n"),
1087 szName, /* The file's 8.3 name */
1088 szExt, /* The file's 8.3 extension */
1089 iSizeFormat, /* print format for size column */
1090 szSize, /* The size of file or "<DIR>" for dirs */
1091 szDate, /* The date of file/dir */
1092 szTime); /* The time of file/dir */
1093 }
1094}
1095
1096/*
1097 * DirPrintBareList
1098 *
1099 * The function that prints in bare format
1100 */
1101static VOID
1102DirPrintBareList(PDIRFINDINFO ptrFiles[], /* [IN] Files' Info */
1103 DWORD dwCount, /* [IN] The number of files */
1104 LPCTSTR szCurPath, /* [IN] Full path of current directory */
1105 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */
1106{
1107 DWORD i;
1108
1109 for (i = 0; i < dwCount && !CheckCtrlBreak(BREAK_INPUT); i++)
1110 {
1111 if (IsDotDirectory(ptrFiles[i]->stFindInfo.cFileName))
1112 {
1113 /* At bare format we don't print the dot-directories "." and ".." */
1114 continue;
1115 }
1116 if (lpFlags->bRecursive)
1117 {
1118 /* At recursive mode we print full path of file */
1119 DirPrintf(lpFlags, _T("%s\\%s\n"), szCurPath, ptrFiles[i]->stFindInfo.cFileName);
1120 }
1121 else
1122 {
1123 /* If we are not in recursive mode we print the file names */
1124 DirPrintf(lpFlags, _T("%s\n"), ptrFiles[i]->stFindInfo.cFileName);
1125 }
1126 }
1127}
1128
1129
1130/*
1131 * DirPrintFiles
1132 *
1133 * The functions that prints the files list
1134 */
1135static VOID
1136DirPrintFiles(PDIRFINDINFO ptrFiles[], /* [IN] Files' Info */
1137 DWORD dwCount, /* [IN] The quantity of files */
1138 LPCTSTR szCurPath, /* [IN] Full path of current directory */
1139 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */
1140{
1142 TCHAR szTemp[MAX_PATH]; /* A buffer to format the directory header */
1143
1144 /* Print trailing backslash for root directory of drive */
1145 _tcscpy(szTemp, szCurPath);
1146 if (_tcslen(szTemp) == 2 && szTemp[1] == _T(':'))
1147 _tcscat(szTemp, _T("\\"));
1148
1149 /* Condition to print header:
1150 We are not printing in bare format
1151 and if we are in recursive mode... we must have results */
1152 if (!lpFlags->bBareFormat && !(lpFlags->bRecursive && (dwCount <= 0)))
1153 {
1155 if (!DirPrintf(lpFlags, szMsg, szTemp))
1156 return;
1157 }
1158
1159 if (lpFlags->bBareFormat)
1160 {
1161 /* Bare format */
1162 DirPrintBareList(ptrFiles, dwCount, szCurPath, lpFlags);
1163 }
1164 else if (lpFlags->bShortName)
1165 {
1166 /* New list style / Short names */
1167 DirPrintNewList(ptrFiles, dwCount, szCurPath, lpFlags);
1168 }
1169 else if (lpFlags->bWideListColSort || lpFlags->bWideList)
1170 {
1171 /* Wide list */
1172 DirPrintWideList(ptrFiles, dwCount, szCurPath, lpFlags);
1173 }
1174 else if (lpFlags->bNewLongList )
1175 {
1176 /* New list style*/
1177 DirPrintNewList(ptrFiles, dwCount, szCurPath, lpFlags);
1178 }
1179 else
1180 {
1181 /* If nothing is selected old list is the default */
1182 DirPrintOldList(ptrFiles, dwCount, szCurPath, lpFlags);
1183 }
1184}
1185
1186/*
1187 * CompareFiles
1188 *
1189 * Compares 2 files based on the order criteria
1190 */
1191static BOOL
1192CompareFiles(PDIRFINDINFO lpFile1, /* [IN] A pointer to WIN32_FIND_DATA of file 1 */
1193 PDIRFINDINFO lpFile2, /* [IN] A pointer to WIN32_FIND_DATA of file 2 */
1194 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags that we use to list */
1195{
1196 ULARGE_INTEGER u64File1;
1197 ULARGE_INTEGER u64File2;
1198 int i;
1199 long iComp = 0; /* The comparison result */
1200
1201 /* Calculate criteria by order given from user */
1202 for (i = 0; i < lpFlags->stOrderBy.sCriteriaCount; i++)
1203 {
1204
1205 /* Calculate criteria */
1206 switch (lpFlags->stOrderBy.eCriteria[i])
1207 {
1208 case ORDER_SIZE: /* Order by size /o:s */
1209 /* concat the 32bit integers to a 64bit */
1210 u64File1.LowPart = lpFile1->stFindInfo.nFileSizeLow;
1211 u64File1.HighPart = lpFile1->stFindInfo.nFileSizeHigh;
1212 u64File2.LowPart = lpFile2->stFindInfo.nFileSizeLow;
1213 u64File2.HighPart = lpFile2->stFindInfo.nFileSizeHigh;
1214
1215 /* In case that difference is too big for a long */
1216 if (u64File1.QuadPart < u64File2.QuadPart)
1217 iComp = -1;
1218 else if (u64File1.QuadPart > u64File2.QuadPart)
1219 iComp = 1;
1220 else
1221 iComp = 0;
1222 break;
1223
1224 case ORDER_DIRECTORY: /* Order by directory attribute /o:g */
1225 iComp = ((lpFile2->stFindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)-
1226 (lpFile1->stFindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY));
1227 break;
1228
1229 case ORDER_EXTENSION: /* Order by extension name /o:e */
1230 iComp = _tcsicmp(getExt(lpFile1->stFindInfo.cFileName),getExt(lpFile2->stFindInfo.cFileName));
1231 break;
1232
1233 case ORDER_NAME: /* Order by filename /o:n */
1234 iComp = _tcsicmp(lpFile1->stFindInfo.cFileName, lpFile2->stFindInfo.cFileName);
1235 break;
1236
1237 case ORDER_TIME: /* Order by file's time /o:t */
1238 /* We compare files based on the time field selected by /t */
1239 switch (lpFlags->stTimeField.eTimeField)
1240 {
1241 case TF_CREATIONDATE:
1242 /* concat the 32bit integers to a 64bit */
1243 u64File1.LowPart = lpFile1->stFindInfo.ftCreationTime.dwLowDateTime;
1244 u64File1.HighPart = lpFile1->stFindInfo.ftCreationTime.dwHighDateTime ;
1245 u64File2.LowPart = lpFile2->stFindInfo.ftCreationTime.dwLowDateTime;
1246 u64File2.HighPart = lpFile2->stFindInfo.ftCreationTime.dwHighDateTime ;
1247 break;
1248 case TF_LASTACCESSEDDATE :
1249 /* concat the 32bit integers to a 64bit */
1250 u64File1.LowPart = lpFile1->stFindInfo.ftLastAccessTime.dwLowDateTime;
1251 u64File1.HighPart = lpFile1->stFindInfo.ftLastAccessTime.dwHighDateTime ;
1252 u64File2.LowPart = lpFile2->stFindInfo.ftLastAccessTime.dwLowDateTime;
1253 u64File2.HighPart = lpFile2->stFindInfo.ftLastAccessTime.dwHighDateTime ;
1254 break;
1255 case TF_MODIFIEDDATE:
1256 /* concat the 32bit integers to a 64bit */
1257 u64File1.LowPart = lpFile1->stFindInfo.ftLastWriteTime.dwLowDateTime;
1258 u64File1.HighPart = lpFile1->stFindInfo.ftLastWriteTime.dwHighDateTime ;
1259 u64File2.LowPart = lpFile2->stFindInfo.ftLastWriteTime.dwLowDateTime;
1260 u64File2.HighPart = lpFile2->stFindInfo.ftLastWriteTime.dwHighDateTime ;
1261 break;
1262 }
1263
1264 /* In case that difference is too big for a long */
1265 if (u64File1.QuadPart < u64File2.QuadPart)
1266 iComp = -1;
1267 else if (u64File1.QuadPart > u64File2.QuadPart)
1268 iComp = 1;
1269 else
1270 iComp = 0;
1271 break;
1272 }
1273
1274 /* Reverse if desired */
1275 if (lpFlags->stOrderBy.bCriteriaRev[i])
1276 iComp *= -1;
1277
1278 /* If that criteria was enough for distinguishing
1279 the files/dirs,there is no need to calculate the others*/
1280 if (iComp != 0) break;
1281 }
1282
1283 /* Translate the value of iComp to boolean */
1284 return iComp > 0;
1285}
1286
1287/*
1288 * QsortFiles
1289 *
1290 * Sort files by the order criterias using quicksort method
1291 */
1292static VOID
1293QsortFiles(PDIRFINDINFO ptrArray[], /* [IN/OUT] The array with file info pointers */
1294 int i, /* [IN] The index of first item in array */
1295 int j, /* [IN] The index to last item in array */
1296 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags that we will use to sort */
1297{
1298 PDIRFINDINFO lpTemp; /* A temporary pointer */
1299 BOOL Way;
1300
1301 if (i < j)
1302 {
1303 int First = i, Last = j, Temp;
1304 Way = TRUE;
1305 while (i != j)
1306 {
1307 if (Way == CompareFiles(ptrArray[i], ptrArray[j], lpFlags))
1308 {
1309 /* Swap the pointers of the array */
1310 lpTemp = ptrArray[i];
1311 ptrArray[i]= ptrArray[j];
1312 ptrArray[j] = lpTemp;
1313
1314 /* Swap the indexes for inverting sorting */
1315 Temp = i;
1316 i = j;
1317 j =Temp;
1318
1319 Way = !Way;
1320 }
1321
1322 j += (!Way - Way);
1323 }
1324
1325 QsortFiles(ptrArray,First, i-1, lpFlags);
1326 QsortFiles(ptrArray,i+1,Last, lpFlags);
1327 }
1328}
1329
1330static VOID
1332 PDWORD pdwCount)
1333{
1334 PDIRFINDLISTNODE ptrNextNode;
1335 PDIRFINDSTREAMNODE ptrFreeNode;
1336 while (ptrStartNode)
1337 {
1338 ptrNextNode = ptrStartNode->ptrNext;
1339 while (ptrStartNode->stInfo.ptrHead)
1340 {
1341 ptrFreeNode = ptrStartNode->stInfo.ptrHead;
1342 ptrStartNode->stInfo.ptrHead = ptrFreeNode->ptrNext;
1343 cmd_free(ptrFreeNode);
1344 }
1345 cmd_free(ptrStartNode);
1346 ptrStartNode = ptrNextNode;
1347 --(*pdwCount);
1348 }
1349}
1350
1351/*
1352 * DirList
1353 *
1354 * The function that does everything except for printing results
1355 */
1356static INT
1357DirList(IN OUT LPTSTR szFullPath, /* [IN] The full path we are listing with trailing '\', where dir starts */
1358 IN LPTSTR pszFilePart, /* [IN] Pointer in the szFullPath buffer where the file (pattern) part starts*/
1359 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags of the listing */
1360{
1361 HANDLE hSearch; /* The handle of the search */
1362 HANDLE hRecSearch; /* The handle for searching recursively */
1363 HANDLE hStreams; /* The handle for alternate streams */
1364 WIN32_FIND_DATA wfdFileInfo; /* The info of file that found */
1365 PDIRFINDINFO * ptrFileArray; /* An array of pointers with all the files */
1366 PDIRFINDLISTNODE ptrStartNode; /* The pointer to the first node */
1367 PDIRFINDLISTNODE ptrNextNode; /* A pointer used for relatives references */
1368 TCHAR szSubPath[MAX_PATH]; /* The full path used for the recursive search */
1369 LPTSTR pszSubFilePart;
1370 TCHAR cPathSep;
1371 DWORD dwCount; /* A counter of files found in directory */
1372 DWORD dwCountFiles; /* Counter for files */
1373 DWORD dwCountDirs; /* Counter for directories */
1374 ULONGLONG u64CountBytes; /* Counter for bytes */
1375 ULARGE_INTEGER u64Temp; /* A temporary counter */
1376 WIN32_FIND_STREAM_DATA wfsdStreamInfo;
1377 PDIRFINDSTREAMNODE * ptrCurNode; /* The pointer to the first stream */
1378 static HANDLE (WINAPI *pFindFirstStreamW)(LPCWSTR, STREAM_INFO_LEVELS, LPVOID, DWORD);
1379 static BOOL (WINAPI *pFindNextStreamW)(HANDLE, LPVOID);
1380
1381 /* Initialize variables */
1382 ptrStartNode = NULL;
1383 ptrNextNode = NULL;
1384 dwCount = 0;
1385 dwCountFiles = 0;
1386 dwCountDirs = 0;
1387 u64CountBytes = 0;
1388
1389 /* Prepare the linked list, first node is allocated */
1390 ptrStartNode = cmd_alloc(sizeof(DIRFINDLISTNODE));
1391 if (ptrStartNode == NULL)
1392 {
1393 WARN("Cannot allocate memory for ptrStartNode!\n");
1394 return 1; /* Error cannot allocate memory for 1st object */
1395 }
1396 ptrStartNode->stInfo.ptrHead = NULL;
1397 ptrNextNode = ptrStartNode;
1398
1399 /* Collect the results for the current directory */
1400 hSearch = FindFirstFile(szFullPath, &wfdFileInfo);
1401 if (hSearch != INVALID_HANDLE_VALUE)
1402 {
1403 do
1404 {
1405 if ((wfdFileInfo.dwFileAttributes & lpFlags->stAttribs.dwAttribMask) ==
1406 (lpFlags->stAttribs.dwAttribMask & lpFlags->stAttribs.dwAttribVal))
1407 {
1408 ptrNextNode->ptrNext = cmd_alloc(sizeof(DIRFINDLISTNODE));
1409 if (ptrNextNode->ptrNext == NULL)
1410 {
1411 WARN("Cannot allocate memory for ptrNextNode->ptrNext!\n");
1412 DirNodeCleanup(ptrStartNode, &dwCount);
1413 FindClose(hSearch);
1414 return 1;
1415 }
1416
1417 /* Copy the info of search at linked list */
1418 memcpy(&ptrNextNode->ptrNext->stInfo.stFindInfo,
1419 &wfdFileInfo,
1420 sizeof(WIN32_FIND_DATA));
1421
1422 /* If lower case is selected do it here */
1423 if (lpFlags->bLowerCase)
1424 {
1425 _tcslwr(ptrNextNode->ptrNext->stInfo.stFindInfo.cAlternateFileName);
1426 _tcslwr(ptrNextNode->ptrNext->stInfo.stFindInfo.cFileName);
1427 }
1428
1429 /* No streams (yet?) */
1430 ptrNextNode->ptrNext->stInfo.ptrHead = NULL;
1431
1432 /* Alternate streams are only displayed with new long list */
1433 if (lpFlags->bNewLongList && lpFlags->bDataStreams)
1434 {
1435 if (!pFindFirstStreamW)
1436 {
1437 pFindFirstStreamW = (PVOID)GetProcAddress(GetModuleHandle(_T("kernel32")), "FindFirstStreamW");
1438 pFindNextStreamW = (PVOID)GetProcAddress(GetModuleHandle(_T("kernel32")), "FindNextStreamW");
1439 }
1440
1441 /* Try to get stream information */
1442 if (pFindFirstStreamW && pFindNextStreamW)
1443 {
1444 hStreams = pFindFirstStreamW(wfdFileInfo.cFileName, FindStreamInfoStandard, &wfsdStreamInfo, 0);
1445 }
1446 else
1447 {
1448 hStreams = INVALID_HANDLE_VALUE;
1449 ERR("FindFirstStreamW not supported!\n");
1450 }
1451
1452 if (hStreams != INVALID_HANDLE_VALUE)
1453 {
1454 /* We totally ignore first stream. It contains data about ::$DATA */
1455 ptrCurNode = &ptrNextNode->ptrNext->stInfo.ptrHead;
1456 while (pFindNextStreamW(hStreams, &wfsdStreamInfo))
1457 {
1458 *ptrCurNode = cmd_alloc(sizeof(DIRFINDSTREAMNODE));
1459 if (*ptrCurNode == NULL)
1460 {
1461 WARN("Cannot allocate memory for *ptrCurNode!\n");
1462 DirNodeCleanup(ptrStartNode, &dwCount);
1463 FindClose(hStreams);
1464 FindClose(hSearch);
1465 return 1;
1466 }
1467
1468 memcpy(&(*ptrCurNode)->stStreamInfo, &wfsdStreamInfo,
1469 sizeof(WIN32_FIND_STREAM_DATA));
1470
1471 /* If lower case is selected do it here */
1472 if (lpFlags->bLowerCase)
1473 {
1474 _tcslwr((*ptrCurNode)->stStreamInfo.cStreamName);
1475 }
1476
1477 ptrCurNode = &(*ptrCurNode)->ptrNext;
1478 }
1479
1480 FindClose(hStreams);
1481 *ptrCurNode = NULL;
1482 }
1483 }
1484
1485 /* Continue at next node at linked list */
1486 ptrNextNode = ptrNextNode->ptrNext;
1487 dwCount++;
1488
1489 /* Grab statistics */
1490 if (wfdFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1491 {
1492 /* Directory */
1493 dwCountDirs++;
1494 }
1495 else
1496 {
1497 /* File */
1498 dwCountFiles++;
1499 u64Temp.HighPart = wfdFileInfo.nFileSizeHigh;
1500 u64Temp.LowPart = wfdFileInfo.nFileSizeLow;
1501 u64CountBytes += u64Temp.QuadPart;
1502 }
1503 }
1504 } while (FindNextFile(hSearch, &wfdFileInfo));
1505 FindClose(hSearch);
1506 }
1507
1508 /* Terminate list */
1509 ptrNextNode->ptrNext = NULL;
1510
1511 /* Calculate and allocate space need for making an array of pointers */
1512 ptrFileArray = cmd_alloc(sizeof(PDIRFINDINFO) * dwCount);
1513 if (ptrFileArray == NULL)
1514 {
1515 WARN("Cannot allocate memory for ptrFileArray!\n");
1516 DirNodeCleanup(ptrStartNode, &dwCount);
1517 return 1;
1518 }
1519
1520 /*
1521 * Create an array of pointers from the linked list
1522 * this will be used to sort and print data, rather than the list
1523 */
1524 ptrNextNode = ptrStartNode;
1525 dwCount = 0;
1526 while (ptrNextNode->ptrNext)
1527 {
1528 ptrFileArray[dwCount] = &ptrNextNode->ptrNext->stInfo;
1529 ptrNextNode = ptrNextNode->ptrNext;
1530 dwCount++;
1531 }
1532
1533 /* Sort Data if requested */
1534 if (lpFlags->stOrderBy.sCriteriaCount > 0)
1535 QsortFiles(ptrFileArray, 0, dwCount-1, lpFlags);
1536
1537 /* Print Data */
1538 cPathSep = pszFilePart[-1];
1539 pszFilePart[-1] = _T('\0'); /* Truncate to directory name only */
1540 DirPrintFiles(ptrFileArray, dwCount, szFullPath, lpFlags);
1541
1542 if (lpFlags->bRecursive)
1543 {
1544 PrintSummary(szFullPath,
1545 dwCountFiles,
1546 dwCountDirs,
1547 u64CountBytes,
1548 lpFlags,
1549 FALSE);
1550 }
1551 pszFilePart[-1] = cPathSep;
1552
1553 /* Free array */
1554 cmd_free(ptrFileArray);
1555
1556 /* Free linked list */
1557 DirNodeCleanup(ptrStartNode, &dwCount);
1558
1560 return 1;
1561
1562 /* Add statistics to recursive statistics */
1563 recurse_dir_cnt += dwCountDirs;
1564 recurse_file_cnt += dwCountFiles;
1565 recurse_bytes += u64CountBytes;
1566
1567 /*
1568 * Do the recursive job if requested.
1569 * The recursion is done on ALL (independent of their attributes)
1570 * directories of the current one.
1571 */
1572 if (lpFlags->bRecursive)
1573 {
1574 /* The new search is involving any *.* file */
1575 memcpy(szSubPath, szFullPath, (pszFilePart - szFullPath) * sizeof(TCHAR));
1576 _tcscpy(&szSubPath[pszFilePart - szFullPath], _T("*.*"));
1577
1578 hRecSearch = FindFirstFile(szSubPath, &wfdFileInfo);
1579 if (hRecSearch != INVALID_HANDLE_VALUE)
1580 {
1581 do
1582 {
1583 /* We search for directories other than "." and ".." */
1584 if (!IsDotDirectory(wfdFileInfo.cFileName) &&
1585 (wfdFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1586 {
1587 /* Concat the path and the directory to do recursive */
1588 memcpy(szSubPath, szFullPath, (pszFilePart - szFullPath) * sizeof(TCHAR));
1589 _tcscpy(&szSubPath[pszFilePart - szFullPath], wfdFileInfo.cFileName);
1590 _tcscat(szSubPath, _T("\\"));
1591 pszSubFilePart = &szSubPath[_tcslen(szSubPath)];
1592 _tcscat(pszSubFilePart, pszFilePart);
1593
1594 /* We do the same for the directory */
1595 if (DirList(szSubPath, pszSubFilePart, lpFlags) != 0)
1596 {
1597 FindClose(hRecSearch);
1598 return 1;
1599 }
1600 }
1601 } while (FindNextFile(hRecSearch, &wfdFileInfo));
1602 }
1603 FindClose(hRecSearch);
1604 }
1605
1606 return 0;
1607}
1608
1609static VOID
1611 IN LPTSTR pszPattern,
1613 OUT LPTSTR pszFullPath,
1614 OUT LPTSTR* ppszPatternPart OPTIONAL)
1615{
1616 LPTSTR pCurDir, pNextDir, ptr;
1617 LPTSTR pszPatternPart;
1618 TCHAR szNewPattern[MAX_PATH];
1619
1620 /*
1621 * We are going to use GetFullPathName() to properly determine the actual
1622 * full path from the pattern. However, due to the fact GetFullPathName()
1623 * strips parts of the file name component in case the pattern contains
1624 * path specification with trailing dots, it is required to perform a
1625 * pre-treatment on the pattern and a post-treatment on the obtained path.
1626 * This is mandatory in order to use the correct file search criterion.
1627 *
1628 * One particular case is when the pattern specifies a dots-only directory
1629 * followed by either the "." or ".." special directories. In this case the
1630 * GetFullPathName() function may completely miss the dots-only directory.
1631 * An example is given by the pattern (C-string notation) "\\...\\." .
1632 * To cope with this problem we need to partially canonicalize the pattern
1633 * by collapsing any "." or ".." special directory that immediately follows
1634 * a dots-only directory. We collapse in addition consecutive backslashes.
1635 *
1636 * Finally, trailing dots are skipped by GetFullPathName(). Therefore
1637 * a pattern that matches files with no extension, for example: "*." ,
1638 * or: "dir\\noextfile." , are reduced to simply "*" or "dir\\noextfile",
1639 * that match files with extensions. Or, a pattern specifying a trailing
1640 * dots-only directory: "dir\\..." gets completely ignored and only the
1641 * full path to "dir" is returned.
1642 * To fix this second problem we need to restore the last part of the path
1643 * pattern using the pattern that has been first partially canonicalized.
1644 *
1645 * Note however that the "." or ".." special directories are always
1646 * interpreted correctly by GetFullPathName().
1647 */
1648
1649 /* Make a copy of the path pattern */
1650 ASSERT(_tcslen(pszPattern) < ARRAYSIZE(szNewPattern));
1651 _tcscpy(szNewPattern, pszPattern);
1652 pszPattern = szNewPattern;
1653
1654 TRACE("Original pszPattern: %S\n", pszPattern);
1655
1656 /* Convert slashes into backslashes */
1657 pNextDir = pszPattern;
1658 while ((pNextDir = _tcschr(pNextDir, _T('/'))))
1659 *pNextDir++ = _T('\\');
1660
1661 /*
1662 * Find any dots-only directory and collapse any "." or ".." special
1663 * directory that immediately follows it.
1664 * Note that we just start looking after the first path separator. Indeed,
1665 * dots-only directories that are not preceded by a path separator, and so
1666 * appear first in the pattern, for example: "...\dir", or: "..." , are
1667 * either correctly handled by GetFullPathName() because they are followed
1668 * by a non-pathological directory, or because they are handled when we
1669 * restore the trailing dots pattern piece in the next step.
1670 */
1671 pNextDir = pszPattern;
1672 while (pNextDir)
1673 {
1674 pCurDir = pNextDir;
1675
1676 /* Find the next path separator in the pattern */
1677 pNextDir = _tcschr(pNextDir, _T('\\'));
1678 if (!pNextDir)
1679 break;
1680
1681 /* Ignore the special "." and ".." directories that are correctly handled */
1682 if ((pNextDir - pCurDir == 0) || IsDotDirectoryN(pCurDir, pNextDir - pCurDir))
1683 {
1684 /* Found such a directory, ignore */
1685 ++pNextDir;
1686 continue;
1687 }
1688
1689 /* Check whether this is a dots-only directory */
1690 for (ptr = pCurDir; ptr < pNextDir; ++ptr)
1691 {
1692 if (*ptr != _T('.'))
1693 break;
1694 }
1695 if (ptr < pNextDir)
1696 {
1697 /* Not a dots-only directory, ignore */
1698 ++pNextDir;
1699 continue;
1700 }
1701
1702 /* Skip any consecutive backslashes */
1703 for (ptr = pNextDir; *ptr == _T('\\'); ++ptr) ;
1704
1705 /* pCurDir is a dots-only directory, perform partial canonicalization */
1706
1707 /* Remove any following "." directory */
1708 if (ptr[0] == _T('.') && (ptr[1] == _T('\\') || ptr[1] == 0))
1709 {
1710 memmove(pNextDir, ptr + 1, (_tcslen(ptr + 1) + 1) * sizeof(TCHAR));
1711 }
1712 /* Remove any following ".." directory */
1713 else if (ptr[0] == _T('.') && ptr[1] == _T('.') && (ptr[2] == _T('\\') || ptr[2] == 0))
1714 {
1715 /* Skip any consecutive backslashes before the next directory */
1716 for (ptr = ptr + 2; *ptr == _T('\\'); ++ptr) ;
1717
1718 memmove(pCurDir, ptr, (_tcslen(ptr) + 1) * sizeof(TCHAR));
1719 pNextDir = pCurDir;
1720 }
1721 else
1722 {
1723 ++pNextDir;
1724
1725 /* Collapse consecutive backslashes */
1726 if (ptr > pNextDir)
1727 memmove(pNextDir, ptr, (_tcslen(ptr) + 1) * sizeof(TCHAR));
1728 }
1729 }
1730
1731 /* An empty pattern means we enumerate all files in the current directory */
1732 if (!*pszPattern)
1733 _tcscpy(pszPattern, _T("*"));
1734
1735 TRACE("New pszPattern: %S\n", pszPattern);
1736
1737 /* Create the full path */
1738 if (GetFullPathName(pszPattern, nBufferLength, pszFullPath, &pszPatternPart) == 0)
1739 {
1740 _tcscpy(pszFullPath, pszPattern);
1741 pszPatternPart = NULL;
1742 }
1743
1744 TRACE("pszFullPath (1): %S\n", pszFullPath);
1745 TRACE("pszPatternPart (1): %S\n", pszPatternPart);
1746
1747 /*
1748 * Restore the correct file name component in case the pattern contained
1749 * trailing dots that have been skipped by GetFullPathName().
1750 */
1751
1752 /* Find the last path separator in the original szPath */
1753 pNextDir = _tcsrchr(pszPattern, _T('\\'));
1754 if (pNextDir)
1755 {
1756 /* Skip past the separator and look at the path */
1757 ++pNextDir;
1758 }
1759 else
1760 {
1761 /* The pattern is the path we need to look at */
1762 pNextDir = pszPattern;
1763 }
1764
1765 /*
1766 * When pszPatternPart == NULL this means that pszFullPath should be a
1767 * directory; however it might have happened that the original pattern
1768 * was specifying a dots-only directory, that has been stripped off by
1769 * GetFullPathName(). In both these cases we need to restore these as
1770 * they are part of the actual directory path; the exception being if
1771 * these are the special "." or ".." directories.
1772 */
1773 if (_istalpha(pNextDir[0]) && pNextDir[1] == _T(':') && pNextDir[2] != _T('\\'))
1774 {
1775 /*
1776 * The syntax "<drive_letter>:" without any trailing backslash actually
1777 * means: "current directory on this drive".
1778 */
1779 }
1780 else if (pszPatternPart == NULL)
1781 {
1782 ASSERT(pszFullPath[_tcslen(pszFullPath)-1] == _T('\\'));
1783
1784 /* Anything NOT being "." or ".." (the special directories) must be fully restored */
1785 if (*pNextDir && !IsDotDirectory(pNextDir))
1786 {
1787 pszPatternPart = &pszFullPath[_tcslen(pszFullPath)];
1788 _tcscpy(pszPatternPart, pNextDir);
1789 pszPatternPart = NULL;
1790 }
1791 }
1792 else if (_tcscmp(pNextDir, pszPatternPart) != 0)
1793 {
1794 /*
1795 * For example, pszPatternPart == "." or ".." and we do not need to
1796 * do anything for these, or pszPatternPart == "dir\\noextfile." and
1797 * we need to restore all the trailing points.
1798 */
1799 TRACE("pszPatternPart: %S is DIFFERENT from file criterion: %S\n", pszPatternPart, pNextDir);
1800
1801 /* Anything NOT being "." or ".." (the special directories) must be fully restored */
1802 if (*pNextDir && !IsDotDirectory(pNextDir))
1803 {
1804 /* Restore the correct file criterion */
1805 _tcscpy(pszPatternPart, pNextDir);
1806 }
1807 }
1808
1809 TRACE("pszFullPath (2): %S\n", pszFullPath);
1810
1811 /*
1812 * If no wildcard or file was specified and this is a directory,
1813 * display all files in it.
1814 */
1815 if (pszPatternPart == NULL || IsExistingDirectory(pszFullPath))
1816 {
1817 pszPatternPart = &pszFullPath[_tcslen(pszFullPath)];
1818 if (pszPatternPart[-1] != _T('\\'))
1819 *pszPatternPart++ = _T('\\');
1820 _tcscpy(pszPatternPart, _T("*"));
1821 }
1822
1823 TRACE("pszPatternPart (2): %S\n", pszPatternPart);
1824
1825 if (ppszPatternPart)
1826 *ppszPatternPart = pszPatternPart;
1827}
1828
1829/*
1830 * dir
1831 *
1832 * internal dir command
1833 */
1834INT
1836{
1837 TCHAR dircmd[MAX_PATH]; /* A variable to store the DIRCMD environment variable */
1838 TCHAR prev_volume[MAX_PATH];
1839 TCHAR szFullPath[MAX_PATH];
1840 LPTSTR* params = NULL;
1841 LPTSTR pszFilePart;
1842 TCHAR cPathSep;
1843 INT entries = 0;
1844 UINT loop = 0;
1845 DIRSWITCHFLAGS stFlags;
1846 INT ret = 1;
1847 BOOL ChangedVolume;
1848
1849 /* Initialize Switch Flags < Default switches are set here! > */
1850 stFlags.b4Digit = TRUE;
1851 stFlags.bBareFormat = FALSE;
1852 stFlags.bDataStreams = FALSE;
1853 stFlags.bLowerCase = FALSE;
1854 stFlags.bNewLongList = TRUE;
1855 stFlags.bPause = FALSE;
1856 stFlags.bRecursive = FALSE;
1857 stFlags.bShortName = FALSE;
1858 stFlags.bTSeparator = TRUE;
1859 stFlags.bUser = FALSE;
1860 stFlags.bWideList = FALSE;
1861 stFlags.bWideListColSort = FALSE;
1862 stFlags.stTimeField.eTimeField = TF_MODIFIEDDATE;
1863 stFlags.stAttribs.dwAttribMask = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM;
1864 stFlags.stAttribs.dwAttribVal = 0L;
1865 stFlags.stOrderBy.sCriteriaCount = 0;
1866
1867 nErrorLevel = 0;
1868
1869 /* Read the parameters from the DIRCMD environment variable */
1870 if (GetEnvironmentVariable (_T("DIRCMD"), dircmd, ARRAYSIZE(dircmd)))
1871 {
1872 if (!DirReadParam(dircmd, &params, &entries, &stFlags))
1873 {
1874 nErrorLevel = 1;
1875 goto cleanup;
1876 }
1877 }
1878
1879 /* Read the parameters */
1880 if (!DirReadParam(rest, &params, &entries, &stFlags) || CheckCtrlBreak(BREAK_INPUT))
1881 {
1882 nErrorLevel = 1;
1883 goto cleanup;
1884 }
1885
1886 /* Default to current directory */
1887 if (entries == 0)
1888 {
1889 if (!add_entry(&entries, &params, _T("*")))
1890 {
1891 nErrorLevel = 1;
1892 goto cleanup;
1893 }
1894 }
1895
1896 prev_volume[0] = _T('\0');
1897
1898 /* Reset paging state */
1899 if (stFlags.bPause)
1901
1902 for (loop = 0; loop < (UINT)entries; loop++)
1903 {
1905 {
1906 nErrorLevel = 1;
1907 goto cleanup;
1908 }
1909
1910 recurse_dir_cnt = 0L;
1912 recurse_bytes = 0;
1913
1914 /* <Debug :>
1915 Uncomment this to show the final state of switch flags*/
1916 {
1917 int i;
1918 TRACE("Attributes mask/value %x/%x\n",stFlags.stAttribs.dwAttribMask,stFlags.stAttribs.dwAttribVal);
1919 TRACE("(B) Bare format : %i\n", stFlags.bBareFormat);
1920 TRACE("(C) Thousand : %i\n", stFlags.bTSeparator);
1921 TRACE("(W) Wide list : %i\n", stFlags.bWideList);
1922 TRACE("(D) Wide list sort by column : %i\n", stFlags.bWideListColSort);
1923 TRACE("(L) Lowercase : %i\n", stFlags.bLowerCase);
1924 TRACE("(N) New : %i\n", stFlags.bNewLongList);
1925 TRACE("(O) Order : %i\n", stFlags.stOrderBy.sCriteriaCount);
1926 for (i =0;i<stFlags.stOrderBy.sCriteriaCount;i++)
1927 TRACE(" Order Criteria [%i]: %i (Reversed: %i)\n",i, stFlags.stOrderBy.eCriteria[i], stFlags.stOrderBy.bCriteriaRev[i]);
1928 TRACE("(P) Pause : %i\n", stFlags.bPause);
1929 TRACE("(Q) Owner : %i\n", stFlags.bUser);
1930 TRACE("(R) Data stream : %i\n", stFlags.bDataStreams);
1931 TRACE("(S) Recursive : %i\n", stFlags.bRecursive);
1932 TRACE("(T) Time field : %i\n", stFlags.stTimeField.eTimeField);
1933 TRACE("(X) Short names : %i\n", stFlags.bShortName);
1934 TRACE("Parameter : %s\n", debugstr_aw(params[loop]));
1935 }
1936
1937 /* Print the drive header if the volume changed */
1938 ChangedVolume = TRUE;
1939
1940 if (!stFlags.bBareFormat &&
1941 GetVolumePathName(params[loop], szFullPath, ARRAYSIZE(szFullPath)))
1942 {
1943 if (!_tcscmp(szFullPath, prev_volume))
1944 ChangedVolume = FALSE;
1945 else
1946 _tcscpy(prev_volume, szFullPath);
1947 }
1948
1949 /* Resolve the pattern */
1950 ResolvePattern(params[loop], ARRAYSIZE(szFullPath), szFullPath, &pszFilePart);
1951
1952 /* Print the header */
1953 cPathSep = pszFilePart[-1];
1954 pszFilePart[-1] = _T('\0'); /* Truncate to directory name only */
1955 if (ChangedVolume && !stFlags.bBareFormat &&
1956 !PrintDirectoryHeader(szFullPath, &stFlags))
1957 {
1958 nErrorLevel = 1;
1959 goto cleanup;
1960 }
1961 pszFilePart[-1] = cPathSep;
1962
1963 /* Perform the actual directory listing */
1964 if (DirList(szFullPath, pszFilePart, &stFlags) != 0)
1965 {
1966 nErrorLevel = 1;
1967 goto cleanup;
1968 }
1969
1970 /* Print the footer */
1971 pszFilePart[-1] = _T('\0'); /* Truncate to directory name only */
1972 PrintSummary(szFullPath,
1976 &stFlags,
1977 TRUE);
1978 }
1979
1980 ret = 0;
1981
1982cleanup:
1983 freep(params);
1984
1985 return ret;
1986}
1987
1988#endif
1989
1990/* EOF */
WCHAR First[]
Definition: FormatMessage.c:11
char * va_list
Definition: acmsvcex.h:78
#define va_end(ap)
Definition: acmsvcex.h:90
#define va_start(ap, A)
Definition: acmsvcex.h:91
#define RC_STRING_MAX_SIZE
Definition: resource.h:3
#define StdOut
Definition: fc.c:14
INT nErrorLevel
Definition: cmd.c:158
HANDLE CMD_ModuleHandle
Definition: cmd.c:165
INT ConvertULargeInteger(ULONGLONG num, LPTSTR des, UINT len, BOOL bPutSeparator)
Definition: cmd.c:189
BOOL IsExistingDirectory(IN LPCTSTR pszPath)
Definition: misc.c:504
TCHAR cDateSeparator
Definition: locale.c:16
INT nTimeFormat
Definition: locale.c:21
INT nDateFormat
Definition: locale.c:20
VOID error_parameter_format(TCHAR ch)
Definition: error.c:65
VOID error_file_not_found(VOID)
Definition: error.c:93
TCHAR cTimeSeparator
Definition: locale.c:17
BOOL CheckCtrlBreak(INT)
Definition: misc.c:132
#define BREAK_INPUT
Definition: cmd.h:36
VOID error_invalid_switch(TCHAR ch)
Definition: error.c:72
BOOL __cdecl ConOutPrintfPaging(BOOL StartPaging, LPTSTR szFormat,...)
Definition: console.c:171
VOID ConOutResPaging(BOOL StartPaging, UINT resID)
Definition: console.c:182
CON_PAGER StdOutPager
Definition: console.c:30
VOID GetScreenSize(PSHORT maxx, PSHORT maxy)
Definition: console.c:236
BOOL ConPrintfVPaging(PCON_PAGER Pager, BOOL StartPaging, LPTSTR szFormat, va_list arg_ptr)
Definition: console.c:155
static VOID DirHelp(VOID)
Definition: dir.c:231
struct _DirSwitchesFlags DIRSWITCHFLAGS
BOOL(WINAPI * PGETFREEDISKSPACEEX)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER)
Definition: dir.c:216
static INT PrintSummary(LPCTSTR szPath, ULONG ulFiles, ULONG ulDirs, ULONGLONG u64Bytes, LPDIRSWITCHFLAGS lpFlags, BOOL TotalSummary)
Definition: dir.c:770
INT FormatTime(TCHAR *lpTime, LPSYSTEMTIME dt)
Definition: dir.c:703
EOrderBy
Definition: dir.c:157
@ ORDER_DIRECTORY
Definition: dir.c:160
@ ORDER_EXTENSION
Definition: dir.c:161
@ ORDER_TIME
Definition: dir.c:162
@ ORDER_SIZE
Definition: dir.c:159
@ ORDER_NAME
Definition: dir.c:158
static VOID DirPrintNewList(PDIRFINDINFO ptrFiles[], DWORD dwCount, LPCTSTR szCurPath, LPDIRSWITCHFLAGS lpFlags)
Definition: dir.c:873
static ULONG recurse_dir_cnt
Definition: dir.c:220
static VOID DirPrintOldList(PDIRFINDINFO ptrFiles[], DWORD dwCount, LPCTSTR szCurPath, LPDIRSWITCHFLAGS lpFlags)
Definition: dir.c:1037
static VOID DirPrintWideList(PDIRFINDINFO ptrFiles[], DWORD dwCount, LPCTSTR szCurPath, LPDIRSWITCHFLAGS lpFlags)
Definition: dir.c:955
struct _DIRFINDLISTNODE * PDIRFINDLISTNODE
ETimeField
Definition: dir.c:149
@ TF_CREATIONDATE
Definition: dir.c:150
@ TF_MODIFIEDDATE
Definition: dir.c:151
@ TF_LASTACCESSEDDATE
Definition: dir.c:152
static LPTSTR getName(const TCHAR *file, TCHAR *dest)
Definition: dir.c:842
INT CommandDir(LPTSTR rest)
Definition: dir.c:1835
static INT DirList(IN OUT LPTSTR szFullPath, IN LPTSTR pszFilePart, LPDIRSWITCHFLAGS lpFlags)
Definition: dir.c:1357
static ULONGLONG recurse_bytes
Definition: dir.c:222
struct _DIRFINDSTREAMNODE * PDIRFINDSTREAMNODE
static BOOL DirReadParam(LPTSTR Line, LPTSTR **params, LPINT entries, LPDIRSWITCHFLAGS lpFlags)
Definition: dir.c:264
static VOID DirNodeCleanup(PDIRFINDLISTNODE ptrStartNode, PDWORD pdwCount)
Definition: dir.c:1331
FORCEINLINE BOOL IsDotDirectory(IN LPCTSTR pszPath)
Definition: dir.c:239
struct _DIRFINDINFO * PDIRFINDINFO
struct _DirSwitchesFlags * LPDIRSWITCHFLAGS
INT FormatDate(TCHAR *lpDate, LPSYSTEMTIME dt, BOOL b4Digit)
Definition: dir.c:674
static VOID ResolvePattern(IN LPTSTR pszPattern, IN DWORD nBufferLength, OUT LPTSTR pszFullPath, OUT LPTSTR *ppszPatternPart OPTIONAL)
Definition: dir.c:1610
FORCEINLINE BOOL IsDotDirectoryN(IN const TCHAR *pPath, IN SIZE_T Length)
Definition: dir.c:250
static BOOL CompareFiles(PDIRFINDINFO lpFile1, PDIRFINDINFO lpFile2, LPDIRSWITCHFLAGS lpFlags)
Definition: dir.c:1192
static BOOL PrintDirectoryHeader(LPCTSTR szPath, LPDIRSWITCHFLAGS lpFlags)
Definition: dir.c:597
struct _DIRFINDLISTNODE DIRFINDLISTNODE
static VOID QsortFiles(PDIRFINDINFO ptrArray[], int i, int j, LPDIRSWITCHFLAGS lpFlags)
Definition: dir.c:1293
static VOID DirPrintFileDateTime(TCHAR *lpDate, TCHAR *lpTime, LPWIN32_FIND_DATA lpFile, LPDIRSWITCHFLAGS lpFlags)
Definition: dir.c:639
static VOID DirPrintFiles(PDIRFINDINFO ptrFiles[], DWORD dwCount, LPCTSTR szCurPath, LPDIRSWITCHFLAGS lpFlags)
Definition: dir.c:1136
TCHAR * getExt(const TCHAR *file)
Definition: dir.c:829
static VOID GetUserDiskFreeSpace(LPCTSTR lpRoot, PULARGE_INTEGER lpFreeSpace)
Definition: dir.c:725
struct _DIRFINDSTREAMNODE DIRFINDSTREAMNODE
static ULONG recurse_file_cnt
Definition: dir.c:221
static BOOL DirPrintf(LPDIRSWITCHFLAGS lpFlags, LPTSTR szFormat,...)
Definition: dir.c:577
static VOID DirPrintBareList(PDIRFINDINFO ptrFiles[], DWORD dwCount, LPCTSTR szCurPath, LPDIRSWITCHFLAGS lpFlags)
Definition: dir.c:1102
struct _DIRFINDINFO DIRFINDINFO
#define debugstr_aw
Definition: precomp.h:43
#define STRING_DIR_HELP8
Definition: resource.h:113
#define STRING_DIR_HELP4
Definition: resource.h:109
#define STRING_DIR_HELP6
Definition: resource.h:111
#define STRING_DIR_HELP5
Definition: resource.h:110
#define STRING_DIR_HELP3
Definition: resource.h:108
#define STRING_DIR_HELP1
Definition: resource.h:106
#define STRING_DIR_HELP7
Definition: resource.h:112
#define STRING_DIR_HELP2
Definition: resource.h:107
#define WARN(fmt,...)
Definition: debug.h:112
#define ERR(fmt,...)
Definition: debug.h:110
HINSTANCE hInstance
Definition: charmap.c:19
static VOID StripQuotes(LPSTR in)
Definition: cmdcons.c:116
static VOID freep(LPSTR *p)
Definition: cmdcons.c:98
BOOL add_entry(LPINT ac, LPSTR **arg, LPCSTR entry)
Definition: cmdcons.c:132
#define cmd_free(ptr)
Definition: cmddbg.h:31
#define cmd_alloc(size)
Definition: cmddbg.h:29
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define ARRAYSIZE(array)
Definition: filtermapper.c:47
#define GetProcAddress(x, y)
Definition: compat.h:753
#define INVALID_HANDLE_VALUE
Definition: compat.h:731
#define MAX_PATH
Definition: compat.h:34
static void cleanup(void)
Definition: main.c:1335
BOOL WINAPI FindClose(HANDLE hFindFile)
Definition: find.c:502
BOOL WINAPI FileTimeToSystemTime(IN CONST FILETIME *lpFileTime, OUT LPSYSTEMTIME lpSystemTime)
Definition: time.c:188
BOOL WINAPI FileTimeToLocalFileTime(IN CONST FILETIME *lpFileTime, OUT LPFILETIME lpLocalFileTime)
Definition: time.c:221
unsigned int BOOL
Definition: ntddk_ex.h:94
unsigned long DWORD
Definition: ntddk_ex.h:95
unsigned short WORD
Definition: ntddk_ex.h:93
GLuint GLuint end
Definition: gl.h:1545
GLenum const GLfloat * params
Definition: glext.h:5645
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint GLint GLint j
Definition: glfuncs.h:250
#define _istspace
Definition: tchar.h:1504
#define _tcscmp
Definition: tchar.h:1424
#define _tcscat
Definition: tchar.h:622
#define _tcscpy
Definition: tchar.h:623
#define _tcsncpy
Definition: tchar.h:1410
#define _tcslwr
Definition: tchar.h:1465
#define _istalpha
Definition: tchar.h:1492
#define _totupper
Definition: tchar.h:1509
#define _tcschr
Definition: tchar.h:1406
#define memcpy(s1, s2, n)
Definition: mkisofs.h:878
#define memmove(s1, s2, n)
Definition: mkisofs.h:881
#define ASSERT(a)
Definition: mode.c:44
#define _stprintf
Definition: utility.h:124
#define _tcsrchr
Definition: utility.h:116
LPCWSTR szPath
Definition: env.c:37
static PVOID ptr
Definition: dispmode.c:27
static char * dest
Definition: rtl.c:135
struct _ULARGE_INTEGER * PULARGE_INTEGER
Definition: drive.c:28
unsigned __int3264 UINT_PTR
Definition: mstsclib_h.h:274
unsigned int UINT
Definition: ndis.h:50
#define BOOL
Definition: nt_native.h:43
#define FILE_ATTRIBUTE_READONLY
Definition: nt_native.h:702
#define FILE_ATTRIBUTE_HIDDEN
Definition: nt_native.h:703
#define FILE_ATTRIBUTE_SYSTEM
Definition: nt_native.h:704
#define FILE_ATTRIBUTE_ARCHIVE
Definition: nt_native.h:706
#define FILE_ATTRIBUTE_DIRECTORY
Definition: nt_native.h:705
#define DWORD
Definition: nt_native.h:44
#define LPVOID
Definition: nt_native.h:45
_In_ ULONG _In_ ULONG _In_ ULONG Length
Definition: ntddpcm.h:102
#define FILE_ATTRIBUTE_REPARSE_POINT
Definition: ntifs_ex.h:381
#define L(x)
Definition: ntvdm.h:50
INT ConPrintfV(IN PCON_STREAM Stream, IN PCWSTR szStr, IN va_list args)
Definition: outstream.c:466
#define LOWORD(l)
Definition: pedump.c:82
DWORD * PDWORD
Definition: pedump.c:68
short SHORT
Definition: pedump.c:59
unsigned short USHORT
Definition: pedump.c:61
static const WCHAR szName[]
Definition: powrprof.c:45
static calc_node_t temp
Definition: rpn_ieee.c:38
#define TRACE(s)
Definition: solgame.cpp:4
TCHAR szTime[64]
Definition: solitaire.cpp:19
PULONG MinorVersion OPTIONAL
Definition: CrossNt.h:68
Definition: ncftp.h:79
WIN32_FIND_DATA stFindInfo
Definition: dir.c:205
PDIRFINDSTREAMNODE ptrHead
Definition: dir.c:206
DIRFINDINFO stInfo
Definition: dir.c:211
struct _DIRFINDLISTNODE * ptrNext
Definition: dir.c:212
struct _DIRFINDSTREAMNODE * ptrNext
Definition: dir.c:200
WIN32_FIND_STREAM_DATA stStreamInfo
Definition: dir.c:199
BOOL bDataStreams
Definition: dir.c:179
enum ETimeField eTimeField
Definition: dir.c:193
DWORD dwAttribMask
Definition: dir.c:183
BOOL bRecursive
Definition: dir.c:176
BOOL bNewLongList
Definition: dir.c:173
enum EOrderBy eCriteria[3]
Definition: dir.c:187
struct _DirSwitchesFlags::@62 stOrderBy
BOOL bCriteriaRev[3]
Definition: dir.c:188
BOOL bWideListColSort
Definition: dir.c:171
BOOL bBareFormat
Definition: dir.c:168
BOOL bWideList
Definition: dir.c:170
BOOL b4Digit
Definition: dir.c:178
BOOL bLowerCase
Definition: dir.c:172
struct _DirSwitchesFlags::@63 stTimeField
struct _DirSwitchesFlags::@61 stAttribs
BOOL bShortName
Definition: dir.c:177
BOOL bPause
Definition: dir.c:174
DWORD dwAttribVal
Definition: dir.c:182
BOOL bUser
Definition: dir.c:175
short sCriteriaCount
Definition: dir.c:189
BOOL bTSeparator
Definition: dir.c:169
WORD wYear
Definition: winbase.h:905
WORD wMonth
Definition: winbase.h:906
WORD wHour
Definition: winbase.h:909
WORD wMinute
Definition: winbase.h:910
WORD wDay
Definition: winbase.h:908
$ULONG LowPart
Definition: ntbasedef.h:569
ULONGLONG QuadPart
Definition: ms-dtyp.idl:185
$ULONG HighPart
Definition: ntbasedef.h:570
Definition: fci.c:127
#define _UNICODE
Definition: textw.c:5
int32_t INT_PTR
Definition: typedefs.h:64
void * PVOID
Definition: typedefs.h:50
PVOID HANDLE
Definition: typedefs.h:73
ULONG_PTR SIZE_T
Definition: typedefs.h:80
int32_t INT
Definition: typedefs.h:58
#define IN
Definition: typedefs.h:39
uint32_t ULONG
Definition: typedefs.h:59
uint64_t ULONGLONG
Definition: typedefs.h:67
#define HIWORD(l)
Definition: typedefs.h:247
#define OUT
Definition: typedefs.h:40
#define _T(x)
Definition: vfdio.h:22
int ret
#define FORCEINLINE
Definition: wdftypes.h:67
#define GetEnvironmentVariable
Definition: winbase.h:3685
#define GetVolumeInformation
Definition: winbase.h:3724
#define GetModuleHandle
Definition: winbase.h:3698
#define GetVolumePathName
Definition: winbase.h:3726
#define FindNextFile
Definition: winbase.h:3659
#define GetDiskFreeSpace
Definition: winbase.h:3678
#define FindFirstFile
Definition: winbase.h:3653
_In_ LPCSTR _In_opt_ LPCSTR _In_ DWORD nBufferLength
Definition: winbase.h:3060
#define GetFullPathName
Definition: winbase.h:3692
int * LPINT
Definition: windef.h:178
#define WINAPI
Definition: msvc.h:6
#define LoadString
Definition: winuser.h:5809
char TCHAR
Definition: xmlstorage.h:189
const CHAR * LPCTSTR
Definition: xmlstorage.h:193
CHAR * LPTSTR
Definition: xmlstorage.h:192
const WCHAR * LPCWSTR
Definition: xmlstorage.h:185
#define _tcslen
Definition: xmlstorage.h:198
#define _tcsicmp
Definition: xmlstorage.h:205