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