2 Copyright © 1995-2010, 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 <utility/tagitem.h>
127 const TEXT version
[] = "$VER: List 41.7 (18.08.2010)\n";
129 #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"
138 typedef struct _Statistics
167 #define MAX_PATH_LEN 1024
169 #define BLOCKSIZE 512
171 int printDirHeader(STRPTR dirname
, BOOL noHead
)
175 char datestr
[LEN_DATSTRING
];
176 char dow
[LEN_DATSTRING
];
180 DateStamp((struct DateStamp
*)&dt
);
181 dt
.dat_Format
= FORMAT_DEF
;
184 dt
.dat_StrDate
= datestr
;
185 dt
.dat_StrTime
= NULL
;
188 Printf("Directory \"%s\" on %s %s:\n", dirname
, dow
, datestr
);
195 /* Possible printf-type switches
197 %A -- file attributes
198 %B -- size of file in blocks rather than bytes
203 %K -- file key block number
204 %L -- size of file in bytes
205 %M -- file name without extension
208 %S -- file name or file path
214 struct AnchorPath
*ap
;
226 #define roundUp(x, bSize) ((x + bSize - 1)/bSize)
228 int printLformat(STRPTR format
, struct lfstruct
*lf
)
230 STRPTR filename
= FilePart(lf
->filename
);
231 STRPTR temp
= format
;
232 LONG substitutePath
= 0;
236 Whether the path or the filename is substituted for an occurrence
237 of %S depends on how many occurrences are in the LFORMAT line, and
238 their order, as follows:
240 Occurrences of %S 1st 2nd 3rd 4th
243 3 path filename filename
244 4 path filename path filename
247 while ( ( substitutePath
< 4 ) && ( '\0' != (c
= *temp
++) ) )
250 if ( 'S' == toupper(*temp
++) )
253 if ( substitutePath
== 3 )
256 while ('\0' != (c
= *format
++))
260 switch (toupper(*format
++))
277 /* File size in blocks of BLOCKSIZE bytes */
285 ULONG tmp
= roundUp(lf
->size
, BLOCKSIZE
);
287 /* File is 0 bytes? */
300 /* Path incl. volume name*/
305 if (NameFromLock(lf
->ap
->ap_Current
->an_Lock
, buf
, 256))
312 if ((len
> 0) && (buf
[len
- 1] != ':') && (buf
[len
- 1] != '/'))
321 /* File attributes (flags) */
328 Printf("[%ld]", lf
->key
);
345 Printf("%lu", lf
->size
);
351 /* File name without extension */
354 STRPTR lastPoint
= strrchr(filename
, '.');
356 if (lastPoint
!= NULL
)
363 /* Resurrect filename in case we should print it once
365 if (lastPoint
!= NULL
)
373 /* Filename or Path name */
375 D(bug("[List] substitutePath = %d\n", substitutePath
));
376 if ( (--substitutePath
== 3) || (substitutePath
== 1) )
378 STRPTR end
= FilePart(lf
->filename
);
383 Printf(lf
->filename
);
385 /* Restore pathname */
398 STRPTR extension
= strrchr(filename
, '.');
400 if (extension
!= NULL
)
408 /* Path name, but without volume */
411 STRPTR end
= FilePart(lf
->filename
);
416 Printf(lf
->filename
);
418 /* Restore pathname */
429 Printf("%%%lc", *format
);
443 int printFileData(struct AnchorPath
*ap
,
444 BOOL showFiles
, BOOL showDirs
, STRPTR parsedPattern
,
445 ULONG
*files
, ULONG
*dirs
, ULONG
*nBlocks
, STRPTR lFormat
,
446 BOOL quick
, BOOL dates
, BOOL noDates
, BOOL block
,
447 struct DateStamp
*sinceDate
, struct DateStamp
*uptoDate
,
448 BOOL doSince
, BOOL doUpto
, STRPTR subpatternStr
,
451 STRPTR filename
= ap
->ap_Buf
;
452 BOOL isDir
= (ap
->ap_Info
.fib_DirEntryType
>= 0);
453 struct DateStamp
*ds
= &ap
->ap_Info
.fib_Date
;
454 ULONG protection
= ap
->ap_Info
.fib_Protection
;
455 ULONG size
= ap
->ap_Info
.fib_Size
;
456 STRPTR filenote
= ap
->ap_Info
.fib_Comment
;
457 LONG diskKey
= ap
->ap_Info
.fib_DiskKey
;
459 int error
= RETURN_OK
;
461 UBYTE date
[LEN_DATSTRING
];
462 UBYTE time
[LEN_DATSTRING
];
467 /* Do the file match the time interval we are looking for?
468 (ARG_SINCE and ARG_UPTO) -- any combination of these may be
470 if ((doSince
&& (CompareDates(sinceDate
, ds
) < 0)) ||
471 (doUpto
&& (CompareDates(uptoDate
, ds
) > 0)))
476 /* Does the filename match a certain pattern? (ARG_PAT) */
477 if (parsedPattern
!= NULL
&&
478 !MatchPatternNoCase(parsedPattern
, FilePart(filename
)))
483 /* Does a substring of the filename match a certain pattern? (ARG_SUB) */
484 if (subpatternStr
!= NULL
&&
485 !MatchPatternNoCase(subpatternStr
, FilePart(filename
)))
490 CopyMem(ds
, &dt
.dat_Stamp
, sizeof(struct DateStamp
));
491 dt
.dat_Format
= FORMAT_DOS
;
492 dt
.dat_Flags
= DTF_SUBST
;
493 dt
.dat_StrDay
= NULL
;
494 dt
.dat_StrDate
= date
;
495 dt
.dat_StrTime
= time
;
496 DateToStr(&dt
); /* returns 0 if invalid */
498 /* Convert the protection bits to a string */
499 flags
[0] = protection
& FIBF_SCRIPT
? 's' : '-';
500 flags
[1] = protection
& FIBF_PURE
? 'p' : '-';
501 flags
[2] = protection
& FIBF_ARCHIVE
? 'a' : '-';
503 /* The following flags are high-active! */
504 flags
[3] = protection
& FIBF_READ
? '-' : 'r';
505 flags
[4] = protection
& FIBF_WRITE
? '-' : 'w';
506 flags
[5] = protection
& FIBF_EXECUTE
? '-' : 'e';
507 flags
[6] = protection
& FIBF_DELETE
? '-' : 'd';
515 ++*nBlocks
; /* dir entry uses 1 block on AROS, 2 on OS31) */
519 struct lfstruct lf
= { ap
, isDir
, date
, time
, flags
, filename
,
520 filenote
, size
, diskKey
};
522 printLformat(lFormat
, &lf
);
527 D(bug("Found file %s\n", filename
));
529 Printf("%-25s ", FilePart(filename
));
533 Printf(" <Dir> %7s ", flags
);
536 if (!noDates
&& (!quick
|| dates
))
538 Printf("%-11s %s", date
, time
);
548 *nBlocks
+= roundUp(size
, BLOCKSIZE
);
552 struct lfstruct lf
= { ap
, isDir
, date
, time
, flags
, filename
,
553 filenote
, size
, diskKey
};
555 printLformat(lFormat
, &lf
);
560 Printf("%-25s ", FilePart(filename
));
568 int i
; /* Loop variable */
570 sprintf(key
, "%ld", diskKey
);
571 fill
= 7 - strlen(key
) - 2;
573 for (i
= 0; i
< fill
; i
++)
578 Printf("[%ld] ", diskKey
);
585 block
? roundUp(size
, BLOCKSIZE
) : size
);
593 Printf("%7s ", flags
);
596 if (!noDates
&& (!quick
|| dates
))
598 Printf("%-11s %s", date
, time
);
601 if (!quick
&& (*filenote
!= 0))
603 Printf("\n: %s", filenote
);
614 /* Print directory summary information */
615 void printSummary(CONST_STRPTR dirname
, int files
, int dirs
, int nBlocks
,
616 BOOL noHead
, BOOL PrintEmpty
)
624 Printf("%ld files - ", files
);
629 Printf("%ld directories - ", dirs
);
631 PutStr("1 directory - ");
634 Printf("%ld blocks used\n", nBlocks
);
635 else if (nBlocks
> 0)
636 PutStr("1 block used\n");
639 Printf("Directory \"%s\" is empty\n", dirname
);
643 int listFile(CONST_STRPTR filename
, BOOL showFiles
, BOOL showDirs
,
644 STRPTR parsedPattern
, BOOL noHead
, STRPTR lFormat
, BOOL quick
,
645 BOOL dates
, BOOL noDates
, BOOL block
, struct DateStamp
*sinceDate
,
646 struct DateStamp
*uptoDate
, BOOL doSince
, BOOL doUpto
,
647 STRPTR subpatternStr
, BOOL all
, BOOL keys
, Statistics
*stats
)
649 struct AnchorPath
*ap
;
650 struct List DirList
, FreeDirNodeList
;
651 struct DirNode
*dirnode
, *prev_dirnode
= NULL
;
659 NewList(&FreeDirNodeList
);
663 ap
= AllocVec(sizeof(struct AnchorPath
) + MAX_PATH_LEN
, MEMF_CLEAR
);
670 ap
->ap_Strlen
= MAX_PATH_LEN
;
672 error
= MatchFirst(filename
, ap
);
674 /* Explicitely named directory and not a pattern? --> enter dir */
678 if (!(ap
->ap_Flags
& APF_ITSWILD
))
680 if (ap
->ap_Info
.fib_DirEntryType
>= 0)
682 //error = printDirHeader(filename, noHead);
683 ap
->ap_Flags
|= APF_DODIR
;
687 error
= MatchNext(ap
);
697 ap
->ap_BreakBits
= SIGBREAKF_CTRL_C
;
698 if (FilePart(ap
->ap_Buf
) == ap
->ap_Buf
)
700 ap
->ap_Flags
&= ~APF_DirChanged
;
706 ** There's something to show.
708 if (!(ap
->ap_Flags
& APF_DIDDIR
))
710 if (ap
->ap_Flags
& APF_DirChanged
)
715 if (!first
) printSummary(filename
, files
, dirs
, nBlocks
, noHead
, TRUE
);
717 /* Update global statistics for (possiblr) ALL option */
718 stats
->nFiles
+= files
;
719 stats
->nDirs
+= dirs
;
720 stats
->nBlocks
+= nBlocks
;
726 p
= PathPart(ap
->ap_Buf
);
730 error
= printDirHeader(ap
->ap_Buf
, noHead
);
736 error
= printFileData(ap
,
755 if (all
&& (ap
->ap_Info
.fib_DirEntryType
>= 0))
757 if ((dirnode
= malloc(sizeof(struct DirNode
))))
759 if ((dirnode
->dirname
= strdup(ap
->ap_Buf
)))
761 Insert(&DirList
, (struct Node
*)dirnode
,
762 (struct Node
*)prev_dirnode
);
764 prev_dirnode
= dirnode
;
774 error
= MatchNext(ap
);
778 } while (0 == error
);
785 if (error
== ERROR_BREAK
)
787 PrintFault(error
, NULL
);
790 if (error
== ERROR_NO_MORE_ENTRIES
)
795 if ((error
== 0) || (error
== ERROR_BREAK
))
797 BOOL printEmpty
= !(ap
->ap_Flags
& APF_ITSWILD
);
798 printSummary(filename
, files
, dirs
, nBlocks
, noHead
, printEmpty
);
801 /* Update global statistics for (possiblr) ALL option */
802 stats
->nFiles
+= files
;
803 stats
->nDirs
+= dirs
;
804 stats
->nBlocks
+= nBlocks
;
813 dirnode
= (struct DirNode
*)RemHead(&DirList
);
817 filename
= dirnode
->dirname
;
821 /* do not free() dirnode, as we reference dirnode->dirname! */
823 AddTail(&FreeDirNodeList
, (struct Node
*)dirnode
);
827 while ((dirnode
= (struct DirNode
*)RemHead(&FreeDirNodeList
)))
829 free(dirnode
->dirname
);
841 IPTR args
[NOOFARGS
] =
843 (IPTR
) NULL
, // ARG_DIR
844 (IPTR
) NULL
, // ARG_PAT
847 FALSE
, // ARG_NODATES
848 (IPTR
) NULL
, // ARG_TO
849 (IPTR
) NULL
, // ARG_SUB
850 (IPTR
) NULL
, // ARG_SINCE
851 (IPTR
) NULL
, // ARG_UPTO
857 FALSE
, // ARG_LFORMAT
860 static CONST_STRPTR default_directories
[] = {(CONST_STRPTR
)"", 0};
863 LONG error
= RETURN_OK
;
864 STRPTR parsedPattern
= NULL
;
865 STRPTR subpatternStr
= NULL
;
866 BPTR oldOutput
= BNULL
;
868 Statistics stats
= { 0, 0, 0 };
870 rda
= ReadArgs(ARG_TEMPLATE
, args
, NULL
);
874 CONST_STRPTR
*directories
= (CONST_STRPTR
*)args
[ARG_DIR
];
875 STRPTR lFormat
= (STRPTR
)args
[ARG_LFORMAT
];
876 STRPTR pattern
= (STRPTR
)args
[ARG_PAT
];
877 STRPTR toFile
= (STRPTR
)args
[ARG_TO
];
878 STRPTR subStr
= (STRPTR
)args
[ARG_SUB
];
879 STRPTR since
= (STRPTR
)args
[ARG_SINCE
];
880 STRPTR upto
= (STRPTR
)args
[ARG_UPTO
];
881 BOOL files
= (BOOL
)args
[ARG_FILES
];
882 BOOL dirs
= (BOOL
)args
[ARG_DIRS
];
883 BOOL noDates
= (BOOL
)args
[ARG_NODATES
];
884 BOOL dates
= (BOOL
)args
[ARG_DATES
];
885 BOOL quick
= (BOOL
)args
[ARG_QUICK
];
886 BOOL noHead
= (BOOL
)args
[ARG_NOHEAD
];
887 BOOL block
= (BOOL
)args
[ARG_BLOCK
];
888 BOOL all
= (BOOL
)args
[ARG_ALL
];
889 BOOL keys
= (BOOL
)args
[ARG_KEYS
];
891 struct DateTime sinceDatetime
;
892 struct DateTime uptoDatetime
;
894 ULONG i
; /* Loop variable */
898 sinceDatetime
.dat_StrDate
= since
;
899 sinceDatetime
.dat_StrTime
= NULL
;
900 sinceDatetime
.dat_Format
= FORMAT_DEF
;
901 sinceDatetime
.dat_Flags
= 0;
902 if (StrToDate(&sinceDatetime
) == DOSFALSE
)
905 Printf("*** Illegal 'SINCE' parameter\n");
913 uptoDatetime
.dat_StrDate
= upto
;
914 uptoDatetime
.dat_StrTime
= NULL
;
915 uptoDatetime
.dat_Format
= FORMAT_DEF
;
916 uptoDatetime
.dat_Flags
= 0;
918 if (StrToDate(&uptoDatetime
) == DOSFALSE
)
921 Printf("*** Illegal 'UPTO' parameter\n");
929 STRPTR subStrWithPat
;
930 ULONG length
= (strlen(subStr
) + sizeof("#?#?"))*2 + 2;
932 subStrWithPat
= AllocVec(length
, MEMF_ANY
);
934 if (subStrWithPat
== NULL
)
937 PrintFault(IoErr(), "List");
942 strcpy(subStrWithPat
, "#?");
943 strcat(subStrWithPat
, subStr
);
944 strcat(subStrWithPat
, "#?");
946 subpatternStr
= AllocVec(length
, MEMF_ANY
);
948 if (subpatternStr
== NULL
||
949 ParsePatternNoCase(subStrWithPat
, subpatternStr
, length
) == -1)
951 FreeVec(subStrWithPat
);
953 PrintFault(IoErr(), "List");
958 FreeVec(subStrWithPat
);
964 ULONG length
= strlen(pattern
)*2 + 2;
966 parsedPattern
= AllocVec(length
, MEMF_ANY
);
968 if (parsedPattern
== NULL
||
969 ParsePatternNoCase(pattern
, parsedPattern
, length
) == -1)
971 FreeVec(subpatternStr
);
980 BPTR file
= Open(toFile
, MODE_NEWFILE
);
984 FreeVec(subpatternStr
);
985 FreeVec(parsedPattern
);
987 PrintFault(IoErr(), "List");
991 oldOutput
= SelectOutput(file
);
1000 /* if (!dates && !noDates)
1010 if ((directories
== NULL
) || (*directories
== NULL
))
1012 directories
= default_directories
;
1015 for (i
= 0; directories
[i
] != NULL
; i
++)
1017 error
= listFile(directories
[i
], files
, dirs
, parsedPattern
,
1018 noHead
, lFormat
, quick
, dates
, noDates
,
1019 block
, &sinceDatetime
.dat_Stamp
,
1020 &uptoDatetime
.dat_Stamp
, since
!= NULL
,
1021 upto
!= NULL
, subpatternStr
, all
, keys
,
1024 if (error
!= RETURN_OK
)
1039 if ((BOOL
)args
[ARG_NOHEAD
] == FALSE
&&
1040 (BOOL
)args
[ARG_LFORMAT
] == FALSE
&&
1041 (BOOL
)args
[ARG_ALL
] &&
1042 (stats
.nFiles
|| stats
.nDirs
))
1044 Printf("\nTOTAL: %ld files - %ld directories - %ld blocks used\n",
1045 stats
.nFiles
, stats
.nDirs
, stats
.nBlocks
);
1049 if (error
!= RETURN_OK
)
1051 if (error
== ERROR_BREAK
)
1053 error
= RETURN_WARN
;
1057 PrintFault(error
, "List");
1058 error
= RETURN_FAIL
;
1062 if (parsedPattern
!= NULL
)
1064 FreeVec(parsedPattern
);
1067 if (subpatternStr
!= NULL
)
1069 FreeVec(subpatternStr
);
1072 if (oldOutput
!= BNULL
)
1074 Close(SelectOutput(oldOutput
));