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 <ekohl@abo.rhein-zeitung.de>)
94 * Converted source code to Win32, except recursive dir ("dir /s").
96 * 10-Dec-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
97 * Fixed recursive dir ("dir /s").
99 * 14-Dec-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
100 * Converted to Win32 directory functions and
101 * fixed some output bugs. There are still some more ;)
103 * 10-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
104 * Added "/N" and "/4" options, "/O" is a dummy.
105 * Added locale support.
107 * 20-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
110 * 01-Mar-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
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
131 #include "resource.h"
133 #ifdef INCLUDE_CMD_DIR
137 /* Time Field enumeration */
142 TF_LASTACCESSEDDATE
= 2
145 /* Ordered by enumeration */
155 /* The struct for holding the switches */
156 typedef struct _DirSwitchesFlags
158 BOOL bBareFormat
; /* Bare Format */
159 BOOL bTSeperator
; /* Thousands seperator */
160 BOOL bWideList
; /* Wide list format */
161 BOOL bWideListColSort
; /* Wide list format but sorted by column */
162 BOOL bLowerCase
; /* Uses lower case */
163 BOOL bNewLongList
; /* New long list */
164 BOOL bPause
; /* Pause per page */
165 BOOL bUser
; /* Displays the owner of file */
166 BOOL bRecursive
; /* Displays files in specified directory and all sub */
167 BOOL bShortName
; /* Displays the sort name of files if exist */
168 BOOL b4Digit
; /* Four digit year */
171 DWORD dwAttribVal
; /* The desired state of attribute */
172 DWORD dwAttribMask
; /* Which attributes to check */
173 BOOL bUnSet
; /* A helper flag if "-" was given with the switch */
174 BOOL bParSetted
; /* A helper flag if parameters of switch were given */
175 } stAttribs
; /* Displays files with this attributes only */
178 enum EOrderBy eCriteria
[3]; /* Criterias used to order by */
179 BOOL bCriteriaRev
[3]; /* If the criteria is in reversed order */
180 short sCriteriaCount
; /* The quantity of criterias */
181 BOOL bUnSet
; /* A helper flag if "-" was given with the switch */
182 BOOL bParSetted
; /* A helper flag if parameters of switch were given */
183 } stOrderBy
; /* Ordered by criterias */
186 enum ETimeField eTimeField
; /* The time field that will be used for */
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 } stTimeField
; /* The time field to display or use for sorting */
190 } DIRSWITCHFLAGS
, *LPDIRSWITCHFLAGS
;
193 typedef struct _DIRFINDLISTNODE
195 WIN32_FIND_DATA stFindInfo
;
196 struct _DIRFINDLISTNODE
*ptrNext
;
197 } DIRFINDLISTNODE
, *PDIRFINDLISTNODE
;
201 (*PGETFREEDISKSPACEEX
)(LPCTSTR
, PULARGE_INTEGER
, PULARGE_INTEGER
, PULARGE_INTEGER
);
204 /* Globally save the # of dirs, files and bytes,
205 * probabaly later pass them to functions. Rob Lake */
206 static ULONG recurse_dir_cnt
;
207 static ULONG recurse_file_cnt
;
208 static ULARGE_INTEGER recurse_bytes
;
214 * displays help screen for dir
217 static VOID
DirHelp(VOID
)
219 ConOutResPuts(STRING_DIR_HELP1
);
227 * Parse the parameters and switches of the command line and exports them
230 DirReadParam(LPTSTR Line
, /* [IN] The line with the parameters & switches */
231 LPTSTR
*param
, /* [OUT] The parameters after parsing */
232 LPDIRSWITCHFLAGS lpFlags
) /* [IN/OUT] The flags after calculating switches */
234 TCHAR cCurSwitch
; /* The current switch */
235 TCHAR cCurChar
; /* Current examing character */
236 TCHAR cCurUChar
; /* Current upper examing character */
237 BOOL bNegative
; /* Negative switch */
238 BOOL bPNegative
; /* Negative switch parameter */
239 BOOL bIntoQuotes
; /* A flag showing if we are in quotes (") */
240 LPTSTR ptrLast
; /* A pointer to the last character of param */
242 /* Initialize variables; */
243 cCurSwitch
= _T(' ');
248 /* No parameters yet */
252 /* We suppose that switch parameters
253 were given to avoid setting them to default
254 if the switch was not given */
255 lpFlags
->stAttribs
.bParSetted
= TRUE
;
256 lpFlags
->stOrderBy
.bParSetted
= TRUE
;
257 lpFlags
->stTimeField
.bParSetted
= TRUE
;
259 /* Main Loop (see README_DIR.txt) */
260 /* scan the command line char per char, and we process its char */
263 /* we save current character as it is and its upper case */
265 cCurUChar
= _totupper(*Line
);
267 /* 1st section (see README_DIR.txt) */
268 /* When a switch is expecting */
269 if (cCurSwitch
== _T('/'))
271 if ((cCurUChar
== _T('A')) ||(cCurUChar
== _T('T')) || (cCurUChar
== _T('O')))
273 cCurSwitch
= cCurUChar
;
277 lpFlags
->stAttribs
.bUnSet
= bNegative
;
278 lpFlags
->stAttribs
.bParSetted
= FALSE
;
281 lpFlags
->stTimeField
.bUnSet
= bNegative
;
282 lpFlags
->stTimeField
.bParSetted
= FALSE
;
285 lpFlags
->stOrderBy
.bUnSet
= bNegative
;
286 lpFlags
->stOrderBy
.bParSetted
= FALSE
;
290 else if (cCurUChar
== _T('L'))
291 lpFlags
->bLowerCase
= ! bNegative
;
292 else if (cCurUChar
== _T('B'))
293 lpFlags
->bBareFormat
= ! bNegative
;
294 else if (cCurUChar
== _T('C'))
295 lpFlags
->bTSeperator
= ! bNegative
;
296 else if (cCurUChar
== _T('W'))
297 lpFlags
->bWideList
= ! bNegative
;
298 else if (cCurUChar
== _T('D'))
299 lpFlags
->bWideListColSort
= ! bNegative
;
300 else if (cCurUChar
== _T('N'))
301 lpFlags
->bNewLongList
= ! bNegative
;
302 else if (cCurUChar
== _T('P'))
303 lpFlags
->bPause
= ! bNegative
;
304 else if (cCurUChar
== _T('Q'))
305 lpFlags
->bUser
= ! bNegative
;
306 else if (cCurUChar
== _T('S'))
307 lpFlags
->bRecursive
= ! bNegative
;
308 else if (cCurUChar
== _T('X'))
309 lpFlags
->bShortName
= ! bNegative
;
310 else if (cCurChar
== _T('4'))
311 lpFlags
->b4Digit
= ! bNegative
;
312 else if (cCurChar
== _T('?'))
317 else if (cCurChar
== _T('-'))
323 error_invalid_switch ((TCHAR
)_totupper (*Line
));
327 /* We check if we calculated the negative value and realese the flag */
328 if ((cCurChar
!= _T('-')) && bNegative
)
331 /* if not a,o,t or - option then next parameter is not a switch */
332 if ((cCurSwitch
== _T('/')) && (!bNegative
))
333 cCurSwitch
= _T(' ');
336 else if ((cCurSwitch
== _T(' ')) || (cCurSwitch
== _T('P')))
338 /* 2nd section (see README_DIR.txt) */
339 /* We are expecting parameter or the unknown */
341 if (cCurChar
== _T('/'))
342 cCurSwitch
= _T('/');
344 /* Process a spacer */
345 else if (cCurChar
== _T(' '))
349 cCurSwitch
= _T(' ');
350 if ((*param
) && !(ptrLast
))
355 else if (cCurChar
== _T('\"'))
357 /* Process a quote */
358 bIntoQuotes
= !bIntoQuotes
;
359 if (!bIntoQuotes
) ptrLast
= Line
;
363 /* Process a character for parameter */
364 if ((cCurSwitch
== _T(' ')) && (*param
))
366 error_too_many_parameters(Line
);
369 cCurSwitch
= _T('P');
376 /* 3rd section (see README_DIR.txt) */
377 /* We are waiting for switch parameters */
379 /* Check if there are no more switch parameters */
380 if ((cCurChar
== _T('/')) || ( cCurChar
== _T(' ')))
382 /* Wrong desicion path, reprocess current character */
383 cCurSwitch
= cCurChar
;
386 /* Process parameter switch */
389 case _T('A'): /* Switch parameters for /A (attributes filter) */
390 /* Ok a switch parameter was given */
391 lpFlags
->stAttribs
.bParSetted
= TRUE
;
393 if (cCurChar
== _T(':'))
394 /* =V= dead command, used to make the "if" work */
396 else if(cCurChar
== _T('-'))
398 else if(cCurUChar
== _T('D'))
400 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_DIRECTORY
;
402 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_DIRECTORY
;
404 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_DIRECTORY
;
406 else if(cCurUChar
== _T('R'))
408 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_READONLY
;
410 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_READONLY
;
412 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_READONLY
;
414 else if(cCurUChar
== _T('H'))
416 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_HIDDEN
;
418 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_HIDDEN
;
420 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_HIDDEN
;
422 else if(cCurUChar
== _T('A'))
424 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_ARCHIVE
;
426 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_ARCHIVE
;
428 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_ARCHIVE
;
430 else if(cCurUChar
== _T('S'))
432 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_SYSTEM
;
434 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_SYSTEM
;
436 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_SYSTEM
;
440 error_parameter_format((TCHAR
)_totupper (*Line
));
444 case _T('T'): /* Switch parameters for /T (time field) */
446 /* Ok a switch parameter was given */
447 lpFlags
->stTimeField
.bParSetted
= TRUE
;
449 if (cCurChar
== _T(':'))
450 /* =V= dead command, used to make the "if" work */
452 else if(cCurUChar
== _T('C'))
453 lpFlags
->stTimeField
.eTimeField
= TF_CREATIONDATE
;
454 else if(cCurUChar
== _T('A'))
455 lpFlags
->stTimeField
.eTimeField
= TF_LASTACCESSEDDATE
;
456 else if(cCurUChar
== _T('W'))
457 lpFlags
->stTimeField
.eTimeField
= TF_MODIFIEDDATE
;
460 error_parameter_format((TCHAR
)_totupper (*Line
));
464 case _T('O'): /* Switch parameters for /O (order) */
465 /* Ok a switch parameter was given */
466 lpFlags
->stOrderBy
.bParSetted
= TRUE
;
468 if (cCurChar
== _T(':'))
469 /* <== dead command, used to make the "if" work */
471 else if(cCurChar
== _T('-'))
473 else if(cCurUChar
== _T('N'))
475 if (lpFlags
->stOrderBy
.sCriteriaCount
< 3) lpFlags
->stOrderBy
.sCriteriaCount
++;
476 lpFlags
->stOrderBy
.bCriteriaRev
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = bPNegative
;
477 lpFlags
->stOrderBy
.eCriteria
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = ORDER_NAME
;
479 else if(cCurUChar
== _T('S'))
481 if (lpFlags
->stOrderBy
.sCriteriaCount
< 3) lpFlags
->stOrderBy
.sCriteriaCount
++;
482 lpFlags
->stOrderBy
.bCriteriaRev
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = bPNegative
;
483 lpFlags
->stOrderBy
.eCriteria
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = ORDER_SIZE
;
485 else if(cCurUChar
== _T('G'))
487 if (lpFlags
->stOrderBy
.sCriteriaCount
< 3) lpFlags
->stOrderBy
.sCriteriaCount
++;
488 lpFlags
->stOrderBy
.bCriteriaRev
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = bPNegative
;
489 lpFlags
->stOrderBy
.eCriteria
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = ORDER_DIRECTORY
;
491 else if(cCurUChar
== _T('E'))
493 if (lpFlags
->stOrderBy
.sCriteriaCount
< 3) lpFlags
->stOrderBy
.sCriteriaCount
++;
494 lpFlags
->stOrderBy
.bCriteriaRev
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = bPNegative
;
495 lpFlags
->stOrderBy
.eCriteria
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = ORDER_EXTENSION
;
497 else if(cCurUChar
== _T('D'))
499 if (lpFlags
->stOrderBy
.sCriteriaCount
< 3) lpFlags
->stOrderBy
.sCriteriaCount
++;
500 lpFlags
->stOrderBy
.bCriteriaRev
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = bPNegative
;
501 lpFlags
->stOrderBy
.eCriteria
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = ORDER_TIME
;
506 error_parameter_format((TCHAR
)_totupper (*Line
));
512 /* We check if we calculated the negative value and realese the flag */
513 if ((cCurChar
!= _T('-')) && bPNegative
)
519 /* Terminate the parameters */
520 if (ptrLast
) *ptrLast
= 0;
522 /* Calculate the switches with no switch paramater */
523 if (!(lpFlags
->stAttribs
.bParSetted
))
525 lpFlags
->stAttribs
.dwAttribVal
= 0L;
526 lpFlags
->stAttribs
.dwAttribMask
= lpFlags
->stAttribs
.dwAttribVal
;
528 if (!(lpFlags
->stOrderBy
.bParSetted
))
530 lpFlags
->stOrderBy
.sCriteriaCount
= 1;
531 lpFlags
->stOrderBy
.eCriteria
[0] = ORDER_NAME
;
532 lpFlags
->stOrderBy
.bCriteriaRev
[0] = FALSE
;
534 if (!(lpFlags
->stOrderBy
.bParSetted
))
535 lpFlags
->stTimeField
.eTimeField
= TF_MODIFIEDDATE
;
537 /* Calculate the unsetted switches (the "-" prefixed)*/
538 if (lpFlags
->stAttribs
.bUnSet
)
540 lpFlags
->stAttribs
.bUnSet
= FALSE
;
541 lpFlags
->stAttribs
.dwAttribVal
= 0L;
542 lpFlags
->stAttribs
.dwAttribMask
= FILE_ATTRIBUTE_HIDDEN
| FILE_ATTRIBUTE_SYSTEM
;
544 if (lpFlags
->stOrderBy
.bUnSet
)
546 lpFlags
->stOrderBy
.bUnSet
= FALSE
;
547 lpFlags
->stOrderBy
.sCriteriaCount
= 0;
549 if (lpFlags
->stTimeField
.bUnSet
)
551 lpFlags
->stTimeField
.bUnSet
= FALSE
;
552 lpFlags
->stTimeField
.eTimeField
= TF_MODIFIEDDATE
;
561 * extend the filespec, possibly adding wildcards
564 ExtendFilespec (LPTSTR file
)
572 /* if no file spec, change to "*.*" */
573 if (*file
== _T('\0'))
575 _tcscpy (file
, _T("*.*"));
579 // add support for *.
580 if ((file
[0] == _T('*')) && (file
[1] == _T('.') ))
585 /* if starts with . add * in front */
586 if (*file
== _T('.'))
588 memmove (&file
[1], &file
[0], (_tcslen (file
) + 1) * sizeof(TCHAR
));
593 if (!_tcschr (file
, _T('.')))
595 _tcscat (file
, _T(".*"));
601 /* if last character is '.' add '*' */
602 len
= _tcslen (file
);
603 if (file
[len
- 1] == _T('.'))
605 _tcscat (file
, _T("*"));
614 * split the pathspec into drive, directory, and filespec
617 DirParsePathspec (LPTSTR szPathspec
, LPTSTR szPath
, LPTSTR szFilespec
)
619 TCHAR szOrigPath
[MAX_PATH
];
623 BOOL bWildcards
= FALSE
;
625 GetCurrentDirectory (MAX_PATH
, szOrigPath
);
627 /* get the drive and change to it */
628 if (szPathspec
[1] == _T(':'))
630 TCHAR szRootPath
[] = _T("A:");
632 szRootPath
[0] = szPathspec
[0];
633 start
= szPathspec
+ 2;
634 if (!SetCurrentDirectory (szRootPath
))
636 ErrorMessage (GetLastError(), NULL
);
646 /* check for wildcards */
647 for (i
= 0; szPathspec
[i
]; i
++)
649 if (szPathspec
[i
] == _T('*') || szPathspec
[i
] == _T('?'))
653 /* check if this spec is a directory */
656 if (SetCurrentDirectory (szPathspec
))
658 _tcscpy (szFilespec
, _T("*.*"));
660 if (!GetCurrentDirectory (MAX_PATH
, szPath
))
662 szFilespec
[0] = _T('\0');
663 SetCurrentDirectory (szOrigPath
);
664 error_out_of_memory();
668 SetCurrentDirectory (szOrigPath
);
673 /* find the file spec */
674 tmp
= _tcsrchr (start
, _T('\\'));
676 /* if no path is specified */
679 _tcscpy (szFilespec
, start
);
680 ExtendFilespec (szFilespec
);
681 if (!GetCurrentDirectory (MAX_PATH
, szPath
))
683 szFilespec
[0] = _T('\0');
684 SetCurrentDirectory (szOrigPath
);
685 error_out_of_memory();
689 SetCurrentDirectory (szOrigPath
);
693 /* get the filename */
694 _tcscpy (szFilespec
, tmp
+1);
695 ExtendFilespec (szFilespec
);
699 /* change to the root directory */
700 if (!SetCurrentDirectory (_T("\\")))
702 szFilespec
[0] = _T('\0');
703 SetCurrentDirectory (szOrigPath
);
704 error_path_not_found ();
712 /* change to this directory */
713 if (!SetCurrentDirectory (start
))
716 szFilespec
[0] = _T('\0');
717 SetCurrentDirectory (szOrigPath
);
718 error_path_not_found ();
723 /* get the full name of the directory */
724 if (!GetCurrentDirectory (MAX_PATH
, szPath
))
727 szFilespec
[0] = _T('\0');
728 SetCurrentDirectory (szOrigPath
);
729 error_out_of_memory ();
735 SetCurrentDirectory (szOrigPath
);
744 * increment our line if paginating, display message at end of screen
748 IncLine (LPINT pLine
, LPDIRSWITCHFLAGS lpFlags
)
751 CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo
;
754 bError
= GetConsoleScreenBufferInfo(hConsole
, &lpConsoleScreenBufferInfo
);
756 WindowHeight
= lpConsoleScreenBufferInfo
.srWindow
.Bottom
- lpConsoleScreenBufferInfo
.srWindow
.Top
;
758 /* That prevents bad behiour if WindowHeight could not be calculated */
761 WindowHeight
= 1000000;
764 if (!(lpFlags
->bPause
))
770 * Because I don't know if WindowsHeight work in all cases,
771 * perhaps then maxy is the right value
773 if (*pLine
>= (int)maxy
- 2 || *pLine
>= WindowHeight
)
776 return (PagePrompt () == PROMPT_BREAK
);
784 * PrintDirectoryHeader
786 * print the header for the dir command
789 PrintDirectoryHeader(LPTSTR szPath
, LPINT pLine
, LPDIRSWITCHFLAGS lpFlags
)
791 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
792 TCHAR szRootName
[MAX_PATH
];
797 if (lpFlags
->bBareFormat
)
800 /* build usable root path */
801 if (szPath
[1] == _T(':') && szPath
[2] == _T('\\'))
804 szRootName
[0] = szPath
[0];
805 szRootName
[1] = _T(':');
806 szRootName
[2] = _T('\\');
809 else if (szPath
[0] == _T('\\') && szPath
[1] == _T('\\'))
812 p
= _tcschr(&szPath
[2], _T('\\'));
815 error_invalid_drive();
818 p
= _tcschr(p
+1, _T('\\'));
821 _tcscpy(szRootName
, szPath
);
822 _tcscat(szRootName
, _T("\\"));
827 _tcscpy(szRootName
, szPath
);
828 _tcscat(szRootName
, _T("\\"));
834 error_invalid_drive();
838 /* get the media ID of the drive */
839 if (!GetVolumeInformation(szRootName
, szVolName
, 80, &dwSerialNr
,
840 NULL
, NULL
, NULL
, 0))
842 error_invalid_drive();
846 /* print drive info */
847 if (szVolName
[0] != _T('\0'))
849 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP2
, szMsg
, RC_STRING_MAX_SIZE
);
850 ConOutPrintf((LPTSTR
)szMsg
, szRootName
[0], szVolName
);
854 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP3
, szMsg
, RC_STRING_MAX_SIZE
);
855 ConOutPrintf(szMsg
, szRootName
[0]);
858 /* print the volume serial number if the return was successful */
859 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP4
, (LPTSTR
) szMsg
, RC_STRING_MAX_SIZE
);
860 ConOutPrintf((LPTSTR
)szMsg
,
871 * insert commas into a number
876 ConvertULong (ULONG num
, LPTSTR des
, INT len
)
893 if (((c
+ 1) % (nNumberGroups
+ 1)) == 0)
894 temp
[30 - c
++] = cThousandSeparator
;
895 temp
[30 - c
++] = (TCHAR
)(num
% 10) + _T('0');
899 for (n
= 0; n
<= c
; n
++)
900 des
[n
] = temp
[31 - c
+ n
];
908 ConvertULargeInteger (ULARGE_INTEGER num
, LPTSTR des
, INT len
, BOOL bPutSeperator
)
914 if (num
.QuadPart
== 0)
923 while (num
.QuadPart
> 0)
925 if ((((c
+ 1) % (nNumberGroups
+ 1)) == 0) && (bPutSeperator
))
926 temp
[30 - c
++] = cThousandSeparator
;
927 temp
[30 - c
++] = (TCHAR
)(num
.QuadPart
% 10) + _T('0');
931 for (n
= 0; n
<= c
; n
++)
932 des
[n
] = temp
[31 - c
+ n
];
940 DirPrintFileDateTime(TCHAR
*lpDate
,
942 LPWIN32_FIND_DATA lpFile
,
943 LPDIRSWITCHFLAGS lpFlags
)
951 /* Select the right time field */
952 switch (lpFlags
->stTimeField
.eTimeField
)
954 case TF_CREATIONDATE
:
955 if (!FileTimeToLocalFileTime(&lpFile
->ftCreationTime
, &ft
))
957 FileTimeToSystemTime(&ft
, &dt
);
960 case TF_LASTACCESSEDDATE
:
961 if (!FileTimeToLocalFileTime(&lpFile
->ftLastAccessTime
, &ft
))
963 FileTimeToSystemTime(&ft
, &dt
);
966 case TF_MODIFIEDDATE
:
967 if (!FileTimeToLocalFileTime(&lpFile
->ftLastWriteTime
, &ft
))
969 FileTimeToSystemTime(&ft
, &dt
);
974 wYear
= (lpFlags
->b4Digit
) ? dt
.wYear
: dt
.wYear
%100;
979 _stprintf (szDate
, _T("%02d%c%02d%c%0*d"),
980 dt
.wMonth
, cDateSeparator
,
981 dt
.wDay
, cDateSeparator
,
982 lpFlags
->b4Digit
?4:2, wYear
);
986 _stprintf (szDate
, _T("%02d%c%02d%c%0*d"),
987 dt
.wDay
, cDateSeparator
, dt
.wMonth
,
988 cDateSeparator
,lpFlags
->b4Digit
?4:2, wYear
);
992 _stprintf (szDate
, _T("%0*d%c%02d%c%02d"),
993 lpFlags
->b4Digit
?4:2, wYear
, cDateSeparator
,
994 dt
.wMonth
, cDateSeparator
, dt
.wDay
);
1000 case 0: /* 12 hour format */
1002 _stprintf (szTime
,_T(" %02d%c%02u%c"),
1003 (dt
.wHour
== 0 ? 12 : (dt
.wHour
<= 12 ? dt
.wHour
: dt
.wHour
- 12)),
1005 dt
.wMinute
, (dt
.wHour
<= 11 ? _T('a') : _T('p')));
1008 case 1: /* 24 hour format */
1009 _stprintf (szTime
, _T(" %02d%c%02u"),
1010 dt
.wHour
, cTimeSeparator
, dt
.wMinute
);
1014 _tcscpy(lpDate
, szDate
);
1015 _tcscpy(lpTime
, szTime
);
1020 GetUserDiskFreeSpace(LPCTSTR lpRoot
,
1021 PULARGE_INTEGER lpFreeSpace
)
1023 PGETFREEDISKSPACEEX pGetFreeDiskSpaceEx
;
1024 HINSTANCE hInstance
;
1029 ULARGE_INTEGER TotalNumberOfBytes
, TotalNumberOfFreeBytes
;
1031 lpFreeSpace
->QuadPart
= 0;
1033 hInstance
= LoadLibrary(_T("KERNEL32"));
1034 if (hInstance
!= NULL
)
1036 pGetFreeDiskSpaceEx
= (PGETFREEDISKSPACEEX
)GetProcAddress(hInstance
,
1038 "GetDiskFreeSpaceExW");
1040 "GetDiskFreeSpaceExA");
1042 if (pGetFreeDiskSpaceEx
!= NULL
)
1044 if (pGetFreeDiskSpaceEx(lpRoot
, lpFreeSpace
, &TotalNumberOfBytes
, &TotalNumberOfFreeBytes
) == TRUE
)
1047 FreeLibrary(hInstance
);
1050 GetDiskFreeSpace(lpRoot
,
1056 lpFreeSpace
->QuadPart
= dwSecPerCl
* dwBytPerSec
* dwFreeCl
;
1061 * print_summary: prints dir summary
1062 * Added by Rob Lake 06/17/98 to compact code
1063 * Just copied Tim's Code and patched it a bit
1067 PrintSummary(LPTSTR szPath
,
1070 ULARGE_INTEGER u64Bytes
,
1072 LPDIRSWITCHFLAGS lpFlags
)
1074 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
1076 ULARGE_INTEGER uliFree
;
1077 TCHAR szRoot
[] = _T("A:\\");
1080 /* Here we check if we didn't find anything */
1081 if (!(ulFiles
+ ulDirs
))
1083 error_file_not_found();
1087 /* In bare format we don't print results */
1088 if (lpFlags
->bBareFormat
)
1091 /* Print recursive specific results */
1092 if (lpFlags
->bRecursive
)
1094 ConvertULargeInteger(u64Bytes
, szBuffer
, sizeof(szBuffer
), lpFlags
->bTSeperator
);
1096 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP5
, szMsg
, RC_STRING_MAX_SIZE
);
1097 ConOutPrintf(szMsg
,ulFiles
, szBuffer
);
1100 /* Print total directories and freespace */
1101 szRoot
[0] = szPath
[0];
1102 GetUserDiskFreeSpace(szRoot
, &uliFree
);
1103 ConvertULargeInteger(uliFree
, szBuffer
, sizeof(szBuffer
), lpFlags
->bTSeperator
);
1104 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP6
, (LPTSTR
) szMsg
, RC_STRING_MAX_SIZE
);
1105 ConOutPrintf((LPTSTR
)szMsg
,ulDirs
, szBuffer
);
1113 * Get the extension of a filename
1115 TCHAR
* getExt(const TCHAR
* file
)
1117 static TCHAR
*NoExt
= _T("");
1118 TCHAR
* lastdot
= _tcsrchr(file
, _T('.'));
1119 return (lastdot
!= NULL
? lastdot
+ 1 : NoExt
);
1125 * Get the name of the file without extension
1127 static LPTSTR
getName(const TCHAR
* file
, TCHAR
* dest
)
1132 /* Check for "." and ".." folders */
1133 if ((_tcscmp(file
, _T(".")) == 0) ||
1134 (_tcscmp(file
, _T("..")) == 0))
1140 end
= _tcsrchr(file
, _T('.'));
1142 iLen
= _tcslen(file
);
1144 iLen
= (end
- file
);
1147 _tcsncpy(dest
, file
, iLen
);
1148 *(dest
+ iLen
) = _T('\0');
1157 * The function that prints in new style
1160 DirPrintNewList(LPWIN32_FIND_DATA ptrFiles
[], /* [IN]Files' Info */
1161 DWORD dwCount
, /* [IN] The quantity of files */
1162 TCHAR
*szCurPath
, /* [IN] Full path of current directory */
1163 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
1167 TCHAR szShortName
[15];
1171 ULARGE_INTEGER u64FileSize
;
1173 for (i
= 0;i
< dwCount
;i
++)
1175 /* Calculate size */
1176 if (ptrFiles
[i
]->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1180 _tcscpy(szSize
, _T("<DIR>"));
1186 u64FileSize
.HighPart
= ptrFiles
[i
]->nFileSizeHigh
;
1187 u64FileSize
.LowPart
= ptrFiles
[i
]->nFileSizeLow
;
1188 ConvertULargeInteger(u64FileSize
, szSize
, 20, lpFlags
->bTSeperator
);
1191 /* Calculate short name */
1192 szShortName
[0] = _T('\0');
1193 if (lpFlags
->bShortName
)
1194 _stprintf(szShortName
, _T(" %-12s"), ptrFiles
[i
]->cAlternateFileName
);
1196 /* Format date and time */
1197 DirPrintFileDateTime(szDate
, szTime
, ptrFiles
[i
], lpFlags
);
1199 /* Print the line */
1200 ConOutPrintf(_T("%10s %-8s %*s%s %s\n"),
1206 ptrFiles
[i
]->cFileName
);
1214 * The function that prints in wide list
1217 DirPrintWideList(LPWIN32_FIND_DATA ptrFiles
[], /* [IN] Files' Info */
1218 DWORD dwCount
, /* [IN] The quantity of files */
1219 TCHAR
*szCurPath
, /* [IN] Full path of current directory */
1220 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
1226 TCHAR szTempFname
[MAX_PATH
];
1231 /* Calculate longest name */
1233 for (i
= 0; i
< dwCount
; i
++)
1235 if (ptrFiles
[i
]->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1237 /* Directories need 2 additinal characters for brackets */
1238 if ((_tcslen(ptrFiles
[i
]->cFileName
) + 2) > iLongestName
)
1239 iLongestName
= _tcslen(ptrFiles
[i
]->cFileName
) + 2;
1243 if (_tcslen(ptrFiles
[i
]->cFileName
) > iLongestName
)
1244 iLongestName
= _tcslen(ptrFiles
[i
]->cFileName
);
1248 /* Count the highest number of columns */
1249 GetScreenSize(&iScreenWidth
, 0);
1250 iColumns
= iScreenWidth
/ iLongestName
;
1252 /* Check if there is enough space for spaces between names */
1253 if (((iLongestName
* iColumns
) + iColumns
) >= iScreenWidth
)
1256 /* A last check at iColumns to avoid division by zero */
1260 /* Print Column sorted */
1261 if (lpFlags
->bWideListColSort
)
1263 /* Calculate the lines that will be printed */
1264 // iLines = ceil((float)dwCount/(float)iColumns);
1265 iLines
= dwCount
/ iColumns
;
1267 for (i
= 0;i
< iLines
;i
++)
1269 for (j
= 0; j
< iColumns
; j
++)
1271 temp
= (j
* iLines
) + i
;
1272 if (temp
>= dwCount
)
1275 if (ptrFiles
[temp
]->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1276 _stprintf(szTempFname
, _T("[%s]"), ptrFiles
[temp
]->cFileName
);
1278 _stprintf(szTempFname
, _T("%s"), ptrFiles
[temp
]->cFileName
);
1280 ConOutPrintf(_T("%-*s"), iLongestName
+ 1 , szTempFname
);
1283 ConOutPrintf(_T("\n"));
1288 /* Print Line sorted */
1289 for (i
= 0; i
< dwCount
; i
++)
1291 if (ptrFiles
[i
]->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1292 _stprintf(szTempFname
, _T("[%s]"), ptrFiles
[i
]->cFileName
);
1294 _stprintf(szTempFname
, _T("%s"), ptrFiles
[i
]->cFileName
);
1296 ConOutPrintf(_T("%-*s"), iLongestName
+ 1, szTempFname
);
1299 * We print a new line at the end of each column
1300 * except for the case that it is the last item.
1302 if (!((i
+ 1) % iColumns
) && (i
< (dwCount
- 1)))
1303 ConOutPrintf(_T("\n"));
1306 /* Add a new line after the last item */
1307 ConOutPrintf(_T("\n"));
1315 * The function that prints in old style
1318 DirPrintOldList(LPWIN32_FIND_DATA ptrFiles
[], /* [IN] Files' Info */
1319 DWORD dwCount
, /* [IN] The quantity of files */
1320 TCHAR
* szCurPath
, /* [IN] Full path of current directory */
1321 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
1323 DWORD i
; /* An indexer for "for"s */
1324 TCHAR szName
[10]; /* The name of file */
1325 TCHAR szExt
[5]; /* The extension of file */
1326 TCHAR szDate
[30],szTime
[30]; /* Used to format time and date */
1327 TCHAR szSize
[30]; /* The size of file */
1328 int iSizeFormat
; /* The format of size field */
1329 ULARGE_INTEGER u64FileSize
; /* The file size */
1331 for(i
= 0;i
< dwCount
;i
++)
1333 /* Broke 8.3 format */
1334 if (*ptrFiles
[i
]->cAlternateFileName
)
1336 /* If the file is long named then we read the alter name */
1337 getName( ptrFiles
[i
]->cAlternateFileName
, szName
);
1338 _tcscpy(szExt
, getExt( ptrFiles
[i
]->cAlternateFileName
));
1342 /* If the file is not long name we read its original name */
1343 getName( ptrFiles
[i
]->cFileName
, szName
);
1344 _tcscpy(szExt
, getExt( ptrFiles
[i
]->cFileName
));
1347 /* Calculate size */
1348 if (ptrFiles
[i
]->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1350 /* Directory, no size it's a directory*/
1352 _tcscpy(szSize
, _T("<DIR>"));
1358 u64FileSize
.HighPart
= ptrFiles
[i
]->nFileSizeHigh
;
1359 u64FileSize
.LowPart
= ptrFiles
[i
]->nFileSizeLow
;
1360 ConvertULargeInteger(u64FileSize
, szSize
, 20, lpFlags
->bTSeperator
);
1363 /* Format date and time */
1364 DirPrintFileDateTime(szDate
,szTime
,ptrFiles
[i
],lpFlags
);
1366 /* Print the line */
1367 ConOutPrintf(_T("%-8s %-3s %*s %s %s\n"),
1368 szName
, /* The file's 8.3 name */
1369 szExt
, /* The file's 8.3 extension */
1370 iSizeFormat
, /* print format for size column */
1371 szSize
, /* The size of file or "<DIR>" for dirs */
1372 szDate
, /* The date of file/dir */
1373 szTime
); /* The time of file/dir */
1380 * The function that prints in bare format
1383 DirPrintBareList(LPWIN32_FIND_DATA ptrFiles
[], /* [IN] Files' Info */
1384 DWORD dwCount
, /* [IN] The number of files */
1385 LPTSTR lpCurPath
, /* [IN] Full path of current directory */
1386 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
1388 TCHAR szFullName
[MAX_PATH
];
1391 for (i
= 0; i
< dwCount
; i
++)
1393 if ((_tcscmp(ptrFiles
[i
]->cFileName
, _T(".")) == 0) ||
1394 (_tcscmp(ptrFiles
[i
]->cFileName
, _T("..")) == 0))
1396 /* at bare format we don't print "." and ".." folder */
1399 if (lpFlags
->bRecursive
)
1401 /* at recursive mode we print full path of file */
1402 _tcscpy(szFullName
, lpCurPath
);
1403 _tcscat(szFullName
, ptrFiles
[i
]->cFileName
);
1404 ConOutPrintf(_T("%s\n"), szFullName
);
1408 /* if we are not in recursive mode we print the file names */
1409 ConOutPrintf(_T("%s\n"),ptrFiles
[i
]->cFileName
);
1418 * The functions that prints the files list
1421 DirPrintFiles(LPWIN32_FIND_DATA ptrFiles
[], /* [IN] Files' Info */
1422 DWORD dwCount
, /* [IN] The quantity of files */
1423 TCHAR
*szCurPath
, /* [IN] Full path of current directory */
1424 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
1426 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
1427 TCHAR szTemp
[MAX_PATH
]; /* A buffer to format the directory header */
1429 /* Print directory header */
1430 _tcscpy(szTemp
, szCurPath
);
1432 /* We cut the trailing \ of the full path */
1433 szTemp
[_tcslen(szTemp
)-1] = _T('\0');
1435 /* Condition to print header:
1436 We are not printing in bare format
1437 and if we are in recursive mode... we must have results */
1438 if (!(lpFlags
->bBareFormat
) && !((lpFlags
->bRecursive
) && (dwCount
<= 0)))
1440 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP7
, szMsg
, RC_STRING_MAX_SIZE
);
1441 ConOutPrintf(szMsg
, szTemp
);
1444 if (lpFlags
->bBareFormat
)
1447 DirPrintBareList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1449 else if(lpFlags
->bShortName
)
1451 /* New list style / Short names */
1452 DirPrintNewList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1454 else if(lpFlags
->bWideListColSort
|| lpFlags
->bWideList
)
1457 DirPrintWideList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1459 else if (lpFlags
->bNewLongList
)
1462 DirPrintNewList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1466 /* If nothing is selected old list is the default */
1467 DirPrintOldList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1476 * Compares 2 files based on the order criteria
1479 CompareFiles(LPWIN32_FIND_DATA lpFile1
, /* [IN] A pointer to WIN32_FIND_DATA of file 1 */
1480 LPWIN32_FIND_DATA lpFile2
, /* [IN] A pointer to WIN32_FIND_DATA of file 2 */
1481 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags that we use to list */
1483 ULARGE_INTEGER u64File1
;
1484 ULARGE_INTEGER u64File2
;
1486 long iComp
= 0; /* The comparison result */
1488 /* Calculate critiries by order given from user */
1489 for (i
= 0;i
< lpFlags
->stOrderBy
.sCriteriaCount
;i
++)
1492 /* Calculate criteria */
1493 switch(lpFlags
->stOrderBy
.eCriteria
[i
])
1495 case ORDER_SIZE
: /* Order by size /o:s */
1496 /* concat the 32bit integers to a 64bit */
1497 u64File1
.LowPart
= lpFile1
->nFileSizeLow
;
1498 u64File1
.HighPart
= lpFile1
->nFileSizeHigh
;
1499 u64File2
.LowPart
= lpFile2
->nFileSizeLow
;
1500 u64File2
.HighPart
= lpFile2
->nFileSizeHigh
;
1502 /* In case that differnce is too big for a long */
1503 if (u64File1
.QuadPart
< u64File2
.QuadPart
)
1505 else if (u64File1
.QuadPart
> u64File2
.QuadPart
)
1511 case ORDER_DIRECTORY
: /* Order by directory attribute /o:g */
1512 iComp
= ((lpFile2
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)-
1513 (lpFile1
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
));
1516 case ORDER_EXTENSION
: /* Order by extension name /o:e */
1517 iComp
= _tcsicmp(getExt(lpFile1
->cFileName
),getExt(lpFile2
->cFileName
));
1520 case ORDER_NAME
: /* Order by filename /o:n */
1521 iComp
= _tcsicmp(lpFile1
->cFileName
, lpFile2
->cFileName
);
1524 case ORDER_TIME
: /* Order by file's time /o:t */
1525 /* We compare files based on the time field selected by /t */
1526 switch(lpFlags
->stTimeField
.eTimeField
)
1528 case TF_CREATIONDATE
:
1529 /* concat the 32bit integers to a 64bit */
1530 u64File1
.LowPart
= lpFile1
->ftCreationTime
.dwLowDateTime
;
1531 u64File1
.HighPart
= lpFile1
->ftCreationTime
.dwHighDateTime
;
1532 u64File2
.LowPart
= lpFile2
->ftCreationTime
.dwLowDateTime
;
1533 u64File2
.HighPart
= lpFile2
->ftCreationTime
.dwHighDateTime
;
1535 case TF_LASTACCESSEDDATE
:
1536 /* concat the 32bit integers to a 64bit */
1537 u64File1
.LowPart
= lpFile1
->ftLastAccessTime
.dwLowDateTime
;
1538 u64File1
.HighPart
= lpFile1
->ftLastAccessTime
.dwHighDateTime
;
1539 u64File2
.LowPart
= lpFile2
->ftLastAccessTime
.dwLowDateTime
;
1540 u64File2
.HighPart
= lpFile2
->ftLastAccessTime
.dwHighDateTime
;
1542 case TF_MODIFIEDDATE
:
1543 /* concat the 32bit integers to a 64bit */
1544 u64File1
.LowPart
= lpFile1
->ftLastWriteTime
.dwLowDateTime
;
1545 u64File1
.HighPart
= lpFile1
->ftLastWriteTime
.dwHighDateTime
;
1546 u64File2
.LowPart
= lpFile2
->ftLastWriteTime
.dwLowDateTime
;
1547 u64File2
.HighPart
= lpFile2
->ftLastWriteTime
.dwHighDateTime
;
1551 /* In case that differnce is too big for a long */
1552 if (u64File1
.QuadPart
< u64File2
.QuadPart
)
1554 else if (u64File1
.QuadPart
> u64File2
.QuadPart
)
1561 /* Reverse if desired */
1562 if (lpFlags
->stOrderBy
.bCriteriaRev
[i
])
1565 /* If that criteria was enough for distinguishing
1566 the files/dirs,there is no need to calculate the others*/
1567 if (iComp
!= 0) break;
1570 /* Translate the value of iComp to boolean */
1580 * Sort files by the order criterias using quicksort method
1583 QsortFiles(LPWIN32_FIND_DATA ptrArray
[], /* [IN/OUT] The array with file info pointers */
1584 int i
, /* [IN] The index of first item in array */
1585 int j
, /* [IN] The index to last item in array */
1586 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags that we will use to sort */
1588 LPWIN32_FIND_DATA lpTemp
; /* A temporary pointer */
1589 int First
, Last
, Temp
;
1599 if (Way
== CompareFiles(ptrArray
[i
], ptrArray
[j
], lpFlags
))
1601 /* Swap the pointers of the array */
1602 lpTemp
= ptrArray
[i
];
1603 ptrArray
[i
]= ptrArray
[j
];
1604 ptrArray
[j
] = lpTemp
;
1606 /* Swap the indexes for inverting sorting */
1617 QsortFiles(ptrArray
,First
, i
-1, lpFlags
);
1618 QsortFiles(ptrArray
,i
+1,Last
, lpFlags
);
1627 * The functions that does everything except for printing results
1630 DirList(LPTSTR szPath
, /* [IN] The path that dir starts */
1631 LPTSTR szFilespec
, /* [IN] The type of file that we are looking for */
1632 LPINT pLine
, /* FIXME: Maybe used for paginating */
1633 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags of the listing */
1635 HANDLE hSearch
; /* The handle of the search */
1636 HANDLE hRecSearch
; /* The handle for searching recursivly */
1637 WIN32_FIND_DATA wfdFileInfo
; /* The info of file that found */
1638 LPWIN32_FIND_DATA
* ptrFileArray
; /* An array of pointers with all the files */
1639 PDIRFINDLISTNODE ptrStartNode
; /* The pointer to the first node */
1640 PDIRFINDLISTNODE ptrNextNode
; /* A pointer used for relatives refernces */
1641 TCHAR szFullPath
[MAX_PATH
]; /* The full path that we are listing with trailing \ */
1642 TCHAR szFullFileSpec
[MAX_PATH
]; /* The full path with file specs that we ll request\ */
1643 TCHAR szBytes
[20]; /* A string for converting ULARGE integer */
1644 DWORD dwCount
; /* A counter of files found in directory */
1645 DWORD dwCountFiles
; /* Counter for files */
1646 DWORD dwCountDirs
; /* Counter for directories */
1647 ULARGE_INTEGER u64CountBytes
; /* Counter for bytes */
1648 ULARGE_INTEGER u64Temp
; /* A temporary counter */
1649 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
1651 /* Initialize Variables */
1652 ptrStartNode
= NULL
;
1657 u64CountBytes
.QuadPart
= 0;
1659 /* Create szFullPath and szFullFileSpec */
1660 _tcscpy (szFullPath
, szPath
);
1661 if (szFullPath
[_tcslen(szFullPath
) - 1] != _T('\\'))
1662 _tcscat (szFullPath
, _T("\\"));
1663 _tcscpy (szFullFileSpec
, szFullPath
);
1664 _tcscat (szFullFileSpec
, szFilespec
);
1666 /* Prepare the linked list, first node is allocated */
1667 ptrStartNode
= malloc(sizeof(DIRFINDLISTNODE
));
1668 if (ptrStartNode
== NULL
)
1671 ConErrPrintf("DEBUG: Cannot allocate memory for ptrStartNode!\n");
1673 return 1; /* Error cannot allocate memory for 1st object */
1675 ptrNextNode
= ptrStartNode
;
1677 /* Collect the results for the current folder */
1678 hSearch
= FindFirstFile(szFullFileSpec
, &wfdFileInfo
);
1681 if (hSearch
!= INVALID_HANDLE_VALUE
)
1683 /* Here we filter all the specified attributes */
1684 if ((wfdFileInfo
.dwFileAttributes
& lpFlags
->stAttribs
.dwAttribMask
)
1685 == (lpFlags
->stAttribs
.dwAttribMask
& lpFlags
->stAttribs
.dwAttribVal
))
1687 ptrNextNode
->ptrNext
= malloc(sizeof(DIRFINDLISTNODE
));
1688 if (ptrNextNode
->ptrNext
== NULL
)
1691 ConErrPrintf("DEBUG: Cannot allocate memory for ptrNextNode->ptrNext!\n");
1693 while (ptrStartNode
)
1695 ptrNextNode
= ptrStartNode
->ptrNext
;
1697 ptrStartNode
= ptrNextNode
;
1703 /* If malloc fails we go to next file in hope it works,
1704 without braking the linked list! */
1705 if (ptrNextNode
->ptrNext
)
1707 /* Copy the info of search at linked list */
1708 memcpy(&ptrNextNode
->ptrNext
->stFindInfo
,
1710 sizeof(WIN32_FIND_DATA
));
1712 /* If lower case is selected do it here */
1713 if (lpFlags
->bLowerCase
)
1715 _tcslwr(ptrNextNode
->ptrNext
->stFindInfo
.cAlternateFileName
);
1716 _tcslwr(ptrNextNode
->ptrNext
->stFindInfo
.cFileName
);
1719 /* Continue at next node at linked list */
1720 ptrNextNode
= ptrNextNode
->ptrNext
;
1723 /* Grab statistics */
1724 if (wfdFileInfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1733 u64Temp
.HighPart
= wfdFileInfo
.nFileSizeHigh
;
1734 u64Temp
.LowPart
= wfdFileInfo
.nFileSizeLow
;
1735 u64CountBytes
.QuadPart
+= u64Temp
.QuadPart
;
1740 }while(FindNextFile(hSearch
, &wfdFileInfo
));
1743 /* Terminate list */
1744 ptrNextNode
->ptrNext
= NULL
;
1746 /* Calculate and allocate space need for making an array of pointers */
1747 ptrFileArray
= malloc(sizeof(LPWIN32_FIND_DATA
) * dwCount
);
1748 if (ptrFileArray
== NULL
)
1751 ConErrPrintf("DEBUG: Cannot allocate memory for ptrFileArray!\n");
1753 while (ptrStartNode
)
1755 ptrNextNode
= ptrStartNode
->ptrNext
;
1757 ptrStartNode
= ptrNextNode
;
1764 * Create an array of pointers from the linked list
1765 * this will be used to sort and print data, rather than the list
1767 ptrNextNode
= ptrStartNode
;
1769 while (ptrNextNode
->ptrNext
)
1771 *(ptrFileArray
+ dwCount
) = &ptrNextNode
->ptrNext
->stFindInfo
;
1772 ptrNextNode
= ptrNextNode
->ptrNext
;
1776 /* Sort Data if requested*/
1777 if (lpFlags
->stOrderBy
.sCriteriaCount
> 0)
1778 QsortFiles(ptrFileArray
, 0, dwCount
-1,lpFlags
);
1781 DirPrintFiles(ptrFileArray
, dwCount
, szFullPath
, lpFlags
);
1786 /* Print Directory Summary */
1787 /* Condition to print summary is:
1788 If we are not in bare format and if we have results! */
1789 if (!(lpFlags
->bBareFormat
) && (dwCount
> 0))
1791 ConvertULargeInteger(u64CountBytes
, szBytes
, 20, lpFlags
->bTSeperator
);
1792 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP8
, szMsg
, RC_STRING_MAX_SIZE
);
1793 ConOutPrintf(szMsg
,dwCountFiles
, szBytes
);
1796 /* Add statistics to recursive statistics*/
1797 recurse_dir_cnt
+= dwCountDirs
;
1798 recurse_file_cnt
+= dwCountFiles
;
1799 recurse_bytes
.QuadPart
+= u64CountBytes
.QuadPart
;
1801 /* Do the recursive job if requested
1802 the recursive is be done on ALL(indepent of their attribs)
1803 directoried of the current one.*/
1804 if (lpFlags
->bRecursive
)
1806 /* The new search is involving any *.* file */
1807 _tcscpy(szFullFileSpec
, szFullPath
);
1808 _tcscat(szFullFileSpec
, _T("*.*"));
1809 hRecSearch
= FindFirstFile (szFullFileSpec
, &wfdFileInfo
);
1812 if (hRecSearch
!= INVALID_HANDLE_VALUE
)
1814 /* We search for directories other than "." and ".." */
1815 if ((_tcsicmp(wfdFileInfo
.cFileName
, _T(".")) != 0) &&
1816 (_tcsicmp(wfdFileInfo
.cFileName
, _T("..")) != 0 ) &&
1817 (wfdFileInfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1819 /* Concat the path and the directory to do recursive */
1820 _tcscpy(szFullFileSpec
, szFullPath
);
1821 _tcscat(szFullFileSpec
, wfdFileInfo
.cFileName
);
1822 /* We do the same for tha folder */
1823 DirList(szFullFileSpec
, szFilespec
, pLine
,lpFlags
);
1826 }while(FindNextFile(hRecSearch
,&wfdFileInfo
));
1827 FindClose(hRecSearch
);
1830 /* Free linked list */
1831 while (ptrStartNode
)
1833 ptrNextNode
= ptrStartNode
->ptrNext
;
1835 ptrStartNode
= ptrNextNode
;
1847 * internal dir command
1849 INT
CommandDir(LPTSTR first
, LPTSTR rest
)
1851 TCHAR dircmd
[256]; /* A variable to store the DIRCMD enviroment variable */
1852 TCHAR szPath
[MAX_PATH
];
1853 TCHAR szFilespec
[MAX_PATH
];
1856 DIRSWITCHFLAGS stFlags
;
1858 /* Initialize variables */
1859 recurse_dir_cnt
= 0L;
1860 recurse_file_cnt
= 0L;
1861 recurse_bytes
.QuadPart
= 0;
1863 /* Initialize Switch Flags < Default switches are setted here!> */
1864 stFlags
.b4Digit
= TRUE
;
1865 stFlags
.bBareFormat
= FALSE
;
1866 stFlags
.bLowerCase
= FALSE
;
1867 stFlags
.bNewLongList
= TRUE
;
1868 stFlags
.bPause
= FALSE
;
1869 stFlags
.bRecursive
= FALSE
;
1870 stFlags
.bShortName
= FALSE
;
1871 stFlags
.bTSeperator
= TRUE
;
1872 stFlags
.bUser
= FALSE
;
1873 stFlags
.bWideList
= FALSE
;
1874 stFlags
.bWideListColSort
= FALSE
;
1875 stFlags
.stTimeField
.eTimeField
= TF_MODIFIEDDATE
;
1876 stFlags
.stTimeField
.bUnSet
= FALSE
;
1877 stFlags
.stAttribs
.dwAttribMask
= FILE_ATTRIBUTE_HIDDEN
| FILE_ATTRIBUTE_SYSTEM
;
1878 stFlags
.stAttribs
.dwAttribVal
= 0L;
1879 stFlags
.stAttribs
.bUnSet
= FALSE
;
1880 stFlags
.stOrderBy
.sCriteriaCount
= 0;
1881 stFlags
.stOrderBy
.bUnSet
= FALSE
;
1883 /* read the parameters from the DIRCMD environment variable */
1884 if (GetEnvironmentVariable (_T("DIRCMD"), dircmd
, 256))
1885 if (!DirReadParam(dircmd
, ¶m
, &stFlags
))
1888 /* read the parameters */
1889 if (!DirReadParam(rest
, ¶m
, &stFlags
))
1892 /* default to current directory */
1896 /* parse the directory info */
1897 if (DirParsePathspec (param
, szPath
, szFilespec
))
1901 Uncomment this to show the final state of switch flags*/
1903 ConOutPrintf("Attributes mask/value %x/%x\n",stFlags.stAttribs.dwAttribMask,stFlags.stAttribs.dwAttribVal );
1904 ConOutPrintf("(B) Bare format : %i\n", stFlags.bBareFormat );
1905 ConOutPrintf("(C) Thousand : %i\n", stFlags.bTSeperator );
1906 ConOutPrintf("(W) Wide list : %i\n", stFlags.bWideList );
1907 ConOutPrintf("(D) Wide list sort by column : %i\n", stFlags.bWideListColSort );
1908 ConOutPrintf("(L) Lowercase : %i\n", stFlags.bLowerCase );
1909 ConOutPrintf("(N) New : %i\n", stFlags.bNewLongList );
1910 ConOutPrintf("(O) Order : %i\n", stFlags.stOrderBy.sCriteriaCount );
1912 for (i =0;i<stFlags.stOrderBy.sCriteriaCount;i++)
1913 ConOutPrintf(" Order Criteria [%i]: %i (Reversed: %i)\n",i, stFlags.stOrderBy.eCriteria[i], stFlags.stOrderBy.bCriteriaRev[i] );
1914 ConOutPrintf("(P) Pause : %i\n", stFlags.bPause );
1915 ConOutPrintf("(Q) Owner : %i\n", stFlags.bUser );
1916 ConOutPrintf("(S) Recursive : %i\n", stFlags.bRecursive );
1917 ConOutPrintf("(T) Time field : %i\n", stFlags.stTimeField.eTimeField );
1918 ConOutPrintf("(X) Short names : %i\n", stFlags.bShortName );
1919 ConOutPrintf("Parameter : %s\n", param );
1922 /* print the header */
1923 if (!stFlags
.bBareFormat
)
1924 if (!PrintDirectoryHeader (szPath
, &nLine
, &stFlags
))
1927 /* do the actual dir */
1928 if (DirList (szPath
, szFilespec
, &nLine
, &stFlags
))
1931 /* print the footer */
1932 PrintSummary(szPath
,