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 rewriten 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
139 #ifdef INCLUDE_CMD_DIR
143 /* Time Field enumeration */
148 TF_LASTACCESSEDDATE
= 2
151 /* Ordered by enumeration */
161 /* The struct for holding the switches */
162 typedef struct _DirSwitchesFlags
164 BOOL bBareFormat
; /* Bare Format */
165 BOOL bTSeperator
; /* Thousands seperator */
166 BOOL bWideList
; /* Wide list format */
167 BOOL bWideListColSort
; /* Wide list format but sorted by column */
168 BOOL bLowerCase
; /* Uses lower case */
169 BOOL bNewLongList
; /* New long list */
170 BOOL bPause
; /* Pause per page */
171 BOOL bUser
; /* Displays the owner of file */
172 BOOL bRecursive
; /* Displays files in specified directory and all sub */
173 BOOL bShortName
; /* Displays the sort name of files if exist */
174 BOOL b4Digit
; /* Four digit year */
177 DWORD dwAttribVal
; /* The desired state of attribute */
178 DWORD dwAttribMask
; /* Which attributes to check */
179 BOOL bUnSet
; /* A helper flag if "-" was given with the switch */
180 BOOL bParSetted
; /* A helper flag if parameters of switch were given */
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 BOOL bUnSet
; /* A helper flag if "-" was given with the switch */
188 BOOL bParSetted
; /* A helper flag if parameters of switch were given */
189 } stOrderBy
; /* Ordered by criterias */
192 enum ETimeField eTimeField
; /* The time field that will be used for */
193 BOOL bUnSet
; /* A helper flag if "-" was given with the switch */
194 BOOL bParSetted
; /* A helper flag if parameters of switch were given */
195 } stTimeField
; /* The time field to display or use for sorting */
196 } DIRSWITCHFLAGS
, *LPDIRSWITCHFLAGS
;
199 typedef struct _DIRFINDLISTNODE
201 WIN32_FIND_DATA stFindInfo
;
202 struct _DIRFINDLISTNODE
*ptrNext
;
203 } DIRFINDLISTNODE
, *PDIRFINDLISTNODE
;
207 (WINAPI
*PGETFREEDISKSPACEEX
)(LPCTSTR
, PULARGE_INTEGER
, PULARGE_INTEGER
, PULARGE_INTEGER
);
210 /* Globally save the # of dirs, files and bytes,
211 * probabaly later pass them to functions. Rob Lake */
212 static ULONG recurse_dir_cnt
;
213 static ULONG recurse_file_cnt
;
214 static ULARGE_INTEGER recurse_bytes
;
220 * displays help screen for dir
226 ConOutResPaging(TRUE
, STRING_DIR_HELP1
);
234 * Parse the parameters and switches of the command line and exports them
237 DirReadParam(LPTSTR Line
, /* [IN] The line with the parameters & switches */
238 LPTSTR
** params
, /* [OUT] The parameters after parsing */
239 LPINT entries
, /* [OUT] The number of parameters after parsing */
240 LPDIRSWITCHFLAGS lpFlags
) /* [IN/OUT] The flags after calculating switches */
242 TCHAR cCurSwitch
; /* The current switch */
243 TCHAR cCurChar
; /* Current examing character */
244 TCHAR cCurUChar
; /* Current upper examing character */
245 BOOL bNegative
; /* Negative switch */
246 BOOL bPNegative
; /* Negative switch parameter */
247 BOOL bIntoQuotes
; /* A flag showing if we are in quotes (") */
248 LPTSTR ptrStart
; /* A pointer to the first character of a parameter */
249 LPTSTR ptrEnd
; /* A pointer to the last character of a parameter */
252 /* Initialize parameter array */
260 /* Initialize variables; */
261 cCurSwitch
= _T(' ');
266 /* We suppose that switch parameters
267 were given to avoid setting them to default
268 if the switch was not given */
269 lpFlags
->stAttribs
.bParSetted
= TRUE
;
270 lpFlags
->stOrderBy
.bParSetted
= TRUE
;
271 lpFlags
->stTimeField
.bParSetted
= TRUE
;
274 /* Main Loop (see README_DIR.txt) */
275 /* scan the command line char per char, and we process its char */
278 /* we save current character as it is and its upper case */
280 cCurUChar
= _totupper(*Line
);
282 /* 1st section (see README_DIR.txt) */
283 /* When a switch is expecting */
284 if (cCurSwitch
== _T('/'))
286 if ((cCurUChar
== _T('A')) ||(cCurUChar
== _T('T')) || (cCurUChar
== _T('O')))
288 cCurSwitch
= cCurUChar
;
292 lpFlags
->stAttribs
.bUnSet
= bNegative
;
293 lpFlags
->stAttribs
.bParSetted
= FALSE
;
296 lpFlags
->stTimeField
.bUnSet
= bNegative
;
297 lpFlags
->stTimeField
.bParSetted
= FALSE
;
300 lpFlags
->stOrderBy
.bUnSet
= bNegative
;
301 lpFlags
->stOrderBy
.bParSetted
= FALSE
;
305 else if (cCurUChar
== _T('L'))
306 lpFlags
->bLowerCase
= ! bNegative
;
307 else if (cCurUChar
== _T('B'))
308 lpFlags
->bBareFormat
= ! bNegative
;
309 else if (cCurUChar
== _T('C'))
310 lpFlags
->bTSeperator
= ! bNegative
;
311 else if (cCurUChar
== _T('W'))
312 lpFlags
->bWideList
= ! bNegative
;
313 else if (cCurUChar
== _T('D'))
314 lpFlags
->bWideListColSort
= ! bNegative
;
315 else if (cCurUChar
== _T('N'))
316 lpFlags
->bNewLongList
= ! bNegative
;
317 else if (cCurUChar
== _T('P'))
318 lpFlags
->bPause
= ! bNegative
;
319 else if (cCurUChar
== _T('Q'))
320 lpFlags
->bUser
= ! bNegative
;
321 else if (cCurUChar
== _T('S'))
322 lpFlags
->bRecursive
= ! bNegative
;
323 else if (cCurUChar
== _T('X'))
324 lpFlags
->bShortName
= ! bNegative
;
325 else if (cCurChar
== _T('4'))
326 lpFlags
->b4Digit
= ! bNegative
;
327 else if (cCurChar
== _T('?'))
332 else if (cCurChar
== _T('-'))
338 error_invalid_switch ((TCHAR
)_totupper (*Line
));
342 /* We check if we calculated the negative value and realese the flag */
343 if ((cCurChar
!= _T('-')) && bNegative
)
346 /* if not a,o,t or - option then next parameter is not a switch */
347 if ((cCurSwitch
== _T('/')) && (!bNegative
))
348 cCurSwitch
= _T(' ');
351 else if ((cCurSwitch
== _T(' ')) || (cCurSwitch
== _T('P')))
353 /* 2nd section (see README_DIR.txt) */
354 /* We are expecting parameter or the unknown */
356 if (cCurChar
== _T('/'))
357 cCurSwitch
= _T('/');
359 /* Process a spacer */
360 else if (cCurChar
== _T(' '))
364 cCurSwitch
= _T(' ');
365 if(ptrStart
&& ptrEnd
)
367 temp
= cmd_alloc(((ptrEnd
- ptrStart
) + 2) * sizeof (TCHAR
));
370 memcpy(temp
, ptrStart
, ((ptrEnd
- ptrStart
) + 2) * sizeof (TCHAR
));
371 temp
[(ptrEnd
- ptrStart
+ 1)] = _T('\0');
372 if(!add_entry(entries
, params
, temp
))
387 else if (cCurChar
== _T('\"'))
389 /* Process a quote */
390 bIntoQuotes
= !bIntoQuotes
;
396 /* Process a character for parameter */
397 if ((cCurSwitch
== _T(' ')) && ptrStart
&& ptrEnd
)
399 temp
= cmd_alloc(((ptrEnd
- ptrStart
) + 2) * sizeof (TCHAR
));
402 memcpy(temp
, ptrStart
, ((ptrEnd
- ptrStart
) + 2) * sizeof (TCHAR
));
403 temp
[(ptrEnd
- ptrStart
+ 1)] = _T('\0');
404 if(!add_entry(entries
, params
, temp
))
416 cCurSwitch
= _T('P');
418 ptrStart
= ptrEnd
= Line
;
424 /* 3rd section (see README_DIR.txt) */
425 /* We are waiting for switch parameters */
427 /* Check if there are no more switch parameters */
428 if ((cCurChar
== _T('/')) || ( cCurChar
== _T(' ')))
430 /* Wrong desicion path, reprocess current character */
431 cCurSwitch
= cCurChar
;
434 /* Process parameter switch */
437 case _T('A'): /* Switch parameters for /A (attributes filter) */
438 /* Ok a switch parameter was given */
439 lpFlags
->stAttribs
.bParSetted
= TRUE
;
441 if (cCurChar
== _T(':'))
442 /* =V= dead command, used to make the "if" work */
444 else if(cCurChar
== _T('-'))
446 else if(cCurUChar
== _T('D'))
448 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_DIRECTORY
;
450 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_DIRECTORY
;
452 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_DIRECTORY
;
454 else if(cCurUChar
== _T('R'))
456 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_READONLY
;
458 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_READONLY
;
460 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_READONLY
;
462 else if(cCurUChar
== _T('H'))
464 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_HIDDEN
;
466 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_HIDDEN
;
468 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_HIDDEN
;
470 else if(cCurUChar
== _T('A'))
472 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_ARCHIVE
;
474 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_ARCHIVE
;
476 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_ARCHIVE
;
478 else if(cCurUChar
== _T('S'))
480 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_SYSTEM
;
482 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_SYSTEM
;
484 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_SYSTEM
;
488 error_parameter_format((TCHAR
)_totupper (*Line
));
492 case _T('T'): /* Switch parameters for /T (time field) */
494 /* Ok a switch parameter was given */
495 lpFlags
->stTimeField
.bParSetted
= TRUE
;
497 if (cCurChar
== _T(':'))
498 /* =V= dead command, used to make the "if" work */
500 else if(cCurUChar
== _T('C'))
501 lpFlags
->stTimeField
.eTimeField
= TF_CREATIONDATE
;
502 else if(cCurUChar
== _T('A'))
503 lpFlags
->stTimeField
.eTimeField
= TF_LASTACCESSEDDATE
;
504 else if(cCurUChar
== _T('W'))
505 lpFlags
->stTimeField
.eTimeField
= TF_MODIFIEDDATE
;
508 error_parameter_format((TCHAR
)_totupper (*Line
));
512 case _T('O'): /* Switch parameters for /O (order) */
513 /* Ok a switch parameter was given */
514 lpFlags
->stOrderBy
.bParSetted
= TRUE
;
516 if (cCurChar
== _T(':'))
517 /* <== dead command, used to make the "if" work */
519 else if(cCurChar
== _T('-'))
521 else if(cCurUChar
== _T('N'))
523 if (lpFlags
->stOrderBy
.sCriteriaCount
< 3) lpFlags
->stOrderBy
.sCriteriaCount
++;
524 lpFlags
->stOrderBy
.bCriteriaRev
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = bPNegative
;
525 lpFlags
->stOrderBy
.eCriteria
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = ORDER_NAME
;
527 else if(cCurUChar
== _T('S'))
529 if (lpFlags
->stOrderBy
.sCriteriaCount
< 3) lpFlags
->stOrderBy
.sCriteriaCount
++;
530 lpFlags
->stOrderBy
.bCriteriaRev
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = bPNegative
;
531 lpFlags
->stOrderBy
.eCriteria
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = ORDER_SIZE
;
533 else if(cCurUChar
== _T('G'))
535 if (lpFlags
->stOrderBy
.sCriteriaCount
< 3) lpFlags
->stOrderBy
.sCriteriaCount
++;
536 lpFlags
->stOrderBy
.bCriteriaRev
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = bPNegative
;
537 lpFlags
->stOrderBy
.eCriteria
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = ORDER_DIRECTORY
;
539 else if(cCurUChar
== _T('E'))
541 if (lpFlags
->stOrderBy
.sCriteriaCount
< 3) lpFlags
->stOrderBy
.sCriteriaCount
++;
542 lpFlags
->stOrderBy
.bCriteriaRev
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = bPNegative
;
543 lpFlags
->stOrderBy
.eCriteria
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = ORDER_EXTENSION
;
545 else if(cCurUChar
== _T('D'))
547 if (lpFlags
->stOrderBy
.sCriteriaCount
< 3) lpFlags
->stOrderBy
.sCriteriaCount
++;
548 lpFlags
->stOrderBy
.bCriteriaRev
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = bPNegative
;
549 lpFlags
->stOrderBy
.eCriteria
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = ORDER_TIME
;
554 error_parameter_format((TCHAR
)_totupper (*Line
));
560 /* We check if we calculated the negative value and realese the flag */
561 if ((cCurChar
!= _T('-')) && bPNegative
)
567 /* Terminate the parameters */
568 if(ptrStart
&& ptrEnd
)
570 temp
= cmd_alloc((ptrEnd
- ptrStart
+ 2) * sizeof(TCHAR
));
573 memcpy(temp
, ptrStart
, (ptrEnd
- ptrStart
+ 1) * sizeof(TCHAR
));
574 temp
[(ptrEnd
- ptrStart
+ 1)] = _T('\0');
575 if(!add_entry(entries
, params
, temp
))
588 /* Calculate the switches with no switch paramater */
589 if (!(lpFlags
->stAttribs
.bParSetted
))
591 lpFlags
->stAttribs
.dwAttribVal
= 0L;
592 lpFlags
->stAttribs
.dwAttribMask
= lpFlags
->stAttribs
.dwAttribVal
;
594 if (!(lpFlags
->stOrderBy
.bParSetted
))
596 lpFlags
->stOrderBy
.sCriteriaCount
= 1;
597 lpFlags
->stOrderBy
.eCriteria
[0] = ORDER_NAME
;
598 lpFlags
->stOrderBy
.bCriteriaRev
[0] = FALSE
;
600 if (!(lpFlags
->stOrderBy
.bParSetted
))
601 lpFlags
->stTimeField
.eTimeField
= TF_MODIFIEDDATE
;
603 /* Calculate the unsetted switches (the "-" prefixed)*/
604 if (lpFlags
->stAttribs
.bUnSet
)
606 lpFlags
->stAttribs
.bUnSet
= FALSE
;
607 lpFlags
->stAttribs
.dwAttribVal
= 0L;
608 lpFlags
->stAttribs
.dwAttribMask
= FILE_ATTRIBUTE_HIDDEN
| FILE_ATTRIBUTE_SYSTEM
;
610 if (lpFlags
->stOrderBy
.bUnSet
)
612 lpFlags
->stOrderBy
.bUnSet
= FALSE
;
613 lpFlags
->stOrderBy
.sCriteriaCount
= 0;
615 if (lpFlags
->stTimeField
.bUnSet
)
617 lpFlags
->stTimeField
.bUnSet
= FALSE
;
618 lpFlags
->stTimeField
.eTimeField
= TF_MODIFIEDDATE
;
625 * PrintDirectoryHeader
627 * print the header for the dir command
630 PrintDirectoryHeader(LPTSTR szPath
, LPDIRSWITCHFLAGS lpFlags
)
632 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
633 TCHAR szFullDir
[MAX_PATH
];
634 TCHAR szRootName
[MAX_PATH
];
639 if (lpFlags
->bBareFormat
)
642 if (GetFullPathName(szPath
, sizeof(szFullDir
) / sizeof(TCHAR
), szFullDir
, &pszFilePart
) == 0)
644 ErrorMessage(GetLastError(), _T("Failed to build full directory path"));
648 if (pszFilePart
!= NULL
)
649 *pszFilePart
= _T('\0');
651 /* get the media ID of the drive */
652 if (!GetVolumePathName(szFullDir
, szRootName
, sizeof(szRootName
) / sizeof(TCHAR
)) ||
653 !GetVolumeInformation(szRootName
, szVolName
, 80, &dwSerialNr
,
654 NULL
, NULL
, NULL
, 0))
659 /* print drive info */
660 if (szVolName
[0] != _T('\0'))
662 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP2
, szMsg
, RC_STRING_MAX_SIZE
);
663 //needs to have first paramter as TRUE because
664 //this is the first output and need to clear the static
666 ConOutPrintfPaging(TRUE
,szMsg
, szRootName
[0], szVolName
);
668 ConOutPrintf(szMsg
, szRootName
[0], szVolName
);
673 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP3
, szMsg
, RC_STRING_MAX_SIZE
);
675 ConOutPrintfPaging(TRUE
,szMsg
, szRootName
[0]);
677 ConOutPrintf(szMsg
, szRootName
[0]);
680 /* print the volume serial number if the return was successful */
681 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP4
, (LPTSTR
) szMsg
, RC_STRING_MAX_SIZE
);
683 ConOutPrintfPaging(FALSE
,szMsg
,
699 * insert commas into a number
704 ConvertULong (ULONG num
, LPTSTR des
, INT len
)
721 if (((c
+ 1) % (nNumberGroups
+ 1)) == 0)
722 temp
[30 - c
++] = cThousandSeparator
;
723 temp
[30 - c
++] = (TCHAR
)(num
% 10) + _T('0');
727 for (n
= 0; n
<= c
; n
++)
728 des
[n
] = temp
[31 - c
+ n
];
736 DirPrintFileDateTime(TCHAR
*lpDate
,
738 LPWIN32_FIND_DATA lpFile
,
739 LPDIRSWITCHFLAGS lpFlags
)
747 /* Select the right time field */
748 switch (lpFlags
->stTimeField
.eTimeField
)
750 case TF_CREATIONDATE
:
751 if (!FileTimeToLocalFileTime(&lpFile
->ftCreationTime
, &ft
))
753 FileTimeToSystemTime(&ft
, &dt
);
756 case TF_LASTACCESSEDDATE
:
757 if (!FileTimeToLocalFileTime(&lpFile
->ftLastAccessTime
, &ft
))
759 FileTimeToSystemTime(&ft
, &dt
);
762 case TF_MODIFIEDDATE
:
763 if (!FileTimeToLocalFileTime(&lpFile
->ftLastWriteTime
, &ft
))
765 FileTimeToSystemTime(&ft
, &dt
);
770 wYear
= (lpFlags
->b4Digit
) ? dt
.wYear
: dt
.wYear
%100;
775 _stprintf (szDate
, _T("%02d%c%02d%c%0*d"),
776 dt
.wMonth
, cDateSeparator
,
777 dt
.wDay
, cDateSeparator
,
778 lpFlags
->b4Digit
?4:2, wYear
);
782 _stprintf (szDate
, _T("%02d%c%02d%c%0*d"),
783 dt
.wDay
, cDateSeparator
, dt
.wMonth
,
784 cDateSeparator
,lpFlags
->b4Digit
?4:2, wYear
);
788 _stprintf (szDate
, _T("%0*d%c%02d%c%02d"),
789 lpFlags
->b4Digit
?4:2, wYear
, cDateSeparator
,
790 dt
.wMonth
, cDateSeparator
, dt
.wDay
);
796 case 0: /* 12 hour format */
798 _stprintf (szTime
,_T("%02d%c%02u%c"),
799 (dt
.wHour
== 0 ? 12 : (dt
.wHour
<= 12 ? dt
.wHour
: dt
.wHour
- 12)),
801 dt
.wMinute
, (dt
.wHour
<= 11 ? _T('a') : _T('p')));
804 case 1: /* 24 hour format */
805 _stprintf (szTime
, _T("%02d%c%02u"),
806 dt
.wHour
, cTimeSeparator
, dt
.wMinute
);
810 _tcscpy(lpDate
, szDate
);
811 _tcscpy(lpTime
, szTime
);
816 GetUserDiskFreeSpace(LPCTSTR lpRoot
,
817 PULARGE_INTEGER lpFreeSpace
)
819 PGETFREEDISKSPACEEX pGetFreeDiskSpaceEx
;
825 ULARGE_INTEGER TotalNumberOfBytes
, TotalNumberOfFreeBytes
;
827 lpFreeSpace
->QuadPart
= 0;
829 hInstance
= LoadLibrary(_T("KERNEL32"));
830 if (hInstance
!= NULL
)
832 pGetFreeDiskSpaceEx
= (PGETFREEDISKSPACEEX
)GetProcAddress(hInstance
,
834 "GetDiskFreeSpaceExW");
836 "GetDiskFreeSpaceExA");
838 if (pGetFreeDiskSpaceEx
!= NULL
)
840 if (pGetFreeDiskSpaceEx(lpRoot
, lpFreeSpace
, &TotalNumberOfBytes
, &TotalNumberOfFreeBytes
) == TRUE
)
843 FreeLibrary(hInstance
);
846 GetDiskFreeSpace(lpRoot
,
852 lpFreeSpace
->QuadPart
= dwSecPerCl
* dwBytPerSec
* dwFreeCl
;
857 * print_summary: prints dir summary
858 * Added by Rob Lake 06/17/98 to compact code
859 * Just copied Tim's Code and patched it a bit
863 PrintSummary(LPTSTR szPath
,
866 ULARGE_INTEGER u64Bytes
,
867 LPDIRSWITCHFLAGS lpFlags
,
870 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
872 ULARGE_INTEGER uliFree
;
875 /* Here we check if we didn't find anything */
876 if (!(ulFiles
+ ulDirs
))
878 if (!lpFlags
->bRecursive
|| (TotalSummary
&& lpFlags
->bRecursive
))
879 error_file_not_found();
884 /* In bare format we don't print results */
885 if (lpFlags
->bBareFormat
)
888 /* Print recursive specific results */
890 /* Take this code offline to fix /S does not print duoble info */
891 if (TotalSummary
&& lpFlags
->bRecursive
)
893 ConvertULargeInteger(u64Bytes
, szBuffer
, sizeof(szBuffer
), lpFlags
->bTSeperator
);
894 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP5
, szMsg
, RC_STRING_MAX_SIZE
);
896 ConOutPrintfPaging(FALSE
,szMsg
,ulFiles
, szBuffer
);
898 ConOutPrintf(szMsg
,ulFiles
, szBuffer
);
902 /* Print File Summary */
903 /* Condition to print summary is:
904 If we are not in bare format and if we have results! */
905 ConvertULargeInteger(u64Bytes
, szBuffer
, 20, lpFlags
->bTSeperator
);
906 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP8
, szMsg
, RC_STRING_MAX_SIZE
);
908 ConOutPrintfPaging(FALSE
,szMsg
,ulFiles
, szBuffer
);
910 ConOutPrintf(szMsg
,ulFiles
, szBuffer
);
913 /* Print total directories and freespace */
914 if (!lpFlags
->bRecursive
|| (TotalSummary
&& lpFlags
->bRecursive
))
916 GetUserDiskFreeSpace(szPath
, &uliFree
);
917 ConvertULargeInteger(uliFree
, szBuffer
, sizeof(szBuffer
), lpFlags
->bTSeperator
);
918 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP6
, (LPTSTR
) szMsg
, RC_STRING_MAX_SIZE
);
920 ConOutPrintfPaging(FALSE
,szMsg
,ulDirs
, szBuffer
);
922 ConOutPrintf(szMsg
,ulDirs
, szBuffer
);
931 * Get the extension of a filename
933 TCHAR
* getExt(const TCHAR
* file
)
935 static TCHAR
*NoExt
= _T("");
936 TCHAR
* lastdot
= _tcsrchr(file
, _T('.'));
937 return (lastdot
!= NULL
? lastdot
+ 1 : NoExt
);
943 * Get the name of the file without extension
946 getName(const TCHAR
* file
, TCHAR
* dest
)
951 /* Check for "." and ".." folders */
952 if ((_tcscmp(file
, _T(".")) == 0) ||
953 (_tcscmp(file
, _T("..")) == 0))
959 end
= _tcsrchr(file
, _T('.'));
961 iLen
= _tcslen(file
);
966 _tcsncpy(dest
, file
, iLen
);
967 *(dest
+ iLen
) = _T('\0');
976 * The function that prints in new style
979 DirPrintNewList(LPWIN32_FIND_DATA ptrFiles
[], /* [IN]Files' Info */
980 DWORD dwCount
, /* [IN] The quantity of files */
981 TCHAR
*szCurPath
, /* [IN] Full path of current directory */
982 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
986 TCHAR szShortName
[15];
990 ULARGE_INTEGER u64FileSize
;
992 for (i
= 0;i
< dwCount
;i
++)
995 if (ptrFiles
[i
]->dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
)
999 _tcscpy(szSize
, _T("<JUNCTION>"));
1001 else if (ptrFiles
[i
]->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1005 _tcscpy(szSize
, _T("<DIR>"));
1011 u64FileSize
.HighPart
= ptrFiles
[i
]->nFileSizeHigh
;
1012 u64FileSize
.LowPart
= ptrFiles
[i
]->nFileSizeLow
;
1013 ConvertULargeInteger(u64FileSize
, szSize
, 20, lpFlags
->bTSeperator
);
1016 /* Calculate short name */
1017 szShortName
[0] = _T('\0');
1018 if (lpFlags
->bShortName
)
1019 _stprintf(szShortName
, _T(" %-12s"), ptrFiles
[i
]->cAlternateFileName
);
1021 /* Format date and time */
1022 DirPrintFileDateTime(szDate
, szTime
, ptrFiles
[i
], lpFlags
);
1024 /* Print the line */
1027 if (ConOutPrintfPaging(FALSE
,_T("%10s %-6s %*s%s %s\n"),
1033 ptrFiles
[i
]->cFileName
) == 1)
1037 ConOutPrintf(_T("%10s %-6s %*s%s %s\n"),
1043 ptrFiles
[i
]->cFileName
);
1051 * The function that prints in wide list
1054 DirPrintWideList(LPWIN32_FIND_DATA ptrFiles
[], /* [IN] Files' Info */
1055 DWORD dwCount
, /* [IN] The quantity of files */
1056 TCHAR
*szCurPath
, /* [IN] Full path of current directory */
1057 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
1063 TCHAR szTempFname
[MAX_PATH
];
1068 /* Calculate longest name */
1070 for (i
= 0; i
< dwCount
; i
++)
1072 if (ptrFiles
[i
]->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1074 /* Directories need 2 additinal characters for brackets */
1075 if ((_tcslen(ptrFiles
[i
]->cFileName
) + 2) > iLongestName
)
1076 iLongestName
= _tcslen(ptrFiles
[i
]->cFileName
) + 2;
1080 if (_tcslen(ptrFiles
[i
]->cFileName
) > iLongestName
)
1081 iLongestName
= _tcslen(ptrFiles
[i
]->cFileName
);
1085 /* Count the highest number of columns */
1086 GetScreenSize(&iScreenWidth
, 0);
1087 iColumns
= iScreenWidth
/ iLongestName
;
1089 /* Check if there is enough space for spaces between names */
1090 if (((iLongestName
* iColumns
) + iColumns
) >= (UINT
)iScreenWidth
)
1093 /* A last check at iColumns to avoid division by zero */
1097 /* Print Column sorted */
1098 if (lpFlags
->bWideListColSort
)
1100 /* Calculate the lines that will be printed */
1101 // iLines = ceil((float)dwCount/(float)iColumns);
1102 iLines
= (USHORT
)(dwCount
/ iColumns
);
1104 for (i
= 0;i
< iLines
;i
++)
1106 for (j
= 0; j
< iColumns
; j
++)
1108 temp
= (j
* iLines
) + i
;
1109 if (temp
>= dwCount
)
1112 if (ptrFiles
[temp
]->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1113 _stprintf(szTempFname
, _T("[%s]"), ptrFiles
[temp
]->cFileName
);
1115 _stprintf(szTempFname
, _T("%s"), ptrFiles
[temp
]->cFileName
);
1118 ConOutPrintfPaging(FALSE
,_T("%-*s"), iLongestName
+ 1 , szTempFname
);
1120 ConOutPrintf(_T("%-*s"), iLongestName
+ 1 , szTempFname
);
1124 ConOutPrintfPaging(FALSE
,_T("\n"));
1126 ConOutPrintf(_T("\n"));
1131 /* Print Line sorted */
1132 for (i
= 0; i
< dwCount
; i
++)
1134 if (ptrFiles
[i
]->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1135 _stprintf(szTempFname
, _T("[%s]"), ptrFiles
[i
]->cFileName
);
1137 _stprintf(szTempFname
, _T("%s"), ptrFiles
[i
]->cFileName
);
1140 ConOutPrintfPaging(FALSE
,_T("%-*s"), iLongestName
+ 1, szTempFname
);
1142 ConOutPrintf(_T("%-*s"), iLongestName
+ 1, szTempFname
);
1145 * We print a new line at the end of each column
1146 * except for the case that it is the last item.
1148 if (!((i
+ 1) % iColumns
) && (i
< (dwCount
- 1)))
1151 ConOutPrintfPaging(FALSE
,_T("\n"));
1153 ConOutPrintf(_T("\n"));
1157 /* Add a new line after the last item */
1159 ConOutPrintfPaging(FALSE
,_T("\n"));
1161 ConOutPrintf(_T("\n"));
1169 * The function that prints in old style
1172 DirPrintOldList(LPWIN32_FIND_DATA ptrFiles
[], /* [IN] Files' Info */
1173 DWORD dwCount
, /* [IN] The quantity of files */
1174 TCHAR
* szCurPath
, /* [IN] Full path of current directory */
1175 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
1177 DWORD i
; /* An indexer for "for"s */
1178 TCHAR szName
[10]; /* The name of file */
1179 TCHAR szExt
[5]; /* The extension of file */
1180 TCHAR szDate
[30],szTime
[30]; /* Used to format time and date */
1181 TCHAR szSize
[30]; /* The size of file */
1182 int iSizeFormat
; /* The format of size field */
1183 ULARGE_INTEGER u64FileSize
; /* The file size */
1185 for(i
= 0;i
< dwCount
;i
++)
1187 /* Broke 8.3 format */
1188 if (*ptrFiles
[i
]->cAlternateFileName
)
1190 /* If the file is long named then we read the alter name */
1191 getName( ptrFiles
[i
]->cAlternateFileName
, szName
);
1192 _tcscpy(szExt
, getExt( ptrFiles
[i
]->cAlternateFileName
));
1196 /* If the file is not long name we read its original name */
1197 getName( ptrFiles
[i
]->cFileName
, szName
);
1198 _tcscpy(szExt
, getExt( ptrFiles
[i
]->cFileName
));
1201 /* Calculate size */
1202 if (ptrFiles
[i
]->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1204 /* Directory, no size it's a directory*/
1206 _tcscpy(szSize
, _T("<DIR>"));
1212 u64FileSize
.HighPart
= ptrFiles
[i
]->nFileSizeHigh
;
1213 u64FileSize
.LowPart
= ptrFiles
[i
]->nFileSizeLow
;
1214 ConvertULargeInteger(u64FileSize
, szSize
, 20, lpFlags
->bTSeperator
);
1217 /* Format date and time */
1218 DirPrintFileDateTime(szDate
,szTime
,ptrFiles
[i
],lpFlags
);
1220 /* Print the line */
1223 if (ConOutPrintfPaging(FALSE
,_T("%-8s %-3s %*s %s %s\n"),
1224 szName
, /* The file's 8.3 name */
1225 szExt
, /* The file's 8.3 extension */
1226 iSizeFormat
, /* print format for size column */
1227 szSize
, /* The size of file or "<DIR>" for dirs */
1228 szDate
, /* The date of file/dir */
1229 szTime
) == 1) /* The time of file/dir */
1235 ConOutPrintf(_T("%-8s %-3s %*s %s %s\n"),
1236 szName
, /* The file's 8.3 name */
1237 szExt
, /* The file's 8.3 extension */
1238 iSizeFormat
, /* print format for size column */
1239 szSize
, /* The size of file or "<DIR>" for dirs */
1240 szDate
, /* The date of file/dir */
1241 szTime
); /* The time of file/dir */
1248 * The function that prints in bare format
1251 DirPrintBareList(LPWIN32_FIND_DATA ptrFiles
[], /* [IN] Files' Info */
1252 DWORD dwCount
, /* [IN] The number of files */
1253 LPTSTR lpCurPath
, /* [IN] Full path of current directory */
1254 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
1256 TCHAR szFullName
[MAX_PATH
];
1259 for (i
= 0; i
< dwCount
; i
++)
1261 if ((_tcscmp(ptrFiles
[i
]->cFileName
, _T(".")) == 0) ||
1262 (_tcscmp(ptrFiles
[i
]->cFileName
, _T("..")) == 0))
1264 /* at bare format we don't print "." and ".." folder */
1267 if (lpFlags
->bRecursive
)
1269 /* at recursive mode we print full path of file */
1270 _tcscpy(szFullName
, lpCurPath
);
1271 _tcscat(szFullName
, ptrFiles
[i
]->cFileName
);
1274 if (ConOutPrintfPaging(FALSE
,_T("%s\n"), szFullName
) == 1)
1280 ConOutPrintf(_T("%s\n"), szFullName
);
1284 /* if we are not in recursive mode we print the file names */
1287 if (ConOutPrintfPaging(FALSE
,_T("%s\n"),ptrFiles
[i
]->cFileName
) == 1)
1293 ConOutPrintf(_T("%s\n"),ptrFiles
[i
]->cFileName
);
1302 * The functions that prints the files list
1305 DirPrintFiles(LPWIN32_FIND_DATA ptrFiles
[], /* [IN] Files' Info */
1306 DWORD dwCount
, /* [IN] The quantity of files */
1307 TCHAR
*szCurPath
, /* [IN] Full path of current directory */
1308 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
1310 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
1311 TCHAR szTemp
[MAX_PATH
]; /* A buffer to format the directory header */
1315 /* We cut the trailing \ of the full path, unless the path is a drive */
1316 if (GetFullPathName(szCurPath
, sizeof(szTemp
) / sizeof(TCHAR
), szTemp
, &pszFilePart
) == 0)
1319 _tcscpy(szTemp
, szCurPath
);
1321 else if (pszFilePart
!= NULL
)
1322 *pszFilePart
= _T('\0');
1325 len
= _tcslen(szTemp
);
1326 if (len
> 0 && szTemp
[len
- 1] != _T('\\') &&
1327 GetFileAttributes(szTemp
) == INVALID_FILE_ATTRIBUTES
&&
1328 GetLastError() == ERROR_PATH_NOT_FOUND
)
1330 /* Special case for some fake dos devices, such as con:
1331 GetFullPathName doesn't return a pszFilePart pointer
1332 so we're going to fix this ourselves */
1333 while (len
> 0 && szTemp
[len
- 1] != _T('\\'))
1334 szTemp
[--len
] = _T('\0');
1338 len
= _tcslen(szTemp
);
1339 if ((len
!= 3 || szTemp
[len
- 2] != _T(':')) && szTemp
[len
- 1] == _T('\\'))
1340 szTemp
[len
-1] = _T('\0');
1342 /* Condition to print header:
1343 We are not printing in bare format
1344 and if we are in recursive mode... we must have results */
1345 if (!(lpFlags
->bBareFormat
) && !((lpFlags
->bRecursive
) && (dwCount
<= 0)))
1347 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP7
, szMsg
, RC_STRING_MAX_SIZE
);
1350 if (ConOutPrintfPaging(FALSE
,szMsg
, szTemp
) == 1)
1356 ConOutPrintf(szMsg
, szTemp
);
1359 if (lpFlags
->bBareFormat
)
1362 DirPrintBareList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1364 else if(lpFlags
->bShortName
)
1366 /* New list style / Short names */
1367 DirPrintNewList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1369 else if(lpFlags
->bWideListColSort
|| lpFlags
->bWideList
)
1372 DirPrintWideList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1374 else if (lpFlags
->bNewLongList
)
1377 DirPrintNewList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1381 /* If nothing is selected old list is the default */
1382 DirPrintOldList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1391 * Compares 2 files based on the order criteria
1394 CompareFiles(LPWIN32_FIND_DATA lpFile1
, /* [IN] A pointer to WIN32_FIND_DATA of file 1 */
1395 LPWIN32_FIND_DATA lpFile2
, /* [IN] A pointer to WIN32_FIND_DATA of file 2 */
1396 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags that we use to list */
1398 ULARGE_INTEGER u64File1
;
1399 ULARGE_INTEGER u64File2
;
1401 long iComp
= 0; /* The comparison result */
1403 /* Calculate critiries by order given from user */
1404 for (i
= 0;i
< lpFlags
->stOrderBy
.sCriteriaCount
;i
++)
1407 /* Calculate criteria */
1408 switch(lpFlags
->stOrderBy
.eCriteria
[i
])
1410 case ORDER_SIZE
: /* Order by size /o:s */
1411 /* concat the 32bit integers to a 64bit */
1412 u64File1
.LowPart
= lpFile1
->nFileSizeLow
;
1413 u64File1
.HighPart
= lpFile1
->nFileSizeHigh
;
1414 u64File2
.LowPart
= lpFile2
->nFileSizeLow
;
1415 u64File2
.HighPart
= lpFile2
->nFileSizeHigh
;
1417 /* In case that differnce is too big for a long */
1418 if (u64File1
.QuadPart
< u64File2
.QuadPart
)
1420 else if (u64File1
.QuadPart
> u64File2
.QuadPart
)
1426 case ORDER_DIRECTORY
: /* Order by directory attribute /o:g */
1427 iComp
= ((lpFile2
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)-
1428 (lpFile1
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
));
1431 case ORDER_EXTENSION
: /* Order by extension name /o:e */
1432 iComp
= _tcsicmp(getExt(lpFile1
->cFileName
),getExt(lpFile2
->cFileName
));
1435 case ORDER_NAME
: /* Order by filename /o:n */
1436 iComp
= _tcsicmp(lpFile1
->cFileName
, lpFile2
->cFileName
);
1439 case ORDER_TIME
: /* Order by file's time /o:t */
1440 /* We compare files based on the time field selected by /t */
1441 switch(lpFlags
->stTimeField
.eTimeField
)
1443 case TF_CREATIONDATE
:
1444 /* concat the 32bit integers to a 64bit */
1445 u64File1
.LowPart
= lpFile1
->ftCreationTime
.dwLowDateTime
;
1446 u64File1
.HighPart
= lpFile1
->ftCreationTime
.dwHighDateTime
;
1447 u64File2
.LowPart
= lpFile2
->ftCreationTime
.dwLowDateTime
;
1448 u64File2
.HighPart
= lpFile2
->ftCreationTime
.dwHighDateTime
;
1450 case TF_LASTACCESSEDDATE
:
1451 /* concat the 32bit integers to a 64bit */
1452 u64File1
.LowPart
= lpFile1
->ftLastAccessTime
.dwLowDateTime
;
1453 u64File1
.HighPart
= lpFile1
->ftLastAccessTime
.dwHighDateTime
;
1454 u64File2
.LowPart
= lpFile2
->ftLastAccessTime
.dwLowDateTime
;
1455 u64File2
.HighPart
= lpFile2
->ftLastAccessTime
.dwHighDateTime
;
1457 case TF_MODIFIEDDATE
:
1458 /* concat the 32bit integers to a 64bit */
1459 u64File1
.LowPart
= lpFile1
->ftLastWriteTime
.dwLowDateTime
;
1460 u64File1
.HighPart
= lpFile1
->ftLastWriteTime
.dwHighDateTime
;
1461 u64File2
.LowPart
= lpFile2
->ftLastWriteTime
.dwLowDateTime
;
1462 u64File2
.HighPart
= lpFile2
->ftLastWriteTime
.dwHighDateTime
;
1466 /* In case that differnce is too big for a long */
1467 if (u64File1
.QuadPart
< u64File2
.QuadPart
)
1469 else if (u64File1
.QuadPart
> u64File2
.QuadPart
)
1476 /* Reverse if desired */
1477 if (lpFlags
->stOrderBy
.bCriteriaRev
[i
])
1480 /* If that criteria was enough for distinguishing
1481 the files/dirs,there is no need to calculate the others*/
1482 if (iComp
!= 0) break;
1485 /* Translate the value of iComp to boolean */
1495 * Sort files by the order criterias using quicksort method
1498 QsortFiles(LPWIN32_FIND_DATA ptrArray
[], /* [IN/OUT] The array with file info pointers */
1499 int i
, /* [IN] The index of first item in array */
1500 int j
, /* [IN] The index to last item in array */
1501 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags that we will use to sort */
1503 LPWIN32_FIND_DATA lpTemp
; /* A temporary pointer */
1504 int First
, Last
, Temp
;
1514 if (Way
== CompareFiles(ptrArray
[i
], ptrArray
[j
], lpFlags
))
1516 /* Swap the pointers of the array */
1517 lpTemp
= ptrArray
[i
];
1518 ptrArray
[i
]= ptrArray
[j
];
1519 ptrArray
[j
] = lpTemp
;
1521 /* Swap the indexes for inverting sorting */
1532 QsortFiles(ptrArray
,First
, i
-1, lpFlags
);
1533 QsortFiles(ptrArray
,i
+1,Last
, lpFlags
);
1542 * The functions that does everything except for printing results
1545 DirList(LPTSTR szPath
, /* [IN] The path that dir starts */
1546 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags of the listing */
1548 HANDLE hSearch
; /* The handle of the search */
1549 HANDLE hRecSearch
; /* The handle for searching recursivly */
1550 WIN32_FIND_DATA wfdFileInfo
; /* The info of file that found */
1551 LPWIN32_FIND_DATA
* ptrFileArray
; /* An array of pointers with all the files */
1552 PDIRFINDLISTNODE ptrStartNode
; /* The pointer to the first node */
1553 PDIRFINDLISTNODE ptrNextNode
; /* A pointer used for relatives refernces */
1554 TCHAR szFullPath
[MAX_PATH
]; /* The full path that we are listing with trailing \ */
1555 TCHAR szSubPath
[MAX_PATH
];
1557 DWORD dwCount
; /* A counter of files found in directory */
1558 DWORD dwCountFiles
; /* Counter for files */
1559 DWORD dwCountDirs
; /* Counter for directories */
1560 ULARGE_INTEGER u64CountBytes
; /* Counter for bytes */
1561 ULARGE_INTEGER u64Temp
; /* A temporary counter */
1563 /* Initialize Variables */
1564 ptrStartNode
= NULL
;
1569 u64CountBytes
.QuadPart
= 0;
1571 /* Create szFullPath */
1572 if (GetFullPathName(szPath
, sizeof(szFullPath
) / sizeof(TCHAR
), szFullPath
, &pszFilePart
) == 0)
1574 _tcscpy (szFullPath
, szPath
);
1575 if (szFullPath
[_tcslen(szFullPath
) - 1] != _T('\\'))
1576 _tcscat (szFullPath
, _T("\\"));
1580 /* If no wildcard or file was specified and this is a directory, then
1581 display all files in it */
1582 wfdFileInfo
.dwFileAttributes
= GetFileAttributes(szFullPath
);
1583 if (wfdFileInfo
.dwFileAttributes
!= INVALID_FILE_ATTRIBUTES
&&
1584 (wfdFileInfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1586 _tcscat(szFullPath
, _T("\\*"));
1589 /* Prepare the linked list, first node is allocated */
1590 ptrStartNode
= cmd_alloc(sizeof(DIRFINDLISTNODE
));
1591 if (ptrStartNode
== NULL
)
1593 WARN("DEBUG: Cannot allocate memory for ptrStartNode!\n");
1594 return 1; /* Error cannot allocate memory for 1st object */
1596 ptrNextNode
= ptrStartNode
;
1598 /* Collect the results for the current folder */
1599 hSearch
= FindFirstFile(szFullPath
, &wfdFileInfo
);
1602 if (hSearch
!= INVALID_HANDLE_VALUE
)
1604 /* Here we filter all the specified attributes */
1605 if ((wfdFileInfo
.dwFileAttributes
& lpFlags
->stAttribs
.dwAttribMask
)
1606 == (lpFlags
->stAttribs
.dwAttribMask
& lpFlags
->stAttribs
.dwAttribVal
))
1608 ptrNextNode
->ptrNext
= cmd_alloc(sizeof(DIRFINDLISTNODE
));
1609 if (ptrNextNode
->ptrNext
== NULL
)
1611 WARN("DEBUG: Cannot allocate memory for ptrNextNode->ptrNext!\n");
1612 while (ptrStartNode
)
1614 ptrNextNode
= ptrStartNode
->ptrNext
;
1615 cmd_free(ptrStartNode
);
1616 ptrStartNode
= ptrNextNode
;
1622 /* If cmd_alloc fails we go to next file in hope it works,
1623 without braking the linked list! */
1624 if (ptrNextNode
->ptrNext
)
1626 /* Copy the info of search at linked list */
1627 memcpy(&ptrNextNode
->ptrNext
->stFindInfo
,
1629 sizeof(WIN32_FIND_DATA
));
1631 /* If lower case is selected do it here */
1632 if (lpFlags
->bLowerCase
)
1634 _tcslwr(ptrNextNode
->ptrNext
->stFindInfo
.cAlternateFileName
);
1635 _tcslwr(ptrNextNode
->ptrNext
->stFindInfo
.cFileName
);
1638 /* Continue at next node at linked list */
1639 ptrNextNode
= ptrNextNode
->ptrNext
;
1642 /* Grab statistics */
1643 if (wfdFileInfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1652 u64Temp
.HighPart
= wfdFileInfo
.nFileSizeHigh
;
1653 u64Temp
.LowPart
= wfdFileInfo
.nFileSizeLow
;
1654 u64CountBytes
.QuadPart
+= u64Temp
.QuadPart
;
1659 } while(FindNextFile(hSearch
, &wfdFileInfo
));
1662 /* Terminate list */
1663 ptrNextNode
->ptrNext
= NULL
;
1665 /* Calculate and allocate space need for making an array of pointers */
1666 ptrFileArray
= cmd_alloc(sizeof(LPWIN32_FIND_DATA
) * dwCount
);
1667 if (ptrFileArray
== NULL
)
1669 WARN("DEBUG: Cannot allocate memory for ptrFileArray!\n");
1670 while (ptrStartNode
)
1672 ptrNextNode
= ptrStartNode
->ptrNext
;
1673 cmd_free(ptrStartNode
);
1674 ptrStartNode
= ptrNextNode
;
1681 * Create an array of pointers from the linked list
1682 * this will be used to sort and print data, rather than the list
1684 ptrNextNode
= ptrStartNode
;
1686 while (ptrNextNode
->ptrNext
)
1688 *(ptrFileArray
+ dwCount
) = &ptrNextNode
->ptrNext
->stFindInfo
;
1689 ptrNextNode
= ptrNextNode
->ptrNext
;
1693 /* Sort Data if requested*/
1694 if (lpFlags
->stOrderBy
.sCriteriaCount
> 0)
1695 QsortFiles(ptrFileArray
, 0, dwCount
-1, lpFlags
);
1698 DirPrintFiles(ptrFileArray
, dwCount
, szFullPath
, lpFlags
);
1700 if (lpFlags
->bRecursive
)
1702 PrintSummary(szFullPath
,
1711 cmd_free(ptrFileArray
);
1712 if (CheckCtrlBreak(BREAK_INPUT
))
1716 /* Add statistics to recursive statistics*/
1717 recurse_dir_cnt
+= dwCountDirs
;
1718 recurse_file_cnt
+= dwCountFiles
;
1719 recurse_bytes
.QuadPart
+= u64CountBytes
.QuadPart
;
1721 /* Do the recursive job if requested
1722 the recursive is be done on ALL(indepent of their attribs)
1723 directoried of the current one.*/
1724 if (lpFlags
->bRecursive
)
1726 /* The new search is involving any *.* file */
1727 if (pszFilePart
!= NULL
)
1729 memcpy(szSubPath
, szFullPath
, (pszFilePart
- szFullPath
) * sizeof(TCHAR
));
1730 szSubPath
[pszFilePart
- szFullPath
] = _T('\0');
1733 _tcscpy(szSubPath
, szFullPath
);
1735 _tcscat(szSubPath
, _T("*.*"));
1737 hRecSearch
= FindFirstFile (szSubPath
, &wfdFileInfo
);
1740 if (hRecSearch
!= INVALID_HANDLE_VALUE
)
1742 /* We search for directories other than "." and ".." */
1743 if ((_tcsicmp(wfdFileInfo
.cFileName
, _T(".")) != 0) &&
1744 (_tcsicmp(wfdFileInfo
.cFileName
, _T("..")) != 0 ) &&
1745 (wfdFileInfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1747 /* Concat the path and the directory to do recursive */
1748 if (pszFilePart
!= NULL
)
1750 memcpy(szSubPath
, szFullPath
, (pszFilePart
- szFullPath
) * sizeof(TCHAR
));
1751 szSubPath
[pszFilePart
- szFullPath
] = _T('\0');
1754 _tcscpy(szSubPath
, szFullPath
);
1756 _tcscat(szSubPath
, wfdFileInfo
.cFileName
);
1757 _tcscat(szSubPath
, _T("\\"));
1758 if (pszFilePart
!= NULL
)
1759 _tcscat(szSubPath
, pszFilePart
);
1761 /* We do the same for the folder */
1762 if (DirList(szSubPath
, lpFlags
) != 0)
1768 }while(FindNextFile(hRecSearch
,&wfdFileInfo
));
1769 FindClose(hRecSearch
);
1772 /* Free linked list */
1773 while (ptrStartNode
)
1775 ptrNextNode
= ptrStartNode
->ptrNext
;
1776 cmd_free(ptrStartNode
);
1777 ptrStartNode
= ptrNextNode
;
1789 * internal dir command
1792 CommandDir(LPTSTR rest
)
1794 TCHAR dircmd
[256]; /* A variable to store the DIRCMD enviroment variable */
1795 TCHAR path
[MAX_PATH
];
1796 TCHAR prev_volume
[MAX_PATH
];
1797 LPTSTR
* params
= NULL
;
1801 DIRSWITCHFLAGS stFlags
;
1805 /* Initialize Switch Flags < Default switches are setted here!> */
1806 stFlags
.b4Digit
= TRUE
;
1807 stFlags
.bBareFormat
= FALSE
;
1808 stFlags
.bLowerCase
= FALSE
;
1809 stFlags
.bNewLongList
= TRUE
;
1810 stFlags
.bPause
= FALSE
;
1811 stFlags
.bRecursive
= FALSE
;
1812 stFlags
.bShortName
= FALSE
;
1813 stFlags
.bTSeperator
= TRUE
;
1814 stFlags
.bUser
= FALSE
;
1815 stFlags
.bWideList
= FALSE
;
1816 stFlags
.bWideListColSort
= FALSE
;
1817 stFlags
.stTimeField
.eTimeField
= TF_MODIFIEDDATE
;
1818 stFlags
.stTimeField
.bUnSet
= FALSE
;
1819 stFlags
.stAttribs
.dwAttribMask
= FILE_ATTRIBUTE_HIDDEN
| FILE_ATTRIBUTE_SYSTEM
;
1820 stFlags
.stAttribs
.dwAttribVal
= 0L;
1821 stFlags
.stAttribs
.bUnSet
= FALSE
;
1822 stFlags
.stOrderBy
.sCriteriaCount
= 0;
1823 stFlags
.stOrderBy
.bUnSet
= FALSE
;
1827 /* read the parameters from the DIRCMD environment variable */
1828 if (GetEnvironmentVariable (_T("DIRCMD"), dircmd
, 256))
1829 if (!DirReadParam(dircmd
, ¶ms
, &entries
, &stFlags
))
1835 /* read the parameters */
1836 if (!DirReadParam(rest
, ¶ms
, &entries
, &stFlags
) || CheckCtrlBreak(BREAK_INPUT
))
1842 /* default to current directory */
1844 if(!add_entry(&entries
, ¶ms
, _T("*"))) {
1850 prev_volume
[0] = _T('\0');
1852 for(loop
= 0; loop
< (UINT
)entries
; loop
++)
1854 if (CheckCtrlBreak(BREAK_INPUT
))
1860 recurse_dir_cnt
= 0L;
1861 recurse_file_cnt
= 0L;
1862 recurse_bytes
.QuadPart
= 0;
1865 Uncomment this to show the final state of switch flags*/
1868 TRACE("Attributes mask/value %x/%x\n",stFlags
.stAttribs
.dwAttribMask
,stFlags
.stAttribs
.dwAttribVal
);
1869 TRACE("(B) Bare format : %i\n", stFlags
.bBareFormat
);
1870 TRACE("(C) Thousand : %i\n", stFlags
.bTSeperator
);
1871 TRACE("(W) Wide list : %i\n", stFlags
.bWideList
);
1872 TRACE("(D) Wide list sort by column : %i\n", stFlags
.bWideListColSort
);
1873 TRACE("(L) Lowercase : %i\n", stFlags
.bLowerCase
);
1874 TRACE("(N) New : %i\n", stFlags
.bNewLongList
);
1875 TRACE("(O) Order : %i\n", stFlags
.stOrderBy
.sCriteriaCount
);
1876 for (i
=0;i
<stFlags
.stOrderBy
.sCriteriaCount
;i
++)
1877 TRACE(" Order Criteria [%i]: %i (Reversed: %i)\n",i
, stFlags
.stOrderBy
.eCriteria
[i
], stFlags
.stOrderBy
.bCriteriaRev
[i
] );
1878 TRACE("(P) Pause : %i\n", stFlags
.bPause
);
1879 TRACE("(Q) Owner : %i\n", stFlags
.bUser
);
1880 TRACE("(S) Recursive : %i\n", stFlags
.bRecursive
);
1881 TRACE("(T) Time field : %i\n", stFlags
.stTimeField
.eTimeField
);
1882 TRACE("(X) Short names : %i\n", stFlags
.bShortName
);
1883 TRACE("Parameter : %s\n", debugstr_aw(params
[loop
]) );
1886 /* Print the drive header if the volume changed */
1887 ChangedVolume
= TRUE
;
1889 if (!stFlags
.bBareFormat
&&
1890 GetVolumePathName(params
[loop
], path
, sizeof(path
) / sizeof(TCHAR
)))
1892 if (!_tcscmp(path
, prev_volume
))
1893 ChangedVolume
= FALSE
;
1895 _tcscpy(prev_volume
, path
);
1897 else if (GetFullPathName(params
[loop
], sizeof(path
) / sizeof(TCHAR
), path
, &pszFilePart
) != 0)
1899 if (pszFilePart
!= NULL
)
1900 *pszFilePart
= _T('\0');
1903 _tcscpy(path
, params
[loop
]);
1905 if (ChangedVolume
&& !stFlags
.bBareFormat
) {
1906 if (!PrintDirectoryHeader (params
[loop
], &stFlags
)) {
1912 /* do the actual dir */
1913 if (DirList (params
[loop
], &stFlags
))
1919 /* print the footer */