home *** CD-ROM | disk | FTP | other *** search
- /* 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;
- }
-