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.9 (04.06.2012)\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("[%lu]", lf
->key
);
340 UQUAD filesize
= lf
->size
;
346 char *bufpos
= &buf
[254];
350 bufpos
[0] = '0' + (filesize
% 10);
355 fill
= 13 - (&buf
[254] - bufpos
);
356 for (i
= 0; i
< fill
; i
++)
360 Printf("%s ", &bufpos
[1]);
366 /* File name without extension */
369 STRPTR lastPoint
= strrchr(filename
, '.');
371 if (lastPoint
!= NULL
)
378 /* Resurrect filename in case we should print it once
380 if (lastPoint
!= NULL
)
388 /* Filename or Path name */
390 D(bug("[List] substitutePath = %d\n", substitutePath
));
391 if ( (--substitutePath
== 3) || (substitutePath
== 1) )
393 STRPTR end
= FilePart(lf
->filename
);
398 Printf(lf
->filename
);
400 /* Restore pathname */
413 STRPTR extension
= strrchr(filename
, '.');
415 if (extension
!= NULL
)
423 /* Path name, but without volume */
426 STRPTR end
= FilePart(lf
->filename
);
431 Printf(lf
->filename
);
433 /* Restore pathname */
444 Printf("%%%lc", *format
);
458 int printFileData(struct AnchorPath
*ap
,
459 BOOL showFiles
, BOOL showDirs
, STRPTR parsedPattern
,
460 ULONG
*files
, ULONG
*dirs
, ULONG
*nBlocks
, STRPTR lFormat
,
461 BOOL quick
, BOOL dates
, BOOL noDates
, BOOL block
,
462 struct DateStamp
*sinceDate
, struct DateStamp
*uptoDate
,
463 BOOL doSince
, BOOL doUpto
, STRPTR subpatternStr
,
466 STRPTR filename
= ap
->ap_Buf
;
467 BOOL isDir
= (ap
->ap_Info
.fib_DirEntryType
>= 0);
468 struct DateStamp
*ds
= &ap
->ap_Info
.fib_Date
;
469 ULONG protection
= ap
->ap_Info
.fib_Protection
;
470 UQUAD size
= ap
->ap_Info
.fib_Size
;
471 STRPTR filenote
= ap
->ap_Info
.fib_Comment
;
472 ULONG diskKey
= ap
->ap_Info
.fib_DiskKey
;
476 UBYTE date
[LEN_DATSTRING
];
477 UBYTE time
[LEN_DATSTRING
];
482 #if defined(ACTION_GET_FILE_SIZE64)
483 if (ap
->ap_Info
.fib_Size
>= 0x7FFFFFFF)
486 flock
= Lock(filename
, ACCESS_READ
);
490 UQUAD
*size_ptr
= (UQUAD
*)DoPkt(((struct FileLock
*)flock
)->fl_Task
, ACTION_GET_FILE_SIZE64
, (IPTR
)flock
, 0, 0, 0, 0);
499 /* Do the file match the time interval we are looking for?
500 (ARG_SINCE and ARG_UPTO) -- any combination of these may be
502 if ((doSince
&& (CompareDates(sinceDate
, ds
) < 0)) ||
503 (doUpto
&& (CompareDates(uptoDate
, ds
) > 0)))
508 /* Does the filename match a certain pattern? (ARG_PAT) */
509 if (parsedPattern
!= NULL
&&
510 !MatchPatternNoCase(parsedPattern
, FilePart(filename
)))
515 /* Does a substring of the filename match a certain pattern? (ARG_SUB) */
516 if (subpatternStr
!= NULL
&&
517 !MatchPatternNoCase(subpatternStr
, FilePart(filename
)))
522 CopyMem(ds
, &dt
.dat_Stamp
, sizeof(struct DateStamp
));
523 dt
.dat_Format
= FORMAT_DOS
;
524 dt
.dat_Flags
= DTF_SUBST
;
525 dt
.dat_StrDay
= NULL
;
526 dt
.dat_StrDate
= date
;
527 dt
.dat_StrTime
= time
;
528 DateToStr(&dt
); /* returns 0 if invalid */
530 /* Convert the protection bits to a string */
531 flags
[0] = protection
& FIBF_SCRIPT
? 's' : '-';
532 flags
[1] = protection
& FIBF_PURE
? 'p' : '-';
533 flags
[2] = protection
& FIBF_ARCHIVE
? 'a' : '-';
535 /* The following flags are high-active! */
536 flags
[3] = protection
& FIBF_READ
? '-' : 'r';
537 flags
[4] = protection
& FIBF_WRITE
? '-' : 'w';
538 flags
[5] = protection
& FIBF_EXECUTE
? '-' : 'e';
539 flags
[6] = protection
& FIBF_DELETE
? '-' : 'd';
547 ++*nBlocks
; /* dir entry uses 1 block on AROS, 2 on OS31) */
551 struct lfstruct lf
= { ap
, isDir
, date
, time
, flags
, filename
,
552 filenote
, size
, diskKey
};
554 printLformat(lFormat
, &lf
);
559 D(bug("Found file %s\n", filename
));
561 Printf("%-25s ", FilePart(filename
));
565 Printf(" <Dir> %7s ", flags
);
568 if (!noDates
&& (!quick
|| dates
))
570 Printf("%-11s %s", date
, time
);
580 *nBlocks
+= roundUp(size
, BLOCKSIZE
);
584 struct lfstruct lf
= { ap
, isDir
, date
, time
, flags
, filename
,
585 filenote
, size
, diskKey
};
587 printLformat(lFormat
, &lf
);
592 Printf("%-25s ", FilePart(filename
));
600 int i
; /* Loop variable */
602 __sprintf(key
, "%lu", (unsigned long)diskKey
);
603 fill
= 7 - strlen(key
) - 2;
605 for (i
= 0; i
< fill
; i
++)
610 Printf("[%lu] ", diskKey
);
616 UQUAD filesize
= block
? roundUp(size
, BLOCKSIZE
) : size
;
622 char *bufpos
= &buf
[254];
626 bufpos
[0] = '0' + (filesize
% 10);
631 fill
= 13 - (&buf
[254] - bufpos
);
632 for (i
= 0; i
< fill
; i
++)
636 Printf("%s ", &bufpos
[1]);
644 Printf("%7s ", flags
);
647 if (!noDates
&& (!quick
|| dates
))
649 Printf("%-11s %s", date
, time
);
652 if (!quick
&& (*filenote
!= 0))
654 Printf("\n: %s", filenote
);
665 /* Print directory summary information */
666 void printSummary(CONST_STRPTR dirname
, int files
, int dirs
, int nBlocks
,
667 BOOL noHead
, BOOL PrintEmpty
)
676 Printf("%ld files", files
);
680 if( files
&& (dirs
|| nBlocks
) ) PutStr(" - ");
683 Printf("%ld directories", dirs
);
685 PutStr("1 directory");
687 if( dirs
&& nBlocks
) PutStr(" - ");
690 Printf("%ld blocks used\n", nBlocks
);
691 else if (nBlocks
> 0)
692 PutStr("1 block used\n");
697 Printf("Directory \"%s\" is empty\n", dirname
);
701 int listFile(CONST_STRPTR filename
, BOOL showFiles
, BOOL showDirs
,
702 STRPTR parsedPattern
, BOOL noHead
, STRPTR lFormat
, BOOL quick
,
703 BOOL dates
, BOOL noDates
, BOOL block
, struct DateStamp
*sinceDate
,
704 struct DateStamp
*uptoDate
, BOOL doSince
, BOOL doUpto
,
705 STRPTR subpatternStr
, BOOL all
, BOOL keys
, Statistics
*stats
)
707 struct AnchorPath
*ap
;
708 struct List DirList
, FreeDirNodeList
;
709 struct DirNode
*dirnode
, *prev_dirnode
= NULL
;
717 NewList(&FreeDirNodeList
);
721 ap
= AllocVec(sizeof(struct AnchorPath
) + MAX_PATH_LEN
, MEMF_CLEAR
);
728 ap
->ap_Strlen
= MAX_PATH_LEN
;
730 error
= MatchFirst(filename
, ap
);
732 /* Explicitly named directory and not a pattern? --> enter dir */
736 if (!(ap
->ap_Flags
& APF_ITSWILD
))
738 if (ap
->ap_Info
.fib_DirEntryType
>= 0)
740 //error = printDirHeader(filename, noHead);
741 ap
->ap_Flags
|= APF_DODIR
;
745 error
= MatchNext(ap
);
755 ap
->ap_BreakBits
= SIGBREAKF_CTRL_C
;
756 if (FilePart(ap
->ap_Buf
) == ap
->ap_Buf
)
758 ap
->ap_Flags
&= ~APF_DirChanged
;
764 ** There's something to show.
766 if (!(ap
->ap_Flags
& APF_DIDDIR
))
768 if (ap
->ap_Flags
& APF_DirChanged
)
773 if (!first
) printSummary(filename
, files
, dirs
, nBlocks
, noHead
, TRUE
);
775 /* Update global statistics for (possible) ALL option */
776 stats
->nFiles
+= files
;
777 stats
->nDirs
+= dirs
;
778 stats
->nBlocks
+= nBlocks
;
784 p
= PathPart(ap
->ap_Buf
);
788 error
= printDirHeader(ap
->ap_Buf
, noHead
);
794 error
= printFileData(ap
,
813 if (all
&& (ap
->ap_Info
.fib_DirEntryType
>= 0))
815 if ((dirnode
= AllocMem(sizeof(struct DirNode
), MEMF_ANY
)))
817 if ((dirnode
->dirname
= StrDup(ap
->ap_Buf
)))
819 Insert(&DirList
, (struct Node
*)dirnode
,
820 (struct Node
*)prev_dirnode
);
822 prev_dirnode
= dirnode
;
826 FreeMem(dirnode
, sizeof(struct DirNode
));
832 error
= MatchNext(ap
);
836 } while (0 == error
);
843 if (error
== ERROR_BREAK
)
845 PrintFault(error
, NULL
);
848 if (error
== ERROR_NO_MORE_ENTRIES
)
853 if ((error
== 0) || (error
== ERROR_BREAK
))
855 BOOL printEmpty
= !(ap
->ap_Flags
& APF_ITSWILD
);
856 printSummary(filename
, files
, dirs
, nBlocks
, noHead
, printEmpty
);
859 /* Update global statistics for (possible) ALL option */
860 stats
->nFiles
+= files
;
861 stats
->nDirs
+= dirs
;
862 stats
->nBlocks
+= nBlocks
;
871 dirnode
= (struct DirNode
*)RemHead(&DirList
);
875 filename
= dirnode
->dirname
;
879 /* do not free() dirnode, as we reference dirnode->dirname! */
881 AddTail(&FreeDirNodeList
, (struct Node
*)dirnode
);
885 while ((dirnode
= (struct DirNode
*)RemHead(&FreeDirNodeList
)))
887 FreeVec(dirnode
->dirname
);
888 FreeMem(dirnode
, sizeof(struct DirNode
));
899 IPTR args
[NOOFARGS
] =
901 (IPTR
) NULL
, // ARG_DIR
902 (IPTR
) NULL
, // ARG_PAT
905 FALSE
, // ARG_NODATES
906 (IPTR
) NULL
, // ARG_TO
907 (IPTR
) NULL
, // ARG_SUB
908 (IPTR
) NULL
, // ARG_SINCE
909 (IPTR
) NULL
, // ARG_UPTO
915 FALSE
, // ARG_LFORMAT
918 static CONST_STRPTR default_directories
[] = {(CONST_STRPTR
)"", 0};
921 LONG result
= RETURN_OK
;
923 STRPTR parsedPattern
= NULL
;
924 STRPTR subpatternStr
= NULL
;
925 BPTR oldOutput
= BNULL
;
927 Statistics stats
= { 0, 0, 0 };
929 rda
= ReadArgs(ARG_TEMPLATE
, args
, NULL
);
933 CONST_STRPTR
*directories
= (CONST_STRPTR
*)args
[ARG_DIR
];
934 STRPTR lFormat
= (STRPTR
)args
[ARG_LFORMAT
];
935 STRPTR pattern
= (STRPTR
)args
[ARG_PAT
];
936 STRPTR toFile
= (STRPTR
)args
[ARG_TO
];
937 STRPTR subStr
= (STRPTR
)args
[ARG_SUB
];
938 STRPTR since
= (STRPTR
)args
[ARG_SINCE
];
939 STRPTR upto
= (STRPTR
)args
[ARG_UPTO
];
940 BOOL files
= (BOOL
)args
[ARG_FILES
];
941 BOOL dirs
= (BOOL
)args
[ARG_DIRS
];
942 BOOL noDates
= (BOOL
)args
[ARG_NODATES
];
943 BOOL dates
= (BOOL
)args
[ARG_DATES
];
944 BOOL quick
= (BOOL
)args
[ARG_QUICK
];
945 BOOL noHead
= (BOOL
)args
[ARG_NOHEAD
];
946 BOOL block
= (BOOL
)args
[ARG_BLOCK
];
947 BOOL all
= (BOOL
)args
[ARG_ALL
];
948 BOOL keys
= (BOOL
)args
[ARG_KEYS
];
950 struct DateTime sinceDatetime
;
951 struct DateTime uptoDatetime
;
953 ULONG i
; /* Loop variable */
957 sinceDatetime
.dat_StrDate
= since
;
958 sinceDatetime
.dat_StrTime
= NULL
;
959 sinceDatetime
.dat_Format
= FORMAT_DEF
;
960 sinceDatetime
.dat_Flags
= 0;
961 if (StrToDate(&sinceDatetime
) == DOSFALSE
)
964 Printf("*** Illegal 'SINCE' parameter\n");
972 uptoDatetime
.dat_StrDate
= upto
;
973 uptoDatetime
.dat_StrTime
= NULL
;
974 uptoDatetime
.dat_Format
= FORMAT_DEF
;
975 uptoDatetime
.dat_Flags
= 0;
977 if (StrToDate(&uptoDatetime
) == DOSFALSE
)
980 Printf("*** Illegal 'UPTO' parameter\n");
988 STRPTR subStrWithPat
;
989 ULONG length
= (strlen(subStr
) + sizeof("#?#?"))*2 + 2;
991 subStrWithPat
= AllocVec(length
, MEMF_ANY
);
993 if (subStrWithPat
== NULL
)
996 PrintFault(IoErr(), "List");
1001 strcpy(subStrWithPat
, "#?");
1002 strcat(subStrWithPat
, subStr
);
1003 strcat(subStrWithPat
, "#?");
1005 subpatternStr
= AllocVec(length
, MEMF_ANY
);
1007 if (subpatternStr
== NULL
||
1008 ParsePatternNoCase(subStrWithPat
, subpatternStr
, length
) == -1)
1010 FreeVec(subStrWithPat
);
1012 PrintFault(IoErr(), "List");
1017 FreeVec(subStrWithPat
);
1021 if (pattern
!= NULL
)
1023 ULONG length
= strlen(pattern
)*2 + 2;
1025 parsedPattern
= AllocVec(length
, MEMF_ANY
);
1027 if (parsedPattern
== NULL
||
1028 ParsePatternNoCase(pattern
, parsedPattern
, length
) == -1)
1030 FreeVec(subpatternStr
);
1039 BPTR file
= Open(toFile
, MODE_NEWFILE
);
1043 FreeVec(subpatternStr
);
1044 FreeVec(parsedPattern
);
1046 PrintFault(IoErr(), "List");
1050 oldOutput
= SelectOutput(file
);
1053 if (!files
&& !dirs
)
1059 /* if (!dates && !noDates)
1069 if ((directories
== NULL
) || (*directories
== NULL
))
1071 directories
= default_directories
;
1074 for (i
= 0; directories
[i
] != NULL
; i
++)
1076 error
= listFile(directories
[i
], files
, dirs
, parsedPattern
,
1077 noHead
, lFormat
, quick
, dates
, noDates
,
1078 block
, &sinceDatetime
.dat_Stamp
,
1079 &uptoDatetime
.dat_Stamp
, since
!= NULL
,
1080 upto
!= NULL
, subpatternStr
, all
, keys
,
1098 if ((BOOL
)args
[ARG_NOHEAD
] == FALSE
&&
1099 (BOOL
)args
[ARG_LFORMAT
] == FALSE
&&
1100 (BOOL
)args
[ARG_ALL
] &&
1101 (stats
.nFiles
|| stats
.nDirs
))
1103 Printf("\nTOTAL: %ld files - %ld directories - %ld blocks used\n",
1104 stats
.nFiles
, stats
.nDirs
, stats
.nBlocks
);
1110 if (error
== ERROR_BREAK
)
1112 result
= RETURN_WARN
;
1116 PrintFault(error
, "List");
1117 result
= RETURN_FAIL
;
1121 if (parsedPattern
!= NULL
)
1123 FreeVec(parsedPattern
);
1126 if (subpatternStr
!= NULL
)
1128 FreeVec(subpatternStr
);
1131 if (oldOutput
!= BNULL
)
1133 Close(SelectOutput(oldOutput
));