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