2 * DIR.C - dir internal command.
7 * 01/29/97 (Tim Norman)
10 * 06/13/97 (Tim Norman)
13 * 07/12/97 (Tim Norman)
14 * Fixed bug that caused the root directory to be unlistable
16 * 07/12/97 (Marc Desrochers)
17 * Changed to use maxx, maxy instead of findxy()
20 * Added compatibility for /w in dir
23 * Compatibility for dir/s started
24 * Tested that program finds directories off root fine
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.
33 * Found problem that caused COM not to work
37 * added free mem routine
40 * debugged the free mem routine
41 * debugged whole thing some more
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
50 * Cleaned up code a bit, added comments
53 * Added error checking to my previously added routines
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
69 * Added check for /-(switch) to turn off previously defined
71 * Added ability to check for DIRCMD in environment and
76 * Now can dir *.ext/X, no spaces!
79 * error message now found in command.h
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.
87 * Changed error messages
89 * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
90 * added config.h include
93 * 04-Dec-1998 (Eric Kohl)
94 * Converted source code to Win32, except recursive dir ("dir /s").
96 * 10-Dec-1998 (Eric Kohl)
97 * Fixed recursive dir ("dir /s").
99 * 14-Dec-1998 (Eric Kohl)
100 * Converted to Win32 directory functions and
101 * fixed some output bugs. There are still some more ;)
103 * 10-Jan-1999 (Eric Kohl)
104 * Added "/N" and "/4" options, "/O" is a dummy.
105 * Added locale support.
107 * 20-Jan-1999 (Eric Kohl)
110 * 01-Mar-1999 (Eric Kohl)
111 * Replaced all runtime io functions by their Win32 counterparts.
113 * 23-Feb-2001 (Carl Nettelblad <cnettel@hem.passagen.se>)
114 * dir /s now works in deeper trees
116 * 28-Jan-2004 (Michael Fritscher <michael@fritscher.net>)
117 * Fix for /p, so it is working under Windows in GUI-mode, too.
119 * 30-Apr-2004 (Filip Navara <xnavara@volny.cz>)
120 * Fix /w to print long names.
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
128 * 1-Jul-2004 (Brandon Turner <turnerb7@msu.edu>)
129 * Added /p back in using ConOutPrintfPaging
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
136 * 25-Aug-2015 (Pierre Schweitzer)
137 * Implemented /R switch
142 #ifdef INCLUDE_CMD_DIR
144 /* Time Field enumeration */
149 TF_LASTACCESSEDDATE
= 2
152 /* Ordered by enumeration */
162 /* The struct for holding the switches */
163 typedef struct _DirSwitchesFlags
165 BOOL bBareFormat
; /* Bare Format */
166 BOOL bTSeparator
; /* Thousands separator */
167 BOOL bWideList
; /* Wide list format */
168 BOOL bWideListColSort
; /* Wide list format but sorted by column */
169 BOOL bLowerCase
; /* Uses lower case */
170 BOOL bNewLongList
; /* New long list */
171 BOOL bPause
; /* Pause per page */
172 BOOL bUser
; /* Displays the owner of file */
173 BOOL bRecursive
; /* Displays files in specified directory and all sub */
174 BOOL bShortName
; /* Displays the sort name of files if exist */
175 BOOL b4Digit
; /* Four digit year */
176 BOOL bDataStreams
; /* Displays alternate data streams */
179 DWORD dwAttribVal
; /* The desired state of attribute */
180 DWORD dwAttribMask
; /* Which attributes to check */
181 } stAttribs
; /* Displays files with this attributes only */
184 enum EOrderBy eCriteria
[3]; /* Criterias used to order by */
185 BOOL bCriteriaRev
[3]; /* If the criteria is in reversed order */
186 short sCriteriaCount
; /* The quantity of criterias */
187 } stOrderBy
; /* Ordered by criterias */
190 enum ETimeField eTimeField
; /* The time field that will be used for */
191 } stTimeField
; /* The time field to display or use for sorting */
192 } DIRSWITCHFLAGS
, *LPDIRSWITCHFLAGS
;
194 typedef struct _DIRFINDSTREAMNODE
196 WIN32_FIND_STREAM_DATA stStreamInfo
;
197 struct _DIRFINDSTREAMNODE
*ptrNext
;
198 } DIRFINDSTREAMNODE
, *PDIRFINDSTREAMNODE
;
200 typedef struct _DIRFINDINFO
202 WIN32_FIND_DATA stFindInfo
;
203 PDIRFINDSTREAMNODE ptrHead
;
204 } DIRFINDINFO
, *PDIRFINDINFO
;
206 typedef struct _DIRFINDLISTNODE
209 struct _DIRFINDLISTNODE
*ptrNext
;
210 } DIRFINDLISTNODE
, *PDIRFINDLISTNODE
;
213 (WINAPI
*PGETFREEDISKSPACEEX
)(LPCTSTR
, PULARGE_INTEGER
, PULARGE_INTEGER
, PULARGE_INTEGER
);
215 /* Globally save the # of dirs, files and bytes,
216 * probably later pass them to functions. Rob Lake */
217 static ULONG recurse_dir_cnt
;
218 static ULONG recurse_file_cnt
;
219 static ULONGLONG recurse_bytes
;
224 * displays help screen for dir
230 ConOutResPaging(TRUE
, STRING_DIR_HELP1
);
236 * Parse the parameters and switches of the command line and exports them
239 DirReadParam(LPTSTR Line
, /* [IN] The line with the parameters & switches */
240 LPTSTR
** params
, /* [OUT] The parameters after parsing */
241 LPINT entries
, /* [OUT] The number of parameters after parsing */
242 LPDIRSWITCHFLAGS lpFlags
) /* [IN/OUT] The flags after calculating switches */
244 TCHAR cCurSwitch
; /* The current switch */
245 TCHAR cCurChar
; /* Current examined character */
246 TCHAR cCurUChar
; /* Current upper examined character */
247 BOOL bNegative
; /* Negative switch */
248 BOOL bPNegative
; /* Negative switch parameter */
249 BOOL bIntoQuotes
; /* A flag showing if we are in quotes (") */
250 LPTSTR ptrStart
; /* A pointer to the first character of a parameter */
251 LPTSTR ptrEnd
; /* A pointer to the last character of a parameter */
252 BOOL bOrderByNoPar
; /* A flag to indicate /O with no switch parameter */
255 /* Initialize parameter array */
259 /* Initialize variables; */
260 cCurSwitch
= _T(' ');
264 /* We suppose that switch parameters
265 were given to avoid setting them to default
266 if the switch was not given */
267 bOrderByNoPar
= FALSE
;
269 /* Main Loop (see README_DIR.txt) */
270 /* scan the command line char per char, and we process its char */
273 /* we save current character as it is and its upper case */
275 cCurUChar
= _totupper(*Line
);
277 /* 1st section (see README_DIR.txt) */
278 /* When a switch is expecting */
279 if (cCurSwitch
== _T('/'))
281 while (_istspace(*Line
))
284 bNegative
= (*Line
== _T('-'));
288 cCurUChar
= _totupper(*Line
);
290 if ((cCurUChar
== _T('A')) ||(cCurUChar
== _T('T')) || (cCurUChar
== _T('O')))
292 /* If positive, prepare for parameters... if negative, reset to defaults */
296 lpFlags
->stAttribs
.dwAttribVal
= 0L;
297 lpFlags
->stAttribs
.dwAttribMask
= 0L;
299 lpFlags
->stAttribs
.dwAttribMask
= FILE_ATTRIBUTE_HIDDEN
| FILE_ATTRIBUTE_SYSTEM
;
303 lpFlags
->stTimeField
.eTimeField
= TF_MODIFIEDDATE
;
306 bOrderByNoPar
= !bNegative
;
307 lpFlags
->stOrderBy
.sCriteriaCount
= 0;
313 /* Positive switch, so it can take parameters. */
314 cCurSwitch
= cCurUChar
;
316 /* Skip optional leading colon */
317 if (*Line
== _T(':'))
322 else if (cCurUChar
== _T('L'))
323 lpFlags
->bLowerCase
= ! bNegative
;
324 else if (cCurUChar
== _T('B'))
325 lpFlags
->bBareFormat
= ! bNegative
;
326 else if (cCurUChar
== _T('C'))
327 lpFlags
->bTSeparator
= ! bNegative
;
328 else if (cCurUChar
== _T('W'))
329 lpFlags
->bWideList
= ! bNegative
;
330 else if (cCurUChar
== _T('D'))
331 lpFlags
->bWideListColSort
= ! bNegative
;
332 else if (cCurUChar
== _T('N'))
333 lpFlags
->bNewLongList
= ! bNegative
;
334 else if (cCurUChar
== _T('P'))
335 lpFlags
->bPause
= ! bNegative
;
336 else if (cCurUChar
== _T('Q'))
337 lpFlags
->bUser
= ! bNegative
;
338 else if (cCurUChar
== _T('S'))
339 lpFlags
->bRecursive
= ! bNegative
;
340 else if (cCurUChar
== _T('X'))
341 lpFlags
->bShortName
= ! bNegative
;
342 else if (cCurUChar
== _T('R'))
343 lpFlags
->bDataStreams
= ! bNegative
;
344 else if (cCurChar
== _T('4'))
345 lpFlags
->b4Digit
= ! bNegative
;
346 else if (cCurChar
== _T('?'))
353 error_invalid_switch ((TCHAR
)_totupper(*Line
));
357 /* Make sure there's no extra characters at the end of the switch */
358 if (Line
[1] && Line
[1] != _T('/') && !_istspace(Line
[1]))
360 error_parameter_format(Line
[1]);
364 cCurSwitch
= _T(' ');
366 else if (cCurSwitch
== _T(' '))
368 /* 2nd section (see README_DIR.txt) */
369 /* We are expecting parameter or the unknown */
371 if (cCurChar
== _T('/'))
372 cCurSwitch
= _T('/');
373 else if (_istspace(cCurChar
))
377 /* This is a file/directory name parameter. Find its end */
382 if (!bIntoQuotes
&& (*Line
== _T('/') || _istspace(*Line
)))
384 bIntoQuotes
^= (*Line
== _T('"'));
389 /* Copy it to the entries list */
390 temp
= cmd_alloc((ptrEnd
- ptrStart
+ 1) * sizeof(TCHAR
));
393 memcpy(temp
, ptrStart
, (ptrEnd
- ptrStart
) * sizeof(TCHAR
));
394 temp
[ptrEnd
- ptrStart
] = _T('\0');
396 if (!add_entry(entries
, params
, temp
))
409 /* 3rd section (see README_DIR.txt) */
410 /* We are waiting for switch parameters */
412 /* Check if there are no more switch parameters */
413 if ((cCurChar
== _T('/')) || _istspace(cCurChar
))
415 /* Wrong decision path, reprocess current character */
416 cCurSwitch
= _T(' ');
419 /* Process parameter switch */
422 case _T('A'): /* Switch parameters for /A (attributes filter) */
423 if (cCurChar
== _T('-'))
425 else if (cCurUChar
== _T('D'))
427 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_DIRECTORY
;
429 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_DIRECTORY
;
431 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_DIRECTORY
;
433 else if (cCurUChar
== _T('R'))
435 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_READONLY
;
437 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_READONLY
;
439 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_READONLY
;
441 else if (cCurUChar
== _T('H'))
443 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_HIDDEN
;
445 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_HIDDEN
;
447 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_HIDDEN
;
449 else if (cCurUChar
== _T('A'))
451 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_ARCHIVE
;
453 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_ARCHIVE
;
455 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_ARCHIVE
;
457 else if (cCurUChar
== _T('S'))
459 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_SYSTEM
;
461 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_SYSTEM
;
463 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_SYSTEM
;
467 error_parameter_format((TCHAR
)_totupper (*Line
));
471 case _T('T'): /* Switch parameters for /T (time field) */
472 if (cCurUChar
== _T('C'))
473 lpFlags
->stTimeField
.eTimeField
= TF_CREATIONDATE
;
474 else if (cCurUChar
== _T('A'))
475 lpFlags
->stTimeField
.eTimeField
= TF_LASTACCESSEDDATE
;
476 else if (cCurUChar
== _T('W'))
477 lpFlags
->stTimeField
.eTimeField
= TF_MODIFIEDDATE
;
480 error_parameter_format((TCHAR
)_totupper (*Line
));
484 case _T('O'): /* Switch parameters for /O (order) */
485 /* Ok a switch parameter was given */
486 bOrderByNoPar
= FALSE
;
488 if (cCurChar
== _T('-'))
490 else if (cCurUChar
== _T('N'))
492 if (lpFlags
->stOrderBy
.sCriteriaCount
< 3) lpFlags
->stOrderBy
.sCriteriaCount
++;
493 lpFlags
->stOrderBy
.bCriteriaRev
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = bPNegative
;
494 lpFlags
->stOrderBy
.eCriteria
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = ORDER_NAME
;
496 else if (cCurUChar
== _T('S'))
498 if (lpFlags
->stOrderBy
.sCriteriaCount
< 3) lpFlags
->stOrderBy
.sCriteriaCount
++;
499 lpFlags
->stOrderBy
.bCriteriaRev
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = bPNegative
;
500 lpFlags
->stOrderBy
.eCriteria
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = ORDER_SIZE
;
502 else if (cCurUChar
== _T('G'))
504 if (lpFlags
->stOrderBy
.sCriteriaCount
< 3) lpFlags
->stOrderBy
.sCriteriaCount
++;
505 lpFlags
->stOrderBy
.bCriteriaRev
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = bPNegative
;
506 lpFlags
->stOrderBy
.eCriteria
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = ORDER_DIRECTORY
;
508 else if (cCurUChar
== _T('E'))
510 if (lpFlags
->stOrderBy
.sCriteriaCount
< 3) lpFlags
->stOrderBy
.sCriteriaCount
++;
511 lpFlags
->stOrderBy
.bCriteriaRev
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = bPNegative
;
512 lpFlags
->stOrderBy
.eCriteria
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = ORDER_EXTENSION
;
514 else if (cCurUChar
== _T('D'))
516 if (lpFlags
->stOrderBy
.sCriteriaCount
< 3) lpFlags
->stOrderBy
.sCriteriaCount
++;
517 lpFlags
->stOrderBy
.bCriteriaRev
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = bPNegative
;
518 lpFlags
->stOrderBy
.eCriteria
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = ORDER_TIME
;
523 error_parameter_format((TCHAR
)_totupper (*Line
));
529 /* We check if we calculated the negative value and release the flag */
530 if ((cCurChar
!= _T('-')) && bPNegative
)
537 /* /O with no switch parameters acts like /O:GN */
540 lpFlags
->stOrderBy
.sCriteriaCount
= 2;
541 lpFlags
->stOrderBy
.eCriteria
[0] = ORDER_DIRECTORY
;
542 lpFlags
->stOrderBy
.bCriteriaRev
[0] = FALSE
;
543 lpFlags
->stOrderBy
.eCriteria
[1] = ORDER_NAME
;
544 lpFlags
->stOrderBy
.bCriteriaRev
[1] = FALSE
;
550 /* Print either with or without paging, depending on /P switch */
552 DirPrintf(LPDIRSWITCHFLAGS lpFlags
, LPTSTR szFormat
, ...)
556 va_start(arg_ptr
, szFormat
);
558 Done
= ConPrintfVPaging(&StdOutPager
, FALSE
, szFormat
, arg_ptr
);
560 ConPrintfV(StdOut
, szFormat
, arg_ptr
);
567 * PrintDirectoryHeader
569 * print the header for the dir command
572 PrintDirectoryHeader(LPCTSTR szPath
, LPDIRSWITCHFLAGS lpFlags
)
574 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
575 TCHAR szFullDir
[MAX_PATH
];
576 TCHAR szRootName
[MAX_PATH
];
581 if (lpFlags
->bBareFormat
)
584 if (GetFullPathName(szPath
, ARRAYSIZE(szFullDir
), szFullDir
, &pszFilePart
) == 0)
586 ErrorMessage(GetLastError(), _T("Failed to build full directory path"));
590 if (pszFilePart
!= NULL
)
591 *pszFilePart
= _T('\0');
593 /* Get the media ID of the drive */
594 if (!GetVolumePathName(szFullDir
, szRootName
, ARRAYSIZE(szRootName
)) ||
595 !GetVolumeInformation(szRootName
, szVolName
, ARRAYSIZE(szVolName
),
596 &dwSerialNr
, NULL
, NULL
, NULL
, 0))
601 /* Print drive info */
602 if (szVolName
[0] != _T('\0'))
604 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP2
, szMsg
, ARRAYSIZE(szMsg
));
605 DirPrintf(lpFlags
, szMsg
, _totupper(szRootName
[0]), szVolName
);
609 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP3
, szMsg
, ARRAYSIZE(szMsg
));
610 DirPrintf(lpFlags
, szMsg
, _totupper(szRootName
[0]));
613 /* Print the volume serial number if the return was successful */
614 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP4
, szMsg
, ARRAYSIZE(szMsg
));
615 DirPrintf(lpFlags
, szMsg
, HIWORD(dwSerialNr
), LOWORD(dwSerialNr
));
622 DirPrintFileDateTime(TCHAR
*lpDate
,
624 LPWIN32_FIND_DATA lpFile
,
625 LPDIRSWITCHFLAGS lpFlags
)
630 /* Select the right time field */
631 switch (lpFlags
->stTimeField
.eTimeField
)
633 case TF_CREATIONDATE
:
634 if (!FileTimeToLocalFileTime(&lpFile
->ftCreationTime
, &ft
))
636 FileTimeToSystemTime(&ft
, &dt
);
639 case TF_LASTACCESSEDDATE
:
640 if (!FileTimeToLocalFileTime(&lpFile
->ftLastAccessTime
, &ft
))
642 FileTimeToSystemTime(&ft
, &dt
);
645 case TF_MODIFIEDDATE
:
646 if (!FileTimeToLocalFileTime(&lpFile
->ftLastWriteTime
, &ft
))
648 FileTimeToSystemTime(&ft
, &dt
);
652 FormatDate(lpDate
, &dt
, lpFlags
->b4Digit
);
653 FormatTime(lpTime
, &dt
);
657 FormatDate(TCHAR
*lpDate
, LPSYSTEMTIME dt
, BOOL b4Digit
)
660 WORD wYear
= b4Digit
? dt
->wYear
: dt
->wYear
%100;
665 return _stprintf(lpDate
, _T("%02d%c%02d%c%0*d"),
666 dt
->wMonth
, cDateSeparator
,
667 dt
->wDay
, cDateSeparator
,
672 return _stprintf(lpDate
, _T("%02d%c%02d%c%0*d"),
673 dt
->wDay
, cDateSeparator
, dt
->wMonth
,
674 cDateSeparator
, b4Digit
?4:2, wYear
);
678 return _stprintf(lpDate
, _T("%0*d%c%02d%c%02d"),
679 b4Digit
?4:2, wYear
, cDateSeparator
,
680 dt
->wMonth
, cDateSeparator
, dt
->wDay
);
686 FormatTime(TCHAR
*lpTime
, LPSYSTEMTIME dt
)
691 case 0: /* 12 hour format */
693 return _stprintf(lpTime
,_T("%02d%c%02u %cM"),
694 (dt
->wHour
== 0 ? 12 : (dt
->wHour
<= 12 ? dt
->wHour
: dt
->wHour
- 12)),
696 dt
->wMinute
, (dt
->wHour
<= 11 ? _T('A') : _T('P')));
699 case 1: /* 24 hour format */
700 return _stprintf(lpTime
, _T("%02d%c%02u"),
701 dt
->wHour
, cTimeSeparator
, dt
->wMinute
);
708 GetUserDiskFreeSpace(LPCTSTR lpRoot
,
709 PULARGE_INTEGER lpFreeSpace
)
711 PGETFREEDISKSPACEEX pGetFreeDiskSpaceEx
;
717 ULARGE_INTEGER TotalNumberOfBytes
, TotalNumberOfFreeBytes
;
719 lpFreeSpace
->QuadPart
= 0;
721 hInstance
= GetModuleHandle(_T("KERNEL32"));
722 if (hInstance
!= NULL
)
724 pGetFreeDiskSpaceEx
= (PGETFREEDISKSPACEEX
)GetProcAddress(hInstance
,
726 "GetDiskFreeSpaceExW");
728 "GetDiskFreeSpaceExA");
730 if (pGetFreeDiskSpaceEx
!= NULL
)
732 if (pGetFreeDiskSpaceEx(lpRoot
, lpFreeSpace
, &TotalNumberOfBytes
, &TotalNumberOfFreeBytes
) != FALSE
)
737 GetDiskFreeSpace(lpRoot
,
743 lpFreeSpace
->QuadPart
= dwSecPerCl
* dwBytPerSec
* dwFreeCl
;
748 * print_summary: prints dir summary
749 * Added by Rob Lake 06/17/98 to compact code
750 * Just copied Tim's Code and patched it a bit
753 PrintSummary(LPCTSTR szPath
,
757 LPDIRSWITCHFLAGS lpFlags
,
760 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
762 ULARGE_INTEGER uliFree
;
764 /* Here we check if we didn't find anything */
765 if (!(ulFiles
+ ulDirs
))
767 if (!lpFlags
->bRecursive
|| (TotalSummary
&& lpFlags
->bRecursive
))
768 error_file_not_found();
772 /* In bare format we don't print results */
773 if (lpFlags
->bBareFormat
)
776 /* Print recursive specific results */
778 /* Take this code offline to fix /S does not print double info */
779 if (TotalSummary
&& lpFlags
->bRecursive
)
781 ConvertULargeInteger(u64Bytes
, szBuffer
, ARRAYSIZE(szBuffer
), lpFlags
->bTSeparator
);
782 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP5
, szMsg
, ARRAYSIZE(szMsg
));
783 DirPrintf(lpFlags
, szMsg
, ulFiles
, szBuffer
);
787 /* Print File Summary */
788 /* Condition to print summary is:
789 If we are not in bare format and if we have results! */
790 ConvertULargeInteger(u64Bytes
, szBuffer
, ARRAYSIZE(szBuffer
), lpFlags
->bTSeparator
);
791 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP8
, szMsg
, ARRAYSIZE(szMsg
));
792 DirPrintf(lpFlags
, szMsg
, ulFiles
, szBuffer
);
795 /* Print total directories and free space */
796 if (!lpFlags
->bRecursive
|| TotalSummary
)
798 GetUserDiskFreeSpace(szPath
, &uliFree
);
799 ConvertULargeInteger(uliFree
.QuadPart
, szBuffer
, ARRAYSIZE(szBuffer
), lpFlags
->bTSeparator
);
800 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP6
, szMsg
, ARRAYSIZE(szMsg
));
801 DirPrintf(lpFlags
, szMsg
, ulDirs
, szBuffer
);
810 * Get the extension of a filename
812 TCHAR
* getExt(const TCHAR
* file
)
814 static TCHAR
*NoExt
= _T("");
815 TCHAR
* lastdot
= _tcsrchr(file
, _T('.'));
816 return (lastdot
!= NULL
? lastdot
+ 1 : NoExt
);
822 * Get the name of the file without extension
825 getName(const TCHAR
* file
, TCHAR
* dest
)
830 /* Check for "." and ".." folders */
831 if ((_tcscmp(file
, _T(".")) == 0) ||
832 (_tcscmp(file
, _T("..")) == 0))
838 end
= _tcsrchr(file
, _T('.'));
840 iLen
= _tcslen(file
);
844 _tcsncpy(dest
, file
, iLen
);
845 *(dest
+ iLen
) = _T('\0');
854 * The function that prints in new style
857 DirPrintNewList(PDIRFINDINFO ptrFiles
[], /* [IN]Files' Info */
858 DWORD dwCount
, /* [IN] The quantity of files */
859 LPCTSTR szCurPath
, /* [IN] Full path of current directory */
860 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
864 TCHAR szShortName
[15];
868 ULARGE_INTEGER u64FileSize
;
869 PDIRFINDSTREAMNODE ptrCurStream
;
871 for (i
= 0; i
< dwCount
&& !CheckCtrlBreak(BREAK_INPUT
); i
++)
874 if (ptrFiles
[i
]->stFindInfo
.dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
)
878 _tcscpy(szSize
, _T("<JUNCTION>"));
880 else if (ptrFiles
[i
]->stFindInfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
884 _tcscpy(szSize
, _T("<DIR>"));
890 u64FileSize
.HighPart
= ptrFiles
[i
]->stFindInfo
.nFileSizeHigh
;
891 u64FileSize
.LowPart
= ptrFiles
[i
]->stFindInfo
.nFileSizeLow
;
892 ConvertULargeInteger(u64FileSize
.QuadPart
, szSize
, 20, lpFlags
->bTSeparator
);
895 /* Calculate short name */
896 szShortName
[0] = _T('\0');
897 if (lpFlags
->bShortName
)
898 _stprintf(szShortName
, _T(" %-12s"), ptrFiles
[i
]->stFindInfo
.cAlternateFileName
);
900 /* Format date and time */
901 DirPrintFileDateTime(szDate
, szTime
, &ptrFiles
[i
]->stFindInfo
, lpFlags
);
904 DirPrintf(lpFlags
, _T("%10s %-6s %*s%s %s\n"),
910 ptrFiles
[i
]->stFindInfo
.cFileName
);
912 /* Now, loop on the streams */
913 ptrCurStream
= ptrFiles
[i
]->ptrHead
;
916 ConvertULargeInteger(ptrCurStream
->stStreamInfo
.StreamSize
.QuadPart
, szSize
, 20, lpFlags
->bTSeparator
);
919 DirPrintf(lpFlags
, _T("%10s %-6s %*s%s %s%s\n"),
925 ptrFiles
[i
]->stFindInfo
.cFileName
,
926 ptrCurStream
->stStreamInfo
.cStreamName
);
927 ptrCurStream
= ptrCurStream
->ptrNext
;
936 * The function that prints in wide list
939 DirPrintWideList(PDIRFINDINFO ptrFiles
[], /* [IN] Files' Info */
940 DWORD dwCount
, /* [IN] The quantity of files */
941 LPCTSTR szCurPath
, /* [IN] Full path of current directory */
942 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
947 UINT_PTR iLongestName
;
948 TCHAR szTempFname
[MAX_PATH
];
953 /* Calculate longest name */
955 for (i
= 0; i
< dwCount
; i
++)
957 if (ptrFiles
[i
]->stFindInfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
959 /* Directories need 2 additional characters for brackets */
960 if ((_tcslen(ptrFiles
[i
]->stFindInfo
.cFileName
) + 2) > iLongestName
)
961 iLongestName
= _tcslen(ptrFiles
[i
]->stFindInfo
.cFileName
) + 2;
965 if (_tcslen(ptrFiles
[i
]->stFindInfo
.cFileName
) > iLongestName
)
966 iLongestName
= _tcslen(ptrFiles
[i
]->stFindInfo
.cFileName
);
970 /* Count the highest number of columns */
971 GetScreenSize(&iScreenWidth
, NULL
);
972 iColumns
= (USHORT
)(iScreenWidth
/ iLongestName
);
974 /* Check if there is enough space for spaces between names */
975 if (((iLongestName
* iColumns
) + iColumns
) >= (UINT
)iScreenWidth
)
978 /* A last check at iColumns to avoid division by zero */
979 if (!iColumns
) iColumns
= 1;
981 /* Calculate the lines that will be printed */
982 iLines
= (USHORT
)((dwCount
+ iColumns
- 1) / iColumns
);
984 for (i
= 0; i
< iLines
&& !CheckCtrlBreak(BREAK_INPUT
); i
++)
986 for (j
= 0; j
< iColumns
; j
++)
988 if (lpFlags
->bWideListColSort
)
990 /* Print Column sorted */
991 temp
= (j
* iLines
) + i
;
995 /* Print Line sorted */
996 temp
= (i
* iColumns
) + j
;
999 if (temp
>= dwCount
) break;
1001 if (ptrFiles
[temp
]->stFindInfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1002 _stprintf(szTempFname
, _T("[%s]"), ptrFiles
[temp
]->stFindInfo
.cFileName
);
1004 _stprintf(szTempFname
, _T("%s"), ptrFiles
[temp
]->stFindInfo
.cFileName
);
1006 DirPrintf(lpFlags
, _T("%-*s"), iLongestName
+ 1, szTempFname
);
1009 /* Add a new line after the last item in the column */
1010 DirPrintf(lpFlags
, _T("\n"));
1018 * The function that prints in old style
1021 DirPrintOldList(PDIRFINDINFO ptrFiles
[], /* [IN] Files' Info */
1022 DWORD dwCount
, /* [IN] The quantity of files */
1023 LPCTSTR szCurPath
, /* [IN] Full path of current directory */
1024 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
1026 DWORD i
; /* An indexer for "for"s */
1027 TCHAR szName
[10]; /* The name of file */
1028 TCHAR szExt
[5]; /* The extension of file */
1029 TCHAR szDate
[30],szTime
[30]; /* Used to format time and date */
1030 TCHAR szSize
[30]; /* The size of file */
1031 int iSizeFormat
; /* The format of size field */
1032 ULARGE_INTEGER u64FileSize
; /* The file size */
1034 for (i
= 0; i
< dwCount
&& !CheckCtrlBreak(BREAK_INPUT
); i
++)
1036 /* Broke 8.3 format */
1037 if (*ptrFiles
[i
]->stFindInfo
.cAlternateFileName
)
1039 /* If the file is long named then we read the alter name */
1040 getName( ptrFiles
[i
]->stFindInfo
.cAlternateFileName
, szName
);
1041 _tcscpy(szExt
, getExt( ptrFiles
[i
]->stFindInfo
.cAlternateFileName
));
1045 /* If the file is not long name we read its original name */
1046 getName( ptrFiles
[i
]->stFindInfo
.cFileName
, szName
);
1047 _tcscpy(szExt
, getExt( ptrFiles
[i
]->stFindInfo
.cFileName
));
1050 /* Calculate size */
1051 if (ptrFiles
[i
]->stFindInfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1053 /* Directory, no size it's a directory */
1055 _tcscpy(szSize
, _T("<DIR>"));
1061 u64FileSize
.HighPart
= ptrFiles
[i
]->stFindInfo
.nFileSizeHigh
;
1062 u64FileSize
.LowPart
= ptrFiles
[i
]->stFindInfo
.nFileSizeLow
;
1063 ConvertULargeInteger(u64FileSize
.QuadPart
, szSize
, 20, lpFlags
->bTSeparator
);
1066 /* Format date and time */
1067 DirPrintFileDateTime(szDate
,szTime
,&ptrFiles
[i
]->stFindInfo
,lpFlags
);
1069 /* Print the line */
1070 DirPrintf(lpFlags
, _T("%-8s %-3s %*s %s %s\n"),
1071 szName
, /* The file's 8.3 name */
1072 szExt
, /* The file's 8.3 extension */
1073 iSizeFormat
, /* print format for size column */
1074 szSize
, /* The size of file or "<DIR>" for dirs */
1075 szDate
, /* The date of file/dir */
1076 szTime
); /* The time of file/dir */
1083 * The function that prints in bare format
1086 DirPrintBareList(PDIRFINDINFO ptrFiles
[], /* [IN] Files' Info */
1087 DWORD dwCount
, /* [IN] The number of files */
1088 LPCTSTR szCurPath
, /* [IN] Full path of current directory */
1089 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
1093 for (i
= 0; i
< dwCount
&& !CheckCtrlBreak(BREAK_INPUT
); i
++)
1095 if ((_tcscmp(ptrFiles
[i
]->stFindInfo
.cFileName
, _T(".")) == 0) ||
1096 (_tcscmp(ptrFiles
[i
]->stFindInfo
.cFileName
, _T("..")) == 0))
1098 /* at bare format we don't print "." and ".." folder */
1101 if (lpFlags
->bRecursive
)
1103 /* at recursive mode we print full path of file */
1104 DirPrintf(lpFlags
, _T("%s\\%s\n"), szCurPath
, ptrFiles
[i
]->stFindInfo
.cFileName
);
1108 /* if we are not in recursive mode we print the file names */
1109 DirPrintf(lpFlags
, _T("%s\n"), ptrFiles
[i
]->stFindInfo
.cFileName
);
1118 * The functions that prints the files list
1121 DirPrintFiles(PDIRFINDINFO ptrFiles
[], /* [IN] Files' Info */
1122 DWORD dwCount
, /* [IN] The quantity of files */
1123 LPCTSTR szCurPath
, /* [IN] Full path of current directory */
1124 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
1126 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
1127 TCHAR szTemp
[MAX_PATH
]; /* A buffer to format the directory header */
1129 /* Print trailing backslash for root directory of drive */
1130 _tcscpy(szTemp
, szCurPath
);
1131 if (_tcslen(szTemp
) == 2 && szTemp
[1] == _T(':'))
1132 _tcscat(szTemp
, _T("\\"));
1134 /* Condition to print header:
1135 We are not printing in bare format
1136 and if we are in recursive mode... we must have results */
1137 if (!lpFlags
->bBareFormat
&& !(lpFlags
->bRecursive
&& (dwCount
<= 0)))
1139 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP7
, szMsg
, ARRAYSIZE(szMsg
));
1140 if (!DirPrintf(lpFlags
, szMsg
, szTemp
))
1144 if (lpFlags
->bBareFormat
)
1147 DirPrintBareList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1149 else if (lpFlags
->bShortName
)
1151 /* New list style / Short names */
1152 DirPrintNewList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1154 else if (lpFlags
->bWideListColSort
|| lpFlags
->bWideList
)
1157 DirPrintWideList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1159 else if (lpFlags
->bNewLongList
)
1162 DirPrintNewList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1166 /* If nothing is selected old list is the default */
1167 DirPrintOldList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1174 * Compares 2 files based on the order criteria
1177 CompareFiles(PDIRFINDINFO lpFile1
, /* [IN] A pointer to WIN32_FIND_DATA of file 1 */
1178 PDIRFINDINFO lpFile2
, /* [IN] A pointer to WIN32_FIND_DATA of file 2 */
1179 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags that we use to list */
1181 ULARGE_INTEGER u64File1
;
1182 ULARGE_INTEGER u64File2
;
1184 long iComp
= 0; /* The comparison result */
1186 /* Calculate criteria by order given from user */
1187 for (i
= 0; i
< lpFlags
->stOrderBy
.sCriteriaCount
; i
++)
1190 /* Calculate criteria */
1191 switch (lpFlags
->stOrderBy
.eCriteria
[i
])
1193 case ORDER_SIZE
: /* Order by size /o:s */
1194 /* concat the 32bit integers to a 64bit */
1195 u64File1
.LowPart
= lpFile1
->stFindInfo
.nFileSizeLow
;
1196 u64File1
.HighPart
= lpFile1
->stFindInfo
.nFileSizeHigh
;
1197 u64File2
.LowPart
= lpFile2
->stFindInfo
.nFileSizeLow
;
1198 u64File2
.HighPart
= lpFile2
->stFindInfo
.nFileSizeHigh
;
1200 /* In case that difference is too big for a long */
1201 if (u64File1
.QuadPart
< u64File2
.QuadPart
)
1203 else if (u64File1
.QuadPart
> u64File2
.QuadPart
)
1209 case ORDER_DIRECTORY
: /* Order by directory attribute /o:g */
1210 iComp
= ((lpFile2
->stFindInfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)-
1211 (lpFile1
->stFindInfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
));
1214 case ORDER_EXTENSION
: /* Order by extension name /o:e */
1215 iComp
= _tcsicmp(getExt(lpFile1
->stFindInfo
.cFileName
),getExt(lpFile2
->stFindInfo
.cFileName
));
1218 case ORDER_NAME
: /* Order by filename /o:n */
1219 iComp
= _tcsicmp(lpFile1
->stFindInfo
.cFileName
, lpFile2
->stFindInfo
.cFileName
);
1222 case ORDER_TIME
: /* Order by file's time /o:t */
1223 /* We compare files based on the time field selected by /t */
1224 switch (lpFlags
->stTimeField
.eTimeField
)
1226 case TF_CREATIONDATE
:
1227 /* concat the 32bit integers to a 64bit */
1228 u64File1
.LowPart
= lpFile1
->stFindInfo
.ftCreationTime
.dwLowDateTime
;
1229 u64File1
.HighPart
= lpFile1
->stFindInfo
.ftCreationTime
.dwHighDateTime
;
1230 u64File2
.LowPart
= lpFile2
->stFindInfo
.ftCreationTime
.dwLowDateTime
;
1231 u64File2
.HighPart
= lpFile2
->stFindInfo
.ftCreationTime
.dwHighDateTime
;
1233 case TF_LASTACCESSEDDATE
:
1234 /* concat the 32bit integers to a 64bit */
1235 u64File1
.LowPart
= lpFile1
->stFindInfo
.ftLastAccessTime
.dwLowDateTime
;
1236 u64File1
.HighPart
= lpFile1
->stFindInfo
.ftLastAccessTime
.dwHighDateTime
;
1237 u64File2
.LowPart
= lpFile2
->stFindInfo
.ftLastAccessTime
.dwLowDateTime
;
1238 u64File2
.HighPart
= lpFile2
->stFindInfo
.ftLastAccessTime
.dwHighDateTime
;
1240 case TF_MODIFIEDDATE
:
1241 /* concat the 32bit integers to a 64bit */
1242 u64File1
.LowPart
= lpFile1
->stFindInfo
.ftLastWriteTime
.dwLowDateTime
;
1243 u64File1
.HighPart
= lpFile1
->stFindInfo
.ftLastWriteTime
.dwHighDateTime
;
1244 u64File2
.LowPart
= lpFile2
->stFindInfo
.ftLastWriteTime
.dwLowDateTime
;
1245 u64File2
.HighPart
= lpFile2
->stFindInfo
.ftLastWriteTime
.dwHighDateTime
;
1249 /* In case that difference is too big for a long */
1250 if (u64File1
.QuadPart
< u64File2
.QuadPart
)
1252 else if (u64File1
.QuadPart
> u64File2
.QuadPart
)
1259 /* Reverse if desired */
1260 if (lpFlags
->stOrderBy
.bCriteriaRev
[i
])
1263 /* If that criteria was enough for distinguishing
1264 the files/dirs,there is no need to calculate the others*/
1265 if (iComp
!= 0) break;
1268 /* Translate the value of iComp to boolean */
1275 * Sort files by the order criterias using quicksort method
1278 QsortFiles(PDIRFINDINFO ptrArray
[], /* [IN/OUT] The array with file info pointers */
1279 int i
, /* [IN] The index of first item in array */
1280 int j
, /* [IN] The index to last item in array */
1281 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags that we will use to sort */
1283 PDIRFINDINFO lpTemp
; /* A temporary pointer */
1288 int First
= i
, Last
= j
, Temp
;
1292 if (Way
== CompareFiles(ptrArray
[i
], ptrArray
[j
], lpFlags
))
1294 /* Swap the pointers of the array */
1295 lpTemp
= ptrArray
[i
];
1296 ptrArray
[i
]= ptrArray
[j
];
1297 ptrArray
[j
] = lpTemp
;
1299 /* Swap the indexes for inverting sorting */
1310 QsortFiles(ptrArray
,First
, i
-1, lpFlags
);
1311 QsortFiles(ptrArray
,i
+1,Last
, lpFlags
);
1316 DirNodeCleanup(PDIRFINDLISTNODE ptrStartNode
,
1319 PDIRFINDLISTNODE ptrNextNode
;
1320 PDIRFINDSTREAMNODE ptrFreeNode
;
1321 while (ptrStartNode
)
1323 ptrNextNode
= ptrStartNode
->ptrNext
;
1324 while (ptrStartNode
->stInfo
.ptrHead
)
1326 ptrFreeNode
= ptrStartNode
->stInfo
.ptrHead
;
1327 ptrStartNode
->stInfo
.ptrHead
= ptrFreeNode
->ptrNext
;
1328 cmd_free(ptrFreeNode
);
1330 cmd_free(ptrStartNode
);
1331 ptrStartNode
= ptrNextNode
;
1339 * The function that does everything except for printing results
1342 DirList(LPTSTR szPath
, /* [IN] The path that dir starts */
1343 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags of the listing */
1345 BOOL fPoint
; /* If szPath is a file with extension fPoint will be True */
1346 HANDLE hSearch
; /* The handle of the search */
1347 HANDLE hRecSearch
; /* The handle for searching recursively */
1348 HANDLE hStreams
; /* The handle for alternate streams */
1349 WIN32_FIND_DATA wfdFileInfo
; /* The info of file that found */
1350 PDIRFINDINFO
* ptrFileArray
; /* An array of pointers with all the files */
1351 PDIRFINDLISTNODE ptrStartNode
; /* The pointer to the first node */
1352 PDIRFINDLISTNODE ptrNextNode
; /* A pointer used for relatives references */
1353 TCHAR szFullPath
[MAX_PATH
]; /* The full path that we are listing with trailing '\' */
1354 TCHAR szSubPath
[MAX_PATH
];
1356 DWORD dwCount
; /* A counter of files found in directory */
1357 DWORD dwCountFiles
; /* Counter for files */
1358 DWORD dwCountDirs
; /* Counter for directories */
1359 ULONGLONG u64CountBytes
; /* Counter for bytes */
1360 ULARGE_INTEGER u64Temp
; /* A temporary counter */
1361 WIN32_FIND_STREAM_DATA wfsdStreamInfo
;
1362 PDIRFINDSTREAMNODE
* ptrCurNode
; /* The pointer to the first stream */
1363 static HANDLE (WINAPI
*pFindFirstStreamW
)(LPCWSTR
, STREAM_INFO_LEVELS
, LPVOID
, DWORD
);
1364 static BOOL (WINAPI
*pFindNextStreamW
)(HANDLE
, LPVOID
);
1366 /* Initialize variables */
1367 ptrStartNode
= NULL
;
1375 /* Create szFullPath */
1376 if (GetFullPathName(szPath
, ARRAYSIZE(szFullPath
), szFullPath
, &pszFilePart
) == 0)
1378 _tcscpy (szFullPath
, szPath
);
1382 /* If no wildcard or file was specified and this is a directory, then
1383 display all files in it */
1384 if (pszFilePart
== NULL
|| IsExistingDirectory(szFullPath
))
1386 pszFilePart
= &szFullPath
[_tcslen(szFullPath
)];
1387 if (pszFilePart
[-1] != _T('\\'))
1388 *pszFilePart
++ = _T('\\');
1389 _tcscpy(pszFilePart
, _T("*"));
1392 /* Prepare the linked list, first node is allocated */
1393 ptrStartNode
= cmd_alloc(sizeof(DIRFINDLISTNODE
));
1394 if (ptrStartNode
== NULL
)
1396 WARN("DEBUG: Cannot allocate memory for ptrStartNode!\n");
1397 return 1; /* Error cannot allocate memory for 1st object */
1399 ptrStartNode
->stInfo
.ptrHead
= NULL
;
1400 ptrNextNode
= ptrStartNode
;
1402 /* Checking if szPath is a File with/wout extension */
1403 if (szPath
[_tcslen(szPath
) - 1] == _T('.'))
1406 /* Collect the results for the current folder */
1407 hSearch
= FindFirstFile(szFullPath
, &wfdFileInfo
);
1408 if (hSearch
!= INVALID_HANDLE_VALUE
)
1412 /* If retrieved FileName has extension,and szPath doesnt have extension then JUMP the retrieved FileName */
1413 if (_tcschr(wfdFileInfo
.cFileName
,_T('.')) && (fPoint
!= FALSE
))
1416 /* Here we filter all the specified attributes */
1418 else if ((wfdFileInfo
.dwFileAttributes
& lpFlags
->stAttribs
.dwAttribMask
)
1419 == (lpFlags
->stAttribs
.dwAttribMask
& lpFlags
->stAttribs
.dwAttribVal
))
1421 ptrNextNode
->ptrNext
= cmd_alloc(sizeof(DIRFINDLISTNODE
));
1422 if (ptrNextNode
->ptrNext
== NULL
)
1424 WARN("DEBUG: Cannot allocate memory for ptrNextNode->ptrNext!\n");
1425 DirNodeCleanup(ptrStartNode
, &dwCount
);
1430 /* Copy the info of search at linked list */
1431 memcpy(&ptrNextNode
->ptrNext
->stInfo
.stFindInfo
,
1433 sizeof(WIN32_FIND_DATA
));
1435 /* If lower case is selected do it here */
1436 if (lpFlags
->bLowerCase
)
1438 _tcslwr(ptrNextNode
->ptrNext
->stInfo
.stFindInfo
.cAlternateFileName
);
1439 _tcslwr(ptrNextNode
->ptrNext
->stInfo
.stFindInfo
.cFileName
);
1442 /* No streams (yet?) */
1443 ptrNextNode
->ptrNext
->stInfo
.ptrHead
= NULL
;
1445 /* Alternate streams are only displayed with new long list */
1446 if (lpFlags
->bNewLongList
&& lpFlags
->bDataStreams
)
1448 if (!pFindFirstStreamW
)
1450 pFindFirstStreamW
= (PVOID
)GetProcAddress(GetModuleHandle(_T("kernel32")), "FindFirstStreamW");
1451 pFindNextStreamW
= (PVOID
)GetProcAddress(GetModuleHandle(_T("kernel32")), "FindNextStreamW");
1454 /* Try to get stream information */
1455 if (pFindFirstStreamW
&& pFindNextStreamW
)
1457 hStreams
= pFindFirstStreamW(wfdFileInfo
.cFileName
, FindStreamInfoStandard
, &wfsdStreamInfo
, 0);
1461 hStreams
= INVALID_HANDLE_VALUE
;
1462 ERR("FindFirstStreamW not supported!\n");
1465 if (hStreams
!= INVALID_HANDLE_VALUE
)
1467 /* We totally ignore first stream. It contains data about ::$DATA */
1468 ptrCurNode
= &ptrNextNode
->ptrNext
->stInfo
.ptrHead
;
1469 while (pFindNextStreamW(hStreams
, &wfsdStreamInfo
))
1471 *ptrCurNode
= cmd_alloc(sizeof(DIRFINDSTREAMNODE
));
1472 if (*ptrCurNode
== NULL
)
1474 WARN("DEBUG: Cannot allocate memory for *ptrCurNode!\n");
1475 DirNodeCleanup(ptrStartNode
, &dwCount
);
1476 FindClose(hStreams
);
1481 memcpy(&(*ptrCurNode
)->stStreamInfo
, &wfsdStreamInfo
,
1482 sizeof(WIN32_FIND_STREAM_DATA
));
1484 /* If lower case is selected do it here */
1485 if (lpFlags
->bLowerCase
)
1487 _tcslwr((*ptrCurNode
)->stStreamInfo
.cStreamName
);
1490 ptrCurNode
= &(*ptrCurNode
)->ptrNext
;
1493 FindClose(hStreams
);
1498 /* Continue at next node at linked list */
1499 ptrNextNode
= ptrNextNode
->ptrNext
;
1502 /* Grab statistics */
1503 if (wfdFileInfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1512 u64Temp
.HighPart
= wfdFileInfo
.nFileSizeHigh
;
1513 u64Temp
.LowPart
= wfdFileInfo
.nFileSizeLow
;
1514 u64CountBytes
+= u64Temp
.QuadPart
;
1517 } while (FindNextFile(hSearch
, &wfdFileInfo
));
1521 /* Terminate list */
1522 ptrNextNode
->ptrNext
= NULL
;
1524 /* Calculate and allocate space need for making an array of pointers */
1525 ptrFileArray
= cmd_alloc(sizeof(PDIRFINDINFO
) * dwCount
);
1526 if (ptrFileArray
== NULL
)
1528 WARN("DEBUG: Cannot allocate memory for ptrFileArray!\n");
1529 DirNodeCleanup(ptrStartNode
, &dwCount
);
1534 * Create an array of pointers from the linked list
1535 * this will be used to sort and print data, rather than the list
1537 ptrNextNode
= ptrStartNode
;
1539 while (ptrNextNode
->ptrNext
)
1541 ptrFileArray
[dwCount
] = &ptrNextNode
->ptrNext
->stInfo
;
1542 ptrNextNode
= ptrNextNode
->ptrNext
;
1546 /* Sort Data if requested */
1547 if (lpFlags
->stOrderBy
.sCriteriaCount
> 0)
1548 QsortFiles(ptrFileArray
, 0, dwCount
-1, lpFlags
);
1551 pszFilePart
[-1] = _T('\0'); /* truncate to directory name only */
1552 DirPrintFiles(ptrFileArray
, dwCount
, szFullPath
, lpFlags
);
1553 pszFilePart
[-1] = _T('\\');
1555 if (lpFlags
->bRecursive
)
1557 PrintSummary(szFullPath
,
1566 cmd_free(ptrFileArray
);
1568 /* Free linked list */
1569 DirNodeCleanup(ptrStartNode
, &dwCount
);
1571 if (CheckCtrlBreak(BREAK_INPUT
))
1574 /* Add statistics to recursive statistics */
1575 recurse_dir_cnt
+= dwCountDirs
;
1576 recurse_file_cnt
+= dwCountFiles
;
1577 recurse_bytes
+= u64CountBytes
;
1580 * Do the recursive job if requested.
1581 * The recursion is done on ALL (independent of their attributes)
1582 * directories of the current one.
1584 if (lpFlags
->bRecursive
)
1586 /* The new search is involving any *.* file */
1587 memcpy(szSubPath
, szFullPath
, (pszFilePart
- szFullPath
) * sizeof(TCHAR
));
1588 _tcscpy(&szSubPath
[pszFilePart
- szFullPath
], _T("*.*"));
1590 hRecSearch
= FindFirstFile(szSubPath
, &wfdFileInfo
);
1591 if (hRecSearch
!= INVALID_HANDLE_VALUE
)
1595 /* We search for directories other than "." and ".." */
1596 if ((_tcsicmp(wfdFileInfo
.cFileName
, _T(".")) != 0) &&
1597 (_tcsicmp(wfdFileInfo
.cFileName
, _T("..")) != 0) &&
1598 (wfdFileInfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1600 /* Concat the path and the directory to do recursive */
1601 memcpy(szSubPath
, szFullPath
, (pszFilePart
- szFullPath
) * sizeof(TCHAR
));
1602 _tcscpy(&szSubPath
[pszFilePart
- szFullPath
], wfdFileInfo
.cFileName
);
1603 _tcscat(szSubPath
, _T("\\"));
1604 _tcscat(szSubPath
, pszFilePart
);
1606 /* We do the same for the folder */
1607 if (DirList(szSubPath
, lpFlags
) != 0)
1609 FindClose(hRecSearch
);
1613 } while (FindNextFile(hRecSearch
, &wfdFileInfo
));
1615 FindClose(hRecSearch
);
1624 * internal dir command
1627 CommandDir(LPTSTR rest
)
1629 TCHAR dircmd
[MAX_PATH
]; /* A variable to store the DIRCMD environment variable */
1630 TCHAR path
[MAX_PATH
];
1631 TCHAR prev_volume
[MAX_PATH
];
1632 LPTSTR
* params
= NULL
;
1636 DIRSWITCHFLAGS stFlags
;
1640 /* Initialize Switch Flags < Default switches are set here! > */
1641 stFlags
.b4Digit
= TRUE
;
1642 stFlags
.bBareFormat
= FALSE
;
1643 stFlags
.bDataStreams
= FALSE
;
1644 stFlags
.bLowerCase
= FALSE
;
1645 stFlags
.bNewLongList
= TRUE
;
1646 stFlags
.bPause
= FALSE
;
1647 stFlags
.bRecursive
= FALSE
;
1648 stFlags
.bShortName
= FALSE
;
1649 stFlags
.bTSeparator
= TRUE
;
1650 stFlags
.bUser
= FALSE
;
1651 stFlags
.bWideList
= FALSE
;
1652 stFlags
.bWideListColSort
= FALSE
;
1653 stFlags
.stTimeField
.eTimeField
= TF_MODIFIEDDATE
;
1654 stFlags
.stAttribs
.dwAttribMask
= FILE_ATTRIBUTE_HIDDEN
| FILE_ATTRIBUTE_SYSTEM
;
1655 stFlags
.stAttribs
.dwAttribVal
= 0L;
1656 stFlags
.stOrderBy
.sCriteriaCount
= 0;
1660 /* Read the parameters from the DIRCMD environment variable */
1661 if (GetEnvironmentVariable (_T("DIRCMD"), dircmd
, ARRAYSIZE(dircmd
)))
1663 if (!DirReadParam(dircmd
, ¶ms
, &entries
, &stFlags
))
1670 /* Read the parameters */
1671 if (!DirReadParam(rest
, ¶ms
, &entries
, &stFlags
) || CheckCtrlBreak(BREAK_INPUT
))
1677 /* Default to current directory */
1680 if (!add_entry(&entries
, ¶ms
, _T("*")))
1687 prev_volume
[0] = _T('\0');
1689 /* Reset paging state */
1691 ConOutPrintfPaging(TRUE
, _T(""));
1693 for (loop
= 0; loop
< (UINT
)entries
; loop
++)
1695 if (CheckCtrlBreak(BREAK_INPUT
))
1701 recurse_dir_cnt
= 0L;
1702 recurse_file_cnt
= 0L;
1706 Uncomment this to show the final state of switch flags*/
1709 TRACE("Attributes mask/value %x/%x\n",stFlags
.stAttribs
.dwAttribMask
,stFlags
.stAttribs
.dwAttribVal
);
1710 TRACE("(B) Bare format : %i\n", stFlags
.bBareFormat
);
1711 TRACE("(C) Thousand : %i\n", stFlags
.bTSeparator
);
1712 TRACE("(W) Wide list : %i\n", stFlags
.bWideList
);
1713 TRACE("(D) Wide list sort by column : %i\n", stFlags
.bWideListColSort
);
1714 TRACE("(L) Lowercase : %i\n", stFlags
.bLowerCase
);
1715 TRACE("(N) New : %i\n", stFlags
.bNewLongList
);
1716 TRACE("(O) Order : %i\n", stFlags
.stOrderBy
.sCriteriaCount
);
1717 for (i
=0;i
<stFlags
.stOrderBy
.sCriteriaCount
;i
++)
1718 TRACE(" Order Criteria [%i]: %i (Reversed: %i)\n",i
, stFlags
.stOrderBy
.eCriteria
[i
], stFlags
.stOrderBy
.bCriteriaRev
[i
]);
1719 TRACE("(P) Pause : %i\n", stFlags
.bPause
);
1720 TRACE("(Q) Owner : %i\n", stFlags
.bUser
);
1721 TRACE("(R) Data stream : %i\n", stFlags
.bDataStreams
);
1722 TRACE("(S) Recursive : %i\n", stFlags
.bRecursive
);
1723 TRACE("(T) Time field : %i\n", stFlags
.stTimeField
.eTimeField
);
1724 TRACE("(X) Short names : %i\n", stFlags
.bShortName
);
1725 TRACE("Parameter : %s\n", debugstr_aw(params
[loop
]));
1728 /* Print the drive header if the volume changed */
1729 ChangedVolume
= TRUE
;
1731 if (!stFlags
.bBareFormat
&&
1732 GetVolumePathName(params
[loop
], path
, ARRAYSIZE(path
)))
1734 if (!_tcscmp(path
, prev_volume
))
1735 ChangedVolume
= FALSE
;
1737 _tcscpy(prev_volume
, path
);
1739 else if (GetFullPathName(params
[loop
], ARRAYSIZE(path
), path
, &pszFilePart
) != 0)
1741 if (pszFilePart
!= NULL
)
1742 *pszFilePart
= _T('\0');
1746 _tcscpy(path
, params
[loop
]);
1749 /* Print the header */
1750 if (ChangedVolume
&& !stFlags
.bBareFormat
&&
1751 !PrintDirectoryHeader(params
[loop
], &stFlags
))
1757 /* Perform the actual directory listing */
1758 if (DirList(params
[loop
], &stFlags
) != 0)
1764 /* Print the footer */