2 Copyright © 1995-2013, 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 -- 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
62 The following attributes of the LFORMAT strings are available
65 %B -- size of file in blocks rather than bytes
67 %D -- modification date
70 %K -- file key block number
71 %L -- size of file in bytes
72 %M -- file name without extension
75 %S -- superceded by %N and %P; obsolete
76 %T -- modification time
80 Standard DOS return codes.
85 Directory "C:" on Wednesday 12-Dec-99
86 AddBuffers 444 --p-rwed 02-Sep-99 11:51:31
87 Assign 3220 --p-rwed 02-Sep-99 11:51:31
88 Avail 728 --p-rwed 02-Sep-99 11:51:31
89 Copy 3652 --p-rwed 02-Sep-99 11:51:31
90 Delete 1972 --p-rwed 02-Sep-99 11:51:31
91 Execute 4432 --p-rwed 02-Sep-99 11:51:31
92 List 5108 --p-rwed 02-Sep-99 11:51:31
93 Installer 109956 ----rwed 02-Sep-99 11:51:31
94 Which 1068 --p-rwed 02-Sep-99 11:51:31
95 9 files - 274 blocks used
105 ******************************************************************************/
108 #include <aros/debug.h>
110 #include <clib/macros.h>
111 #include <exec/memory.h>
112 #include <proto/exec.h>
113 #include <dos/datetime.h>
115 #include <dos/exall.h>
116 #include <dos/dosasl.h>
117 #include <dos/datetime.h>
118 #include <proto/dos.h>
119 #include <proto/alib.h>
120 #include <proto/utility.h>
121 #include <utility/tagitem.h>
123 const TEXT version
[] = "$VER: List 41.11 (11.7.2013)\n";
125 #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"
134 typedef struct _Statistics
163 #define MAX_PATH_LEN 1024
165 #define BLOCKSIZE 512
167 int printDirHeader(STRPTR dirname
, BOOL noHead
)
171 char datestr
[LEN_DATSTRING
];
172 char dow
[LEN_DATSTRING
];
176 DateStamp((struct DateStamp
*)&dt
);
177 dt
.dat_Format
= FORMAT_DEF
;
180 dt
.dat_StrDate
= datestr
;
181 dt
.dat_StrTime
= NULL
;
184 Printf("Directory \"%s\" on %s %s:\n", dirname
, dow
, datestr
);
191 /* Possible printf-type switches
193 %A -- file attributes
194 %B -- size of file in blocks rather than bytes
199 %K -- file key block number
200 %L -- size of file in bytes
201 %M -- file name without extension
204 %S -- file name or file path
210 struct AnchorPath
*ap
;
222 #define roundUp(x, bSize) ((x + bSize - 1)/bSize)
224 int printLformat(STRPTR format
, struct lfstruct
*lf
)
226 STRPTR filename
= FilePart(lf
->filename
);
227 STRPTR temp
= format
;
228 LONG substitutePath
= 0;
232 Whether the path or the filename is substituted for an occurrence
233 of %S depends on how many occurrences are in the LFORMAT line, and
234 their order, as follows:
236 Occurrences of %S 1st 2nd 3rd 4th
239 3 path filename filename
240 4 path filename path filename
243 while ( ( substitutePath
< 4 ) && ( '\0' != (c
= *temp
++) ) )
246 if ( 'S' == ToUpper(*temp
++) )
249 if ( substitutePath
== 3 )
252 while ('\0' != (c
= *format
++))
256 switch (ToUpper(*format
++))
263 /* Modification date */
268 /* Modification time */
273 /* File size in blocks of BLOCKSIZE bytes */
281 ULONG tmp
= roundUp(lf
->size
, BLOCKSIZE
);
283 /* File is 0 bytes? */
296 /* Path incl. volume name*/
301 if (NameFromLock(lf
->ap
->ap_Current
->an_Lock
, buf
, 256))
308 if ((len
> 0) && (buf
[len
- 1] != ':') && (buf
[len
- 1] != '/'))
317 /* File attributes (flags) */
324 Printf("[%lu]", lf
->key
);
341 UQUAD filesize
= lf
->size
;
347 char *bufpos
= &buf
[254];
351 bufpos
[0] = '0' + (filesize
% 10);
356 fill
= 13 - (&buf
[254] - bufpos
);
357 for (i
= 0; i
< fill
; i
++)
361 Printf("%s ", &bufpos
[1]);
367 /* File name without extension */
370 STRPTR lastPoint
= strrchr(filename
, '.');
372 if (lastPoint
!= NULL
)
379 /* Resurrect filename in case we should print it once
381 if (lastPoint
!= NULL
)
389 /* Filename or Path name */
391 D(bug("[List] substitutePath = %d\n", substitutePath
));
392 if ( (--substitutePath
== 3) || (substitutePath
== 1) )
394 STRPTR end
= FilePart(lf
->filename
);
399 Printf(lf
->filename
);
401 /* Restore pathname */
414 STRPTR extension
= strrchr(filename
, '.');
416 if (extension
!= NULL
)
424 /* Path name, but without volume */
427 STRPTR end
= FilePart(lf
->filename
);
432 Printf(lf
->filename
);
434 /* Restore pathname */
445 Printf("%%%lc", *format
);
459 int printFileData(struct AnchorPath
*ap
,
460 BOOL showFiles
, BOOL showDirs
, STRPTR parsedPattern
,
461 ULONG
*files
, ULONG
*dirs
, ULONG
*nBlocks
, STRPTR lFormat
,
462 BOOL quick
, BOOL dates
, BOOL noDates
, BOOL block
,
463 struct DateStamp
*sinceDate
, struct DateStamp
*uptoDate
,
464 BOOL doSince
, BOOL doUpto
, STRPTR subpatternStr
,
467 STRPTR filename
= ap
->ap_Buf
;
468 BOOL isDir
= (ap
->ap_Info
.fib_DirEntryType
>= 0);
469 struct DateStamp
*ds
= &ap
->ap_Info
.fib_Date
;
470 ULONG protection
= ap
->ap_Info
.fib_Protection
;
471 UQUAD size
= ap
->ap_Info
.fib_Size
;
472 STRPTR filenote
= ap
->ap_Info
.fib_Comment
;
473 ULONG diskKey
= ap
->ap_Info
.fib_DiskKey
;
477 UBYTE date
[LEN_DATSTRING
];
478 UBYTE time
[LEN_DATSTRING
];
483 #if defined(ACTION_GET_FILE_SIZE64)
484 if (ap
->ap_Info
.fib_Size
>= 0x7FFFFFFF)
487 flock
= Lock(filename
, ACCESS_READ
);
491 UQUAD
*size_ptr
= (UQUAD
*)DoPkt(((struct FileLock
*)flock
)->fl_Task
, ACTION_GET_FILE_SIZE64
, (IPTR
)flock
, 0, 0, 0, 0);
500 /* Do the file match the time interval we are looking for?
501 (ARG_SINCE and ARG_UPTO) -- any combination of these may be
503 if ((doSince
&& (CompareDates(sinceDate
, ds
) < 0)) ||
504 (doUpto
&& (CompareDates(uptoDate
, ds
) > 0)))
509 /* Does the filename match a certain pattern? (ARG_PAT) */
510 if (parsedPattern
!= NULL
&&
511 !MatchPatternNoCase(parsedPattern
, FilePart(filename
)))
516 /* Does a substring of the filename match a certain pattern? (ARG_SUB) */
517 if (subpatternStr
!= NULL
&&
518 !MatchPatternNoCase(subpatternStr
, FilePart(filename
)))
523 CopyMem(ds
, &dt
.dat_Stamp
, sizeof(struct DateStamp
));
524 dt
.dat_Format
= FORMAT_DOS
;
528 dt
.dat_Flags
= DTF_SUBST
;
529 dt
.dat_StrDay
= NULL
;
530 dt
.dat_StrDate
= date
;
531 dt
.dat_StrTime
= time
;
534 /* Convert the protection bits to a string */
535 flags
[0] = protection
& FIBF_SCRIPT
? 's' : '-';
536 flags
[1] = protection
& FIBF_PURE
? 'p' : '-';
537 flags
[2] = protection
& FIBF_ARCHIVE
? 'a' : '-';
539 /* The following flags are high-active! */
540 flags
[3] = protection
& FIBF_READ
? '-' : 'r';
541 flags
[4] = protection
& FIBF_WRITE
? '-' : 'w';
542 flags
[5] = protection
& FIBF_EXECUTE
? '-' : 'e';
543 flags
[6] = protection
& FIBF_DELETE
? '-' : 'd';
551 ++*nBlocks
; /* dir entry uses 1 block on AROS, 2 on OS31) */
555 struct lfstruct lf
= { ap
, isDir
, date
, time
, flags
, filename
,
556 filenote
, size
, diskKey
};
558 printLformat(lFormat
, &lf
);
563 D(bug("Found file %s\n", filename
));
565 Printf("%-25s ", FilePart(filename
));
569 Printf(" <Dir> %7s ", flags
);
572 if (!noDates
&& (!quick
|| dates
))
574 Printf("%-11s %s", date
, time
);
584 *nBlocks
+= roundUp(size
, BLOCKSIZE
);
588 struct lfstruct lf
= { ap
, isDir
, date
, time
, flags
, filename
,
589 filenote
, size
, diskKey
};
591 printLformat(lFormat
, &lf
);
596 Printf("%-25s ", FilePart(filename
));
604 int i
; /* Loop variable */
606 __sprintf(key
, "%lu", (unsigned long)diskKey
);
607 fill
= 7 - strlen(key
) - 2;
609 for (i
= 0; i
< fill
; i
++)
614 Printf("[%lu] ", diskKey
);
620 UQUAD filesize
= block
? roundUp(size
, BLOCKSIZE
) : size
;
626 char *bufpos
= &buf
[254];
630 bufpos
[0] = '0' + (filesize
% 10);
635 fill
= 13 - (&buf
[254] - bufpos
);
636 for (i
= 0; i
< fill
; i
++)
640 Printf("%s ", &bufpos
[1]);
648 Printf("%7s ", flags
);
651 if (!noDates
&& (!quick
|| dates
))
653 Printf("%-11s %s", date
, time
);
656 if (!quick
&& (*filenote
!= 0))
658 Printf("\n: %s", filenote
);
669 /* Print directory summary information */
670 void printSummary(CONST_STRPTR dirname
, int files
, int dirs
, int nBlocks
,
671 BOOL noHead
, BOOL PrintEmpty
)
680 Printf("%ld files", files
);
684 if( files
&& (dirs
|| nBlocks
) ) PutStr(" - ");
687 Printf("%ld directories", dirs
);
689 PutStr("1 directory");
691 if( dirs
&& nBlocks
) PutStr(" - ");
694 Printf("%ld blocks used\n", nBlocks
);
695 else if (nBlocks
> 0)
696 PutStr("1 block used\n");
701 Printf("Directory \"%s\" is empty\n", dirname
);
705 int listFile(CONST_STRPTR filename
, BOOL showFiles
, BOOL showDirs
,
706 STRPTR parsedPattern
, BOOL noHead
, STRPTR lFormat
, BOOL quick
,
707 BOOL dates
, BOOL noDates
, BOOL block
, struct DateStamp
*sinceDate
,
708 struct DateStamp
*uptoDate
, BOOL doSince
, BOOL doUpto
,
709 STRPTR subpatternStr
, BOOL all
, BOOL keys
, Statistics
*stats
)
711 struct AnchorPath
*ap
;
712 struct List DirList
, FreeDirNodeList
;
713 struct DirNode
*dirnode
, *prev_dirnode
= NULL
;
721 NewList(&FreeDirNodeList
);
725 ap
= AllocVec(sizeof(struct AnchorPath
) + MAX_PATH_LEN
, MEMF_CLEAR
);
732 ap
->ap_Strlen
= MAX_PATH_LEN
;
734 error
= MatchFirst(filename
, ap
);
736 /* Explicitly named directory and not a pattern? --> enter dir */
740 if (!(ap
->ap_Flags
& APF_ITSWILD
))
742 if (ap
->ap_Info
.fib_DirEntryType
>= 0)
744 //error = printDirHeader(filename, noHead);
745 ap
->ap_Flags
|= APF_DODIR
;
749 error
= MatchNext(ap
);
759 ap
->ap_BreakBits
= SIGBREAKF_CTRL_C
;
760 if (FilePart(ap
->ap_Buf
) == ap
->ap_Buf
)
762 ap
->ap_Flags
&= ~APF_DirChanged
;
768 ** There's something to show.
770 if (!(ap
->ap_Flags
& APF_DIDDIR
))
772 if (ap
->ap_Flags
& APF_DirChanged
)
777 if (!first
) printSummary(filename
, files
, dirs
, nBlocks
, noHead
, TRUE
);
779 /* Update global statistics for (possible) ALL option */
780 stats
->nFiles
+= files
;
781 stats
->nDirs
+= dirs
;
782 stats
->nBlocks
+= nBlocks
;
788 p
= PathPart(ap
->ap_Buf
);
792 error
= printDirHeader(ap
->ap_Buf
, noHead
);
798 error
= printFileData(ap
,
817 if (all
&& (ap
->ap_Info
.fib_DirEntryType
>= 0))
819 if ((dirnode
= AllocMem(sizeof(struct DirNode
), MEMF_ANY
)))
821 if ((dirnode
->dirname
= StrDup(ap
->ap_Buf
)))
823 Insert(&DirList
, (struct Node
*)dirnode
,
824 (struct Node
*)prev_dirnode
);
826 prev_dirnode
= dirnode
;
830 FreeMem(dirnode
, sizeof(struct DirNode
));
836 error
= MatchNext(ap
);
840 } while (0 == error
);
847 if (error
== ERROR_BREAK
)
849 PrintFault(error
, NULL
);
852 if (error
== ERROR_NO_MORE_ENTRIES
)
857 if ((error
== 0) || (error
== ERROR_BREAK
))
859 BOOL printEmpty
= !(ap
->ap_Flags
& APF_ITSWILD
);
860 printSummary(filename
, files
, dirs
, nBlocks
, noHead
, printEmpty
);
863 /* Update global statistics for (possible) ALL option */
864 stats
->nFiles
+= files
;
865 stats
->nDirs
+= dirs
;
866 stats
->nBlocks
+= nBlocks
;
875 dirnode
= (struct DirNode
*)RemHead(&DirList
);
879 filename
= dirnode
->dirname
;
883 /* do not free() dirnode, as we reference dirnode->dirname! */
885 AddTail(&FreeDirNodeList
, (struct Node
*)dirnode
);
889 while ((dirnode
= (struct DirNode
*)RemHead(&FreeDirNodeList
)))
891 FreeVec(dirnode
->dirname
);
892 FreeMem(dirnode
, sizeof(struct DirNode
));
903 IPTR args
[NOOFARGS
] =
905 (IPTR
) NULL
, // ARG_DIR
906 (IPTR
) NULL
, // ARG_PAT
909 FALSE
, // ARG_NODATES
910 (IPTR
) NULL
, // ARG_TO
911 (IPTR
) NULL
, // ARG_SUB
912 (IPTR
) NULL
, // ARG_SINCE
913 (IPTR
) NULL
, // ARG_UPTO
919 FALSE
, // ARG_LFORMAT
922 static CONST_STRPTR default_directories
[] = {(CONST_STRPTR
)"", 0};
925 LONG result
= RETURN_OK
;
927 STRPTR parsedPattern
= NULL
;
928 STRPTR subpatternStr
= NULL
;
929 BPTR oldOutput
= BNULL
;
931 Statistics stats
= { 0, 0, 0 };
933 rda
= ReadArgs(ARG_TEMPLATE
, args
, NULL
);
937 CONST_STRPTR
*directories
= (CONST_STRPTR
*)args
[ARG_DIR
];
938 STRPTR lFormat
= (STRPTR
)args
[ARG_LFORMAT
];
939 STRPTR pattern
= (STRPTR
)args
[ARG_PAT
];
940 STRPTR toFile
= (STRPTR
)args
[ARG_TO
];
941 STRPTR subStr
= (STRPTR
)args
[ARG_SUB
];
942 STRPTR since
= (STRPTR
)args
[ARG_SINCE
];
943 STRPTR upto
= (STRPTR
)args
[ARG_UPTO
];
944 BOOL files
= (BOOL
)args
[ARG_FILES
];
945 BOOL dirs
= (BOOL
)args
[ARG_DIRS
];
946 BOOL noDates
= (BOOL
)args
[ARG_NODATES
];
947 BOOL dates
= (BOOL
)args
[ARG_DATES
];
948 BOOL quick
= (BOOL
)args
[ARG_QUICK
];
949 BOOL noHead
= (BOOL
)args
[ARG_NOHEAD
];
950 BOOL block
= (BOOL
)args
[ARG_BLOCK
];
951 BOOL all
= (BOOL
)args
[ARG_ALL
];
952 BOOL keys
= (BOOL
)args
[ARG_KEYS
];
954 struct DateTime sinceDatetime
;
955 struct DateTime uptoDatetime
;
957 ULONG i
; /* Loop variable */
961 sinceDatetime
.dat_StrDate
= since
;
962 sinceDatetime
.dat_StrTime
= NULL
;
963 sinceDatetime
.dat_Format
= FORMAT_DEF
;
964 sinceDatetime
.dat_Flags
= 0;
965 if (StrToDate(&sinceDatetime
) == DOSFALSE
)
968 Printf("*** Illegal 'SINCE' parameter\n");
972 sinceDatetime
.dat_Stamp
.ds_Minute
= 0;
973 sinceDatetime
.dat_Stamp
.ds_Tick
= 0;
978 uptoDatetime
.dat_StrDate
= upto
;
979 uptoDatetime
.dat_StrTime
= NULL
;
980 uptoDatetime
.dat_Format
= FORMAT_DEF
;
981 uptoDatetime
.dat_Flags
= 0;
983 if (StrToDate(&uptoDatetime
) == DOSFALSE
)
986 Printf("*** Illegal 'UPTO' parameter\n");
990 uptoDatetime
.dat_Stamp
.ds_Minute
= 1439;
991 uptoDatetime
.dat_Stamp
.ds_Tick
= 2999;
996 STRPTR subStrWithPat
;
997 ULONG length
= (strlen(subStr
) + sizeof("#?#?"))*2 + 2;
999 subStrWithPat
= AllocVec(length
, MEMF_ANY
);
1001 if (subStrWithPat
== NULL
)
1004 PrintFault(IoErr(), "List");
1009 strcpy(subStrWithPat
, "#?");
1010 strcat(subStrWithPat
, subStr
);
1011 strcat(subStrWithPat
, "#?");
1013 subpatternStr
= AllocVec(length
, MEMF_ANY
);
1015 if (subpatternStr
== NULL
||
1016 ParsePatternNoCase(subStrWithPat
, subpatternStr
, length
) == -1)
1018 FreeVec(subStrWithPat
);
1020 PrintFault(IoErr(), "List");
1025 FreeVec(subStrWithPat
);
1029 if (pattern
!= NULL
)
1031 ULONG length
= strlen(pattern
)*2 + 2;
1033 parsedPattern
= AllocVec(length
, MEMF_ANY
);
1035 if (parsedPattern
== NULL
||
1036 ParsePatternNoCase(pattern
, parsedPattern
, length
) == -1)
1038 FreeVec(subpatternStr
);
1047 BPTR file
= Open(toFile
, MODE_NEWFILE
);
1051 FreeVec(subpatternStr
);
1052 FreeVec(parsedPattern
);
1054 PrintFault(IoErr(), "List");
1058 oldOutput
= SelectOutput(file
);
1061 if (!files
&& !dirs
)
1067 /* if (!dates && !noDates)
1077 if ((directories
== NULL
) || (*directories
== NULL
))
1079 directories
= default_directories
;
1082 for (i
= 0; directories
[i
] != NULL
; i
++)
1084 error
= listFile(directories
[i
], files
, dirs
, parsedPattern
,
1085 noHead
, lFormat
, quick
, dates
, noDates
,
1086 block
, &sinceDatetime
.dat_Stamp
,
1087 &uptoDatetime
.dat_Stamp
, since
!= NULL
,
1088 upto
!= NULL
, subpatternStr
, all
, keys
,
1106 if ((BOOL
)args
[ARG_NOHEAD
] == FALSE
&&
1107 (BOOL
)args
[ARG_LFORMAT
] == FALSE
&&
1108 (BOOL
)args
[ARG_ALL
] &&
1109 (stats
.nFiles
|| stats
.nDirs
))
1111 Printf("\nTOTAL: %ld files - %ld directories - %ld blocks used\n",
1112 stats
.nFiles
, stats
.nDirs
, stats
.nBlocks
);
1118 if (error
== ERROR_BREAK
)
1120 result
= RETURN_WARN
;
1124 PrintFault(error
, "List");
1125 result
= RETURN_FAIL
;
1129 if (parsedPattern
!= NULL
)
1131 FreeVec(parsedPattern
);
1134 if (subpatternStr
!= NULL
)
1136 FreeVec(subpatternStr
);
1139 if (oldOutput
!= BNULL
)
1141 Close(SelectOutput(oldOutput
));