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