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