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.7 (18.08.2010)\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 Printf("%lu", lf
->size
);
346 /* File name without extension */
349 STRPTR lastPoint
= strrchr(filename
, '.');
351 if (lastPoint
!= NULL
)
358 /* Resurrect filename in case we should print it once
360 if (lastPoint
!= NULL
)
368 /* Filename or Path name */
370 D(bug("[List] substitutePath = %d\n", substitutePath
));
371 if ( (--substitutePath
== 3) || (substitutePath
== 1) )
373 STRPTR end
= FilePart(lf
->filename
);
378 Printf(lf
->filename
);
380 /* Restore pathname */
393 STRPTR extension
= strrchr(filename
, '.');
395 if (extension
!= NULL
)
403 /* Path name, but without volume */
406 STRPTR end
= FilePart(lf
->filename
);
411 Printf(lf
->filename
);
413 /* Restore pathname */
424 Printf("%%%lc", *format
);
438 int printFileData(struct AnchorPath
*ap
,
439 BOOL showFiles
, BOOL showDirs
, STRPTR parsedPattern
,
440 ULONG
*files
, ULONG
*dirs
, ULONG
*nBlocks
, STRPTR lFormat
,
441 BOOL quick
, BOOL dates
, BOOL noDates
, BOOL block
,
442 struct DateStamp
*sinceDate
, struct DateStamp
*uptoDate
,
443 BOOL doSince
, BOOL doUpto
, STRPTR subpatternStr
,
446 STRPTR filename
= ap
->ap_Buf
;
447 BOOL isDir
= (ap
->ap_Info
.fib_DirEntryType
>= 0);
448 struct DateStamp
*ds
= &ap
->ap_Info
.fib_Date
;
449 ULONG protection
= ap
->ap_Info
.fib_Protection
;
450 ULONG size
= ap
->ap_Info
.fib_Size
;
451 STRPTR filenote
= ap
->ap_Info
.fib_Comment
;
452 LONG diskKey
= ap
->ap_Info
.fib_DiskKey
;
456 UBYTE date
[LEN_DATSTRING
];
457 UBYTE time
[LEN_DATSTRING
];
462 /* Do the file match the time interval we are looking for?
463 (ARG_SINCE and ARG_UPTO) -- any combination of these may be
465 if ((doSince
&& (CompareDates(sinceDate
, ds
) < 0)) ||
466 (doUpto
&& (CompareDates(uptoDate
, ds
) > 0)))
471 /* Does the filename match a certain pattern? (ARG_PAT) */
472 if (parsedPattern
!= NULL
&&
473 !MatchPatternNoCase(parsedPattern
, FilePart(filename
)))
478 /* Does a substring of the filename match a certain pattern? (ARG_SUB) */
479 if (subpatternStr
!= NULL
&&
480 !MatchPatternNoCase(subpatternStr
, FilePart(filename
)))
485 CopyMem(ds
, &dt
.dat_Stamp
, sizeof(struct DateStamp
));
486 dt
.dat_Format
= FORMAT_DOS
;
487 dt
.dat_Flags
= DTF_SUBST
;
488 dt
.dat_StrDay
= NULL
;
489 dt
.dat_StrDate
= date
;
490 dt
.dat_StrTime
= time
;
491 DateToStr(&dt
); /* returns 0 if invalid */
493 /* Convert the protection bits to a string */
494 flags
[0] = protection
& FIBF_SCRIPT
? 's' : '-';
495 flags
[1] = protection
& FIBF_PURE
? 'p' : '-';
496 flags
[2] = protection
& FIBF_ARCHIVE
? 'a' : '-';
498 /* The following flags are high-active! */
499 flags
[3] = protection
& FIBF_READ
? '-' : 'r';
500 flags
[4] = protection
& FIBF_WRITE
? '-' : 'w';
501 flags
[5] = protection
& FIBF_EXECUTE
? '-' : 'e';
502 flags
[6] = protection
& FIBF_DELETE
? '-' : 'd';
510 ++*nBlocks
; /* dir entry uses 1 block on AROS, 2 on OS31) */
514 struct lfstruct lf
= { ap
, isDir
, date
, time
, flags
, filename
,
515 filenote
, size
, diskKey
};
517 printLformat(lFormat
, &lf
);
522 D(bug("Found file %s\n", filename
));
524 Printf("%-25s ", FilePart(filename
));
528 Printf(" <Dir> %7s ", flags
);
531 if (!noDates
&& (!quick
|| dates
))
533 Printf("%-11s %s", date
, time
);
543 *nBlocks
+= roundUp(size
, BLOCKSIZE
);
547 struct lfstruct lf
= { ap
, isDir
, date
, time
, flags
, filename
,
548 filenote
, size
, diskKey
};
550 printLformat(lFormat
, &lf
);
555 Printf("%-25s ", FilePart(filename
));
563 int i
; /* Loop variable */
565 __sprintf(key
, "%ld", (long)diskKey
);
566 fill
= 7 - strlen(key
) - 2;
568 for (i
= 0; i
< fill
; i
++)
573 Printf("[%ld] ", diskKey
);
580 block
? roundUp(size
, BLOCKSIZE
) : size
);
588 Printf("%7s ", flags
);
591 if (!noDates
&& (!quick
|| dates
))
593 Printf("%-11s %s", date
, time
);
596 if (!quick
&& (*filenote
!= 0))
598 Printf("\n: %s", filenote
);
609 /* Print directory summary information */
610 void printSummary(CONST_STRPTR dirname
, int files
, int dirs
, int nBlocks
,
611 BOOL noHead
, BOOL PrintEmpty
)
619 Printf("%ld files - ", files
);
624 Printf("%ld directories - ", dirs
);
626 PutStr("1 directory - ");
629 Printf("%ld blocks used\n", nBlocks
);
630 else if (nBlocks
> 0)
631 PutStr("1 block used\n");
634 Printf("Directory \"%s\" is empty\n", dirname
);
638 int listFile(CONST_STRPTR filename
, BOOL showFiles
, BOOL showDirs
,
639 STRPTR parsedPattern
, BOOL noHead
, STRPTR lFormat
, BOOL quick
,
640 BOOL dates
, BOOL noDates
, BOOL block
, struct DateStamp
*sinceDate
,
641 struct DateStamp
*uptoDate
, BOOL doSince
, BOOL doUpto
,
642 STRPTR subpatternStr
, BOOL all
, BOOL keys
, Statistics
*stats
)
644 struct AnchorPath
*ap
;
645 struct List DirList
, FreeDirNodeList
;
646 struct DirNode
*dirnode
, *prev_dirnode
= NULL
;
654 NewList(&FreeDirNodeList
);
658 ap
= AllocVec(sizeof(struct AnchorPath
) + MAX_PATH_LEN
, MEMF_CLEAR
);
665 ap
->ap_Strlen
= MAX_PATH_LEN
;
667 error
= MatchFirst(filename
, ap
);
669 /* Explicitly named directory and not a pattern? --> enter dir */
673 if (!(ap
->ap_Flags
& APF_ITSWILD
))
675 if (ap
->ap_Info
.fib_DirEntryType
>= 0)
677 //error = printDirHeader(filename, noHead);
678 ap
->ap_Flags
|= APF_DODIR
;
682 error
= MatchNext(ap
);
692 ap
->ap_BreakBits
= SIGBREAKF_CTRL_C
;
693 if (FilePart(ap
->ap_Buf
) == ap
->ap_Buf
)
695 ap
->ap_Flags
&= ~APF_DirChanged
;
701 ** There's something to show.
703 if (!(ap
->ap_Flags
& APF_DIDDIR
))
705 if (ap
->ap_Flags
& APF_DirChanged
)
710 if (!first
) printSummary(filename
, files
, dirs
, nBlocks
, noHead
, TRUE
);
712 /* Update global statistics for (possible) ALL option */
713 stats
->nFiles
+= files
;
714 stats
->nDirs
+= dirs
;
715 stats
->nBlocks
+= nBlocks
;
721 p
= PathPart(ap
->ap_Buf
);
725 error
= printDirHeader(ap
->ap_Buf
, noHead
);
731 error
= printFileData(ap
,
750 if (all
&& (ap
->ap_Info
.fib_DirEntryType
>= 0))
752 if ((dirnode
= AllocMem(sizeof(struct DirNode
), MEMF_ANY
)))
754 if ((dirnode
->dirname
= StrDup(ap
->ap_Buf
)))
756 Insert(&DirList
, (struct Node
*)dirnode
,
757 (struct Node
*)prev_dirnode
);
759 prev_dirnode
= dirnode
;
763 FreeMem(dirnode
, sizeof(struct DirNode
));
769 error
= MatchNext(ap
);
773 } while (0 == error
);
780 if (error
== ERROR_BREAK
)
782 PrintFault(error
, NULL
);
785 if (error
== ERROR_NO_MORE_ENTRIES
)
790 if ((error
== 0) || (error
== ERROR_BREAK
))
792 BOOL printEmpty
= !(ap
->ap_Flags
& APF_ITSWILD
);
793 printSummary(filename
, files
, dirs
, nBlocks
, noHead
, printEmpty
);
796 /* Update global statistics for (possible) ALL option */
797 stats
->nFiles
+= files
;
798 stats
->nDirs
+= dirs
;
799 stats
->nBlocks
+= nBlocks
;
808 dirnode
= (struct DirNode
*)RemHead(&DirList
);
812 filename
= dirnode
->dirname
;
816 /* do not free() dirnode, as we reference dirnode->dirname! */
818 AddTail(&FreeDirNodeList
, (struct Node
*)dirnode
);
822 while ((dirnode
= (struct DirNode
*)RemHead(&FreeDirNodeList
)))
824 FreeVec(dirnode
->dirname
);
825 FreeMem(dirnode
, sizeof(struct DirNode
));
836 IPTR args
[NOOFARGS
] =
838 (IPTR
) NULL
, // ARG_DIR
839 (IPTR
) NULL
, // ARG_PAT
842 FALSE
, // ARG_NODATES
843 (IPTR
) NULL
, // ARG_TO
844 (IPTR
) NULL
, // ARG_SUB
845 (IPTR
) NULL
, // ARG_SINCE
846 (IPTR
) NULL
, // ARG_UPTO
852 FALSE
, // ARG_LFORMAT
855 static CONST_STRPTR default_directories
[] = {(CONST_STRPTR
)"", 0};
858 LONG result
= RETURN_OK
;
860 STRPTR parsedPattern
= NULL
;
861 STRPTR subpatternStr
= NULL
;
862 BPTR oldOutput
= BNULL
;
864 Statistics stats
= { 0, 0, 0 };
866 rda
= ReadArgs(ARG_TEMPLATE
, args
, NULL
);
870 CONST_STRPTR
*directories
= (CONST_STRPTR
*)args
[ARG_DIR
];
871 STRPTR lFormat
= (STRPTR
)args
[ARG_LFORMAT
];
872 STRPTR pattern
= (STRPTR
)args
[ARG_PAT
];
873 STRPTR toFile
= (STRPTR
)args
[ARG_TO
];
874 STRPTR subStr
= (STRPTR
)args
[ARG_SUB
];
875 STRPTR since
= (STRPTR
)args
[ARG_SINCE
];
876 STRPTR upto
= (STRPTR
)args
[ARG_UPTO
];
877 BOOL files
= (BOOL
)args
[ARG_FILES
];
878 BOOL dirs
= (BOOL
)args
[ARG_DIRS
];
879 BOOL noDates
= (BOOL
)args
[ARG_NODATES
];
880 BOOL dates
= (BOOL
)args
[ARG_DATES
];
881 BOOL quick
= (BOOL
)args
[ARG_QUICK
];
882 BOOL noHead
= (BOOL
)args
[ARG_NOHEAD
];
883 BOOL block
= (BOOL
)args
[ARG_BLOCK
];
884 BOOL all
= (BOOL
)args
[ARG_ALL
];
885 BOOL keys
= (BOOL
)args
[ARG_KEYS
];
887 struct DateTime sinceDatetime
;
888 struct DateTime uptoDatetime
;
890 ULONG i
; /* Loop variable */
894 sinceDatetime
.dat_StrDate
= since
;
895 sinceDatetime
.dat_StrTime
= NULL
;
896 sinceDatetime
.dat_Format
= FORMAT_DEF
;
897 sinceDatetime
.dat_Flags
= 0;
898 if (StrToDate(&sinceDatetime
) == DOSFALSE
)
901 Printf("*** Illegal 'SINCE' parameter\n");
909 uptoDatetime
.dat_StrDate
= upto
;
910 uptoDatetime
.dat_StrTime
= NULL
;
911 uptoDatetime
.dat_Format
= FORMAT_DEF
;
912 uptoDatetime
.dat_Flags
= 0;
914 if (StrToDate(&uptoDatetime
) == DOSFALSE
)
917 Printf("*** Illegal 'UPTO' parameter\n");
925 STRPTR subStrWithPat
;
926 ULONG length
= (strlen(subStr
) + sizeof("#?#?"))*2 + 2;
928 subStrWithPat
= AllocVec(length
, MEMF_ANY
);
930 if (subStrWithPat
== NULL
)
933 PrintFault(IoErr(), "List");
938 strcpy(subStrWithPat
, "#?");
939 strcat(subStrWithPat
, subStr
);
940 strcat(subStrWithPat
, "#?");
942 subpatternStr
= AllocVec(length
, MEMF_ANY
);
944 if (subpatternStr
== NULL
||
945 ParsePatternNoCase(subStrWithPat
, subpatternStr
, length
) == -1)
947 FreeVec(subStrWithPat
);
949 PrintFault(IoErr(), "List");
954 FreeVec(subStrWithPat
);
960 ULONG length
= strlen(pattern
)*2 + 2;
962 parsedPattern
= AllocVec(length
, MEMF_ANY
);
964 if (parsedPattern
== NULL
||
965 ParsePatternNoCase(pattern
, parsedPattern
, length
) == -1)
967 FreeVec(subpatternStr
);
976 BPTR file
= Open(toFile
, MODE_NEWFILE
);
980 FreeVec(subpatternStr
);
981 FreeVec(parsedPattern
);
983 PrintFault(IoErr(), "List");
987 oldOutput
= SelectOutput(file
);
996 /* if (!dates && !noDates)
1006 if ((directories
== NULL
) || (*directories
== NULL
))
1008 directories
= default_directories
;
1011 for (i
= 0; directories
[i
] != NULL
; i
++)
1013 error
= listFile(directories
[i
], files
, dirs
, parsedPattern
,
1014 noHead
, lFormat
, quick
, dates
, noDates
,
1015 block
, &sinceDatetime
.dat_Stamp
,
1016 &uptoDatetime
.dat_Stamp
, since
!= NULL
,
1017 upto
!= NULL
, subpatternStr
, all
, keys
,
1035 if ((BOOL
)args
[ARG_NOHEAD
] == FALSE
&&
1036 (BOOL
)args
[ARG_LFORMAT
] == FALSE
&&
1037 (BOOL
)args
[ARG_ALL
] &&
1038 (stats
.nFiles
|| stats
.nDirs
))
1040 Printf("\nTOTAL: %ld files - %ld directories - %ld blocks used\n",
1041 stats
.nFiles
, stats
.nDirs
, stats
.nBlocks
);
1047 if (error
== ERROR_BREAK
)
1049 result
= RETURN_WARN
;
1053 PrintFault(error
, "List");
1054 result
= RETURN_FAIL
;
1058 if (parsedPattern
!= NULL
)
1060 FreeVec(parsedPattern
);
1063 if (subpatternStr
!= NULL
)
1065 FreeVec(subpatternStr
);
1068 if (oldOutput
!= BNULL
)
1070 Close(SelectOutput(oldOutput
));