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