diff --git a/bobbi/README.md b/bobbi/README.md index 78b1a9b..e3ca852 100644 --- a/bobbi/README.md +++ b/bobbi/README.md @@ -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. -``` - diff --git a/bobbi/sortdir.c#b00008 b/bobbi/sortdir.c#b00008 index 73cd2a8..0cdf673 100644 --- a/bobbi/sortdir.c#b00008 +++ b/bobbi/sortdir.c#b00008 @@ -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