mirror of
https://github.com/GnoConsortium/gno.git
synced 2024-12-21 23:29:16 +00:00
587 lines
18 KiB
C
587 lines
18 KiB
C
/*
|
|
* ls
|
|
*
|
|
* version 2.0
|
|
*
|
|
* Rewritten for speed, memory usage, and reliability
|
|
*
|
|
* now checks TERM var and displays MouseText folder only if set to gnocon
|
|
* does not apply name quicksort if files are on an HFS volume
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <getopt.h>
|
|
#include <fcntl.h>
|
|
#include <gsos.h>
|
|
#include <unistd.h>
|
|
#include <misctool.h>
|
|
|
|
#pragma stacksize 1024
|
|
#pragma optimize -1
|
|
/*#pragma optimize 8*/
|
|
|
|
#define CALCULATE -1
|
|
#define ONLYONE -2
|
|
|
|
unsigned fl_all,fl_reverse;
|
|
unsigned fl_gnocon;
|
|
unsigned fl_sortname,fl_sortcreate,fl_sortmod;
|
|
unsigned firstflag;
|
|
|
|
struct directory {
|
|
ResultBuf32Ptr dir_name;
|
|
struct directory *parent;
|
|
unsigned fake;
|
|
word num_entries;
|
|
longword num_bytes;
|
|
word no_sort;
|
|
unsigned width;
|
|
DirEntryRecPtrGS *entry_ptrs;
|
|
DirEntryRecPtrGS entries;
|
|
};
|
|
|
|
DirEntryRecGS dirinfo;
|
|
ResultBuf32 nameb;
|
|
|
|
lsexit(int x_code)
|
|
{
|
|
SYSTEMQUITFLAGS(0x4000);
|
|
SYSTEMQUITPATH(NULL);
|
|
exit(x_code);
|
|
}
|
|
/*#define lsexit(x) exit(x)*/
|
|
|
|
#define qsort(a,b,c,d) nqsort(a,b,c,d)
|
|
extern void SortList(void *a, int c, int b, void *d);
|
|
void nqsort(void *,unsigned, unsigned,int (*cmp)(void *,void *));
|
|
|
|
void printGS(GSString255Ptr g)
|
|
{
|
|
fwrite(g->text,g->length,1,stdout);
|
|
}
|
|
|
|
typedef struct listStruct *list;
|
|
const char month[12][4] =
|
|
{ "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" };
|
|
|
|
typedef struct FileTypeConv {
|
|
word type;
|
|
char rep[10];
|
|
} FileTypeConv;
|
|
|
|
FileTypeConv FTTable[] = {
|
|
0x0f, "DIR ", /* directory */
|
|
0xB0, "src ", /* apw source file */
|
|
0xb1, "obj ", /* apw object file */
|
|
0x04, "txt ", /* ascii text file */
|
|
0xb3, "s16 ", /* gs/os program file */
|
|
0xb5, "exe ", /* gs/os shell program file */
|
|
0xb8, "nda ",
|
|
0xb9, "cda ",
|
|
0xba, "tol ",
|
|
0x00, "non ", /* typeless file */
|
|
0x01, "bad ", /* bad block file */
|
|
0x06, "bin ", /* general binary file */
|
|
0x08, "fot ", /* graphics screen file */
|
|
0x19, "adb ", /* appleworks data base file */
|
|
0x1a, "awp ", /* appleworks word processor file */
|
|
0x1b, "asp ", /* appleworks spreadsheet */
|
|
0xb2, "lib ", /* apw library file */
|
|
0xb4, "rtl ", /* apw runtime library */
|
|
0xef, "pas ", /* pascal partition */
|
|
0xf0, "cmd ",
|
|
0xfa, "int ", /* integer basic program */
|
|
0xfb, "var ", /* int basic var file */
|
|
0xfc, "bas ", /* applesloth basic file */
|
|
0xfd, "var ", /* applesloth variable file */
|
|
0xfe, "rel ", /* EDASM relocatable code */
|
|
0xff, "sys ", /* prodos 8 system program file */
|
|
-1, ""};
|
|
|
|
char conv[10];
|
|
char *getFileType(int type)
|
|
{
|
|
int i;
|
|
|
|
i = 0;
|
|
while (FTTable[i].type != -1)
|
|
{
|
|
if (FTTable[i].type == type)
|
|
{
|
|
return FTTable[i].rep;
|
|
}
|
|
else i++;
|
|
}
|
|
sprintf(conv,"$%2X ",type);
|
|
return conv;
|
|
}
|
|
|
|
word curYear;
|
|
|
|
void *sortRoutine;
|
|
int columns;
|
|
int openDirectory, fl_longOutput, inK;
|
|
int dirOnly, idType, fl_recursive, fl_nosort;
|
|
int more,less;
|
|
unsigned whichTime;
|
|
|
|
void printDirName(struct directory *d, unsigned level)
|
|
{
|
|
if (d == NULL) return;
|
|
else {
|
|
printDirName(d->parent,level+1);
|
|
if (d->dir_name != NULL) {
|
|
printGS((GSString255Ptr)&d->dir_name->bufString);
|
|
if (level) putchar('/');
|
|
}
|
|
}
|
|
if (!level) putchar(':');
|
|
}
|
|
|
|
void long_out(DirEntryRecGS *entry)
|
|
{
|
|
TimeRec *time;
|
|
|
|
if (whichTime) time = &entry->createDateTime;
|
|
else time = &entry->modDateTime;
|
|
|
|
printf("%c%c%c%c%c%c%c %04lX %4s %8ld",
|
|
(entry->fileType == 0x0f) ? 'd' :
|
|
((entry->flags & 0x8000) ? 'e' : '-'),
|
|
(entry->access & 0x01) ? 'r' : '-',
|
|
(entry->access & 0x02) ? 'w' : '-',
|
|
((entry->fileType == 0xff) || (entry->fileType == 0xb5) ||
|
|
(entry->fileType == 0xb3) ||
|
|
((entry->fileType == 0xb0) && (entry->auxType == 6l)))
|
|
? 'x' : '-',
|
|
(entry->access & 0x20) ? 'b' : '-',
|
|
(entry->access & 0x40) ? 'r' : '-',
|
|
(entry->access & 0x80) ? 'd' : '-',
|
|
entry->auxType,
|
|
getFileType(entry->fileType),
|
|
entry->eof+entry->resourceEOF);
|
|
if (time->year+1900 == curYear)
|
|
printf(" %3s %2d %02d:%02d ",
|
|
month[time->month],time->day+1,time->hour,time->minute);
|
|
else printf(" %3s %2d %4d ",month[time->month],time->day+1,
|
|
time->year+1900);
|
|
/*puts(entry->name->bufString.text);*/
|
|
}
|
|
|
|
/* fake a directory read for command-line arguments */
|
|
struct directory *fakeDirect(int argc, char **argv,struct directory *par)
|
|
{
|
|
struct directory *new;
|
|
unsigned i,file_count = 0,maxWidth = 0;
|
|
unsigned err;
|
|
DirEntryRecPtrGS newents,nptr;
|
|
FileInfoRecGS fi;
|
|
ResultBuf255Ptr newn;
|
|
extern GSString255Ptr __C2GSMALLOC(char *);
|
|
|
|
new = malloc(sizeof(struct directory));
|
|
new->num_entries = argc;
|
|
new->num_bytes = 0l;
|
|
new->parent = par;
|
|
new->dir_name = NULL;
|
|
new->no_sort = 0;
|
|
new->fake = 1;
|
|
if ((argc * sizeof(DirEntryRecGS)) > 60000)
|
|
printf("exceeded ORCA pointer math bug range\n");
|
|
newents = new->entries = malloc(argc * sizeof(DirEntryRecGS));
|
|
new->entry_ptrs = malloc(argc * sizeof(DirEntryRecGS *));
|
|
fi.pCount = 12;
|
|
fi.optionList = 0l;
|
|
|
|
nptr = newents;
|
|
for (i = 0; i < argc; i++) {
|
|
new->entry_ptrs[file_count] = nptr;
|
|
nptr->pCount = 17;
|
|
nptr->optionList = NULL;
|
|
|
|
fi.pathname = __C2GSMALLOC(argv[i]);
|
|
GetFileInfoGS(&fi);
|
|
if (err=_toolErr) {
|
|
fprintf(stderr,"ls: %s: %s\n",strerror(_mapErr(err)),argv[i]);
|
|
lsexit(1);
|
|
}
|
|
nptr->name = malloc(fi.pathname->length+5);
|
|
nptr->name->bufSize = fi.pathname->length+5;
|
|
memcpy(&nptr->name->bufString,fi.pathname, fi.pathname->length+2);
|
|
nptr->name->bufString.text[fi.pathname->length] = 0;
|
|
free(fi.pathname);
|
|
|
|
if (fi.storageType == 0x05) /* extended file? */
|
|
nptr->flags = 0x8000;
|
|
else nptr->flags = 0;
|
|
nptr->fileType = fi.fileType;
|
|
/* this is used by the qsort routine to maintain
|
|
command-line ordering for directories */
|
|
if (fi.fileType == 0x0F) nptr->entryNum = i;
|
|
nptr->auxType = fi.auxType;
|
|
nptr->eof = fi.eof;
|
|
nptr->blockCount = fi.blocksUsed;
|
|
nptr->createDateTime = fi.createDateTime;
|
|
nptr->modDateTime = fi.modDateTime;
|
|
nptr->access = fi.access;
|
|
nptr->resourceEOF = fi.resourceEOF;
|
|
nptr->resourceBlocks = fi.resourceBlocks;
|
|
|
|
file_count++;
|
|
new->num_bytes += (nptr->eof + nptr->resourceEOF);
|
|
if (nptr->name->bufString.length > maxWidth)
|
|
maxWidth = nptr->name->bufString.length;
|
|
/* NOTHING AFTER THIS LINE, PLEASE */
|
|
nptr = (DirEntryRecPtrGS) ((unsigned long) nptr + sizeof(DirEntryRecGS));
|
|
}
|
|
new->width = maxWidth;
|
|
new->num_entries = file_count;
|
|
return new;
|
|
}
|
|
|
|
struct directory *readDirect(int fd,struct directory *par, ResultBuf32Ptr n)
|
|
{
|
|
struct directory *new;
|
|
unsigned i,file_count = 0,maxWidth = 0;
|
|
DirEntryRecPtrGS newents,nptr;
|
|
ResultBuf255Ptr newn;
|
|
|
|
nameb.bufSize = 36;
|
|
|
|
dirinfo.pCount = 14;
|
|
dirinfo.refNum = fd;
|
|
dirinfo.base = dirinfo.displacement = 0;
|
|
dirinfo.name = (ResultBuf255Ptr) &nameb;
|
|
GetDirEntry(&dirinfo);
|
|
new = malloc(sizeof(struct directory));
|
|
new->num_entries = dirinfo.entryNum;
|
|
new->num_bytes = 0l;
|
|
new->parent = par;
|
|
new->dir_name = n;
|
|
new->fake = 0;
|
|
if (dirinfo.fileSysID == hfsFSID) new->no_sort = 1;
|
|
else new->no_sort = 0;
|
|
if ((dirinfo.entryNum * sizeof(DirEntryRecGS)) > 60000)
|
|
printf("exceeded ORCA pointer math bug range\n");
|
|
newents = new->entries = malloc(dirinfo.entryNum * sizeof(DirEntryRecGS));
|
|
new->entry_ptrs = malloc(dirinfo.entryNum * sizeof(DirEntryRecGS *));
|
|
|
|
nptr = newents;
|
|
for (i = 0; i < new->num_entries; i++) {
|
|
new->entry_ptrs[file_count] = nptr;
|
|
nptr->pCount = 17;
|
|
nptr->refNum = fd;
|
|
nptr->base = nptr->displacement = 1;
|
|
nptr->name = (ResultBuf255Ptr) &nameb;
|
|
nptr->optionList = NULL;
|
|
GetDirEntry(nptr);
|
|
if ((fl_all) || (!(nptr->access & fileInvisible))) {
|
|
file_count++;
|
|
newn = malloc(37);
|
|
nameb.bufString.text[nameb.bufString.length] = 0;
|
|
memcpy(newn,&nameb,36);
|
|
nptr->name = newn;
|
|
new->num_bytes += (nptr->eof + nptr->resourceEOF);
|
|
if (nptr->name->bufString.length > maxWidth)
|
|
maxWidth = nptr->name->bufString.length;
|
|
/* NOTHING AFTER THIS LINE, PLEASE */
|
|
nptr = (DirEntryRecPtrGS) ((unsigned long) nptr + sizeof(DirEntryRecGS));
|
|
}
|
|
}
|
|
new->width = maxWidth;
|
|
new->num_entries = file_count;
|
|
return new;
|
|
}
|
|
|
|
int sortByType(DirEntryRecGS **a,DirEntryRecGS **b)
|
|
{
|
|
unsigned ea,eb;
|
|
|
|
if ((*a)->fileType == 0x0F) {
|
|
if ((*b)->fileType == 0x0F) {
|
|
/* if both are dirs, maintain command-line ordering */
|
|
ea = (*a)->entryNum;
|
|
eb = (*b)->entryNum;
|
|
if (ea > eb) return 1;
|
|
else if (ea == eb) return 0;
|
|
else return -1;
|
|
}
|
|
else return 1;
|
|
} else {
|
|
if ((*b)->fileType == 0x0F) return -1;
|
|
}
|
|
}
|
|
|
|
int sortByName(DirEntryRecGS **a,DirEntryRecGS **b)
|
|
{
|
|
return strcmp((*a)->name->bufString.text,(*b)->name->bufString.text) * more;
|
|
}
|
|
|
|
int sortByCreate(DirEntryRecGS **a,DirEntryRecGS **b)
|
|
{
|
|
TimeRec *time1, *time2;
|
|
|
|
time1 = &((*a)->createDateTime);
|
|
time2 = &((*b)->createDateTime);
|
|
if (time1->year > time2->year) return less;
|
|
if (time1->year < time2->year) return more;
|
|
if (time1->month > time2->month) return less;
|
|
if (time1->month < time2->month) return more;
|
|
if (time1->day > time2->day) return less;
|
|
if (time1->day < time2->day) return more;
|
|
if (time1->hour > time2->hour) return less;
|
|
if (time1->hour < time2->hour) return more;
|
|
if (time1->minute > time2->minute) return less;
|
|
if (time1->minute < time2->minute) return more;
|
|
if (time1->second > time2->second) return less;
|
|
if (time1->second < time2->second) return more;
|
|
return 0;
|
|
}
|
|
|
|
int sortByMod(DirEntryRecGS **a,DirEntryRecGS **b)
|
|
{
|
|
TimeRec *time1, *time2;
|
|
|
|
time1 = &((*a)->modDateTime);
|
|
time2 = &((*b)->modDateTime);
|
|
if (time1->year > time2->year) return less;
|
|
if (time1->year < time2->year) return more;
|
|
if (time1->month > time2->month) return less;
|
|
if (time1->month < time2->month) return more;
|
|
if (time1->day > time2->day) return less;
|
|
if (time1->day < time2->day) return more;
|
|
if (time1->hour > time2->hour) return less;
|
|
if (time1->hour < time2->hour) return more;
|
|
if (time1->minute > time2->minute) return less;
|
|
if (time1->minute < time2->minute) return more;
|
|
if (time1->second > time2->second) return less;
|
|
if (time1->second < time2->second) return more;
|
|
return 0;
|
|
}
|
|
|
|
void listDir(struct directory *d)
|
|
{
|
|
unsigned i;
|
|
unsigned dirflag;
|
|
struct directory *dd;
|
|
int fd;
|
|
int Col,Row,Height;
|
|
int j, afile = FALSE, numColumns = 1;
|
|
unsigned rWidth,width;
|
|
unsigned fakeind;
|
|
unsigned num_entries;
|
|
char *cwd;
|
|
DirEntryRecGS *entry;
|
|
|
|
num_entries = d->num_entries;
|
|
if ((fl_longOutput) && (!d->fake))
|
|
printf("total %ldk\n",(d->num_bytes / 1024l));
|
|
if (!num_entries) return;
|
|
if (d->fake && openDirectory) {
|
|
/* sort by type first */
|
|
qsort(d->entry_ptrs, num_entries, sizeof(DirEntryRecGS *), sortByType);
|
|
num_entries = 0;
|
|
while ((num_entries < d->num_entries) &&
|
|
(d->entry_ptrs[num_entries]->fileType != 0x0F))
|
|
num_entries++;
|
|
/* finally, sort the non-directory files the way they wanted */
|
|
if (num_entries)
|
|
qsort(d->entry_ptrs, num_entries, sizeof(DirEntryRecGS *), sortRoutine);
|
|
} else
|
|
if ((!fl_nosort) && (!(d->no_sort && fl_sortname)))
|
|
qsort(d->entry_ptrs, num_entries, sizeof(DirEntryRecGS *),sortRoutine);
|
|
if (!fl_longOutput && columns == CALCULATE) {
|
|
rWidth = d->width;
|
|
width = rWidth;
|
|
if (inK) width+=5;
|
|
if (idType) width++;
|
|
if (width < 15) {
|
|
rWidth = 15 - (width - rWidth);
|
|
width = 15;
|
|
} /* for jawaid :-) */
|
|
numColumns = 80 / (++width);
|
|
}
|
|
Height = (num_entries / numColumns);
|
|
if (num_entries % numColumns) Height++;
|
|
for (Row=0; Row < Height; Row ++) {
|
|
for (Col = 0; Col < numColumns; Col ++) {
|
|
i = Col * Height + Row;
|
|
if (i >= num_entries) continue;
|
|
|
|
entry = d->entry_ptrs[i];
|
|
if (entry->fileType == 0x0F) dirflag = TRUE;
|
|
/*if (mult && entry->fileType == 0x0f) continue;*/
|
|
afile = TRUE;
|
|
if (inK) printf("%4ld ",(entry->eof+entry->resourceEOF+1023)/1024);
|
|
/* time = 0 means do mod date, = 1 means do create date */
|
|
/*time = (sortRoutine == CompareCreate) ? 1 : 0;*/
|
|
if (fl_longOutput) long_out(d->entry_ptrs[i]);
|
|
printGS(&entry->name->bufString);
|
|
if (idType) {
|
|
if (entry->fileType == 0x0f) putchar('/');
|
|
else if ((entry->fileType == 0xff) || (entry->fileType == 0xb5) ||
|
|
(entry->fileType == 0xb3) ||
|
|
((entry->fileType == 0xb0) && (entry->auxType == 6l)))
|
|
putchar('*');
|
|
else putchar(' ');
|
|
}
|
|
if (Col+1 < numColumns) {
|
|
for (j=0;j<(rWidth-(int)(entry->name->bufString.length));j++)
|
|
putchar(' ');
|
|
putchar(' ');
|
|
}
|
|
}
|
|
putchar('\n');
|
|
}
|
|
|
|
if ((dirflag && fl_recursive) || (d->fake && openDirectory)) {
|
|
unsigned start_ent = (d->fake ? num_entries : 0);
|
|
|
|
/* if there were regular files in a command-line list,
|
|
put a blank line in there but don't count as first */
|
|
if (d->fake && (num_entries != 0)) {
|
|
putchar('\n');
|
|
}
|
|
for (i = start_ent; i < d->num_entries; i++) {
|
|
if (d->entry_ptrs[i]->fileType == 0x0F) {
|
|
ResultBuf32Ptr p;
|
|
|
|
p = (ResultBuf32Ptr) d->entry_ptrs[i]->name;
|
|
/* let the danged kernel do some of the work! */
|
|
if (fl_recursive) {
|
|
cwd = malloc(1024l);
|
|
getwd(cwd);
|
|
chdir(p->bufString.text);
|
|
fd = open(".",O_RDONLY);
|
|
}
|
|
else fd = open(p->bufString.text,O_RDONLY);
|
|
if (fd <= 0) {
|
|
perror("could not open");
|
|
exit(1);
|
|
}
|
|
dd = readDirect(fd,d,p);
|
|
close(fd);
|
|
/* print the directory name for recursive listings */
|
|
if (!firstflag) firstflag = 1;
|
|
else putchar('\n');
|
|
printDirName(dd,0);
|
|
putchar('\n');
|
|
listDir(dd);
|
|
if (fl_recursive) {
|
|
chdir(cwd);
|
|
free(cwd);
|
|
}
|
|
/* DISPOSE of 'dd' at this point */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int fd;
|
|
struct directory *dd;
|
|
TimeRec curtime;
|
|
int ch;
|
|
extern int getopt_restart(void);
|
|
|
|
getopt_restart();
|
|
curtime = ReadTimeHex();
|
|
curYear = curtime.year + 1900;
|
|
|
|
if (isatty(STDOUT_FILENO) && (!strncmp(ttyname(STDOUT_FILENO),".ttyco",6)))
|
|
strcpy(FTTable[0].rep,"\x1B\xFXY\xE\x18 ");
|
|
else strcpy(FTTable[0].rep,"DIR "); /* we restart, remember? */
|
|
|
|
/* initialize our restartable global variables */
|
|
fl_nosort = 0; fl_reverse = 0;
|
|
more = 1; less = -1;
|
|
sortRoutine = sortByName;
|
|
columns = CALCULATE;
|
|
openDirectory = TRUE;
|
|
fl_all = FALSE;
|
|
fl_longOutput = FALSE;
|
|
inK = FALSE;
|
|
dirOnly = FALSE;
|
|
idType = FALSE;
|
|
fl_recursive = FALSE;
|
|
whichTime = 0;
|
|
firstflag = 0;
|
|
|
|
while ((ch = getopt(argc, argv, "acdflnqrst1CFR")) != EOF) {
|
|
switch(ch) {
|
|
case 'n' :
|
|
fl_nosort = TRUE;
|
|
break;
|
|
case 'r' :
|
|
more = -1; less = 1;
|
|
break;
|
|
case 't' :
|
|
sortRoutine = sortByMod;
|
|
whichTime = 0;
|
|
break;
|
|
case 'c' :
|
|
sortRoutine = sortByCreate;
|
|
whichTime = 1;
|
|
break;
|
|
case '1' :
|
|
columns = ONLYONE;
|
|
break;
|
|
case 'C' :
|
|
columns = CALCULATE;
|
|
break;
|
|
case 'a' :
|
|
fl_all = TRUE;;
|
|
break;
|
|
case 'l' :
|
|
fl_longOutput = TRUE;
|
|
break;
|
|
case 's' :
|
|
inK = TRUE;
|
|
break;
|
|
case 'f' :
|
|
dirOnly = TRUE;
|
|
fl_longOutput = FALSE;
|
|
inK = FALSE;
|
|
fl_all = TRUE;
|
|
break;
|
|
case 'q' :
|
|
break;
|
|
case 'F' :
|
|
idType = TRUE;
|
|
break;
|
|
case 'R' :
|
|
fl_recursive = TRUE;
|
|
break;
|
|
case 'd' :
|
|
openDirectory = FALSE;
|
|
fl_recursive = FALSE;
|
|
break;
|
|
default:
|
|
(void)fprintf(stderr,
|
|
"usage: ls [-acdfilnqrstu1ACLFR] [name ...]\n");
|
|
lsexit(1);
|
|
}
|
|
}
|
|
argv += optind;
|
|
argc -= optind;
|
|
|
|
if (!argc) {
|
|
fd = open(".",O_RDONLY);
|
|
if (fd <= 0) {
|
|
perror("ls:");
|
|
exit(1);
|
|
}
|
|
dd = readDirect(fd,NULL,NULL);
|
|
close(fd);
|
|
listDir(dd);
|
|
} else { /* files as arguments */
|
|
/* add the args to a special list one by one with GetFileInfo */
|
|
dd = fakeDirect(argc, argv, NULL);
|
|
if (dd->num_entries) listDir(dd);
|
|
}
|
|
lsexit(0);
|
|
}
|