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