only show one screen of text at a time for displaying help.
[reactos.git] / reactos / subsys / system / cmd / dir.c
1 /*
2 * DIR.C - dir internal command.
3 *
4 *
5 * History:
6 *
7 * 01/29/97 (Tim Norman)
8 * started.
9 *
10 * 06/13/97 (Tim Norman)
11 * Fixed code.
12 *
13 * 07/12/97 (Tim Norman)
14 * Fixed bug that caused the root directory to be unlistable
15 *
16 * 07/12/97 (Marc Desrochers)
17 * Changed to use maxx, maxy instead of findxy()
18 *
19 * 06/08/98 (Rob Lake)
20 * Added compatibility for /w in dir
21 *
22 * 06/09/98 (Rob Lake)
23 * Compatibility for dir/s started
24 * Tested that program finds directories off root fine
25 *
26 * 06/10/98 (Rob Lake)
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.
31 *
32 * 06/11/98 (Rob Lake)
33 * Found problem that caused COM not to work
34 *
35 * 06/12/98 (Rob Lake)
36 * debugged...
37 * added free mem routine
38 *
39 * 06/13/98 (Rob Lake)
40 * debugged the free mem routine
41 * debugged whole thing some more
42 * Notes:
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
47 * changes throughout
48 *
49 * 06/14/98 (Rob Lake)
50 * Cleaned up code a bit, added comments
51 *
52 * 06/16/98 (Rob Lake)
53 * Added error checking to my previously added routines
54 *
55 * 06/17/98 (Rob Lake)
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
66 * Added help screen
67 *
68 * 06/20/98 (Rob Lake)
69 * Added check for /-(switch) to turn off previously defined
70 * switches.
71 * Added ability to check for DIRCMD in environment and
72 * process it
73 *
74 * 06/21/98 (Rob Lake)
75 * Fixed up /B
76 * Now can dir *.ext/X, no spaces!
77 *
78 * 06/29/98 (Rob Lake)
79 * error message now found in command.h
80 *
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.
85 *
86 * 07/12/98 (Rob Lake)
87 * Changed error messages
88 *
89 * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
90 * added config.h include
91 *
92 *
93 * 04-Dec-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
94 * Converted source code to Win32, except recursive dir ("dir /s").
95 *
96 * 10-Dec-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
97 * Fixed recursive dir ("dir /s").
98 *
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 ;)
102 *
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.
106 *
107 * 20-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
108 * Redirection safe!
109 *
110 * 01-Mar-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
111 * Replaced all runtime io functions by their Win32 counterparts.
112 *
113 * 23-Feb-2001 (Carl Nettelblad <cnettel@hem.passagen.se>)
114 * dir /s now works in deeper trees
115 *
116 * 28-Jan-2004 (Michael Fritscher <michael@fritscher.net>)
117 * Fix for /p, so it is working under Windows in GUI-mode, too.
118 *
119 * 30-Apr-2004 (Filip Navara <xnavara@volny.cz>)
120 * Fix /w to print long names.
121 *
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
126 * the main cmd code.
127 *
128 * 1-Jul-2004 (Brandon Turner <turnerb7@msu.edu>)
129 * Added /p back in using ConOutPrintfPaging
130 */
131
132 #include <precomp.h>
133 #include "resource.h"
134
135 #ifdef INCLUDE_CMD_DIR
136
137
138
139 /* Time Field enumeration */
140 enum ETimeField
141 {
142 TF_CREATIONDATE = 0,
143 TF_MODIFIEDDATE = 1,
144 TF_LASTACCESSEDDATE = 2
145 };
146
147 /* Ordered by enumeration */
148 enum EOrderBy
149 {
150 ORDER_NAME = 0,
151 ORDER_SIZE = 1,
152 ORDER_DIRECTORY = 2,
153 ORDER_EXTENSION = 3,
154 ORDER_TIME = 4
155 };
156
157 /* The struct for holding the switches */
158 typedef struct _DirSwitchesFlags
159 {
160 BOOL bBareFormat; /* Bare Format */
161 BOOL bTSeperator; /* Thousands seperator */
162 BOOL bWideList; /* Wide list format */
163 BOOL bWideListColSort; /* Wide list format but sorted by column */
164 BOOL bLowerCase; /* Uses lower case */
165 BOOL bNewLongList; /* New long list */
166 BOOL bPause; /* Pause per page */
167 BOOL bUser; /* Displays the owner of file */
168 BOOL bRecursive; /* Displays files in specified directory and all sub */
169 BOOL bShortName; /* Displays the sort name of files if exist */
170 BOOL b4Digit; /* Four digit year */
171 struct
172 {
173 DWORD dwAttribVal; /* The desired state of attribute */
174 DWORD dwAttribMask; /* Which attributes to check */
175 BOOL bUnSet; /* A helper flag if "-" was given with the switch */
176 BOOL bParSetted; /* A helper flag if parameters of switch were given */
177 } stAttribs; /* Displays files with this attributes only */
178 struct
179 {
180 enum EOrderBy eCriteria[3]; /* Criterias used to order by */
181 BOOL bCriteriaRev[3]; /* If the criteria is in reversed order */
182 short sCriteriaCount; /* The quantity of criterias */
183 BOOL bUnSet; /* A helper flag if "-" was given with the switch */
184 BOOL bParSetted; /* A helper flag if parameters of switch were given */
185 } stOrderBy; /* Ordered by criterias */
186 struct
187 {
188 enum ETimeField eTimeField; /* The time field that will be used for */
189 BOOL bUnSet; /* A helper flag if "-" was given with the switch */
190 BOOL bParSetted; /* A helper flag if parameters of switch were given */
191 } stTimeField; /* The time field to display or use for sorting */
192 } DIRSWITCHFLAGS, *LPDIRSWITCHFLAGS;
193
194
195 typedef struct _DIRFINDLISTNODE
196 {
197 WIN32_FIND_DATA stFindInfo;
198 struct _DIRFINDLISTNODE *ptrNext;
199 } DIRFINDLISTNODE, *PDIRFINDLISTNODE;
200
201
202 typedef BOOL
203 (WINAPI *PGETFREEDISKSPACEEX)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER);
204
205
206 /* Globally save the # of dirs, files and bytes,
207 * probabaly later pass them to functions. Rob Lake */
208 static ULONG recurse_dir_cnt;
209 static ULONG recurse_file_cnt;
210 static ULARGE_INTEGER recurse_bytes;
211
212
213 /*
214 * help
215 *
216 * displays help screen for dir
217 * Rob Lake
218 */
219 static VOID DirHelp(VOID)
220 {
221 ConOutResPaging(TRUE, STRING_DIR_HELP1);
222 }
223
224
225
226 /*
227 * DirReadParameters
228 *
229 * Parse the parameters and switches of the command line and exports them
230 */
231 static BOOL
232 DirReadParam(LPTSTR Line, /* [IN] The line with the parameters & switches */
233 LPTSTR *param, /* [OUT] The parameters after parsing */
234 LPDIRSWITCHFLAGS lpFlags) /* [IN/OUT] The flags after calculating switches */
235 {
236 TCHAR cCurSwitch; /* The current switch */
237 TCHAR cCurChar; /* Current examing character */
238 TCHAR cCurUChar; /* Current upper examing character */
239 BOOL bNegative; /* Negative switch */
240 BOOL bPNegative; /* Negative switch parameter */
241 BOOL bIntoQuotes; /* A flag showing if we are in quotes (") */
242 LPTSTR ptrLast; /* A pointer to the last character of param */
243
244
245
246 /* Initialize variables; */
247 cCurSwitch = _T(' ');
248 bNegative = FALSE;
249 bPNegative = FALSE;
250 bIntoQuotes = FALSE;
251
252 /* No parameters yet */
253 *param = NULL;
254 ptrLast = NULL;
255
256 /* We suppose that switch parameters
257 were given to avoid setting them to default
258 if the switch was not given */
259 lpFlags->stAttribs.bParSetted = TRUE;
260 lpFlags->stOrderBy.bParSetted = TRUE;
261 lpFlags->stTimeField.bParSetted = TRUE;
262
263
264 /* Main Loop (see README_DIR.txt) */
265 /* scan the command line char per char, and we process its char */
266 while (*Line)
267 {
268 /* we save current character as it is and its upper case */
269 cCurChar = *Line;
270 cCurUChar = _totupper(*Line);
271
272 /* 1st section (see README_DIR.txt) */
273 /* When a switch is expecting */
274 if (cCurSwitch == _T('/'))
275 {
276 if ((cCurUChar == _T('A')) ||(cCurUChar == _T('T')) || (cCurUChar == _T('O')))
277 {
278 cCurSwitch = cCurUChar;
279 switch (cCurUChar)
280 {
281 case _T('A'):
282 lpFlags->stAttribs.bUnSet = bNegative;
283 lpFlags->stAttribs.bParSetted = FALSE;
284 break;
285 case _T('T'):
286 lpFlags->stTimeField.bUnSet = bNegative;
287 lpFlags->stTimeField.bParSetted = FALSE;
288 break;
289 case _T('O'):
290 lpFlags->stOrderBy.bUnSet = bNegative;
291 lpFlags->stOrderBy.bParSetted = FALSE;
292 break;
293 }
294 }
295 else if (cCurUChar == _T('L'))
296 lpFlags->bLowerCase = ! bNegative;
297 else if (cCurUChar == _T('B'))
298 lpFlags->bBareFormat = ! bNegative;
299 else if (cCurUChar == _T('C'))
300 lpFlags->bTSeperator = ! bNegative;
301 else if (cCurUChar == _T('W'))
302 lpFlags->bWideList = ! bNegative;
303 else if (cCurUChar == _T('D'))
304 lpFlags->bWideListColSort = ! bNegative;
305 else if (cCurUChar == _T('N'))
306 lpFlags->bNewLongList = ! bNegative;
307 else if (cCurUChar == _T('P'))
308 lpFlags->bPause = ! bNegative;
309 else if (cCurUChar == _T('Q'))
310 lpFlags->bUser = ! bNegative;
311 else if (cCurUChar == _T('S'))
312 lpFlags->bRecursive = ! bNegative;
313 else if (cCurUChar == _T('X'))
314 lpFlags->bShortName = ! bNegative;
315 else if (cCurChar == _T('4'))
316 lpFlags->b4Digit = ! bNegative;
317 else if (cCurChar == _T('?'))
318 {
319 DirHelp();
320 return FALSE;
321 }
322 else if (cCurChar == _T('-'))
323 {
324 bNegative = TRUE;
325 }
326 else
327 {
328 error_invalid_switch ((TCHAR)_totupper (*Line));
329 return FALSE;
330 }
331
332 /* We check if we calculated the negative value and realese the flag */
333 if ((cCurChar != _T('-')) && bNegative)
334 bNegative = FALSE;
335
336 /* if not a,o,t or - option then next parameter is not a switch */
337 if ((cCurSwitch == _T('/')) && (!bNegative))
338 cCurSwitch = _T(' ');
339
340 }
341 else if ((cCurSwitch == _T(' ')) || (cCurSwitch == _T('P')))
342 {
343 /* 2nd section (see README_DIR.txt) */
344 /* We are expecting parameter or the unknown */
345
346 if (cCurChar == _T('/'))
347 cCurSwitch = _T('/');
348
349 /* Process a spacer */
350 else if (cCurChar == _T(' '))
351 {
352 if (!bIntoQuotes)
353 {
354 cCurSwitch = _T(' ');
355 if ((*param) && !(ptrLast))
356 ptrLast = Line;
357 }
358
359 }
360 else if (cCurChar == _T('\"'))
361 {
362 /* Process a quote */
363 bIntoQuotes = !bIntoQuotes;
364 if (!bIntoQuotes) ptrLast = Line;
365 }
366 else
367 {
368 /* Process a character for parameter */
369 if ((cCurSwitch == _T(' ')) && (*param))
370 {
371 error_too_many_parameters(Line);
372 return FALSE;
373 }
374 cCurSwitch = _T('P');
375 if (!(*param))
376 *param = Line;
377 }
378 }
379 else
380 {
381 /* 3rd section (see README_DIR.txt) */
382 /* We are waiting for switch parameters */
383
384 /* Check if there are no more switch parameters */
385 if ((cCurChar == _T('/')) || ( cCurChar == _T(' ')))
386 {
387 /* Wrong desicion path, reprocess current character */
388 cCurSwitch = cCurChar;
389 continue;
390 }
391 /* Process parameter switch */
392 switch(cCurSwitch)
393 {
394 case _T('A'): /* Switch parameters for /A (attributes filter) */
395 /* Ok a switch parameter was given */
396 lpFlags->stAttribs.bParSetted = TRUE;
397
398 if (cCurChar == _T(':'))
399 /* =V= dead command, used to make the "if" work */
400 cCurChar = cCurChar;
401 else if(cCurChar == _T('-'))
402 bPNegative = TRUE;
403 else if(cCurUChar == _T('D'))
404 {
405 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_DIRECTORY;
406 if (bPNegative)
407 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_DIRECTORY;
408 else
409 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_DIRECTORY;
410 }
411 else if(cCurUChar == _T('R'))
412 {
413 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_READONLY;
414 if (bPNegative)
415 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_READONLY;
416 else
417 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_READONLY;
418 }
419 else if(cCurUChar == _T('H'))
420 {
421 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_HIDDEN;
422 if (bPNegative)
423 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_HIDDEN;
424 else
425 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_HIDDEN;
426 }
427 else if(cCurUChar == _T('A'))
428 {
429 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_ARCHIVE;
430 if (bPNegative)
431 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_ARCHIVE;
432 else
433 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_ARCHIVE;
434 }
435 else if(cCurUChar == _T('S'))
436 {
437 lpFlags->stAttribs.dwAttribMask |= FILE_ATTRIBUTE_SYSTEM;
438 if (bPNegative)
439 lpFlags->stAttribs.dwAttribVal &= ~FILE_ATTRIBUTE_SYSTEM;
440 else
441 lpFlags->stAttribs.dwAttribVal |= FILE_ATTRIBUTE_SYSTEM;
442 }
443 else
444 {
445 error_parameter_format((TCHAR)_totupper (*Line));
446 return FALSE;
447 }
448 break;
449 case _T('T'): /* Switch parameters for /T (time field) */
450
451 /* Ok a switch parameter was given */
452 lpFlags->stTimeField.bParSetted = TRUE;
453
454 if (cCurChar == _T(':'))
455 /* =V= dead command, used to make the "if" work */
456 cCurChar = cCurChar;
457 else if(cCurUChar == _T('C'))
458 lpFlags->stTimeField.eTimeField= TF_CREATIONDATE ;
459 else if(cCurUChar == _T('A'))
460 lpFlags->stTimeField.eTimeField= TF_LASTACCESSEDDATE ;
461 else if(cCurUChar == _T('W'))
462 lpFlags->stTimeField.eTimeField= TF_MODIFIEDDATE ;
463 else
464 {
465 error_parameter_format((TCHAR)_totupper (*Line));
466 return FALSE;
467 }
468 break;
469 case _T('O'): /* Switch parameters for /O (order) */
470 /* Ok a switch parameter was given */
471 lpFlags->stOrderBy.bParSetted = TRUE;
472
473 if (cCurChar == _T(':'))
474 /* <== dead command, used to make the "if" work */
475 cCurChar = cCurChar;
476 else if(cCurChar == _T('-'))
477 bPNegative = TRUE;
478 else if(cCurUChar == _T('N'))
479 {
480 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++;
481 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative;
482 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_NAME;
483 }
484 else if(cCurUChar == _T('S'))
485 {
486 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++;
487 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative;
488 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_SIZE;
489 }
490 else if(cCurUChar == _T('G'))
491 {
492 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++;
493 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative;
494 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_DIRECTORY;
495 }
496 else if(cCurUChar == _T('E'))
497 {
498 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++;
499 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative;
500 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_EXTENSION;
501 }
502 else if(cCurUChar == _T('D'))
503 {
504 if (lpFlags->stOrderBy.sCriteriaCount < 3) lpFlags->stOrderBy.sCriteriaCount++;
505 lpFlags->stOrderBy.bCriteriaRev[lpFlags->stOrderBy.sCriteriaCount - 1] = bPNegative;
506 lpFlags->stOrderBy.eCriteria[lpFlags->stOrderBy.sCriteriaCount - 1] = ORDER_TIME;
507 }
508
509 else
510 {
511 error_parameter_format((TCHAR)_totupper (*Line));
512 return FALSE;
513 }
514
515
516 }
517 /* We check if we calculated the negative value and realese the flag */
518 if ((cCurChar != _T('-')) && bPNegative)
519 bPNegative = FALSE;
520 }
521
522 Line++;
523 }
524 /* Terminate the parameters */
525 if (ptrLast) *ptrLast = 0;
526
527 /* Calculate the switches with no switch paramater */
528 if (!(lpFlags->stAttribs.bParSetted))
529 {
530 lpFlags->stAttribs.dwAttribVal = 0L;
531 lpFlags->stAttribs.dwAttribMask = lpFlags->stAttribs.dwAttribVal;
532 }
533 if (!(lpFlags->stOrderBy.bParSetted))
534 {
535 lpFlags->stOrderBy.sCriteriaCount = 1;
536 lpFlags->stOrderBy.eCriteria[0] = ORDER_NAME;
537 lpFlags->stOrderBy.bCriteriaRev[0] = FALSE;
538 }
539 if (!(lpFlags->stOrderBy.bParSetted))
540 lpFlags->stTimeField.eTimeField = TF_MODIFIEDDATE ;
541
542 /* Calculate the unsetted switches (the "-" prefixed)*/
543 if (lpFlags->stAttribs.bUnSet)
544 {
545 lpFlags->stAttribs.bUnSet = FALSE;
546 lpFlags->stAttribs.dwAttribVal = 0L;
547 lpFlags->stAttribs.dwAttribMask = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM;
548 }
549 if (lpFlags->stOrderBy.bUnSet)
550 {
551 lpFlags->stOrderBy.bUnSet = FALSE;
552 lpFlags->stOrderBy.sCriteriaCount = 0;
553 }
554 if (lpFlags->stTimeField.bUnSet )
555 {
556 lpFlags->stTimeField.bUnSet = FALSE;
557 lpFlags->stTimeField.eTimeField = TF_MODIFIEDDATE;
558 }
559 return TRUE;
560 }
561
562
563 /*
564 * ExtendFilespec
565 *
566 * extend the filespec, possibly adding wildcards
567 */
568 static VOID
569 ExtendFilespec (LPTSTR file)
570 {
571 INT len = 0;
572
573 if (!file)
574 return;
575
576
577 /* if no file spec, change to "*.*" */
578 if (*file == _T('\0'))
579 {
580 _tcscpy (file, _T("*.*"));
581 return;
582 }
583
584 // add support for *.
585 if ((file[0] == _T('*')) && (file[1] == _T('.') ))
586 {
587 return;
588 }
589
590 /* if starts with . add * in front */
591 if (*file == _T('.'))
592 {
593 memmove (&file[1], &file[0], (_tcslen (file) + 1) * sizeof(TCHAR));
594 file[0] = _T('*');
595 }
596
597 /* if no . add .* */
598 if (!_tcschr (file, _T('.')))
599 {
600 _tcscat (file, _T(".*"));
601 return;
602 }
603
604
605
606 /* if last character is '.' add '*' */
607 len = _tcslen (file);
608 if (file[len - 1] == _T('.'))
609 {
610 _tcscat (file, _T("*"));
611 return;
612 }
613 }
614
615
616 /*
617 * dir_parse_pathspec
618 *
619 * split the pathspec into drive, directory, and filespec
620 */
621 static INT
622 DirParsePathspec (LPTSTR szPathspec, LPTSTR szPath, LPTSTR szFilespec)
623 {
624 TCHAR szOrigPath[MAX_PATH];
625 LPTSTR start;
626 LPTSTR tmp;
627 INT i;
628 BOOL bWildcards = FALSE;
629
630 GetCurrentDirectory (MAX_PATH, szOrigPath);
631
632 /* get the drive and change to it */
633 if (szPathspec[1] == _T(':'))
634 {
635 TCHAR szRootPath[] = _T("A:");
636
637 szRootPath[0] = szPathspec[0];
638 start = szPathspec + 2;
639 if (!SetCurrentDirectory (szRootPath))
640 {
641 ErrorMessage (GetLastError(), NULL);
642 return 1;
643 }
644 }
645 else
646 {
647 start = szPathspec;
648 }
649
650
651 /* check for wildcards */
652 for (i = 0; szPathspec[i]; i++)
653 {
654 if (szPathspec[i] == _T('*') || szPathspec[i] == _T('?'))
655 bWildcards = TRUE;
656 }
657
658 /* check if this spec is a directory */
659 if (!bWildcards)
660 {
661 if (SetCurrentDirectory (szPathspec))
662 {
663 _tcscpy (szFilespec, _T("*.*"));
664
665 if (!GetCurrentDirectory (MAX_PATH, szPath))
666 {
667 szFilespec[0] = _T('\0');
668 SetCurrentDirectory (szOrigPath);
669 error_out_of_memory();
670 return 1;
671 }
672
673 SetCurrentDirectory (szOrigPath);
674 return 0;
675 }
676 }
677
678 /* find the file spec */
679 tmp = _tcsrchr (start, _T('\\'));
680
681 /* if no path is specified */
682 if (!tmp)
683 {
684 _tcscpy (szFilespec, start);
685 ExtendFilespec (szFilespec);
686 if (!GetCurrentDirectory (MAX_PATH, szPath))
687 {
688 szFilespec[0] = _T('\0');
689 SetCurrentDirectory (szOrigPath);
690 error_out_of_memory();
691 return 1;
692 }
693
694 SetCurrentDirectory (szOrigPath);
695 return 0;
696 }
697
698 /* get the filename */
699 _tcscpy (szFilespec, tmp+1);
700 ExtendFilespec (szFilespec);
701
702 if (tmp == start)
703 {
704 /* change to the root directory */
705 if (!SetCurrentDirectory (_T("\\")))
706 {
707 szFilespec[0] = _T('\0');
708 SetCurrentDirectory (szOrigPath);
709 error_path_not_found ();
710 return 1;
711 }
712 }
713 else
714 {
715 *tmp = _T('\0');
716
717 /* change to this directory */
718 if (!SetCurrentDirectory (start))
719 {
720 *tmp = _T('\\');
721 szFilespec[0] = _T('\0');
722 SetCurrentDirectory (szOrigPath);
723 error_path_not_found ();
724 return 1;
725 }
726 }
727
728 /* get the full name of the directory */
729 if (!GetCurrentDirectory (MAX_PATH, szPath))
730 {
731 *tmp = _T('\\');
732 szFilespec[0] = _T('\0');
733 SetCurrentDirectory (szOrigPath);
734 error_out_of_memory ();
735 return 1;
736 }
737
738 *tmp = _T('\\');
739
740 SetCurrentDirectory (szOrigPath);
741
742 return 0;
743 }
744
745
746 /*
747 * incline
748 *
749 * increment our line if paginating, display message at end of screen
750 */
751 #if 0
752 static BOOL
753 IncLine (LPINT pLine, LPDIRSWITCHFLAGS lpFlags)
754 {
755 BOOL bError;
756 CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo;
757 LONG WindowHeight;
758
759 bError = GetConsoleScreenBufferInfo(hConsole, &lpConsoleScreenBufferInfo);
760
761 WindowHeight = lpConsoleScreenBufferInfo.srWindow.Bottom - lpConsoleScreenBufferInfo.srWindow.Top;
762
763 /* That prevents bad behiour if WindowHeight could not be calculated */
764 if (!WindowHeight)
765 {
766 WindowHeight= 1000000;
767 }
768
769 if (!(lpFlags->bPause))
770 return FALSE;
771
772 (*pLine)++;
773
774 /*
775 * Because I don't know if WindowsHeight work in all cases,
776 * perhaps then maxy is the right value
777 */
778 if (*pLine >= (int)maxy - 2 || *pLine >= WindowHeight)
779 {
780 *pLine = 0;
781 return (PagePrompt () == PROMPT_BREAK);
782 }
783
784 return FALSE;
785 }
786 #endif
787
788 /*
789 * PrintDirectoryHeader
790 *
791 * print the header for the dir command
792 */
793 static BOOL
794 PrintDirectoryHeader(LPTSTR szPath, LPINT pLine, LPDIRSWITCHFLAGS lpFlags)
795 {
796 TCHAR szMsg[RC_STRING_MAX_SIZE];
797 TCHAR szRootName[MAX_PATH];
798 TCHAR szVolName[80];
799 DWORD dwSerialNr;
800 LPTSTR p;
801
802 if (lpFlags->bBareFormat)
803 return TRUE;
804
805 /* build usable root path */
806 if (szPath[1] == _T(':') && szPath[2] == _T('\\'))
807 {
808 /* normal path */
809 szRootName[0] = szPath[0];
810 szRootName[1] = _T(':');
811 szRootName[2] = _T('\\');
812 szRootName[3] = 0;
813 }
814 else if (szPath[0] == _T('\\') && szPath[1] == _T('\\'))
815 {
816 /* UNC path */
817 p = _tcschr(&szPath[2], _T('\\'));
818 if (p == NULL)
819 {
820 error_invalid_drive();
821 return(FALSE);
822 }
823 p = _tcschr(p+1, _T('\\'));
824 if (p == NULL)
825 {
826 _tcscpy(szRootName, szPath);
827 _tcscat(szRootName, _T("\\"));
828 }
829 else
830 {
831 *p = 0;
832 _tcscpy(szRootName, szPath);
833 _tcscat(szRootName, _T("\\"));
834 *p = _T('\\');
835 }
836 }
837 else
838 {
839 error_invalid_drive();
840 return(FALSE);
841 }
842
843 /* get the media ID of the drive */
844 if (!GetVolumeInformation(szRootName, szVolName, 80, &dwSerialNr,
845 NULL, NULL, NULL, 0))
846 {
847 error_invalid_drive();
848 return(FALSE);
849 }
850
851 /* print drive info */
852 if (szVolName[0] != _T('\0'))
853 {
854 LoadString(CMD_ModuleHandle, STRING_DIR_HELP2, szMsg, RC_STRING_MAX_SIZE);
855 //needs to have first paramter as TRUE because
856 //this is the first output and need to clear the static
857 if(lpFlags->bPause)
858 ConOutPrintfPaging(TRUE,szMsg, szRootName[0], szVolName);
859 else
860 ConOutPrintf(szMsg, szRootName[0], szVolName);
861
862 }
863 else
864 {
865 LoadString(CMD_ModuleHandle, STRING_DIR_HELP3, szMsg, RC_STRING_MAX_SIZE);
866 if(lpFlags->bPause)
867 ConOutPrintfPaging(TRUE,szMsg, szRootName[0]);
868 else
869 ConOutPrintf(szMsg, szRootName[0]);
870 }
871
872 /* print the volume serial number if the return was successful */
873 LoadString(CMD_ModuleHandle, STRING_DIR_HELP4, (LPTSTR) szMsg, RC_STRING_MAX_SIZE);
874 if(lpFlags->bPause)
875 ConOutPrintfPaging(FALSE,szMsg,
876 HIWORD(dwSerialNr),
877 LOWORD(dwSerialNr));
878 else
879 ConOutPrintf(szMsg,
880 HIWORD(dwSerialNr),
881 LOWORD(dwSerialNr));
882
883
884 return TRUE;
885 }
886
887
888 /*
889 * convert
890 *
891 * insert commas into a number
892 *
893 */
894 #if 0
895 static INT
896 ConvertULong (ULONG num, LPTSTR des, INT len)
897 {
898 TCHAR temp[32];
899 INT c = 0;
900 INT n = 0;
901
902 if (num == 0)
903 {
904 des[0] = _T('0');
905 des[1] = _T('\0');
906 n = 1;
907 }
908 else
909 {
910 temp[31] = 0;
911 while (num > 0)
912 {
913 if (((c + 1) % (nNumberGroups + 1)) == 0)
914 temp[30 - c++] = cThousandSeparator;
915 temp[30 - c++] = (TCHAR)(num % 10) + _T('0');
916 num /= 10;
917 }
918
919 for (n = 0; n <= c; n++)
920 des[n] = temp[31 - c + n];
921 }
922
923 return n;
924 }
925 #endif
926
927 static INT
928 ConvertULargeInteger (ULARGE_INTEGER num, LPTSTR des, INT len, BOOL bPutSeperator)
929 {
930 TCHAR temp[32];
931 INT c = 0;
932 INT n = 0;
933
934 if (num.QuadPart == 0)
935 {
936 des[0] = _T('0');
937 des[1] = _T('\0');
938 n = 1;
939 }
940 else
941 {
942 temp[31] = 0;
943 while (num.QuadPart > 0)
944 {
945 if ((((c + 1) % (nNumberGroups + 1)) == 0) && (bPutSeperator))
946 temp[30 - c++] = cThousandSeparator;
947 temp[30 - c++] = (TCHAR)(num.QuadPart % 10) + _T('0');
948 num.QuadPart /= 10;
949 }
950
951 for (n = 0; n <= c; n++)
952 des[n] = temp[31 - c + n];
953 }
954
955 return n;
956 }
957
958
959 static VOID
960 DirPrintFileDateTime(TCHAR *lpDate,
961 TCHAR *lpTime,
962 LPWIN32_FIND_DATA lpFile,
963 LPDIRSWITCHFLAGS lpFlags)
964 {
965 FILETIME ft;
966 SYSTEMTIME dt;
967 TCHAR szDate[30];
968 TCHAR szTime[30];
969 WORD wYear;
970
971 /* Select the right time field */
972 switch (lpFlags->stTimeField.eTimeField)
973 {
974 case TF_CREATIONDATE:
975 if (!FileTimeToLocalFileTime(&lpFile->ftCreationTime, &ft))
976 return;
977 FileTimeToSystemTime(&ft, &dt);
978 break;
979
980 case TF_LASTACCESSEDDATE :
981 if (!FileTimeToLocalFileTime(&lpFile->ftLastAccessTime, &ft))
982 return;
983 FileTimeToSystemTime(&ft, &dt);
984 break;
985
986 case TF_MODIFIEDDATE:
987 if (!FileTimeToLocalFileTime(&lpFile->ftLastWriteTime, &ft))
988 return;
989 FileTimeToSystemTime(&ft, &dt);
990 break;
991 }
992
993 /* Format date */
994 wYear = (lpFlags->b4Digit) ? dt.wYear : dt.wYear%100;
995 switch (nDateFormat)
996 {
997 case 0: /* mmddyy */
998 default:
999 _stprintf (szDate, _T("%02d%c%02d%c%0*d"),
1000 dt.wMonth, cDateSeparator,
1001 dt.wDay, cDateSeparator,
1002 lpFlags->b4Digit?4:2, wYear);
1003 break;
1004
1005 case 1: /* ddmmyy */
1006 _stprintf (szDate, _T("%02d%c%02d%c%0*d"),
1007 dt.wDay, cDateSeparator, dt.wMonth,
1008 cDateSeparator,lpFlags->b4Digit?4:2, wYear);
1009 break;
1010
1011 case 2: /* yymmdd */
1012 _stprintf (szDate, _T("%0*d%c%02d%c%02d"),
1013 lpFlags->b4Digit?4:2, wYear, cDateSeparator,
1014 dt.wMonth, cDateSeparator, dt.wDay);
1015 break;
1016 }
1017 /* Format Time */
1018 switch (nTimeFormat)
1019 {
1020 case 0: /* 12 hour format */
1021 default:
1022 _stprintf (szTime,_T(" %02d%c%02u%c"),
1023 (dt.wHour == 0 ? 12 : (dt.wHour <= 12 ? dt.wHour : dt.wHour - 12)),
1024 cTimeSeparator,
1025 dt.wMinute, (dt.wHour <= 11 ? _T('a') : _T('p')));
1026 break;
1027
1028 case 1: /* 24 hour format */
1029 _stprintf (szTime, _T(" %02d%c%02u"),
1030 dt.wHour, cTimeSeparator, dt.wMinute);
1031 break;
1032 }
1033 /* Copy results */
1034 _tcscpy(lpDate, szDate);
1035 _tcscpy(lpTime, szTime);
1036 }
1037
1038
1039 static VOID
1040 GetUserDiskFreeSpace(LPCTSTR lpRoot,
1041 PULARGE_INTEGER lpFreeSpace)
1042 {
1043 PGETFREEDISKSPACEEX pGetFreeDiskSpaceEx;
1044 HINSTANCE hInstance;
1045 DWORD dwSecPerCl;
1046 DWORD dwBytPerSec;
1047 DWORD dwFreeCl;
1048 DWORD dwTotCl;
1049 ULARGE_INTEGER TotalNumberOfBytes, TotalNumberOfFreeBytes;
1050
1051 lpFreeSpace->QuadPart = 0;
1052
1053 hInstance = LoadLibrary(_T("KERNEL32"));
1054 if (hInstance != NULL)
1055 {
1056 pGetFreeDiskSpaceEx = (PGETFREEDISKSPACEEX)GetProcAddress(hInstance,
1057 #ifdef _UNICODE
1058 "GetDiskFreeSpaceExW");
1059 #else
1060 "GetDiskFreeSpaceExA");
1061 #endif
1062 if (pGetFreeDiskSpaceEx != NULL)
1063 {
1064 if (pGetFreeDiskSpaceEx(lpRoot, lpFreeSpace, &TotalNumberOfBytes, &TotalNumberOfFreeBytes) == TRUE)
1065 return;
1066 }
1067 FreeLibrary(hInstance);
1068 }
1069
1070 GetDiskFreeSpace(lpRoot,
1071 &dwSecPerCl,
1072 &dwBytPerSec,
1073 &dwFreeCl,
1074 &dwTotCl);
1075
1076 lpFreeSpace->QuadPart = dwSecPerCl * dwBytPerSec * dwFreeCl;
1077 }
1078
1079
1080 /*
1081 * print_summary: prints dir summary
1082 * Added by Rob Lake 06/17/98 to compact code
1083 * Just copied Tim's Code and patched it a bit
1084 *
1085 */
1086 static INT
1087 PrintSummary(LPTSTR szPath,
1088 ULONG ulFiles,
1089 ULONG ulDirs,
1090 ULARGE_INTEGER u64Bytes,
1091 LPINT pLine,
1092 LPDIRSWITCHFLAGS lpFlags)
1093 {
1094 TCHAR szMsg[RC_STRING_MAX_SIZE];
1095 TCHAR szBuffer[64];
1096 ULARGE_INTEGER uliFree;
1097 TCHAR szRoot[] = _T("A:\\");
1098
1099
1100 /* Here we check if we didn't find anything */
1101 if (!(ulFiles + ulDirs))
1102 {
1103 error_file_not_found();
1104 return 1;
1105 }
1106
1107 /* In bare format we don't print results */
1108 if (lpFlags->bBareFormat)
1109 return 0;
1110
1111 /* Print recursive specific results */
1112 if (lpFlags->bRecursive)
1113 {
1114 ConvertULargeInteger(u64Bytes, szBuffer, sizeof(szBuffer), lpFlags->bTSeperator);
1115
1116 LoadString(CMD_ModuleHandle, STRING_DIR_HELP5, szMsg, RC_STRING_MAX_SIZE);
1117 if(lpFlags->bPause)
1118 ConOutPrintfPaging(FALSE,szMsg,ulFiles, szBuffer);
1119 else
1120 ConOutPrintf(szMsg,ulFiles, szBuffer);
1121 }
1122
1123 /* Print total directories and freespace */
1124 szRoot[0] = szPath[0];
1125 GetUserDiskFreeSpace(szRoot, &uliFree);
1126 ConvertULargeInteger(uliFree, szBuffer, sizeof(szBuffer), lpFlags->bTSeperator);
1127 LoadString(CMD_ModuleHandle, STRING_DIR_HELP6, (LPTSTR) szMsg, RC_STRING_MAX_SIZE);
1128 if(lpFlags->bPause)
1129 ConOutPrintfPaging(FALSE,szMsg,ulDirs, szBuffer);
1130 else
1131 ConOutPrintf(szMsg,ulDirs, szBuffer);
1132
1133 return 0;
1134 }
1135
1136 /*
1137 * getExt
1138 *
1139 * Get the extension of a filename
1140 */
1141 TCHAR* getExt(const TCHAR* file)
1142 {
1143 static TCHAR *NoExt = _T("");
1144 TCHAR* lastdot = _tcsrchr(file, _T('.'));
1145 return (lastdot != NULL ? lastdot + 1 : NoExt);
1146 }
1147
1148 /*
1149 * getName
1150 *
1151 * Get the name of the file without extension
1152 */
1153 static LPTSTR getName(const TCHAR* file, TCHAR * dest)
1154 {
1155 int iLen;
1156 LPTSTR end;
1157
1158 /* Check for "." and ".." folders */
1159 if ((_tcscmp(file, _T(".")) == 0) ||
1160 (_tcscmp(file, _T("..")) == 0))
1161 {
1162 _tcscpy(dest,file);
1163 return dest;
1164 }
1165
1166 end = _tcsrchr(file, _T('.'));
1167 if (!end)
1168 iLen = _tcslen(file);
1169 else
1170 iLen = (end - file);
1171
1172
1173 _tcsncpy(dest, file, iLen);
1174 *(dest + iLen) = _T('\0');
1175
1176 return dest;
1177 }
1178
1179
1180 /*
1181 * DirPrintNewList
1182 *
1183 * The function that prints in new style
1184 */
1185 static VOID
1186 DirPrintNewList(LPWIN32_FIND_DATA ptrFiles[], /* [IN]Files' Info */
1187 DWORD dwCount, /* [IN] The quantity of files */
1188 TCHAR *szCurPath, /* [IN] Full path of current directory */
1189 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */
1190 {
1191 DWORD i;
1192 TCHAR szSize[30];
1193 TCHAR szShortName[15];
1194 TCHAR szDate[20];
1195 TCHAR szTime[20];
1196 INT iSizeFormat;
1197 ULARGE_INTEGER u64FileSize;
1198
1199 for (i = 0;i < dwCount;i++)
1200 {
1201 /* Calculate size */
1202 if (ptrFiles[i]->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1203 {
1204 /* Directory */
1205 iSizeFormat = -14;
1206 _tcscpy(szSize, _T("<DIR>"));
1207 }
1208 else
1209 {
1210 /* File */
1211 iSizeFormat = 14;
1212 u64FileSize.HighPart = ptrFiles[i]->nFileSizeHigh;
1213 u64FileSize.LowPart = ptrFiles[i]->nFileSizeLow;
1214 ConvertULargeInteger(u64FileSize, szSize, 20, lpFlags->bTSeperator);
1215 }
1216
1217 /* Calculate short name */
1218 szShortName[0] = _T('\0');
1219 if (lpFlags->bShortName)
1220 _stprintf(szShortName, _T(" %-12s"), ptrFiles[i]->cAlternateFileName);
1221
1222 /* Format date and time */
1223 DirPrintFileDateTime(szDate, szTime, ptrFiles[i], lpFlags);
1224
1225 /* Print the line */
1226 if(lpFlags->bPause)
1227 ConOutPrintfPaging(FALSE,_T("%10s %-8s %*s%s %s\n"),
1228 szDate,
1229 szTime,
1230 iSizeFormat,
1231 szSize,
1232 szShortName,
1233 ptrFiles[i]->cFileName);
1234 else
1235 ConOutPrintf(_T("%10s %-8s %*s%s %s\n"),
1236 szDate,
1237 szTime,
1238 iSizeFormat,
1239 szSize,
1240 szShortName,
1241 ptrFiles[i]->cFileName);
1242 }
1243 }
1244
1245
1246 /*
1247 * DirPrintWideList
1248 *
1249 * The function that prints in wide list
1250 */
1251 static VOID
1252 DirPrintWideList(LPWIN32_FIND_DATA ptrFiles[], /* [IN] Files' Info */
1253 DWORD dwCount, /* [IN] The quantity of files */
1254 TCHAR *szCurPath, /* [IN] Full path of current directory */
1255 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */
1256 {
1257 SHORT iScreenWidth;
1258 USHORT iColumns;
1259 USHORT iLines;
1260 UINT iLongestName;
1261 TCHAR szTempFname[MAX_PATH];
1262 DWORD i;
1263 DWORD j;
1264 DWORD temp;
1265
1266 /* Calculate longest name */
1267 iLongestName = 1;
1268 for (i = 0; i < dwCount; i++)
1269 {
1270 if (ptrFiles[i]->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1271 {
1272 /* Directories need 2 additinal characters for brackets */
1273 if ((_tcslen(ptrFiles[i]->cFileName) + 2) > iLongestName)
1274 iLongestName = _tcslen(ptrFiles[i]->cFileName) + 2;
1275 }
1276 else
1277 {
1278 if (_tcslen(ptrFiles[i]->cFileName) > iLongestName)
1279 iLongestName = _tcslen(ptrFiles[i]->cFileName);
1280 }
1281 }
1282
1283 /* Count the highest number of columns */
1284 GetScreenSize(&iScreenWidth, 0);
1285 iColumns = iScreenWidth / iLongestName;
1286
1287 /* Check if there is enough space for spaces between names */
1288 if (((iLongestName * iColumns) + iColumns) >= (UINT)iScreenWidth)
1289 iColumns --;
1290
1291 /* A last check at iColumns to avoid division by zero */
1292 if (!(iColumns))
1293 iColumns = 1;
1294
1295 /* Print Column sorted */
1296 if (lpFlags->bWideListColSort)
1297 {
1298 /* Calculate the lines that will be printed */
1299 // iLines = ceil((float)dwCount/(float)iColumns);
1300 iLines = (USHORT)(dwCount / iColumns);
1301
1302 for (i = 0;i < iLines;i++)
1303 {
1304 for (j = 0; j < iColumns; j++)
1305 {
1306 temp = (j * iLines) + i;
1307 if (temp >= dwCount)
1308 break;
1309
1310 if (ptrFiles[temp]->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1311 _stprintf(szTempFname, _T("[%s]"), ptrFiles[temp]->cFileName);
1312 else
1313 _stprintf(szTempFname, _T("%s"), ptrFiles[temp]->cFileName);
1314
1315 if(lpFlags->bPause)
1316 ConOutPrintfPaging(FALSE,_T("%-*s"), iLongestName + 1 , szTempFname);
1317 else
1318 ConOutPrintf(_T("%-*s"), iLongestName + 1 , szTempFname);
1319 }
1320
1321 if(lpFlags->bPause)
1322 ConOutPrintfPaging(FALSE,_T("\n"));
1323 else
1324 ConOutPrintf(_T("\n"));
1325 }
1326 }
1327 else
1328 {
1329 /* Print Line sorted */
1330 for (i = 0; i < dwCount; i++)
1331 {
1332 if (ptrFiles[i]->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1333 _stprintf(szTempFname, _T("[%s]"), ptrFiles[i]->cFileName);
1334 else
1335 _stprintf(szTempFname, _T("%s"), ptrFiles[i]->cFileName);
1336
1337 if(lpFlags->bPause)
1338 ConOutPrintfPaging(FALSE,_T("%-*s"), iLongestName + 1, szTempFname );
1339 else
1340 ConOutPrintf(_T("%-*s"), iLongestName + 1, szTempFname );
1341
1342 /*
1343 * We print a new line at the end of each column
1344 * except for the case that it is the last item.
1345 */
1346 if (!((i + 1) % iColumns) && (i < (dwCount - 1)))
1347 {
1348 if(lpFlags->bPause)
1349 ConOutPrintfPaging(FALSE,_T("\n"));
1350 else
1351 ConOutPrintf(_T("\n"));
1352 }
1353 }
1354
1355 /* Add a new line after the last item */
1356 if(lpFlags->bPause)
1357 ConOutPrintfPaging(FALSE,_T("\n"));
1358 else
1359 ConOutPrintf(_T("\n"));
1360 }
1361 }
1362
1363
1364 /*
1365 * DirPrintOldList
1366 *
1367 * The function that prints in old style
1368 */
1369 static VOID
1370 DirPrintOldList(LPWIN32_FIND_DATA ptrFiles[], /* [IN] Files' Info */
1371 DWORD dwCount, /* [IN] The quantity of files */
1372 TCHAR * szCurPath, /* [IN] Full path of current directory */
1373 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */
1374 {
1375 DWORD i; /* An indexer for "for"s */
1376 TCHAR szName[10]; /* The name of file */
1377 TCHAR szExt[5]; /* The extension of file */
1378 TCHAR szDate[30],szTime[30]; /* Used to format time and date */
1379 TCHAR szSize[30]; /* The size of file */
1380 int iSizeFormat; /* The format of size field */
1381 ULARGE_INTEGER u64FileSize; /* The file size */
1382
1383 for(i = 0;i < dwCount;i++)
1384 {
1385 /* Broke 8.3 format */
1386 if (*ptrFiles[i]->cAlternateFileName )
1387 {
1388 /* If the file is long named then we read the alter name */
1389 getName( ptrFiles[i]->cAlternateFileName, szName);
1390 _tcscpy(szExt, getExt( ptrFiles[i]->cAlternateFileName));
1391 }
1392 else
1393 {
1394 /* If the file is not long name we read its original name */
1395 getName( ptrFiles[i]->cFileName, szName);
1396 _tcscpy(szExt, getExt( ptrFiles[i]->cFileName));
1397 }
1398
1399 /* Calculate size */
1400 if (ptrFiles[i]->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1401 {
1402 /* Directory, no size it's a directory*/
1403 iSizeFormat = -17;
1404 _tcscpy(szSize, _T("<DIR>"));
1405 }
1406 else
1407 {
1408 /* File */
1409 iSizeFormat = 17;
1410 u64FileSize.HighPart = ptrFiles[i]->nFileSizeHigh;
1411 u64FileSize.LowPart = ptrFiles[i]->nFileSizeLow;
1412 ConvertULargeInteger(u64FileSize, szSize, 20, lpFlags->bTSeperator);
1413 }
1414
1415 /* Format date and time */
1416 DirPrintFileDateTime(szDate,szTime,ptrFiles[i],lpFlags);
1417
1418 /* Print the line */
1419 if(lpFlags->bPause)
1420 ConOutPrintfPaging(FALSE,_T("%-8s %-3s %*s %s %s\n"),
1421 szName, /* The file's 8.3 name */
1422 szExt, /* The file's 8.3 extension */
1423 iSizeFormat, /* print format for size column */
1424 szSize, /* The size of file or "<DIR>" for dirs */
1425 szDate, /* The date of file/dir */
1426 szTime); /* The time of file/dir */
1427 else
1428 ConOutPrintf(_T("%-8s %-3s %*s %s %s\n"),
1429 szName, /* The file's 8.3 name */
1430 szExt, /* The file's 8.3 extension */
1431 iSizeFormat, /* print format for size column */
1432 szSize, /* The size of file or "<DIR>" for dirs */
1433 szDate, /* The date of file/dir */
1434 szTime); /* The time of file/dir */
1435 }
1436 }
1437
1438 /*
1439 * DirPrintBareList
1440 *
1441 * The function that prints in bare format
1442 */
1443 static VOID
1444 DirPrintBareList(LPWIN32_FIND_DATA ptrFiles[], /* [IN] Files' Info */
1445 DWORD dwCount, /* [IN] The number of files */
1446 LPTSTR lpCurPath, /* [IN] Full path of current directory */
1447 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */
1448 {
1449 TCHAR szFullName[MAX_PATH];
1450 DWORD i;
1451
1452 for (i = 0; i < dwCount; i++)
1453 {
1454 if ((_tcscmp(ptrFiles[i]->cFileName, _T(".")) == 0) ||
1455 (_tcscmp(ptrFiles[i]->cFileName, _T("..")) == 0))
1456 {
1457 /* at bare format we don't print "." and ".." folder */
1458 continue;
1459 }
1460 if (lpFlags->bRecursive)
1461 {
1462 /* at recursive mode we print full path of file */
1463 _tcscpy(szFullName, lpCurPath);
1464 _tcscat(szFullName, ptrFiles[i]->cFileName);
1465 if(lpFlags->bPause)
1466 ConOutPrintfPaging(FALSE,_T("%s\n"), szFullName);
1467 else
1468 ConOutPrintf(_T("%s\n"), szFullName);
1469 }
1470 else
1471 {
1472 /* if we are not in recursive mode we print the file names */
1473 if(lpFlags->bPause)
1474 ConOutPrintfPaging(FALSE,_T("%s\n"),ptrFiles[i]->cFileName);
1475 else
1476 ConOutPrintf(_T("%s\n"),ptrFiles[i]->cFileName);
1477 }
1478 }
1479 }
1480
1481
1482 /*
1483 * DirPrintFiles
1484 *
1485 * The functions that prints the files list
1486 */
1487 static VOID
1488 DirPrintFiles(LPWIN32_FIND_DATA ptrFiles[], /* [IN] Files' Info */
1489 DWORD dwCount, /* [IN] The quantity of files */
1490 TCHAR *szCurPath, /* [IN] Full path of current directory */
1491 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags used */
1492 {
1493 TCHAR szMsg[RC_STRING_MAX_SIZE];
1494 TCHAR szTemp[MAX_PATH]; /* A buffer to format the directory header */
1495
1496 /* Print directory header */
1497 _tcscpy(szTemp, szCurPath);
1498
1499 /* We cut the trailing \ of the full path */
1500 szTemp[_tcslen(szTemp)-1] = _T('\0');
1501
1502 /* Condition to print header:
1503 We are not printing in bare format
1504 and if we are in recursive mode... we must have results */
1505 if (!(lpFlags->bBareFormat ) && !((lpFlags->bRecursive) && (dwCount <= 0)))
1506 {
1507 LoadString(CMD_ModuleHandle, STRING_DIR_HELP7, szMsg, RC_STRING_MAX_SIZE);
1508 if(lpFlags->bPause)
1509 ConOutPrintfPaging(FALSE,szMsg, szTemp);
1510 else
1511 ConOutPrintf(szMsg, szTemp);
1512 }
1513
1514 if (lpFlags->bBareFormat)
1515 {
1516 /* Bare format */
1517 DirPrintBareList(ptrFiles, dwCount, szCurPath, lpFlags);
1518 }
1519 else if(lpFlags->bShortName)
1520 {
1521 /* New list style / Short names */
1522 DirPrintNewList(ptrFiles, dwCount, szCurPath, lpFlags);
1523 }
1524 else if(lpFlags->bWideListColSort || lpFlags->bWideList)
1525 {
1526 /* Wide list */
1527 DirPrintWideList(ptrFiles, dwCount, szCurPath, lpFlags);
1528 }
1529 else if (lpFlags->bNewLongList )
1530 {
1531 /* New list style*/
1532 DirPrintNewList(ptrFiles, dwCount, szCurPath, lpFlags);
1533 }
1534 else
1535 {
1536 /* If nothing is selected old list is the default */
1537 DirPrintOldList(ptrFiles, dwCount, szCurPath, lpFlags);
1538 }
1539 }
1540
1541
1542
1543 /*
1544 * CompareFiles
1545 *
1546 * Compares 2 files based on the order criteria
1547 */
1548 static BOOL
1549 CompareFiles(LPWIN32_FIND_DATA lpFile1, /* [IN] A pointer to WIN32_FIND_DATA of file 1 */
1550 LPWIN32_FIND_DATA lpFile2, /* [IN] A pointer to WIN32_FIND_DATA of file 2 */
1551 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags that we use to list */
1552 {
1553 ULARGE_INTEGER u64File1;
1554 ULARGE_INTEGER u64File2;
1555 int i;
1556 long iComp = 0; /* The comparison result */
1557
1558 /* Calculate critiries by order given from user */
1559 for (i = 0;i < lpFlags->stOrderBy.sCriteriaCount;i++)
1560 {
1561
1562 /* Calculate criteria */
1563 switch(lpFlags->stOrderBy.eCriteria[i])
1564 {
1565 case ORDER_SIZE: /* Order by size /o:s */
1566 /* concat the 32bit integers to a 64bit */
1567 u64File1.LowPart = lpFile1->nFileSizeLow;
1568 u64File1.HighPart = lpFile1->nFileSizeHigh;
1569 u64File2.LowPart = lpFile2->nFileSizeLow;
1570 u64File2.HighPart = lpFile2->nFileSizeHigh;
1571
1572 /* In case that differnce is too big for a long */
1573 if (u64File1.QuadPart < u64File2.QuadPart)
1574 iComp = -1;
1575 else if (u64File1.QuadPart > u64File2.QuadPart)
1576 iComp = 1;
1577 else
1578 iComp = 0;
1579 break;
1580
1581 case ORDER_DIRECTORY: /* Order by directory attribute /o:g */
1582 iComp = ((lpFile2->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)-
1583 (lpFile1->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY));
1584 break;
1585
1586 case ORDER_EXTENSION: /* Order by extension name /o:e */
1587 iComp = _tcsicmp(getExt(lpFile1->cFileName),getExt(lpFile2->cFileName));
1588 break;
1589
1590 case ORDER_NAME: /* Order by filename /o:n */
1591 iComp = _tcsicmp(lpFile1->cFileName, lpFile2->cFileName);
1592 break;
1593
1594 case ORDER_TIME: /* Order by file's time /o:t */
1595 /* We compare files based on the time field selected by /t */
1596 switch(lpFlags->stTimeField.eTimeField)
1597 {
1598 case TF_CREATIONDATE:
1599 /* concat the 32bit integers to a 64bit */
1600 u64File1.LowPart = lpFile1->ftCreationTime.dwLowDateTime;
1601 u64File1.HighPart = lpFile1->ftCreationTime.dwHighDateTime ;
1602 u64File2.LowPart = lpFile2->ftCreationTime.dwLowDateTime;
1603 u64File2.HighPart = lpFile2->ftCreationTime.dwHighDateTime ;
1604 break;
1605 case TF_LASTACCESSEDDATE :
1606 /* concat the 32bit integers to a 64bit */
1607 u64File1.LowPart = lpFile1->ftLastAccessTime.dwLowDateTime;
1608 u64File1.HighPart = lpFile1->ftLastAccessTime.dwHighDateTime ;
1609 u64File2.LowPart = lpFile2->ftLastAccessTime.dwLowDateTime;
1610 u64File2.HighPart = lpFile2->ftLastAccessTime.dwHighDateTime ;
1611 break;
1612 case TF_MODIFIEDDATE:
1613 /* concat the 32bit integers to a 64bit */
1614 u64File1.LowPart = lpFile1->ftLastWriteTime.dwLowDateTime;
1615 u64File1.HighPart = lpFile1->ftLastWriteTime.dwHighDateTime ;
1616 u64File2.LowPart = lpFile2->ftLastWriteTime.dwLowDateTime;
1617 u64File2.HighPart = lpFile2->ftLastWriteTime.dwHighDateTime ;
1618 break;
1619 }
1620
1621 /* In case that differnce is too big for a long */
1622 if (u64File1.QuadPart < u64File2.QuadPart)
1623 iComp = -1;
1624 else if (u64File1.QuadPart > u64File2.QuadPart)
1625 iComp = 1;
1626 else
1627 iComp = 0;
1628 break;
1629 }
1630
1631 /* Reverse if desired */
1632 if (lpFlags->stOrderBy.bCriteriaRev[i])
1633 iComp *= -1;
1634
1635 /* If that criteria was enough for distinguishing
1636 the files/dirs,there is no need to calculate the others*/
1637 if (iComp != 0) break;
1638 }
1639
1640 /* Translate the value of iComp to boolean */
1641 if (iComp > 0)
1642 return TRUE;
1643 else
1644 return FALSE;
1645 }
1646
1647 /*
1648 * QsortFiles
1649 *
1650 * Sort files by the order criterias using quicksort method
1651 */
1652 static VOID
1653 QsortFiles(LPWIN32_FIND_DATA ptrArray[], /* [IN/OUT] The array with file info pointers */
1654 int i, /* [IN] The index of first item in array */
1655 int j, /* [IN] The index to last item in array */
1656 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags that we will use to sort */
1657 {
1658 LPWIN32_FIND_DATA lpTemp; /* A temporary pointer */
1659 int First, Last, Temp;
1660 BOOL Way;
1661
1662 if (i < j)
1663 {
1664 First = i;
1665 Last = j;
1666 Way = TRUE;
1667 while (i != j)
1668 {
1669 if (Way == CompareFiles(ptrArray[i], ptrArray[j], lpFlags))
1670 {
1671 /* Swap the pointers of the array */
1672 lpTemp = ptrArray[i];
1673 ptrArray[i]= ptrArray[j];
1674 ptrArray[j] = lpTemp;
1675
1676 /* Swap the indexes for inverting sorting */
1677 Temp = i;
1678 i = j;
1679 j =Temp;
1680
1681 Way = !Way;
1682 }
1683
1684 j += (!Way - Way);
1685 }
1686
1687 QsortFiles(ptrArray,First, i-1, lpFlags);
1688 QsortFiles(ptrArray,i+1,Last, lpFlags);
1689 }
1690 }
1691
1692
1693
1694 /*
1695 * DirList
1696 *
1697 * The functions that does everything except for printing results
1698 */
1699 static INT
1700 DirList(LPTSTR szPath, /* [IN] The path that dir starts */
1701 LPTSTR szFilespec, /* [IN] The type of file that we are looking for */
1702 LPINT pLine, /* FIXME: Maybe used for paginating */
1703 LPDIRSWITCHFLAGS lpFlags) /* [IN] The flags of the listing */
1704 {
1705 HANDLE hSearch; /* The handle of the search */
1706 HANDLE hRecSearch; /* The handle for searching recursivly */
1707 WIN32_FIND_DATA wfdFileInfo; /* The info of file that found */
1708 LPWIN32_FIND_DATA * ptrFileArray; /* An array of pointers with all the files */
1709 PDIRFINDLISTNODE ptrStartNode; /* The pointer to the first node */
1710 PDIRFINDLISTNODE ptrNextNode; /* A pointer used for relatives refernces */
1711 TCHAR szFullPath[MAX_PATH]; /* The full path that we are listing with trailing \ */
1712 TCHAR szFullFileSpec[MAX_PATH]; /* The full path with file specs that we ll request\ */
1713 TCHAR szBytes[20]; /* A string for converting ULARGE integer */
1714 DWORD dwCount; /* A counter of files found in directory */
1715 DWORD dwCountFiles; /* Counter for files */
1716 DWORD dwCountDirs; /* Counter for directories */
1717 ULARGE_INTEGER u64CountBytes; /* Counter for bytes */
1718 ULARGE_INTEGER u64Temp; /* A temporary counter */
1719 TCHAR szMsg[RC_STRING_MAX_SIZE];
1720
1721 /* Initialize Variables */
1722 ptrStartNode = NULL;
1723 ptrNextNode = NULL;
1724 dwCount = 0;
1725 dwCountFiles = 0;
1726 dwCountDirs = 0;
1727 u64CountBytes.QuadPart = 0;
1728
1729 /* Create szFullPath and szFullFileSpec */
1730 _tcscpy (szFullPath, szPath);
1731 if (szFullPath[_tcslen(szFullPath) - 1] != _T('\\'))
1732 _tcscat (szFullPath, _T("\\"));
1733 _tcscpy (szFullFileSpec, szFullPath);
1734 _tcscat (szFullFileSpec, szFilespec);
1735
1736 /* Prepare the linked list, first node is allocated */
1737 ptrStartNode = malloc(sizeof(DIRFINDLISTNODE));
1738 if (ptrStartNode == NULL)
1739 {
1740 #ifdef _DEBUG
1741 ConErrPrintf(_T("DEBUG: Cannot allocate memory for ptrStartNode!\n"));
1742 #endif
1743 return 1; /* Error cannot allocate memory for 1st object */
1744 }
1745 ptrNextNode = ptrStartNode;
1746
1747 /* Collect the results for the current folder */
1748 hSearch = FindFirstFile(szFullFileSpec, &wfdFileInfo);
1749 do
1750 {
1751 if (hSearch != INVALID_HANDLE_VALUE)
1752 {
1753 /* Here we filter all the specified attributes */
1754 if ((wfdFileInfo.dwFileAttributes & lpFlags->stAttribs.dwAttribMask )
1755 == (lpFlags->stAttribs.dwAttribMask & lpFlags->stAttribs.dwAttribVal ))
1756 {
1757 ptrNextNode->ptrNext = malloc(sizeof(DIRFINDLISTNODE));
1758 if (ptrNextNode->ptrNext == NULL)
1759 {
1760 #ifdef _DEBUG
1761 ConErrPrintf(_T("DEBUG: Cannot allocate memory for ptrNextNode->ptrNext!\n"));
1762 #endif
1763 while (ptrStartNode)
1764 {
1765 ptrNextNode = ptrStartNode->ptrNext;
1766 free(ptrStartNode);
1767 ptrStartNode = ptrNextNode;
1768 dwCount --;
1769 }
1770 return 1;
1771 }
1772
1773 /* If malloc fails we go to next file in hope it works,
1774 without braking the linked list! */
1775 if (ptrNextNode->ptrNext)
1776 {
1777 /* Copy the info of search at linked list */
1778 memcpy(&ptrNextNode->ptrNext->stFindInfo,
1779 &wfdFileInfo,
1780 sizeof(WIN32_FIND_DATA));
1781
1782 /* If lower case is selected do it here */
1783 if (lpFlags->bLowerCase)
1784 {
1785 _tcslwr(ptrNextNode->ptrNext->stFindInfo.cAlternateFileName);
1786 _tcslwr(ptrNextNode->ptrNext->stFindInfo.cFileName);
1787 }
1788
1789 /* Continue at next node at linked list */
1790 ptrNextNode = ptrNextNode->ptrNext;
1791 dwCount ++;
1792
1793 /* Grab statistics */
1794 if (wfdFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1795 {
1796 /* Directory */
1797 dwCountDirs++;
1798 }
1799 else
1800 {
1801 /* File */
1802 dwCountFiles++;
1803 u64Temp.HighPart = wfdFileInfo.nFileSizeHigh;
1804 u64Temp.LowPart = wfdFileInfo.nFileSizeLow;
1805 u64CountBytes.QuadPart += u64Temp.QuadPart;
1806 }
1807 }
1808 }
1809 }
1810 }while(FindNextFile(hSearch, &wfdFileInfo));
1811 FindClose(hSearch);
1812
1813 /* Terminate list */
1814 ptrNextNode->ptrNext = NULL;
1815
1816 /* Calculate and allocate space need for making an array of pointers */
1817 ptrFileArray = malloc(sizeof(LPWIN32_FIND_DATA) * dwCount);
1818 if (ptrFileArray == NULL)
1819 {
1820 #ifdef _DEBUG
1821 ConErrPrintf(_T("DEBUG: Cannot allocate memory for ptrFileArray!\n"));
1822 #endif
1823 while (ptrStartNode)
1824 {
1825 ptrNextNode = ptrStartNode->ptrNext;
1826 free(ptrStartNode);
1827 ptrStartNode = ptrNextNode;
1828 dwCount --;
1829 }
1830 return 1;
1831 }
1832
1833 /*
1834 * Create an array of pointers from the linked list
1835 * this will be used to sort and print data, rather than the list
1836 */
1837 ptrNextNode = ptrStartNode;
1838 dwCount = 0;
1839 while (ptrNextNode->ptrNext)
1840 {
1841 *(ptrFileArray + dwCount) = &ptrNextNode->ptrNext->stFindInfo;
1842 ptrNextNode = ptrNextNode->ptrNext;
1843 dwCount++;
1844 }
1845
1846 /* Sort Data if requested*/
1847 if (lpFlags->stOrderBy.sCriteriaCount > 0)
1848 QsortFiles(ptrFileArray, 0, dwCount-1,lpFlags);
1849
1850 /* Print Data */
1851 DirPrintFiles(ptrFileArray, dwCount, szFullPath, lpFlags);
1852
1853 /* Free array */
1854 free(ptrFileArray);
1855
1856 /* Print Directory Summary */
1857 /* Condition to print summary is:
1858 If we are not in bare format and if we have results! */
1859 if (!(lpFlags->bBareFormat) && (dwCount > 0))
1860 {
1861 ConvertULargeInteger(u64CountBytes, szBytes, 20, lpFlags->bTSeperator);
1862 LoadString(CMD_ModuleHandle, STRING_DIR_HELP8, szMsg, RC_STRING_MAX_SIZE);
1863 if(lpFlags->bPause)
1864 ConOutPrintfPaging(FALSE,szMsg,dwCountFiles, szBytes);
1865 else
1866 ConOutPrintf(szMsg,dwCountFiles, szBytes);
1867
1868 }
1869
1870 /* Add statistics to recursive statistics*/
1871 recurse_dir_cnt += dwCountDirs;
1872 recurse_file_cnt += dwCountFiles;
1873 recurse_bytes.QuadPart += u64CountBytes.QuadPart;
1874
1875 /* Do the recursive job if requested
1876 the recursive is be done on ALL(indepent of their attribs)
1877 directoried of the current one.*/
1878 if (lpFlags->bRecursive)
1879 {
1880 /* The new search is involving any *.* file */
1881 _tcscpy(szFullFileSpec, szFullPath);
1882 _tcscat(szFullFileSpec, _T("*.*"));
1883 hRecSearch = FindFirstFile (szFullFileSpec, &wfdFileInfo);
1884 do
1885 {
1886 if (hRecSearch != INVALID_HANDLE_VALUE)
1887 {
1888 /* We search for directories other than "." and ".." */
1889 if ((_tcsicmp(wfdFileInfo.cFileName, _T(".")) != 0) &&
1890 (_tcsicmp(wfdFileInfo.cFileName, _T("..")) != 0 ) &&
1891 (wfdFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1892 {
1893 /* Concat the path and the directory to do recursive */
1894 _tcscpy(szFullFileSpec, szFullPath);
1895 _tcscat(szFullFileSpec, wfdFileInfo.cFileName);
1896 /* We do the same for tha folder */
1897 DirList(szFullFileSpec, szFilespec, pLine,lpFlags);
1898 }
1899 }
1900 }while(FindNextFile(hRecSearch,&wfdFileInfo));
1901 FindClose(hRecSearch);
1902 }
1903
1904 /* Free linked list */
1905 while (ptrStartNode)
1906 {
1907 ptrNextNode = ptrStartNode->ptrNext;
1908 free(ptrStartNode);
1909 ptrStartNode = ptrNextNode;
1910 dwCount --;
1911 }
1912
1913 return 0;
1914 }
1915
1916
1917
1918 /*
1919 * dir
1920 *
1921 * internal dir command
1922 */
1923 INT CommandDir(LPTSTR first, LPTSTR rest)
1924 {
1925 TCHAR dircmd[256]; /* A variable to store the DIRCMD enviroment variable */
1926 TCHAR szPath[MAX_PATH];
1927 TCHAR szFilespec[MAX_PATH];
1928 LPTSTR param;
1929 INT nLine = 0;
1930 DIRSWITCHFLAGS stFlags;
1931
1932 /* Initialize variables */
1933 recurse_dir_cnt = 0L;
1934 recurse_file_cnt = 0L;
1935 recurse_bytes.QuadPart = 0;
1936
1937 /* Initialize Switch Flags < Default switches are setted here!> */
1938 stFlags.b4Digit = TRUE;
1939 stFlags.bBareFormat = FALSE;
1940 stFlags.bLowerCase = FALSE;
1941 stFlags.bNewLongList = TRUE;
1942 stFlags.bPause = FALSE;
1943 stFlags.bRecursive = FALSE;
1944 stFlags.bShortName = FALSE;
1945 stFlags.bTSeperator = TRUE;
1946 stFlags.bUser = FALSE;
1947 stFlags.bWideList = FALSE;
1948 stFlags.bWideListColSort = FALSE;
1949 stFlags.stTimeField.eTimeField = TF_MODIFIEDDATE;
1950 stFlags.stTimeField.bUnSet = FALSE;
1951 stFlags.stAttribs.dwAttribMask = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM;
1952 stFlags.stAttribs.dwAttribVal = 0L;
1953 stFlags.stAttribs.bUnSet = FALSE;
1954 stFlags.stOrderBy.sCriteriaCount = 0;
1955 stFlags.stOrderBy.bUnSet = FALSE;
1956
1957 nErrorLevel = 0;
1958
1959 /* read the parameters from the DIRCMD environment variable */
1960 if (GetEnvironmentVariable (_T("DIRCMD"), dircmd, 256))
1961 if (!DirReadParam(dircmd, &param, &stFlags))
1962 {
1963 nErrorLevel = 1;
1964 return 1;
1965 }
1966
1967 /* read the parameters */
1968 if (!DirReadParam(rest, &param, &stFlags))
1969 {
1970 nErrorLevel = 1;
1971 return 1;
1972 }
1973
1974 /* default to current directory */
1975 if (!param)
1976 param = _T(".");
1977
1978 /* parse the directory info */
1979 if (DirParsePathspec (param, szPath, szFilespec))
1980 {
1981 nErrorLevel = 1;
1982 return 1;
1983 }
1984
1985 /* <Debug :>
1986 Uncomment this to show the final state of switch flags*/
1987 #ifdef _DEBUG
1988 {
1989 int i;
1990 ConOutPrintf(_T("Attributes mask/value %x/%x\n"),stFlags.stAttribs.dwAttribMask,stFlags.stAttribs.dwAttribVal );
1991 ConOutPrintf(_T("(B) Bare format : %i\n"), stFlags.bBareFormat );
1992 ConOutPrintf(_T("(C) Thousand : %i\n"), stFlags.bTSeperator );
1993 ConOutPrintf(_T("(W) Wide list : %i\n"), stFlags.bWideList );
1994 ConOutPrintf(_T("(D) Wide list sort by column : %i\n"), stFlags.bWideListColSort );
1995 ConOutPrintf(_T("(L) Lowercase : %i\n"), stFlags.bLowerCase );
1996 ConOutPrintf(_T("(N) New : %i\n"), stFlags.bNewLongList );
1997 ConOutPrintf(_T("(O) Order : %i\n"), stFlags.stOrderBy.sCriteriaCount );
1998 for (i =0;i<stFlags.stOrderBy.sCriteriaCount;i++)
1999 ConOutPrintf(_T(" Order Criteria [%i]: %i (Reversed: %i)\n"),i, stFlags.stOrderBy.eCriteria[i], stFlags.stOrderBy.bCriteriaRev[i] );
2000 ConOutPrintf(_T("(P) Pause : %i\n"), stFlags.bPause );
2001 ConOutPrintf(_T("(Q) Owner : %i\n"), stFlags.bUser );
2002 ConOutPrintf(_T("(S) Recursive : %i\n"), stFlags.bRecursive );
2003 ConOutPrintf(_T("(T) Time field : %i\n"), stFlags.stTimeField.eTimeField );
2004 ConOutPrintf(_T("(X) Short names : %i\n"), stFlags.bShortName );
2005 ConOutPrintf(_T("Parameter : %s\n"), param );
2006 }
2007 #endif
2008
2009 /* print the header */
2010 if (!stFlags.bBareFormat)
2011 if (!PrintDirectoryHeader (szPath, &nLine, &stFlags))
2012 {
2013 nErrorLevel = 1;
2014 return 1;
2015 }
2016
2017 /* do the actual dir */
2018 if (DirList (szPath, szFilespec, &nLine, &stFlags))
2019 {
2020 nErrorLevel = 1;
2021 return 1;
2022 }
2023
2024 /* print the footer */
2025 PrintSummary(szPath,
2026 recurse_file_cnt,
2027 recurse_dir_cnt,
2028 recurse_bytes,
2029 &nLine,
2030 &stFlags);
2031
2032 return 0;
2033 }
2034
2035 #endif
2036
2037 /* EOF */