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
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
138 #include "resource.h"
140 #ifdef INCLUDE_CMD_DIR
144 /* Time Field enumeration */
149 TF_LASTACCESSEDDATE
= 2
152 /* Ordered by enumeration */
162 /* The struct for holding the switches */
163 typedef struct _DirSwitchesFlags
165 BOOL bBareFormat
; /* Bare Format */
166 BOOL bTSeperator
; /* Thousands seperator */
167 BOOL bWideList
; /* Wide list format */
168 BOOL bWideListColSort
; /* Wide list format but sorted by column */
169 BOOL bLowerCase
; /* Uses lower case */
170 BOOL bNewLongList
; /* New long list */
171 BOOL bPause
; /* Pause per page */
172 BOOL bUser
; /* Displays the owner of file */
173 BOOL bRecursive
; /* Displays files in specified directory and all sub */
174 BOOL bShortName
; /* Displays the sort name of files if exist */
175 BOOL b4Digit
; /* Four digit year */
178 DWORD dwAttribVal
; /* The desired state of attribute */
179 DWORD dwAttribMask
; /* Which attributes to check */
180 BOOL bUnSet
; /* A helper flag if "-" was given with the switch */
181 BOOL bParSetted
; /* A helper flag if parameters of switch were given */
182 } stAttribs
; /* Displays files with this attributes only */
185 enum EOrderBy eCriteria
[3]; /* Criterias used to order by */
186 BOOL bCriteriaRev
[3]; /* If the criteria is in reversed order */
187 short sCriteriaCount
; /* The quantity of criterias */
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 } stOrderBy
; /* Ordered by criterias */
193 enum ETimeField eTimeField
; /* The time field that will be used for */
194 BOOL bUnSet
; /* A helper flag if "-" was given with the switch */
195 BOOL bParSetted
; /* A helper flag if parameters of switch were given */
196 } stTimeField
; /* The time field to display or use for sorting */
197 } DIRSWITCHFLAGS
, *LPDIRSWITCHFLAGS
;
200 typedef struct _DIRFINDLISTNODE
202 WIN32_FIND_DATA stFindInfo
;
203 struct _DIRFINDLISTNODE
*ptrNext
;
204 } DIRFINDLISTNODE
, *PDIRFINDLISTNODE
;
208 (WINAPI
*PGETFREEDISKSPACEEX
)(LPCTSTR
, PULARGE_INTEGER
, PULARGE_INTEGER
, PULARGE_INTEGER
);
211 /* Globally save the # of dirs, files and bytes,
212 * probabaly later pass them to functions. Rob Lake */
213 static ULONG recurse_dir_cnt
;
214 static ULONG recurse_file_cnt
;
215 static ULARGE_INTEGER recurse_bytes
;
221 * displays help screen for dir
227 ConOutResPaging(TRUE
, STRING_DIR_HELP1
);
235 * Parse the parameters and switches of the command line and exports them
238 DirReadParam(LPTSTR Line
, /* [IN] The line with the parameters & switches */
239 LPTSTR
** params
, /* [OUT] The parameters after parsing */
240 LPINT entries
, /* [OUT] The number of parameters after parsing */
241 LPDIRSWITCHFLAGS lpFlags
) /* [IN/OUT] The flags after calculating switches */
243 TCHAR cCurSwitch
; /* The current switch */
244 TCHAR cCurChar
; /* Current examing character */
245 TCHAR cCurUChar
; /* Current upper examing character */
246 BOOL bNegative
; /* Negative switch */
247 BOOL bPNegative
; /* Negative switch parameter */
248 BOOL bIntoQuotes
; /* A flag showing if we are in quotes (") */
249 LPTSTR ptrStart
; /* A pointer to the first character of a parameter */
250 LPTSTR ptrEnd
; /* A pointer to the last character of a parameter */
253 /* Initialize parameter array */
254 *params
= malloc(sizeof(LPTSTR
));
262 /* Initialize variables; */
263 cCurSwitch
= _T(' ');
268 /* We suppose that switch parameters
269 were given to avoid setting them to default
270 if the switch was not given */
271 lpFlags
->stAttribs
.bParSetted
= TRUE
;
272 lpFlags
->stOrderBy
.bParSetted
= TRUE
;
273 lpFlags
->stTimeField
.bParSetted
= TRUE
;
276 /* Main Loop (see README_DIR.txt) */
277 /* scan the command line char per char, and we process its char */
280 /* we save current character as it is and its upper case */
282 cCurUChar
= _totupper(*Line
);
284 /* 1st section (see README_DIR.txt) */
285 /* When a switch is expecting */
286 if (cCurSwitch
== _T('/'))
288 if ((cCurUChar
== _T('A')) ||(cCurUChar
== _T('T')) || (cCurUChar
== _T('O')))
290 cCurSwitch
= cCurUChar
;
294 lpFlags
->stAttribs
.bUnSet
= bNegative
;
295 lpFlags
->stAttribs
.bParSetted
= FALSE
;
298 lpFlags
->stTimeField
.bUnSet
= bNegative
;
299 lpFlags
->stTimeField
.bParSetted
= FALSE
;
302 lpFlags
->stOrderBy
.bUnSet
= bNegative
;
303 lpFlags
->stOrderBy
.bParSetted
= FALSE
;
307 else if (cCurUChar
== _T('L'))
308 lpFlags
->bLowerCase
= ! bNegative
;
309 else if (cCurUChar
== _T('B'))
310 lpFlags
->bBareFormat
= ! bNegative
;
311 else if (cCurUChar
== _T('C'))
312 lpFlags
->bTSeperator
= ! bNegative
;
313 else if (cCurUChar
== _T('W'))
314 lpFlags
->bWideList
= ! bNegative
;
315 else if (cCurUChar
== _T('D'))
316 lpFlags
->bWideListColSort
= ! bNegative
;
317 else if (cCurUChar
== _T('N'))
318 lpFlags
->bNewLongList
= ! bNegative
;
319 else if (cCurUChar
== _T('P'))
320 lpFlags
->bPause
= ! bNegative
;
321 else if (cCurUChar
== _T('Q'))
322 lpFlags
->bUser
= ! bNegative
;
323 else if (cCurUChar
== _T('S'))
324 lpFlags
->bRecursive
= ! bNegative
;
325 else if (cCurUChar
== _T('X'))
326 lpFlags
->bShortName
= ! bNegative
;
327 else if (cCurChar
== _T('4'))
328 lpFlags
->b4Digit
= ! bNegative
;
329 else if (cCurChar
== _T('?'))
334 else if (cCurChar
== _T('-'))
340 error_invalid_switch ((TCHAR
)_totupper (*Line
));
344 /* We check if we calculated the negative value and realese the flag */
345 if ((cCurChar
!= _T('-')) && bNegative
)
348 /* if not a,o,t or - option then next parameter is not a switch */
349 if ((cCurSwitch
== _T('/')) && (!bNegative
))
350 cCurSwitch
= _T(' ');
353 else if ((cCurSwitch
== _T(' ')) || (cCurSwitch
== _T('P')))
355 /* 2nd section (see README_DIR.txt) */
356 /* We are expecting parameter or the unknown */
358 if (cCurChar
== _T('/'))
359 cCurSwitch
= _T('/');
361 /* Process a spacer */
362 else if (cCurChar
== _T(' '))
366 cCurSwitch
= _T(' ');
367 if(ptrStart
&& ptrEnd
)
369 temp
= malloc((ptrEnd
- ptrStart
) + 2 * sizeof (TCHAR
));
372 memcpy(temp
, ptrStart
, (ptrEnd
- ptrStart
) + 2 * sizeof (TCHAR
));
373 temp
[(ptrEnd
- ptrStart
+ 1)] = _T('\0');
374 if(!add_entry(entries
, params
, temp
))
389 else if (cCurChar
== _T('\"'))
391 /* Process a quote */
392 bIntoQuotes
= !bIntoQuotes
;
398 /* Process a character for parameter */
399 if ((cCurSwitch
== _T(' ')) && ptrStart
&& ptrEnd
)
401 temp
= malloc((ptrEnd
- ptrStart
) + 2 * sizeof (TCHAR
));
404 memcpy(temp
, ptrStart
, (ptrEnd
- ptrStart
) + 2 * sizeof (TCHAR
));
405 temp
[(ptrEnd
- ptrStart
+ 1)] = _T('\0');
406 if(!add_entry(entries
, params
, temp
))
418 cCurSwitch
= _T('P');
420 ptrStart
= ptrEnd
= Line
;
426 /* 3rd section (see README_DIR.txt) */
427 /* We are waiting for switch parameters */
429 /* Check if there are no more switch parameters */
430 if ((cCurChar
== _T('/')) || ( cCurChar
== _T(' ')))
432 /* Wrong desicion path, reprocess current character */
433 cCurSwitch
= cCurChar
;
436 /* Process parameter switch */
439 case _T('A'): /* Switch parameters for /A (attributes filter) */
440 /* Ok a switch parameter was given */
441 lpFlags
->stAttribs
.bParSetted
= TRUE
;
443 if (cCurChar
== _T(':'))
444 /* =V= dead command, used to make the "if" work */
446 else if(cCurChar
== _T('-'))
448 else if(cCurUChar
== _T('D'))
450 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_DIRECTORY
;
452 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_DIRECTORY
;
454 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_DIRECTORY
;
456 else if(cCurUChar
== _T('R'))
458 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_READONLY
;
460 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_READONLY
;
462 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_READONLY
;
464 else if(cCurUChar
== _T('H'))
466 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_HIDDEN
;
468 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_HIDDEN
;
470 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_HIDDEN
;
472 else if(cCurUChar
== _T('A'))
474 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_ARCHIVE
;
476 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_ARCHIVE
;
478 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_ARCHIVE
;
480 else if(cCurUChar
== _T('S'))
482 lpFlags
->stAttribs
.dwAttribMask
|= FILE_ATTRIBUTE_SYSTEM
;
484 lpFlags
->stAttribs
.dwAttribVal
&= ~FILE_ATTRIBUTE_SYSTEM
;
486 lpFlags
->stAttribs
.dwAttribVal
|= FILE_ATTRIBUTE_SYSTEM
;
490 error_parameter_format((TCHAR
)_totupper (*Line
));
494 case _T('T'): /* Switch parameters for /T (time field) */
496 /* Ok a switch parameter was given */
497 lpFlags
->stTimeField
.bParSetted
= TRUE
;
499 if (cCurChar
== _T(':'))
500 /* =V= dead command, used to make the "if" work */
502 else if(cCurUChar
== _T('C'))
503 lpFlags
->stTimeField
.eTimeField
= TF_CREATIONDATE
;
504 else if(cCurUChar
== _T('A'))
505 lpFlags
->stTimeField
.eTimeField
= TF_LASTACCESSEDDATE
;
506 else if(cCurUChar
== _T('W'))
507 lpFlags
->stTimeField
.eTimeField
= TF_MODIFIEDDATE
;
510 error_parameter_format((TCHAR
)_totupper (*Line
));
514 case _T('O'): /* Switch parameters for /O (order) */
515 /* Ok a switch parameter was given */
516 lpFlags
->stOrderBy
.bParSetted
= TRUE
;
518 if (cCurChar
== _T(':'))
519 /* <== dead command, used to make the "if" work */
521 else if(cCurChar
== _T('-'))
523 else if(cCurUChar
== _T('N'))
525 if (lpFlags
->stOrderBy
.sCriteriaCount
< 3) lpFlags
->stOrderBy
.sCriteriaCount
++;
526 lpFlags
->stOrderBy
.bCriteriaRev
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = bPNegative
;
527 lpFlags
->stOrderBy
.eCriteria
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = ORDER_NAME
;
529 else if(cCurUChar
== _T('S'))
531 if (lpFlags
->stOrderBy
.sCriteriaCount
< 3) lpFlags
->stOrderBy
.sCriteriaCount
++;
532 lpFlags
->stOrderBy
.bCriteriaRev
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = bPNegative
;
533 lpFlags
->stOrderBy
.eCriteria
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = ORDER_SIZE
;
535 else if(cCurUChar
== _T('G'))
537 if (lpFlags
->stOrderBy
.sCriteriaCount
< 3) lpFlags
->stOrderBy
.sCriteriaCount
++;
538 lpFlags
->stOrderBy
.bCriteriaRev
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = bPNegative
;
539 lpFlags
->stOrderBy
.eCriteria
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = ORDER_DIRECTORY
;
541 else if(cCurUChar
== _T('E'))
543 if (lpFlags
->stOrderBy
.sCriteriaCount
< 3) lpFlags
->stOrderBy
.sCriteriaCount
++;
544 lpFlags
->stOrderBy
.bCriteriaRev
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = bPNegative
;
545 lpFlags
->stOrderBy
.eCriteria
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = ORDER_EXTENSION
;
547 else if(cCurUChar
== _T('D'))
549 if (lpFlags
->stOrderBy
.sCriteriaCount
< 3) lpFlags
->stOrderBy
.sCriteriaCount
++;
550 lpFlags
->stOrderBy
.bCriteriaRev
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = bPNegative
;
551 lpFlags
->stOrderBy
.eCriteria
[lpFlags
->stOrderBy
.sCriteriaCount
- 1] = ORDER_TIME
;
556 error_parameter_format((TCHAR
)_totupper (*Line
));
562 /* We check if we calculated the negative value and realese the flag */
563 if ((cCurChar
!= _T('-')) && bPNegative
)
569 /* Terminate the parameters */
570 if(ptrStart
&& ptrEnd
)
572 temp
= malloc((ptrEnd
- ptrStart
) + 2 * sizeof (TCHAR
));
575 memcpy(temp
, ptrStart
, (ptrEnd
- ptrStart
) + 2 * sizeof (TCHAR
));
576 temp
[(ptrEnd
- ptrStart
+ 1)] = _T('\0');
577 if(!add_entry(entries
, params
, temp
))
590 /* Calculate the switches with no switch paramater */
591 if (!(lpFlags
->stAttribs
.bParSetted
))
593 lpFlags
->stAttribs
.dwAttribVal
= 0L;
594 lpFlags
->stAttribs
.dwAttribMask
= lpFlags
->stAttribs
.dwAttribVal
;
596 if (!(lpFlags
->stOrderBy
.bParSetted
))
598 lpFlags
->stOrderBy
.sCriteriaCount
= 1;
599 lpFlags
->stOrderBy
.eCriteria
[0] = ORDER_NAME
;
600 lpFlags
->stOrderBy
.bCriteriaRev
[0] = FALSE
;
602 if (!(lpFlags
->stOrderBy
.bParSetted
))
603 lpFlags
->stTimeField
.eTimeField
= TF_MODIFIEDDATE
;
605 /* Calculate the unsetted switches (the "-" prefixed)*/
606 if (lpFlags
->stAttribs
.bUnSet
)
608 lpFlags
->stAttribs
.bUnSet
= FALSE
;
609 lpFlags
->stAttribs
.dwAttribVal
= 0L;
610 lpFlags
->stAttribs
.dwAttribMask
= FILE_ATTRIBUTE_HIDDEN
| FILE_ATTRIBUTE_SYSTEM
;
612 if (lpFlags
->stOrderBy
.bUnSet
)
614 lpFlags
->stOrderBy
.bUnSet
= FALSE
;
615 lpFlags
->stOrderBy
.sCriteriaCount
= 0;
617 if (lpFlags
->stTimeField
.bUnSet
)
619 lpFlags
->stTimeField
.bUnSet
= FALSE
;
620 lpFlags
->stTimeField
.eTimeField
= TF_MODIFIEDDATE
;
629 * extend the filespec, possibly adding wildcards
632 ExtendFilespec (LPTSTR file
)
640 /* if no file spec, change to "*.*" */
641 if (*file
== _T('\0'))
643 _tcscpy (file
, _T("*.*"));
647 // add support for *.
648 if ((file
[0] == _T('*')) && (file
[1] == _T('.') ))
653 /* if starts with . add * in front */
654 if (*file
== _T('.'))
656 memmove (&file
[1], &file
[0], (_tcslen (file
) + 1) * sizeof(TCHAR
));
661 if (!_tcschr (file
, _T('.')))
663 _tcscat (file
, _T(".*"));
669 /* if last character is '.' add '*' */
670 len
= _tcslen (file
);
671 if (file
[len
- 1] == _T('.'))
673 _tcscat (file
, _T("*"));
682 * split the pathspec into drive, directory, and filespec
685 DirParsePathspec (LPTSTR szPathspec
, LPTSTR szPath
, LPTSTR szFilespec
)
687 TCHAR szOrigPath
[MAX_PATH
];
691 BOOL bWildcards
= FALSE
;
693 GetCurrentDirectory (MAX_PATH
, szOrigPath
);
695 /* get the drive and change to it */
696 if (szPathspec
[1] == _T(':'))
698 TCHAR szRootPath
[] = _T("A:");
700 szRootPath
[0] = szPathspec
[0];
701 start
= szPathspec
+ 2;
702 if (!SetCurrentDirectory (szRootPath
))
704 ErrorMessage (GetLastError(), NULL
);
714 /* check for wildcards */
715 for (i
= 0; szPathspec
[i
]; i
++)
717 if (szPathspec
[i
] == _T('*') || szPathspec
[i
] == _T('?'))
721 /* check if this spec is a directory */
724 if (SetCurrentDirectory (szPathspec
))
726 _tcscpy (szFilespec
, _T("*.*"));
728 if (!GetCurrentDirectory (MAX_PATH
, szPath
))
730 szFilespec
[0] = _T('\0');
731 SetCurrentDirectory (szOrigPath
);
732 error_out_of_memory();
736 SetCurrentDirectory (szOrigPath
);
741 /* find the file spec */
742 tmp
= _tcsrchr (start
, _T('\\'));
744 /* if no path is specified */
747 _tcscpy (szFilespec
, start
);
748 ExtendFilespec (szFilespec
);
749 if (!GetCurrentDirectory (MAX_PATH
, szPath
))
751 szFilespec
[0] = _T('\0');
752 SetCurrentDirectory (szOrigPath
);
753 error_out_of_memory();
757 SetCurrentDirectory (szOrigPath
);
761 /* get the filename */
762 _tcscpy (szFilespec
, tmp
+1);
763 ExtendFilespec (szFilespec
);
767 /* change to the root directory */
768 if (!SetCurrentDirectory (_T("\\")))
770 szFilespec
[0] = _T('\0');
771 SetCurrentDirectory (szOrigPath
);
772 error_path_not_found ();
780 /* change to this directory */
781 if (!SetCurrentDirectory (start
))
784 szFilespec
[0] = _T('\0');
785 SetCurrentDirectory (szOrigPath
);
786 error_path_not_found ();
791 /* get the full name of the directory */
792 if (!GetCurrentDirectory (MAX_PATH
, szPath
))
795 szFilespec
[0] = _T('\0');
796 SetCurrentDirectory (szOrigPath
);
797 error_out_of_memory ();
803 SetCurrentDirectory (szOrigPath
);
810 * PrintDirectoryHeader
812 * print the header for the dir command
815 PrintDirectoryHeader(LPTSTR szPath
, LPDIRSWITCHFLAGS lpFlags
)
817 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
818 TCHAR szRootName
[MAX_PATH
];
823 if (lpFlags
->bBareFormat
)
826 /* build usable root path */
827 if (szPath
[1] == _T(':') && szPath
[2] == _T('\\'))
830 szRootName
[0] = szPath
[0];
831 szRootName
[1] = _T(':');
832 szRootName
[2] = _T('\\');
835 else if (szPath
[0] == _T('\\') && szPath
[1] == _T('\\'))
838 p
= _tcschr(&szPath
[2], _T('\\'));
841 error_invalid_drive();
844 p
= _tcschr(p
+1, _T('\\'));
847 _tcscpy(szRootName
, szPath
);
848 _tcscat(szRootName
, _T("\\"));
853 _tcscpy(szRootName
, szPath
);
854 _tcscat(szRootName
, _T("\\"));
860 error_invalid_drive();
864 /* get the media ID of the drive */
865 if (!GetVolumeInformation(szRootName
, szVolName
, 80, &dwSerialNr
,
866 NULL
, NULL
, NULL
, 0))
868 error_invalid_drive();
872 /* print drive info */
873 if (szVolName
[0] != _T('\0'))
875 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP2
, szMsg
, RC_STRING_MAX_SIZE
);
876 //needs to have first paramter as TRUE because
877 //this is the first output and need to clear the static
879 ConOutPrintfPaging(TRUE
,szMsg
, szRootName
[0], szVolName
);
881 ConOutPrintf(szMsg
, szRootName
[0], szVolName
);
886 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP3
, szMsg
, RC_STRING_MAX_SIZE
);
888 ConOutPrintfPaging(TRUE
,szMsg
, szRootName
[0]);
890 ConOutPrintf(szMsg
, szRootName
[0]);
893 /* print the volume serial number if the return was successful */
894 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP4
, (LPTSTR
) szMsg
, RC_STRING_MAX_SIZE
);
896 ConOutPrintfPaging(FALSE
,szMsg
,
912 * insert commas into a number
917 ConvertULong (ULONG num
, LPTSTR des
, INT len
)
934 if (((c
+ 1) % (nNumberGroups
+ 1)) == 0)
935 temp
[30 - c
++] = cThousandSeparator
;
936 temp
[30 - c
++] = (TCHAR
)(num
% 10) + _T('0');
940 for (n
= 0; n
<= c
; n
++)
941 des
[n
] = temp
[31 - c
+ n
];
949 DirPrintFileDateTime(TCHAR
*lpDate
,
951 LPWIN32_FIND_DATA lpFile
,
952 LPDIRSWITCHFLAGS lpFlags
)
960 /* Select the right time field */
961 switch (lpFlags
->stTimeField
.eTimeField
)
963 case TF_CREATIONDATE
:
964 if (!FileTimeToLocalFileTime(&lpFile
->ftCreationTime
, &ft
))
966 FileTimeToSystemTime(&ft
, &dt
);
969 case TF_LASTACCESSEDDATE
:
970 if (!FileTimeToLocalFileTime(&lpFile
->ftLastAccessTime
, &ft
))
972 FileTimeToSystemTime(&ft
, &dt
);
975 case TF_MODIFIEDDATE
:
976 if (!FileTimeToLocalFileTime(&lpFile
->ftLastWriteTime
, &ft
))
978 FileTimeToSystemTime(&ft
, &dt
);
983 wYear
= (lpFlags
->b4Digit
) ? dt
.wYear
: dt
.wYear
%100;
988 _stprintf (szDate
, _T("%02d%c%02d%c%0*d"),
989 dt
.wMonth
, cDateSeparator
,
990 dt
.wDay
, cDateSeparator
,
991 lpFlags
->b4Digit
?4:2, wYear
);
995 _stprintf (szDate
, _T("%02d%c%02d%c%0*d"),
996 dt
.wDay
, cDateSeparator
, dt
.wMonth
,
997 cDateSeparator
,lpFlags
->b4Digit
?4:2, wYear
);
1000 case 2: /* yymmdd */
1001 _stprintf (szDate
, _T("%0*d%c%02d%c%02d"),
1002 lpFlags
->b4Digit
?4:2, wYear
, cDateSeparator
,
1003 dt
.wMonth
, cDateSeparator
, dt
.wDay
);
1007 switch (nTimeFormat
)
1009 case 0: /* 12 hour format */
1011 _stprintf (szTime
,_T(" %02d%c%02u%c"),
1012 (dt
.wHour
== 0 ? 12 : (dt
.wHour
<= 12 ? dt
.wHour
: dt
.wHour
- 12)),
1014 dt
.wMinute
, (dt
.wHour
<= 11 ? _T('a') : _T('p')));
1017 case 1: /* 24 hour format */
1018 _stprintf (szTime
, _T(" %02d%c%02u"),
1019 dt
.wHour
, cTimeSeparator
, dt
.wMinute
);
1023 _tcscpy(lpDate
, szDate
);
1024 _tcscpy(lpTime
, szTime
);
1029 GetUserDiskFreeSpace(LPCTSTR lpRoot
,
1030 PULARGE_INTEGER lpFreeSpace
)
1032 PGETFREEDISKSPACEEX pGetFreeDiskSpaceEx
;
1033 HINSTANCE hInstance
;
1038 ULARGE_INTEGER TotalNumberOfBytes
, TotalNumberOfFreeBytes
;
1040 lpFreeSpace
->QuadPart
= 0;
1042 hInstance
= LoadLibrary(_T("KERNEL32"));
1043 if (hInstance
!= NULL
)
1045 pGetFreeDiskSpaceEx
= (PGETFREEDISKSPACEEX
)GetProcAddress(hInstance
,
1047 "GetDiskFreeSpaceExW");
1049 "GetDiskFreeSpaceExA");
1051 if (pGetFreeDiskSpaceEx
!= NULL
)
1053 if (pGetFreeDiskSpaceEx(lpRoot
, lpFreeSpace
, &TotalNumberOfBytes
, &TotalNumberOfFreeBytes
) == TRUE
)
1056 FreeLibrary(hInstance
);
1059 GetDiskFreeSpace(lpRoot
,
1065 lpFreeSpace
->QuadPart
= dwSecPerCl
* dwBytPerSec
* dwFreeCl
;
1070 * print_summary: prints dir summary
1071 * Added by Rob Lake 06/17/98 to compact code
1072 * Just copied Tim's Code and patched it a bit
1076 PrintSummary(LPTSTR szPath
,
1079 ULARGE_INTEGER u64Bytes
,
1080 LPDIRSWITCHFLAGS lpFlags
)
1082 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
1084 ULARGE_INTEGER uliFree
;
1085 TCHAR szRoot
[] = _T("A:\\");
1088 /* Here we check if we didn't find anything */
1089 if (!(ulFiles
+ ulDirs
))
1091 error_file_not_found();
1096 /* In bare format we don't print results */
1097 if (lpFlags
->bBareFormat
)
1100 /* Print recursive specific results */
1102 /* Take this code offline to fix /S does not print duoble info */
1103 if (lpFlags
->bRecursive
)
1105 ConvertULargeInteger(u64Bytes
, szBuffer
, sizeof(szBuffer
), lpFlags
->bTSeperator
);
1106 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP5
, szMsg
, RC_STRING_MAX_SIZE
);
1108 ConOutPrintfPaging(FALSE
,szMsg
,ulFiles
, szBuffer
);
1110 ConOutPrintf(szMsg
,ulFiles
, szBuffer
);
1115 /* Print File Summary */
1116 /* Condition to print summary is:
1117 If we are not in bare format and if we have results! */
1120 ConvertULargeInteger(u64Bytes
, szBuffer
, 20, lpFlags
->bTSeperator
);
1121 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP8
, szMsg
, RC_STRING_MAX_SIZE
);
1123 ConOutPrintfPaging(FALSE
,szMsg
,ulFiles
, szBuffer
);
1125 ConOutPrintf(szMsg
,ulFiles
, szBuffer
);
1130 /* Print total directories and freespace */
1131 szRoot
[0] = szPath
[0];
1132 GetUserDiskFreeSpace(szRoot
, &uliFree
);
1133 ConvertULargeInteger(uliFree
, szBuffer
, sizeof(szBuffer
), lpFlags
->bTSeperator
);
1134 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP6
, (LPTSTR
) szMsg
, RC_STRING_MAX_SIZE
);
1136 ConOutPrintfPaging(FALSE
,szMsg
,ulDirs
, szBuffer
);
1138 ConOutPrintf(szMsg
,ulDirs
, szBuffer
);
1146 * Get the extension of a filename
1148 TCHAR
* getExt(const TCHAR
* file
)
1150 static TCHAR
*NoExt
= _T("");
1151 TCHAR
* lastdot
= _tcsrchr(file
, _T('.'));
1152 return (lastdot
!= NULL
? lastdot
+ 1 : NoExt
);
1158 * Get the name of the file without extension
1161 getName(const TCHAR
* file
, TCHAR
* dest
)
1166 /* Check for "." and ".." folders */
1167 if ((_tcscmp(file
, _T(".")) == 0) ||
1168 (_tcscmp(file
, _T("..")) == 0))
1174 end
= _tcsrchr(file
, _T('.'));
1176 iLen
= _tcslen(file
);
1178 iLen
= (end
- file
);
1181 _tcsncpy(dest
, file
, iLen
);
1182 *(dest
+ iLen
) = _T('\0');
1191 * The function that prints in new style
1194 DirPrintNewList(LPWIN32_FIND_DATA ptrFiles
[], /* [IN]Files' Info */
1195 DWORD dwCount
, /* [IN] The quantity of files */
1196 TCHAR
*szCurPath
, /* [IN] Full path of current directory */
1197 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
1201 TCHAR szShortName
[15];
1205 ULARGE_INTEGER u64FileSize
;
1207 for (i
= 0;i
< dwCount
;i
++)
1209 /* Calculate size */
1210 if (ptrFiles
[i
]->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1214 _tcscpy(szSize
, _T("<DIR>"));
1220 u64FileSize
.HighPart
= ptrFiles
[i
]->nFileSizeHigh
;
1221 u64FileSize
.LowPart
= ptrFiles
[i
]->nFileSizeLow
;
1222 ConvertULargeInteger(u64FileSize
, szSize
, 20, lpFlags
->bTSeperator
);
1225 /* Calculate short name */
1226 szShortName
[0] = _T('\0');
1227 if (lpFlags
->bShortName
)
1228 _stprintf(szShortName
, _T(" %-12s"), ptrFiles
[i
]->cAlternateFileName
);
1230 /* Format date and time */
1231 DirPrintFileDateTime(szDate
, szTime
, ptrFiles
[i
], lpFlags
);
1233 /* Print the line */
1236 if (ConOutPrintfPaging(FALSE
,_T("%10s %-8s %*s%s %s\n"),
1242 ptrFiles
[i
]->cFileName
) == 1)
1246 ConOutPrintf(_T("%10s %-8s %*s%s %s\n"),
1252 ptrFiles
[i
]->cFileName
);
1260 * The function that prints in wide list
1263 DirPrintWideList(LPWIN32_FIND_DATA ptrFiles
[], /* [IN] Files' Info */
1264 DWORD dwCount
, /* [IN] The quantity of files */
1265 TCHAR
*szCurPath
, /* [IN] Full path of current directory */
1266 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
1272 TCHAR szTempFname
[MAX_PATH
];
1277 /* Calculate longest name */
1279 for (i
= 0; i
< dwCount
; i
++)
1281 if (ptrFiles
[i
]->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1283 /* Directories need 2 additinal characters for brackets */
1284 if ((_tcslen(ptrFiles
[i
]->cFileName
) + 2) > iLongestName
)
1285 iLongestName
= _tcslen(ptrFiles
[i
]->cFileName
) + 2;
1289 if (_tcslen(ptrFiles
[i
]->cFileName
) > iLongestName
)
1290 iLongestName
= _tcslen(ptrFiles
[i
]->cFileName
);
1294 /* Count the highest number of columns */
1295 GetScreenSize(&iScreenWidth
, 0);
1296 iColumns
= iScreenWidth
/ iLongestName
;
1298 /* Check if there is enough space for spaces between names */
1299 if (((iLongestName
* iColumns
) + iColumns
) >= (UINT
)iScreenWidth
)
1302 /* A last check at iColumns to avoid division by zero */
1306 /* Print Column sorted */
1307 if (lpFlags
->bWideListColSort
)
1309 /* Calculate the lines that will be printed */
1310 // iLines = ceil((float)dwCount/(float)iColumns);
1311 iLines
= (USHORT
)(dwCount
/ iColumns
);
1313 for (i
= 0;i
< iLines
;i
++)
1315 for (j
= 0; j
< iColumns
; j
++)
1317 temp
= (j
* iLines
) + i
;
1318 if (temp
>= dwCount
)
1321 if (ptrFiles
[temp
]->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1322 _stprintf(szTempFname
, _T("[%s]"), ptrFiles
[temp
]->cFileName
);
1324 _stprintf(szTempFname
, _T("%s"), ptrFiles
[temp
]->cFileName
);
1327 ConOutPrintfPaging(FALSE
,_T("%-*s"), iLongestName
+ 1 , szTempFname
);
1329 ConOutPrintf(_T("%-*s"), iLongestName
+ 1 , szTempFname
);
1333 ConOutPrintfPaging(FALSE
,_T("\n"));
1335 ConOutPrintf(_T("\n"));
1340 /* Print Line sorted */
1341 for (i
= 0; i
< dwCount
; i
++)
1343 if (ptrFiles
[i
]->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1344 _stprintf(szTempFname
, _T("[%s]"), ptrFiles
[i
]->cFileName
);
1346 _stprintf(szTempFname
, _T("%s"), ptrFiles
[i
]->cFileName
);
1349 ConOutPrintfPaging(FALSE
,_T("%-*s"), iLongestName
+ 1, szTempFname
);
1351 ConOutPrintf(_T("%-*s"), iLongestName
+ 1, szTempFname
);
1354 * We print a new line at the end of each column
1355 * except for the case that it is the last item.
1357 if (!((i
+ 1) % iColumns
) && (i
< (dwCount
- 1)))
1360 ConOutPrintfPaging(FALSE
,_T("\n"));
1362 ConOutPrintf(_T("\n"));
1366 /* Add a new line after the last item */
1368 ConOutPrintfPaging(FALSE
,_T("\n"));
1370 ConOutPrintf(_T("\n"));
1378 * The function that prints in old style
1381 DirPrintOldList(LPWIN32_FIND_DATA ptrFiles
[], /* [IN] Files' Info */
1382 DWORD dwCount
, /* [IN] The quantity of files */
1383 TCHAR
* szCurPath
, /* [IN] Full path of current directory */
1384 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
1386 DWORD i
; /* An indexer for "for"s */
1387 TCHAR szName
[10]; /* The name of file */
1388 TCHAR szExt
[5]; /* The extension of file */
1389 TCHAR szDate
[30],szTime
[30]; /* Used to format time and date */
1390 TCHAR szSize
[30]; /* The size of file */
1391 int iSizeFormat
; /* The format of size field */
1392 ULARGE_INTEGER u64FileSize
; /* The file size */
1394 for(i
= 0;i
< dwCount
;i
++)
1396 /* Broke 8.3 format */
1397 if (*ptrFiles
[i
]->cAlternateFileName
)
1399 /* If the file is long named then we read the alter name */
1400 getName( ptrFiles
[i
]->cAlternateFileName
, szName
);
1401 _tcscpy(szExt
, getExt( ptrFiles
[i
]->cAlternateFileName
));
1405 /* If the file is not long name we read its original name */
1406 getName( ptrFiles
[i
]->cFileName
, szName
);
1407 _tcscpy(szExt
, getExt( ptrFiles
[i
]->cFileName
));
1410 /* Calculate size */
1411 if (ptrFiles
[i
]->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1413 /* Directory, no size it's a directory*/
1415 _tcscpy(szSize
, _T("<DIR>"));
1421 u64FileSize
.HighPart
= ptrFiles
[i
]->nFileSizeHigh
;
1422 u64FileSize
.LowPart
= ptrFiles
[i
]->nFileSizeLow
;
1423 ConvertULargeInteger(u64FileSize
, szSize
, 20, lpFlags
->bTSeperator
);
1426 /* Format date and time */
1427 DirPrintFileDateTime(szDate
,szTime
,ptrFiles
[i
],lpFlags
);
1429 /* Print the line */
1432 if (ConOutPrintfPaging(FALSE
,_T("%-8s %-3s %*s %s %s\n"),
1433 szName
, /* The file's 8.3 name */
1434 szExt
, /* The file's 8.3 extension */
1435 iSizeFormat
, /* print format for size column */
1436 szSize
, /* The size of file or "<DIR>" for dirs */
1437 szDate
, /* The date of file/dir */
1438 szTime
) == 1) /* The time of file/dir */
1444 ConOutPrintf(_T("%-8s %-3s %*s %s %s\n"),
1445 szName
, /* The file's 8.3 name */
1446 szExt
, /* The file's 8.3 extension */
1447 iSizeFormat
, /* print format for size column */
1448 szSize
, /* The size of file or "<DIR>" for dirs */
1449 szDate
, /* The date of file/dir */
1450 szTime
); /* The time of file/dir */
1457 * The function that prints in bare format
1460 DirPrintBareList(LPWIN32_FIND_DATA ptrFiles
[], /* [IN] Files' Info */
1461 DWORD dwCount
, /* [IN] The number of files */
1462 LPTSTR lpCurPath
, /* [IN] Full path of current directory */
1463 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
1465 TCHAR szFullName
[MAX_PATH
];
1468 for (i
= 0; i
< dwCount
; i
++)
1470 if ((_tcscmp(ptrFiles
[i
]->cFileName
, _T(".")) == 0) ||
1471 (_tcscmp(ptrFiles
[i
]->cFileName
, _T("..")) == 0))
1473 /* at bare format we don't print "." and ".." folder */
1476 if (lpFlags
->bRecursive
)
1478 /* at recursive mode we print full path of file */
1479 _tcscpy(szFullName
, lpCurPath
);
1480 _tcscat(szFullName
, ptrFiles
[i
]->cFileName
);
1483 if (ConOutPrintfPaging(FALSE
,_T("%s\n"), szFullName
) == 1)
1489 ConOutPrintf(_T("%s\n"), szFullName
);
1493 /* if we are not in recursive mode we print the file names */
1496 if (ConOutPrintfPaging(FALSE
,_T("%s\n"),ptrFiles
[i
]->cFileName
) == 1)
1502 ConOutPrintf(_T("%s\n"),ptrFiles
[i
]->cFileName
);
1511 * The functions that prints the files list
1514 DirPrintFiles(LPWIN32_FIND_DATA ptrFiles
[], /* [IN] Files' Info */
1515 DWORD dwCount
, /* [IN] The quantity of files */
1516 TCHAR
*szCurPath
, /* [IN] Full path of current directory */
1517 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags used */
1519 TCHAR szMsg
[RC_STRING_MAX_SIZE
];
1520 TCHAR szTemp
[MAX_PATH
]; /* A buffer to format the directory header */
1522 /* Print directory header */
1523 _tcscpy(szTemp
, szCurPath
);
1525 /* We cut the trailing \ of the full path */
1526 szTemp
[_tcslen(szTemp
)-1] = _T('\0');
1528 /* Condition to print header:
1529 We are not printing in bare format
1530 and if we are in recursive mode... we must have results */
1531 if (!(lpFlags
->bBareFormat
) && !((lpFlags
->bRecursive
) && (dwCount
<= 0)))
1533 LoadString(CMD_ModuleHandle
, STRING_DIR_HELP7
, szMsg
, RC_STRING_MAX_SIZE
);
1536 if (ConOutPrintfPaging(FALSE
,szMsg
, szTemp
) == 1)
1542 ConOutPrintf(szMsg
, szTemp
);
1545 if (lpFlags
->bBareFormat
)
1548 DirPrintBareList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1550 else if(lpFlags
->bShortName
)
1552 /* New list style / Short names */
1553 DirPrintNewList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1555 else if(lpFlags
->bWideListColSort
|| lpFlags
->bWideList
)
1558 DirPrintWideList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1560 else if (lpFlags
->bNewLongList
)
1563 DirPrintNewList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1567 /* If nothing is selected old list is the default */
1568 DirPrintOldList(ptrFiles
, dwCount
, szCurPath
, lpFlags
);
1577 * Compares 2 files based on the order criteria
1580 CompareFiles(LPWIN32_FIND_DATA lpFile1
, /* [IN] A pointer to WIN32_FIND_DATA of file 1 */
1581 LPWIN32_FIND_DATA lpFile2
, /* [IN] A pointer to WIN32_FIND_DATA of file 2 */
1582 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags that we use to list */
1584 ULARGE_INTEGER u64File1
;
1585 ULARGE_INTEGER u64File2
;
1587 long iComp
= 0; /* The comparison result */
1589 /* Calculate critiries by order given from user */
1590 for (i
= 0;i
< lpFlags
->stOrderBy
.sCriteriaCount
;i
++)
1593 /* Calculate criteria */
1594 switch(lpFlags
->stOrderBy
.eCriteria
[i
])
1596 case ORDER_SIZE
: /* Order by size /o:s */
1597 /* concat the 32bit integers to a 64bit */
1598 u64File1
.LowPart
= lpFile1
->nFileSizeLow
;
1599 u64File1
.HighPart
= lpFile1
->nFileSizeHigh
;
1600 u64File2
.LowPart
= lpFile2
->nFileSizeLow
;
1601 u64File2
.HighPart
= lpFile2
->nFileSizeHigh
;
1603 /* In case that differnce is too big for a long */
1604 if (u64File1
.QuadPart
< u64File2
.QuadPart
)
1606 else if (u64File1
.QuadPart
> u64File2
.QuadPart
)
1612 case ORDER_DIRECTORY
: /* Order by directory attribute /o:g */
1613 iComp
= ((lpFile2
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)-
1614 (lpFile1
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
));
1617 case ORDER_EXTENSION
: /* Order by extension name /o:e */
1618 iComp
= _tcsicmp(getExt(lpFile1
->cFileName
),getExt(lpFile2
->cFileName
));
1621 case ORDER_NAME
: /* Order by filename /o:n */
1622 iComp
= _tcsicmp(lpFile1
->cFileName
, lpFile2
->cFileName
);
1625 case ORDER_TIME
: /* Order by file's time /o:t */
1626 /* We compare files based on the time field selected by /t */
1627 switch(lpFlags
->stTimeField
.eTimeField
)
1629 case TF_CREATIONDATE
:
1630 /* concat the 32bit integers to a 64bit */
1631 u64File1
.LowPart
= lpFile1
->ftCreationTime
.dwLowDateTime
;
1632 u64File1
.HighPart
= lpFile1
->ftCreationTime
.dwHighDateTime
;
1633 u64File2
.LowPart
= lpFile2
->ftCreationTime
.dwLowDateTime
;
1634 u64File2
.HighPart
= lpFile2
->ftCreationTime
.dwHighDateTime
;
1636 case TF_LASTACCESSEDDATE
:
1637 /* concat the 32bit integers to a 64bit */
1638 u64File1
.LowPart
= lpFile1
->ftLastAccessTime
.dwLowDateTime
;
1639 u64File1
.HighPart
= lpFile1
->ftLastAccessTime
.dwHighDateTime
;
1640 u64File2
.LowPart
= lpFile2
->ftLastAccessTime
.dwLowDateTime
;
1641 u64File2
.HighPart
= lpFile2
->ftLastAccessTime
.dwHighDateTime
;
1643 case TF_MODIFIEDDATE
:
1644 /* concat the 32bit integers to a 64bit */
1645 u64File1
.LowPart
= lpFile1
->ftLastWriteTime
.dwLowDateTime
;
1646 u64File1
.HighPart
= lpFile1
->ftLastWriteTime
.dwHighDateTime
;
1647 u64File2
.LowPart
= lpFile2
->ftLastWriteTime
.dwLowDateTime
;
1648 u64File2
.HighPart
= lpFile2
->ftLastWriteTime
.dwHighDateTime
;
1652 /* In case that differnce is too big for a long */
1653 if (u64File1
.QuadPart
< u64File2
.QuadPart
)
1655 else if (u64File1
.QuadPart
> u64File2
.QuadPart
)
1662 /* Reverse if desired */
1663 if (lpFlags
->stOrderBy
.bCriteriaRev
[i
])
1666 /* If that criteria was enough for distinguishing
1667 the files/dirs,there is no need to calculate the others*/
1668 if (iComp
!= 0) break;
1671 /* Translate the value of iComp to boolean */
1681 * Sort files by the order criterias using quicksort method
1684 QsortFiles(LPWIN32_FIND_DATA ptrArray
[], /* [IN/OUT] The array with file info pointers */
1685 int i
, /* [IN] The index of first item in array */
1686 int j
, /* [IN] The index to last item in array */
1687 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags that we will use to sort */
1689 LPWIN32_FIND_DATA lpTemp
; /* A temporary pointer */
1690 int First
, Last
, Temp
;
1700 if (Way
== CompareFiles(ptrArray
[i
], ptrArray
[j
], lpFlags
))
1702 /* Swap the pointers of the array */
1703 lpTemp
= ptrArray
[i
];
1704 ptrArray
[i
]= ptrArray
[j
];
1705 ptrArray
[j
] = lpTemp
;
1707 /* Swap the indexes for inverting sorting */
1718 QsortFiles(ptrArray
,First
, i
-1, lpFlags
);
1719 QsortFiles(ptrArray
,i
+1,Last
, lpFlags
);
1728 * The functions that does everything except for printing results
1731 DirList(LPTSTR szPath
, /* [IN] The path that dir starts */
1732 LPTSTR szFilespec
, /* [IN] The type of file that we are looking for */
1733 LPDIRSWITCHFLAGS lpFlags
) /* [IN] The flags of the listing */
1735 HANDLE hSearch
; /* The handle of the search */
1736 HANDLE hRecSearch
; /* The handle for searching recursivly */
1737 WIN32_FIND_DATA wfdFileInfo
; /* The info of file that found */
1738 LPWIN32_FIND_DATA
* ptrFileArray
; /* An array of pointers with all the files */
1739 PDIRFINDLISTNODE ptrStartNode
; /* The pointer to the first node */
1740 PDIRFINDLISTNODE ptrNextNode
; /* A pointer used for relatives refernces */
1741 TCHAR szFullPath
[MAX_PATH
]; /* The full path that we are listing with trailing \ */
1742 TCHAR szFullFileSpec
[MAX_PATH
]; /* The full path with file specs that we ll request\ */
1743 DWORD dwCount
; /* A counter of files found in directory */
1744 DWORD dwCountFiles
; /* Counter for files */
1745 DWORD dwCountDirs
; /* Counter for directories */
1746 ULARGE_INTEGER u64CountBytes
; /* Counter for bytes */
1747 ULARGE_INTEGER u64Temp
; /* A temporary counter */
1749 /* Initialize Variables */
1750 ptrStartNode
= NULL
;
1755 u64CountBytes
.QuadPart
= 0;
1757 /* Create szFullPath and szFullFileSpec */
1758 _tcscpy (szFullPath
, szPath
);
1759 if (szFullPath
[_tcslen(szFullPath
) - 1] != _T('\\'))
1760 _tcscat (szFullPath
, _T("\\"));
1761 _tcscpy (szFullFileSpec
, szFullPath
);
1762 _tcscat (szFullFileSpec
, szFilespec
);
1764 /* Prepare the linked list, first node is allocated */
1765 ptrStartNode
= malloc(sizeof(DIRFINDLISTNODE
));
1766 if (ptrStartNode
== NULL
)
1769 ConErrPrintf(_T("DEBUG: Cannot allocate memory for ptrStartNode!\n"));
1771 return 1; /* Error cannot allocate memory for 1st object */
1773 ptrNextNode
= ptrStartNode
;
1775 /* Collect the results for the current folder */
1776 hSearch
= FindFirstFile(szFullFileSpec
, &wfdFileInfo
);
1779 if (hSearch
!= INVALID_HANDLE_VALUE
)
1781 /* Here we filter all the specified attributes */
1782 if ((wfdFileInfo
.dwFileAttributes
& lpFlags
->stAttribs
.dwAttribMask
)
1783 == (lpFlags
->stAttribs
.dwAttribMask
& lpFlags
->stAttribs
.dwAttribVal
))
1785 ptrNextNode
->ptrNext
= malloc(sizeof(DIRFINDLISTNODE
));
1786 if (ptrNextNode
->ptrNext
== NULL
)
1789 ConErrPrintf(_T("DEBUG: Cannot allocate memory for ptrNextNode->ptrNext!\n"));
1791 while (ptrStartNode
)
1793 ptrNextNode
= ptrStartNode
->ptrNext
;
1795 ptrStartNode
= ptrNextNode
;
1801 /* If malloc fails we go to next file in hope it works,
1802 without braking the linked list! */
1803 if (ptrNextNode
->ptrNext
)
1805 /* Copy the info of search at linked list */
1806 memcpy(&ptrNextNode
->ptrNext
->stFindInfo
,
1808 sizeof(WIN32_FIND_DATA
));
1810 /* If lower case is selected do it here */
1811 if (lpFlags
->bLowerCase
)
1813 _tcslwr(ptrNextNode
->ptrNext
->stFindInfo
.cAlternateFileName
);
1814 _tcslwr(ptrNextNode
->ptrNext
->stFindInfo
.cFileName
);
1817 /* Continue at next node at linked list */
1818 ptrNextNode
= ptrNextNode
->ptrNext
;
1821 /* Grab statistics */
1822 if (wfdFileInfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1831 u64Temp
.HighPart
= wfdFileInfo
.nFileSizeHigh
;
1832 u64Temp
.LowPart
= wfdFileInfo
.nFileSizeLow
;
1833 u64CountBytes
.QuadPart
+= u64Temp
.QuadPart
;
1838 }while(FindNextFile(hSearch
, &wfdFileInfo
));
1841 /* Terminate list */
1842 ptrNextNode
->ptrNext
= NULL
;
1844 /* Calculate and allocate space need for making an array of pointers */
1845 ptrFileArray
= malloc(sizeof(LPWIN32_FIND_DATA
) * dwCount
);
1846 if (ptrFileArray
== NULL
)
1849 ConErrPrintf(_T("DEBUG: Cannot allocate memory for ptrFileArray!\n"));
1851 while (ptrStartNode
)
1853 ptrNextNode
= ptrStartNode
->ptrNext
;
1855 ptrStartNode
= ptrNextNode
;
1862 * Create an array of pointers from the linked list
1863 * this will be used to sort and print data, rather than the list
1865 ptrNextNode
= ptrStartNode
;
1867 while (ptrNextNode
->ptrNext
)
1869 *(ptrFileArray
+ dwCount
) = &ptrNextNode
->ptrNext
->stFindInfo
;
1870 ptrNextNode
= ptrNextNode
->ptrNext
;
1874 /* Sort Data if requested*/
1875 if (lpFlags
->stOrderBy
.sCriteriaCount
> 0)
1876 QsortFiles(ptrFileArray
, 0, dwCount
-1,lpFlags
);
1879 DirPrintFiles(ptrFileArray
, dwCount
, szFullPath
, lpFlags
);
1883 if (CheckCtrlBreak(BREAK_INPUT
))
1887 /* Add statistics to recursive statistics*/
1888 recurse_dir_cnt
+= dwCountDirs
;
1889 recurse_file_cnt
+= dwCountFiles
;
1890 recurse_bytes
.QuadPart
+= u64CountBytes
.QuadPart
;
1892 /* Do the recursive job if requested
1893 the recursive is be done on ALL(indepent of their attribs)
1894 directoried of the current one.*/
1895 if (lpFlags
->bRecursive
)
1897 /* The new search is involving any *.* file */
1898 _tcscpy(szFullFileSpec
, szFullPath
);
1899 _tcscat(szFullFileSpec
, _T("*.*"));
1900 hRecSearch
= FindFirstFile (szFullFileSpec
, &wfdFileInfo
);
1903 if (hRecSearch
!= INVALID_HANDLE_VALUE
)
1905 /* We search for directories other than "." and ".." */
1906 if ((_tcsicmp(wfdFileInfo
.cFileName
, _T(".")) != 0) &&
1907 (_tcsicmp(wfdFileInfo
.cFileName
, _T("..")) != 0 ) &&
1908 (wfdFileInfo
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1910 /* Concat the path and the directory to do recursive */
1911 _tcscpy(szFullFileSpec
, szFullPath
);
1912 _tcscat(szFullFileSpec
, wfdFileInfo
.cFileName
);
1913 /* We do the same for tha folder */
1914 if (DirList(szFullFileSpec
, szFilespec
, lpFlags
) != 0)
1920 }while(FindNextFile(hRecSearch
,&wfdFileInfo
));
1921 FindClose(hRecSearch
);
1924 /* Free linked list */
1925 while (ptrStartNode
)
1927 ptrNextNode
= ptrStartNode
->ptrNext
;
1929 ptrStartNode
= ptrNextNode
;
1941 * internal dir command
1944 CommandDir(LPTSTR first
, LPTSTR rest
)
1946 TCHAR dircmd
[256]; /* A variable to store the DIRCMD enviroment variable */
1948 TCHAR szPath
[MAX_PATH
];
1949 TCHAR szFilespec
[MAX_PATH
];
1953 DIRSWITCHFLAGS stFlags
;
1955 /* Initialize variables */
1957 recurse_dir_cnt
= 0L;
1958 recurse_file_cnt
= 0L;
1959 recurse_bytes
.QuadPart
= 0;
1961 /* Initialize Switch Flags < Default switches are setted here!> */
1962 stFlags
.b4Digit
= TRUE
;
1963 stFlags
.bBareFormat
= FALSE
;
1964 stFlags
.bLowerCase
= FALSE
;
1965 stFlags
.bNewLongList
= TRUE
;
1966 stFlags
.bPause
= FALSE
;
1967 stFlags
.bRecursive
= FALSE
;
1968 stFlags
.bShortName
= FALSE
;
1969 stFlags
.bTSeperator
= TRUE
;
1970 stFlags
.bUser
= FALSE
;
1971 stFlags
.bWideList
= FALSE
;
1972 stFlags
.bWideListColSort
= FALSE
;
1973 stFlags
.stTimeField
.eTimeField
= TF_MODIFIEDDATE
;
1974 stFlags
.stTimeField
.bUnSet
= FALSE
;
1975 stFlags
.stAttribs
.dwAttribMask
= FILE_ATTRIBUTE_HIDDEN
| FILE_ATTRIBUTE_SYSTEM
;
1976 stFlags
.stAttribs
.dwAttribVal
= 0L;
1977 stFlags
.stAttribs
.bUnSet
= FALSE
;
1978 stFlags
.stOrderBy
.sCriteriaCount
= 0;
1979 stFlags
.stOrderBy
.bUnSet
= FALSE
;
1983 /* read the parameters from the DIRCMD environment variable */
1984 if (GetEnvironmentVariable (_T("DIRCMD"), dircmd
, 256))
1985 if (!DirReadParam(dircmd
, ¶ms
, &entries
, &stFlags
))
1991 /* read the parameters */
1992 if (!DirReadParam(rest
, ¶ms
, &entries
, &stFlags
) || CheckCtrlBreak(BREAK_INPUT
))
1998 /* default to current directory */
2000 if(!add_entry(&entries
, ¶ms
, _T("."))) {
2006 for(loop
= 0; loop
< entries
; loop
++)
2008 /* parse the directory info */
2009 if (DirParsePathspec (params
[loop
], szPath
, szFilespec
) || CheckCtrlBreak(BREAK_INPUT
))
2016 Uncomment this to show the final state of switch flags*/
2020 ConOutPrintf(_T("Attributes mask/value %x/%x\n"),stFlags
.stAttribs
.dwAttribMask
,stFlags
.stAttribs
.dwAttribVal
);
2021 ConOutPrintf(_T("(B) Bare format : %i\n"), stFlags
.bBareFormat
);
2022 ConOutPrintf(_T("(C) Thousand : %i\n"), stFlags
.bTSeperator
);
2023 ConOutPrintf(_T("(W) Wide list : %i\n"), stFlags
.bWideList
);
2024 ConOutPrintf(_T("(D) Wide list sort by column : %i\n"), stFlags
.bWideListColSort
);
2025 ConOutPrintf(_T("(L) Lowercase : %i\n"), stFlags
.bLowerCase
);
2026 ConOutPrintf(_T("(N) New : %i\n"), stFlags
.bNewLongList
);
2027 ConOutPrintf(_T("(O) Order : %i\n"), stFlags
.stOrderBy
.sCriteriaCount
);
2028 for (i
=0;i
<stFlags
.stOrderBy
.sCriteriaCount
;i
++)
2029 ConOutPrintf(_T(" Order Criteria [%i]: %i (Reversed: %i)\n"),i
, stFlags
.stOrderBy
.eCriteria
[i
], stFlags
.stOrderBy
.bCriteriaRev
[i
] );
2030 ConOutPrintf(_T("(P) Pause : %i\n"), stFlags
.bPause
);
2031 ConOutPrintf(_T("(Q) Owner : %i\n"), stFlags
.bUser
);
2032 ConOutPrintf(_T("(S) Recursive : %i\n"), stFlags
.bRecursive
);
2033 ConOutPrintf(_T("(T) Time field : %i\n"), stFlags
.stTimeField
.eTimeField
);
2034 ConOutPrintf(_T("(X) Short names : %i\n"), stFlags
.bShortName
);
2035 ConOutPrintf(_T("Parameter : %s\n"), params
[loop
] );
2039 /* Print the drive header if the drive changed */
2040 if(cDrive
!= szPath
[0] && !stFlags
.bBareFormat
) {
2041 if (!PrintDirectoryHeader (szPath
, &stFlags
)) {
2050 /* do the actual dir */
2051 if (DirList (szPath
, szFilespec
, &stFlags
))
2058 /* print the footer */
2059 PrintSummary(szPath
,