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