Added support for sorting on ctime and mtime

This commit is contained in:
Bobbi Webber-Manners 2020-02-19 21:42:12 -05:00
parent 6d8b73ed87
commit 14b68a8268
2 changed files with 144 additions and 31 deletions

View File

@ -2,25 +2,40 @@
** THIS IS ALPHA-QUALITY CODE **
** USE AT YOUR OWN RISK! **
usage: sortdir [-s xxx] [-rwv] path
usage: sortdir [-s xxx] [-n x] [-rDwcvVh] path
Options: -s xxx Directory sort options
-n x Filename upper/lower case options
-r Recursive descent
-D Whole-disk mode (implies -r)
-w Enable writing to disk
-c Use creation time rather than modification
-v Verbose output
-V Verbose debugging output
-h This help
Upper/lower case option x:
l convert filenames to lower case eg: read.me
u convert filenames to upper case eg: READ.ME
i convert filenames to initial upper case eg: Read.me
c convert filenames to camel case eg: Read.Me
Directory sort options xxx, is a list of fields on which to
sort. The sort options are processed left-to-right.
n sort by filename ascending
N sort by filename descending
d sort by modification (or creation [-k]) date ascending
D sort by modification (or creation [-k]) date descending
t sort by type ascending
T sort by type descending
d sort directories to top
D sort directories to bottom
f sort folders (directories) to top
F sort folders (directories) to bottom
b sort by blocks used ascending
B sort by blocks used descending
e sort by EOF position ascending
E sort by EOF position descending
e.g.: sortdir -w -s nd .
Will sort the current directory first by name (ascending),
then sort directories to the top, and will write the sorted
directory to disk.
```

View File

@ -4,7 +4,6 @@
* TODO: Error counting & fix error return codes
* TODO: Error log file option
* TODO: Fix mode
* TODO: Sort by mtime, ctime
* TODO: Tool for 'extending' volume dir to more than 4 blocks
* TODO: Legacy/extended date format conversion
* TODO: Trimming unused directory blocks
@ -100,13 +99,14 @@ struct block {
* Entry for array of filenames used by qsort()
*/
struct fileent {
char name[NMLEN+1];/* Name converted to upper/lower case */
uchar type; /* ProDOS file type */
uint blocks; /* Size in blocks */
ulong eof; /* EOF position in bytes */
uint order; /* Hack to make qsort() stable */
uchar blockidx; /* Index of dir block (1,2,3 ...) */
uchar entrynum; /* Entry within the block */
char name[NMLEN+1]; /* Name converted to upper/lower case */
char datetime[20]; /* Date/time as a yyyy-mm-dd hh:mm string */
uchar type; /* ProDOS file type */
uint blocks; /* Size in blocks */
ulong eof; /* EOF position in bytes */
uint order; /* Hack to make qsort() stable */
uchar blockidx; /* Index of dir block (1,2,3 ...) */
uchar entrynum; /* Entry within the block */
};
/*
@ -117,6 +117,18 @@ struct dirblk {
struct dirblk *next;
};
/*
* Represents a date and time
*/
struct datetime {
uint year;
uchar month;
uchar day;
uchar hour;
uchar minute;
uchar ispd25format;
};
/* Globals */
static uint totblks; /* Total # blocks on volume */
static uchar *freelist; /* Free-list bitmap */
@ -136,17 +148,20 @@ static uchar dorecurse = 0; /* -r recurse option */
static uchar dowrite = 0; /* -w write option */
static uchar doverbose = 0; /* -v verbose option */
static uchar dodebug = 0; /* -V very verbose option */
static uchar do_ctime = 0; /* -k ctime option */
static char sortopts[5] = ""; /* -s:abc list of sort options */
static char caseopts[2] = ""; /* -c:x case conversion option */
/* Prototypes */
void prerr(char *s);
void hline(void);
int readdiskblock(uchar device, uint blocknum, char *buf);
int writediskblock(uchar device, uint blocknum, char *buf);
void fixcase(char *in, char *out, uchar minvers, uchar vers, uchar len);
void lowercase(char *p, uchar len, uchar *minvers, uchar *vers);
void uppercase(char *p, uchar len, uchar *minvers, uchar *vers);
void initialcase(uchar mode, char *p, uchar len, uchar *minvers, uchar *vers);
void convertdatetime(uchar time[4], struct datetime *dt);
int readfreelist(uchar device);
int isfree(uint blk);
int isused(uint blk);
@ -162,6 +177,8 @@ void enqueuesubdir(uint blocknum, uint subdiridx);
int readdir(uint device, uint blocknum);
int cmp_name_asc(const void *a, const void *b);
int cmp_name_desc(const void *a, const void *b);
int cmp_datetime_asc(const void *a, const void *b);
int cmp_datetime_desc(const void *a, const void *b);
int cmp_type_asc(const void *a, const void *b);
int cmp_type_desc(const void *a, const void *b);
int cmp_dir_beg(const void *a, const void *b);
@ -187,6 +204,12 @@ void prerr(char *s) {
fputs("\n", stderr);
}
/* Horizontal line */
void hline(void) {
for (uint i=0; i<80; ++i)
putchar('-');
}
/*
* Read block from disk using ProDOS call
* buf must point to buffer with at least 512 bytes
@ -399,6 +422,39 @@ ret:
return rv;
}
segment "extra";
/*
* Parse mtime or ctime fields and populate the fields of the datetime struct
* Supports the legacy ProDOS date/time format as used by ProDOS 1.0->2.4.0
* and also the new format introduced with ProDOS 2.5.
*/
void convertdatetime(uchar time[4], struct datetime *dt) {
uint d = time[0] + 256U * time[1];
uint t = time[2] + 256U * time[3];
if (!(t & 0xe000)) {
/* ProDOS 1.0 to 2.4.2 date format */
dt->year = (d & 0xfe00) >> 9;
dt->month = (d & 0x01e0) >> 5;
dt->day = d & 0x001f;
dt->hour = (t & 0x1f00) >> 8;
dt->minute = t & 0x003f;
dt->ispd25format = 0;
if (dt->year < 40) /* See ProDOS-8 Tech Note 48 */
dt->year += 2000;
else
dt->year += 1900;
} else {
/* ProDOS 2.5.0+ */
dt->year = t & 0x0fff;
dt->month = ((t & 0xf000) >> 12) - 1;
dt->day = (d & 0xf800) >> 11;
dt->hour = (d & 0x07c0) >> 6;
dt->minute = d & 0x003f;
dt->ispd25format = 1;
}
}
/*
* Read the free list
*/
@ -717,9 +773,9 @@ int readdir(uint device, uint blocknum) {
fixcase(hdr->name, namebuf,
hdr->vers, hdr->minvers, hdr->typ_len & 0x0f);
puts("---------------------------------------------------------");
hline();
printf("Directory %s (%d entries)\n", namebuf, filecount);
puts("---------------------------------------------------------");
hline();
/* Copy pointers and header to sorteddata[], zero the rest */
bzero(curblk->sorteddata, BLKSZ);
@ -815,6 +871,15 @@ int readdir(uint device, uint blocknum) {
filelist[numfiles].entrynum = blkentries;
filelist[numfiles].blocks = blks;
filelist[numfiles].eof = eof;
struct datetime dt;
convertdatetime(do_ctime ? ent->ctime : ent->mtime,
&dt);
sprintf(filelist[numfiles].datetime,
"%04d-%02d-%02d %02d:%02d %s",
dt.year, dt.month, dt.day, dt.hour, dt.minute,
(dt.ispd25format ? "*" : " "));
#ifdef CHECK
uint keyblk = ent->keyptr[0] + 256U * ent->keyptr[1];
uint hdrblk = ent->hdrptr[0] + 256U * ent->hdrptr[1];
@ -926,6 +991,22 @@ int cmp_name_desc(const void *a, const void *b) {
((struct fileent*)a)->name, NMLEN);
}
/*
* Compare - date/time sort in ascending order
*/
int cmp_datetime_asc(const void *a, const void *b) {
return strncmp(((struct fileent*)a)->datetime,
((struct fileent*)b)->datetime, 16);
}
/*
* Compare - date/time sort in descending order
*/
int cmp_datetime_desc(const void *a, const void *b) {
return strncmp(((struct fileent*)b)->datetime,
((struct fileent*)a)->datetime, 16);
}
/*
* Compare - type sort in ascending order
* Uses the order field to make qsort() stable
@ -1042,6 +1123,14 @@ void sortlist(char s) {
qsort(filelist, numfiles, sizeof(struct fileent),
cmp_name_desc);
break;
case 'd':
qsort(filelist, numfiles, sizeof(struct fileent),
cmp_datetime_asc);
break;
case 'D':
qsort(filelist, numfiles, sizeof(struct fileent),
cmp_datetime_desc);
break;
case 't':
qsort(filelist, numfiles, sizeof(struct fileent),
cmp_type_asc);
@ -1050,11 +1139,11 @@ void sortlist(char s) {
qsort(filelist, numfiles, sizeof(struct fileent),
cmp_type_desc);
break;
case 'd':
case 'f':
qsort(filelist, numfiles, sizeof(struct fileent),
cmp_dir_beg);
break;
case 'D':
case 'F':
qsort(filelist, numfiles, sizeof(struct fileent),
cmp_dir_end);
break;
@ -1084,9 +1173,13 @@ void sortlist(char s) {
* Print the file info stored in filelist[]
*/
void printlist(void) {
puts("---------------------------------------------------------");
hline();
printf("numfiles=%u\n", numfiles);
puts("Dirblk Entry Type : Name : Blocks EOF");
fputs("Dirblk Entry Type : Name : Blocks EOF ", stdout);
if (do_ctime)
puts("Created");
else
puts("Modified");
for (uint i=0; i<numfiles; ++i) {
printf(" %03u %02u %02x : %s",
filelist[i].blockidx,
@ -1095,9 +1188,10 @@ void printlist(void) {
filelist[i].name);
for (uint j=0; j<(16-strlen(filelist[i].name)); ++j)
putchar(' ');
printf(": %5u %8lu\n", filelist[i].blocks, filelist[i].eof);
printf(": %5u %8lu", filelist[i].blocks, filelist[i].eof);
printf(" %s\n", filelist[i].datetime);
}
puts("---------------------------------------------------------");
hline();
}
/*
@ -1214,15 +1308,14 @@ void freeblocks(void) {
blocks = NULL;
}
segment "extra";
void usage(void) {
prerr("usage: sortdir [-s xxx] [-rwv] path\n");
prerr(" Options: -s xxx Directory sort options");
prerr(" -c x upper/lower case options");
prerr(" -n x Filename upper/lower case options");
prerr(" -r Recursive descent");
prerr(" -D Whole-disk mode (implies -r)");
prerr(" -w Enable writing to disk");
prerr(" -c Use creation time rather than modification");
prerr(" -v Verbose output");
prerr(" -V Verbose debugging output");
prerr(" -h This help");
@ -1237,10 +1330,12 @@ void usage(void) {
prerr("sort. The sort options are processed left-to-right.");
prerr(" n sort by filename ascending");
prerr(" N sort by filename descending");
prerr(" d sort by modification (or creation [-k]) date ascending");
prerr(" D sort by modification (or creation [-k]) date descending");
prerr(" t sort by type ascending");
prerr(" T sort by type descending");
prerr(" d sort directories to top");
prerr(" D sort directories to bottom");
prerr(" f sort folders (directories) to top");
prerr(" F sort folders (directories) to bottom");
prerr(" b sort by blocks used ascending");
prerr(" B sort by blocks used descending");
prerr(" e sort by EOF position ascending");
@ -1320,10 +1415,10 @@ int main(int argc, char *argv[]) {
exit(1);
}
int opt;
while ((opt = getopt(argc, argv, "c:DrhwvVs:")) != -1) {
while ((opt = getopt(argc, argv, "cDrwvVs:n:h")) != -1) {
switch (opt) {
case 'c':
strncpy(caseopts, optarg, 1);
do_ctime = 1;
break;
case 'D':
dowholedisk = 1;
@ -1332,9 +1427,6 @@ int main(int argc, char *argv[]) {
case 'r':
dorecurse = 1;
break;
case 's':
strncpy(sortopts, optarg, 5);
break;
case 'w':
dowrite = 1;
break;
@ -1344,6 +1436,12 @@ int main(int argc, char *argv[]) {
case 'V':
dodebug = 1;
break;
case 's':
strncpy(sortopts, optarg, 5);
break;
case 'n':
strncpy(caseopts, optarg, 1);
break;
case 'h':
default:
usage();