[comp.sources.amiga] v90i133: ls 3.1 - UNIX-like ls utility, Part03/03

Amiga-Request@cs.odu.edu (Amiga Sources/Binaries Moderator) (04/11/90)

Submitted-by: kim@uts.amdahl.com (Kim E. DeVaughn)
Posting-number: Volume 90, Issue 133
Archive-name: unix/ls-3.1/part03

#!/bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 3 (of 3)."
# Contents:  src/ls.c
# Wrapped by tadguy@xanth on Tue Apr 10 17:22:51 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/ls.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/ls.c'\"
else
echo shar: Extracting \"'src/ls.c'\" \(43452 characters\)
sed "s/^X//" >'src/ls.c' <<'END_OF_FILE'
X/* ---------------------------------------------------------------------
X  LS.C -- Source code for the "improved" directory utility.
X
X  Author: Justin V. McCormick
X
X  V1.0    August 1986 Written from scratch, my first Amiga project.
X  V2.0  November 1988 Revised for Lattice 5.0 and made 1.3 compatible.
X  V2.1  December 1988 Minor size reduction, fixed a few bugs from 2.0.
X  V2.2  December 1988 Fixed status return and multiple wildcard pathnames.
X  V3.0  July 25, 1989 Instant sorting, best-fit output, new features.
X  V3.1  July 29, 1989 Bug fixes, new output width and height options.
X
XNotice:
X
X  This program is placed in the public domain with the
Xunderstanding that the author makes no claims or guarantees with
Xregard to its suitability for any given application, and that
Xthe author assumes no responsibility for damages incurred by its
Xusage.  Any resemblance of this program to any other program,
Xeither living or dead, is purely coincidental.
X
X  Feel free to borrow this code and make millions of dollars
Xfrom its sale or commercial use, but please give credit where
Xcredit is due.
X
XSynopsis:
X
X  Features intelligent columnar listing, versatile sort options,
XUNIX-style pattern matching, recursive subdirectory listing, and
Xmore!
X
XUsage:
X        LS [options] [pattern1] [pattern2] ...
X
XBugs and Limitations:
X
X  LS cannot pattern match devices (like "dh*:"), since this
Xwould involve another level of recursion and parsing the Device
XList.  LS shows signs of the same "creeping featurism" which
Xafflicted the UNIX command, evident from its bloated code size.
X
XChanges From 1.0 to 2.0:
X
X o Source code prototyped, linted, traced, optimized, tweaked, etc.
X o Made resident ("pseudo-pure") by linking with cres.o from LC 5.0.
X o High-volume routines recoded in assembly (lssup.a).
X o Now handles multiple paths/files on a command line, up to 30.
X o New sort flags, including no sort.
X o Enhanced wildcards, understands complex *.?*.* expressions now.
X o More efficient ExNext() performance, less ram used for recursion.
X o SIGBREAKF_CTRL_C signal (Ctrl-C) cleanly aborts at any point now.
X o Command line parser handles quoted pathnames now (LC 5.0 benefit).
X o Short listing finally auto-adjusts to new console window sizes!
X o Pen color escape codes bypassed when redirecting long output.
X o Sorting by size or date is also subsorted alphabetically now.
X o Long listing shows new 1.3 file attributes, plus comment indicator.
X o File dates are now in international format, YY-MM-DD.
X o Fixed listings with files datestamped after 99-12-31 (overflow).
X o Fixed listings with files datestamped before 78-01-01 (time < 0).
X
XChanges From 2.0 to 2.1
X
X o Fixed the show comment feature, a last minute bug in 2.0.
X o Fixed the "Unknown option ''" message problem.
X o Optimized the assembly branches, reduced code size another few bytes.
X
XChanges From 2.1 to 2.2
X
X o Fixed erroneous Status returns.
X o Fixed bug with multiple wildcarded pathnames.
X o Compiled with LC 5.0 Patch 3 and CAPE 2.0, saved another 46 bytes.
X o Eliminated an extra stpcpy(), saved another few bytes.
X
XChanges From 2.2 to 3.0
X
X o Added many new command line options!  See Usage descriptions and docs.
X o "Instant" output sorting, now using an on-the-fly insertion sort.
X o New Short listing technique is row-by-row, redirects to PRT: or file.
X o Better columnization of short listings, uses best-fit optimization.
X o Formatted output parsing system implemented for long listings.
X o Improved ^C handling, now breaks immediately in mid-output.
X o New command line parser, now handles multiple options mixed with names.
X o Added separate pattern matching for directories and files.
X o Now inhibits system requesters from popping up by default.
X o Added custom cres.o module for more size savings.
X o Now uses Exec List functions for smaller, faster code.
X o Fixed random cli_ReturnCode and pr_Result2 value CLI returns.
X o Rewrote many sections, further code cleaning/commenting.
X o Eliminated main(), LS handles the _main() function for smaller code.
X o Compiled with 5.03.90 (beta) Lattice compiler, saved a few bytes.
X
XChanges From 3.0 to 3.1
X o Fixed random errors from uninitialized argument count in GetCLIArgs().
X o New -X and -Y options to specify short format columns and rows.
X
X --------------------------------------------------------------------- */
X
X/* ---------------------- LS SOURCE ---------------------------------- */
X
X/* Use Real strlen unless you comment out the following */
X#define strlen strlen
X
X/* Maximum number of command line arguments */
X#define MAXARG 32
X#define PADTABSIZE 100
X#define WORKSIZE 300
X
X#include "WORK:ls.h"
X
X/* Extern CODE from lssup.a */
Xextern BYTE *aindex __ARGS((BYTE *, BYTE));
Xextern LONG  __stdargs asprintf __ARGS((BYTE *, BYTE *,...));
Xextern LONG CompareDateStamps __ARGS((struct DateStamp *, struct DateStamp *));
Xextern LONG CompFibs __ARGS((LONG, struct FileInfoBlock *, struct FileInfoBlock *));
Xextern LONG FillFibEntry __ARGS((struct List *, struct FileInfoBlock *));
Xextern LONG GetFileString __ARGS((BYTE *, BYTE *));
Xextern LONG GetPathString __ARGS((BYTE *, BYTE *));
Xextern LONG iswild __ARGS((BYTE *));
Xextern LONG wildmatch __ARGS((BYTE *, BYTE *));
Xextern VOID *myalloc __ARGS((LONG));
Xextern VOID FibFileDate __ARGS((struct DateStamp *, BYTE *, BYTE *));
Xextern VOID GetWinBounds __ARGS((LONG *, LONG *));
Xextern VOID InsertFibNode __ARGS((struct List *, struct FibEntry *));
Xextern VOID MakePathString __ARGS((struct FileLock *, BYTE *));
Xextern VOID myfree __ARGS((VOID *));
Xextern VOID NoFileExit __ARGS((BYTE *));
Xextern VOID NoMemExit __ARGS((VOID));
X
X/* Local DATA */
Xstruct DateStamp theolddate;     /* Show files older than this date */
Xstruct DateStamp thenewdate;     /* Show files newer than this date */
Xstruct FileHandle *ConOut;       /* Standard output */
Xstruct FileHandle *ConIn;        /* Standard input */
Xstruct FileLock *CurFLock;       /* Global Directory lock */
Xstruct FileInfoBlock *GFibp;     /* Global FIB for Examine/ExNext */
XAPTR OldWindowPtr;               /* Copy of what was in pr_WindowPtr */
X
X/* User flags, See ls.h for bit definitions */
XULONG LSFlags = SHOWDIRS | SHOWFILES;
X
XLONG gdircount;                 /* Grand total # of dirs for Recursive */
XLONG gfilecount;                /* Grand total # of files found */
XLONG gtotblocks;                /* Grand total # of blocks */
XLONG gtotbytes;                 /* Grand total # of bytes */
X
XLONG dircount;                  /* Number of directories found */
XLONG filecount;                 /* Number of files found */
XLONG totblocks;                 /* total # of blocks */
XLONG totbytes;                  /* total # of bytes */
XLONG maxnamlen;                 /* Longest name found */
XLONG sortkey;                   /* 0=alpha, 1=size, 2=date */
X
XBYTE padtab[PADTABSIZE];        /* Column table tab amounts */
XBYTE thePath[WORKSIZE];         /* Current filename space */
XBYTE theDirPat[WORKSIZE];       /* Dirname pattern workspace */
XBYTE theFilePat[WORKSIZE];      /* Filename pattern workspace */
XBYTE workstr[WORKSIZE+64];      /* Temp string space */
X
XBYTE theblksstr[20];
XBYTE thedatestr[12];
XBYTE theprotstr[12];
XBYTE thesizestr[20];
XBYTE thetimestr[12];
X
XLONG CurWinRows;                /* Window bounds, determined at runtime */
XLONG CurWinCols;
X
X/* Initialized Strings */
XBYTE Author[]  = "\23333mLS 3.1\2330m PD Freeware by Justin V. McCormick 89-07-29";
XBYTE usage[]   = "\n  Usage: LS [-options <args>] [path1] [path2] ...\n";
XBYTE usage0[]  = "\tc  Show filenotes           D  Show dirs last\n";
XBYTE usage1[]  = "\td  Dirs only                H  No headings and subtotals\n";
XBYTE usage2[]  = "\tf  Files only               I  No page prompts\n";
XBYTE usage3[]  = "\tk  No ANSI display codes    M  Mixed output files and dirs\n";
XBYTE usage4[]  = "\tl  Long listing             N <name> Show newer than entry\n";
XBYTE usage5[]  = "\tn  No sort                  O <name> Show older than entry\n";
XBYTE usage6[]  = "\tp  Permit system requests   P  Show full pathnames\n";
XBYTE usage7[]  = "\tr  Reverse sort             R  Recursive listing\n";
XBYTE usage8[]  = "\ts  Sort by size             T  Total all subtotals\n";
XBYTE usage9[]  = "\tt  Sort by date             X <wide> Set output columns\n";
XBYTE usage10[] = "\tv <pattern> Avoid pattern   Y <high> Set output rows\n";
XBYTE usage11[] = "\tF <format> Format output (default: \042";
XBYTE usage12[] = "\042)\n";
X
X/* Date and output format strings */
XBYTE baddatestr[]     = "00-00-00";
XBYTE badtimestr[]     = "00:00:00";
XBYTE datepat[]        = "%02ld-%02ld-%02ld";
XBYTE timepat[]        = "%02ld:%02ld:%02ld";
XBYTE dayspermonth[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
XBYTE deffmtstr[40]    = "%p %d %t %4b %8s %n\\n";
XBYTE LongFmtStr[]     = "%ld";
XBYTE totalfmtstr[]    = "Dirs:%-4ld Files:%-4ld Blocks:%-5ld Bytes:%-8ld\n";
XBYTE TotHeaderMsg[]   = "Totals:\n";
XBYTE ColonStr[]       = ":";
XBYTE SlashStr[]       = "/";
XBYTE RamNameStr[]     = "RAM:";
X
X/* ANSI color control strings */
XBYTE penstr0[]        = "\2330m";       /* Reset CON:   */
XBYTE penstr1[]        = "\2330 p";      /* Cursor off   */
XBYTE penstr2[]        = "\233 p";       /* Cursor on    */
XBYTE penstr3[]        = "\23333m";      /* Color 3 NORM */
XBYTE penstr4[]        = "\2331;33;40m"; /* Color 3 BOLD */
XBYTE penstr5[]        = "\2331;31;40m"; /* Color 1 BOLD */
X
X/* CON: command sequence for window bounds report */
XBYTE gwbrstr[4] = {  0x9b, '0', ' ', 'q' };
X
X/* Newline and "" source */
XBYTE NLine[4] = { 10, 0, 0, 0 };
X
X/* Error messages */
XBYTE BadOptFmtStr[]   = "Unknown option: -%s\n";
XBYTE ErrFmtStr[]      = "LS: %s, Error #%ld\n";
XBYTE NoExamFmtStr[]   = "Cannot examine file or directory, Error #%ld\n";
XBYTE NoFindFmtStr[]   = "'%s' not found";
XBYTE NoRAMMsg[]       = "No RAM";
XBYTE NoWildPathMsg[]  = "Unable to pattern match paths";
XBYTE VolEmptyMsg[]    = "Volume or directory is empty.\n";
XBYTE NoMatchMsg[]     = "No match.\n";
X
X/* Pointers to usage strings for quick easy output */
XBYTE *usagestrs[] =
X{
X  Author, usage, usage0, usage1, usage2, usage3, usage4, usage5,
X  usage6, usage7, usage8, usage9, usage10, usage11, deffmtstr, usage12, 0L
X};
X
XBYTE *thefmtstr = deffmtstr;    /* Format string pointer for long output */
XBYTE *curpath;
XBYTE *theAntiPat;		/* Avoid pattern string */
X
X/* Local CODE */
XBYTE *GetDecNum __ARGS((BYTE *, LONG *));
XLONG CmpDateBounds __ARGS((struct DateStamp *));
XLONG GetFileDate __ARGS((BYTE *, struct DateStamp *));
XLONG ParseCmdOptions __ARGS((LONG, LONG, BYTE **));
Xstruct FibEntry *AllocFib __ARGS((VOID));
Xstruct FibEntry *ModNextFib __ARGS((struct FibEntry *, LONG));
Xstruct List *GetDir __ARGS((struct FileLock *, struct FileInfoBlock *));
XVOID CleanUp __ARGS((BYTE *, LONG, LONG));
XVOID DirIt __ARGS((struct FileLock *, BYTE *));
XVOID FreeAllFibs __ARGS((struct List *));
XVOID GetCLIArgs __ARGS((BYTE *, LONG *, BYTE **));
XVOID LListDir __ARGS((struct List *));
XVOID LListEntry __ARGS((struct FileInfoBlock *));
XVOID LongList __ARGS((LONG *, LONG *, struct List *));
XVOID PagePrompt __ARGS((LONG, LONG));
XVOID ParseFormatOpts __ARGS((struct FileInfoBlock *));
XVOID SetConPen __ARGS((BYTE *));
XVOID SListDir __ARGS((struct List *));
XVOID TestBreak __ARGS((VOID));
XVOID Usage __ARGS((VOID));
XVOID WCHR __ARGS((BYTE *));
XVOID WSTR __ARGS((BYTE *));
XVOID _main __ARGS((BYTE *));
X
X/* --------------------------------------------------------------------
X * Allocate a FibEntry structure and associated FileInfoBlock.
X * -------------------------------------------------------------------- */
Xstruct FibEntry *AllocFib ()
X{
X  struct FibEntry *tfibp;
X
X  if ((tfibp = myalloc ((LONG)(sizeof (struct FibEntry) + sizeof (struct FileInfoBlock)))) != 0)
X  {
X    tfibp->fe_Fib = (struct FileInfoBlock *)((ULONG)tfibp + sizeof (struct FibEntry));
X  }
X  else
X    LSFlags |= BREAKFLAG;
X  return (tfibp);
X}
X
X/* --------------------------------------------------------------------
X * Use AmigaDOS to output a string to the stdout channel
X * -------------------------------------------------------------------- */
XVOID WSTR (tstring)
X  BYTE *tstring;
X{
X  LONG i;
X
X  i = strlen (tstring);
X  if (i > 0)
X  {
X    (VOID) Write ((BPTR)ConOut, tstring, i);
X  }
X}
X
X/* --------------------------------------------------------------------
X * Use AmigaDOS to put a character to the stdout
X * -------------------------------------------------------------------- */
XVOID WCHR (ch)
X  BYTE *ch;
X{
X  (VOID) Write ((BPTR)ConOut, ch, 1L);
X}
X
X/* --------------------------------------------------------------------
X * Check to see if the user hit ^C
X * -------------------------------------------------------------------- */
XVOID TestBreak ()
X{
X  ULONG oldsig;
X
X  oldsig = SetSignal (0L, (LONG)SIGBREAKF_CTRL_C);
X  if ((oldsig & SIGBREAKF_CTRL_C) != 0)
X  {
X    WSTR ("\2330m\233 p**BREAK\n");
X    LSFlags |= BREAKFLAG;
X  }
X}
X
X/* --------------------------------------------------------------------
X * Prompt the user to hit return, wait till return is hit
X * -------------------------------------------------------------------- */
XVOID PagePrompt (page, maxpage)
X  LONG page, maxpage;
X{
X  if (CurWinCols > maxnamlen && page > 1 && (LSFlags & NOINTERACT) == 0)
X  {
X    WSTR ("\2337m -- MORE -- ");
X    if (CurWinCols >= 27)
X      WSTR ("Press Return: ");
X    WSTR ("\2330m\233 p");
X    (VOID) Read ((BPTR)ConIn, workstr, 1L);
X    WSTR ("\2330 p\233F\233K");
X    TestBreak ();
X  }
X
X  if ((LSFlags & (NOHEADERS|BREAKFLAG)) == 0)
X  {
X    if ((LSFlags & CONSOLE) != 0)
X    {
X      WSTR ("\2330 p\2334;33m");
X    }
X    (VOID) asprintf (workstr, "Page %ld of %ld:", page, maxpage);
X    WSTR (workstr);
X    SetConPen (penstr0);
X    WSTR (NLine);
X  }
X}
X
X/* -------------------------------------------------------------------- */
Xstruct FibEntry *ModNextFib(tfibp, rows)
X  struct FibEntry *tfibp;
X  LONG rows;
X{
X  LONG i;
X
X  for (i = 0; i < rows && tfibp->fe_Node.mln_Succ != 0; i++)
X  {
X    tfibp = (struct FibEntry *)tfibp->fe_Node.mln_Succ;
X  }
X  return(tfibp);
X}
X
X/* --------------------------------------------------------------------
X * set CON: character color to default Pen1 colors 
X * -------------------------------------------------------------------- */
XVOID SetConPen (penstr)
X  BYTE *penstr;
X{
X  if ((LSFlags & CONSOLE) != 0)
X    WSTR (penstr);
X}
X
X/* #define DEBUGSLD 1 */
X/* --------------------------------------------------------------------
X * List a FibEntry list in a compact fashion
X * -------------------------------------------------------------------- */
XVOID SListDir (fibheadp)
X  struct List *fibheadp;
X{
X  LONG avglen;
X  LONG colcnt;
X  LONG currow;
X  LONG dfcount;
X  LONG i, j, wlen;
X  LONG maxcol;
X  LONG maxpage;
X  LONG maxrow;
X  LONG maxwinrow;
X  LONG pagecnt;
X  LONG rowcnt;
X  LONG tlen;
X  LONG totlen;
X  struct FibEntry *hfibp, *tfibp;
X
X  SetConPen (penstr1);        /* Turn the cursor off since it will blink anyway */
X  GetWinBounds (&colcnt, &currow); /* Get current window size */
X
X  if (CurWinCols == 0)
X    CurWinCols = colcnt;
X  if (CurWinRows == 0)
X    CurWinRows = currow;
X
X/* Make a average-case WxH estimate for # of display columns */
X  for (totlen = dfcount = 0, hfibp = (struct FibEntry *)fibheadp->lh_Head;
X       hfibp->fe_Node.mln_Succ != 0;
X       hfibp = (struct FibEntry *)hfibp->fe_Node.mln_Succ)
X  {
X    if (hfibp->fe_Fib->fib_DirEntryType > 0 && (LSFlags & CONSOLE) == 0)
X      (VOID) strcat (hfibp->fe_Fib->fib_FileName, SlashStr);
X    totlen += strlen (hfibp->fe_Fib->fib_FileName);
X    dfcount++;
X  }
X
X/* Calc average length of all entries */
X  avglen = totlen / dfcount;            /* Avg name length = totlen/numentries */
X  if ((totlen % dfcount) != 0)
X    avglen++;
X
X  if ((CurWinCols) <= maxnamlen)        /* Longest name wider than window? */
X    maxcol = 1;                         /* Yep, just print one column */
X  else
X  {
X  /* Else maxcols = winwidth/namewidth */
X    for (maxcol = 0, colcnt = CurWinCols; colcnt >= avglen; maxcol++)
X    {
X      colcnt -= avglen + 2;
X    }
X  }
X#ifdef DEBUGSLD
X  asprintf(workstr, "avg:%ld max:%ld\n", avglen, maxnamlen); WSTR(workstr);
X#endif
X
X/* Dry run output avg-case WxH table to see if it needs adjusting */
X  for (;;)
X  {
X  /* Clear out previous padtab */
X    memset(padtab, 0, PADTABSIZE);
X
X  /* Number of rows = total entries / entries per row */
X    maxrow = dfcount / maxcol;
X    if ((dfcount % maxcol) != 0)        /* Round up if non-integral */
X      maxrow++;
X#ifdef DEBUGSLD
X    asprintf(workstr, "avg: %ld rows by %ld cols\n", maxrow, maxcol); WSTR(workstr);
X#endif
X
X    for (rowcnt = 0, hfibp = (struct FibEntry *)fibheadp->lh_Head;
X         rowcnt < maxrow && hfibp->fe_Node.mln_Succ != 0;
X         rowcnt++, hfibp = (struct FibEntry *)hfibp->fe_Node.mln_Succ)
X    {
X      for (colcnt = 0, tfibp = hfibp;
X           colcnt < maxcol && tfibp->fe_Node.mln_Succ != 0;
X           colcnt++, tfibp = ModNextFib (tfibp, maxrow))
X      {
X        tlen = strlen (tfibp->fe_Fib->fib_FileName);
X        if (tlen > padtab[colcnt])
X          padtab[colcnt] = tlen;
X      }
X
X    /* If this is the first row, calc actual maxcol/maxrow for this dfcount */
X      if (rowcnt == 0)
X      {
X        maxcol = colcnt;
X        maxrow = dfcount / maxcol;
X        if ((dfcount % maxcol) != 0)    /* Round up if non-integral */
X          maxrow++;
X      }
X    }
X
X  /* Calculate actual total width by adding up width of all columns */
X    for (colcnt = totlen = 0; (colcnt + 1) < maxcol; colcnt++)
X    {
X      totlen += (LONG)padtab[colcnt] + 2;
X#ifdef DEBUGSLD
X      asprintf(workstr, "padtab[%ld]=%ld\n", colcnt, (LONG)padtab[colcnt]); WSTR(workstr);
X#endif
X    }
X    totlen += (LONG)padtab[colcnt];
X#ifdef DEBUGSLD
X    asprintf(workstr, "padtab[%ld]=%ld\n", colcnt, (LONG)padtab[colcnt]); WSTR(workstr);
X    asprintf(workstr, "totlen %ld\n", totlen); WSTR(workstr);
X#endif
X
X  /* if More than one column and 
X   * total width of all columns is greater > our window width,
X   * then decrease number of display columns
X   */
X    if (maxcol > 1 && totlen > CurWinCols)
X    {
X      maxcol--;
X#ifdef DEBUGSLD
X      asprintf(workstr, "new maxcol:%ld\n", maxcol); WSTR(workstr);
X#endif
X    }
X    else
X      break;
X  }
X#ifdef DEBUGSLD
X  asprintf(workstr, "adjusted: maxrow:%ld maxcol:%ld (winwidth:%ld totlen:%ld)\n", maxrow, maxcol, CurWinCols, totlen); WSTR(workstr);
X#endif
X
X/* Calc number of pages */
X  maxwinrow = CurWinRows - 3;
X  if (maxwinrow <= 0)
X    maxwinrow = 1;
X  pagecnt = 1;
X  maxpage = maxrow / maxwinrow;
X  if ((maxrow % maxwinrow) != 0)
X    maxpage++;
X
X/* Do actual output scan */
X  for (rowcnt = 0, currow = maxwinrow, hfibp = (struct FibEntry *)fibheadp->lh_Head;
X       (LSFlags & BREAKFLAG) == 0 && rowcnt < maxrow && hfibp->fe_Node.mln_Succ != 0;
X       TestBreak(), rowcnt++, currow++, hfibp = (struct FibEntry *)hfibp->fe_Node.mln_Succ)
X  {
X    if (maxpage > 1 && currow == maxwinrow)
X    {
X      currow = 0;
X      if ((LSFlags & CONSOLE) != 0)
X      {
X        PagePrompt (pagecnt, maxpage);
X        if ((LSFlags & BREAKFLAG) != 0)
X          break;
X      }
X      pagecnt++;
X    }
X
X    for (colcnt = 0, tfibp = hfibp;
X         colcnt < maxcol && tfibp->fe_Node.mln_Succ != 0;
X         colcnt++)
X    {
X      if (tfibp->fe_Fib->fib_DirEntryType < 0)
X      {
X      /* Print a file entry */
X        (VOID) stpcpy (workstr, tfibp->fe_Fib->fib_FileName);
X        wlen = strlen (workstr);
X      }
X      else
X      {
X      /* Print a directory entry */
X        workstr[0] = 0;
X        wlen = strlen (tfibp->fe_Fib->fib_FileName);
X        if ((LSFlags & CONSOLE) != 0)
X          (VOID) strcat (workstr, penstr3);
X        (VOID) strcat (workstr, tfibp->fe_Fib->fib_FileName);
X        if ((LSFlags & CONSOLE) != 0)
X          (VOID) strcat (workstr, penstr0);
X      }
X
X    /* Move along list to the next entry, mod maxcol, print this entry */
X      tfibp = ModNextFib (tfibp, maxrow);
X
X    /* If this is not the last column, pad with spaces till we get to next column */
X      if ((colcnt + 1) < maxcol && tfibp->fe_Node.mln_Succ != 0)
X      {
X        for (i = (LONG)padtab[colcnt] + 1, j = strlen (workstr); i >= wlen; i--, j++)
X          workstr[j] = ' ';
X        workstr[j] = 0;
X      }
X    /* Output the final entry */
X      WSTR (workstr);
X    }
X
X  /* Filled this row, start next down */
X    WCHR (NLine);
X  }
X
X  SetConPen (penstr2);        /* Turn cursor back on */
X}
X
X/* -------------------------------------------------------------------- */
XBYTE *GetDecNum (cp, spccnt)
X  BYTE *cp;
X  LONG *spccnt;
X{
X  for (*spccnt = 0; *cp >= '0' && *cp <= '9'; cp++)
X  {
X    *spccnt = *spccnt * 10 + (LONG)*cp - '0';
X  }
X  return (cp);
X}
X
X/* -------------------------------------------------------------------- */
XVOID ParseFormatOpts (fib)
X  struct FileInfoBlock *fib;
X{
X  BYTE *cp1, *cp2, *reps;
X  BYTE *pathend, *thenamestr;
X  LONG i, spccnt;
X
X  i = strlen (curpath);
X  pathend = curpath + i;
X  if (i > 1 && *(curpath + i - 1) != ':')
X  {
X    *(curpath + i) = '/';
X    i++;
X    *(curpath + i) = 0;
X  }
X  thenamestr = curpath + strlen(curpath);
X  cp2 = thenamestr;
X  if (fib->fib_DirEntryType > 0 && (LSFlags & CONSOLE) != 0)
X    cp2 = stpcpy (cp2, penstr3);
X  cp2 = stpcpy (cp2, fib->fib_FileName);
X  if (fib->fib_DirEntryType > 0 && (LSFlags & CONSOLE) != 0)
X    (VOID) stpcpy (cp2, penstr0);
X
X  for (cp1 = workstr, cp2 = thefmtstr; *cp2 != 0; cp2++)
X  {
X    if (*cp2 != '%' && *cp2 != '\\')
X      *cp1++ = *cp2;
X    else
X    {
X      if (*cp2 == '%')
X      {
X        cp2++;
X        cp2 = GetDecNum (cp2, &spccnt);
X        if (spccnt > 99)
X        {
X          spccnt = 99;
X        }
X          
X        switch (*cp2)
X        {
X          case 'p':
X            reps = theprotstr;
X            break;
X          case 'd':
X            reps = thedatestr;
X            break;
X          case 't':
X            reps = thetimestr;
X            break;
X          case 'b':
X            reps = theblksstr;
X            break;
X          case 's':
X            reps = thesizestr;
X            break;
X          case 'n':
X            if (fib->fib_DirEntryType > 0 && (LSFlags & CONSOLE) != 0 && spccnt > 0)
X              spccnt += 7;
X            if ((LSFlags & FULLPATHNAMES) != 0)
X              reps = curpath;
X            else
X              reps = thenamestr;
X            break;
X          case '%':
X            *cp1++ = '%';
X            *cp1++ = 0;
X          default:
X            reps = &NLine[1];
X            break;
X        }
X        for (i = strlen(reps); i < spccnt; i++)
X          *cp1++ = ' ';
X        cp1 = stpcpy (cp1, reps);
X      }
X      else
X      {
X        cp2++;
X        switch (*cp2)
X        {
X          case 'n':
X            *cp1++ = '\n';
X            break;
X          case 't':
X            *cp1++ = '\t';
X            break;
X          case '\\':
X            *cp1++ = '\\';
X            break;
X          default:
X            break;
X        }
X        *cp1 = 0;
X      }
X    }
X  }
X  WSTR (workstr);
X
X  if ((LSFlags & NOTEFLAG) != 0 && fib->fib_Comment[0] != 0)
X  {
X    SetConPen(penstr3);
X    (VOID)asprintf(workstr, "/* %s */\n", fib->fib_Comment);
X    WSTR (workstr);
X    SetConPen(penstr0);
X  }
X  *pathend = 0;
X}
X
X/* --------------------------------------------------------------------
X * Verbosely list a particular FibEntry
X * -------------------------------------------------------------------- */
XVOID LListEntry (fib)
X  struct FileInfoBlock *fib;
X{
X  LONG i, pmodes;
X
X  pmodes = fib->fib_Protection & 0xff;
X  (VOID)stpcpy (theprotstr, "chsparwed");
X  for (i = 3; i >=  0; i--)
X  {
X    if ((pmodes & (1 << i)) != 0)
X      theprotstr[8 - i] = '-';
X    if ((pmodes & (1 << (i + 4))) == 0)
X      theprotstr[4 - i] = '-';
X  }
X
X  if (fib->fib_Comment[0] == 0)
X    theprotstr[0] = '-';
X
X  FibFileDate (&fib->fib_Date, thedatestr, thetimestr);
X
X  if (fib->fib_DirEntryType > 0)
X  {
X    (VOID)stpcpy (theblksstr, "0");
X    (VOID)stpcpy (thesizestr, "Dir");
X  }
X  else
X  {
X    (VOID) asprintf (theblksstr, LongFmtStr, fib->fib_NumBlocks);
X    (VOID) asprintf (thesizestr, LongFmtStr, fib->fib_Size);
X  }
X	
X  ParseFormatOpts (fib);
X}
X
X/* --------------------------------------------------------------------
X * List a directory in a verbose informative manner
X * -------------------------------------------------------------------- */
XVOID LListDir (fibheadp)
X  struct List *fibheadp;
X{
X  struct FibEntry *tfibp;
X
X  SetConPen(penstr1);
X
X  totblocks = totbytes = 0;
X  for (tfibp = (struct FibEntry *)fibheadp->lh_Head;
X       tfibp->fe_Node.mln_Succ != 0;
X       tfibp = (struct FibEntry *)tfibp->fe_Node.mln_Succ)
X  {
X    TestBreak();
X    if ((LSFlags & BREAKFLAG) != 0)
X      return;
X    LListEntry (tfibp->fe_Fib);
X    if (tfibp->fe_Fib->fib_DirEntryType < 0)
X    {
X      totblocks += tfibp->fe_Fib->fib_NumBlocks;
X      totbytes += tfibp->fe_Fib->fib_Size;
X    }
X  }
X
X  if ((LSFlags & (BREAKFLAG | NOHEADERS)) == 0)
X  {
X    (VOID) asprintf (workstr, totalfmtstr, dircount, filecount, totblocks, totbytes);
X    WSTR (workstr);
X  }
X
X  SetConPen(penstr2);
X}
X
X/* -------------------------------------------------------------------- */
XLONG CmpDateBounds (tdate)
X  struct DateStamp *tdate;
X{
X  if ((LSFlags & SHOWNEWERTHAN) != 0)
X  {
X    if (CompareDateStamps(&thenewdate, tdate) >= 0)
X      return (0L);
X  }
X  if ((LSFlags & SHOWOLDERTHAN) != 0)
X  {
X    if (CompareDateStamps(tdate, &theolddate) >= 0)
X      return (0L);
X  }
X  return (1L);
X}
X
X/* --------------------------------------------------------------------
X * Free up memory allocated to a linked list of FibEntrys
X * -------------------------------------------------------------------- */
XVOID FreeAllFibs (fibheadp)
X  struct List *fibheadp;
X{
X  struct FibEntry *tfibp;
X
X  if (fibheadp != 0)
X  {
X    while (fibheadp->lh_Head->ln_Succ != 0)
X    {
X      tfibp = (struct FibEntry *)RemTail(fibheadp);
X      myfree (tfibp);
X    }
X
X  /* Now free the MinList itself */
X    myfree (fibheadp);
X  }
X}
X
X/* --------------------------------------------------------------------
X * Allocate and fill a linked list of FileInfoBlocks
X * -------------------------------------------------------------------- */
Xstruct List *GetDir (lockp, fibp)
X  struct FileLock *lockp;
X  struct FileInfoBlock *fibp;
X{
X  BYTE *thepat;                /* Pattern pointer to dir or file pattern */
X  LONG matchstat;              /* Result of wildmatch() */
X  LONG nextstat;               /* Status of ExNext() */
X  LONG tempnamlen;             /* Compare length for strings */
X  LONG dfcount;
X  struct List *fibhead;        /* Temp list of Fibs created */
X  struct List *dirhead;        /* Temp list of dirs if SHOWDIRS == 0 */
X
X  maxnamlen = dfcount = dircount = filecount = 0L;
X
X/* Initialize an exec list of nodes, zero entries */
X  if ((fibhead = myalloc ((LONG)sizeof(struct MinList))) == 0)
X    return (0L);
X  NewList (fibhead);
X
X/* Allocate an separate list for directories that don't match specs */
X  if ((dirhead = myalloc((LONG)sizeof(struct MinList))) == 0)
X    goto BADALLOC;
X  NewList (dirhead);
X
X  do
X  {
X    TestBreak ();
X    if ((LSFlags & BREAKFLAG) != 0)
X      goto GOODRET;
X
X  /* If we got something */
X    if ((nextstat = ExNext ((BPTR)lockp, fibp)) != 0)
X    {
X      dfcount++;
X
X    /* If the entry is wanted bump count of files or directories */
X      if (CmpDateBounds (&fibp->fib_Date) != 0)
X      {
X        if (fibp->fib_DirEntryType > 0) /* It's a directory */
X          thepat = theDirPat;
X        else
X          thepat = theFilePat;
X
X        matchstat = wildmatch (fibp->fib_FileName, thepat);
X        if ((LSFlags & ANTIMATCH) != 0 && matchstat != 0)
X          matchstat = wildmatch (fibp->fib_FileName, theAntiPat) ^ 1;
X
X	if (matchstat == 0)	/* No match? Then move on */
X	  continue;
X
X        if (fibp->fib_DirEntryType > 0) /* It's a directory */
X        {
X          if (FillFibEntry (dirhead, fibp) == 0)
X            goto BADALLOC;
X          if ((LSFlags & SHOWDIRS) != 0)
X          {
X            dircount++;
X            gdircount++;
X          }
X          else
X            continue;
X        }
X        else                            /* It's a file entry */
X        {
X          if ((LSFlags & SHOWFILES) != 0)
X          {
X            filecount++;
X            gfilecount++;
X            gtotblocks += fibp->fib_NumBlocks;
X            gtotbytes += fibp->fib_Size;
X          }
X          else                /* Don't want this file, move on to next entry */
X            continue;
X        }
X
X      /* See if this is the longest filename for later use in listing */
X        tempnamlen = strlen (fibp->fib_FileName);
X        if (tempnamlen > maxnamlen)
X          maxnamlen = tempnamlen;
X
X      /* Allocate another FibEntry, put the info inside */
X        if (FillFibEntry (fibhead, fibp) == 0)
X          goto BADALLOC;
X      }
X    }
X  } while (nextstat != 0);
X
X/* No entries found? print message and return FALSE */
X  if ((dircount + filecount) == 0)
X  {
X    if ((LSFlags & NOHEADERS) == 0)
X    {
X      if (dfcount == 0)
X        WSTR (VolEmptyMsg);
X      else
X        WSTR (NoMatchMsg);
X    }
X  }
X  else
X  {
X    if ((LSFlags & (SHOWDIRS|SHOWFILES)) != 0)
X    {
X      if ((LSFlags & LONGLIST) == 0)  /* Short listing wanted */
X        SListDir (fibhead);
X      else                            /* Full listing */
X        LListDir (fibhead);
X    }
X  }
X
XGOODRET:
X  FreeAllFibs (fibhead);
X  return (dirhead);
X
XBADALLOC:
X  FreeAllFibs (fibhead);
X  FreeAllFibs (dirhead);
X  return (0L);
X}
X
X/* --------------------------------------------------------------------
X * Given a directory name and a lock on that directory, create a list
X * of entries.  Recursively decends into subdirectories if flaged.
X * -------------------------------------------------------------------- */
XVOID DirIt (curlock, dirname)
X  struct FileLock *curlock;
X  BYTE *dirname;
X{
X  BYTE *subdir;
X  LONG dnamlen;
X  struct FibEntry *tfibp;
X  struct FileLock *sublock;
X  struct List *fibheadp;
X
X/* Try to fill FileInfoBlock, bomb if not readable for some reason */
X  if (Examine ((BPTR)curlock, GFibp) == 0)
X  {
X    (VOID) asprintf (workstr, NoExamFmtStr, IoErr());
X    WSTR (workstr);
X    return;
X  }
X
X/* Put directory header for each listing, if we know the name && recursive */
X  if (dirname[0] != 0 && (LSFlags & LISTALL) != 0 && (LSFlags & NOHEADERS) == 0)
X  {
X    if ((LSFlags & CONSOLE) != 0)
X     (VOID)asprintf (workstr, "\23330;41m %s \2330m\n", dirname);
X    else
X     (VOID)asprintf (workstr, "%s\n", dirname);
X    WSTR (workstr);
X  }
X
X/* If this is a single file list it verbosely */
X  if (GFibp->fib_DirEntryType < 0)
X  {
X    if ((LSFlags & SHOWFILES) != 0)
X    {
X      (VOID) GetPathString (thePath, thePath);
X      LListEntry (GFibp);
X    }
X  }
X  else /* It is a directory entry */
X  {
X  /* Allocate, fill, and display a dir of entries, check for no ram or abort */
X    if ((fibheadp = GetDir (curlock, GFibp)) != 0)
X    {
X    /* Recursively descend any subdirs in this list, if wanted */
X      if ((LSFlags & LISTALL) != 0)
X      {
X        for (tfibp = (struct FibEntry *)fibheadp->lh_Head;
X              tfibp->fe_Node.mln_Succ != 0;
X              tfibp = (struct FibEntry *)tfibp->fe_Node.mln_Succ)
X        {
X          TestBreak ();
X          if ((LSFlags & BREAKFLAG) != 0)
X            break;
X
X          if (tfibp->fe_Fib->fib_DirEntryType > 0)
X          {
X          /* Alloc length of path + 1 + newdir name +1 + length of maxname + 1 for NULL
X           * + 1 for null + 3 for rounding.
X           */
X            dnamlen = (LONG)(strlen (dirname) + strlen (tfibp->fe_Fib->fib_FileName) + 36);
X            if ((subdir = myalloc ((LONG)dnamlen)) != 0)
X            {
X              if (dirname[0] != 0)
X              {
X                (VOID) stpcpy (subdir, dirname);
X                dnamlen = strlen (dirname) - 1;
X                if (dirname[dnamlen] != ':' && dirname[dnamlen] != '/')
X                {
X                  (VOID) strcat (subdir, SlashStr);
X                }
X              }
X              (VOID) strcat (subdir, tfibp->fe_Fib->fib_FileName);
X
X              if ((sublock = (struct FileLock *)Lock (subdir, (LONG)ACCESS_READ)) != 0)
X              {
X                if ((LSFlags & NOHEADERS) == 0)
X                  WCHR (NLine);                 /* Put a blank line between directories */
X		
X		curpath = subdir;
X
X                DirIt (sublock, subdir); /* Recurse into this subdirectory */
X
X                UnLock ((BPTR)sublock);	 /* Unlock our sublock */
X              }
X              myfree (subdir);      	 /* Free the current namespace */
X            }
X          }
X        }
X      }
X    /* Free up this fib list */
X      FreeAllFibs (fibheadp);
X    }
X  }
X}
X
X/* -------------------------------------------------------------------- */
XVOID GetCLIArgs (line, argc, argv)
X  BYTE *line;
X  LONG *argc;
X  BYTE **argv;
X{
X  BYTE **pargv, *qarg;
X
X  *argc = 0;
X  while (*argc < MAXARG)
X  {
X    while (*line == ' ' || *line == '\t' || *line == '\n')
X      line++;
X    if (*line == 0)
X      break;
X    pargv = &argv[*argc];
X    *argc += 1;
X
X    if (*line == '"')
X    {
X      qarg = line;
X      line += 1;
X      *pargv = line;                 /* ptr inside quoted string */
X      while (*line != 0 && *line != '"')
X        line++;
X      if (*line == 0)                /* Hit end of string without quote! */
X      {
X        *pargv = qarg;               /* Must be okay */
X        break;
X      }
X      else
X        *line++ = 0;                 /* terminate arg ontopof quote */
X    }
X    else                             /* non-quoted arg */
X    {
X      *pargv = line;
X      while (*line != 0 && !(*line == ' ' || *line == '\t' || *line == '\n'))
X        line++;
X      if (*line == 0)
X        break;
X      else
X        *line++ = 0;                 /* terminate arg */
X    }
X  }                                  /* while */
X}
X
X/* -------------------------------------------------------------------- */
XLONG GetFileDate(name, ptime)
X  char *name;
X  struct DateStamp *ptime;
X{
X  LONG status;
X  struct FileLock *flock;
X  struct FileInfoBlock *fib;
X
X  status = 0;
X  if ((fib = myalloc ((LONG)sizeof(struct FileInfoBlock))) != 0)
X  {
X    if ((flock = (struct FileLock *)Lock (name, (LONG)ACCESS_READ)) != 0)
X    {
X      if (Examine ((BPTR)flock, fib) != 0)
X      {
X        *ptime = fib->fib_Date; 	/* Copy the Date structure */
X        status = 1;
X      }
X      UnLock ((BPTR)flock);
X    }
X    myfree (fib);
X  }
X  return (status);
X}
X
X/* --------------------------------------------------------------------
X * Deallocate and close everything
X * -------------------------------------------------------------------- */
XVOID CleanUp (exit_msg, exit_status, result2)
X  BYTE *exit_msg;
X  LONG exit_status, result2;
X{
X  BYTE workstr[WORKSIZE];
X  struct Process *procp;
X
X/* Make sure we unlock any locks we created! */
X  if (CurFLock != 0 && (LSFlags & PATHNAMED) != 0)
X    UnLock ((BPTR)CurFLock);
X
X/* Free our fib */
X  myfree (GFibp);
X
X  if (*exit_msg != 0)
X  {
X    (VOID) asprintf (workstr, ErrFmtStr, exit_msg, result2);
X    WSTR (workstr);
X  }
X
X/* Put windowptr back */
X  procp = (struct Process *) FindTask (0L);
X  procp->pr_WindowPtr = OldWindowPtr;
X
X/* Set return status, exit */
X  procp->pr_Result2 = result2;
X  exit ((int)exit_status);
X}
X
X/* --------------------------------------------------------------------
X * Explain how to use.
X * -------------------------------------------------------------------- */
XVOID Usage ()
X{
X  LONG i;
X
X  for (i = 0; usagestrs[i] != 0; i++)
X    WSTR (usagestrs[i]);
X  CleanUp (&NLine[1], 20L, 120L);
X}
X
X/* -------------------------------------------------------------------- */
XLONG ParseCmdOptions (ncnt, argc, argv)
X  LONG ncnt, argc;
X  BYTE **argv;
X{
X  BYTE tmpmsg[4];
X  LONG i, cnt, len;
X  struct Process *procp;
X
X  cnt = ncnt;      /* Current arg number that contains the options */
X  ncnt += 1;       /* Advance to next arg */
X
X  for (i = 1, len = strlen (argv[cnt]); i < len; i++)
X  {
X    switch (argv[cnt][i])
X    {
X      case '?':
X      case 'h':
X        Usage ();
X        break;
X      case 'D':
X        LSFlags |= FILESFIRST;
X        break;
X      case 'F':
X        if (argc < (ncnt + 1))     /* Missing required format string */
X          Usage ();
X        thefmtstr = argv[ncnt];    /* Else our format string is the next arg */
X        ncnt += 1;                 /* Bump arg counter */
X        LSFlags |= LONGLIST;
X        break;
X      case 'H':
X        LSFlags |= NOHEADERS;
X        break;
X      case 'I':
X        LSFlags |= NOINTERACT;
X        break;
X      case 'M':
X        LSFlags |= MIXFILESDIRS;
X        break;
X      case 'N':
X        if (argc < (ncnt + 1))     /* Missing required name string */
X          Usage ();
X        else
X        {
X          if (GetFileDate (argv[ncnt], &thenewdate) == 0)
X            NoFileExit (argv[ncnt]);
X          ncnt += 1;                 /* Bump arg counter */
X          LSFlags |= SHOWNEWERTHAN;
X        }
X        break;
X      case 'O':
X        if (argc < (ncnt + 1))     /* Missing required name string */
X        {
X          Usage ();
X        }
X        else
X        {
X          if (GetFileDate (argv[ncnt], &theolddate) == 0)
X            NoFileExit (argv[ncnt]);
X          ncnt += 1;                 /* Bump arg counter */
X          LSFlags |= SHOWOLDERTHAN;
X        }
X        break;
X      case 'P':
X        LSFlags |= (FULLPATHNAMES | LONGLIST);
X        break;
X      case 'R':
X        LSFlags |= LISTALL;
X        break;
X      case 'T':
X        LSFlags |= TOTALIZE;
X        break;
X      case 'X':
X        if (argc < (ncnt + 1))     /* Missing required width number */
X        {
X          Usage ();
X        }
X        else
X        {
X          (VOID) GetDecNum (argv[ncnt], &CurWinCols);
X          ncnt += 1;                 /* Bump arg counter */
X        }
X        break;
X      case 'Y':
X        if (argc < (ncnt + 1))     /* Missing required height number */
X        {
X          Usage ();
X        }
X        else
X        {
X          (VOID) GetDecNum (argv[ncnt], &CurWinRows);
X          ncnt += 1;                 /* Bump arg counter */
X        }
X        break;
X      case 'c':
X        LSFlags |= LONGLIST | NOTEFLAG;
X        break;
X      case 'd':
X        LSFlags &= ~SHOWFILES;
X        break;
X      case 'f':
X        LSFlags &= ~SHOWDIRS;
X        break;
X      case 'k':
X        LSFlags &= ~CONSOLE;
X        break;
X      case 'l':
X        LSFlags |= LONGLIST;
X        break;
X      case 'n':
X        LSFlags |= NOSORTFLAG;
X        break;
X      case 'p':
X        procp = (struct Process *) FindTask (0L);
X        procp->pr_WindowPtr = OldWindowPtr;
X        break;
X      case 'r':
X        LSFlags |= REVFLAG;
X        break;
X      case 's':
X        sortkey = 1;
X        break;
X      case 't':
X        sortkey = 2;
X        break;
X      case 'v':
X        if (argc < (ncnt + 1))     	/* Missing required pattern string */
X          Usage ();
X        theAntiPat = argv[ncnt];	/* Point to pattern area */
X        ncnt += 1;			/* Skip over arg */
X        LSFlags |= ANTIMATCH;		/* Set flag to use antipat */
X        break;
X      default:
X        tmpmsg[0] = argv[cnt][i];
X        tmpmsg[1] = 0;
X        (VOID) asprintf (workstr, BadOptFmtStr, tmpmsg);
X        WSTR (workstr);
X        Usage ();
X        break;
X    }
X  }
X  return(ncnt);
X}
X
X/* -------------------------------------------------------------------- */
XVOID _main (line)
X  BYTE *line;
X{
X  BYTE *argv[MAXARG];             /* arg pointers */
X  LONG argc;                      /* arg count */
X  LONG cnt;
X  struct Process *procp;
X
X/* Prevent system request from occuring by default */
X  procp = (struct Process *) FindTask (0L);
X  OldWindowPtr = procp->pr_WindowPtr;
X  procp->pr_WindowPtr = (APTR)-1L;
X  
X/* Construct list of args */
X  GetCLIArgs (line, &argc, argv);
X
X/* Grab FileHandles for input and output to console (or redirection file) */
X  ConIn = (struct FileHandle *)Input ();
X  ConOut = (struct FileHandle *)Output ();
X
X/* Is this a CLI? Set a flag */
X  if (IsInteractive ((BPTR)ConOut) != 0 && IsInteractive ((BPTR)ConIn) != 0)
X    LSFlags |= CONSOLE;
X
X/* Allocate a global FileInfoBlock for ExNext() */
X  if ((GFibp = myalloc ((LONG) sizeof (struct FileInfoBlock))) == 0)
X    NoMemExit ();
X
X/* Initialize arg count, zero grand totals */
X  cnt = 1;
X  gtotblocks = gtotbytes = gdircount = gfilecount = 0;
X
X/* Parse command line arguments, if any */
X  do
X  {
X    if (cnt < argc && argv[cnt][0] == '-')
X    {
X    /* Reset for next arg */ 
X      LSFlags &= CONSOLE;
X      LSFlags |= SHOWDIRS | SHOWFILES;
X      thefmtstr = deffmtstr;
X
X      do
X      {
X        cnt = ParseCmdOptions(cnt, argc, argv);
X      } while (cnt < argc && argv[cnt][0] == '-');
X    }
X
X  /* Is there an named path arg to do? */
X    if (cnt < argc)
X    {
X      LSFlags |= PATHNAMED;                  /* Flag that we have a name */
X      theFilePat[0] = 0;                     /* Terminate pattern strings */
X      theDirPat[0] = 0;
X      (VOID) stpcpy (thePath, argv[cnt]);    /* Copy this arg to work space */
X      cnt++;                                 /* Advance arg counter */
X
X    /* Wild pathname? Separate into components until we find a non-wild path */
X      if (iswild (thePath) != 0)
X      {
X        (VOID) GetFileString (theFilePat, thePath);
X        (VOID) stpcpy (theDirPat, theFilePat);
X        (VOID) GetPathString (thePath, thePath);
X
X      /* Still wild?  First part must be wild filename to match */
X        if (iswild (thePath) != 0)
X        {
X          (VOID) GetFileString (theDirPat, thePath);
X          (VOID) GetPathString (thePath, thePath);
X        }
X      }
X#ifdef DEBUGIT
X      asprintf(workstr, "path: %s\n", thePath); WSTR (workstr);
X      asprintf(workstr, " dir: %s\n", theDirPat); WSTR (workstr);
X      asprintf(workstr, "file: %s\n", theFilePat); WSTR (workstr);
X#endif
X
X    /* No wildcards allowed in the final pathname! */
X      if (iswild (thePath) != 0)
X        CleanUp (NoWildPathMsg, 20L, 120L);
X
X    /* Now try to lock the dir (or file) */
X      if ((CurFLock = (struct FileLock *)Lock (thePath, (LONG)ACCESS_READ)) == 0)
X      {
X        NoFileExit (thePath);
X      }
X    }
X    else
X    {
X    /*
X     * If no filename was specified, steal Lock on current directory from
X     * CLI process task info.  We durn well better get something useful back;
X     * since we don't do any error checking on the "borrowed" Lock.
X     */
X      CurFLock = (struct FileLock *)procp->pr_CurrentDir;
X    }
X
X  /* Make a full pathname string from given CurFLock if no colon in path */
X    if (aindex(thePath, ':') == 0)
X      MakePathString (CurFLock, thePath);
X    curpath = thePath;
X
X#ifdef DEBUGIT
X    asprintf(workstr, "Final path: %s\n", thePath); WSTR (workstr);
X#endif
X
X  /* If there isn't a dir pattern or file pattern specified, match everything */
X    if (theDirPat[0] == 0)
X    {
X      theDirPat[0] = '*';               /* "*" default matchall dir pattern */
X      theDirPat[1] = 0;                 /* Null terminate string */
X    }
X
X    if (theFilePat[0] == 0)
X    {
X      theFilePat[0] = '*';
X      theFilePat[1] = 0;
X    }
X
X  /* Get the directory for this path, display it */
X    DirIt (CurFLock, thePath);
X
X  /* Release the lock if we locked it */
X    if (CurFLock != 0 && (LSFlags & PATHNAMED) != 0)
X    {
X      UnLock ((BPTR)CurFLock);
X    }
X    CurFLock = 0;
X
X  /* Still more args? Put a linefeed between listing outputs
X    if (cnt < argc && (LSFlags & NOHEADERS) == 0)
X      WCHR (NLine);
X   */
X    TestBreak();
X    if ((LSFlags & TOTALIZE) != 0)
X    {
X      SetConPen (penstr4);
X      WSTR (TotHeaderMsg);
X      SetConPen (penstr5);
X      (VOID) asprintf (workstr, totalfmtstr, gdircount, gfilecount, gtotblocks, gtotbytes);
X      WSTR (workstr);
X      SetConPen (penstr0);
X    }
X  } while (cnt < argc && (LSFlags & BREAKFLAG) == 0);
X
X/* All done! Clean exit */
X  CleanUp (&NLine[1], 0L, 0L);
X}
END_OF_FILE
if test 43452 -ne `wc -c <'src/ls.c'`; then
    echo shar: \"'src/ls.c'\" unpacked with wrong size!
fi
# end of 'src/ls.c'
fi
echo shar: End of archive 3 \(of 3\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 3 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Mail submissions (sources or binaries) to <amiga@cs.odu.edu>.
Mail comments to the moderator at <amiga-request@cs.odu.edu>.
Post requests for sources, and general discussion to comp.sys.amiga.