revert between 56095 -> 55830 in arch
[AROS.git] / workbench / c / List.c
blobc88885f3bfdd5a8190385d113179bbb5f08e6a76
1 /*
2 Copyright © 1995-2015, The AROS Development Team. All rights reserved.
3 $Id$
5 List the contents of a directory.
6 */
7 /*****************************************************************************
9 NAME
11 List
13 FORMAT
15 List [(dir | pattern | filename)] [ PAT (pattern)] [KEYS] [DATES]
16 [NODATES] [TO (name)] [SUB (string)] [SINCE (date)] [UPTO (date)]
17 [QUICK] [BLOCK] [NOHEAD] [FILES] [DIRS] [LFORMAT (string)] [ALL]
19 TEMPLATE
21 DIR/M,P=PAT/K,KEYS/S,DATES/S,NODATES/S,TO/K,SUB/K,SINCE/K,UPTO/K,QUICK/S,BLOCK/S,NOHEAD/S,FILES/S,DIRS/S,LFORMAT/K,ALL/S
23 LOCATION
27 FUNCTION
29 Lists detailed information about the files and directories in the
30 current directory or in the directory specified by DIR.
32 The information for each file or directory is presented on a separate
33 line, containing the following information:
35 name
36 size (in bytes)
37 protection bits
38 date and time
40 INPUTS
42 DIR -- The directory to list. If left out, the current
43 directory will be listed.
44 PAT -- Display only files matching 'string'
45 KEYS -- Display the block number of each file or directory
46 DATES -- Always display the full modification date of files
47 and directories instead of a day name.
48 NODATES -- Don't display dates
49 TO (name) -- Write the listing to a file instead of stdout
50 SUB (string) -- Display only files, a substring of which matches
51 the substring 'string'
52 SINCE (date) -- Display only files newer than 'date'
53 UPTO (date) -- Display only files older than 'date'
54 QUICK -- Display only the names of files
55 BLOCK -- File sizes are in blocks of 512 bytes
56 NOHEAD -- Don't print any header information
57 FILES -- Display files only
58 DIRS -- Display directories only
59 LFORMAT -- Specify the list output in printf-style
60 ALL -- List the contents of directories recursively
63 The following attributes of the LFORMAT strings are available
65 %A -- file attributes
66 %B -- size of file in blocks rather than bytes
67 %C -- file comment
68 %D -- modification date
69 %E -- file extension
70 %F -- absolute file path, with volume label
71 %K -- file key block number
72 %L -- size of file in bytes
73 %M -- file name without extension
74 %N -- file name
75 %P -- file path
76 %S -- superseded by %N and %P; obsolete
77 %T -- modification time
80 Additionally, the following modifiers, each optional, can be used,
81 in this order, following the % character:
83 left-justify -- minus sign
84 field width minimum -- value
85 value width maximum -- dot value
87 Value width maximum is not available for all numeric fields.
89 RESULT
91 Standard DOS return codes.
93 EXAMPLE
95 1> List C:
96 Directory "c:" on Wednesday 12/18/14:
97 Assign 6548 ---rwed Saturday 01:12:16
98 Copy 17772 ---rwed Saturday 01:12:24
99 AddBuffers 5268 ---rwed Saturday 01:14:46
100 Avail 8980 ---rwed Saturday 01:14:51
101 Delete 8756 ---rwed Saturday 01:14:59
102 Install 13024 ---rwed Saturday 01:15:09
103 List 20228 ---rwed Today 12:06:38
104 Which 7840 ---rwed Saturday 01:16:09
105 8 file - 167 blocks used
107 1> List C: lformat "[%10.5M] -- >%-4b<"
108 [ Assig] -- >13 <
109 [ Copy] -- >35 <
110 [ AddBu] -- >11 <
111 [ Avail] -- >18 <
112 [ Delet] -- >18 <
113 [ Insta] -- >26 <
114 [ List] -- >40 <
115 [ Which] -- >16 <
118 BUGS
120 SEE ALSO
124 INTERNALS
126 Current lformat interpretation requires re-interpretation of the format for each entry.
129 ******************************************************************************/
131 #define DEBUG 0
132 #include <aros/debug.h>
134 #include <clib/macros.h>
135 #include <exec/memory.h>
136 #include <proto/exec.h>
137 #include <dos/datetime.h>
138 #include <dos/dos.h>
139 #include <dos/exall.h>
140 #include <dos/dosasl.h>
141 #include <dos/datetime.h>
142 #include <proto/dos.h>
143 #include <proto/alib.h>
144 #include <proto/utility.h>
145 #include <utility/tagitem.h>
147 const TEXT version[] = "$VER: List 41.14 (26.01.2018)";
149 #define ARG_TEMPLATE "DIR/M,P=PAT/K,KEYS/S,DATES/S,NODATES/S,TO/K,SUB/K,SINCE/K,UPTO/K,QUICK/S,BLOCK/S,NOHEAD/S,FILES/S,DIRS/S,LFORMAT/K,ALL/S"
151 struct DirNode
153 struct MinNode node;
154 char *dirname;
158 typedef struct _Statistics
160 ULONG nFiles;
161 ULONG nDirs;
162 UQUAD nBlocks;
163 } Statistics;
166 enum
168 ARG_DIR = 0,
169 ARG_PAT,
170 ARG_KEYS,
171 ARG_DATES,
172 ARG_NODATES,
173 ARG_TO,
174 ARG_SUB,
175 ARG_SINCE,
176 ARG_UPTO,
177 ARG_QUICK,
178 ARG_BLOCK,
179 ARG_NOHEAD,
180 ARG_FILES,
181 ARG_DIRS,
182 ARG_LFORMAT,
183 ARG_ALL,
184 NOOFARGS
187 #define MAX_PATH_LEN 1024
189 #define BLOCKSIZE 512
191 #define DIRTEXT "Dir"
192 #define EMPTYTEXT "empty"
195 /* UQUAD2string: Helper function to generate a decimal string representation for a UQUAD value.
196 Arguments: Value; a UBYTE buffer large enough to hold the string; length of the buffer.
197 Returns: Pointer to string inside the buffer. (String is end-aligned in the buffer!)
198 Note: Just a helper: Not safe with incorrect input!
201 UBYTE *UQUAD2string( UQUAD value, UBYTE *buffer, int buflen)
203 buffer[ --buflen] = '\0';
207 buffer[ --buflen] = '0' + (value % 10);
208 value /= 10;
209 } while (value);
211 return buffer +buflen;
216 int printDirHeader(STRPTR dirname, BOOL noHead)
218 struct DateTime dt;
220 char datestr[LEN_DATSTRING];
221 char dow[LEN_DATSTRING];
223 if (!noHead)
225 DateStamp((struct DateStamp *)&dt);
226 dt.dat_Format = FORMAT_DEF;
227 dt.dat_Flags = 0;
228 dt.dat_StrDay = dow;
229 dt.dat_StrDate = datestr;
230 dt.dat_StrTime = NULL;
231 DateToStr(&dt);
233 Printf("Directory \"%s\" on %s %s:\n", dirname, dow, datestr);
236 return RETURN_OK;
240 /* Possible lformat switches
242 %A -- file attributes
243 %B -- size of file in blocks rather than bytes
244 %C -- file comment
245 %D -- file date
246 %E -- file extension
247 %F -- absolute file path, with volume label
248 %K -- file key block number
249 %L -- size of file in bytes
250 %M -- file name without extension
251 %N -- file name
252 %P -- file path
253 %S -- file name or file path
254 %T -- file time
256 %F used to be documented as just volume name. Though not Amiga-compatible, volume and path separate might indeed be useful.
259 struct lfstruct
261 struct AnchorPath *ap;
262 BOOL isdir;
263 STRPTR date;
264 STRPTR time;
265 STRPTR flags;
266 STRPTR filename;
267 STRPTR comment;
268 UQUAD size;
269 ULONG key;
273 #define roundUp(x, bSize) ((x + bSize - 1)/bSize)
276 int printLformat(STRPTR format, struct lfstruct *lf)
278 STRPTR filename = FilePart(lf->filename);
279 STRPTR temp = format;
280 LONG substitutePath = 0;
281 char c, cu;
282 UBYTE fbuf[ 260]; // 256 plus 3 characters format string plus '\0'.
283 int fbufindex, dot;
286 Whether the path or the filename is substituted for an occurrence
287 of %S depends on how many occurrences are in the LFORMAT line, and
288 their order, as follows:
290 Occurrences of %S 1st 2nd 3rd 4th
291 1 filename
292 2 path filename
293 3 path filename filename
294 4 path filename path filename
296 For 5 or more occurences: As with 4, with occurences beyond the 4th the filename.
299 while ( ( substitutePath < 4 ) && ( '\0' != (c = *temp++) ) )
301 if ( '%' == c )
302 if ( 'S' == ToUpper(*temp++) )
303 substitutePath++;
305 if ( substitutePath == 3 )
306 substitutePath = 2;
309 while ('\0' != (c = *format++))
311 if ('%' == c) // Character introducing a format switch in lformat.
313 /* Try for modifiers */
314 fbufindex= 0;
315 dot= 0;
316 fbuf[ fbufindex++]= '%'; // Introducing a format type for PrintF.
318 while ((c = *format++))
320 if (c == '-') // Left align
322 fbufindex= 1; // Only the last one counts
323 dot= 0; // Reset max value width as well
325 else if (c == '.') // Max value width.
327 if (dot)
329 fbufindex= dot; // Only the last one counts.
331 else
333 dot= fbufindex; // Max value width starts after the dot
336 else if ( c < '0' || '9' < c) // It's not a digit either ==> end of modifiers
338 break;
340 if (fbufindex < 256) // Leave room for a three character format string plus \0.
342 fbuf[ fbufindex++]= c;
343 } // Squeezes out any overflow silently. Not a likely event, but is it acceptable? Desperado 20141217
347 /* Interpret argument */
348 switch (cu= ToUpper(c))
350 /* File comment */
351 case 'C':
352 strcpy( fbuf +fbufindex, "s");
353 D(bug("[List] rawFormat = [%s]", fbuf));
354 Printf( fbuf, lf->comment);
355 break;
357 /* Modification date */
358 case 'D':
359 strcpy( fbuf +fbufindex, "s");
360 D(bug("[List] rawFormat = [%s]", fbuf));
361 Printf( fbuf, lf->date);
362 break;
364 /* Modification time */
365 case 'T':
366 strcpy( fbuf +fbufindex, "s");
367 D(bug("[List] rawFormat = [%s]", fbuf));
368 Printf( fbuf, lf->time);
369 break;
371 /* File size */
372 case 'L':
373 /* File size in blocks of BLOCKSIZE bytes */
374 case 'B':
376 if (lf->isdir)
378 strcpy( fbuf +fbufindex, "s");
379 D(bug("[List] rawFormat = [%s]", fbuf));
380 Printf( fbuf, DIRTEXT);
382 else
384 UQUAD size= ( cu == 'B' ? roundUp(lf->size, BLOCKSIZE) : lf->size); // Blocks or bytes.
386 /* File has no content? */
387 if (size == 0)
389 strcpy( fbuf +fbufindex, "s");
390 D(bug("[List] rawFormat = [%s]", fbuf));
391 Printf( fbuf, EMPTYTEXT);
393 else
395 UBYTE buf[ 256]; // Should be UQUADSTRSIZE +1, but this will suffice.
396 UBYTE *quadstr= UQUAD2string( size, buf, 256);
398 strcpy( fbuf +fbufindex, "s"); // Should we implement a '%q' type?
399 D(bug("[List] rawFormat = [%s]", fbuf));
400 Printf( fbuf, quadstr);
401 } // Side effect of converting uquad to string: User can even maxsize numbers, if wanted.
404 break;
406 /* Path incl. volume name*/
407 case 'F':
409 UBYTE buf[257]; // 256 + room for an extra '/'.
411 if (NameFromLock(lf->ap->ap_Current->an_Lock, buf, 256))
413 int len = strlen(buf); // For checking the end of the string
415 if ((len > 0) && (buf[len - 1] != ':') && (buf[len - 1] != '/')) // We need a separator:
417 strcpy( buf +len, "/"); // Add an /.
420 strcpy( fbuf +fbufindex, "s");
421 D(bug("[List] rawFormat = [%s]", fbuf));
422 Printf( fbuf, buf);
426 break;
428 /* File attributes (flags) */
429 case 'A':
430 strcpy( fbuf +fbufindex, "s");
431 D(bug("[List] rawFormat = [%s]", fbuf));
432 Printf( fbuf, lf->flags);
433 break;
435 /* Disk block key */
436 case 'K':
437 strcpy( fbuf +fbufindex, "lu");
438 D(bug("[List] rawFormat = [%s]", fbuf));
439 Printf( fbuf, lf->key);
440 break;
442 /* File name without extension */
443 case 'M':
445 STRPTR lastPoint = strrchr(filename, '.');
447 if (lastPoint != NULL)
449 *lastPoint = 0;
452 strcpy( fbuf +fbufindex, "s");
453 D(bug("[List] rawFormat = [%s]", fbuf));
454 Printf( fbuf, filename);
456 /* Resurrect filename in case we need to print it again */
457 if (lastPoint != NULL)
459 *lastPoint = '.';
463 break;
465 /* Filename or Path name */
466 case 'S':
467 D(bug("[List] substitutePath = %d\n", substitutePath));
468 if ( (--substitutePath == 3) || (substitutePath == 1) )
470 STRPTR end = FilePart(lf->filename);
471 UBYTE token = *end;
473 *end = '\0';
475 strcpy( fbuf +fbufindex, "s");
476 D(bug("[List] rawFormat = [%s]", fbuf));
477 Printf( fbuf, lf->filename);
479 /* Restore pathname */
480 *end = token;
482 break;
484 /* Fall through */
485 case 'N':
486 strcpy( fbuf +fbufindex, "s");
487 D(bug("[List] rawFormat = [%s]", fbuf));
488 Printf( fbuf, filename);
489 break;
491 /* File extension */
492 case 'E':
494 STRPTR extension = strrchr(filename, '.');
496 if (extension != NULL)
498 strcpy( fbuf +fbufindex, "s");
499 D(bug("[List] rawFormat = [%s]", fbuf));
500 Printf( fbuf, ++extension); // Skip the dot.
504 break;
506 /* File path as specified */
507 case 'P':
509 STRPTR end = FilePart(lf->filename);
510 UBYTE token = *end;
512 *end = 0;
514 strcpy( fbuf +fbufindex, "s");
515 D(bug("[List] rawFormat = [%s]", fbuf));
516 Printf( fbuf, lf->filename);
518 /* Restore pathname */
519 *end = token;
522 break;
524 /* Unexpected end of format */
525 case 0:
526 fbuf[ fbufindex]= '\0'; // Just add end to the string.
527 Printf( "%s", fbuf); // We never found the switch, so print as text.
528 return 0;
529 break;
531 /* Unrecognised %-sequence */
532 default:
533 fbuf[ fbufindex]= '\0'; // Just add end to the string.
534 Printf("%s%lc", fbuf, c); // Print interpreted format part as text.
535 break;
538 else
540 Printf("%lc", c);
544 return 0;
548 int printFileData(struct AnchorPath *ap,
549 BOOL showFiles, BOOL showDirs, STRPTR parsedPattern,
550 ULONG *files, ULONG *dirs, ULONG *nBlocks, STRPTR lFormat,
551 BOOL quick, BOOL dates, BOOL noDates, BOOL block,
552 struct DateStamp *sinceDate, struct DateStamp *uptoDate,
553 BOOL doSince, BOOL doUpto, STRPTR subpatternStr,
554 BOOL keys)
556 STRPTR filename = ap->ap_Buf;
557 BOOL isDir = (ap->ap_Info.fib_DirEntryType >= 0);
558 struct DateStamp *ds = &ap->ap_Info.fib_Date;
559 ULONG protection = ap->ap_Info.fib_Protection;
560 UQUAD size = ap->ap_Info.fib_Size;
561 STRPTR filenote = ap->ap_Info.fib_Comment;
562 ULONG diskKey = ap->ap_Info.fib_DiskKey;
564 int error = 0;
566 UBYTE date[LEN_DATSTRING];
567 UBYTE time[LEN_DATSTRING];
568 UBYTE flags[8];
570 struct DateTime dt;
572 #if defined(ACTION_GET_FILE_SIZE64)
573 if (ap->ap_Info.fib_Size >= 0x7FFFFFFF)
575 BPTR flock = BNULL;
576 flock = Lock(filename, ACCESS_READ);
578 if (flock)
580 UQUAD *size_ptr = (UQUAD *)DoPkt(((struct FileLock *)flock)->fl_Task, ACTION_GET_FILE_SIZE64, (IPTR)flock, 0, 0, 0, 0);
581 if (size_ptr)
583 size = *size_ptr;
585 UnLock(flock);
588 #endif
589 /* Do the file match the time interval we are looking for?
590 (ARG_SINCE and ARG_UPTO) -- any combination of these may be
591 specified */
592 if ((doSince && (CompareDates(sinceDate, ds) < 0)) ||
593 (doUpto && (CompareDates(uptoDate, ds) > 0)))
595 return 0;
598 /* Does the filename match a certain pattern? (ARG_PAT) */
599 if (parsedPattern != NULL &&
600 !MatchPatternNoCase(parsedPattern, FilePart(filename)))
602 return 0;
605 /* Does a substring of the filename match a certain pattern? (ARG_SUB) */
606 if (subpatternStr != NULL &&
607 !MatchPatternNoCase(subpatternStr, FilePart(filename)))
609 return 0;
612 CopyMem(ds, &dt.dat_Stamp, sizeof(struct DateStamp));
613 dt.dat_Format = FORMAT_DOS;
614 if (dates)
615 dt.dat_Flags = 0;
616 else
617 dt.dat_Flags = DTF_SUBST;
618 dt.dat_StrDay = NULL;
619 dt.dat_StrDate = date;
620 dt.dat_StrTime = time;
621 DateToStr(&dt);
623 /* Convert the protection bits to a string */
624 // Active when set
625 flags[0] = protection & FIBF_SCRIPT ? 's' : '-';
626 flags[1] = protection & FIBF_PURE ? 'p' : '-';
627 flags[2] = protection & FIBF_ARCHIVE ? 'a' : '-';
629 // Active when unset!
630 flags[3] = protection & FIBF_READ ? '-' : 'r';
631 flags[4] = protection & FIBF_WRITE ? '-' : 'w';
632 flags[5] = protection & FIBF_EXECUTE ? '-' : 'e';
633 flags[6] = protection & FIBF_DELETE ? '-' : 'd';
634 flags[7] = 0x00;
636 if (isDir)
638 if (showDirs)
640 ++*dirs;
641 ++*nBlocks; /* dir entry uses 1 block on AROS, 2 on OS31) */
643 if (lFormat != NULL)
645 struct lfstruct lf = { ap, isDir, date, time, flags, filename,
646 filenote, size, diskKey};
648 printLformat(lFormat, &lf);
649 Printf("\n");
651 else
653 D(bug("Found file %s\n", filename));
655 Printf("%-24s ", FilePart(filename)); // Entry name field width
657 if (!quick)
659 Printf("%7s %7s ", DIRTEXT, flags); // Size field width, flags field width
662 if (!noDates && (!quick || dates))
664 Printf("%-11s %s", date, time); // Date field width
667 Printf("\n");
671 else if (showFiles)
673 ++*files;
674 *nBlocks += roundUp(size, BLOCKSIZE);
676 if (lFormat != NULL)
678 struct lfstruct lf = { ap, isDir, date, time, flags, filename,
679 filenote, size, diskKey };
681 printLformat(lFormat, &lf);
682 Printf("\n");
684 else
686 Printf("%-24s ", FilePart(filename)); // Entryname field width
688 if (!quick)
690 if(keys)
692 char key[16];
694 __sprintf(key, "%lu", (unsigned long)diskKey);
696 Printf("[%5lu] ", diskKey); // Key field width
698 else
700 if (0 != size)
702 UQUAD filesize = block ? roundUp(size, BLOCKSIZE) : size;
704 UBYTE buf[ 256]; // Should be UQUADSTRSIZE +1, but this will suffice.
705 UBYTE *quadstr= UQUAD2string( filesize, buf, 256);
707 Printf("%7s ", quadstr); // Size field width
709 else
711 Printf("%7s ", EMPTYTEXT); // Size field width
715 Printf("%7s ", flags); // Flags field width
718 if (!noDates && (!quick || dates))
720 Printf("%-11s %s", date, time); // Date field width
723 if (!quick && (*filenote != 0))
725 Printf("\n: %s", filenote);
728 Printf("\n");
732 return error;
736 /* Print directory summary information */
737 void printSummary(CONST_STRPTR dirname, int files, int dirs, int nBlocks,
738 BOOL noHead, BOOL PrintEmpty)
741 if (noHead) return;
743 if (files || dirs)
746 if (files > 1)
747 Printf("%ld files", files);
748 else if (files > 0)
749 PutStr("1 file");
751 if( files && (dirs || nBlocks) ) PutStr(" - ");
753 if (dirs > 1)
754 Printf("%ld directories", dirs);
755 else if (dirs > 0)
756 PutStr("1 directory");
758 if( dirs && nBlocks ) PutStr(" - ");
760 if (nBlocks > 1)
761 Printf("%ld blocks used\n", nBlocks);
762 else if (nBlocks > 0)
763 PutStr("1 block used\n");
764 else PutStr("\n");
767 else if (PrintEmpty)
768 Printf("Directory \"%s\" is empty\n", dirname);
772 int listFile(CONST_STRPTR filename, BOOL showFiles, BOOL showDirs,
773 STRPTR parsedPattern, BOOL noHead, STRPTR lFormat, BOOL quick,
774 BOOL dates, BOOL noDates, BOOL block, struct DateStamp *sinceDate,
775 struct DateStamp *uptoDate, BOOL doSince, BOOL doUpto,
776 STRPTR subpatternStr, BOOL all, BOOL keys, Statistics *stats)
778 struct AnchorPath *ap;
779 struct List DirList, FreeDirNodeList;
780 struct DirNode *dirnode, *prev_dirnode = NULL;
782 ULONG files = 0;
783 ULONG dirs = 0;
784 ULONG nBlocks = 0;
785 LONG error;
787 NewList(&DirList);
788 NewList(&FreeDirNodeList);
792 ap = AllocVec(sizeof(struct AnchorPath) + MAX_PATH_LEN, MEMF_CLEAR);
794 if (ap == NULL)
796 return 0;
799 ap->ap_Strlen = MAX_PATH_LEN;
801 error = MatchFirst(filename, ap);
803 /* Explicitly named directory and not a pattern? --> enter dir */
805 if (0 == error)
807 if (!(ap->ap_Flags & APF_ITSWILD))
809 if (ap->ap_Info.fib_DirEntryType >= 0)
811 //error = printDirHeader(filename, noHead);
812 ap->ap_Flags |= APF_DODIR;
814 if (0 == error)
816 error = MatchNext(ap);
822 if (0 == error)
824 BOOL first = TRUE;
826 ap->ap_BreakBits = SIGBREAKF_CTRL_C;
827 if (FilePart(ap->ap_Buf) == ap->ap_Buf)
829 ap->ap_Flags &= ~APF_DirChanged;
835 ** There's something to show.
837 if (!(ap->ap_Flags & APF_DIDDIR))
839 if (ap->ap_Flags & APF_DirChanged)
841 STRPTR p;
842 UBYTE c;
844 if (!first) printSummary(filename, files, dirs, nBlocks, noHead, TRUE);
846 /* Update global statistics for (possible) ALL option */
847 stats->nFiles += files;
848 stats->nDirs += dirs;
849 stats->nBlocks += nBlocks;
851 files = 0;
852 dirs = 0;
853 nBlocks = 0;
855 p = PathPart(ap->ap_Buf);
856 c = *p;
857 *p = 0;
859 error = printDirHeader(ap->ap_Buf, noHead);
861 *p = c;
865 error = printFileData(ap,
866 showFiles,
867 showDirs,
868 parsedPattern,
869 &files,
870 &dirs,
871 &nBlocks,
872 lFormat,
873 quick,
874 dates,
875 noDates,
876 block,
877 sinceDate,
878 uptoDate,
879 doSince,
880 doUpto,
881 subpatternStr,
882 keys);
884 if (all && (ap->ap_Info.fib_DirEntryType >= 0))
886 if ((dirnode = AllocMem(sizeof(struct DirNode), MEMF_ANY)))
888 if ((dirnode->dirname = StrDup(ap->ap_Buf)))
890 Insert(&DirList, (struct Node *)dirnode,
891 (struct Node *)prev_dirnode);
893 prev_dirnode = dirnode;
895 else
897 FreeMem(dirnode, sizeof(struct DirNode));
903 error = MatchNext(ap);
905 first = FALSE;
907 } while (0 == error);
910 MatchEnd(ap);
912 FreeVec(ap);
914 if (error == ERROR_BREAK)
916 PrintFault(error, NULL);
919 if (error == ERROR_NO_MORE_ENTRIES)
921 error = 0;
924 if ((error == 0) || (error == ERROR_BREAK))
926 BOOL printEmpty = !(ap->ap_Flags & APF_ITSWILD);
927 printSummary(filename, files, dirs, nBlocks, noHead, printEmpty);
930 /* Update global statistics for (possible) ALL option */
931 stats->nFiles += files;
932 stats->nDirs += dirs;
933 stats->nBlocks += nBlocks;
935 files = 0;
936 dirs = 0;
937 nBlocks = 0;
940 if (error) break;
942 dirnode = (struct DirNode *)RemHead(&DirList);
944 if (dirnode != NULL)
946 filename = dirnode->dirname;
948 prev_dirnode = NULL;
950 /* do not free() dirnode, as we reference dirnode->dirname! */
952 AddTail(&FreeDirNodeList, (struct Node *)dirnode);
954 } while (dirnode);
956 while ((dirnode = (struct DirNode *)RemHead(&FreeDirNodeList)))
958 FreeVec(dirnode->dirname);
959 FreeMem(dirnode, sizeof(struct DirNode));
962 return error;
966 int __nocommandline;
968 int main(void)
970 IPTR args[NOOFARGS] =
972 (IPTR) NULL, // ARG_DIR
973 (IPTR) NULL, // ARG_PAT
974 FALSE, // ARG_KEYS
975 FALSE, // ARG_DATES
976 FALSE, // ARG_NODATES
977 (IPTR) NULL, // ARG_TO
978 (IPTR) NULL, // ARG_SUB
979 (IPTR) NULL, // ARG_SINCE
980 (IPTR) NULL, // ARG_UPTO
981 FALSE, // ARG_QUICK
982 FALSE, // ARG_BLOCK
983 FALSE, // ARG_NOHEAD
984 FALSE, // ARG_FILES
985 FALSE, // ARG_DIRS
986 FALSE, // ARG_LFORMAT
987 FALSE // ARG_ALL
989 static CONST_STRPTR default_directories[] = {(CONST_STRPTR)"", 0};
990 struct RDArgs *rda;
992 LONG result = RETURN_OK;
993 LONG error = 0;
994 STRPTR parsedPattern = NULL;
995 STRPTR subpatternStr = NULL;
996 BPTR oldOutput = BNULL;
998 Statistics stats = { 0, 0, 0 };
1000 rda = ReadArgs(ARG_TEMPLATE, args, NULL);
1002 if (rda != NULL)
1004 CONST_STRPTR *directories = (CONST_STRPTR *)args[ARG_DIR];
1005 STRPTR lFormat = (STRPTR)args[ARG_LFORMAT];
1006 STRPTR pattern = (STRPTR)args[ARG_PAT];
1007 STRPTR toFile = (STRPTR)args[ARG_TO];
1008 STRPTR subStr = (STRPTR)args[ARG_SUB];
1009 STRPTR since = (STRPTR)args[ARG_SINCE];
1010 STRPTR upto = (STRPTR)args[ARG_UPTO];
1011 BOOL files = (BOOL)args[ARG_FILES];
1012 BOOL dirs = (BOOL)args[ARG_DIRS];
1013 BOOL noDates = (BOOL)args[ARG_NODATES];
1014 BOOL dates = (BOOL)args[ARG_DATES];
1015 BOOL quick = (BOOL)args[ARG_QUICK];
1016 BOOL noHead = (BOOL)args[ARG_NOHEAD];
1017 BOOL block = (BOOL)args[ARG_BLOCK];
1018 BOOL all = (BOOL)args[ARG_ALL];
1019 BOOL keys = (BOOL)args[ARG_KEYS];
1021 struct DateTime sinceDatetime;
1022 struct DateTime uptoDatetime;
1024 ULONG i; /* Loop variable */
1026 if (since != NULL)
1028 sinceDatetime.dat_StrDate = since;
1029 sinceDatetime.dat_StrTime = NULL;
1030 sinceDatetime.dat_Format = FORMAT_DEF;
1031 sinceDatetime.dat_Flags = 0;
1032 if (StrToDate(&sinceDatetime) == DOSFALSE)
1034 FreeArgs(rda);
1035 Printf("*** Illegal 'SINCE' parameter\n");
1037 return RETURN_FAIL;
1039 sinceDatetime.dat_Stamp.ds_Minute = 0;
1040 sinceDatetime.dat_Stamp.ds_Tick = 0;
1043 if (upto != NULL)
1045 uptoDatetime.dat_StrDate = upto;
1046 uptoDatetime.dat_StrTime = NULL;
1047 uptoDatetime.dat_Format = FORMAT_DEF;
1048 uptoDatetime.dat_Flags = 0;
1050 if (StrToDate(&uptoDatetime) == DOSFALSE)
1052 FreeArgs(rda);
1053 Printf("*** Illegal 'UPTO' parameter\n");
1055 return RETURN_FAIL;
1057 uptoDatetime.dat_Stamp.ds_Minute = 1439;
1058 uptoDatetime.dat_Stamp.ds_Tick = 2999;
1061 if (subStr != NULL)
1063 STRPTR subStrWithPat;
1064 ULONG length = (strlen(subStr) + sizeof("#?#?"))*2 + 2;
1066 subStrWithPat = AllocVec(length, MEMF_ANY);
1068 if (subStrWithPat == NULL)
1070 error = IoErr();
1071 FreeArgs(rda);
1072 PrintFault(error, "List");
1074 return RETURN_FAIL;
1077 strcpy(subStrWithPat, "#?");
1078 strcat(subStrWithPat, subStr);
1079 strcat(subStrWithPat, "#?");
1081 subpatternStr = AllocVec(length, MEMF_ANY);
1083 if (subpatternStr == NULL ||
1084 ParsePatternNoCase(subStrWithPat, subpatternStr, length) == -1)
1086 error = IoErr();
1087 FreeVec(subStrWithPat);
1088 FreeArgs(rda);
1089 PrintFault(error, "List");
1091 return RETURN_FAIL;
1094 FreeVec(subStrWithPat);
1098 if (pattern != NULL)
1100 ULONG length = strlen(pattern)*2 + 2;
1102 parsedPattern = AllocVec(length, MEMF_ANY);
1104 if (parsedPattern == NULL ||
1105 ParsePatternNoCase(pattern, parsedPattern, length) == -1)
1107 FreeVec(subpatternStr);
1108 FreeArgs(rda);
1110 return RETURN_FAIL;
1114 if (toFile != NULL)
1116 BPTR file = Open(toFile, MODE_NEWFILE);
1118 if (file == BNULL)
1120 error = IoErr();
1121 FreeVec(subpatternStr);
1122 FreeVec(parsedPattern);
1123 FreeArgs(rda);
1124 PrintFault(error, "List");
1126 return RETURN_FAIL;
1128 oldOutput = SelectOutput(file);
1131 if (!files && !dirs)
1133 files = TRUE;
1134 dirs = TRUE;
1137 /* if (!dates && !noDates)
1139 dates = TRUE;
1142 if (lFormat)
1144 noHead = TRUE;
1147 if ((directories == NULL) || (*directories == NULL))
1149 directories = default_directories;
1152 for (i = 0; directories[i] != NULL; i++)
1154 if (!IsFileSystem(directories[i]))
1156 Printf("\"%s\" cannot be listed: not a FileSystem device\n", directories[i]);
1157 result = RETURN_FAIL;
1158 break;
1161 error = listFile(directories[i], files, dirs, parsedPattern,
1162 noHead, lFormat, quick, dates, noDates,
1163 block, &sinceDatetime.dat_Stamp,
1164 &uptoDatetime.dat_Stamp, since != NULL,
1165 upto != NULL, subpatternStr, all, keys,
1166 &stats);
1168 if (error != 0)
1170 break;
1173 // Printf("\n");
1176 FreeArgs(rda);
1178 else
1180 error = IoErr();
1183 if ((BOOL)args[ARG_NOHEAD] == FALSE &&
1184 (BOOL)args[ARG_LFORMAT] == FALSE &&
1185 (BOOL)args[ARG_ALL] &&
1186 (stats.nFiles || stats.nDirs))
1188 Printf("\nTOTAL: %ld files - %ld directories - %ld blocks used\n",
1189 stats.nFiles, stats.nDirs, stats.nBlocks);
1193 if (error != 0)
1195 if (error == ERROR_BREAK)
1197 result = RETURN_WARN;
1199 else
1201 PrintFault(error, "List");
1202 result = RETURN_FAIL;
1206 if (parsedPattern != NULL)
1208 FreeVec(parsedPattern);
1211 if (subpatternStr != NULL)
1213 FreeVec(subpatternStr);
1216 if (oldOutput != BNULL)
1218 Close(SelectOutput(oldOutput));
1221 return result;