3 * DIR.C - dir internal command.
8 * 01/29/97 (Tim Norman)
11 * 06/13/97 (Tim Norman)
14 * 07/12/97 (Tim Norman)
15 * Fixed bug that caused the root directory to be unlistable
17 * 07/12/97 (Marc Desrochers)
18 * Changed to use maxx, maxy instead of findxy()
21 * Added compatibility for /w in dir
24 * Compatibility for dir/s started
25 * Tested that program finds directories off root fine
28 * do_recurse saves the cwd and also stores it in Root
29 * build_tree adds the cwd to the beginning of its' entries
30 * Program runs fine, added print_tree -- works fine.. as EXE,
31 * program won't work properly as COM.
34 * Found problem that caused COM not to work
38 * added free mem routine
41 * debugged the free mem routine
42 * debugged whole thing some more
44 * ReadDir stores Root name and _Read_Dir does the hard work
45 * PrintDir prints Root and _Print_Dir does the hard work
46 * KillDir kills Root _after_ _Kill_Dir does the hard work
47 * Integrated program into DIR.C(this file) and made some same
51 * Cleaned up code a bit, added comments
54 * Added error checking to my previously added routines
57 * Rewrote recursive functions, again! Most other recursive
58 * functions are now obsolete -- ReadDir, PrintDir, _Print_Dir,
59 * KillDir and _Kill_Dir. do_recurse does what PrintDir did
60 * and _Read_Dir did what it did before along with what _Print_Dir
61 * did. Makes /s a lot faster!
62 * Reports 2 more files/dirs that MS-DOS actually reports
63 * when used in root directory(is this because dir defaults
64 * to look for read only files?)
65 * Added support for /b, /a and /l
66 * Made error message similar to DOS error messages
70 * Added check for /-(switch) to turn off previously defined
72 * Added ability to check for DIRCMD in environment and
77 * Now can dir *.ext/X, no spaces!
80 * error message now found in command.h
82 * 07/08/1998 (John P. Price)
83 * removed extra returns; closer to MSDOS
84 * fixed wide display so that an extra return is not displayed
85 * when there is five filenames in the last line.
88 * Changed error messages
90 * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
91 * added config.h include
94 * 04-Dec-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
95 * Converted source code to Win32, except recursive dir ("dir /s").
97 * 10-Dec-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
98 * Fixed recursive dir ("dir /s").
100 * 14-Dec-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
101 * Converted to Win32 directory functions and
102 * fixed some output bugs. There are still some more ;)
104 * 10-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
105 * Added "/N" and "/4" options, "/O" is a dummy.
106 * Added locale support.
108 * 20-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
111 * 01-Mar-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
112 * Replaced all runtime io functions by their Win32 counterparts.
114 * 23-Feb-2001 (Carl Nettelblad <cnettel@hem.passagen.se>)
115 * dir /s now works in deeper trees
117 * 28-Jan-2004 (Michael Fritscher <michael@fritscher.net>)
118 * Fix for /p, so it is working under Windows in GUI-mode, too.
120 * 30-Apr-2004 (Filip Navara <xnavara@volny.cz>)
121 * Fix /w to print long names.
123 * 27-Feb-2005 (Konstantinos Paliouras <squarious@gmail.com>)
124 * Implemented all the switches that were missing, and made
125 * the ros dir very similar to windows dir. Major part of
126 * the code is rewritten. /p is removed, to be rewriten in
132 #include "resource.h"
134 #ifdef INCLUDE_CMD_DIR
138 /* Time Field enumeration */
143 TF_LASTACCESSEDDATE
= 2
146 /* Ordered by enumeration */
156 /* The struct for holding the switches */
157 typedef struct TDirSwitchesFlags
159 BOOL bBareFormat
; /* Bare Format */
160 BOOL bTSeperator
; /* Thousands seperator */
161 BOOL bWideList
; /* Wide list format */
162 BOOL bWideListColSort
; /* Wide list format but sorted by column */
163 BOOL bLowerCase
; /* Uses lower case */
164 BOOL bNewLongList
; /* New long list */
165 BOOL bPause
; /* Pause per page */
166 BOOL bUser
; /* Displays the owner of file */
167 BOOL bRecursive
; /* Displays files in specified directory and all sub */
168 BOOL bShortName
; /* Displays the sort name of files if exist */
169 BOOL b4Digit
; /* Four digit year */
172 DWORD dwAttribVal
; /* The desired state of attribute */
173 DWORD dwAttribMask
; /* Which attributes to check */
174 BOOL bUnSet
; /* A helper flag if "-" was given with the switch */
175 BOOL bParSetted
; /* A helper flag if parameters of switch were given */
176 } stAttribs
; /* Displays files with this attributes only */
179 enum EOrderBy eCriteria
[3]; /* Criterias used to order by */
180 BOOL bCriteriaRev
[3]; /* If the criteria is in reversed order */
181 short sCriteriaCount
; /* The quantity of criterias */
182 BOOL bUnSet
; /* A helper flag if "-" was given with the switch */
183 BOOL bParSetted
; /* A helper flag if parameters of switch were given */
184 } stOrderBy
; /* Ordered by criterias */
187 enum ETimeField eTimeField
; /* The time field that will be used for */
188 BOOL bUnSet
; /* A helper flag if "-" was given with the switch */
189 BOOL bParSetted
; /* A helper flag if parameters of switch were given */
190 } stTimeField
; /* The time field to display or use for sorting */
191 }DIRSWITCHFLAGS
, * LPDIRSWITCHFLAGS
;
194 (*PGETFREEDISKSPACEEX
)(LPCTSTR
, PULARGE_INTEGER
, PULARGE_INTEGER
, PULARGE_INTEGER
);
197 /* Globally save the # of dirs, files and bytes,
198 * probabaly later pass them to functions. Rob Lake */
199 static ULONG recurse_dir_cnt
;
200 static ULONG recurse_file_cnt
;
201 static ULARGE_INTEGER recurse_bytes
;
207 * displays help screen for dir
213 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
214 LoadString( GetModuleHandle(NULL
), STRING_DIR_HELP1
, szMsg
,sizeof(szMsg
)/sizeof(TCHAR
));
223 * Parse the parameters and switches of the command line and exports them
226 DirReadParam (LPTSTR line
, /* [IN] The line with the parameters & switches */
227 LPTSTR
*param
, /* [OUT] The parameters after parsing */
228 LPDIRSWITCHFLAGS lpFlags
/* [IN/OUT] The flags after calculating switches */
231 TCHAR cCurSwitch
; /* The current switch */
232 TCHAR cCurChar
; /* Current examing character */
233 TCHAR cCurUChar
; /* Current upper examing character */
234 BOOL bNegative
; /* Negative switch */
235 BOOL bPNegative
; /* Negative switch parameter */
236 BOOL bIntoQuotes
; /* A flag showing if we are in quotes (") */
237 LPTSTR ptrLast
; /* A pointer to the last character of param */
239 /* Initialize variables; */
240 cCurSwitch
= _T(' ');
245 /* No parameters yet */
249 /* We suppose that switch parameters
250 were given to avoid setting them to default
251 if the switch was not given */
252 lpFlags
->stAttribs
.bParSetted
= TRUE
;
253 lpFlags
->stOrderBy
.bParSetted
= TRUE
;
254 lpFlags
->stTimeField
.bParSetted
= TRUE
;
256 /* Main Loop (see README_DIR.txt) */
257 /* scan the command line char per char, and we process its char */
260 /* we save current character as it is and its upper case */
262 cCurUChar
= _toupper(*line
);
264 /* 1st section (see README_DIR.txt) */
265 /* When a switch is expecting */
266 if (cCurSwitch
== _T('/'))
268 if ((cCurUChar
== _T('A')) ||(cCurUChar
== _T('T')) || (cCurUChar
== _T('O')))
270 cCurSwitch
= cCurUChar
;
274 lpFlags
->stAttribs
.bUnSet
= bNegative
;
275 lpFlags
->stAttribs
.bParSetted
= FALSE
;
278 lpFlags
->stTimeField
.bUnSet
= bNegative
;
279 lpFlags
->stTimeField
.bParSetted
= FALSE
;
282 lpFlags
->stOrderBy
.bUnSet
= bNegative
;
283 lpFlags
->stOrderBy
.bParSetted
= FALSE
;
287 else if (cCurUChar
== _T('L'))
288 lpFlags
->bLowerCase
= ! bNegative
;
289 else if (cCurUChar
== _T('B'))
290 lpFlags
->bBareFormat
= ! bNegative
;
291 else if (cCurUChar
== _T('C'))
292 lpFlags
->bTSeperator
= ! bNegative
;
293 else if (cCurUChar
== _T('W'))
294 lpFlags
->bWideList
= ! bNegative
;
295 else if (cCurUChar
== _T('D'))
296 lpFlags
->bWideListColSort
= ! bNegative
;
297 else if (cCurUChar
== _T('N'))
298 lpFlags
->bNewLongList
= ! bNegative
;
299 else if (cCurUChar
== _T('P'))
300 lpFlags
->bPause
= ! bNegative
;
301 else if (cCurUChar
== _T('Q'))
302 lpFlags
->bUser
= ! bNegative
;
303 else if (cCurUChar
== _T('S'))
304 lpFlags
->bRecursive
= ! bNegative
;
305 else if (cCurUChar
== _T('X'))
306 lpFlags
->bShortName
= ! bNegative
;
307 else if (cCurChar
== _T('4'))
308 lpFlags
->b4Digit
= ! bNegative
;
309 else if (cCurChar
== _T('?'))
314 else if (cCurChar
== _T('-'))
318 error_invalid_switch ((TCHAR
)_totupper (*line
));
322 /* We check if we calculated the negative value and realese the flag */
323 if ((cCurChar
!= _T('-')) && bNegative
)
326 /* if not a,o,t or - option then next parameter is not a switch */
327 if ((cCurSwitch
== _T('/')) && (!bNegative
))
328 cCurSwitch
= _T(' ');
331 /* 2nd section (see README_DIR.txt) */
332 /* We are expecting parameter or the unknown */
333 else if ((cCurSwitch
== _T(' ')) || (cCurSwitch
== _T('P')))
335 if (cCurChar
== _T('/'))
336 cCurSwitch
= _T('/');
338 /* Process a spacer */
339 else if (cCurChar
== _T(' '))
343 cCurSwitch
= _T(' ');
344 if ((*param
) && !(ptrLast
))
349 /* Process a quote */
350 else if (cCurChar
== _T('\"'))
352 bIntoQuotes
= !bIntoQuotes
;
353 if (!bIntoQuotes
) ptrLast
= line
;
355 /* Process a character for parameter */
358 if ((cCurSwitch
== _T(' ')) && (*param
))
360 error_too_many_parameters(line
);
363 cCurSwitch
= _T('P');
369 /* 3rd section (see README_DIR.txt) */
370 /* We are waiting for switch parameters */
373 /* Check if there are no more switch parameters */
374 if ((cCurChar
== _T('/')) || ( cCurChar
== _T(' ')))
376 /* Wrong desicion path, reprocess current character */
377 cCurSwitch
= cCurChar
;
380 /* Process parameter switch */
383 case _T('A'): /* Switch parameters for /A (attributes filter) */
384 /* Ok a switch parameter was given */
385 lpFlags
->stAttribs
.bParSetted
= TRUE
;
387 if (cCurChar
== _T(':'))
388 /* =V= dead command, used to make the "if" work */
390 else if(cCurChar
== _T('-'))
392 else if(cCurUChar
== _T('D'))
394 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_DIRECTORY
;
396 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_DIRECTORY
;
398 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_DIRECTORY
;
400 else if(cCurUChar
== _T('R'))
402 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_READONLY
;
404 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_READONLY
;
406 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_READONLY
;
408 else if(cCurUChar
== _T('H'))
410 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_HIDDEN
;
412 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_HIDDEN
;
414 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_HIDDEN
;
416 else if(cCurUChar
== _T('A'))
418 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_ARCHIVE
;
420 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_ARCHIVE
;
422 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_ARCHIVE
;
424 else if(cCurUChar
== _T('S'))
426 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_SYSTEM
;
428 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_SYSTEM
;
430 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_SYSTEM
;
434 error_parameter_format((TCHAR
)_totupper (*line
));
438 case _T('T'): /* Switch parameters for /T (time field) */
440 /* Ok a switch parameter was given */
441 lpFlags
->stTimeField
.bParSetted
= TRUE
;
443 if (cCurChar
== _T(':'))
444 /* =V= dead command, used to make the "if" work */
446 else if(cCurUChar
== _T('C'))
447 lpFlags
->stTimeField
.eTimeField
= TF_CREATIONDATE
;
448 else if(cCurUChar
== _T('A'))
449 lpFlags
->stTimeField
.eTimeField
= TF_LASTACCESSEDDATE
;
450 else if(cCurUChar
== _T('W'))
451 lpFlags
->stTimeField
.eTimeField
= TF_MODIFIEDDATE
;
454 error_parameter_format((TCHAR
)_totupper (*line
));
458 case _T('O'): /* Switch parameters for /O (order) */
459 /* Ok a switch parameter was given */
460 lpFlags
->stOrderBy
.bParSetted
= TRUE
;
462 if (cCurChar
== _T(':'))
463 /* <== dead command, used to make the "if" work */
465 else if(cCurChar
== _T('-'))
467 else if(cCurUChar
== _T('N'))
469 if (lpFlags
->stOrderBy
.sCriteriaCount
< 3) lpFlags
->stOrderBy
.sCriteriaCount
++;
470 lpFlags
->stOrderBy
.bCriteriaRev
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = bPNegative
;
471 lpFlags
->stOrderBy
.eCriteria
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = ORDER_NAME
;
473 else if(cCurUChar
== _T('S'))
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_SIZE
;
479 else if(cCurUChar
== _T('G'))
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_DIRECTORY
;
485 else if(cCurUChar
== _T('E'))
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_EXTENSION
;
491 else if(cCurUChar
== _T('D'))
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_TIME
;
500 error_parameter_format((TCHAR
)_totupper (*line
));
506 /* We check if we calculated the negative value and realese the flag */
507 if ((cCurChar
!= _T('-')) && bPNegative
)
513 /* Terminate the parameters */
514 if (ptrLast
) *ptrLast
= 0;
516 /* Calculate the switches with no switch paramater */
517 if (!(lpFlags
->stAttribs
.bParSetted
))
519 lpFlags
->stAttribs
.dwAttribVal
= 0L;
520 lpFlags
->stAttribs
.dwAttribMask
= lpFlags
->stAttribs
.dwAttribVal
;
522 if (!(lpFlags
->stOrderBy
.bParSetted
))
524 lpFlags
->stOrderBy
.sCriteriaCount
= 1;
525 lpFlags
->stOrderBy
.eCriteria
[0] = ORDER_NAME
;
526 lpFlags
->stOrderBy
.bCriteriaRev
[0] = FALSE
;
528 if (!(lpFlags
->stOrderBy
.bParSetted
))
529 lpFlags
->stTimeField
.eTimeField
= TF_MODIFIEDDATE
;
531 /* Calculate the unsetted switches (the "-" prefixed)*/
532 if (lpFlags
->stAttribs
.bUnSet
)
534 lpFlags
->stAttribs
.bUnSet
= FALSE
;
535 lpFlags
->stAttribs
.dwAttribVal
= 0L;
536 lpFlags
->stAttribs
.dwAttribMask
= FILE_ATTRIBUTE_HIDDEN
| FILE_ATTRIBUTE_SYSTEM
;
538 if (lpFlags
->stOrderBy
.bUnSet
)
540 lpFlags
->stOrderBy
.bUnSet
= FALSE
;
541 lpFlags
->stOrderBy
.sCriteriaCount
= 0;
543 if (lpFlags
->stTimeField
.bUnSet
)
545 lpFlags
->stTimeField
.bUnSet
= FALSE
;
546 lpFlags
->stTimeField
.eTimeField
= TF_MODIFIEDDATE
;
554 * extend the filespec, possibly adding wildcards
557 ExtendFilespec (LPTSTR file
)
564 /* if no file spec, change to "*.*" */
565 if (*file
== _T('\0'))
567 _tcscpy (file
, _T("*.*"));
571 /* if starts with . add * in front */
572 if (*file
== _T('.'))
574 memmove (&file
[1], &file
[0], (_tcslen (file
) + 1) * sizeof(TCHAR
));
579 if (!_tcschr (file
, _T('.')))
581 _tcscat (file
, _T(".*"));
585 /* if last character is '.' add '*' */
586 len
= _tcslen (file
);
587 if (file
[len
- 1] == _T('.'))
589 _tcscat (file
, _T("*"));
598 * split the pathspec into drive, directory, and filespec
601 DirParsePathspec (LPTSTR szPathspec
, LPTSTR szPath
, LPTSTR szFilespec
)
603 TCHAR szOrigPath
[MAX_PATH
];
607 BOOL bWildcards
= FALSE
;
609 GetCurrentDirectory (MAX_PATH
, szOrigPath
);
611 /* get the drive and change to it */
612 if (szPathspec
[1] == _T(':'))
614 TCHAR szRootPath
[] = _T("A:");
616 szRootPath
[0] = szPathspec
[0];
617 start
= szPathspec
+ 2;
618 if (!SetCurrentDirectory (szRootPath
))
620 ErrorMessage (GetLastError(), NULL
);
630 /* check for wildcards */
631 for (i
= 0; szPathspec
[i
]; i
++)
633 if (szPathspec
[i
] == _T('*') || szPathspec
[i
] == _T('?'))
637 /* check if this spec is a directory */
640 if (SetCurrentDirectory (szPathspec
))
642 _tcscpy (szFilespec
, _T("*.*"));
644 if (!GetCurrentDirectory (MAX_PATH
, szPath
))
646 szFilespec
[0] = _T('\0');
647 SetCurrentDirectory (szOrigPath
);
648 error_out_of_memory();
652 SetCurrentDirectory (szOrigPath
);
657 /* find the file spec */
658 tmp
= _tcsrchr (start
, _T('\\'));
660 /* if no path is specified */
663 _tcscpy (szFilespec
, start
);
664 ExtendFilespec (szFilespec
);
665 if (!GetCurrentDirectory (MAX_PATH
, szPath
))
667 szFilespec
[0] = _T('\0');
668 SetCurrentDirectory (szOrigPath
);
669 error_out_of_memory();
673 SetCurrentDirectory (szOrigPath
);
677 /* get the filename */
678 _tcscpy (szFilespec
, tmp
+1);
679 ExtendFilespec (szFilespec
);
683 /* change to the root directory */
684 if (!SetCurrentDirectory (_T("\\")))
686 szFilespec
[0] = _T('\0');
687 SetCurrentDirectory (szOrigPath
);
688 error_path_not_found ();
697 /* change to this directory */
698 if (!SetCurrentDirectory (start
))
701 szFilespec
[0] = _T('\0');
702 SetCurrentDirectory (szOrigPath
);
703 error_path_not_found ();
707 /* get the full name of the directory */
708 if (!GetCurrentDirectory (MAX_PATH
, szPath
))
711 szFilespec
[0] = _T('\0');
712 SetCurrentDirectory (szOrigPath
);
713 error_out_of_memory ();
719 SetCurrentDirectory (szOrigPath
);
728 * increment our line if paginating, display message at end of screen
729 *//*Maybe needed in future
731 IncLine (LPINT pLine, LPDIRSWITCHFLAGS lpFlags)
734 CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo;
736 error = GetConsoleScreenBufferInfo(hConsole, &lpConsoleScreenBufferInfo);
738 WindowHeight= lpConsoleScreenBufferInfo.srWindow.Bottom - lpConsoleScreenBufferInfo.srWindow.Top;
740 //That prevents bad behave if WindowHeight couln't calc
743 WindowHeight= 1000000;
745 if (!(lpFlags->bPause))
750 // Because I don't know if WindowsHeight
751 work under all cases, perhaps then maxy
754 if (*pLine >= (int)maxy - 2 || *pLine >= WindowHeight)
757 return (PagePrompt () == PROMPT_BREAK);
765 * PrintDirectoryHeader
767 * print the header for the dir command
770 PrintDirectoryHeader (LPTSTR szPath
, LPINT pLine
, LPDIRSWITCHFLAGS lpFlags
)
772 TCHAR szRootName
[MAX_PATH
];
776 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
778 if (lpFlags
->bBareFormat
)
781 /* build usable root path */
782 if (szPath
[1] == _T(':') && szPath
[2] == _T('\\'))
785 szRootName
[0] = szPath
[0];
786 szRootName
[1] = _T(':');
787 szRootName
[2] = _T('\\');
790 else if (szPath
[0] == _T('\\') && szPath
[1] == _T('\\'))
793 p
= _tcschr(&szPath
[2], _T('\\'));
796 error_invalid_drive();
799 p
= _tcschr(p
+1, _T('\\'));
802 _tcscpy(szRootName
, szPath
);
803 _tcscat(szRootName
, _T("\\"));
808 _tcscpy(szRootName
, szPath
);
809 _tcscat(szRootName
, _T("\\"));
815 error_invalid_drive();
819 /* get the media ID of the drive */
820 if (!GetVolumeInformation(szRootName
, szVolName
, 80, &dwSerialNr
,
821 NULL
, NULL
, NULL
, 0))
823 error_invalid_drive();
827 /* print drive info */
829 if (szVolName
[0] != _T('\0'))
831 LoadString( GetModuleHandle(NULL
), STRING_DIR_HELP2
, szMsg
,sizeof(szMsg
)/sizeof(TCHAR
));
832 ConOutPrintf (szMsg
, szRootName
[0], szVolName
);
836 LoadString( GetModuleHandle(NULL
), STRING_DIR_HELP3
, szMsg
,sizeof(szMsg
)/sizeof(TCHAR
));
837 ConOutPrintf (szMsg
, szRootName
[0]);
843 /* print the volume serial number if the return was successful */
845 LoadString( GetModuleHandle(NULL
), STRING_DIR_HELP4
, szMsg
,sizeof(szMsg
)/sizeof(TCHAR
));
857 * insert commas into a number
858 *//* Maybe needed in future
860 ConvertULong (ULONG num, LPTSTR des, INT len)
877 if (((c + 1) % (nNumberGroups + 1)) == 0)
878 temp[30 - c++] = cThousandSeparator;
879 temp[30 - c++] = (TCHAR)(num % 10) + _T('0');
883 for (n = 0; n <= c; n++)
884 des[n] = temp[31 - c + n];
892 ConvertULargeInteger (ULARGE_INTEGER num
, LPTSTR des
, INT len
, BOOL bPutSeperator
)
898 if (num
.QuadPart
== 0)
907 while (num
.QuadPart
> 0)
909 if ((((c
+ 1) % (nNumberGroups
+ 1)) == 0) && (bPutSeperator
))
910 temp
[30 - c
++] = cThousandSeparator
;
911 temp
[30 - c
++] = (TCHAR
)(num
.QuadPart
% 10) + _T('0');
915 for (n
= 0; n
<= c
; n
++)
916 des
[n
] = temp
[31 - c
+ n
];
924 DirPrintFileDateTime (TCHAR
* lpDate
,
926 LPWIN32_FIND_DATA lpFile
,
927 LPDIRSWITCHFLAGS lpFlags
)
935 /* Select the right time field */
936 switch (lpFlags
->stTimeField
.eTimeField
)
938 case TF_CREATIONDATE
:
939 if (!FileTimeToLocalFileTime(&lpFile
->ftCreationTime
, &ft
))
941 FileTimeToSystemTime(&ft
, &dt
);
943 case TF_LASTACCESSEDDATE
:
944 if (!FileTimeToLocalFileTime(&lpFile
->ftLastAccessTime
, &ft
))
946 FileTimeToSystemTime(&ft
, &dt
);
948 case TF_MODIFIEDDATE
:
949 if (!FileTimeToLocalFileTime(&lpFile
->ftLastWriteTime
, &ft
))
951 FileTimeToSystemTime(&ft
, &dt
);
956 wYear
= (lpFlags
->b4Digit
) ? dt
.wYear
: dt
.wYear
%100;
961 _stprintf (szDate
,_T("%02d%c%02d%c%0*d"),
962 dt
.wMonth
, cDateSeparator
,
963 dt
.wDay
, cDateSeparator
,
964 lpFlags
->b4Digit
?4:2, wYear
);
968 _stprintf (szDate
, _T("%02d%c%02d%c%0*d"),
969 dt
.wDay
, cDateSeparator
, dt
.wMonth
,
970 cDateSeparator
,lpFlags
->b4Digit
?4:2, wYear
);
974 _stprintf (szDate
, _T("%0*d%c%02d%c%02d"),
975 lpFlags
->b4Digit
?4:2, wYear
, cDateSeparator
,
976 dt
.wMonth
, cDateSeparator
, dt
.wDay
);
982 case 0: /* 12 hour format */
984 _stprintf (szTime
,_T(" %02d%c%02u%c"),
985 (dt
.wHour
== 0 ? 12 : (dt
.wHour
<= 12 ? dt
.wHour
: dt
.wHour
- 12)),
987 dt
.wMinute
, (dt
.wHour
<= 11 ? 'a' : 'p'));
990 case 1: /* 24 hour format */
991 _stprintf (szTime
, _T(" %02d%c%02u"),
992 dt
.wHour
, cTimeSeparator
, dt
.wMinute
);
996 _tcscpy(lpDate
, szDate
);
997 _tcscpy(lpTime
, szTime
);
1002 GetUserDiskFreeSpace(LPCTSTR lpRoot
,
1003 PULARGE_INTEGER lpFreeSpace
)
1005 PGETFREEDISKSPACEEX pGetFreeDiskSpaceEx
;
1006 HINSTANCE hInstance
;
1011 ULARGE_INTEGER TotalNumberOfBytes
, TotalNumberOfFreeBytes
;
1013 lpFreeSpace
->QuadPart
= 0;
1015 hInstance
= LoadLibrary(_T("KERNEL32"));
1016 if (hInstance
!= NULL
)
1018 pGetFreeDiskSpaceEx
= (PGETFREEDISKSPACEEX
)GetProcAddress(hInstance
,
1020 "GetDiskFreeSpaceExW");
1022 "GetDiskFreeSpaceExA");
1024 if (pGetFreeDiskSpaceEx
!= NULL
)
1026 if (pGetFreeDiskSpaceEx(lpRoot
, lpFreeSpace
, &TotalNumberOfBytes
, &TotalNumberOfFreeBytes
) == TRUE
)
1029 FreeLibrary(hInstance
);
1032 GetDiskFreeSpace(lpRoot
,
1038 lpFreeSpace
->QuadPart
= dwSecPerCl
* dwBytPerSec
* dwFreeCl
;
1043 * print_summary: prints dir summary
1044 * Added by Rob Lake 06/17/98 to compact code
1045 * Just copied Tim's Code and patched it a bit
1049 PrintSummary(LPTSTR szPath
,
1052 ULARGE_INTEGER u64Bytes
,
1054 LPDIRSWITCHFLAGS lpFlags
)
1057 ULARGE_INTEGER uliFree
;
1058 TCHAR szRoot
[] = _T("A:\\");
1059 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
1062 /* Here we check if we didn't find anything */
1063 if (!(ulFiles
+ ulDirs
))
1065 error_file_not_found();
1068 /* In bare format we don't print results */
1069 if (lpFlags
->bBareFormat
)
1072 /* Print recursive specific results */
1073 if (lpFlags
->bRecursive
)
1075 ConvertULargeInteger (u64Bytes
, szBuffer
, sizeof(szBuffer
), lpFlags
->bTSeperator
);
1077 LoadString( GetModuleHandle(NULL
), STRING_DIR_HELP5
, szMsg
,sizeof(szMsg
)/sizeof(TCHAR
));
1078 ConOutPrintf (szMsg
,ulFiles
, szBuffer
);
1081 /* Print total directories and freespace */
1082 szRoot
[0] = szPath
[0];
1083 GetUserDiskFreeSpace(szRoot
, &uliFree
);
1084 ConvertULargeInteger (uliFree
, szBuffer
, sizeof(szBuffer
), lpFlags
->bTSeperator
);
1085 LoadString( GetModuleHandle(NULL
), STRING_DIR_HELP6
, szMsg
,sizeof(szMsg
)/sizeof(TCHAR
));
1086 ConOutPrintf (szMsg
,ulDirs
, szBuffer
);
1094 * Get the extension of a filename
1096 TCHAR
* getExt(const TCHAR
* file
)
1099 TCHAR
* tmp
= _tcsrchr(file
,_T('.'));
1100 return tmp
?tmp
+1:_T("");
1106 * Get the name of the file without extension
1108 TCHAR
* getName(const TCHAR
* file
, TCHAR
* dest
)
1113 /* Check for "." and ".." folders */
1114 if ((_tcscmp(file
, _T(".")) == 0)
1115 || (_tcscmp(file
, _T("..")) == 0))
1121 end
= _tcsrchr(file
,'.');
1123 iLen
= _tcslen(file
);
1125 iLen
= (end
- file
);
1128 _tcsncpy(dest
, file
, iLen
);
1129 *(dest
+ iLen
) = _T('\0');
1136 * The function that prints in new style
1139 DirPrintNewList(LPWIN32_FIND_DATA ptrFiles
[], /* [IN]Files' Info */
1140 DWORD dwCount
, /* [IN] The quantity of files */
1141 TCHAR
* szCurPath
, /* [IN] Full path of current directory */
1142 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
1144 DWORD i
; /* An indexer for "for"s */
1145 TCHAR szSize
[30]; /* The size of file */
1146 TCHAR szShortName
[15]; /* The sort name */
1147 TCHAR szDate
[20], szTime
[20]; /* Date and time strings */
1148 int iSizeFormat
; /* The format of size field */
1149 ULARGE_INTEGER u64FileSize
; /* The file size */
1151 for(i
= 0;i
< dwCount
;i
++)
1154 /* Calculate size */
1155 if (ptrFiles
[i
]->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1159 _tcscpy(szSize
, _T("<DIR>"));
1165 u64FileSize
.HighPart
= ptrFiles
[i
]->nFileSizeHigh
;
1166 u64FileSize
.LowPart
= ptrFiles
[i
]->nFileSizeLow
;
1167 ConvertULargeInteger(u64FileSize
, szSize
, 20,lpFlags
->bTSeperator
);
1169 /* Calculate short name */
1170 szShortName
[0] = _T('\0');
1171 if (lpFlags
->bShortName
)
1172 _stprintf(szShortName
,_T(" %-12s"), ptrFiles
[i
]->cAlternateFileName
);
1174 /* Format date and time */
1175 DirPrintFileDateTime(szDate
,szTime
,ptrFiles
[i
],lpFlags
);
1177 /* Print the line */
1178 ConOutPrintf(_T("%10s %-8s %*s%s %s\n"),
1184 ptrFiles
[i
]->cFileName
);
1192 * The function that prints in wide list
1195 DirPrintWideList(LPWIN32_FIND_DATA ptrFiles
[], /* [IN] Files' Info */
1196 DWORD dwCount
, /* [IN] The quantity of files */
1197 TCHAR
* szCurPath
, /* [IN] Full path of current directory */
1198 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
1200 DWORD i
,j
; /* An indexer for "for"s */
1201 short iScreenWidth
; /* The screen width */
1202 short iColumns
; /* The columns (used by wide list) */
1203 short iLines
; /* The lines (used by the wide list /d) */
1204 int iBiggestName
; /* The biggest name, used for wide list */
1205 TCHAR szTempFname
[MAX_PATH
]; /* Temporary string */
1207 /* Calculate biggest name */
1209 for(i
= 0;i
< dwCount
;i
++)
1211 if (ptrFiles
[i
]->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1213 /* Directories need 2 additinal characters for brackets */
1214 if ((_tcslen(ptrFiles
[i
]->cFileName
) + 2) > iBiggestName
)
1215 iBiggestName
= _tcslen(ptrFiles
[i
]->cFileName
) + 2;
1219 if (_tcslen(ptrFiles
[i
]->cFileName
) > iBiggestName
)
1220 iBiggestName
= _tcslen(ptrFiles
[i
]->cFileName
);
1224 /* Count the highest number of columns */
1225 GetScreenSize(&iScreenWidth
, 0);
1226 iColumns
= iScreenWidth
/ iBiggestName
;
1228 /* Check if there is enough space for spaces between names */
1229 if (((iBiggestName
* iColumns
) + iColumns
) >= iScreenWidth
)
1231 /* A last check at iColumns to avoid division by zero */
1232 if (!(iColumns
)) iColumns
= 1;
1234 /* Print Column sorted */
1235 if (lpFlags
->bWideListColSort
)
1237 /* Calculate the lines that will be printed */
1238 iLines
= ceil((float)dwCount
/(float)iColumns
);
1240 for (i
= 0;i
< iLines
;i
++)
1242 for (j
= 0;j
< iColumns
;j
++)
1244 DWORD temp
= (j
*iLines
)+i
;
1245 if (temp
>= dwCount
) break;
1246 if (ptrFiles
[temp
]->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1247 _stprintf(szTempFname
, _T("[%s]"), ptrFiles
[temp
]->cFileName
);
1249 _stprintf(szTempFname
, _T("%s"), ptrFiles
[temp
]->cFileName
);
1251 ConOutPrintf(_T("%-*s"),iBiggestName
+ 1 , szTempFname
);
1253 ConOutPrintf(_T("\n"));
1256 /* Print Line sorted */
1259 for (i
= 0;i
< dwCount
;i
++)
1261 if (ptrFiles
[i
]->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1262 _stprintf(szTempFname
, _T("[%s]"), ptrFiles
[i
]->cFileName
);
1264 _stprintf(szTempFname
, _T("%s"), ptrFiles
[i
]->cFileName
);
1266 ConOutPrintf(_T("%-*s"), iBiggestName
+ 1, szTempFname
);
1267 /* We print a new line at the end of each column
1268 except for the case that it is the last item.*/
1269 if (!((i
+1)%iColumns
) && (i
< (dwCount
-1)))
1270 ConOutPrintf(_T("\n"));
1272 /* Add a new line after the last item */
1273 ConOutPrintf(_T("\n"));
1280 * The function that prints in old style
1283 DirPrintOldList(LPWIN32_FIND_DATA ptrFiles
[], /* [IN] Files' Info */
1284 DWORD dwCount
, /* [IN] The quantity of files */
1285 TCHAR
* szCurPath
, /* [IN] Full path of current directory */
1286 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
1288 DWORD i
; /* An indexer for "for"s */
1289 TCHAR szName
[10]; /* The name of file */
1290 TCHAR szExt
[5]; /* The extension of file */
1291 TCHAR szDate
[30],szTime
[30]; /* Used to format time and date */
1292 TCHAR szSize
[30]; /* The size of file */
1293 int iSizeFormat
; /* The format of size field */
1294 ULARGE_INTEGER u64FileSize
; /* The file size */
1296 for(i
= 0;i
< dwCount
;i
++)
1298 /* Broke 8.3 format */
1299 if (*ptrFiles
[i
]->cAlternateFileName
)
1301 /* If the file is long named then we read the alter name */
1302 getName( ptrFiles
[i
]->cAlternateFileName
, szName
);
1303 _tcscpy(szExt
, getExt( ptrFiles
[i
]->cAlternateFileName
));
1307 /* If the file is not long name we read its original name */
1308 getName( ptrFiles
[i
]->cFileName
, szName
);
1309 _tcscpy(szExt
, getExt( ptrFiles
[i
]->cFileName
));
1312 /* Calculate size */
1313 if (ptrFiles
[i
]->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1315 /* Directory, no size it's a directory*/
1317 _tcscpy(szSize
, _T("<DIR>"));
1323 u64FileSize
.HighPart
= ptrFiles
[i
]->nFileSizeHigh
;
1324 u64FileSize
.LowPart
= ptrFiles
[i
]->nFileSizeLow
;
1325 ConvertULargeInteger(u64FileSize
, szSize
, 20, lpFlags
->bTSeperator
);
1328 /* Format date and time */
1329 DirPrintFileDateTime(szDate
,szTime
,ptrFiles
[i
],lpFlags
);
1331 /* Print the line */
1332 ConOutPrintf(_T("%-8s %-3s %*s %s %s\n"),
1333 szName
, /* The file's 8.3 name */
1334 szExt
, /* The file's 8.3 extension */
1335 iSizeFormat
, /* print format for size column */
1336 szSize
, /* The size of file or "<DIR>" for dirs */
1337 szDate
, /* The date of file/dir */
1338 szTime
); /* The time of file/dir */
1346 * The function that prints in bare format
1349 DirPrintBareList(LPWIN32_FIND_DATA ptrFiles
[], /* [IN] Files' Info */
1350 DWORD dwCount
, /* [IN] The quantity of files */
1351 TCHAR
* szCurPath
, /* [IN] Full path of current directory */
1352 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
1354 DWORD i
; /* An indexer for "for"s */
1355 TCHAR szFullName
[MAX_PATH
]; /* The fullpath name of file */
1357 for(i
= 0;i
< dwCount
;i
++)
1359 if ((_tcscmp(ptrFiles
[i
]->cFileName
, _T(".")) == 0) ||
1360 (_tcscmp(ptrFiles
[i
]->cFileName
, _T("..")) == 0))
1362 /* at bare format we don't print "." and ".." folder */
1365 /* at recursive mode we print full path of file */
1366 if (lpFlags
->bRecursive
)
1368 _tcscpy(szFullName
, szCurPath
);
1369 _tcscat(szFullName
, ptrFiles
[i
]->cFileName
);
1370 ConOutPrintf(_T("%s\n"), szFullName
);
1372 /* if we are not in recursive mode we print the file names */
1374 ConOutPrintf(_T("%s\n"),ptrFiles
[i
]->cFileName
);
1382 * The functions that prints the files list
1385 DirPrintFiles(LPWIN32_FIND_DATA ptrFiles
[], /* [IN] Files' Info */
1386 DWORD dwCount
, /* [IN] The quantity of files */
1387 TCHAR
* szCurPath
, /* [IN] Full path of current directory */
1388 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
1390 TCHAR szTemp
[MAX_PATH
]; /* A buffer to format the directory header */
1391 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
1393 /* Print directory header */
1394 _tcscpy(szTemp
, szCurPath
);
1395 /* We cut the trailing \ of the full path */
1396 szTemp
[_tcslen(szTemp
)-1] = _T('\0');
1397 /* Condition to print header:
1398 We are not printing in bare format
1399 and if we are in recursive mode... we must have results */
1400 if (!(lpFlags
->bBareFormat
) && !((lpFlags
->bRecursive
) && (dwCount
<= 0)))
1402 LoadString( GetModuleHandle(NULL
), STRING_DIR_HELP7
, szMsg
,sizeof(szMsg
)/sizeof(TCHAR
));
1403 ConOutPrintf (szMsg
, szTemp
);
1407 if (lpFlags
->bBareFormat
)
1409 DirPrintBareList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1411 /* New list style / Short names */
1412 else if(lpFlags
->bShortName
)
1414 DirPrintNewList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1417 else if(lpFlags
->bWideListColSort
|| lpFlags
->bWideList
)
1419 DirPrintWideList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1422 else if (lpFlags
->bNewLongList
)
1424 DirPrintNewList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1426 /* If nothing is selected old list is the default */
1429 DirPrintOldList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1439 * Compares 2 files based on the order criteria
1443 LPWIN32_FIND_DATA lpFile1
, /* [IN] A pointer to WIN32_FIND_DATA of file 1 */
1444 LPWIN32_FIND_DATA lpFile2
, /* [IN] A pointer to WIN32_FIND_DATA of file 2 */
1445 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags that we use to list */
1447 /* u64 variables used to translate some broken 32bit info */
1448 ULARGE_INTEGER u64File1
, u64File2
;
1449 int i
; /* An indexer for "for"s */
1450 long iComp
= 0; /* The comparison result */
1452 /* Calculate critiries by order given from user */
1453 for (i
= 0;i
< lpFlags
->stOrderBy
.sCriteriaCount
;i
++)
1456 /* Calculate criteria */
1457 switch(lpFlags
->stOrderBy
.eCriteria
[i
])
1459 case ORDER_SIZE
: /* Order by size /o:s */
1460 /* concat the 32bit integers to a 64bit */
1461 u64File1
.LowPart
= lpFile1
->nFileSizeLow
;
1462 u64File1
.HighPart
= lpFile1
->nFileSizeHigh
;
1463 u64File2
.LowPart
= lpFile2
->nFileSizeLow
;
1464 u64File2
.HighPart
= lpFile2
->nFileSizeHigh
;
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 case ORDER_DIRECTORY
: /* Order by directory attribute /o:g */
1477 iComp
= ((lpFile2
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)-
1478 (lpFile1
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
));
1480 case ORDER_EXTENSION
: /* Order by extension name /o:e */
1481 iComp
= _tcsicmp(getExt(lpFile1
->cFileName
),getExt(lpFile2
->cFileName
));
1483 case ORDER_NAME
: /* Order by filename /o:n */
1484 iComp
= _tcsicmp(lpFile1
->cFileName
, lpFile2
->cFileName
);
1486 case ORDER_TIME
: /* Order by file's time /o:t */
1488 /* We compare files based on the time field selected by /t */
1489 switch(lpFlags
->stTimeField
.eTimeField
)
1491 case TF_CREATIONDATE
:
1492 /* concat the 32bit integers to a 64bit */
1493 u64File1
.LowPart
= lpFile1
->ftCreationTime
.dwLowDateTime
;
1494 u64File1
.HighPart
= lpFile1
->ftCreationTime
.dwHighDateTime
;
1495 u64File2
.LowPart
= lpFile2
->ftCreationTime
.dwLowDateTime
;
1496 u64File2
.HighPart
= lpFile2
->ftCreationTime
.dwHighDateTime
;
1498 case TF_LASTACCESSEDDATE
:
1499 /* concat the 32bit integers to a 64bit */
1500 u64File1
.LowPart
= lpFile1
->ftLastAccessTime
.dwLowDateTime
;
1501 u64File1
.HighPart
= lpFile1
->ftLastAccessTime
.dwHighDateTime
;
1502 u64File2
.LowPart
= lpFile2
->ftLastAccessTime
.dwLowDateTime
;
1503 u64File2
.HighPart
= lpFile2
->ftLastAccessTime
.dwHighDateTime
;
1505 case TF_MODIFIEDDATE
:
1506 /* concat the 32bit integers to a 64bit */
1507 u64File1
.LowPart
= lpFile1
->ftLastWriteTime
.dwLowDateTime
;
1508 u64File1
.HighPart
= lpFile1
->ftLastWriteTime
.dwHighDateTime
;
1509 u64File2
.LowPart
= lpFile2
->ftLastWriteTime
.dwLowDateTime
;
1510 u64File2
.HighPart
= lpFile2
->ftLastWriteTime
.dwHighDateTime
;
1514 /* In case that differnce is too big for a long */
1515 if (u64File1
.QuadPart
< u64File2
.QuadPart
)
1517 else if (u64File1
.QuadPart
> u64File2
.QuadPart
)
1523 /* Reverse if desired */
1524 if (lpFlags
->stOrderBy
.bCriteriaRev
[i
])
1527 /* If that criteria was enough for distinguishing
1528 the files/dirs,there is no need to calculate the others*/
1529 if (iComp
!= 0) break;
1532 /* Translate the value of iComp to boolean */
1542 * Sort files by the order criterias using quicksort method
1546 LPWIN32_FIND_DATA ptrArray
[], /* [IN/OUT] The array with file info pointers */
1547 int i
, /* [IN] The index of first item in array */
1548 int j
, /* [IN] The index to last item in array */
1549 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags that we will use to sort */
1551 LPWIN32_FIND_DATA lpTemp
; /* A temporary pointer */
1552 /* used for exchangin pointers */
1553 int First
, Last
, Temp
;
1558 First
= i
, Last
= j
, Way
= TRUE
;
1561 if (Way
== CompareFiles(ptrArray
[i
], ptrArray
[j
], lpFlags
))
1563 /* Swap the pointers of the array */
1564 lpTemp
= ptrArray
[i
];
1565 ptrArray
[i
]= ptrArray
[j
];
1566 ptrArray
[j
] = lpTemp
;
1567 /* Swap the indexers for inverting sorting */
1568 Temp
= i
, i
=j
,j
=Temp
;
1574 QsortFiles(ptrArray
,First
, i
-1, lpFlags
);
1575 QsortFiles(ptrArray
,i
+1,Last
, lpFlags
);
1584 * The functions that does everything except for printing results
1586 static INT
DirList (LPTSTR szPath
, /* [IN] The path that dir starts */
1587 LPTSTR szFilespec
, /* [IN] The type of file that we are looking for */
1588 LPINT pLine
, /* FIXME: Maybe used for paginating */
1589 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags of the listing */
1592 /* Internal linked list */
1593 struct TDirFindListNode
1595 WIN32_FIND_DATA stFindInfo
;
1596 struct TDirFindListNode
* ptrNext
;
1599 HANDLE hSearch
; /* The handle of the search */
1600 HANDLE hRecSearch
; /* The handle for searching recursivly */
1601 WIN32_FIND_DATA wfdFileInfo
; /* The info of file that found */
1602 LPWIN32_FIND_DATA
* ptrFileArray
; /* An array of pointers with all the files */
1603 struct TDirFindListNode
* ptrStartNode
; /* The pointer to the first node */
1604 struct TDirFindListNode
* ptrNextNode
; /* A pointer used for relatives refernces */
1605 TCHAR szFullPath
[MAX_PATH
]; /* The full path that we are listing with trailing \ */
1606 TCHAR szFullFileSpec
[MAX_PATH
]; /* The full path with file specs that we ll request\ */
1607 TCHAR szBytes
[20]; /* A string for converting ULARGE integer */
1608 DWORD dwCount
; /* A counter of files found in directory */
1609 DWORD dwCountFiles
; /* Counter for files */
1610 DWORD dwCountDirs
; /* Counter for directories */
1611 ULARGE_INTEGER u64CountBytes
; /* Counter for bytes */
1612 ULARGE_INTEGER u64Temp
; /* A temporary counter */
1613 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
1615 /* Initialize Variables */
1616 ptrStartNode
= NULL
;
1621 u64CountBytes
.QuadPart
= 0;
1623 /* Create szFullPath and szFullFileSpec */
1624 _tcscpy (szFullPath
, szPath
);
1625 if (szFullPath
[_tcslen(szFullPath
) - 1] != _T('\\'))
1626 _tcscat (szFullPath
, _T("\\"));
1627 _tcscpy (szFullFileSpec
, szFullPath
);
1628 _tcscat (szFullFileSpec
, szFilespec
);
1630 /* Prepare the linked list, first node is allocated */
1631 if ((ptrStartNode
= malloc(sizeof(struct TDirFindListNode
))) == NULL
)
1634 ConErrPrintf("DEBUG: Cannot allocate memory for ptrStartNode!\n");
1636 return 1; /* Error cannot allocate memory for 1st object */
1638 ptrNextNode
= ptrStartNode
;
1640 /* Collect the results for the current folder */
1641 hSearch
= FindFirstFile (szFullFileSpec
, &wfdFileInfo
);
1644 if (hSearch
!= INVALID_HANDLE_VALUE
)
1646 /* Here we filter all the specified attributes */
1647 if ((wfdFileInfo
.dwFileAttributes
& lpFlags
->stAttribs
.dwAttribMask
)
1648 == (lpFlags
->stAttribs
.dwAttribMask
& lpFlags
->stAttribs
.dwAttribVal
))
1650 ptrNextNode
->ptrNext
= malloc(sizeof(struct TDirFindListNode
));
1651 /* If malloc fails we go to next file in hope it works,
1652 without braking the linked list! */
1653 if (ptrNextNode
->ptrNext
)
1655 /* Copy the info of search at linked list */
1656 memcpy((void *)&ptrNextNode
->ptrNext
->stFindInfo
,
1657 (void *)&wfdFileInfo
,sizeof(WIN32_FIND_DATA
));
1659 /* If lower case is selected do it here */
1660 if (lpFlags
->bLowerCase
)
1662 _tcslwr(ptrNextNode
->ptrNext
->stFindInfo
.cAlternateFileName
);
1663 _tcslwr(ptrNextNode
->ptrNext
->stFindInfo
.cFileName
);
1666 /* Continue at next node at linked list */
1667 ptrNextNode
= ptrNextNode
->ptrNext
;
1670 /* Grab statistics */
1671 if (wfdFileInfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1680 u64Temp
.HighPart
= wfdFileInfo
.nFileSizeHigh
;
1681 u64Temp
.LowPart
= wfdFileInfo
.nFileSizeLow
;
1682 u64CountBytes
.QuadPart
+= u64Temp
.QuadPart
;
1688 }while(FindNextFile(hSearch
,&wfdFileInfo
));
1691 /* Terminate list */
1692 ptrNextNode
->ptrNext
= NULL
;
1694 /* Calculate and allocate space need for making an array of pointers */
1695 ptrFileArray
= malloc(sizeof(LPWIN32_FIND_DATA
)*dwCount
);
1696 if (!(ptrFileArray
))
1699 ConErrPrintf("DEBUG: Cannot allocate memory for ptrFileArray!\n");
1701 goto _DirList_clear_n_exit
;
1704 /* Create an array of pointers from the linked list
1705 this will be used to sort and print data, rather than the list */
1706 ptrNextNode
= ptrStartNode
;
1708 while(ptrNextNode
->ptrNext
)
1710 *(ptrFileArray
+ dwCount
) = &ptrNextNode
->ptrNext
->stFindInfo
;
1711 ptrNextNode
= ptrNextNode
->ptrNext
;
1715 /* Sort Data if requested*/
1716 if (lpFlags
->stOrderBy
.sCriteriaCount
> 0)
1717 QsortFiles(ptrFileArray
, 0, dwCount
-1,lpFlags
);
1720 DirPrintFiles(ptrFileArray
, dwCount
, szFullPath
, lpFlags
);
1725 /* Print Directory Summary */
1726 /* Condition to print summary is:
1727 If we are not in bare format and if we have results! */
1728 if (!(lpFlags
->bBareFormat
) && (dwCount
> 0))
1730 ConvertULargeInteger(u64CountBytes
, szBytes
, 20, lpFlags
->bTSeperator
);
1731 LoadString( GetModuleHandle(NULL
), STRING_DIR_HELP8
, szMsg
,sizeof(szMsg
)/sizeof(TCHAR
));
1732 ConOutPrintf (szMsg
,dwCountFiles
, szBytes
);
1735 /* Add statistics to recursive statistics*/
1736 recurse_dir_cnt
+= dwCountDirs
;
1737 recurse_file_cnt
+= dwCountFiles
;
1738 recurse_bytes
.QuadPart
+= u64CountBytes
.QuadPart
;
1740 /* Do the recursive job if requested
1741 the recursive is be done on ALL(indepent of their attribs)
1742 directoried of the current one.*/
1743 if (lpFlags
->bRecursive
)
1745 /* The new search is involving any *.* file */
1746 _tcscpy(szFullFileSpec
, szFullPath
);
1747 _tcscat(szFullFileSpec
, _T("*.*"));
1748 hRecSearch
= FindFirstFile (szFullFileSpec
, &wfdFileInfo
);
1751 if (hRecSearch
!= INVALID_HANDLE_VALUE
)
1753 /* We search for directories other than "." and ".." */
1754 if ((_tcsicmp(wfdFileInfo
.cFileName
, _T(".")) != 0)
1755 && (_tcsicmp(wfdFileInfo
.cFileName
, _T("..")) != 0 )
1756 && (wfdFileInfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1758 /* Concat the path and the directory to do recursive */
1759 _tcscpy(szFullFileSpec
, szFullPath
);
1760 _tcscat(szFullFileSpec
, wfdFileInfo
.cFileName
);
1761 /* We do the same for tha folder */
1762 DirList(szFullFileSpec
, szFilespec
, pLine
,lpFlags
);
1765 }while(FindNextFile(hRecSearch
,&wfdFileInfo
));
1766 FindClose(hRecSearch
);
1769 _DirList_clear_n_exit
:
1770 /* Deallocate memory */
1771 /* Free linked list */
1772 while (ptrStartNode
)
1774 ptrNextNode
= ptrStartNode
->ptrNext
;
1776 ptrStartNode
= ptrNextNode
;
1788 * internal dir command
1790 INT
CommandDir (LPTSTR first
, LPTSTR rest
)
1792 TCHAR dircmd
[256]; /* A variable to store the DIRCMD enviroment variable */
1793 TCHAR szPath
[MAX_PATH
];
1794 TCHAR szFilespec
[MAX_PATH
];
1797 DIRSWITCHFLAGS stFlags
;
1799 /* Initialize variables */
1800 recurse_dir_cnt
= 0L;
1801 recurse_file_cnt
= 0L;
1802 recurse_bytes
.QuadPart
= 0;
1804 /* Initialize Switch Flags < Default switches are setted here!> */
1805 stFlags
.b4Digit
= TRUE
;
1806 stFlags
.bBareFormat
= FALSE
;
1807 stFlags
.bLowerCase
= FALSE
;
1808 stFlags
.bNewLongList
= TRUE
;
1809 stFlags
.bPause
= FALSE
;
1810 stFlags
.bRecursive
= FALSE
;
1811 stFlags
.bShortName
= FALSE
;
1812 stFlags
.bTSeperator
= TRUE
;
1813 stFlags
.bUser
= FALSE
;
1814 stFlags
.bWideList
= FALSE
;
1815 stFlags
.bWideListColSort
= FALSE
;
1816 stFlags
.stTimeField
.eTimeField
= TF_MODIFIEDDATE
;
1817 stFlags
.stTimeField
.bUnSet
= FALSE
;
1818 stFlags
.stAttribs
.dwAttribMask
= FILE_ATTRIBUTE_HIDDEN
| FILE_ATTRIBUTE_SYSTEM
;
1819 stFlags
.stAttribs
.dwAttribVal
= 0L;
1820 stFlags
.stAttribs
.bUnSet
= FALSE
;
1821 stFlags
.stOrderBy
.sCriteriaCount
= 0;
1822 stFlags
.stOrderBy
.bUnSet
= FALSE
;
1824 /* read the parameters from the DIRCMD environment variable */
1825 if (GetEnvironmentVariable (_T("DIRCMD"), dircmd
, 256))
1826 if (!DirReadParam(dircmd
, ¶m
, &stFlags
))
1829 /* read the parameters */
1830 if (!DirReadParam(rest
, ¶m
, &stFlags
))
1833 /* default to current directory */
1837 /* parse the directory info */
1838 if (DirParsePathspec (param
, szPath
, szFilespec
))
1842 Uncomment this to show the final state of switch flags*/
1844 ConOutPrintf("Attributes mask/value %x/%x\n",stFlags.stAttribs.dwAttribMask,stFlags.stAttribs.dwAttribVal );
1845 ConOutPrintf("(B) Bare format : %i\n", stFlags.bBareFormat );
1846 ConOutPrintf("(C) Thousand : %i\n", stFlags.bTSeperator );
1847 ConOutPrintf("(W) Wide list : %i\n", stFlags.bWideList );
1848 ConOutPrintf("(D) Wide list sort by column : %i\n", stFlags.bWideListColSort );
1849 ConOutPrintf("(L) Lowercase : %i\n", stFlags.bLowerCase );
1850 ConOutPrintf("(N) New : %i\n", stFlags.bNewLongList );
1851 ConOutPrintf("(O) Order : %i\n", stFlags.stOrderBy.sCriteriaCount );
1853 for (i =0;i<stFlags.stOrderBy.sCriteriaCount;i++)
1854 ConOutPrintf(" Order Criteria [%i]: %i (Reversed: %i)\n",i, stFlags.stOrderBy.eCriteria[i], stFlags.stOrderBy.bCriteriaRev[i] );
1855 ConOutPrintf("(P) Pause : %i\n", stFlags.bPause );
1856 ConOutPrintf("(Q) Owner : %i\n", stFlags.bUser );
1857 ConOutPrintf("(S) Recursive : %i\n", stFlags.bRecursive );
1858 ConOutPrintf("(T) Time field : %i\n", stFlags.stTimeField.eTimeField );
1859 ConOutPrintf("(X) Short names : %i\n", stFlags.bShortName );
1860 ConOutPrintf("Parameter : %s\n", param );
1863 /* print the header */
1864 if (!stFlags
.bBareFormat
)
1865 if (!PrintDirectoryHeader (szPath
, &nLine
, &stFlags
))
1868 /* do the actual dir */
1869 if (DirList (szPath
, szFilespec
, &nLine
, &stFlags
))
1872 /* print the footer */
1873 PrintSummary(szPath
,