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 ** ** THIS IS ALPHA-QUALITY CODE **
** USE AT YOUR OWN RISK! ** ** 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 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 -w Enable writing to disk
-c Use creation time rather than modification
-v Verbose output -v Verbose output
-V Verbose debugging output
-h This help -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 Directory sort options xxx, is a list of fields on which to
sort. The sort options are processed left-to-right. sort. The sort options are processed left-to-right.
n sort by filename ascending n sort by filename ascending
N sort by filename descending 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 ascending
T sort by type descending T sort by type descending
d sort directories to top f sort folders (directories) to top
D sort directories to bottom 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 . e.g.: sortdir -w -s nd .
Will sort the current directory first by name (ascending), Will sort the current directory first by name (ascending),
then sort directories to the top, and will write the sorted then sort directories to the top, and will write the sorted
directory to disk. directory to disk.
```

View File

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