2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
5 List the contents of a directory.
7 /*****************************************************************************
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]
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
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:
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 -- Display the creation date of files and directories
47 NODATES -- Don't display dates
48 TO (name) -- Write the listing to a file instead of stdout
49 SUB (string) -- Display only files, a substring of which matches
50 the substring 'string'
51 SINCE (date) -- Display only files newer than 'date'
52 UPTO (date) -- Display only files older than 'date'
53 QUICK -- Display only the names of files
54 BLOCK -- File sizes are in blocks of 512 bytes
55 NOHEAD -- Don't print any header information
56 FILES -- Display files only
57 DIRS -- Display directories only
58 LFORMAT -- Specify the list output in printf-style
59 ALL -- List the contents of directories recursively
61 The following attributes of the LFORMAT strings are available
64 %B -- size of file in blocks rather than bytes
69 %K -- file key block number
70 %L -- size of file in bytes
71 %M -- file name without extension
74 %S -- superceded by %N and %P; obsolete
79 Standard DOS return codes.
84 Directory "C:" on Wednesday 12-Dec-99
85 AddBuffers 444 --p-rwed 02-Sep-99 11:51:31
86 Assign 3220 --p-rwed 02-Sep-99 11:51:31
87 Avail 728 --p-rwed 02-Sep-99 11:51:31
88 Copy 3652 --p-rwed 02-Sep-99 11:51:31
89 Delete 1972 --p-rwed 02-Sep-99 11:51:31
90 Execute 4432 --p-rwed 02-Sep-99 11:51:31
91 List 5108 --p-rwed 02-Sep-99 11:51:31
92 Installer 109956 ----rwed 02-Sep-99 11:51:31
93 Which 1068 --p-rwed 02-Sep-99 11:51:31
94 9 files - 274 blocks used
104 ******************************************************************************/
107 #include <aros/debug.h>
109 #include <clib/macros.h>
110 #include <exec/memory.h>
111 #include <proto/exec.h>
112 #include <dos/datetime.h>
114 #include <dos/exall.h>
115 #include <dos/dosasl.h>
116 #include <dos/datetime.h>
117 #include <proto/dos.h>
118 #include <proto/alib.h>
119 #include <proto/utility.h>
120 #include <utility/tagitem.h>
122 const TEXT version
[] = "$VER: List 41.8 (05.11.2011)\n";
124 #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"
133 typedef struct _Statistics
162 #define MAX_PATH_LEN 1024
164 #define BLOCKSIZE 512
166 int printDirHeader(STRPTR dirname
, BOOL noHead
)
170 char datestr
[LEN_DATSTRING
];
171 char dow
[LEN_DATSTRING
];
175 DateStamp((struct DateStamp
*)&dt
);
176 dt
.dat_Format
= FORMAT_DEF
;
179 dt
.dat_StrDate
= datestr
;
180 dt
.dat_StrTime
= NULL
;
183 Printf("Directory \"%s\" on %s %s:\n", dirname
, dow
, datestr
);
190 /* Possible printf-type switches
192 %A -- file attributes
193 %B -- size of file in blocks rather than bytes
198 %K -- file key block number
199 %L -- size of file in bytes
200 %M -- file name without extension
203 %S -- file name or file path
209 struct AnchorPath
*ap
;
221 #define roundUp(x, bSize) ((x + bSize - 1)/bSize)
223 int printLformat(STRPTR format
, struct lfstruct
*lf
)
225 STRPTR filename
= FilePart(lf
->filename
);
226 STRPTR temp
= format
;
227 LONG substitutePath
= 0;
231 Whether the path or the filename is substituted for an occurrence
232 of %S depends on how many occurrences are in the LFORMAT line, and
233 their order, as follows:
235 Occurrences of %S 1st 2nd 3rd 4th
238 3 path filename filename
239 4 path filename path filename
242 while ( ( substitutePath
< 4 ) && ( '\0' != (c
= *temp
++) ) )
245 if ( 'S' == ToUpper(*temp
++) )
248 if ( substitutePath
== 3 )
251 while ('\0' != (c
= *format
++))
255 switch (ToUpper(*format
++))
272 /* File size in blocks of BLOCKSIZE bytes */
280 ULONG tmp
= roundUp(lf
->size
, BLOCKSIZE
);
282 /* File is 0 bytes? */
295 /* Path incl. volume name*/
300 if (NameFromLock(lf
->ap
->ap_Current
->an_Lock
, buf
, 256))
307 if ((len
> 0) && (buf
[len
- 1] != ':') && (buf
[len
- 1] != '/'))
316 /* File attributes (flags) */
323 Printf("[%ld]", lf
->key
);
340 UQUAD filesize
= lf
->size
;
345 char *bufpos
= &buf
[254];
349 bufpos
[0] = '0' + (filesize
% 10);
354 Printf("%s", &bufpos
[1]);
360 /* File name without extension */
363 STRPTR lastPoint
= strrchr(filename
, '.');
365 if (lastPoint
!= NULL
)
372 /* Resurrect filename in case we should print it once
374 if (lastPoint
!= NULL
)
382 /* Filename or Path name */
384 D(bug("[List] substitutePath = %d\n", substitutePath
));
385 if ( (--substitutePath
== 3) || (substitutePath
== 1) )
387 STRPTR end
= FilePart(lf
->filename
);
392 Printf(lf
->filename
);
394 /* Restore pathname */
407 STRPTR extension
= strrchr(filename
, '.');
409 if (extension
!= NULL
)
417 /* Path name, but without volume */
420 STRPTR end
= FilePart(lf
->filename
);
425 Printf(lf
->filename
);
427 /* Restore pathname */
438 Printf("%%%lc", *format
);
452 int printFileData(struct AnchorPath
*ap
,
453 BOOL showFiles
, BOOL showDirs
, STRPTR parsedPattern
,
454 ULONG
*files
, ULONG
*dirs
, ULONG
*nBlocks
, STRPTR lFormat
,
455 BOOL quick
, BOOL dates
, BOOL noDates
, BOOL block
,
456 struct DateStamp
*sinceDate
, struct DateStamp
*uptoDate
,
457 BOOL doSince
, BOOL doUpto
, STRPTR subpatternStr
,
460 STRPTR filename
= ap
->ap_Buf
;
461 BOOL isDir
= (ap
->ap_Info
.fib_DirEntryType
>= 0);
462 struct DateStamp
*ds
= &ap
->ap_Info
.fib_Date
;
463 ULONG protection
= ap
->ap_Info
.fib_Protection
;
464 UQUAD size
= ap
->ap_Info
.fib_Size
;
465 STRPTR filenote
= ap
->ap_Info
.fib_Comment
;
466 LONG diskKey
= ap
->ap_Info
.fib_DiskKey
;
470 UBYTE date
[LEN_DATSTRING
];
471 UBYTE time
[LEN_DATSTRING
];
476 #if defined(ACTION_GET_FILE_SIZE64)
477 if (ap
->ap_Info
.fib_Size
>= 0x7FFFFFFF)
480 flock
= Lock(filename
, ACCESS_READ
);
484 UQUAD
*size_ptr
= (UQUAD
*)DoPkt(((struct FileLock
*)flock
)->fl_Task
, ACTION_GET_FILE_SIZE64
, (IPTR
)flock
, 0, 0, 0, 0);
493 /* Do the file match the time interval we are looking for?
494 (ARG_SINCE and ARG_UPTO) -- any combination of these may be
496 if ((doSince
&& (CompareDates(sinceDate
, ds
) < 0)) ||
497 (doUpto
&& (CompareDates(uptoDate
, ds
) > 0)))
502 /* Does the filename match a certain pattern? (ARG_PAT) */
503 if (parsedPattern
!= NULL
&&
504 !MatchPatternNoCase(parsedPattern
, FilePart(filename
)))
509 /* Does a substring of the filename match a certain pattern? (ARG_SUB) */
510 if (subpatternStr
!= NULL
&&
511 !MatchPatternNoCase(subpatternStr
, FilePart(filename
)))
516 CopyMem(ds
, &dt
.dat_Stamp
, sizeof(struct DateStamp
));
517 dt
.dat_Format
= FORMAT_DOS
;
518 dt
.dat_Flags
= DTF_SUBST
;
519 dt
.dat_StrDay
= NULL
;
520 dt
.dat_StrDate
= date
;
521 dt
.dat_StrTime
= time
;
522 DateToStr(&dt
); /* returns 0 if invalid */
524 /* Convert the protection bits to a string */
525 flags
[0] = protection
& FIBF_SCRIPT
? 's' : '-';
526 flags
[1] = protection
& FIBF_PURE
? 'p' : '-';
527 flags
[2] = protection
& FIBF_ARCHIVE
? 'a' : '-';
529 /* The following flags are high-active! */
530 flags
[3] = protection
& FIBF_READ
? '-' : 'r';
531 flags
[4] = protection
& FIBF_WRITE
? '-' : 'w';
532 flags
[5] = protection
& FIBF_EXECUTE
? '-' : 'e';
533 flags
[6] = protection
& FIBF_DELETE
? '-' : 'd';
541 ++*nBlocks
; /* dir entry uses 1 block on AROS, 2 on OS31) */
545 struct lfstruct lf
= { ap
, isDir
, date
, time
, flags
, filename
,
546 filenote
, size
, diskKey
};
548 printLformat(lFormat
, &lf
);
553 D(bug("Found file %s\n", filename
));
555 Printf("%-25s ", FilePart(filename
));
559 Printf(" <Dir> %7s ", flags
);
562 if (!noDates
&& (!quick
|| dates
))
564 Printf("%-11s %s", date
, time
);
574 *nBlocks
+= roundUp(size
, BLOCKSIZE
);
578 struct lfstruct lf
= { ap
, isDir
, date
, time
, flags
, filename
,
579 filenote
, size
, diskKey
};
581 printLformat(lFormat
, &lf
);
586 Printf("%-25s ", FilePart(filename
));
594 int i
; /* Loop variable */
596 __sprintf(key
, "%ld", (long)diskKey
);
597 fill
= 7 - strlen(key
) - 2;
599 for (i
= 0; i
< fill
; i
++)
604 Printf("[%ld] ", diskKey
);
610 UQUAD filesize
= block
? roundUp(size
, BLOCKSIZE
) : size
;
615 char *bufpos
= &buf
[254];
619 bufpos
[0] = '0' + (filesize
% 10);
624 Printf("%s", &bufpos
[1]);
632 Printf("%7s ", flags
);
635 if (!noDates
&& (!quick
|| dates
))
637 Printf("%-11s %s", date
, time
);
640 if (!quick
&& (*filenote
!= 0))
642 Printf("\n: %s", filenote
);
653 /* Print directory summary information */
654 void printSummary(CONST_STRPTR dirname
, int files
, int dirs
, int nBlocks
,
655 BOOL noHead
, BOOL PrintEmpty
)
664 Printf("%ld files", files
);
668 if( files
&& (dirs
|| nBlocks
) ) PutStr(" - ");
671 Printf("%ld directories", dirs
);
673 PutStr("1 directory");
675 if( dirs
&& nBlocks
) PutStr(" - ");
678 Printf("%ld blocks used\n", nBlocks
);
679 else if (nBlocks
> 0)
680 PutStr("1 block used\n");
685 Printf("Directory \"%s\" is empty\n", dirname
);
689 int listFile(CONST_STRPTR filename
, BOOL showFiles
, BOOL showDirs
,
690 STRPTR parsedPattern
, BOOL noHead
, STRPTR lFormat
, BOOL quick
,
691 BOOL dates
, BOOL noDates
, BOOL block
, struct DateStamp
*sinceDate
,
692 struct DateStamp
*uptoDate
, BOOL doSince
, BOOL doUpto
,
693 STRPTR subpatternStr
, BOOL all
, BOOL keys
, Statistics
*stats
)
695 struct AnchorPath
*ap
;
696 struct List DirList
, FreeDirNodeList
;
697 struct DirNode
*dirnode
, *prev_dirnode
= NULL
;
705 NewList(&FreeDirNodeList
);
709 ap
= AllocVec(sizeof(struct AnchorPath
) + MAX_PATH_LEN
, MEMF_CLEAR
);
716 ap
->ap_Strlen
= MAX_PATH_LEN
;
718 error
= MatchFirst(filename
, ap
);
720 /* Explicitly named directory and not a pattern? --> enter dir */
724 if (!(ap
->ap_Flags
& APF_ITSWILD
))
726 if (ap
->ap_Info
.fib_DirEntryType
>= 0)
728 //error = printDirHeader(filename, noHead);
729 ap
->ap_Flags
|= APF_DODIR
;
733 error
= MatchNext(ap
);
743 ap
->ap_BreakBits
= SIGBREAKF_CTRL_C
;
744 if (FilePart(ap
->ap_Buf
) == ap
->ap_Buf
)
746 ap
->ap_Flags
&= ~APF_DirChanged
;
752 ** There's something to show.
754 if (!(ap
->ap_Flags
& APF_DIDDIR
))
756 if (ap
->ap_Flags
& APF_DirChanged
)
761 if (!first
) printSummary(filename
, files
, dirs
, nBlocks
, noHead
, TRUE
);
763 /* Update global statistics for (possible) ALL option */
764 stats
->nFiles
+= files
;
765 stats
->nDirs
+= dirs
;
766 stats
->nBlocks
+= nBlocks
;
772 p
= PathPart(ap
->ap_Buf
);
776 error
= printDirHeader(ap
->ap_Buf
, noHead
);
782 error
= printFileData(ap
,
801 if (all
&& (ap
->ap_Info
.fib_DirEntryType
>= 0))
803 if ((dirnode
= AllocMem(sizeof(struct DirNode
), MEMF_ANY
)))
805 if ((dirnode
->dirname
= StrDup(ap
->ap_Buf
)))
807 Insert(&DirList
, (struct Node
*)dirnode
,
808 (struct Node
*)prev_dirnode
);
810 prev_dirnode
= dirnode
;
814 FreeMem(dirnode
, sizeof(struct DirNode
));
820 error
= MatchNext(ap
);
824 } while (0 == error
);
831 if (error
== ERROR_BREAK
)
833 PrintFault(error
, NULL
);
836 if (error
== ERROR_NO_MORE_ENTRIES
)
841 if ((error
== 0) || (error
== ERROR_BREAK
))
843 BOOL printEmpty
= !(ap
->ap_Flags
& APF_ITSWILD
);
844 printSummary(filename
, files
, dirs
, nBlocks
, noHead
, printEmpty
);
847 /* Update global statistics for (possible) ALL option */
848 stats
->nFiles
+= files
;
849 stats
->nDirs
+= dirs
;
850 stats
->nBlocks
+= nBlocks
;
859 dirnode
= (struct DirNode
*)RemHead(&DirList
);
863 filename
= dirnode
->dirname
;
867 /* do not free() dirnode, as we reference dirnode->dirname! */
869 AddTail(&FreeDirNodeList
, (struct Node
*)dirnode
);
873 while ((dirnode
= (struct DirNode
*)RemHead(&FreeDirNodeList
)))
875 FreeVec(dirnode
->dirname
);
876 FreeMem(dirnode
, sizeof(struct DirNode
));
887 IPTR args
[NOOFARGS
] =
889 (IPTR
) NULL
, // ARG_DIR
890 (IPTR
) NULL
, // ARG_PAT
893 FALSE
, // ARG_NODATES
894 (IPTR
) NULL
, // ARG_TO
895 (IPTR
) NULL
, // ARG_SUB
896 (IPTR
) NULL
, // ARG_SINCE
897 (IPTR
) NULL
, // ARG_UPTO
903 FALSE
, // ARG_LFORMAT
906 static CONST_STRPTR default_directories
[] = {(CONST_STRPTR
)"", 0};
909 LONG result
= RETURN_OK
;
911 STRPTR parsedPattern
= NULL
;
912 STRPTR subpatternStr
= NULL
;
913 BPTR oldOutput
= BNULL
;
915 Statistics stats
= { 0, 0, 0 };
917 rda
= ReadArgs(ARG_TEMPLATE
, args
, NULL
);
921 CONST_STRPTR
*directories
= (CONST_STRPTR
*)args
[ARG_DIR
];
922 STRPTR lFormat
= (STRPTR
)args
[ARG_LFORMAT
];
923 STRPTR pattern
= (STRPTR
)args
[ARG_PAT
];
924 STRPTR toFile
= (STRPTR
)args
[ARG_TO
];
925 STRPTR subStr
= (STRPTR
)args
[ARG_SUB
];
926 STRPTR since
= (STRPTR
)args
[ARG_SINCE
];
927 STRPTR upto
= (STRPTR
)args
[ARG_UPTO
];
928 BOOL files
= (BOOL
)args
[ARG_FILES
];
929 BOOL dirs
= (BOOL
)args
[ARG_DIRS
];
930 BOOL noDates
= (BOOL
)args
[ARG_NODATES
];
931 BOOL dates
= (BOOL
)args
[ARG_DATES
];
932 BOOL quick
= (BOOL
)args
[ARG_QUICK
];
933 BOOL noHead
= (BOOL
)args
[ARG_NOHEAD
];
934 BOOL block
= (BOOL
)args
[ARG_BLOCK
];
935 BOOL all
= (BOOL
)args
[ARG_ALL
];
936 BOOL keys
= (BOOL
)args
[ARG_KEYS
];
938 struct DateTime sinceDatetime
;
939 struct DateTime uptoDatetime
;
941 ULONG i
; /* Loop variable */
945 sinceDatetime
.dat_StrDate
= since
;
946 sinceDatetime
.dat_StrTime
= NULL
;
947 sinceDatetime
.dat_Format
= FORMAT_DEF
;
948 sinceDatetime
.dat_Flags
= 0;
949 if (StrToDate(&sinceDatetime
) == DOSFALSE
)
952 Printf("*** Illegal 'SINCE' parameter\n");
960 uptoDatetime
.dat_StrDate
= upto
;
961 uptoDatetime
.dat_StrTime
= NULL
;
962 uptoDatetime
.dat_Format
= FORMAT_DEF
;
963 uptoDatetime
.dat_Flags
= 0;
965 if (StrToDate(&uptoDatetime
) == DOSFALSE
)
968 Printf("*** Illegal 'UPTO' parameter\n");
976 STRPTR subStrWithPat
;
977 ULONG length
= (strlen(subStr
) + sizeof("#?#?"))*2 + 2;
979 subStrWithPat
= AllocVec(length
, MEMF_ANY
);
981 if (subStrWithPat
== NULL
)
984 PrintFault(IoErr(), "List");
989 strcpy(subStrWithPat
, "#?");
990 strcat(subStrWithPat
, subStr
);
991 strcat(subStrWithPat
, "#?");
993 subpatternStr
= AllocVec(length
, MEMF_ANY
);
995 if (subpatternStr
== NULL
||
996 ParsePatternNoCase(subStrWithPat
, subpatternStr
, length
) == -1)
998 FreeVec(subStrWithPat
);
1000 PrintFault(IoErr(), "List");
1005 FreeVec(subStrWithPat
);
1009 if (pattern
!= NULL
)
1011 ULONG length
= strlen(pattern
)*2 + 2;
1013 parsedPattern
= AllocVec(length
, MEMF_ANY
);
1015 if (parsedPattern
== NULL
||
1016 ParsePatternNoCase(pattern
, parsedPattern
, length
) == -1)
1018 FreeVec(subpatternStr
);
1027 BPTR file
= Open(toFile
, MODE_NEWFILE
);
1031 FreeVec(subpatternStr
);
1032 FreeVec(parsedPattern
);
1034 PrintFault(IoErr(), "List");
1038 oldOutput
= SelectOutput(file
);
1041 if (!files
&& !dirs
)
1047 /* if (!dates && !noDates)
1057 if ((directories
== NULL
) || (*directories
== NULL
))
1059 directories
= default_directories
;
1062 for (i
= 0; directories
[i
] != NULL
; i
++)
1064 error
= listFile(directories
[i
], files
, dirs
, parsedPattern
,
1065 noHead
, lFormat
, quick
, dates
, noDates
,
1066 block
, &sinceDatetime
.dat_Stamp
,
1067 &uptoDatetime
.dat_Stamp
, since
!= NULL
,
1068 upto
!= NULL
, subpatternStr
, all
, keys
,
1086 if ((BOOL
)args
[ARG_NOHEAD
] == FALSE
&&
1087 (BOOL
)args
[ARG_LFORMAT
] == FALSE
&&
1088 (BOOL
)args
[ARG_ALL
] &&
1089 (stats
.nFiles
|| stats
.nDirs
))
1091 Printf("\nTOTAL: %ld files - %ld directories - %ld blocks used\n",
1092 stats
.nFiles
, stats
.nDirs
, stats
.nBlocks
);
1098 if (error
== ERROR_BREAK
)
1100 result
= RETURN_WARN
;
1104 PrintFault(error
, "List");
1105 result
= RETURN_FAIL
;
1109 if (parsedPattern
!= NULL
)
1111 FreeVec(parsedPattern
);
1114 if (subpatternStr
!= NULL
)
1116 FreeVec(subpatternStr
);
1119 if (oldOutput
!= BNULL
)
1121 Close(SelectOutput(oldOutput
));