- Fixed a regression in decoding 8-bit images (introduced in my r46589).
[AROS.git] / workbench / c / List.c
blob91a73b0b02553dd9399f92ae682c739bdb2cce78
1 /*
2 Copyright © 1995-2014, 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 -- superceded 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 minumum -- 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.13 (18.12.2014)\n";
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 fuction to generates 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];
693 int i; /* Loop variable */
695 __sprintf(key, "%lu", (unsigned long)diskKey);
697 Printf("[%5lu] ", diskKey); // Key field width
699 else
701 if (0 != size)
703 UQUAD filesize = block ? roundUp(size, BLOCKSIZE) : size;
705 UBYTE buf[ 256]; // Should be UQUADSTRSIZE +1, but this will suffice.
706 UBYTE *quadstr= UQUAD2string( filesize, buf, 256);
708 Printf("%7s ", quadstr); // Size field width
710 else
712 Printf("%7s ", EMPTYTEXT); // Size field width
716 Printf("%7s ", flags); // Flags field width
719 if (!noDates && (!quick || dates))
721 Printf("%-11s %s", date, time); // Date field width
724 if (!quick && (*filenote != 0))
726 Printf("\n: %s", filenote);
729 Printf("\n");
733 return error;
737 /* Print directory summary information */
738 void printSummary(CONST_STRPTR dirname, int files, int dirs, int nBlocks,
739 BOOL noHead, BOOL PrintEmpty)
742 if (noHead) return;
744 if (files || dirs)
747 if (files > 1)
748 Printf("%ld files", files);
749 else if (files > 0)
750 PutStr("1 file");
752 if( files && (dirs || nBlocks) ) PutStr(" - ");
754 if (dirs > 1)
755 Printf("%ld directories", dirs);
756 else if (dirs > 0)
757 PutStr("1 directory");
759 if( dirs && nBlocks ) PutStr(" - ");
761 if (nBlocks > 1)
762 Printf("%ld blocks used\n", nBlocks);
763 else if (nBlocks > 0)
764 PutStr("1 block used\n");
765 else PutStr("\n");
768 else if (PrintEmpty)
769 Printf("Directory \"%s\" is empty\n", dirname);
773 int listFile(CONST_STRPTR filename, BOOL showFiles, BOOL showDirs,
774 STRPTR parsedPattern, BOOL noHead, STRPTR lFormat, BOOL quick,
775 BOOL dates, BOOL noDates, BOOL block, struct DateStamp *sinceDate,
776 struct DateStamp *uptoDate, BOOL doSince, BOOL doUpto,
777 STRPTR subpatternStr, BOOL all, BOOL keys, Statistics *stats)
779 struct AnchorPath *ap;
780 struct List DirList, FreeDirNodeList;
781 struct DirNode *dirnode, *prev_dirnode = NULL;
783 ULONG files = 0;
784 ULONG dirs = 0;
785 ULONG nBlocks = 0;
786 LONG error;
788 NewList(&DirList);
789 NewList(&FreeDirNodeList);
793 ap = AllocVec(sizeof(struct AnchorPath) + MAX_PATH_LEN, MEMF_CLEAR);
795 if (ap == NULL)
797 return 0;
800 ap->ap_Strlen = MAX_PATH_LEN;
802 error = MatchFirst(filename, ap);
804 /* Explicitly named directory and not a pattern? --> enter dir */
806 if (0 == error)
808 if (!(ap->ap_Flags & APF_ITSWILD))
810 if (ap->ap_Info.fib_DirEntryType >= 0)
812 //error = printDirHeader(filename, noHead);
813 ap->ap_Flags |= APF_DODIR;
815 if (0 == error)
817 error = MatchNext(ap);
823 if (0 == error)
825 BOOL first = TRUE;
827 ap->ap_BreakBits = SIGBREAKF_CTRL_C;
828 if (FilePart(ap->ap_Buf) == ap->ap_Buf)
830 ap->ap_Flags &= ~APF_DirChanged;
836 ** There's something to show.
838 if (!(ap->ap_Flags & APF_DIDDIR))
840 if (ap->ap_Flags & APF_DirChanged)
842 STRPTR p;
843 UBYTE c;
845 if (!first) printSummary(filename, files, dirs, nBlocks, noHead, TRUE);
847 /* Update global statistics for (possible) ALL option */
848 stats->nFiles += files;
849 stats->nDirs += dirs;
850 stats->nBlocks += nBlocks;
852 files = 0;
853 dirs = 0;
854 nBlocks = 0;
856 p = PathPart(ap->ap_Buf);
857 c = *p;
858 *p = 0;
860 error = printDirHeader(ap->ap_Buf, noHead);
862 *p = c;
866 error = printFileData(ap,
867 showFiles,
868 showDirs,
869 parsedPattern,
870 &files,
871 &dirs,
872 &nBlocks,
873 lFormat,
874 quick,
875 dates,
876 noDates,
877 block,
878 sinceDate,
879 uptoDate,
880 doSince,
881 doUpto,
882 subpatternStr,
883 keys);
885 if (all && (ap->ap_Info.fib_DirEntryType >= 0))
887 if ((dirnode = AllocMem(sizeof(struct DirNode), MEMF_ANY)))
889 if ((dirnode->dirname = StrDup(ap->ap_Buf)))
891 Insert(&DirList, (struct Node *)dirnode,
892 (struct Node *)prev_dirnode);
894 prev_dirnode = dirnode;
896 else
898 FreeMem(dirnode, sizeof(struct DirNode));
904 error = MatchNext(ap);
906 first = FALSE;
908 } while (0 == error);
911 MatchEnd(ap);
913 FreeVec(ap);
915 if (error == ERROR_BREAK)
917 PrintFault(error, NULL);
920 if (error == ERROR_NO_MORE_ENTRIES)
922 error = 0;
925 if ((error == 0) || (error == ERROR_BREAK))
927 BOOL printEmpty = !(ap->ap_Flags & APF_ITSWILD);
928 printSummary(filename, files, dirs, nBlocks, noHead, printEmpty);
931 /* Update global statistics for (possible) ALL option */
932 stats->nFiles += files;
933 stats->nDirs += dirs;
934 stats->nBlocks += nBlocks;
936 files = 0;
937 dirs = 0;
938 nBlocks = 0;
941 if (error) break;
943 dirnode = (struct DirNode *)RemHead(&DirList);
945 if (dirnode != NULL)
947 filename = dirnode->dirname;
949 prev_dirnode = NULL;
951 /* do not free() dirnode, as we reference dirnode->dirname! */
953 AddTail(&FreeDirNodeList, (struct Node *)dirnode);
955 } while (dirnode);
957 while ((dirnode = (struct DirNode *)RemHead(&FreeDirNodeList)))
959 FreeVec(dirnode->dirname);
960 FreeMem(dirnode, sizeof(struct DirNode));
963 return error;
967 int __nocommandline;
969 int main(void)
971 IPTR args[NOOFARGS] =
973 (IPTR) NULL, // ARG_DIR
974 (IPTR) NULL, // ARG_PAT
975 FALSE, // ARG_KEYS
976 FALSE, // ARG_DATES
977 FALSE, // ARG_NODATES
978 (IPTR) NULL, // ARG_TO
979 (IPTR) NULL, // ARG_SUB
980 (IPTR) NULL, // ARG_SINCE
981 (IPTR) NULL, // ARG_UPTO
982 FALSE, // ARG_QUICK
983 FALSE, // ARG_BLOCK
984 FALSE, // ARG_NOHEAD
985 FALSE, // ARG_FILES
986 FALSE, // ARG_DIRS
987 FALSE, // ARG_LFORMAT
988 FALSE // ARG_ALL
990 static CONST_STRPTR default_directories[] = {(CONST_STRPTR)"", 0};
991 struct RDArgs *rda;
993 LONG result = RETURN_OK;
994 LONG error = 0;
995 STRPTR parsedPattern = NULL;
996 STRPTR subpatternStr = NULL;
997 BPTR oldOutput = BNULL;
999 Statistics stats = { 0, 0, 0 };
1001 rda = ReadArgs(ARG_TEMPLATE, args, NULL);
1003 if (rda != NULL)
1005 CONST_STRPTR *directories = (CONST_STRPTR *)args[ARG_DIR];
1006 STRPTR lFormat = (STRPTR)args[ARG_LFORMAT];
1007 STRPTR pattern = (STRPTR)args[ARG_PAT];
1008 STRPTR toFile = (STRPTR)args[ARG_TO];
1009 STRPTR subStr = (STRPTR)args[ARG_SUB];
1010 STRPTR since = (STRPTR)args[ARG_SINCE];
1011 STRPTR upto = (STRPTR)args[ARG_UPTO];
1012 BOOL files = (BOOL)args[ARG_FILES];
1013 BOOL dirs = (BOOL)args[ARG_DIRS];
1014 BOOL noDates = (BOOL)args[ARG_NODATES];
1015 BOOL dates = (BOOL)args[ARG_DATES];
1016 BOOL quick = (BOOL)args[ARG_QUICK];
1017 BOOL noHead = (BOOL)args[ARG_NOHEAD];
1018 BOOL block = (BOOL)args[ARG_BLOCK];
1019 BOOL all = (BOOL)args[ARG_ALL];
1020 BOOL keys = (BOOL)args[ARG_KEYS];
1022 struct DateTime sinceDatetime;
1023 struct DateTime uptoDatetime;
1025 ULONG i; /* Loop variable */
1027 if (since != NULL)
1029 sinceDatetime.dat_StrDate = since;
1030 sinceDatetime.dat_StrTime = NULL;
1031 sinceDatetime.dat_Format = FORMAT_DEF;
1032 sinceDatetime.dat_Flags = 0;
1033 if (StrToDate(&sinceDatetime) == DOSFALSE)
1035 FreeArgs(rda);
1036 Printf("*** Illegal 'SINCE' parameter\n");
1038 return RETURN_FAIL;
1040 sinceDatetime.dat_Stamp.ds_Minute = 0;
1041 sinceDatetime.dat_Stamp.ds_Tick = 0;
1044 if (upto != NULL)
1046 uptoDatetime.dat_StrDate = upto;
1047 uptoDatetime.dat_StrTime = NULL;
1048 uptoDatetime.dat_Format = FORMAT_DEF;
1049 uptoDatetime.dat_Flags = 0;
1051 if (StrToDate(&uptoDatetime) == DOSFALSE)
1053 FreeArgs(rda);
1054 Printf("*** Illegal 'UPTO' parameter\n");
1056 return RETURN_FAIL;
1058 uptoDatetime.dat_Stamp.ds_Minute = 1439;
1059 uptoDatetime.dat_Stamp.ds_Tick = 2999;
1062 if (subStr != NULL)
1064 STRPTR subStrWithPat;
1065 ULONG length = (strlen(subStr) + sizeof("#?#?"))*2 + 2;
1067 subStrWithPat = AllocVec(length, MEMF_ANY);
1069 if (subStrWithPat == NULL)
1071 error = IoErr();
1072 FreeArgs(rda);
1073 PrintFault(error, "List");
1075 return RETURN_FAIL;
1078 strcpy(subStrWithPat, "#?");
1079 strcat(subStrWithPat, subStr);
1080 strcat(subStrWithPat, "#?");
1082 subpatternStr = AllocVec(length, MEMF_ANY);
1084 if (subpatternStr == NULL ||
1085 ParsePatternNoCase(subStrWithPat, subpatternStr, length) == -1)
1087 error = IoErr();
1088 FreeVec(subStrWithPat);
1089 FreeArgs(rda);
1090 PrintFault(error, "List");
1092 return RETURN_FAIL;
1095 FreeVec(subStrWithPat);
1099 if (pattern != NULL)
1101 ULONG length = strlen(pattern)*2 + 2;
1103 parsedPattern = AllocVec(length, MEMF_ANY);
1105 if (parsedPattern == NULL ||
1106 ParsePatternNoCase(pattern, parsedPattern, length) == -1)
1108 FreeVec(subpatternStr);
1109 FreeArgs(rda);
1111 return RETURN_FAIL;
1115 if (toFile != NULL)
1117 BPTR file = Open(toFile, MODE_NEWFILE);
1119 if (file == BNULL)
1121 error = IoErr();
1122 FreeVec(subpatternStr);
1123 FreeVec(parsedPattern);
1124 FreeArgs(rda);
1125 PrintFault(error, "List");
1127 return RETURN_FAIL;
1129 oldOutput = SelectOutput(file);
1132 if (!files && !dirs)
1134 files = TRUE;
1135 dirs = TRUE;
1138 /* if (!dates && !noDates)
1140 dates = TRUE;
1143 if (lFormat)
1145 noHead = TRUE;
1148 if ((directories == NULL) || (*directories == NULL))
1150 directories = default_directories;
1153 for (i = 0; directories[i] != NULL; i++)
1155 error = listFile(directories[i], files, dirs, parsedPattern,
1156 noHead, lFormat, quick, dates, noDates,
1157 block, &sinceDatetime.dat_Stamp,
1158 &uptoDatetime.dat_Stamp, since != NULL,
1159 upto != NULL, subpatternStr, all, keys,
1160 &stats);
1162 if (error != 0)
1164 break;
1167 // Printf("\n");
1170 FreeArgs(rda);
1172 else
1174 error = IoErr();
1177 if ((BOOL)args[ARG_NOHEAD] == FALSE &&
1178 (BOOL)args[ARG_LFORMAT] == FALSE &&
1179 (BOOL)args[ARG_ALL] &&
1180 (stats.nFiles || stats.nDirs))
1182 Printf("\nTOTAL: %ld files - %ld directories - %ld blocks used\n",
1183 stats.nFiles, stats.nDirs, stats.nBlocks);
1187 if (error != 0)
1189 if (error == ERROR_BREAK)
1191 result = RETURN_WARN;
1193 else
1195 PrintFault(error, "List");
1196 result = RETURN_FAIL;
1200 if (parsedPattern != NULL)
1202 FreeVec(parsedPattern);
1205 if (subpatternStr != NULL)
1207 FreeVec(subpatternStr);
1210 if (oldOutput != BNULL)
1212 Close(SelectOutput(oldOutput));
1215 return result;