page@swan.ulowell.edu (Bob Page) (12/13/88)
Submitted-by: nop@cup.portal.com Posting-number: Volume 2, Issue 97 Archive-name: unix/ls20.1 Here is a new version of Justin McCormick's "ls" program. Hope the net enjoys the program -- I sure do! # This is a shell archive. # Remove everything above and including the cut line. # Then run the rest of the file through sh. #----cut here-----cut here-----cut here-----cut here----# #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # c2.a # ls.c # ls.doc # ls.h # ls.lnk # lssup.a # makefile # This archive created: Mon Dec 12 17:01:16 1988 cat << \SHAR_EOF > c2.a * * C initial startup procedure under AmigaDOS * * Use the following command line to make c.o * asm -u -iINCLUDE: c.a * * Use the following command line to make cres.o * asm -u -dRESIDENT -iINCLUDE: -ocres.o c.a IDNT "c2.a" OPTION L LISTSYMS BASEREG B SMALLOBJ OPTIMON ADDSYM DEBUG * --------------------------------------------------------------------- * * Macros: * --------------------------------------------------------------------- * SYS MACRO * IFGT NARG-2 FAIL !!! ENDC IFEQ NARG-2 MOVE.L \2,a6 ENDC JSR _LVO\1(a6) ENDM XLVO MACRO * XREF _LVO\1 ENDM * --------------------------------------------------------------------- * * Equates: * --------------------------------------------------------------------- * RESIDENT EQU 1 MEMF_PUBLIC EQU $1 MEMF_CLEAR EQU $10000 MEMFLAGS EQU MEMF_CLEAR+MEMF_PUBLIC AbsExecBase EQU $0004 cli_CommandName EQU $10 pr_CLI EQU $AC pr_CurrentDir EQU $98 ThisTask EQU $114 _LVOCloseLibrary EQU $FE62 _LVOAllocMem EQU $FF3A _LVOFreeMem EQU $FF2E _LVOSetSignal EQU $FECE _LVOOpenLibrary EQU $FDD8 XREF _DOSBase XREF _LinkerDB ; linker defined base value XREF __BSSBAS ; linker defined base of BSS XREF __BSSLEN ; linker defined length of BSS IFD RESIDENT XREF _RESLEN XREF _RESBASE XREF _NEWDATAL ENDC XREF __main ; Name of C program to start with. XREF _MemCleanup ; Free all allocated memory XREF ___fpinit ; initialize floating point XREF ___fpterm ; terminate floating point * --------------------------------------------------------------------- * SECTION TEXT,CODE * --------------------------------------------------------------------- * XDEF zzstart zzstart: move.l a0,a2 ; save command pointer move.l d0,d2 ; and command length lea _LinkerDB,a4 ; load base register IFND RESIDENT lea __BSSBAS,a3 ; get base of BSS moveq #0,d1 move.l #__BSSLEN,d0 ; get length of BSS in longwords bra.b clr_lp ; and clear for length given clr_bss move.l d1,(a3)+ clr_lp dbf d0,clr_bss ENDC IFD RESIDENT movea.l AbsExecBase,a6 movem.l d0-d1/a0-a2,-(sp) sub.l #_RESBASE,a4 move.l #_RESLEN,d0 move.l #MEMFLAGS,d1 SYS AllocMem tst.l d0 beq.w abort move.l d0,a0 move.l d0,a2 ;a2 now has difference move.l d0,a1 move.l #_NEWDATAL,d0 ;copy data over cpy: move.l (a4)+,(a0)+ subq.l #1,d0 bne cpy ;a4 now points at number of relocs move.l (a4)+,d0 reloc: beq.b nreloc move.l a1,a0 add.l (a4)+,a0 ; a0 now has add of reloc add.l (a0),a2 move.l a2,(a0) move.l a1,a2 ; restore offset subq.l #1,d0 bra reloc nreloc: move.l a1,a4 ; set up new base register add.l #_RESBASE,a4 movem.l (sp)+,d0-d1/a0-a2 ENDC movea.l AbsExecBase,a6 move.l a6,_SysBase(a4) move.l sp,__StackPtr(a4) ; Save stack ptr clr.l _WBenchMsg(a4) * get the address of our task move.l ThisTask(a6),a3 *- clear any pending signals moveq #0,d0 move.l #$00003000,d1 SYS SetSignal * are we running as a son of Workbench? move.l pr_CurrentDir(a3),_curdir(a4) IFD WBENCH tst.l pr_CLI(a3) beq fromWorkbench ENDC *======================================================================= *====== CLI Startup Code =============================================== *======================================================================= * ; Entry: d2 = command length ; a2 = Command pointer fromCLI: move.l sp,d0 ; get top of stack sub.l 4(sp),d0 ; compute bottom add.l #128,d0 ; allow for parms overflow move.l d0,__base(a4) ; save for stack checking * attempt to open DOS library: bsr.w openDOS * find command name: move.l pr_CLI(a3),a0 add.l a0,a0 ; bcpl pointer conversion add.l a0,a0 move.l cli_CommandName(a0),a1 add.l a1,a1 ; bcpl pointer conversion add.l a1,a1 * collect parameters: move.l d2,d0 ; get command line length moveq.l #0,d1 move.b (a1)+,d1 move.l a1,__ProgramName(a4) add.l d1,d0 ; add length of command name addq.l #1,d0 ; allow for space after command clr.w -(sp) ; set null terminator for command line addq.l #1,d0 ; force to even number of bytes andi.w #$fffe,d0 ; (round up) sub.l d0,sp ; make room on stack for command line subq.l #2,d0 clr.w 0(sp,d0) * copy command line onto stack move.l d2,d0 ; get command line length subq.l #1,d0 add.l d1,d2 copy_line: move.b 0(a2,d0.w),0(sp,d2.w) ; copy command line to stack subq.l #1,d2 dbf d0,copy_line move.b #' ',0(sp,d2.w) ; add space between command and parms subq.l #1,d2 copy_cmd: move.b 0(a1,d2.w),0(sp,d2.w) ; copy command name to stack dbf d2,copy_cmd move.l sp,a1 move.l a1,-(sp) ; push command line address IFD WBENCH bra main ; call C entrypoint * --------------------------------------------------------------------- * * Workbench Startup Code * --------------------------------------------------------------------- * fromWorkbench: move.l TC_SPLOWER(a3),__base(a4) ; set base of stack moveq #127,d0 addq.l #1,d0 ; Efficient way of getting in 128 add.l d0,__base(a4) ; allow for parms overflow * open the DOS library: bsr.w openDOS * we are now set up. wait for a message from our starter lea pr_MsgPort(a3),a0 ; our process base SYS WaitPort lea pr_MsgPort(a3),a0 ; our process base SYS GetMsg move.l d0,_WBenchMsg(a4) move.l d0,-(sp) move.l d0,a2 ; get first argument move.l sm_ArgList(a2),d0 beq do_cons move.l _DOSBase(a4),a6 move.l d0,a0 move.l wa_Lock(a0),d1 move.l d1,_curdir(a4) SYS CurrentDir do_cons: move.l sm_ToolWindow(a2),d1 ; get the window argument beq do_main move.l #MODE_OLDFILE,d2 SYS Open move.l d0,_stdin(a4) beq do_main lsl.l #2,d0 move.l d0,a0 move.l fh_Type(a0),pr_ConsoleTask(a3) do_main: move.l _WBenchMsg(a4),a0 ; get address of workbench message move.l a0,-(sp) ; push argv pea _NULL(a4) ; push argc move.l sm_ArgList(a0),a0 ; get address of arguments move.l wa_Name(a0),__ProgramName(a4) ; get name of program ENDC * --------------------------------------------------------------------- * * Common CLI/WBENCH code * --------------------------------------------------------------------- * main: IFD FLOAT jsr ___fpinit(pc) ; Initialize floating point ENDC jsr __main(pc) ; call C entrypoint moveq.l #0,d0 ; set successful status bra.b exit2 XDEF _XCEXIT _XCEXIT: move.l 4(sp),d0 ; extract return code XDEF xXCEXIT xXCEXIT: exit2: IFD ERRTRAPS move.l d0,-(sp) move.l __ONEXIT(a4),d0 ; exit trap function? beq.b exit3 move.l d0,a0 jsr (a0) exit3: ENDC IFD MALLOC jsr _MemCleanup(pc) ; cleanup leftover memory alloc. ENDC movea.l AbsExecBase,a6 move.l _DOSBase(a4),a1 SYS CloseLibrary ; close Dos library IFD FLOAT jsr ___fpterm(pc) ; clean up any floating point ENDC IFD WBENCH done_1c: * if we ran from CLI, skip workbench cleanup: tst.l _WBenchMsg(a4) beq exitToDOS move.l _stdin(a4),d1 beq.b done_4 SYS Close done_4: * return the startup message to our parent * we forbid so workbench can't UnLoadSeg() us * before we are done: movea.l AbsExecBase,a6 SYS Forbid move.l _WBenchMsg(a4),a1 SYS ReplyMsg ENDC * this rts sends us back to DOS: exitToDOS: IFD RESIDENT move.l #_RESLEN,d0 move.l a4,a1 sub.l #_RESBASE,a1 movea.l AbsExecBase,a6 SYS FreeMem ENDC move.l (sp)+,d0 movea.l __StackPtr(a4),sp ; restore stack ptr XDEF xchkabort xchkabort: rts IFD RESIDENT abort: movem.l (sp)+,d0-d1/a0-a2 rts ENDC * --------------------------------------------------------------------- * noDOS: moveq.l #100,d0 bra exit2 * --------------------------------------------------------------------- * * Open the DOS library: * --------------------------------------------------------------------- * openDOS: lea DOSName(pc),a1 moveq.l #0,d0 SYS OpenLibrary move.l d0,_DOSBase(a4) beq noDOS rts DOSName: dc.b 'dos.library',0 * --------------------------------------------------------------------- * section __MERGED,BSS * --------------------------------------------------------------------- * XDEF _NULL,_SysBase,_WBenchMsg XDEF _curdir,__mbase,__mnext,__msize,__tsize XDEF __oserr,__OSERR IFD FLOAT XDEF __FPERR XDEF __SIGFPE ENDC IFD ERRTRAPS XDEF __ONERR,__ONEXIT,__ONBREAK XDEF __SIGINT ENDC XDEF _stdin XDEF __ProgramName,__StackPtr,__base * --------------------------------------------------------------------- * _NULL ds.l 1 ; Huh? __base ds.l 1 ; base of stack __mbase ds.l 1 ; base of memory pool __mnext ds.l 1 ; next available memory location __msize ds.l 1 ; size of memory pool __tsize ds.l 1 ; total size? __oserr __OSERR ds.l 1 IFD FLOAT __FPERR ds.l 1 __SIGFPE ds.l 1 ENDC IFD ERRTRAPS __SIGINT ds.l 1 __ONERR ds.l 1 __ONEXIT ds.l 1 __ONBREAK ds.l 1 ENDC _curdir ds.l 1 _SysBase ds.l 1 _WBenchMsg ds.l 1 __StackPtr ds.l 1 _stdin ds.l 1 __ProgramName ds.l 1 * --------------------------------------------------------------------- * END * --------------------------------------------------------------------- * SHAR_EOF cat << \SHAR_EOF > ls.c /* --------------------------------------------------------------------- * LS.C -- an "improved" directory listing utility to replace the AmigaDOS DIR and LIST commands. V1.0 August 1986 Written from scratch by Justin V. McCormick. V2.0 November 1988 Revised for Lattice 5.0 and made 1.3 compatible. Notice: This program is placed in the public domain with the understanding that the author makes no claims or guarantees with regard to its suitability for any given application, and that the author assumes no responsibility for damages incurred by its usage. Any resemblance of this program to any other program, either living or dead, is purely coincidental. Feel free to steal this code and make millions of dollars from its sale or commercial use, but please give credit where credit is due. Synopsis: Features adaptive columnar listing, versatile sort options, UNIX-style pattern matching, recursive subdirectory listing, etc! Usage: ls [options] [path1] [path2] ... Options: -? Help! -c Show file comment info, -c implies -l -d Show directory names only -f Show filenames only -l Long verbose listing showing filesizes and dates -n No sort, just spit them out in the order ExNext() returns -r Reverse sort direction -s Sorted by size smallest to largest -t Sorted by date oldest to newest -R Recursive descent of subdirectories All arguments are optional. Default is to give short columnar listing, sorted alphabetically, using the current directory. Alphabetizing is case insensitive. Patterns may be matched in the given names, using the UNIX-style '*' to wildcard any number of characters, and '?' to wildcard a single character. If you need to specify a pathname with spaces in it like "Wombat Soup", you need to put quotes around it. LS can process up to 30 separate pathname patterns in one command line. Bugs: Redirecting the shortlist output to PRT: gives undesirable results, since I am using relative cursor positioning commands to format the screen output. I thought about using an array to store a virtual screen, but my primary goals were to keep the size down and display speed at a maxiumum. Also, LS cannot pattern match devices (like "dh*:") or support multiple levels of pattern matching (like "dh0:?/L*.info"). This would involve another level of recursion and groking the Device List. Changes From 1.0 to 2.0: o Source code prototyped, linted, traced, optimized, tweaked, etc. o Made resident ("pseudo-pure") by linking with cres.o from LC 5.0. o High-volume routines recoded in assembly (lssup.a). o Now handles multiple paths/files on a command line, up to 30. o New sort flags, including no sort. o Enhanced wildcards, understands complex *.?*.* expressions now. o More efficient ExNext() performance, less ram used for recursion. o SIGBREAKF_CTRL_C signal (Ctrl-C) cleanly aborts at any point now. o Command line parser handles quoted pathnames now (LC 5.0 benefit). o Short listing finally auto-adjusts to new console window sizes! o Pen color escape codes bypassed when redirecting long output. o Sorting by size or date is also subsorted alphabetically now. o Long listing shows new 1.3 file attributes, plus comment indicator. o File dates are now in international format, YY-MM-DD. o Fixed listings with files datestamped after 99-12-31 (overflow). o Fixed listings with files datestamped before 78-01-01 (time < 0). * --------------------------------------------------------------------- */ #include "ls.h" /* Structure used to hold file info in a linked list */ struct FibEntry { struct FibEntry *NextFib; struct FibEntry *LastFib; struct FileInfoBlock *Fibp; }; /* Externs from lssup.a */ /*lint +fva2 */ extern int __stdargs asprintf (char *, char *,...); /*lint -fva2 */ extern __asm void SortFibs (R_D0 long, R_D1 long, R_A0 struct FibEntry *); extern __stdargs long iswild (char *); extern __stdargs long wildmatch (char *, char *); extern __stdargs char *FibFileDate (struct DateStamp *); extern __stdargs void GetWinBounds(long *, long *); /* Local CODE */ struct FibEntry *GetDir (BPTR, struct FileInfoBlock *); struct FibEntry *AllocFib (void); void CleanUp (char *, long); void ColorPen2 (void); void CursorOff (void); void CursorOn (void); void DirIt (BPTR, char *); void FreeAllFibs (struct FibEntry *); void FreeFib (struct FibEntry *); void LListDir (struct FibEntry *); void LListEntry (struct FileInfoBlock *); void LongList (long *, long *, struct FibEntry *); void main (int, char **); void PagePrompt (long); void ResetPen (void); void SListDir (struct FibEntry *); void TestBreak (void); void Usage (void); void WCHR (char *); void WSTR (char *); BPTR Out = 0L; BPTR In = 0L; BPTR lockp = 0L; struct FileInfoBlock *GFibp = 0L; long BREAKFLAG = 0; long CONSOLE = 0; long dircount = 0; long DIRFILEFLAG = 0; long errstat = 0; long filecount = 0; long LISTALL = 0; long LONGLIST = 0; long maxnamlen = 0; long NOSORTFLAG = 0; long NOTEFLAG = 0; long PATHNAMED = 0; long REVFLAG = 0; long sortkey = 0; long WILDCARD = 0; long CurWinRows = 20L; long CurWinCols = 77L; char filename[160]; char pattern[160]; char workstr[160]; static char Author[] = "\23333mLS 2.0\2330m by Justin V. McCormick 1988"; static char usage[] = "\nUsage: ls [-cdflnrstR] [path1] [path2] ...\n"; /* -------------------------------------------------------------------- */ void main (argc, argv) int argc; char **argv; { struct Process *procp; long cnt, i; if (argc == 0) /* son of Workbench -- no go! */ exit (0); /* Grab FileHandles for input and output to console (or redirection file) */ In = Input (); Out = Output (); CONSOLE = (IsInteractive (Out) == 0L) ? 0L : 1L; /* Is this console output? */ /* Allocate a global FileInfoBlock for ExNext() */ if ( (GFibp = (struct FileInfoBlock *)AllocMem((long)sizeof(struct FileInfoBlock), MEMF_PUBLIC | MEMF_CLEAR)) == 0L) CleanUp("No RAM?", 103L); /* Parse command line arguments <ugh> */ cnt = 1; if (argc >= 2) { if (argv[1][0] == '-') { cnt++; for (i = strlen(argv[1]) - 1; i > 0; i--) { switch (argv[1][i]) { case 'c': LONGLIST = 1; NOTEFLAG = 1; break; case 'd': DIRFILEFLAG |= 1; break; case 'f': DIRFILEFLAG |= 2; break; case 'l': LONGLIST = 1; break; case 'n': NOSORTFLAG = 1; break; case 'r': REVFLAG = 1; break; case 's': sortkey = 1; break; case 't': sortkey = 2; break; case 'R': LISTALL = 1; break; case '?': Usage (); break; default: (void) asprintf (workstr, "Unknown option \'%c\'\n", argv[1][i]); WSTR (workstr); Usage (); break; } } } } /* Clean up the state flags */ if ( (argc - cnt) > 1) LISTALL |= 2; if (DIRFILEFLAG == 0) DIRFILEFLAG = 3; /* Loop through the remaining args now */ do { PATHNAMED = 0; if (cnt < argc) { (void) stpcpy (pattern, argv[cnt]); PATHNAMED = 1; } if (PATHNAMED) /* If user specified a pathname */ { WILDCARD = iswild (pattern); /* check for wildcards */ if (WILDCARD) /* If wildcards, separate */ { /* pattern from pathname */ for (i = (strlen (pattern) - 1); i >= 0; i--) { if (pattern[i] == '/' || pattern[i] == ':') { (void) strncpy (filename, pattern, (UWORD) (i + 1)); filename[i + 2] = (BYTE) 0; (void) stpcpy (workstr, &pattern[i + 1]); (void) stpcpy (pattern, workstr); break; } } /* Disallow wildcards in pathname */ if (iswild (filename)) CleanUp ("Sorry, can't pattern match paths", 5L); } else /* No wildcards, use filename as is */ { (void) stpcpy (filename, pattern); } /* If the user specified a pathname, try to grab a FileLock on it */ /* Discard trailing slash if silly Joe User put one there */ if (filename[1] != 0 && filename[strlen (filename) - 1] == '/') filename[strlen (filename) - 1] = (BYTE) 0; lockp = Lock (filename, ACCESS_READ); if (!lockp) /* Can't Lock it! */ { errstat = IoErr (); (void) strcat (filename, " not found"); CleanUp (filename, (long)errstat); } } else { /* * If no filename was specified, steal Lock on current directory from * CLI process task info. We durn well better get something useful back; * we don't do any error checking on the stolen Lock. */ procp = (struct Process *) FindTask (0L); lockp = procp->pr_CurrentDir; filename[0] = 0; /* Tell DirIt() to use current dir */ } /* Get the directory for this path, display it */ DirIt (lockp, filename); /* Release the lock, bump our arg counter */ if (lockp != 0 && PATHNAMED != 0) UnLock (lockp); lockp = 0L; cnt++; if (cnt < argc) WCHR("\n"); } while (cnt < argc && BREAKFLAG == 0); CleanUp ("", 0L); } /* -------------------------------------------------------------------- */ /* Deallocate and close everything */ /* -------------------------------------------------------------------- */ void CleanUp (exit_msg, exit_status) char *exit_msg; long exit_status; { if (lockp && PATHNAMED) UnLock (lockp); if (GFibp != 0L) FreeMem(GFibp, (long)sizeof(struct FileInfoBlock)); if (exit_status) { (void) asprintf (workstr, "ls: %s, Error #%ld\n", exit_msg, exit_status); WSTR (workstr); } exit ((int) exit_status); } /* -------------------------------------------------------------------- */ void DirIt (lockp, dirname) BPTR lockp; char *dirname; { BPTR tlockp; struct FibEntry *fibheadp; struct FibEntry *tfibp; char *subdir; long strsize; /* Try to fill FileInfoBlock, bomb if not readable for some reason */ if (!Examine (lockp, GFibp)) { (void) asprintf (workstr, "Can't examine file or directory\n"); WSTR (workstr); return; } /* Put directory header if this is a recursive listing */ if (dirname[0] && LISTALL > 0) { if (CONSOLE != 0) (void)asprintf(workstr, "\23330;41m %s \2330m\n", dirname); else (void)asprintf(workstr, "%s\n", dirname); WSTR(workstr); } /* If this is a single file list it verbosely */ if (GFibp->fib_EntryType < 0 && (DIRFILEFLAG & 2) != 0) { LListEntry (GFibp); } else { /* Otherwise do a directory */ /* Allocate and initialize a FibEntry head node */ if ( (fibheadp = GetDir (lockp, GFibp)) != 0L && BREAKFLAG == 0) { if (NOSORTFLAG == 0) SortFibs ((long)sortkey, REVFLAG, fibheadp); if (LONGLIST == 0) SListDir (fibheadp); else LListDir (fibheadp); if ( (LISTALL & 1) != 0) { tfibp = fibheadp; do { if (tfibp->Fibp->fib_EntryType > 0) { strsize = (strlen (dirname) + strlen (tfibp->Fibp->fib_FileName) + 2); subdir = (char *) AllocMem ((LONG)strsize, 0L); if (strlen (dirname) != 0) { (void) stpcpy (subdir, dirname); if (dirname[strlen (dirname) - 1] != ':') (void) strcat (subdir, "/"); } (void) strcat (subdir, tfibp->Fibp->fib_FileName); tlockp = Lock (subdir, ACCESS_READ); if (tlockp == 0L) { WSTR (subdir); WSTR (" -- can't lock it!\n"); break; } else { WCHR ("\n"); /* Put a blank line between directories */ DirIt (tlockp, subdir); UnLock (tlockp); FreeMem (subdir, (LONG)strsize); } } tfibp = tfibp->NextFib; } while (tfibp != fibheadp && BREAKFLAG == 0); } } /* Clean up */ FreeAllFibs (fibheadp); } } /* -------------------------------------------------------------------- */ /* Allocate and fill a linked list of FileInfoBlocks */ /* -------------------------------------------------------------------- */ struct FibEntry *GetDir (lockp, fibp) BPTR lockp; struct FileInfoBlock *fibp; { long nextstat; long tempnamlen; struct FibEntry *tfibp; struct FibEntry *headfib; maxnamlen = dircount = filecount = 0L; headfib = 0L; do { TestBreak(); if (BREAKFLAG != 0) return(headfib); nextstat = ExNext (lockp, fibp); if (nextstat != 0) /* We got something */ { /* See if it matches our pattern */ if (!WILDCARD || wildmatch (fibp->fib_FileName, pattern)) { /* Bump count of files or directories */ if (fibp->fib_EntryType > 0) { if ((DIRFILEFLAG & 1) != 0) dircount++; else goto ALLOCFIB; } else { if ((DIRFILEFLAG & 2) != 0) filecount++; else continue; } /* See if this is the longest filename for later use in listing */ tempnamlen = strlen (fibp->fib_FileName); if (tempnamlen > maxnamlen) maxnamlen = tempnamlen; ALLOCFIB: /* Allocate another FibEntry to put the info in */ if (headfib == 0L) { headfib = AllocFib(); if (headfib == 0L) return(headfib); headfib->NextFib = headfib; headfib->LastFib = headfib; *(headfib->Fibp) = *(fibp); tfibp = headfib; } else { tfibp->NextFib = AllocFib (); if (tfibp->NextFib == 0L) return(0L); /* Copy FIB contents to next entry for ExNext to work with */ *(tfibp->NextFib->Fibp) = *(fibp); /* Link it into the list */ tfibp->NextFib->LastFib = tfibp; tfibp = tfibp->NextFib; tfibp->NextFib = headfib; } } } } while (nextstat); /* Return TRUE if entries found, else print message and return FALSE */ if ( (dircount + filecount) != 0) { return (headfib); } else { if (WILDCARD == 0 && DIRFILEFLAG == 3) WSTR ("Volume or directory is empty.\n"); else WSTR ("No match.\n"); return (0L); } } /* -------------------------------------------------------------------- */ /* List a FibEntry list in a compact fashion */ /* -------------------------------------------------------------------- */ void SListDir (fibheadp) struct FibEntry *fibheadp; { struct FibEntry *tfibp; long tabsize; long maxtab; long totrows; long maxwinrow; long colcnt; long rowcnt; long tabcnt; long pagecnt = 1; CursorOff (); /* Turn the cursor off since it will blink anyway */ GetWinBounds(&CurWinCols, &CurWinRows); tfibp = fibheadp; tabcnt = rowcnt = colcnt = 0; tabsize = maxnamlen + 2; if (CurWinCols < tabsize) maxtab = 1; else maxtab = CurWinCols / tabsize; maxwinrow = CurWinRows - 3; if (maxwinrow <= 0) maxwinrow = 1; totrows = (dircount + filecount) / maxtab; if ((dircount + filecount) % maxtab != 0) totrows++; do { if (tfibp->Fibp->fib_EntryType > 0) { if ( (DIRFILEFLAG & 1) == 0) goto GETNEXTFIB; else ColorPen2 (); } if (tabcnt) (void) asprintf (workstr, "\233%ldC", (long)tabcnt); else workstr[0] = (BYTE) 0; (void) strcat (workstr, tfibp->Fibp->fib_FileName); (void) strcat (workstr, "\n"); WSTR (workstr); if (tfibp->Fibp->fib_EntryType > 0) ResetPen (); rowcnt++; if (rowcnt == maxwinrow || rowcnt == totrows) { colcnt++; /* Start a new column */ /* Check to see if we have used the last column up and are about to run * off the screen entirely. If so, give the user a chance to read it first. */ if (colcnt == maxtab && rowcnt == maxwinrow && CONSOLE != 0) { if (maxwinrow > 1) { ++pagecnt; /* Advance page number count */ PagePrompt (pagecnt); /* Print it, wait for user */ } totrows -= maxwinrow; colcnt = tabcnt = rowcnt = 0L; } else /* Just move over one row and back up to the top */ { (void) asprintf (workstr, "\233%ldA", (long)rowcnt); WSTR (workstr); tabcnt += tabsize; rowcnt = 0L; } } GETNEXTFIB: tfibp = tfibp->NextFib; } while (tfibp != fibheadp); if (totrows - rowcnt > 0) /* Cursor down till level with */ { /* lowest line on screen */ (void) asprintf (workstr, "\233%ldE", (long)(totrows - rowcnt)); WSTR (workstr); } CursorOn (); } /* -------------------------------------------------------------------- */ /* Reset character color to default Pen1 colors */ /* -------------------------------------------------------------------- */ void ResetPen () { if (CONSOLE != 0) WSTR ("\2330m"); } /* -------------------------------------------------------------------- */ /* Turn the cursor on */ /* -------------------------------------------------------------------- */ void CursorOn () { if (CONSOLE != 0) WSTR ("\233 p"); } /* -------------------------------------------------------------------- */ /* Turn the cursor off for faster text output */ /* -------------------------------------------------------------------- */ void CursorOff () { if (CONSOLE != 0) WSTR ("\2330 p"); } /* -------------------------------------------------------------------- */ /* Make Pen2 <usually yellow/orange> the current charcter color */ /* -------------------------------------------------------------------- */ void ColorPen2 () { if (CONSOLE != 0) WSTR ("\23333m"); } /* -------------------------------------------------------------------- */ /* Prompt the user to hit return, wait till return is hit */ /* -------------------------------------------------------------------- */ void PagePrompt (page) long page; { WSTR ("\2337m -- MORE -- Press Return: \2330m"); (void) Read (In, workstr, 1L); (void) asprintf (workstr, "\233F\233K\2334;33mPage %ld:\2330m\n", (long)page); WSTR (workstr); } /* -------------------------------------------------------------------- */ /* List a directory in a verbose informative manner */ /* -------------------------------------------------------------------- */ void LListDir (fibheadp) struct FibEntry *fibheadp; { long totblocks = 0L; long totbytes = 0L; CursorOff (); LongList (&totblocks, &totbytes, fibheadp); (void) asprintf (workstr, "Dirs:%-3ld Files:%-4ld Blocks:%-5ld Bytes:%-8ld\n", (long)dircount, (long)filecount, totblocks, totbytes); WSTR (workstr); CursorOn (); } /* -------------------------------------------------------------------- */ void LongList (totblocks, totbytes, fibheadp) long *totblocks, *totbytes; struct FibEntry *fibheadp; { struct FibEntry *tfibp; tfibp = fibheadp; do { LListEntry (tfibp->Fibp); if (tfibp->Fibp->fib_EntryType < 0) { *totblocks += tfibp->Fibp->fib_NumBlocks; *totbytes += tfibp->Fibp->fib_Size; } tfibp = tfibp->NextFib; } while (tfibp != fibheadp); } /* -------------------------------------------------------------------- */ /* Verbosely list a particular FibEntry */ /* -------------------------------------------------------------------- */ void LListEntry (fib) struct FileInfoBlock *fib; { long i, pmodes; char *cp1; char entry[160]; pmodes = fib->fib_Protection & 0xff; cp1 = stpcpy (entry, "chsparwed "); for (i = 0; i < 4; i++) { if ((pmodes & (1 << i)) != 0) entry[8 - i] = '-'; } for (; i < 8; i++) { if ((pmodes & (1 << i)) == 0) entry[8 - i] = '-'; } if (fib->fib_Comment[0] == 0) entry[0] = '-'; cp1 = stpcpy (cp1, FibFileDate (&fib->fib_Date)); if (fib->fib_EntryType > 0) { if ( (DIRFILEFLAG & 1) == 0) return; if (CONSOLE) cp1 = stpcpy (cp1, "\23333m"); cp1 = stpcpy (cp1, " Directory "); if (CONSOLE) cp1 = strcat (cp1, "\2330m"); } else (void) asprintf (&entry[27], " %4ld %8ld ", fib->fib_NumBlocks, fib->fib_Size); (void)strcat(entry, fib->fib_FileName); (void)strcat(cp1, "\n"); WSTR (entry); if (NOTEFLAG != 0 && fib->fib_Comment[0] != 0) { if (CONSOLE) (void)asprintf(cp1, "\23333m/* %s */\2330m\n", fib->fib_Comment); else (void)asprintf(cp1, "/* %s */\n", fib->fib_Comment); WSTR (workstr); } } #ifdef NOASM /* -------------------------------------------------------------------- */ /* Calculate date based on DateStamp structure, return a string pointer */ /* -------------------------------------------------------------------- */ char *CalcDate (fib) struct FileInfoBlock *fib; { static long days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static char *months[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; char datestr[25]; long i, mdays; long day, hour; long minute, sec; long ldays; long year; ldays = 1461L; year = 78L; day = fib->fib_Date.ds_Days; minute = fib->fib_Date.ds_Minute; sec = fib->fib_Date.ds_Tick / 50L; year += (day / ldays) * 4L; day %= ldays; while (day) { mdays = 365; if ((year & 3) == 0) mdays++; if (day < mdays) break; day -= mdays; year++; } for (i = 0L, day++; i < 12; i++) { mdays = days[i]; if (i == 1 && (year & 3) == 0) mdays++; if (day <= mdays) break; day -= mdays; } hour = minute / 60; minute -= hour * 60; (void) asprintf (datestr, "%02ld-%s-%02ld %02ld:%02ld:%02ld ", day, (long)months[i], year, hour, minute, sec); return (datestr); } #endif /* -------------------------------------------------------------------- */ /* Use AmigaDos to put a string on the stdout */ /* -------------------------------------------------------------------- */ void WSTR (tstring) char *tstring; { (void) Write (Out, tstring, (long) strlen (tstring)); } /* -------------------------------------------------------------------- */ /* Use AmigaDos to put a character on the stdout */ /* -------------------------------------------------------------------- */ void WCHR (ch) char *ch; { (void) Write (Out, ch, 1L); } /* -------------------------------------------------------------------- */ /* Check to see if the user hit ^C */ /* -------------------------------------------------------------------- */ void TestBreak () { unsigned long oldsig; oldsig = SetSignal (0L, SIGBREAKF_CTRL_C); if ( (oldsig & SIGBREAKF_CTRL_C) != 0L) { WSTR ("\2330m\233 p**BREAK\n"); BREAKFLAG = 1; } } /* -------------------------------------------------------------------- */ /* Explain how to use */ /* -------------------------------------------------------------------- */ void Usage () { WSTR (Author); WSTR (usage); WSTR (" c > Show comments\n"); WSTR (" d > Dirs only\n"); WSTR (" f > Files only\n"); WSTR (" l > Long listing\n"); WSTR (" n > No sort\n"); WSTR (" r > Reverse sort\n"); WSTR (" s > Sort by size\n"); WSTR (" t > Sort by date\n"); WSTR (" R > Recursive listing\n"); CleanUp ("", 0L); } /* -------------------------------------------------------------------- */ /* Allocate a FibEntry structure and associated FileInfoBlock */ /* -------------------------------------------------------------------- */ struct FibEntry *AllocFib () { struct FibEntry *tfibp; tfibp = (struct FibEntry *) AllocMem \ ((long)( sizeof (struct FibEntry) + sizeof(struct FileInfoBlock) ), 0L); if (tfibp != 0L) { tfibp->Fibp = (struct FileInfoBlock *)((ULONG)tfibp + sizeof(struct FibEntry)); } else BREAKFLAG = 1; return (tfibp); } /* -------------------------------------------------------------------- */ /* Free up memory allocated to a linked list of FibEntrys */ /* -------------------------------------------------------------------- */ void FreeAllFibs (fibheadp) struct FibEntry *fibheadp; { struct FibEntry *fibp; struct FibEntry *tfibp; if (fibheadp != 0) { fibp = fibheadp; do { tfibp = fibp->NextFib; FreeFib (fibp); fibp = tfibp; } while (tfibp != fibheadp); } } /* -------------------------------------------------------------------- */ /* Deallocate a single FibEntry structure */ /* -------------------------------------------------------------------- */ void FreeFib (fibp) struct FibEntry *fibp; { if (fibp != 0L) FreeMem (fibp, (long)(sizeof (struct FibEntry) + sizeof(struct FileInfoBlock))); } SHAR_EOF cat << \SHAR_EOF > ls.doc LS.C -- an "improved" directory listing utility to replace the AmigaDOS DIR and LIST commands. V1.0 August 1986 Written from scratch by Justin V. McCormick. V2.0 November 1988 Revised for Lattice 5.0 and made 1.3 compatible. Notice: This program is placed in the public domain with the understanding that the author makes no claims or guarantees with regard to its suitability for any given application, and that the author assumes no responsibility for damages incurred by its usage. Any resemblance of this program to any other program, either living or dead, is purely coincidental. Feel free to steal this code and make millions of dollars from its sale or commercial use, but please give credit where credit is due. Synopsis: Features adaptive columnar listing, versatile sort options, UNIX-style pattern matching, recursive subdirectory listing, etc! Usage: ls [options] [path1] [path2] ... Options: -? Help! -c Show file comment info, -c implies -l -d Show directory names only -f Show filenames only -l Long verbose listing showing filesizes and dates -n No sort, just spit them out in the order ExNext() returns -r Reverse sort direction -s Sorted by size smallest to largest -t Sorted by date oldest to newest -R Recursive descent of subdirectories All arguments are optional. Default is to give short columnar listing, sorted alphabetically, using the current directory. Alphabetizing is case insensitive. Patterns may be matched in the given names, using the UNIX-style '*' to wildcard any number of characters, and '?' to wildcard a single character. If you need to specify a pathname with spaces in it like "Wombat Soup", you need to put quotes around it. LS can process up to 30 separate pathname patterns in one command line. Bugs: Redirecting the shortlist output to PRT: gives undesirable results, since I am using relative cursor positioning commands to format the screen output. I thought about using an array to store a virtual screen, but my primary goals were to keep the size down and display speed at a maxiumum. Also, LS cannot pattern match devices (like "dh*:") or support multiple levels of pattern matching (like "dh0:?/L*.info"). This would involve another level of recursion and groking the Device List. Changes From 1.0 to 2.0: o Source code prototyped, linted, traced, optimized, tweaked, etc. o Made resident ("pseudo-pure") by linking with cres.o from LC 5.0. o High-volume routines recoded in assembly (lssup.a). o Now handles multiple paths/files on a command line, up to 30. o New sort flags, including no sort. o Enhanced wildcards, understands complex *.?*.* expressions now. o More efficient ExNext() performance, less ram used for recursion. o SIGBREAKF_CTRL_C signal (Ctrl-C) cleanly aborts at any point now. o Command line parser handles quoted pathnames now (LC 5.0 benefit). o Short listing finally auto-adjusts to new console window sizes! o Pen color escape codes bypassed when redirecting long output. o Sorting by size or date is also subsorted alphabetically now. o Long listing shows new 1.3 file attributes, plus comment indicator. o File dates are now in international format, YY-MM-DD. o Fixed listings with files datestamped after 99-12-31 (overflow). o Fixed listings with files datestamped before 78-01-01 (time < 0). Don't send me money for this one! Its a Christmas present 8-) However, if you find any bugs I'd like to hear about them! Justin V. McCormick 8330 E. Quincy Ave., C-312 Denver, CO 80237 Home: 303-290-8429 Work: 303-825-4144 SHAR_EOF cat << \SHAR_EOF > ls.h #include <dos.h> #include <libraries/dosextens.h> /*lint -save */ /*lint -library */ #include <proto/exec.h> #include <proto/dos.h> #include <stdio.h> #include <string.h> #include <stdlib.h> /*lint -restore */ extern int tolower (char); /* Prevent Lint from complaining about ANSI prototype extensions */ #ifdef _lint #define __asm #define __stdargs #define R_D0 #define R_D1 #define R_A0 #else #define R_D0 register __d0 #define R_D1 register __d1 #define R_A0 register __a0 #endif #define MEMF_PUBLIC (1L<<0) #define MEMF_CHIP (1L<<1) #define MEMF_FAST (1L<<2) #define MEMF_CLEAR (1L<<16) SHAR_EOF cat << \SHAR_EOF > ls.lnk DEFINE @_main = @_tinymain DEFINE @XCEXIT = xXCEXIT DEFINE @chkabort = xchkabort DEFINE @write = xchkabort DEFINE @_dclose = xchkabort SC SD ND SHAR_EOF cat << \SHAR_EOF > lssup.a * --------------------------------------------------------------------- * * LSSUP.A - Assembly support routines for ls.c * * Copyright (c) 1988 by Justin V. McCormick. All Rights Reserved. * * --------------------------------------------------------------------- * IDNT "lssup.a" include "asm:inc/macros.i" ; Assembler options for CAPE BASEREG B SMALLOBJ OPTIMON ADDSYM DEBUG ; Equates fib_FileName equ $8 fib_Size equ $7C fib_NumBlocks equ $80 fib_DateStamp equ $84 ds_Days equ $0 ds_Minute equ $4 ds_Tick equ $8 _LVOAddPort equ $FFFFFE9E _LVOAllocMem equ $FFFFFF3A _LVOAllocSignal equ $FFFFFEB6 _LVODebug equ $FFFFFF8E _LVOFindTask equ $FFFFFEDA _LVOFreeMem equ $FFFFFF2E _LVOFreeSignal equ $FFFFFEB0 _LVOGetMsg equ $FFFFFE8C _LVOPutMsg equ $FFFFFE92 _LVORawDoFmt equ $FFFFFDF6 _LVORemPort equ $FFFFFE98 _LVORead equ $FFFFFFD6 _LVOWaitPort equ $FFFFFE80 _LVOWrite equ $FFFFFFD0 pr_ConsoleTask EQU $A4 MEMF_CLEAR EQU $10000 MEMF_PUBLIC EQU $1 sp_Msg EQU $0 sp_Pkt EQU $14 sp_SIZEOF EQU $44 dp_Link EQU $0 dp_Port EQU $4 dp_Arg1 EQU $14 dp_Type EQU $8 ACTION_SCREEN_MODE EQU $3E2 LN_NAME EQU $A LN_PRI EQU $9 LN_TYPE EQU $8 MP_FLAGS EQU $E MP_MSGLIST EQU $14 MP_SIGBIT EQU $F MP_SIGTASK EQU $10 MP_SIZE EQU $22 NT_MSGPORT EQU $4 PA_SIGNAL EQU $0 * External constants XREF _DOSBase XREF _Out XREF _In * --------------------------------------------------------------------- * SECTION lssup,CODE * ------------------------------------------------------------------------- * * void asprintf(wstr, formatstring, args) * char *wstr; * char *formatstring; * char **args; * * Synopsis: Given formatstring and args to format, formats output to wstr. * Similar to sprintf(), except doesn't handle floats. * ------------------------------------------------------------------------- * XDEF _asprintf _asprintf: link a5,#0 movem.l d0-d2/a0-a3,-(sp) ;Save everything we might clobber * Call format function to convert fmtstring and args to buffer on the stack movea.l 12(a5),a0 ;Grab format string lea 16(a5),a1 ;Grab EA of arguments lea kput1,a2 ;Grab EA of output subroutine movea.l 8(a5),a3 ;Grab EA of dest workspace SYS RawDoFmt,4 ;Format it into workspace movem.l (sp)+,d0-d2/a0-a3 ;Restore registers unlk a5 ;And stack frame rts * ------------------------------------------------------------------------- * * RawDoFmt() output routine for xprintf, called for each formatted char. * Takes byte in d0 and puts in buffer pointed to by a3, then increments a3. * ------------------------------------------------------------------------- * XDEF kput1 kput1: move.b d0,(a3)+ rts * --------------------------------------------------------------------- * * void GetWinBounds(width, height) * long *width, *height; * * Find current console window, determine width and height * in terms of current font, update width and height VPARMS passed. * --------------------------------------------------------------------- * rpstr equ -32 rpport equ -12 packet equ -8 conid equ -4 width equ 8 height equ 12 XDEF _GetWinBounds _GetWinBounds: link a5,#-32 movem.l d2-d4/a2,-(sp) suba.l a1,a1 SYS FindTask,4 ;d0 = FindTask(0L), our process movea.l d0,a0 ;Transfer to address reg move.l pr_ConsoleTask(a0),conid(a5) ;Save proc->pr_ConsoleTask moveq #0,d4 ;Clear our success status register moveq #0,d0 movea.l d0,a0 bsr.w CreatePort move.l d0,rpport(a5) ;rpport = CreatePort(0L, 0L) beq.w gwbdone ;Oops, no signals or ram available! move.l #MEMF_PUBLIC+MEMF_CLEAR,d1 moveq #sp_SIZEOF,d0 SYS AllocMem move.l d0,packet(a5) ;packet = AllocMem(sizeof(*packet),MEMF_PUBLIC|MEMF_CLEAR) beq.w gwbfreeport ;Oops, no ram, free up port * Okay, we got our process id, reply port, and packet * Now toggle the console into raw mode movea.l rpport(a5),a2 movea.l d0,a1 movea.l conid(a5),a0 moveq #1,d0 bsr.w SetConsoleType ;SetConsoleType(1L, conid, packet, rpport) * Request a window bounds report moveq #4,d3 lea gwbrstr(a4),a0 move.l a0,d2 move.l _Out(a4),d1 SYS Write,_DOSBase(a4) ;Write(Output(), "\2330 q", 4L); cmpi.l #$0004,d0 ;Did the console choke on it? bne.w gwbsetcook ;hmmm, see if we can back out gracefully * Read the report string into stack buffer moveq #16,d3 ;Don't let it get longer than 16 characters lea rpstr(a5),a0 ;Point to input string area move.l a0,d2 move.l _In(a4),d1 SYS Read ;Read(Input(), rpstr, 16L) move.l d0,d4 ;Save read length while we close shop * Turn the console back to cooked mode pronto to avoid cursor blink gwbsetcook: movea.l rpport(a5),a2 movea.l packet(a5),a1 movea.l conid(a5),a0 moveq #0,d0 bsr.w SetConsoleType ;SetConsoleType(0L, conid, packet, rpport) * Release resources we borrowed gwbfreepack: move.l packet(a5),d0 ;Did we allocate a packet? beq.b gwbfreeport ;nay, check for port to free movea.l d0,a1 moveq #sp_SIZEOF,d0 SYS FreeMem ;Else FreeMem(packet, sizeof(*packet)) gwbfreeport: move.l rpport(a5),d0 ;if (rpport)... beq.w gwbdone ;nope bsr.w DeletePort ;Else DeletePort(rpport) * Finally, sanity check window bounds report string * d4 = length of report string according to Read() cmpi.l #9,d4 ;Less than 8 characters returned? ble.w gwbdone ;hmmm, phonky bounds report from DOS? lea rpstr(a5),a2 ;a2 = rpstr cmpi.b #';',4(a2) ;Matches a typical report template? bne.w gwbdone ;nope, got some weird junk back? cmpi.b #'r',-1(a2,d4.w) ;Last byte is 'r' for report? bne.w gwbdone ;Nope, message fubar! * Parse the height and width variables from the field now * Our report format looks like this in hex: * 9b 31 3b 31 3b y2 y1 3b x2 x1 20 72 * Or in ascii: * <0x9b>1;1;20;77 r * Which would indicate a width of 77 cols and a height of 20 rows for * the current console device * * REGS: a2 points to beginning of 'r' terminated string lea 5(a2),a2 ;Point to first char of Y size moveq #0,d1 ;Clear out work reg * Convert ascii rows value to LONG, update host data move.b (a2)+,d1 ;Grab a Y subi.w #'0',d1 ;Less ascii offset cmpi.b #';',(a2) ;Any more Y digits? beq.b 1$ ;Nope mulu #10,d1 ;Else shift by 10 add.b (a2)+,d1 ;Add least significant Y digit subi.b #'0',d1 ;Less ascii offset cmpi.b #';',(a2) ;Any more Y digits? beq.b 1$ ;Nope mulu #$000a,d1 ;Else shift by 10 add.b (a2)+,d1 ;Add least significant Y digit subi.b #'0',d1 ;Less ascii offset ;We'll assume screen height < 999 rows 1$ * Convert ascii columns value to LONG, update host data addq.w #1,a2 ;Move past the ';' separator moveq #0,d2 ;Zap work reg move.b (a2)+,d2 ;Grab msd of X cmpi.b #' ',d2 ;Premature end? beq.w gwbdone ;Huh, must be garbage - don't update VPARMS cmpi.b #';',d2 ;Also a possible error beq.w gwbdone cmpi.b #'r',d2 ;And what about this? beq.w gwbdone subi.b #'0',d2 ;Okay, adjust ascii offset cmpi.b #' ',(a2) ;Hit end of report? beq.b 2$ ;Yep mulu #$000a,d2 ;Else shift by 10 add.b (a2)+,d2 ;Add next digit subi.b #'0',d2 ;Ascii adjust cmpi.b #' ',(a2) ;Hit end of report? beq.b 2$ ;Yep mulu #$000a,d2 ;Else shift by 10 add.b (a2),d2 ;Add next digit subi.b #'0',d2 ;Ascii adjust 2$ * Finally, update parameters by reference movea.l height(a5),a0 ;Grab height VPARM move.l d1,(a0) ;*height = d1 movea.l width(a5),a0 ;Grab width VPARM move.l d2,(a0) ;*width = d2 gwbdone: movem.l (sp)+,d2-d4/a2 unlk a5 rts * --------------------------------------------------------------------- * * __asm void SetConsoleType(flag, id, packet, port) * register __d0 long flag; * register __a0 struct Process *id; * register __a1 struct StandardPacket *packet; * register __a2 struct MsgPort *port; * * Flag = 1L -- Raw mode * = 0L -- Cooked mode * --------------------------------------------------------------------- * XDEF SetConsoleType SetConsoleType: movem.l a3/a5,-(sp) movea.l a0,a3 ;Copy process pointer movea.l a1,a5 ;Copy packet pointer lea sp_Pkt(a5),a0 ;a0 = &packet->sp_Pkt move.l a0,sp_Msg+LN_NAME(a5) ;p->sp_Msg.mn_Node.ln_Name = &p->sp_Pkt lea sp_Msg(a5),a0 ;a0 = &packet->sp_Msg move.l a0,sp_Pkt+dp_Link(a5) ;p->sp_Pkt.dp_Link = &p->sp_Msg move.l a2,sp_Pkt+dp_Port(a5) ;p->sp_Pkt.dp_Port = replyport move.l #ACTION_SCREEN_MODE,sp_Pkt+dp_Type(a5) ;Set function tst.w d0 ;On or Off? beq.w 1$ move.l #-1,sp_Pkt+dp_Arg1(a5) ;RAW ON bra.b 2$ 1$ clr.l sp_Pkt+dp_Arg1(a5) ;RAW OFF 2$ movea.l a3,a0 movea.l a5,a1 SYS PutMsg,4 ;PutMsg(proc, packet) movea.l a2,a0 SYS WaitPort ;WaitPort(port) movea.l a2,a0 SYS GetMsg ;(void)GetMsg(port) movem.l (sp)+,a3/a5 rts * ------------------------------------------------------------------------- * * struct MsgPort *CreatePort(name, pri) (a0/d0) * ------------------------------------------------------------------------- * XDEF CreatePort CreatePort: movem.l d5/d7/a2/a5,-(sp) move.l a0,a5 ;Save Name move.l d0,d5 ;Save Pri * Allocate a free signal, crap out if we can't moveq #-1,d0 SYS AllocSignal,4 cmp.l #-1,d0 ;Did we get a signal? bne.b cpgotsig ;Yep moveq #0,d0 ;Otherwise return NULL bra.w cpdone cpgotsig: move.l d0,d7 ;Save our signal * Allocate memory for MsgPort moveq.l #MP_SIZE,d0 ;Size of MsgPort move.l #MEMF_PUBLIC!MEMF_CLEAR,d1 ;Type of memory SYS AllocMem ;Allocate it tst.l d0 ;Did we get it? bne.b cpgotport ;Yep move.l d7,d0 ;Otherwise crap out, free signal SYS FreeSignal moveq #0,d0 ;Return NULL bra.w cpdone cpgotport: move.l d0,a2 ;This is our new port! move.l a5,LN_NAME(a2) ;port->mp_Node.ln_Name = name move.b d5,LN_PRI(a2) ;port->mp_Node.ln_Pri = priority move.b #NT_MSGPORT,LN_TYPE(a2) ;port->mp_Node.ln_Type = NT_MSGPORT move.b #PA_SIGNAL,MP_FLAGS(a2) ;port->mp_Flags = PA_SIGNAL move.b d7,MP_SIGBIT(a2) ;port->mp_SIGBIT = sigBit suba.l a1,a1 SYS FindTask move.l d0,MP_SIGTASK(a2) ;port->mp_SIGTASK = FindTask(0L) cmpa.l #0,a5 ;Is this a new name? beq.b cpnoname ;Nope, add it to the msg list movea.l a2,a1 SYS AddPort ;Otherwise add this port move.l a2,d0 ;Return port pointer bra.b cpdone cpnoname: * Initialized New List head lea MP_MSGLIST(a2),a0 ;a0 = &port->mp_MsgList move.l a0,(a0) ;list->lh_Head = list addq.l #4,(a0) ;list->lh_Head += 4L clr.l 4(a0) ;list->lh_Tail = 0L move.l a0,8(a0) ;list->lh_TailPred = list move.l a2,d0 ;Return port pointer cpdone: movem.l (sp)+,d5/d7/a2/a5 rts * ------------------------------------------------------------------------- * * DeletePort(port)(d0) * ------------------------------------------------------------------------- * XDEF DeletePort DeletePort: move.l a5,-(sp) move.l d0,a5 tst.l LN_NAME(a5) ;Is there a name? beq.s dpnoname move.l d0,a1 SYS RemPort,4 ;RemPort(port) dpnoname: move.b #$ff,LN_TYPE(a5) ;port->mp_Node.ln_Type = 0xff move.l #-1,MP_MSGLIST(a5) ;port->mp_MsgList.lh_Head = -1L moveq #0,d0 move.b MP_SIGBIT(a5),d0 ;d0 = port->mp_SigBit SYS FreeSignal,4 ;FreeSignal(d0) moveq #MP_SIZE,d0 move.l a5,a1 SYS FreeMem ;FreeMem(port, sizeof(*port)) move.l (sp)+,a5 rts * ------------------------------------------------------------------------ * char *FibFileDate(fib_date) * register struct DateStamp *fib_date; * * Calculate date based on DateStamp structure and return a pointer * to the formatted date string. * ------------------------------------------------------------------------ XDEF _FibFileDate _FibFileDate: link a5,#0 movem.l d3-d7/a4,-(sp) movea.l 8(a5),a1 ;Grab datestamp pointer moveq #78,d7 ;Initial year = 1978 move.l (a1),d5 ;days = fib_date->ds_Days blt ffdbaddate ;Hey! you can't be negative! Invalid date... * Determine what year it is divu #1461,d5 move.l d5,d0 ;Stash it ext.l d5 lsl.l #2,d5 add.l d5,d7 ;year += (days / 1461) * 4 * Count how many months into that year ffdgetmo: swap d0 ;days %= 1461 move.w d0,d5 1$ tst.w d5 ;Out of days yet? beq 3$ ;Yep, done here move.w #365,d6 ;Else month_days = 365 move.w d7,d0 ;Grab year andi.w #3,d0 ;if (year & 3) == 0 Leap year? bne 2$ ;Nope addq.w #1,d6 ;Otherwise bump month_days 2$ cmp.w d6,d5 ;is day < month_days? blt 3$ ;yep, done here sub.w d6,d5 ;otherwise day -= month_days addq.l #1,d7 ; year++ bra 1$ 3$ * Count how many days into that month of that year ffdgetday: ;for (i = 0, day++; i < 12; i++) moveq #0,d4 ;current month = 0 moveq #0,d6 ;Zap hinybs addq.w #1,d5 lea _dayspermonth(a4),a0 1$ move.b 0(a0,d4.w),d6 ;month_days = dayspermonth[i] cmpi.w #1,d4 ;if (i == 1 && (year & 3) == 0) bne 2$ move.w d7,d0 andi.w #3,d0 bne 2$ addq.w #1,d6 ;month_days++ 2$ cmp.w d6,d5 ;if (day <= month_days) ble 4$ ;Break out, found the right month sub.w d6,d5 ;Else, day -= month_days addq.w #1,d4 ;i++ 3$ cmpi.w #12,d4 ;Done all months yet? blt 1$ ;Nope 4$ ffdprint: 1$ cmpi.l #99,d7 ;while (year >= 100) ble 2$ subi.l #100,d7 ;year -= 100 bra 1$ 2$ ;asprintf(datestr, "%02d-%02d-%02d %02d:%02d:%02d", i + 1, day, year, hour, min, sec) move.l 8(a1),d0 ;sec = fib_date->ds_Tick / 50; divu #50,d0 move.w d0,-(sp) ;Push secs moveq #0,d0 ;Zap reg move.w 6(a1),d0 ;min = fib_date->ds_Minute move.w d0,d1 ;Clone it divu #60,d0 move.w d0,d3 ;hour = min / 60 mulu #60,d0 sub.w d0,d1 ;min -= hour * 60 move.w d1,-(sp) ;Push mins move.w d3,-(sp) ;Push hours addq.w #1,d4 ;Push day of month (offset by 1!) move.w d5,-(sp) ;Push month move.w d4,-(sp) move.w d7,-(sp) ;Push year pea _datepat(a4) ;Push the format pattern pea _datestr(a4) ;Push destination buffer jsr _asprintf lea 20(sp),sp lea _datestr(a4),a0 move.l a0,d0 ;return((char *)&datestr[0]) ffddone: movem.l (sp)+,d3-d7/a4 unlk a5 rts ffdbaddate: lea _baddatestr(a4),a0 ;return (" <Invalid Date> "); move.l a0,d0 bra ffddone *---------------------------------------------------------------------- * LONG iswild(name) * char *name; * * Search a string for wild characters, return 1 if found *---------------------------------------------------------------------- XDEF _iswild _iswild: movea.l 4(sp),a0 ;Grab string pointer moveq #0,d0 ;Clear out our character register ischk1: move.b (a0)+,d0 ;Grab a char beq iwdone ;Might be end of string? cmpi.b #'*',d0 ;Is it *? beq iswdone ;yep, is wild cmpi.b #'?',d0 ;Is it a qmark bne ischk1 ;Nope, check next character iswdone: moveq #1,d0 iwdone: rts * ------------------------------------------------------------------------ ; Compare a wild card name with a normal name ; LONG wildmatch (name, wild) ; char *name, *wild; * ------------------------------------------------------------------------ XDEF _wildmatch _wildmatch: link a5,#-64 movem.l d3/a2-a3,-(sp) movea.l 8(a5),a2 ;Grab name movea.l 12(a5),a3 ;Grab pattern lea -64(a5),a0 ;back[0][0] lea -60(a5),a1 ;back[0][1] moveq #0,d3 ;bi = 0 wmloop1: tst.b (a2) ;End of name? bne wmnoteon tst.b (a3) ;End of pattern? beq wmmatched ;Yep, we matched wmnoteon: cmpi.b #'*',(a3) ;Is it a splat? bne wmnotstar ;Nope, maybe '?' cmpi.w #64,d3 ;Have we hit max expression depth? beq wmnomatch ;Yep, ran out of room in recursion table ;back[bi][0] = w move.l a3,0(a0,d3.w) ;Stash pointer to this '*' in table ;back[bi][1] = n move.l a2,0(a1,d3.w) addq.w #8,d3 ;++bi addq.w #1,a3 ;++w bra wmloop1 ;Check next wmgoback: subq.w #8,d3 ;--bi move.l a0,d0 wmback1: tst.w d3 ;while (bi >= 0 && *back[bi][1] == '\x0') blt wmbacked movea.l 0(a1,d3.l),a0 tst.b (a0) bne wmbacked subq.w #8,d3 ;--bi bra wmback1 wmbacked: tst.w d3 ;if (bi < 0) blt wmnomatch ;return (0) movea.l d0,a0 movea.l 0(a0,d3.w),a3 ;w = back[bi][0] + 1 addq.w #1,a3 addq.l #1,0(a1,d3.w) movea.l 0(a1,d3.l),a2 ;n = ++back[bi][1] addq.w #8,d3 ;++bi bra wmloop1 wmnotstar: cmpi.b #'?',(a3) ;Is it '?' bne wmnotqmark tst.b (a2) ;Reached end of string? bne wmincpoint ;Nope, move on to next char tst.w d3 ;Are we at top level of expression? beq wmnomatch ;Yep, expression didn't match bra wmgoback ;Otherwise pop a level and try to match wmnotqmark: move.b (a2),d0 ;Grab a char from bstr cmpi.b #$40,d0 ;less than @ character? bls 1$ ;Yep cmpi.b #$5a,d0 ;Greater than Z? bhi 1$ ;Yep addi.b #$20,d0 1$ move.b (a3),d1 ;Grab a char from bstr cmpi.b #$40,d1 ;less than @ character? bls 2$ ;Yep cmpi.b #$5a,d1 ;Greater than Z? bhi 2$ ;Yep addi.b #$20,d1 2$ cmp.b d0,d1 ;*n = *w? beq wmincpoint ;Yep, move on past tst.w d3 ;Are we at top expression level? beq wmnomatch ;Yep, they didn't match bra wmgoback ;Nope, process next part wmincpoint: tst.b (a2) ;Done with name? beq wmnamend ;Yep addq.w #1,a2 ;Otherwise increment name pointer wmnamend: tst.b (a3) ;End of pattern? beq wmmatched ;Yep, we matched addq.w #1,a3 ;Otherwise inc wild pointer, match next char bra wmloop1 wmmatched: moveq #1,d0 bra wmdone wmnomatch: moveq #0,d0 wmdone: movem.l (sp)+,d3/a2-a3 unlk a5 rts * --------------------------------------------------------------------- * * void SortFibs (keytype, direction, fibheadp) * d0 d1 a0 * int keytype; * struct FibEntry *fibheadp; * * Selection sort a linked list of FibEntrys based on a keycode * * --------------------------------------------------------------------- * XDEF _SortFibs _SortFibs: link a5,#0 movem.l d2-d4/a2-a3/a6,-(sp) move.l d0,d2 ;Save keytype move.l d1,d4 ;Save direction of sort movea.l a0,d3 ;Save fibheadp movea.l a0,a2 ;a2 = a0 = i sfILoop: cmp.l (a2),d3 ;i->NextFib != fibheadp? beq sfdone ;Nope, wrapped around to start movea.l a2,a6 ;k = i movea.l (a2),a3 ;j = i->NextFib sfJLoop: cmp.l a3,d3 ;j != fibheadp? beq sfJdone ;Nope compared them all, see if swapped any movea.l 8(a6),a0 ;a0 = k->Fibp movea.l 8(a3),a1 ;a1 = j->Fibp move.l d2,d0 ;d0 = keytype jsr _CompFibs ;d0 = CompFibs(keytype, k->Fibp, j->Fibp) tst.w d4 ;Reverse sort? beq 1$ ;Nope bchg.l #0,d0 ;Else reverse sense of return 1$ tst.l d0 ;Return != 0? beq sfNextJ ;Nope, these two are in order movea.l a3,a6 ;else k = j, this is new swap sfNextJ: movea.l (a3),a3 ;j = j->NextFib bra sfJLoop ;Check bounds sfJdone: cmpa.l a6,a2 ;k != i, did we swap? beq sfNextI ;Nope, i was in correct position already move.l 8(a6),d0 move.l 8(a2),8(a6) move.l d0,8(a2) ;Else SwapFibs (k, i) sfNextI: movea.l (a2),a2 ;i = i->NextFib bra sfILoop ;Check bounds sfdone: movem.l (sp)+,d2-d4/a2-a3/a6 unlk a5 rts * --------------------------------------------------------------------- * * int CompFibs (keytype, a, b) * d0 a0 a1 * int keytype; * struct FileInfoBlock *a, *b; * * Used by SortFibs to determine precedence of Fibs. * --------------------------------------------------------------------- * XDEF _CompFibs _CompFibs: tst.w d0 ;Alphabetize? bne cfnalpha ;Nope * Compare lexigraphically, ignoring case differences cfalpha: lea fib_FileName(a0),a0 ;a = &Fipb->fib_FileName lea fib_FileName(a1),a1 ;b = &Fipb->fib_FileName ; for(; *a && tolower(*a) == tolower(*b); a++, b++); lccstart: tst.b (a0) ;Is there a char here at source? beq lcceostr ;Nope, fell off the end move.b (a1)+,d1 ;Grab a char from bstr cmpi.b #$40,d1 ;less than @ character? bls 1$ ;Yep cmpi.b #$5a,d1 ;Greater than Z? bhi 1$ ;Yep addi.b #$20,d1 1$ move.b (a0)+,d0 ;Grab a char from astr cmpi.b #$40,d0 ;less than @ character? bls 2$ ;Yep cmpi.b #$5a,d0 ;Greater than Z? bhi 2$ ;Yep addi.b #$20,d0 2$ cmp.b d0,d1 ;are they the same? beq lccstart ;Yep, compare next pair of chars lcceostr: sub.b d1,d0 ;return(tolower(*astr) - tolower(*bstr)) bgt cftrue ; > 0?, return TRUE bra cffalse ;Else return FALSE cfnalpha: subq.w #1,d0 ;Size? bne cfnsize ;Nope * Compare fib_Sizes move.l fib_Size(a1),d0 ;d0 = bfib->fib_Size cmp.l fib_Size(a0),d0 ;a->fib_Size > b->fib_Size? blt cftrue ;Yep, return TRUE bgt cffalse ;<, return FALSE bra cfalpha ;Else it's a tie, alphabetize cfnsize: subq.w #1,d0 ;Time? bne cffalse ;Nope, an error! * Compare fib_DateStamps lea fib_DateStamp(a0),a0 ;a = &afib->fib_DateStamp lea fib_DateStamp(a1),a1 ;b = &bfib->fib_DateStamp move.l ds_Days(a1),d0 ;d0 = bdate->ds_Days cmp.l ds_Days(a0),d0 ;a->ds_Days > b->ds_Days? blt cftrue ;Yep, a is older bgt cffalse ;Else if a < b, b is older ;Else they are the same day, check min/tick move.l ds_Minute(a0),d0 sub.l ds_Minute(a1),d0 ;d0 = amin - bmin muls #3000,d0 ; * 3000 add.l ds_Tick(a0),d0 sub.l ds_Tick(a1),d0 ; + atick - btick bgt cftrue ;Hey, a > b blt cffalse ;a < b, return false bra cfalpha ;Else its the same date, alphabetize cftrue: moveq #1,d0 rts cffalse: moveq #0,d0 rts * --------------------------------------------------------------------- * SECTION __MERGED,DATA * --------------------------------------------------------------------- * XDEF _dayspermonth _dayspermonth: dc.b 31,28,31,30,31,30,31,31,30,31,30,31 XDEF _datepat _datepat: dc.b '%02d-%02d-%02d %02d:%02d:%02d',0 CNOP 0,2 XDEF _baddatestr _baddatestr: dc.b '00-00-00 00:00:00',0 CNOP 0,2 XDEF gwbrstr gwbrstr: dc.b $9b,'0 q' * --------------------------------------------------------------------- * SECTION __MERGED,BSS * --------------------------------------------------------------------- * XDEF _datestr _datestr: ds.b 40 * --------------------------------------------------------------------- * END * --------------------------------------------------------------------- * SHAR_EOF cat << \SHAR_EOF > makefile # ------------------------------------------- # LS 2.0 lmkfile by Justin V. McCormick 88/11/27 # ------------------------------------------- OBJS = c2.o ls.o lssup.o DEST = lcs ASM = casm ASFLAGS = -cv AINC = ainc: LC = lc LC1 = lc1 LC2 = lc2 LCFLAGS = -ccfmsu -d2 -rr -v LINK = blink LIBS = lib:lcr.lib LNMAP = F H L S X PLAIN WIDTH 84 HEIGHT 0 SWIDTH 20 .a.o: $(ASM) -a $*.a -o$*.o -i$(AINC) $(ASFLAGS) .c.o: $(LC) $(LCFLAGS) $* $(DEST): $(OBJS) $(LINK) $(OBJS) to $(DEST) LIB $(LIBS) WITH $(DEST).lnk MAP $(DEST).map $(LNMAP) SHAR_EOF # End of shell archive exit 0 -- Bob Page, U of Lowell CS Dept. page@swan.ulowell.edu ulowell!page Have five nice days.