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