Removed duplicate output of used disk space in the summary.
[reactos.git] / reactos / subsys / system / cmd / dir.c
1 /* $Id: dir.c,v 1.2 2003/05/09 21:58:05 ekohl Exp $
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
118 #include "config.h"
119
120 #ifdef INCLUDE_CMD_DIR
121 #include <windows.h>
122 #include <tchar.h>
123 #include <string.h>
124 #include <stdlib.h>
125 #include <stdio.h>
126 #include <ctype.h>
127
128 #include "cmd.h"
129
130
131 typedef BOOL STDCALL
132 (*PGETFREEDISKSPACEEX)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER);
133
134
135 /* flag definitions */
136 enum
137 {
138 DIR_RECURSE = 0x0001,
139 DIR_PAGE = 0x0002,
140 DIR_WIDE = 0x0004, /* Rob Lake */
141 DIR_BARE = 0x0008, /* Rob Lake */
142 DIR_ALL = 0x0010, /* Rob Lake */
143 DIR_LWR = 0x0020, /* Rob Lake */
144 DIR_SORT = 0x0040, /* /O sort */
145 DIR_NEW = 0x0080, /* /N new style */
146 DIR_FOUR = 0x0100 /* /4 four digit year */
147 };
148
149
150 /* Globally save the # of dirs, files and bytes,
151 * probabaly later pass them to functions. Rob Lake */
152 static ULONG recurse_dir_cnt;
153 static ULONG recurse_file_cnt;
154 static ULARGE_INTEGER recurse_bytes;
155
156
157 /*
158 * help
159 *
160 * displays help screen for dir
161 * Rob Lake
162 */
163 static VOID Help (VOID)
164 {
165 ConOutPuts(_T("Displays a list of files and subdirectories in a directory.\n"
166 "\n"
167 "DIR [drive:][path][filename] [/A] [/B] [/L] [/N] [/S] [/P] [/W] [/4]\n"
168 "\n"
169 " [drive:][path][filename]\n"
170 " Specifies drive, directory, and/or files to list.\n"
171 "\n"
172 " /A Displays files with HIDDEN SYSTEM attributes\n"
173 " default is ARCHIVE and READ ONLY\n"
174 " /B Uses bare format (no heading information or summary).\n"
175 " /L Uses lowercase.\n"
176 " /N New long list format where filenames are on the far right.\n"
177 " /S Displays files in specified directory and all subdirectories\n"
178 " /P Pauses after each screen full\n"
179 " /W Prints in wide format\n"
180 " /4 Display four digit years.\n"
181 "\n"
182 "Switches may be present in the DIRCMD environment variable. Use\n"
183 "of the - (hyphen) can turn off defined swtiches. Ex. /-W would\n"
184 "turn off printing in wide format.\n"
185 ));
186 }
187
188
189 /*
190 * DirReadParam
191 *
192 * read the parameters from the command line
193 */
194 static BOOL
195 DirReadParam (LPTSTR line, LPTSTR *param, LPDWORD lpFlags)
196 {
197 INT slash = 0;
198
199 if (!line)
200 return TRUE;
201
202 *param = NULL;
203
204 /* scan the command line, processing switches */
205 while (*line)
206 {
207 /* process switch */
208 if (*line == _T('/') || slash)
209 {
210 if (!slash)
211 line++;
212 slash = 0;
213 if (*line == _T('-'))
214 {
215 line++;
216 if (_totupper (*line) == _T('S'))
217 *lpFlags &= ~DIR_RECURSE;
218 else if (_totupper (*line) == _T('P'))
219 *lpFlags &= ~DIR_PAGE;
220 else if (_totupper (*line) == _T('W'))
221 *lpFlags &= ~DIR_WIDE;
222 else if (_totupper (*line) == _T('B'))
223 *lpFlags &= ~DIR_BARE;
224 else if (_totupper (*line) == _T('A'))
225 *lpFlags &= ~DIR_ALL;
226 else if (_totupper (*line) == _T('L'))
227 *lpFlags &= ~DIR_LWR;
228 else if (_totupper (*line) == _T('N'))
229 *lpFlags &= ~DIR_NEW;
230 else if (_totupper (*line) == _T('O'))
231 *lpFlags &= ~DIR_SORT;
232 else if (_totupper (*line) == _T('4'))
233 *lpFlags &= ~DIR_FOUR;
234 else
235 {
236 error_invalid_switch ((TCHAR)_totupper (*line));
237 return FALSE;
238 }
239 line++;
240 continue;
241 }
242 else
243 {
244 if (_totupper (*line) == _T('S'))
245 *lpFlags |= DIR_RECURSE;
246 else if (_totupper (*line) == _T('P'))
247 *lpFlags |= DIR_PAGE;
248 else if (_totupper (*line) == _T('W'))
249 *lpFlags |= DIR_WIDE;
250 else if (_totupper (*line) == _T('B'))
251 *lpFlags |= DIR_BARE;
252 else if (_totupper (*line) == _T('A'))
253 *lpFlags |= DIR_ALL;
254 else if (_totupper (*line) == _T('L'))
255 *lpFlags |= DIR_LWR;
256 else if (_totupper (*line) == _T('N'))
257 *lpFlags |= DIR_NEW;
258 else if (_totupper (*line) == _T('O'))
259 *lpFlags |= DIR_SORT;
260 else if (_totupper (*line) == _T('4'))
261 *lpFlags |= DIR_FOUR;
262 else if (*line == _T('?'))
263 {
264 Help();
265 return FALSE;
266 }
267 else
268 {
269 error_invalid_switch ((TCHAR)_totupper (*line));
270 return FALSE;
271 }
272 line++;
273 continue;
274 }
275 }
276
277 /* process parameter */
278 if (!_istspace (*line))
279 {
280 if (*param)
281 {
282 error_too_many_parameters (*param);
283 return FALSE;
284 }
285
286 *param = line;
287
288 /* skip to end of line or next whitespace or next / */
289 while (*line && !_istspace (*line) && *line != _T('/'))
290 line++;
291
292 /* if end of line, return */
293 if (!*line)
294 return TRUE;
295
296 /* if parameter, remember to process it later */
297 if (*line == _T('/'))
298 slash = 1;
299
300 *line++ = 0;
301 continue;
302 }
303
304 line++;
305 }
306
307 if (slash)
308 {
309 error_invalid_switch ((TCHAR)_totupper (*line));
310 return FALSE;
311 }
312
313 return TRUE;
314 }
315
316
317 /*
318 * ExtendFilespec
319 *
320 * extend the filespec, possibly adding wildcards
321 */
322 static VOID
323 ExtendFilespec (LPTSTR file)
324 {
325 INT len = 0;
326
327 if (!file)
328 return;
329
330 /* if no file spec, change to "*.*" */
331 if (*file == _T('\0'))
332 {
333 _tcscpy (file, _T("*.*"));
334 return;
335 }
336
337 /* if starts with . add * in front */
338 if (*file == _T('.'))
339 {
340 memmove (&file[1], &file[0], (_tcslen (file) + 1) * sizeof(TCHAR));
341 file[0] = _T('*');
342 }
343
344 /* if no . add .* */
345 if (!_tcschr (file, _T('.')))
346 {
347 _tcscat (file, _T(".*"));
348 return;
349 }
350
351 /* if last character is '.' add '*' */
352 len = _tcslen (file);
353 if (file[len - 1] == _T('.'))
354 {
355 _tcscat (file, _T("*"));
356 return;
357 }
358 }
359
360
361 /*
362 * dir_parse_pathspec
363 *
364 * split the pathspec into drive, directory, and filespec
365 */
366 static INT
367 DirParsePathspec (LPTSTR szPathspec, LPTSTR szPath, LPTSTR szFilespec)
368 {
369 TCHAR szOrigPath[MAX_PATH];
370 LPTSTR start;
371 LPTSTR tmp;
372 INT i;
373 BOOL bWildcards = FALSE;
374
375 GetCurrentDirectory (MAX_PATH, szOrigPath);
376
377 /* get the drive and change to it */
378 if (szPathspec[1] == _T(':'))
379 {
380 TCHAR szRootPath[] = _T("A:");
381
382 szRootPath[0] = szPathspec[0];
383 start = szPathspec + 2;
384 SetCurrentDirectory (szRootPath);
385 }
386 else
387 {
388 start = szPathspec;
389 }
390
391
392 /* check for wildcards */
393 for (i = 0; szPathspec[i]; i++)
394 {
395 if (szPathspec[i] == _T('*') || szPathspec[i] == _T('?'))
396 bWildcards = TRUE;
397 }
398
399 /* check if this spec is a directory */
400 if (!bWildcards)
401 {
402 if (SetCurrentDirectory (szPathspec))
403 {
404 _tcscpy (szFilespec, _T("*.*"));
405
406 if (!GetCurrentDirectory (MAX_PATH, szPath))
407 {
408 szFilespec[0] = _T('\0');
409 SetCurrentDirectory (szOrigPath);
410 error_out_of_memory();
411 return 1;
412 }
413
414 SetCurrentDirectory (szOrigPath);
415 return 0;
416 }
417 }
418
419 /* find the file spec */
420 tmp = _tcsrchr (start, _T('\\'));
421
422 /* if no path is specified */
423 if (!tmp)
424 {
425 _tcscpy (szFilespec, start);
426 ExtendFilespec (szFilespec);
427 if (!GetCurrentDirectory (MAX_PATH, szPath))
428 {
429 szFilespec[0] = _T('\0');
430 SetCurrentDirectory (szOrigPath);
431 error_out_of_memory();
432 return 1;
433 }
434
435 SetCurrentDirectory (szOrigPath);
436 return 0;
437 }
438
439 /* get the filename */
440 _tcscpy (szFilespec, tmp+1);
441 ExtendFilespec (szFilespec);
442
443 *tmp = _T('\0');
444
445 /* change to this directory and get its full name */
446 if (!SetCurrentDirectory (start))
447 {
448 *tmp = _T('\\');
449 szFilespec[0] = _T('\0');
450 SetCurrentDirectory (szOrigPath);
451 error_path_not_found ();
452 return 1;
453 }
454
455 if (!GetCurrentDirectory (MAX_PATH, szPath))
456 {
457 *tmp = _T('\\');
458 szFilespec[0] = _T('\0');
459 SetCurrentDirectory (szOrigPath);
460 error_out_of_memory ();
461 return 1;
462 }
463
464 *tmp = _T('\\');
465
466 SetCurrentDirectory (szOrigPath);
467
468 return 0;
469 }
470
471
472 /*
473 * incline
474 *
475 * increment our line if paginating, display message at end of screen
476 */
477 static BOOL
478 IncLine (LPINT pLine, DWORD dwFlags)
479 {
480 if (!(dwFlags & DIR_PAGE))
481 return FALSE;
482
483 (*pLine)++;
484
485 if (*pLine >= (int)maxy - 2)
486 {
487 *pLine = 0;
488 return (PagePrompt () == PROMPT_BREAK);
489 }
490
491 return FALSE;
492 }
493
494
495 /*
496 * PrintDirectoryHeader
497 *
498 * print the header for the dir command
499 */
500 static BOOL
501 PrintDirectoryHeader (LPTSTR szPath, LPINT pLine, DWORD dwFlags)
502 {
503 TCHAR szRootName[MAX_PATH];
504 TCHAR szVolName[80];
505 DWORD dwSerialNr;
506 LPTSTR p;
507
508 if (dwFlags & DIR_BARE)
509 return(TRUE);
510
511 /* build usable root path */
512 if (szPath[1] == _T(':') && szPath[2] == _T('\\'))
513 {
514 /* normal path */
515 szRootName[0] = szPath[0];
516 szRootName[1] = _T(':');
517 szRootName[2] = _T('\\');
518 szRootName[3] = 0;
519 }
520 else if (szPath[0] == _T('\\') && szPath[1] == _T('\\'))
521 {
522 /* UNC path */
523 p = _tcschr(&szPath[2], _T('\\'));
524 if (p == NULL)
525 {
526 error_invalid_drive();
527 return(FALSE);
528 }
529 p = _tcschr(p+1, _T('\\'));
530 if (p == NULL)
531 {
532 _tcscpy(szRootName, szPath);
533 _tcscat(szRootName, _T("\\"));
534 }
535 else
536 {
537 *p = 0;
538 _tcscpy(szRootName, szPath);
539 _tcscat(szRootName, _T("\\"));
540 *p = _T('\\');
541 }
542 }
543 else
544 {
545 error_invalid_drive();
546 return(FALSE);
547 }
548
549 /* get the media ID of the drive */
550 if (!GetVolumeInformation(szRootName, szVolName, 80, &dwSerialNr,
551 NULL, NULL, NULL, 0))
552 {
553 error_invalid_drive();
554 return(FALSE);
555 }
556
557 /* print drive info */
558 ConOutPrintf(_T(" Volume in drive %c"), szRootName[0]);
559
560 if (szVolName[0] != _T('\0'))
561 ConOutPrintf(_T(" is %s\n"), szVolName);
562 else
563 ConOutPrintf(_T(" has no label\n"));
564
565 if (IncLine(pLine, dwFlags))
566 return(FALSE);
567
568 /* print the volume serial number if the return was successful */
569 ConOutPrintf(_T(" Volume Serial Number is %04X-%04X\n"),
570 HIWORD(dwSerialNr),
571 LOWORD(dwSerialNr));
572 if (IncLine(pLine, dwFlags))
573 return(FALSE);
574
575 return(TRUE);
576 }
577
578
579 /*
580 * convert
581 *
582 * insert commas into a number
583 */
584 static INT
585 ConvertULong (ULONG num, LPTSTR des, INT len)
586 {
587 TCHAR temp[32];
588 INT c = 0;
589 INT n = 0;
590
591 if (num == 0)
592 {
593 des[0] = _T('0');
594 des[1] = _T('\0');
595 n = 1;
596 }
597 else
598 {
599 temp[31] = 0;
600 while (num > 0)
601 {
602 if (((c + 1) % (nNumberGroups + 1)) == 0)
603 temp[30 - c++] = cThousandSeparator;
604 temp[30 - c++] = (TCHAR)(num % 10) + _T('0');
605 num /= 10;
606 }
607
608 for (n = 0; n <= c; n++)
609 des[n] = temp[31 - c + n];
610 }
611
612 return n;
613 }
614
615
616 static INT
617 ConvertULargeInteger (ULARGE_INTEGER num, LPTSTR des, INT len)
618 {
619 TCHAR temp[32];
620 INT c = 0;
621 INT n = 0;
622
623 if (num.QuadPart == 0)
624 {
625 des[0] = _T('0');
626 des[1] = _T('\0');
627 n = 1;
628 }
629 else
630 {
631 temp[31] = 0;
632 while (num.QuadPart > 0)
633 {
634 if (((c + 1) % (nNumberGroups + 1)) == 0)
635 temp[30 - c++] = cThousandSeparator;
636 temp[30 - c++] = (TCHAR)(num.QuadPart % 10) + _T('0');
637 num.QuadPart /= 10;
638 }
639
640 for (n = 0; n <= c; n++)
641 des[n] = temp[31 - c + n];
642 }
643
644 return n;
645 }
646
647
648 static VOID
649 PrintFileDateTime (LPSYSTEMTIME dt, DWORD dwFlags)
650 {
651 WORD wYear = (dwFlags & DIR_FOUR) ? dt->wYear : dt->wYear%100;
652
653 switch (nDateFormat)
654 {
655 case 0: /* mmddyy */
656 default:
657 ConOutPrintf (_T("%.2d%c%.2d%c%d"),
658 dt->wMonth, cDateSeparator, dt->wDay, cDateSeparator, wYear);
659 break;
660
661 case 1: /* ddmmyy */
662 ConOutPrintf (_T("%.2d%c%.2d%c%d"),
663 dt->wDay, cDateSeparator, dt->wMonth, cDateSeparator, wYear);
664 break;
665
666 case 2: /* yymmdd */
667 ConOutPrintf (_T("%d%c%.2d%c%.2d"),
668 wYear, cDateSeparator, dt->wMonth, cDateSeparator, dt->wDay);
669 break;
670 }
671
672 switch (nTimeFormat)
673 {
674 case 0: /* 12 hour format */
675 default:
676 ConOutPrintf (_T(" %2d%c%.2u%c"),
677 (dt->wHour == 0 ? 12 : (dt->wHour <= 12 ? dt->wHour : dt->wHour - 12)),
678 cTimeSeparator,
679 dt->wMinute, (dt->wHour <= 11 ? 'a' : 'p'));
680 break;
681
682 case 1: /* 24 hour format */
683 ConOutPrintf (_T(" %2d%c%.2u"),
684 dt->wHour, cTimeSeparator, dt->wMinute);
685 break;
686 }
687 }
688
689
690 static VOID
691 GetUserDiskFreeSpace(LPCTSTR lpRoot,
692 PULARGE_INTEGER lpFreeSpace)
693 {
694 PGETFREEDISKSPACEEX pGetFreeDiskSpaceEx;
695 HINSTANCE hInstance;
696 DWORD dwSecPerCl;
697 DWORD dwBytPerSec;
698 DWORD dwFreeCl;
699 DWORD dwTotCl;
700
701 lpFreeSpace->QuadPart = 0;
702
703 hInstance = LoadLibrary(_T("KERNEL32"));
704 if (hInstance != NULL)
705 {
706 #ifndef UNICODE
707 pGetFreeDiskSpaceEx = GetProcAddress(hInstance,
708 "GetDiskFreeSpaceExA");
709 #else
710 pGetFreeDiskSpaceEx = GetProcAddress(hInstance,
711 "GetDiskFreeSpaceExW");
712 #endif
713 if (pGetFreeDiskSpaceEx != NULL)
714 {
715 if (pGetFreeDiskSpaceEx(lpRoot, lpFreeSpace, NULL, NULL) == TRUE)
716 return;
717 }
718 FreeLibrary(hInstance);
719 }
720
721 GetDiskFreeSpace(lpRoot,
722 &dwSecPerCl,
723 &dwBytPerSec,
724 &dwFreeCl,
725 &dwTotCl);
726
727 lpFreeSpace->QuadPart = dwSecPerCl * dwBytPerSec * dwFreeCl;
728 }
729
730
731 /*
732 * print_summary: prints dir summary
733 * Added by Rob Lake 06/17/98 to compact code
734 * Just copied Tim's Code and patched it a bit
735 *
736 */
737 static INT
738 PrintSummary(LPTSTR szPath,
739 ULONG ulFiles,
740 ULONG ulDirs,
741 ULARGE_INTEGER bytes,
742 LPINT pLine,
743 DWORD dwFlags)
744 {
745 TCHAR buffer[64];
746 ULARGE_INTEGER uliFree;
747 TCHAR szRoot[] = _T("A:\\");
748
749 if (dwFlags & DIR_BARE)
750 return(0);
751
752 /* Print number of files and bytes */
753 ConvertULong (ulFiles, buffer, sizeof(buffer));
754 ConOutPrintf (_T(" %6s File%c"),
755 buffer, ulFiles == 1 ? _T(' ') : _T('s'));
756
757 ConvertULargeInteger (bytes, buffer, sizeof(buffer));
758 ConOutPrintf (_T(" %15s byte%c\n"),
759 buffer, bytes.QuadPart == 1 ? _T(' ') : _T('s'));
760
761 if (IncLine (pLine, dwFlags))
762 return 1;
763
764 /* Print number of dirs and bytes free */
765 ConvertULong (ulDirs, buffer, sizeof(buffer));
766 ConOutPrintf (_T(" %6s Dir%c"),
767 buffer, ulDirs == 1 ? _T(' ') : _T('s'));
768
769 if (!(dwFlags & DIR_RECURSE))
770 {
771 szRoot[0] = szPath[0];
772 GetUserDiskFreeSpace(szRoot, &uliFree);
773 ConvertULargeInteger (uliFree, buffer, sizeof(buffer));
774 ConOutPrintf (_T(" %15s bytes free\n"), buffer);
775 }
776
777 if (IncLine (pLine, dwFlags))
778 return 1;
779
780 return 0;
781 }
782
783
784 /*
785 * dir_list
786 *
787 * list the files in the directory
788 */
789 static INT
790 DirList (LPTSTR szPath, LPTSTR szFilespec, LPINT pLine, DWORD dwFlags)
791 {
792 TCHAR szFullPath[MAX_PATH];
793 WIN32_FIND_DATA file;
794 ULARGE_INTEGER bytecount;
795 FILETIME ft;
796 SYSTEMTIME dt;
797 HANDLE hFile;
798 TCHAR buffer[32];
799 ULONG filecount = 0;
800 ULONG dircount = 0;
801 INT count;
802
803 bytecount.QuadPart = 0;
804
805 _tcscpy (szFullPath, szPath);
806 if (szFullPath[_tcslen(szFullPath) - 1] != _T('\\'))
807 _tcscat (szFullPath, _T("\\"));
808 _tcscat (szFullPath, szFilespec);
809
810 hFile = FindFirstFile (szFullPath, &file);
811 if (hFile == INVALID_HANDLE_VALUE)
812 {
813 /* Don't want to print anything if scanning recursively
814 * for a file. RL
815 */
816 if ((dwFlags & DIR_RECURSE) == 0)
817 {
818 FindClose (hFile);
819 error_file_not_found ();
820 if (IncLine (pLine, dwFlags))
821 return 0;
822 return 1;
823 }
824 FindClose (hFile);
825 return 0;
826 }
827
828 /* moved down here because if we are recursively searching and
829 * don't find any files, we don't want just to print
830 * Directory of C:\SOMEDIR
831 * with nothing else
832 * Rob Lake 06/13/98
833 */
834 if ((dwFlags & DIR_BARE) == 0)
835 {
836 ConOutPrintf (_T(" Directory of %s\n"), szPath);
837 if (IncLine (pLine, dwFlags))
838 return 1;
839 ConOutPrintf (_T("\n"));
840 if (IncLine (pLine, dwFlags))
841 return 1;
842 }
843
844 /* For counting columns of output */
845 count = 0;
846
847 do
848 {
849 /* next file, if user doesn't want all files */
850 if (!(dwFlags & DIR_ALL) &&
851 ((file.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ||
852 (file.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)))
853 continue;
854
855 if (dwFlags & DIR_LWR)
856 {
857 _tcslwr (file.cAlternateFileName);
858 }
859
860 if (dwFlags & DIR_WIDE && (dwFlags & DIR_BARE) == 0)
861 {
862 ULARGE_INTEGER uliSize;
863
864 if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
865 {
866 if (file.cAlternateFileName[0] == _T('\0'))
867 _stprintf (buffer, _T("[%s]"), file.cFileName);
868 else
869 _stprintf (buffer, _T("[%s]"), file.cAlternateFileName);
870 dircount++;
871 }
872 else
873 {
874 if (file.cAlternateFileName[0] == _T('\0'))
875 _stprintf (buffer, _T("%s"), file.cFileName);
876 else
877 _stprintf (buffer, _T("%s"), file.cAlternateFileName);
878 filecount++;
879 }
880
881 ConOutPrintf (_T("%-15s"), buffer);
882 count++;
883 if (count == 5)
884 {
885 /* output 5 columns */
886 ConOutPrintf (_T("\n"));
887 if (IncLine (pLine, dwFlags))
888 return 1;
889 count = 0;
890 }
891
892 uliSize.LowPart = file.nFileSizeLow;
893 uliSize.HighPart = file.nFileSizeHigh;
894 bytecount.QuadPart += uliSize.QuadPart;
895 }
896 else if (dwFlags & DIR_BARE)
897 {
898 ULARGE_INTEGER uliSize;
899
900 if (_tcscmp (file.cFileName, _T(".")) == 0 ||
901 _tcscmp (file.cFileName, _T("..")) == 0)
902 continue;
903
904 if (dwFlags & DIR_RECURSE)
905 {
906 TCHAR dir[MAX_PATH];
907
908 _tcscpy (dir, szPath);
909 _tcscat (dir, _T("\\"));
910 if (dwFlags & DIR_LWR)
911 _tcslwr (dir);
912 ConOutPrintf (dir);
913 }
914
915 ConOutPrintf (_T("%-13s\n"), file.cFileName);
916 if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
917 dircount++;
918 else
919 filecount++;
920 if (IncLine (pLine, dwFlags))
921 return 1;
922
923 uliSize.LowPart = file.nFileSizeLow;
924 uliSize.HighPart = file.nFileSizeHigh;
925 bytecount.QuadPart += uliSize.QuadPart;
926 }
927 else
928 {
929 if (dwFlags & DIR_NEW)
930 {
931 /* print file date and time */
932 if (FileTimeToLocalFileTime (&file.ftLastWriteTime, &ft))
933 {
934 FileTimeToSystemTime (&ft, &dt);
935 PrintFileDateTime (&dt, dwFlags);
936 }
937
938 /* print file size */
939 if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
940 {
941 ConOutPrintf (_T(" <DIR> "));
942 dircount++;
943 }
944 else
945 {
946 ULARGE_INTEGER uliSize;
947
948 uliSize.LowPart = file.nFileSizeLow;
949 uliSize.HighPart = file.nFileSizeHigh;
950
951 ConvertULargeInteger (uliSize, buffer, sizeof(buffer));
952 ConOutPrintf (_T(" %20s"), buffer);
953
954 bytecount.QuadPart += uliSize.QuadPart;
955 filecount++;
956 }
957
958 /* print long filename */
959 ConOutPrintf (_T(" %s\n"), file.cFileName);
960 }
961 else
962 {
963 if (file.cFileName[0] == _T('.'))
964 ConOutPrintf (_T("%-13s "), file.cFileName);
965 else if (file.cAlternateFileName[0] == _T('\0'))
966 {
967 TCHAR szShortName[13];
968 LPTSTR ext;
969
970 _tcsncpy (szShortName, file.cFileName, 13);
971 ext = _tcschr (szShortName, _T('.'));
972 if (!ext)
973 ext = _T("");
974 else
975 *ext++ = _T('\0');
976 ConOutPrintf (_T("%-8s %-3s "), szShortName, ext);
977 }
978 else
979 {
980 LPTSTR ext;
981
982 ext = _tcschr (file.cAlternateFileName, _T('.'));
983 if (!ext)
984 ext = _T("");
985 else
986 *ext++ = _T('\0');
987 ConOutPrintf (_T("%-8s %-3s "), file.cAlternateFileName, ext);
988 }
989
990 /* print file size */
991 if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
992 {
993 ConOutPrintf ("%-14s", "<DIR>");
994 dircount++;
995 }
996 else
997 {
998 ULARGE_INTEGER uliSize;
999
1000 uliSize.LowPart = file.nFileSizeLow;
1001 uliSize.HighPart = file.nFileSizeHigh;
1002 ConvertULargeInteger (uliSize, buffer, sizeof(buffer));
1003 ConOutPrintf (_T(" %10s "), buffer);
1004 bytecount.QuadPart += uliSize.QuadPart;
1005 filecount++;
1006 }
1007
1008 /* print file date and time */
1009 if (FileTimeToLocalFileTime (&file.ftLastWriteTime, &ft))
1010 {
1011 FileTimeToSystemTime (&ft, &dt);
1012 PrintFileDateTime (&dt, dwFlags);
1013 }
1014
1015 /* print long filename */
1016 ConOutPrintf (" %s\n", file.cFileName);
1017 }
1018
1019 if (IncLine (pLine, dwFlags))
1020 return 1;
1021 }
1022 }
1023 while (FindNextFile (hFile, &file));
1024 FindClose (hFile);
1025
1026 /* Rob Lake, need to make clean output */
1027 /* JPP 07/08/1998 added check for count != 0 */
1028 if ((dwFlags & DIR_WIDE) && (count != 0))
1029 {
1030 ConOutPrintf (_T("\n"));
1031 if (IncLine (pLine, dwFlags))
1032 return 1;
1033 }
1034
1035 if (filecount || dircount)
1036 {
1037 recurse_dir_cnt += dircount;
1038 recurse_file_cnt += filecount;
1039 recurse_bytes.QuadPart += bytecount.QuadPart;
1040
1041 /* print_summary */
1042 if (PrintSummary (szPath, filecount, dircount, bytecount, pLine, dwFlags))
1043 return 1;
1044 }
1045 else
1046 {
1047 error_file_not_found ();
1048 return 1;
1049 }
1050
1051 return 0;
1052 }
1053
1054
1055 /*
1056 * _Read_Dir: Actual function that does recursive listing
1057 */
1058 static INT
1059 DirRead (LPTSTR szPath, LPTSTR szFilespec, LPINT pLine, DWORD dwFlags)
1060 {
1061 TCHAR szFullPath[MAX_PATH];
1062 WIN32_FIND_DATA file;
1063 HANDLE hFile;
1064
1065 _tcscpy (szFullPath, szPath);
1066 if (szFullPath[_tcslen (szFullPath) - 1] != _T('\\'))
1067 _tcscat (szFullPath, _T("\\"));
1068 _tcscat (szFullPath, szFilespec);
1069
1070 hFile = FindFirstFile (szFullPath, &file);
1071 if (hFile == INVALID_HANDLE_VALUE)
1072 return 1;
1073
1074 do
1075 {
1076 /* don't list "." and ".." */
1077 if (_tcscmp (file.cFileName, _T(".")) == 0 ||
1078 _tcscmp (file.cFileName, _T("..")) == 0)
1079 continue;
1080
1081 if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1082 {
1083 _tcscpy (szFullPath, szPath);
1084 if (szFullPath[_tcslen (szFullPath) - 1] != _T('\\'))
1085 _tcscat (szFullPath, _T("\\"));
1086 _tcscat (szFullPath, file.cFileName);
1087
1088 if (DirList (szFullPath, szFilespec, pLine, dwFlags))
1089 {
1090 FindClose (hFile);
1091 return 1;
1092 }
1093
1094 if ((dwFlags & DIR_BARE) == 0)
1095 {
1096 ConOutPrintf ("\n");
1097 if (IncLine (pLine, dwFlags) != 0)
1098 return 1;
1099 ConOutPrintf ("\n");
1100 if (IncLine (pLine, dwFlags) != 0)
1101 return 1;
1102 }
1103
1104 if (DirRead (szFullPath, szFilespec, pLine, dwFlags) == 1)
1105 {
1106 FindClose (hFile);
1107 return 1;
1108 }
1109 }
1110 }
1111 while (FindNextFile (hFile, &file));
1112
1113 if (!FindClose (hFile))
1114 return 1;
1115
1116 return 0;
1117 }
1118
1119
1120 /*
1121 * do_recurse: Sets up for recursive directory listing
1122 */
1123 static INT
1124 DirRecurse (LPTSTR szPath, LPTSTR szSpec, LPINT pLine, DWORD dwFlags)
1125 {
1126 if (!PrintDirectoryHeader (szPath, pLine, dwFlags))
1127 return 1;
1128
1129 if (DirList (szPath, szSpec, pLine, dwFlags))
1130 return 1;
1131
1132 if ((dwFlags & DIR_BARE) == 0)
1133 {
1134 ConOutPrintf (_T("\n"));
1135 if (IncLine (pLine, dwFlags))
1136 return 1;
1137 }
1138
1139 if (DirRead (szPath, szSpec, pLine, dwFlags))
1140 return 1;
1141
1142 if ((dwFlags & DIR_BARE) == 0)
1143 ConOutPrintf (_T("Total files listed:\n"));
1144
1145 dwFlags &= ~DIR_RECURSE;
1146
1147 if (PrintSummary (szPath, recurse_file_cnt,
1148 recurse_dir_cnt, recurse_bytes, pLine, dwFlags))
1149 return 1;
1150
1151 if ((dwFlags & DIR_BARE) == 0)
1152 {
1153 ConOutPrintf (_T("\n"));
1154 if (IncLine (pLine, dwFlags))
1155 return 1;
1156 }
1157
1158 return 0;
1159 }
1160
1161
1162 /*
1163 * dir
1164 *
1165 * internal dir command
1166 */
1167 INT CommandDir (LPTSTR first, LPTSTR rest)
1168 {
1169 DWORD dwFlags = DIR_NEW | DIR_FOUR;
1170 TCHAR dircmd[256];
1171 TCHAR szPath[MAX_PATH];
1172 TCHAR szFilespec[MAX_PATH];
1173 LPTSTR param;
1174 INT nLine = 0;
1175
1176
1177 recurse_dir_cnt = 0L;
1178 recurse_file_cnt = 0L;
1179 recurse_bytes.QuadPart = 0;
1180
1181 /* read the parameters from the DIRCMD environment variable */
1182 if (GetEnvironmentVariable (_T("DIRCMD"), dircmd, 256))
1183 {
1184 if (!DirReadParam (dircmd, &param, &dwFlags))
1185 return 1;
1186 }
1187
1188 /* read the parameters */
1189 if (!DirReadParam (rest, &param, &dwFlags))
1190 return 1;
1191
1192 /* default to current directory */
1193 if (!param)
1194 param = ".";
1195
1196 /* parse the directory info */
1197 if (DirParsePathspec (param, szPath, szFilespec))
1198 return 1;
1199
1200 if (dwFlags & DIR_RECURSE)
1201 {
1202 if (IncLine (&nLine, dwFlags))
1203 return 0;
1204 if (DirRecurse (szPath, szFilespec, &nLine, dwFlags))
1205 return 1;
1206 return 0;
1207 }
1208
1209 /* print the header */
1210 if (!PrintDirectoryHeader (szPath, &nLine, dwFlags))
1211 return 1;
1212
1213 if (DirList (szPath, szFilespec, &nLine, dwFlags))
1214 return 1;
1215
1216 return 0;
1217 }
1218
1219 #endif
1220
1221 /* EOF */