home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gold Fish 1
/
GoldFishApril1994_CD2.img
/
d4xx
/
d429
/
dr
/
source
/
dr.c
next >
Wrap
C/C++ Source or Header
|
1991-01-10
|
34KB
|
1,279 lines
/* TO DO:
Make -o intermix dirs and files instead of separating? Not when -r though!
Some way to make -x info follow -r info when both are specified?
Option to choose between absolute dates and "Yesterday" etc.
Lformat type option. Option to execute that output instead of printing it.
Upto/Since date options. -K option to show disk keys.
Make -P able to handle more complex bit tests.
Organize .infos some way so that .info search when using chron sort is faster?
Disk key sort when -o? No, unsorted. Option to sort alpha/cron?
Have an option that means apply pattern to all depths of -r. Like, foo/???.c
also acts like foo/#?/???.c and foo/#?/#?/???.c, etc. Make it use
:: after the pattern (foo/#?.c::) to mean this?
Make ~ in front of pattern accept all files that DO NOT fit.
Handle wildcards anywhere in the path (low priority). Handling :: may be
tough. Allow :: only at very end. Allow ~ in middle.
Avoid repeating "unknown option" for same letter?
-A means show age of file? #ifdef'd out?
Later: use two processes on same seglist: background scans disk, sends
flist(s) to foreground which does output.
Wow, in testing this ... I found that as of July 18, 1990, my hard disk
contains 1944 files in 152 directories. A mere 20 megger!
*/
/* ==========================================================================
The idea here is to make Yet Another Cli Directory Command. What's special
about this one? It's really really fast, and it doesn't show .info files!
Instead it just shows all files that have .info's associated with them by
writing the name in orange instead of white when output is to the screen.
And it puts file names (and directory names also) in as many columns (up to
five) as will fit comfortably in the window. And everything is alphabetized
in columns. It will also do Amiga patterns. It tries a pattern first as an
exact literal before expanding it, in case you have a drawer named
"Doesn't work?" or something. And it does recursive descents, faster than
the Fast File System. It is intended to replace Dir, List, rls, and du.
Future versions might even replace things like foreach and SPAT/DPAT.
Dr is written for Aztec C for Amiga, by Paul Kienitz. Public Domain. See
the files Dr.doc and FastExNext.doc for useful information. Documentation
for pureio.c is in the source.
========================================================================== */
/* some #defines which you can make different verisons with:
if SMALLSLOW is defined it uses regular ExNext instead of FastExNext.
if WEEEEK is defined it gives recent dates as "Yesterday" or
"Tuesday" instead of an absolute date.
if LEAKAGE is defined it reports all memory allocations and freeings.
if C_NOT_ASM it does not use inline assembly language instead of C
for some sorting functions (works only with Aztec).
In fastex.c, if QUEST is defined it has the ability to put up "Please
replace volume ..." and "... read/write error" system requesters
when an error occurs; otherwise it reports errors silently.
*/
#define FLACK ':' /* NOT USED */
#define WIDEFAULT 77
#define HYPH 0xAD
#define CSI "\233"
#define LWID 25
#define MAXCOLS 5
#define PREPENGTH 300
#define PUREBUFSIZE 128L
#define STACKNEEDED (1500 + 300)
/* WIDEFAULT is the assumed output width when we can't measure the window.
FLACK is char used to mark icon'd files in output. Not used these days.
HYPH is used to mark option arguments for mane (8-bit ascii soft hyphen).
CSI is the character that starts "escape sequences"; same as esc [.
LWID is how many spaces (ideally) to use for name and size in -L output.
MAXCOLS is the max number of text columns to stack listed names in.
PREPENGTH is the maximum length of pathnames labelling recursive levels.
PUREBUFSIZE is the size of the buffer for pureio.
STACKNEEDED is the amount of stack space needed to scan a directory.
*/
#include <exec/io.h>
#include <exec/memory.h>
#include <libraries/dosextens.h>
#include <string.h>
#include <Paul.h>
#undef put
/* Here we have a simplified version of <devices/conunit.h> which does not
pull in stuff like struct Window and struct TextFont: */
struct ConUnit {
short pad[21];
short cu_XMax, cu_YMax;
};
/* pretty simplified, wasn't it */
typedef struct _fly *flip;
typedef struct _fly {
flip next; /* list link */
long length, blox, tection;
struct DateStamp when;
str comment;
ubyte /* bool */ jected, infoed, wanted, dirred;
char name[31];
ubyte size; /* either 70 or 80 */
short ordination; /* consumption rounds up to 72 anyway... */
} fly;
typedef struct {
struct FileInfoBlock f;
long p, s;
} fib;
struct cuont {
long blok, byt, fil, dir, jb;
};
/* Here is the stuff that would be global variables if we weren't reentrant: */
typedef struct {
adr stacklimit;
str argline;
stray argv;
int arglen, argc, hyphc;
short cwid, song, cols, wid, abort;
bool color, curse, cize, complete, cron, cons, colsort, cutdirs, cutfils;
bool /* cage, canydepth, */ csternal, completenames, consumption;
bool zize, zomplete, /* zons, FLACK */ zurse, zutdirs, zutfils, zmptnames;
long protlook, protwant;
struct cuont tot, gran;
struct ConUnit *cuca;
struct Process *me;
long hair;
short purestuff[16]; /* pad in case changes */
int mesh[128];
str pat;
bool patty, didaninny;
char prepath[PREPENGTH];
char pat0, nullpat;
} glob;
#define GG register glob *g
/* the only global variables visible here: */
import int Enable_Abort; /* has to stay always 1 */
long goofset; /* works like boofset in pureio */
/* there are actually others, like library bases and pureio's boofset, but
they are all constants, not variables. */
str helpslab[] = {
" -C means sort oldest to newest, not alphabetically\n",
" -D means show subdirectory names only\n",
" -F means show file names only\n",
" -H means sort in rows instead of columns\n",
" -I means show .info files like normal files\n",
/* " -K means show disk addresses (keys) of files and directories\n", */
" -L means show size, protection, datestamp, and filenote\n",
" -O means list each file on a separate line as a complete pathname\n",
" -R means recursively show subdirectory contents\n",
" -S means show length of each file (-L overrides)\n",
" -U means list no names, just show total disk space consumption\n",
" -X means show directory's info -L style instead of its contents\n",
" -Pb or -P~b where b is one of H S P A R W E D means show files with\n",
" the named protection bit clear (if ~ present) or set (if no ~)\n",
null
};
/* ================== functions: ================== */
#ifdef SMALLSLOW
#define FastExamine(L, F) Examine(L, (struct FileInfoBlock *) F)
#define FastExNext(L, F) ExNext(L, (struct FileInfoBlock *) F)
#define FastExCleanup(F)
#define Get80(F) Alloc(80)
#else
import long FastExamine(BPTR l, fib *f), FastExNext(BPTR l, fib *f);
import void FastExCleanup(fib *f);
import adr Get80(fib *f);
#endif
#ifdef LEAKAGE
import adr AllocYell(long a, long b, str c, long d);
import void FreeYell(adr a, long b, str c, long d);
#define AllocMem(a, b) AllocYell((long) a, (long) b, __FUNC__, (long) __LINE__)
#define FreeMem(a, b) FreeYell(a, (long) b, __FUNC__, (long) __LINE__)
#define _AllocMem(a, b) AllocYell((long) a, (long) b, __FUNC__, (long) __LINE__)
#define _FreeMem(a, b) FreeYell(a, (long) b, __FUNC__, (long) __LINE__)
#endif
import bool CmplPat(str pat, int *aux), /* PatMatch by Jeff Lydiat */
Match(str pat, int *aux, str S);
import bool OpenPureIO(short *b, ulong s);
import void ClosePureIO(void), puch(short c), put(str s),
putfmt(str f, ...), pflush(void);
long StackLeft(GG)
{
int i;
return (long) &i + 14 - (long) g->stacklimit;
}
short digits(register ulong l)
{
register short d = 0;
while (l) {
d++;
l /= 10;
}
if (d) return d; else return 1;
}
void pad(short w)
{
while (--w >= 0)
puch(' ');
}
void padong(ulong n, short w)
{
pad(w - digits(n)); /* putfmt doesn't have %*ld */
putfmt("%ld", n);
}
/* putfmt doesn't have %lu either, so let's just hope n is always positive */
void putection(register ulong bits)
{
char bee[10];
register short b;
bits ^= 15;
strcpy(bee, " hsparwed");
for (b = 0; b <= 7; b++)
if (!(bits & bit(b)))
bee[8 - b] = '-';
put(bee);
}
void putdate(struct DateStamp *when)
{
long day = when->ds_Days;
register short yell, mday, month, year;
short hour = (short) when->ds_Minute / 60,
minute = (short) when->ds_Minute % 60,
second = (short) when->ds_Tick / (short) TICKS_PER_SECOND;
static short smods[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
short mods[12];
static char mane[] = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0"
"Sep\0Oct\0Nov\0Dec";
#ifdef WEEEEK
/* betcha the reason they started the calendar from the beginning of
1978 in particular is because that way day zero is a Sunday */
static str weak[7] = { "Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday" };
struct DateStamp today;
short daydif;
DateStamp(&today);
daydif = today.ds_Days - day;
if (!daydif)
put(" Today ");
else if (daydif == 1)
put(" Yesterday");
else if (daydif > 1 && daydif < 7)
putfmt(" %-9s", weak[day % 7]);
else
#endif
{
if (day <= 0 || day >= 44618)
putfmt(" ?(%ld)", day);
else {
year = 78 + ((day / 1461) << 2);
mday = day % 1461;
while (mday >= (yell = (year & 3 ? 365 : 366))) {
mday -= yell;
year++;
}
/* 2000 is a leap year?, 1900 and 2100 are <invalid> */
for (month = 0; month < 12; month++)
mods[month] = smods[month];
if (!(year & 3)) mods[1] = 29;
month = 0;
while (mday >= mods[month])
mday -= mods[month++];
putfmt(" %2d-%s-%02d", mday + 1, mane + (month << 2), year % 100);
}
}
if (hour < 0 || hour >= 24)
putfmt(" ?(%ld,%ld)", when->ds_Minute, when->ds_Tick);
else {
putfmt(" %2d:%02d:", hour, minute);
if (second < 0 || second >= 60)
put("??");
else
putfmt("%02d", second);
}
}
/* Another personal ad found in EXPRESS "The East Bay's Free Weekly":
HI.
Yup, that was the whole ad. Right after it:
RALPH, a 1967 Cadillac, now accepting devotees.
*/
void Lose(register flip f)
{
if (f) {
if (f->comment)
FreeMem(f->comment, 80L);
FreeMem(f, (long) f->size);
}
}
void _abort(void) /* called by ^C checker */
{ /* is this a hack, or is this a hack? */
GG = (adr) (((str) ThisProcess()->pr_ReturnAddr) + goofset);
if (g->abort) return;
g->abort = 5;
put(" *** BREAK\n");
}
/* This is more efficient that a series of &&'s: */
#define bask(C) bit(C - 33)
#define PASK (bask('?') | bask('\'') | bask('#') | bask('%') | \
bask('(') | bask(')'))
bool patchar(register ubyte cc)
{
register ubyte c = cc;
return c > ' ' && (c == '|' || (c < 'A' && bit(c - 33) & PASK));
}
/* Takes dir/pat string in from and translates it into a compiled pattern in
mesh (and uncompiled in pat) and a lock on the directory in deer to be
scanned using the pattern */
bool SplitTailPat(str from, BPTR *deer, GG)
{
register str p;
register bool f = true;
g->pat = from;
for (p = from; *p; p++)
if (*p == ':' || *p == '/')
g->pat = p + 1; /* pat will point to path tail */
for (p = g->pat; *p; p++)
if (patchar(*p)) {f = false; break;} /* f is for fucked */
if (f) {
putfmt("Couldn't find \"%s\".\n", from);
g->hair = ERROR_OBJECT_NOT_FOUND;
return false;
}
g->pat0 = *g->pat; /* truncate from by temporarily damaging pat */
/* should change CmplPat to be more forgiving of unmatched parens and empty
vertical-bar halves, but I don't understand how it works yet ****/
if (!CmplPat(g->pat, g->mesh)) {
putfmt("Bogus pattern \"%s\".\n", g->pat);
return false;
}
*g->pat = 0;
#ifdef UNNECESSARY
for (p = from; *p; p++) /* truncate final slash in from */
if (*p != '/') f = true; /* if it contains non-slashes */
if (f && *(--p) == '/') *p = 0;
#endif
if (!(*deer = RLock(from))) {
putfmt("Couldn't find directory \"%s\".\n", from);
g->hair = ERROR_OBJECT_NOT_FOUND;
return false;
}
return true;
}
void HairSpray(register long hair)
{
if (hair == ERROR_DEVICE_NOT_MOUNTED)
put("disk removed from drive.\n");
else if (hair == ERROR_NO_FREE_STORE)
put("not enough memory.\n");
else if (hair == ERROR_NOT_A_DOS_DISK)
put("disk unreadable.\n");
else if (hair == ERROR_TOO_MANY_LEVELS)
put("not enough stack space.\n");
else
putfmt("DOS error code %ld.\n", hair);
}
flip Fly(register fib *b)
{
register flip z = New(fly);
if (!z) {
z = Get80(b);
if (!z) return null;
z->size = 80;
} else z->size = sizeof(fly);
if (*b->f.fib_Comment)
z->comment = Get80(b); /* if alloc fails, fuck it */
else z->comment = null;
if (z->comment)
strcpy(z->comment, b->f.fib_Comment);
z->next = null;
z->length = b->f.fib_Size;
z->blox = b->f.fib_NumBlocks;
z->tection = b->f.fib_Protection;
z->when = b->f.fib_Date;
strcpy(z->name, b->f.fib_FileName);
z->dirred = b->f.fib_DirEntryType > 0;
z->wanted = z->jected = z->infoed = false;
return z;
}
flip Scan1(short *ficou, fib *deef, BPTR deer, GG)
{
flip result = null;
BPTR dp, ocd, fo;
char fone[31];
if (!(result = Fly(deef))) return null;
if (!g->curse)
g->zomplete = true;
*ficou = 1; /* we're seeing if it has a .info file */
if (!g->cons && strlen(deef->f.fib_FileName) < 26 && (dp = ParentDir(deer))) {
ocd = CurrentDir(dp);
strcpy(fone, result->name);
strcat(fone, ".info");
Chk_Abort();
if (!g->abort && (fo = RLock(fone))) {
result->infoed = true;
UnLock(fo);
}
CurrentDir(ocd);
UnLock(dp);
}
/* WE SEEM to be running into an undocumented feature here... the
ding bling ParentDir function sometimes sets IoErr = 212 after a
perfectly normal and SUCCESSFUL call. When it does this, the
following RLock also does so. So we just band-aid it: */
if (g->me->pr_Result2 == ERROR_OBJECT_NOT_FOUND
|| g->me->pr_Result2 == ERROR_OBJECT_WRONG_TYPE)
g->me->pr_Result2 = 0;
return result;
}
flip ScanInside(short *ficou, fib *deef, BPTR deer, GG)
{
flip result = null, more;
long air = 0;
bool pokey;
void DoInner(str n, BPTR l, fib *f, GG);
/* vvvv prevent multiple "please replace" in -R */
while (g->hair != ERROR_DEVICE_NOT_MOUNTED && FastExNext(deer, deef)) {
Chk_Abort();
if (g->abort || !(more = Fly(deef))) break;
*g->pat = g->pat0;
if (g->zutdirs & more->dirred || g->zutfils & ~more->dirred
|| (more->tection & g->protlook) != g->protwant
|| !(pokey = !g->patty
|| Match(g->pat, g->mesh, more->name)))
more->jected = true;
if (pokey && g->curse && more->dirred) {
BPTR ocd = CurrentDir(deer), innerdeer;
if (innerdeer = RLock(more->name)) {
register bool p = g->patty;
g->patty = false;
*g->pat = 0;
DoInner(more->name, innerdeer, deef, g);
g->patty = p;
UnLock(innerdeer);
} else {
air = g->me->pr_Result2;
putfmt(
"Can't lock inner directory \"%s\"! Probably no memory.\n", more->name);
}
CurrentDir(ocd);
}
more->next = result;
result = more;
(*ficou)++;
}
if (air) g->me->pr_Result2 = air;
if (g->me->pr_Result2 == ERROR_NO_MORE_ENTRIES)
g->me->pr_Result2 = 0;
return result;
}
/* scans a Dos directory and returns the findings in a linked list of fly's.
*ficou tells the number of elements in the list. */
flip ScanDeer(BPTR deer, short *ficou, fib *parent, GG)
{
flip result = null, more;
register fib *deef;
*ficou = 0;
if (!(deef = New(fib))) {
g->me->pr_Result2 = ERROR_NO_FREE_STORE;
return null;
}
if (parent)
deef->s = parent->s;
else
deef->s = g->curse ? -1 : 0;
if (FastExamine(deer, deef)) {
Chk_Abort();
if (!g->abort)
if (deef->f.fib_DirEntryType < 0 ||
(g->csternal && !(g->patty | g->curse))) {
result = Scan1(ficou, deef, deer, g); /* don't look inside */
if (!parent)
g->zurse = g->zutfils = g->zutdirs = false;
} else
result = ScanInside(ficou, deef, deer, g);
}
if (g->me->pr_Result2) {
g->hair = g->me->pr_Result2;
put("DR COULDN'T FINISH SCAN; ");
HairSpray(g->hair);
}
if (!parent)
FastExCleanup(deef);
Free(fib, deef);
if (g->abort)
while (result) {
more = result;
result = result->next;
Lose(more);
}
return result;
}
#ifdef C_NOT_ASM /* redo these in assembly for speed */
short alpha(register ubyte *a, register ubyte *b)
{
register char ac, bc;
do {
ac = toupper(*(a++));
bc = toupper(*(b++));
} while (ac && ac == bc);
return ac - bc;
}
short alpo(flip a, flip b)
{
return alpha(a->name, b->name);
}
#else
/* these optimized versions make a noticeable difference when sorting a
directory with 75 files or more */
#asm
public _alpha
public _alpo
_alpo: move.l 4(sp),a0 ; first arg
move.l 8(sp),a1 ; second arg
lea 36(a0),a0 ; first->name
lea 36(a1),a1 ; second->name
bra alfa ; call-and-return alpha
_alpha: move.l 4(sp),a0 ; first arg
move.l 8(sp),a1 ; second arg
alfa: moveq #0,d0
moveq #0,d1
nxt: move.b (a0)+,d0
move.b (a1)+,d1
beq out ; end of string
cmp.b d0,d1
beq nxt ; exactly equal chars
cmp.b #'Z',d0 ; tolower(*a)
bgt noupa
cmp.b #'A',d0
blt noupa
add.b #'a'-'A',d0
noupa: cmp.b #'Z',d1 ; tolower(*b)
bgt noupb
cmp.b #'A',d1
blt noupb
add.b #'a'-'A',d1
noupb: cmp.b d0,d1
beq nxt ; equal after tolower
out: sub.w d1,d0 ; compare as unsigned bytes
rts
#endasm
import short alpha(ubyte *a, ubyte *b), alpo(flip a, flip b);
#endif
short olda(register flip a, register flip b)
{
register long t;
if (t = a->when.ds_Days - b->when.ds_Days)
return t;
if (t = a->when.ds_Minute - b->when.ds_Minute)
return t;
return a->when.ds_Tick - b->when.ds_Tick;
}
/* the C code generation does perfectly well with olda */
#ifdef C_NOT_ASM
short infoo(ubyte *a, ubyte *b)
/* returns 0 if filename b is the .info of filename a, positive if b is
alphabetically after a's .info name, negative if before. */
{
char acat[37];
register short f = (*a | 0x20) - (*b | 0x20);
if (f) return f;
strcpy(acat, a);
strcat(acat, ".info");
return alpha(b, acat);
}
#else
#asm
public _infoo
_infoo: move.l 4(sp),a1 ; first arg
move.l 8(sp),a0 ; second arg
infu: move.b (a1),d0
move.b (a0),d1
bset #5,d0 ; primitive tolower()
bset #5,d1
sub.b d1,d0 ; quick pre-test
beq check
ext.w d0
rts
check: move.l a2,-(sp) ; the real test
link a5,#-40
move.l sp,a2 ; temporary copy area
cpy: move.b (a1)+,(a2)+
bne cpy
move.b #'.',-1(a2) ; overwrite final nul
move.b #'i',(a2)+
move.b #'n',(a2)+
move.b #'f',(a2)+
move.b #'o',(a2)+
clr.b (a2)
move.l sp,a1
bsr alfa
unlk a5
move.l (sp)+,a2
rts
#endasm
import short infoo(ubyte *a, ubyte *b);
#endif
flip Soart(flip flist, GG)
{
short (*sortie)(flip a, flip b) = (g->cron ? &olda : &alpo);
bool crudecons = g->cron | g->zmptnames;
if (!flist) return null;
if (!g->zmptnames) {
register flip *head, *dot, *foist, t; /* last 2 get D regs */
for (head = &flist; (*head)->next; head = &(*head)->next) {
foist = head;
for (dot = &(*head)->next; *dot; dot = &(*dot)->next)
if (sortie(*foist, *dot) > 0)
foist = dot;
t = *foist;
*foist = (*foist)->next;
t->next = *head;
*head = t;
}
}
if (!g->cons) {
register flip t, tt;
register short k;
for (t = flist; t; t = t->next)
for (tt = (crudecons ? flist : t->next); tt; tt = tt->next)
if (!(k = infoo((ubyte *) t->name, (ubyte *) tt->name))) {
if (!tt->dirred) {
t->infoed = true;
if (!t->jected) tt->jected = true;
}
break;
} else
if (!crudecons && k > 0) break;
}
return flist;
}
short CheckWindowWidth(GG)
{
import long dos_packet(struct MsgPort *p, long c, ...);
struct InfoData *ind; /* ^^^ a Manx convenience */
adr cont = g->me->pr_ConsoleTask;
if (!g->cuca)
if (g->color && cont && (ind = NewP(struct InfoData))) {
if (dos_packet(cont, ACTION_DISK_INFO, (long) ind >> 2))
g->cuca = (adr) ((struct IOStdReq *) ind->id_InUse)->io_Unit;
else g->color = false;
Free(struct InfoData, ind);
} else g->color = false;
return g->color ? g->cuca->cu_XMax + 1 : WIDEFAULT;
}
/* I know, there's an escape sequence. But it don't work without you does
set_raw, which does dos_packet, and it comes out smaller this way. */
void Columnate(flip flist, short ficou, GG)
{
short n;
register flip f;
/* bool oneinfo = false; FLACK */
g->song = 1;
for (f = flist; f; f = f->next)
if (!f->jected) {
if ((n = digits(f->length)) > g->song)
g->song = n;
} else /* Assumes 512-byte blocks! vvvv ****/
g->tot.jb += (f->dirred ? 1 : f->blox + f->blox / 72 + 1);
if (g->consumption) return;
g->cwid = 1;
if (!g->wid)
g->wid = CheckWindowWidth(g);
for (f = flist; f; f = f->next)
if (!f->jected) {
n = strlen(f->name) + 1 /* 2 - g->cons */ ; /* FLACK */
if (f->dirred) n++;
else if (g->zize) n += g->song + 1;
if (n > g->cwid) g->cwid = n;
/* if (f->infoed) oneinfo = true; FLACK */
}
/* g->zons = g->cons;
if (!(g->cons | oneinfo)) {
g->zons = true;
if (!--g->cwid) g->cwid = 1;
} FLACK */
if (g->zomplete) {
n = g->wid - 28;
if (g->cwid > n) g->cwid = n; /* not enough space */
if (n > LWID) n = LWID; /* use at least LWID if possible */
if (n > g->cwid) g->cwid = n;
} else {
g->cols = g->wid / g->cwid;
if (g->cols > MAXCOLS) g->cols = MAXCOLS;
/* if (g->cols > ficou) g->cols = ficou; */ /* on second thought, nah */
if (!g->cols) g->cols = 1; /* window too narrow */
g->cwid = g->wid / g->cols; /* share extra space evenly */
}
}
void Cough1(register flip y, bool dirs, short *col, GG)
{
register short h, lused = strlen(y->name);
bool icon = g->color & y->infoed;
char p;
if (dirs) {
g->tot.blok++;
g->tot.dir++;
} else {
g->tot.byt += y->length;
g->tot.fil++;
g->tot.blok += y->blox + y->blox / 72 + 1;
} /* this assumes ^^^^ 512-byte block size! ****/
if (g->consumption) return;
if (g->zize && !g->zomplete && !g->completenames) {
if (dirs) pad(g->song);
else padong(y->length, g->song);
puch(' ');
lused += g->song + 1;
}
/* if (!g->zons) {
puch(y->infoed ? FLACK : ' ');
lused++;
} */
if (icon) put(CSI "33m");
if (g->completenames) {
put(g->prepath);
if (*g->prepath) {
register short l = strlen(g->prepath);
/* HEY, I just discovered another Aztec 3.6a compiler bug. If you leave */
/* out the line above and go "p = g->prepath[strlen(g->prepath) - 1];" */
/* where strlen is #defined as _BUILTIN_strlen, it generates code that */
/* uses the special non-existent 68000 instruction "ext.l a0". */
p = g->prepath[l - 1];
if (p != '/' && p != ':')
puch('/');
}
}
put(y->name);
if (icon) put(CSI "31m");
if (y->dirred) {
puch('/');
lused++;
}
h = g->cwid - lused;
if (g->completenames)
puch('\n');
else if (!g->zomplete) {
if (++*col >= g->cols) {
puch('\n');
*col = 0;
} else
pad(h);
} else {
if (h > digits(y->length))
if (dirs) pad(h);
else padong(y->length, h);
else {
puch('\n');
if (dirs) pad(g->cwid);
else padong(y->length, g->cwid);
}
putection(y->tection);
putdate(&y->when);
puch('\n');
if (y->comment)
putfmt(": %s\n", y->comment);
}
}
bool CoughHalf(flip flist, bool dirs, GG)
{
short h, col = 0, n = 0;
bool anyleft = false;
register flip y;
if (!flist) return false;
for (y = flist; y; y = y->next)
if (!y->jected)
if (!(dirs ^ y->dirred)) {
y->wanted = true;
n++;
} else {
y->wanted = false;
anyleft = true;
}
/* else wanted is always false */
if (!n) return false;
h = 0;
for (y = flist; y; y = y->next)
if (y->wanted) {
y->ordination = h;
if (g->colsort) {
if ((h += g->cols) >= n)
h = h % g->cols + 1;
} else h++;
} else y->ordination = -1;
y = flist;
for (h = 0; h < n; h++) {
while (y->ordination != h)
if (!(y = y->next)) y = flist; /* a bit clumsy... */
Chk_Abort();
if (g->abort) break;
Cough1(y, dirs, &col, g);
}
if (col > 0) puch('\n');
return anyleft && !g->abort && !g->zutfils;
}
void plural(str s, long n)
{
putfmt(s, n, (n == 1 ? 0 : 's'));
}
void Tote(register struct cuont *c)
{
plural("%ld dir%c ", c->dir);
plural("and %ld file%c ", c->fil);
plural("with %ld byte%c ", c->byt);
plural("use %ld block%c ", c->blok);
putfmt("(of %ld).\n", c->blok + c->jb);
}
void CoughUp(flip flist, short ficou, GG)
{
flip p, t;
if (g->zurse && !g->abort && !g->zmptnames) {
if (g->didaninny && !g->consumption) puch('\n');
if (flist)
putfmt(" ------- \"%s\":\n", g->prepath);
else putfmt(" ------- \"%s\" is empty.\n", g->prepath);
g->didaninny = true;
}
if (!flist) return;
g->tot.blok = g->tot.byt = g->tot.fil = g->tot.dir = g->tot.jb = 0;
g->cols = 1; /* default */
if (!g->zmptnames)
Columnate(flist, ficou, g);
#ifdef STUPID_DASHES
if (!g->zutdirs && CoughHalf(flist, true, g)
&& !g->consumption && !g->completenames) {
short i;
put(" ");
for (i = 0; i < g->wid - 8; i++)
puch('-');
puch('\n');
}
#else
if (!g->zutdirs) {
#ifdef BLACKGROUND
if (g->color) put(CSI "42m" CSI "J");
#endif
#ifdef BOLDDIRS
if (g->color) put(CSI "1m");
#endif
CoughHalf(flist, true, g);
#ifdef BLACKGROUND
if (g->color) put(CSI "0m" CSI "J");
#endif
#ifdef BOLDDIRS
if (g->color) put(CSI "0m");
#endif
}
#endif
Chk_Abort();
if (!g->zutfils && !g->abort)
CoughHalf(flist, false, g);
for (p = flist; p; p = t) {
t = p->next;
Lose(p);
}
if (g->abort || g->zmptnames) return;
if (((g->complete | g->cize) && g->tot.fil > 1) || g->consumption)
Tote(&g->tot);
else if ((g->zomplete | g->cize) && g->tot.fil)
plural("%ld block%c used.\n", g->tot.blok);
g->gran.blok += g->tot.blok;
g->gran.byt += g->tot.byt;
g->gran.fil += g->tot.fil;
g->gran.dir += g->tot.dir;
g->gran.jb += g->tot.jb;
}
void DoInner(str what, BPTR deer, fib *parent, GG)
{
flip filist;
short ficou, prength = strlen(g->prepath), preng1 = prength;
char pe = g->prepath[prength - 1];
if (!g->abort && StackLeft(g) < STACKNEEDED) {
putfmt("Cannot scan \"%s\" -- insufficient stack space!\n", what);
g->hair = ERROR_TOO_MANY_LEVELS;
/* dammit, my books don't define what TOO_MANY_LEVELS means, or */
/* when it is appropriate to set it! */
return;
}
if (prength && pe != ':' && pe != '/') {
g->prepath[prength++] = '/';
g->prepath[prength] = 0;
}
if (prength < PREPENGTH - 30)
strcpy(g->prepath + prength, what);
g->zize = g->complete || (g->cize && !g->cutfils);
g->zomplete = g->complete;
g->zurse = g->curse;
g->zutfils = g->cutfils;
g->zutdirs = g->cutdirs;
filist = ScanDeer(deer, &ficou, parent, g);
if (!filist && !g->abort && (g->me->pr_Result2 == ERROR_NO_FREE_STORE)) {
putfmt("Not enough memory to scan \"%s\"!\n", what);
g->hair = ERROR_NO_FREE_STORE;
}
g->zize |= g->zomplete;
if (!g->consumption)
filist = Soart(filist, g);
if (!g->abort)
CoughUp(filist, ficou, g);
g->prepath[preng1] = 0; /* remove tail just added */
}
void Do(str what, GG)
{
BPTR deer;
g->pat = &g->nullpat;
if (g->patty = !(deer = RLock(what)))
if (!SplitTailPat(what, &deer, g))
return;
if (!deer) {
putfmt("Couldn't find \"%s\".\n", what);
g->hair = ERROR_OBJECT_NOT_FOUND;
return;
}
g->gran.blok = g->gran.byt = g->gran.fil = g->gran.dir = g->gran.jb = 0;
*g->prepath = 0;
g->didaninny = false;
g->zmptnames = g->completenames && !g->consumption;
DoInner(what, deer, null, g);
UnLock(deer);
if (!g->abort && (g->complete || g->consumption || g->cize)
&& g->gran.blok > g->tot.blok) {
put("\nTotal: ");
Tote(&g->gran);
}
}
#define lower(C) ((C) | 0x20)
void Opt(register str p, GG)
{
register char c;
for (c = lower(*++p) ; c > ' '; c = lower(*++p)) {
switch (c) {
case 'r': { g->curse ^= true; continue; }
case 'i': { g->cons ^= true; continue; }
case 's': { g->cize ^= true; continue; }
case 'c': { g->cron ^= true; continue; }
case 'l': { g->complete ^= true; continue; }
case 'h': { g->colsort ^= true; continue; }
case 'f': { g->cutdirs ^= true; g->cutfils = false; continue; }
case 'd': { g->cutfils ^= true; g->cutdirs = false; continue; }
case 'x': { g->csternal ^= true; continue; }
case 'o': { g->completenames ^= true; continue; }
case 'u': { g->consumption ^= true; continue; }
/* case 'a': { g->cage ^= true; continue; } */
/* case 'e': { g->canydepth ^= true; continue; } */
case 'p': {
register bool tilt = false;
register short b = -1;
g->protlook = g->protwant = 0;
c = lower(p[1]);
if (c == '~') {
tilt = true;
c = lower((++p)[1]);
}
if (c <= ' ')
continue;
else p++;
switch (c) {
case 'h': b = 7; break;
case 's': b = 6; break;
case 'p': b = 5; break;
case 'a': b = 4; break;
case 'r': b = 3; break;
case 'w': b = 2; break;
case 'e': b = 1; break;
case 'd': b = 0;
}
if (b < 0)
put(" *** letter after -P must be one of H S P A R W E D.\n");
else {
g->protlook = bit(b);
g->protwant = tilt ^ (b >= 4) ? bit(b) : 0;
}
continue;
}
}
putfmt(" *** Unknown option -%c.\n", *p);
}
}
void mane(GG)
{
struct FileHandle *out = gbip(g->me->pr_COS);
short a, aa;
bool help = g->argc == 2 && g->argv[1][0] == '?' && !g->argv[1][1];
bool laybull = g->argc - g->hyphc > 2, didone = false;
str *hp;
if (help) { /* [-cdfhilorsux | -pB | -p~B] */
putfmt("\nUsage: %s { [-options] [directory|pattern] } ...\n\n"
"where options are:\n",
*g->argv);
for (hp = helpslab; *hp; hp++) put(*hp);
g->abort = 1;
return;
}
g->color = IsInteractive(g->me->pr_COS) & true; /* -1 => 1 */
g->wid = CheckWindowWidth(g); /* may reset g->color */
for (aa = g->argc - 1; aa > 0 && (ubyte) *g->argv[aa] == HYPH; aa--)
Opt(g->argv[aa], g);
if (aa) {
for (a = 1; a < g->argc && !g->abort; a++)
if ((ubyte) *g->argv[a] == HYPH) {
Opt(g->argv[a], g);
} else {
if (laybull && (!g->completenames || g->consumption)) {
if (didone) puch('\n');
didone = true;
pad((g->wid - (short) strlen(g->argv[a]) - 10) >> 1);
putfmt("-- %s --\n", g->argv[a]);
}
Do(g->argv[a], g);
}
} else Do("", g);
if (g->hair && g->hair != ERROR_OBJECT_NOT_FOUND && !g->abort) {
put("*** LISTING NOT COMPLETE; ");
HairSpray(g->hair);
}
}
/* This version of cliparse features handling of internal quotes BCPL-style
with *" instead of Manx "". It marks -args with a special character
(HYPH), accepting a -arg in quotes as a filename. *N and *E are
not supported, since filenames can't contain newline or escape. */
void cliparse(GG, long alen, register str ap)
{
register short coml;
bool quoted, star;
register ubyte c;
str tempargv[200];
register str poik = bip(char, bip(struct CommandLineInterface,
g->me->pr_CLI)->cli_CommandName);
g->hyphc = 0;
coml = *poik;
g->arglen = coml + (short) alen + 2;
if (!(g->argline = Alloc(g->arglen))) return;
strncpy(g->argline, poik + 1, (size_t) coml);
g->argline[coml] = '\0';
*tempargv = g->argline;
g->argc = 1;
if (alen) {
poik = g->argline + coml + 1;
ap[--alen] = '\0'; /* probably just the newline */
for (;;) {
while (*ap && *ap <= ' ') ap++;
if (!*ap) break;
if (*ap == '-') {
g->hyphc++;
(ubyte) *ap = HYPH;
}
quoted = *ap == '"';
star = false;
ap += quoted;
tempargv[g->argc++] = poik;
while ((c = (ubyte) *ap) && (quoted ? star || c != '"' : c > ' ')) {
if (!(star = quoted && c == '*'))
*(poik++) = c;
ap++;
}
if (*ap == '"') ap++;
*(poik++) = '\0';
}
}
tempargv[g->argc] = null;
if (!(g->argv = Alloc((g->argc + 1) << 2))) {
g->argc = 0;
FreeMem(g->argline, (long) g->arglen);
}
for (coml = 0; coml <= g->argc; coml++)
g->argv[coml] = tempargv[coml];
}
/* simplified reentrant startup code */
long _main(long alen, str aptr)
{
glob g; /* the single instance for everyone */
adr reta;
Enable_Abort = 1;
memset(&g, 0, sizeof(glob));
g.me = ThisProcess();
reta = g.me->pr_ReturnAddr;
goofset = (str) &g - (str) reta;
if (!g.me->pr_CLI) {
/**** maybe reply wbstartup msg here? naah */
return 999999L; /* never gets unloaded by workbench */
}
g.stacklimit = (adr) ((str) reta - *(long *) reta + 4);
if (OpenPureIO(&g.purestuff[0], PUREBUFSIZE)) { /* move this to mane? */
g.me->pr_Result2 = ERROR_NO_FREE_STORE;
return 20L;
}
g.colsort = true;
cliparse(&g, alen, aptr);
if (g.argc)
mane(&g);
else {
put("Dr: Not enough memory to do a durn thing!\n");
g.abort = 20;
}
if (g.argline) {
FreeMem(g.argline, (long) g.arglen);
FreeMem(g.argv, (long) (g.argc + 1) << 2);
}
ClosePureIO();
if (g.hair) {
g.me->pr_Result2 = g.hair;
if (!g.abort) g.abort = 10;
}
return (long) g.abort;
}