home *** CD-ROM | disk | FTP | other *** search
/ Amiga Elysian Archive / AmigaElysianArchive.iso / newc_dev / ls32.lha / ls.c < prev    next >
C/C++ Source or Header  |  1992-06-10  |  45KB  |  1,477 lines

  1. /* ---------------------------------------------------------------------
  2.   LS.C -- Source code for the "improved" directory utility.
  3.  
  4.   Author: Justin V. McCormick
  5.  
  6.   V1.0    August 1986 Written from scratch, my first Amiga project.
  7.   V2.0  November 1988 Revised for Lattice 5.0 and made 1.3 compatible.
  8.   V2.1  December 1988 Minor size reduction, fixed a few bugs from 2.0.
  9.   V2.2  December 1988 Fixed status return and multiple wildcard pathnames.
  10.   V3.0  July 25, 1989 Instant sorting, best-fit output, new features.
  11.   V3.1  July 29, 1989 Bug fixes, new output width and height options.
  12.   V3.1a May 29,  1992 Now work fine on 2.0 operating system.
  13.   V3.2  June 4,  1992 Now real blocks occupation with -a switch.
  14.  
  15. Notice:
  16.  
  17.   This program is placed in the public domain with the
  18. understanding that the author makes no claims or guarantees with
  19. regard to its suitability for any given application, and that
  20. the author assumes no responsibility for damages incurred by its
  21. usage.  Any resemblance of this program to any other program,
  22. either living or dead, is purely coincidental.
  23.  
  24.   Feel free to borrow this code and make millions of dollars
  25. from its sale or commercial use, but please give credit where
  26. credit is due.
  27.  
  28. Synopsis:
  29.  
  30.   Features intelligent columnar listing, versatile sort options,
  31. UNIX-style pattern matching, recursive subdirectory listing, and
  32. more!
  33.  
  34. Usage:
  35.         LS [options] [pattern1] [pattern2] ...
  36.  
  37. Bugs and Limitations:
  38.  
  39.   LS cannot pattern match devices (like "dh*:"), since this
  40. would involve another level of recursion and parsing the Device
  41. List.  LS shows signs of the same "creeping featurism" which
  42. afflicted the UNIX command, evident from its bloated code size.
  43.  
  44. Changes From 1.0 to 2.0:
  45.  
  46.  o Source code prototyped, linted, traced, optimized, tweaked, etc.
  47.  o Made resident ("pseudo-pure") by linking with cres.o from LC 5.0.
  48.  o High-volume routines recoded in assembly (lssup.a).
  49.  o Now handles multiple paths/files on a command line, up to 30.
  50.  o New sort flags, including no sort.
  51.  o Enhanced wildcards, understands complex *.?*.* expressions now.
  52.  o More efficient ExNext() performance, less ram used for recursion.
  53.  o SIGBREAKF_CTRL_C signal (Ctrl-C) cleanly aborts at any point now.
  54.  o Command line parser handles quoted pathnames now (LC 5.0 benefit).
  55.  o Short listing finally auto-adjusts to new console window sizes!
  56.  o Pen color escape codes bypassed when redirecting long output.
  57.  o Sorting by size or date is also subsorted alphabetically now.
  58.  o Long listing shows new 1.3 file attributes, plus comment indicator.
  59.  o File dates are now in international format, YY-MM-DD.
  60.  o Fixed listings with files datestamped after 99-12-31 (overflow).
  61.  o Fixed listings with files datestamped before 78-01-01 (time < 0).
  62.  
  63. Changes From 2.0 to 2.1
  64.  
  65.  o Fixed the show comment feature, a last minute bug in 2.0.
  66.  o Fixed the "Unknown option ''" message problem.
  67.  o Optimized the assembly branches, reduced code size another few bytes.
  68.  
  69. Changes From 2.1 to 2.2
  70.  
  71.  o Fixed erroneous Status returns.
  72.  o Fixed bug with multiple wildcarded pathnames.
  73.  o Compiled with LC 5.0 Patch 3 and CAPE 2.0, saved another 46 bytes.
  74.  o Eliminated an extra stpcpy(), saved another few bytes.
  75.  
  76. Changes From 2.2 to 3.0
  77.  
  78.  o Added many new command line options!  See Usage descriptions and docs.
  79.  o "Instant" output sorting, now using an on-the-fly insertion sort.
  80.  o New Short listing technique is row-by-row, redirects to PRT: or file.
  81.  o Better columnization of short listings, uses best-fit optimization.
  82.  o Formatted output parsing system implemented for long listings.
  83.  o Improved ^C handling, now breaks immediately in mid-output.
  84.  o New command line parser, now handles multiple options mixed with names.
  85.  o Added separate pattern matching for directories and files.
  86.  o Now inhibits system requesters from popping up by default.
  87.  o Added custom cres.o module for more size savings.
  88.  o Now uses Exec List functions for smaller, faster code.
  89.  o Fixed random cli_ReturnCode and pr_Result2 value CLI returns.
  90.  o Rewrote many sections, further code cleaning/commenting.
  91.  o Eliminated main(), LS handles the _main() function for smaller code.
  92.  o Compiled with 5.03.90 (beta) Lattice compiler, saved a few bytes.
  93.  
  94. Changes From 3.0 to 3.1
  95.  o Fixed random errors from uninitialized argument count in GetCLIArgs().
  96.  o New -X and -Y options to specify short format columns and rows.
  97.  
  98. Changes From 3.1 to 3.1a
  99.  o Not a real change, just returned to the cres.o modules from SAS.
  100.  o So you can use it on a 2.0 Machine (it works fine on my A3000T)
  101.  
  102. Changes From 3.1a to 3.2
  103.  o Add -a switch to select the real block occupation istead of data occupation.
  104.  
  105.  --------------------------------------------------------------------- */
  106.  
  107. /* ---------------------- LS SOURCE ---------------------------------- */
  108.  
  109. /* Use Real strlen unless you comment out the following */
  110. #define strlen strlen
  111.  
  112. /* Maximum number of command line arguments */
  113. #define MAXARG 32
  114. #define PADTABSIZE 100
  115. #define WORKSIZE 300
  116.  
  117. #include "ls.h"
  118.  
  119. /* Extern CODE from lssup.a */
  120. extern BYTE *aindex __ARGS((BYTE *, BYTE));
  121. extern LONG  __stdargs asprintf __ARGS((BYTE *, BYTE *,...));
  122. extern LONG CompareDateStamps __ARGS((struct DateStamp *, struct DateStamp *));
  123. extern LONG CompFibs __ARGS((LONG, struct FileInfoBlock *, struct FileInfoBlock *));
  124. extern LONG FillFibEntry __ARGS((struct List *, struct FileInfoBlock *));
  125. extern LONG GetFileString __ARGS((BYTE *, BYTE *));
  126. extern LONG GetPathString __ARGS((BYTE *, BYTE *));
  127. extern LONG iswild __ARGS((BYTE *));
  128. extern LONG wildmatch __ARGS((BYTE *, BYTE *));
  129. extern VOID *myalloc __ARGS((LONG));
  130. extern VOID FibFileDate __ARGS((struct DateStamp *, BYTE *, BYTE *));
  131. extern VOID GetWinBounds __ARGS((LONG *, LONG *));
  132. extern VOID InsertFibNode __ARGS((struct List *, struct FibEntry *));
  133. extern VOID MakePathString __ARGS((struct FileLock *, BYTE *));
  134. extern VOID myfree __ARGS((VOID *));
  135. extern VOID NoFileExit __ARGS((BYTE *));
  136. extern VOID NoMemExit __ARGS((VOID));
  137.  
  138. /* Local DATA */
  139. struct DateStamp theolddate;     /* Show files older than this date */
  140. struct DateStamp thenewdate;     /* Show files newer than this date */
  141. struct FileHandle *ConOut;       /* Standard output */
  142. struct FileHandle *ConIn;        /* Standard input */
  143. struct FileLock *CurFLock;       /* Global Directory lock */
  144. struct FileInfoBlock *GFibp;     /* Global FIB for Examine/ExNext */
  145. APTR OldWindowPtr;               /* Copy of what was in pr_WindowPtr */
  146.  
  147. /* User flags, See ls.h for bit definitions */
  148. ULONG LSFlags = SHOWDIRS | SHOWFILES;
  149.  
  150. LONG gdircount;                 /* Grand total # of dirs for Recursive */
  151. LONG gfilecount;                /* Grand total # of files found */
  152. LONG gtotblocks;                /* Grand total # of blocks */
  153. LONG gtotbytes;                 /* Grand total # of bytes */
  154.  
  155. LONG dircount;                  /* Number of directories found */
  156. LONG filecount;                 /* Number of files found */
  157. LONG totblocks;                 /* total # of blocks */
  158. LONG totOFS;
  159. LONG totFFS;
  160. LONG totbytes;                  /* total # of bytes */
  161. LONG maxnamlen;                 /* Longest name found */
  162. LONG sortkey;                   /* 0=alpha, 1=size, 2=date */
  163.  
  164. BYTE padtab[PADTABSIZE];        /* Column table tab amounts */
  165. BYTE thePath[WORKSIZE];         /* Current filename space */
  166. BYTE theDirPat[WORKSIZE];       /* Dirname pattern workspace */
  167. BYTE theFilePat[WORKSIZE];      /* Filename pattern workspace */
  168. BYTE workstr[WORKSIZE+64];      /* Temp string space */
  169.  
  170. BYTE theblksstr[20];
  171. BYTE thebreal[20];
  172. BYTE thedatestr[12];
  173. BYTE theprotstr[12];
  174. BYTE thesizestr[20];
  175. BYTE thetimestr[12];
  176.  
  177. LONG CurWinRows;                /* Window bounds, determined at runtime */
  178. LONG CurWinCols;
  179.  
  180. /* Initialized Strings */
  181. BYTE Author[]  = "\23333mLS 3.2\2330m By Justin V. McCormick & Paolo Dell'Aquila";
  182. BYTE usage[]   = "\n  Usage: LS [-options <args>] [path1] [path2] ...\n";
  183. BYTE usage0[]  = "\tc  Show filenotes           D  Show dirs last\n";
  184. BYTE usage1[]  = "\td  Dirs only                H  No headings and subtotals\n";
  185. BYTE usage2[]  = "\tf  Files only               I  No page prompts\n";
  186. BYTE usage3[]  = "\tk  No ANSI display codes    M  Mixed output files and dirs\n";
  187. BYTE usage4[]  = "\tl  Long listing             N <name> Show newer than entry\n";
  188. BYTE usage5[]  = "\tn  No sort                  O <name> Show older than entry\n";
  189. BYTE usage6[]  = "\tp  Permit system requests   P  Show full pathnames\n";
  190. BYTE usage7[]  = "\tr  Reverse sort             R  Recursive listing\n";
  191. BYTE usage8[]  = "\ts  Sort by size             T  Total all subtotals\n";
  192. BYTE usage9[]  = "\tt  Sort by date             X <wide> Set output columns\n";
  193. BYTE usage10[] = "\tv <pattern> Avoid pattern   Y <high> Set output rows\n";
  194. BYTE usage11[] = "\tF <format> Format output (default: \042";
  195. BYTE usage13[] = "\ta  Real block occupation\n";
  196. BYTE usage12[] = "\042)\n";
  197.  
  198. /* Date and output format strings */
  199. BYTE baddatestr[]     = "00-00-00";
  200. BYTE badtimestr[]     = "00:00:00";
  201. BYTE datepat[]        = "%02ld-%02ld-%02ld";
  202. BYTE timepat[]        = "%02ld:%02ld:%02ld";
  203. BYTE dayspermonth[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
  204. BYTE deffmtstr[40]    = "%p %d %t %4b %8s %n\\n";
  205. BYTE deffmtstr2[40]   = "%p %d %t %4b %4q %8s %n\\n";
  206. BYTE LongFmtStr[]     = "%ld";
  207. BYTE totalfmtstr[]    = "Dirs:%-4ld Files:%-4ld Blocks:%-5ld Bytes:%-8ld\n";
  208. BYTE totalrealstr[]   = "Dirs:%-4ld Files:%-4ld BlocksOFS:%-5ld BlocksFFS:%-5ld Bytes:%-8ld\n";
  209. BYTE TotHeaderMsg[]   = "Totals:\n";
  210. BYTE ColonStr[]       = ":";
  211. BYTE SlashStr[]       = "/";
  212. BYTE RamNameStr[]     = "RAM:";
  213.  
  214. /* ANSI color control strings */
  215. BYTE penstr0[]        = "\2330m";       /* Reset CON:   */
  216. BYTE penstr1[]        = "\2330 p";      /* Cursor off   */
  217. BYTE penstr2[]        = "\233 p";       /* Cursor on    */
  218. BYTE penstr3[]        = "\23333m";      /* Color 3 NORM */
  219. BYTE penstr4[]        = "\2331;33;40m"; /* Color 3 BOLD */
  220. BYTE penstr5[]        = "\2331;31;40m"; /* Color 1 BOLD */
  221.  
  222. /* CON: command sequence for window bounds report */
  223. BYTE gwbrstr[4] = {  0x9b, '0', ' ', 'q' };
  224.  
  225. /* Newline and "" source */
  226. BYTE NLine[4] = { 10, 0, 0, 0 };
  227.  
  228. /* Error messages */
  229. BYTE BadOptFmtStr[]   = "Unknown option: -%s\n";
  230. BYTE ErrFmtStr[]      = "LS: %s, Error #%ld\n";
  231. BYTE NoExamFmtStr[]   = "Cannot examine file or directory, Error #%ld\n";
  232. BYTE NoFindFmtStr[]   = "'%s' not found";
  233. BYTE NoRAMMsg[]       = "No RAM";
  234. BYTE NoWildPathMsg[]  = "Unable to pattern match paths";
  235. BYTE VolEmptyMsg[]    = "Volume or directory is empty.\n";
  236. BYTE NoMatchMsg[]     = "No match.\n";
  237.  
  238. /* Pointers to usage strings for quick easy output */
  239. BYTE *usagestrs[] =
  240. {
  241.   Author, usage, usage13, usage0, usage1, usage2, usage3, usage4, usage5,
  242.   usage6, usage7, usage8, usage9, usage10,usage11, deffmtstr,
  243.   usage12, 0L
  244. };
  245.  
  246. BYTE *thefmtstr = deffmtstr;    /* Format string pointer for long output */
  247. BYTE *curpath;
  248. BYTE *theAntiPat;        /* Avoid pattern string */
  249.  
  250. /* Local CODE */
  251. BYTE *GetDecNum __ARGS((BYTE *, LONG *));
  252. LONG CmpDateBounds __ARGS((struct DateStamp *));
  253. LONG GetFileDate __ARGS((BYTE *, struct DateStamp *));
  254. LONG ParseCmdOptions __ARGS((LONG, LONG, BYTE **));
  255. struct FibEntry *AllocFib __ARGS((VOID));
  256. struct FibEntry *ModNextFib __ARGS((struct FibEntry *, LONG));
  257. struct List *GetDir __ARGS((struct FileLock *, struct FileInfoBlock *));
  258. VOID CleanUp __ARGS((BYTE *, LONG, LONG));
  259. VOID DirIt __ARGS((struct FileLock *, BYTE *));
  260. VOID FreeAllFibs __ARGS((struct List *));
  261. VOID GetCLIArgs __ARGS((BYTE *, LONG *, BYTE **));
  262. VOID LListDir __ARGS((struct List *));
  263. VOID LListEntry __ARGS((struct FileInfoBlock *));
  264. VOID LongList __ARGS((LONG *, LONG *, struct List *));
  265. VOID PagePrompt __ARGS((LONG, LONG));
  266. VOID ParseFormatOpts __ARGS((struct FileInfoBlock *));
  267. VOID SetConPen __ARGS((BYTE *));
  268. VOID SListDir __ARGS((struct List *));
  269. VOID TestBreak __ARGS((VOID));
  270. VOID Usage __ARGS((VOID));
  271. VOID WCHR __ARGS((BYTE *));
  272. VOID WSTR __ARGS((BYTE *));
  273. VOID _main __ARGS((BYTE *));
  274.  
  275. /* --------------------------------------------------------------------
  276.  * Allocate a FibEntry structure and associated FileInfoBlock.
  277.  * -------------------------------------------------------------------- */
  278. struct FibEntry *AllocFib ()
  279. {
  280.   struct FibEntry *tfibp;
  281.  
  282.   if ((tfibp = myalloc ((LONG)(sizeof (struct FibEntry) + sizeof (struct FileInfoBlock)))) != 0)
  283.   {
  284.     tfibp->fe_Fib = (struct FileInfoBlock *)((ULONG)tfibp + sizeof (struct FibEntry));
  285.   }
  286.   else
  287.     LSFlags |= BREAKFLAG;
  288.   return (tfibp);
  289. }
  290.  
  291. /* --------------------------------------------------------------------
  292.  * Use AmigaDOS to output a string to the stdout channel
  293.  * -------------------------------------------------------------------- */
  294. VOID WSTR (tstring)
  295.   BYTE *tstring;
  296. {
  297.   LONG i;
  298.  
  299.   i = strlen (tstring);
  300.   if (i > 0)
  301.   {
  302.     (VOID) Write ((BPTR)ConOut, tstring, i);
  303.   }
  304. }
  305.  
  306. /* --------------------------------------------------------------------
  307.  * Use AmigaDOS to put a character to the stdout
  308.  * -------------------------------------------------------------------- */
  309. VOID WCHR (ch)
  310.   BYTE *ch;
  311. {
  312.   (VOID) Write ((BPTR)ConOut, ch, 1L);
  313. }
  314.  
  315. /* --------------------------------------------------------------------
  316.  * Check to see if the user hit ^C
  317.  * -------------------------------------------------------------------- */
  318. VOID TestBreak ()
  319. {
  320.   ULONG oldsig;
  321.  
  322.   oldsig = SetSignal (0L, (LONG)SIGBREAKF_CTRL_C);
  323.   if ((oldsig & SIGBREAKF_CTRL_C) != 0)
  324.   {
  325.     WSTR ("\2330m\233 p**BREAK\n");
  326.     LSFlags |= BREAKFLAG;
  327.   }
  328. }
  329.  
  330. /* --------------------------------------------------------------------
  331.  * Prompt the user to hit return, wait till return is hit
  332.  * -------------------------------------------------------------------- */
  333. VOID PagePrompt (page, maxpage)
  334.   LONG page, maxpage;
  335. {
  336.   if (CurWinCols > maxnamlen && page > 1 && (LSFlags & NOINTERACT) == 0)
  337.   {
  338.     WSTR ("\2337m -- MORE -- ");
  339.     if (CurWinCols >= 27)
  340.       WSTR ("Press Return: ");
  341.     WSTR ("\2330m\233 p");
  342.     (VOID) Read ((BPTR)ConIn, workstr, 1L);
  343.     WSTR ("\2330 p\233F\233K");
  344.     TestBreak ();
  345.   }
  346.  
  347.   if ((LSFlags & (NOHEADERS|BREAKFLAG)) == 0)
  348.   {
  349.     if ((LSFlags & CONSOLE) != 0)
  350.     {
  351.       WSTR ("\2330 p\2334;33m");
  352.     }
  353.     (VOID) asprintf (workstr, "Page %ld of %ld:", page, maxpage);
  354.     WSTR (workstr);
  355.     SetConPen (penstr0);
  356.     WSTR (NLine);
  357.   }
  358. }
  359.  
  360. /* -------------------------------------------------------------------- */
  361. struct FibEntry *ModNextFib(tfibp, rows)
  362.   struct FibEntry *tfibp;
  363.   LONG rows;
  364. {
  365.   LONG i;
  366.  
  367.   for (i = 0; i < rows && tfibp->fe_Node.mln_Succ != 0; i++)
  368.   {
  369.     tfibp = (struct FibEntry *)tfibp->fe_Node.mln_Succ;
  370.   }
  371.   return(tfibp);
  372. }
  373.  
  374. /* --------------------------------------------------------------------
  375.  * set CON: character color to default Pen1 colors 
  376.  * -------------------------------------------------------------------- */
  377. VOID SetConPen (penstr)
  378.   BYTE *penstr;
  379. {
  380.   if ((LSFlags & CONSOLE) != 0)
  381.     WSTR (penstr);
  382. }
  383.  
  384. /* #define DEBUGSLD 1 */
  385. /* --------------------------------------------------------------------
  386.  * List a FibEntry list in a compact fashion
  387.  * -------------------------------------------------------------------- */
  388. VOID SListDir (fibheadp)
  389.   struct List *fibheadp;
  390. {
  391.   LONG avglen;
  392.   LONG colcnt;
  393.   LONG currow;
  394.   LONG dfcount;
  395.   LONG i, j, wlen;
  396.   LONG maxcol;
  397.   LONG maxpage;
  398.   LONG maxrow;
  399.   LONG maxwinrow;
  400.   LONG pagecnt;
  401.   LONG rowcnt;
  402.   LONG tlen;
  403.   LONG totlen;
  404.   struct FibEntry *hfibp, *tfibp;
  405.  
  406.   SetConPen (penstr1);        /* Turn the cursor off since it will blink anyway */
  407.   GetWinBounds (&colcnt, &currow); /* Get current window size */
  408.  
  409.   if (CurWinCols == 0)
  410.     CurWinCols = colcnt;
  411.   if (CurWinRows == 0)
  412.     CurWinRows = currow;
  413.  
  414. /* Make a average-case WxH estimate for # of display columns */
  415.   for (totlen = dfcount = 0, hfibp = (struct FibEntry *)fibheadp->lh_Head;
  416.        hfibp->fe_Node.mln_Succ != 0;
  417.        hfibp = (struct FibEntry *)hfibp->fe_Node.mln_Succ)
  418.   {
  419.     if (hfibp->fe_Fib->fib_DirEntryType > 0 && (LSFlags & CONSOLE) == 0)
  420.       (VOID) strcat (hfibp->fe_Fib->fib_FileName, SlashStr);
  421.     totlen += strlen (hfibp->fe_Fib->fib_FileName);
  422.     dfcount++;
  423.   }
  424.  
  425. /* Calc average length of all entries */
  426.   avglen = totlen / dfcount;            /* Avg name length = totlen/numentries */
  427.   if ((totlen % dfcount) != 0)
  428.     avglen++;
  429.  
  430.   if ((CurWinCols) <= maxnamlen)        /* Longest name wider than window? */
  431.     maxcol = 1;                         /* Yep, just print one column */
  432.   else
  433.   {
  434.   /* Else maxcols = winwidth/namewidth */
  435.     for (maxcol = 0, colcnt = CurWinCols; colcnt >= avglen; maxcol++)
  436.     {
  437.       colcnt -= avglen + 2;
  438.     }
  439.   }
  440. #ifdef DEBUGSLD
  441.   asprintf(workstr, "avg:%ld max:%ld\n", avglen, maxnamlen); WSTR(workstr);
  442. #endif
  443.  
  444. /* Dry run output avg-case WxH table to see if it needs adjusting */
  445.   for (;;)
  446.   {
  447.   /* Clear out previous padtab */
  448.     memset(padtab, 0, PADTABSIZE);
  449.  
  450.   /* Number of rows = total entries / entries per row */
  451.     maxrow = dfcount / maxcol;
  452.     if ((dfcount % maxcol) != 0)        /* Round up if non-integral */
  453.       maxrow++;
  454. #ifdef DEBUGSLD
  455.     asprintf(workstr, "avg: %ld rows by %ld cols\n", maxrow, maxcol); WSTR(workstr);
  456. #endif
  457.  
  458.     for (rowcnt = 0, hfibp = (struct FibEntry *)fibheadp->lh_Head;
  459.          rowcnt < maxrow && hfibp->fe_Node.mln_Succ != 0;
  460.          rowcnt++, hfibp = (struct FibEntry *)hfibp->fe_Node.mln_Succ)
  461.     {
  462.       for (colcnt = 0, tfibp = hfibp;
  463.            colcnt < maxcol && tfibp->fe_Node.mln_Succ != 0;
  464.            colcnt++, tfibp = ModNextFib (tfibp, maxrow))
  465.       {
  466.         tlen = strlen (tfibp->fe_Fib->fib_FileName);
  467.         if (tlen > padtab[colcnt])
  468.           padtab[colcnt] = tlen;
  469.       }
  470.  
  471.     /* If this is the first row, calc actual maxcol/maxrow for this dfcount */
  472.       if (rowcnt == 0)
  473.       {
  474.         maxcol = colcnt;
  475.         maxrow = dfcount / maxcol;
  476.         if ((dfcount % maxcol) != 0)    /* Round up if non-integral */
  477.           maxrow++;
  478.       }
  479.     }
  480.  
  481.   /* Calculate actual total width by adding up width of all columns */
  482.     for (colcnt = totlen = 0; (colcnt + 1) < maxcol; colcnt++)
  483.     {
  484.       totlen += (LONG)padtab[colcnt] + 2;
  485. #ifdef DEBUGSLD
  486.       asprintf(workstr, "padtab[%ld]=%ld\n", colcnt, (LONG)padtab[colcnt]); WSTR(workstr);
  487. #endif
  488.     }
  489.     totlen += (LONG)padtab[colcnt];
  490. #ifdef DEBUGSLD
  491.     asprintf(workstr, "padtab[%ld]=%ld\n", colcnt, (LONG)padtab[colcnt]); WSTR(workstr);
  492.     asprintf(workstr, "totlen %ld\n", totlen); WSTR(workstr);
  493. #endif
  494.  
  495.   /* if More than one column and 
  496.    * total width of all columns is greater > our window width,
  497.    * then decrease number of display columns
  498.    */
  499.     if (maxcol > 1 && totlen > CurWinCols)
  500.     {
  501.       maxcol--;
  502. #ifdef DEBUGSLD
  503.       asprintf(workstr, "new maxcol:%ld\n", maxcol); WSTR(workstr);
  504. #endif
  505.     }
  506.     else
  507.       break;
  508.   }
  509. #ifdef DEBUGSLD
  510.   asprintf(workstr, "adjusted: maxrow:%ld maxcol:%ld (winwidth:%ld totlen:%ld)\n", maxrow, maxcol, CurWinCols, totlen); WSTR(workstr);
  511. #endif
  512.  
  513. /* Calc number of pages */
  514.   maxwinrow = CurWinRows - 3;
  515.   if (maxwinrow <= 0)
  516.     maxwinrow = 1;
  517.   pagecnt = 1;
  518.   maxpage = maxrow / maxwinrow;
  519.   if ((maxrow % maxwinrow) != 0)
  520.     maxpage++;
  521.  
  522. /* Do actual output scan */
  523.   for (rowcnt = 0, currow = maxwinrow, hfibp = (struct FibEntry *)fibheadp->lh_Head;
  524.        (LSFlags & BREAKFLAG) == 0 && rowcnt < maxrow && hfibp->fe_Node.mln_Succ != 0;
  525.        TestBreak(), rowcnt++, currow++, hfibp = (struct FibEntry *)hfibp->fe_Node.mln_Succ)
  526.   {
  527.     if (maxpage > 1 && currow == maxwinrow)
  528.     {
  529.       currow = 0;
  530.       if ((LSFlags & CONSOLE) != 0)
  531.       {
  532.         PagePrompt (pagecnt, maxpage);
  533.         if ((LSFlags & BREAKFLAG) != 0)
  534.           break;
  535.       }
  536.       pagecnt++;
  537.     }
  538.  
  539.     for (colcnt = 0, tfibp = hfibp;
  540.          colcnt < maxcol && tfibp->fe_Node.mln_Succ != 0;
  541.          colcnt++)
  542.     {
  543.       if (tfibp->fe_Fib->fib_DirEntryType < 0)
  544.       {
  545.       /* Print a file entry */
  546.         (VOID) stpcpy (workstr, tfibp->fe_Fib->fib_FileName);
  547.         wlen = strlen (workstr);
  548.       }
  549.       else
  550.       {
  551.       /* Print a directory entry */
  552.         workstr[0] = 0;
  553.         wlen = strlen (tfibp->fe_Fib->fib_FileName);
  554.         if ((LSFlags & CONSOLE) != 0)
  555.           (VOID) strcat (workstr, penstr3);
  556.         (VOID) strcat (workstr, tfibp->fe_Fib->fib_FileName);
  557.         if ((LSFlags & CONSOLE) != 0)
  558.           (VOID) strcat (workstr, penstr0);
  559.       }
  560.  
  561.     /* Move along list to the next entry, mod maxcol, print this entry */
  562.       tfibp = ModNextFib (tfibp, maxrow);
  563.  
  564.     /* If this is not the last column, pad with spaces till we get to next column */
  565.       if ((colcnt + 1) < maxcol && tfibp->fe_Node.mln_Succ != 0)
  566.       {
  567.         for (i = (LONG)padtab[colcnt] + 1, j = strlen (workstr); i >= wlen; i--, j++)
  568.           workstr[j] = ' ';
  569.         workstr[j] = 0;
  570.       }
  571.     /* Output the final entry */
  572.       WSTR (workstr);
  573.     }
  574.  
  575.   /* Filled this row, start next down */
  576.     WCHR (NLine);
  577.   }
  578.  
  579.   SetConPen (penstr2);        /* Turn cursor back on */
  580. }
  581.  
  582. /* -------------------------------------------------------------------- */
  583. BYTE *GetDecNum (cp, spccnt)
  584.   BYTE *cp;
  585.   LONG *spccnt;
  586. {
  587.   for (*spccnt = 0; *cp >= '0' && *cp <= '9'; cp++)
  588.   {
  589.     *spccnt = *spccnt * 10 + (LONG)*cp - '0';
  590.   }
  591.   return (cp);
  592. }
  593.  
  594. /* -------------------------------------------------------------------- */
  595. VOID ParseFormatOpts (fib)
  596.   struct FileInfoBlock *fib;
  597. {
  598.   BYTE *cp1, *cp2, *reps;
  599.   BYTE *pathend, *thenamestr;
  600.   LONG i, spccnt;
  601.  
  602.   i = strlen (curpath);
  603.   pathend = curpath + i;
  604.   if (i > 1 && *(curpath + i - 1) != ':')
  605.   {
  606.     *(curpath + i) = '/';
  607.     i++;
  608.     *(curpath + i) = 0;
  609.   }
  610.   thenamestr = curpath + strlen(curpath);
  611.   cp2 = thenamestr;
  612.   if (fib->fib_DirEntryType > 0 && (LSFlags & CONSOLE) != 0)
  613.     cp2 = stpcpy (cp2, penstr3);
  614.   cp2 = stpcpy (cp2, fib->fib_FileName);
  615.   if (fib->fib_DirEntryType > 0 && (LSFlags & CONSOLE) != 0)
  616.     (VOID) stpcpy (cp2, penstr0);
  617.  
  618.   for (cp1 = workstr, cp2 = thefmtstr; *cp2 != 0; cp2++)
  619.   {
  620.     if (*cp2 != '%' && *cp2 != '\\')
  621.       *cp1++ = *cp2;
  622.     else
  623.     {
  624.       if (*cp2 == '%')
  625.       {
  626.         cp2++;
  627.         cp2 = GetDecNum (cp2, &spccnt);
  628.         if (spccnt > 99)
  629.         {
  630.           spccnt = 99;
  631.         }
  632.           
  633.         switch (*cp2)
  634.         {
  635.           case 'p':
  636.             reps = theprotstr;
  637.             break;
  638.           case 'd':
  639.             reps = thedatestr;
  640.             break;
  641.           case 't':
  642.             reps = thetimestr;
  643.             break;
  644.           case 'b':
  645.             reps = theblksstr;
  646.             break;
  647.           case 'q':
  648.               reps = thebreal;
  649.               break;
  650.           case 's':
  651.             reps = thesizestr;
  652.             break;
  653.           case 'n':
  654.             if (fib->fib_DirEntryType > 0 && (LSFlags & CONSOLE) != 0 && spccnt > 0)
  655.               spccnt += 7;
  656.             if ((LSFlags & FULLPATHNAMES) != 0)
  657.               reps = curpath;
  658.             else
  659.               reps = thenamestr;
  660.             break;
  661.           case '%':
  662.             *cp1++ = '%';
  663.             *cp1++ = 0;
  664.           default:
  665.             reps = &NLine[1];
  666.             break;
  667.         }
  668.         for (i = strlen(reps); i < spccnt; i++)
  669.           *cp1++ = ' ';
  670.         cp1 = stpcpy (cp1, reps);
  671.       }
  672.       else
  673.       {
  674.         cp2++;
  675.         switch (*cp2)
  676.         {
  677.           case 'n':
  678.             *cp1++ = '\n';
  679.             break;
  680.           case 't':
  681.             *cp1++ = '\t';
  682.             break;
  683.           case '\\':
  684.             *cp1++ = '\\';
  685.             break;
  686.           default:
  687.             break;
  688.         }
  689.         *cp1 = 0;
  690.       }
  691.     }
  692.   }
  693.   WSTR (workstr);
  694.  
  695.   if ((LSFlags & NOTEFLAG) != 0 && fib->fib_Comment[0] != 0)
  696.   {
  697.     SetConPen(penstr3);
  698.     (VOID)asprintf(workstr, "/* %s */\n", fib->fib_Comment);
  699.     WSTR (workstr);
  700.     SetConPen(penstr0);
  701.   }
  702.   *pathend = 0;
  703. }
  704.  
  705. /* --------------------------------------------------------------------
  706.  * Verbosely list a particular FibEntry
  707.  * -------------------------------------------------------------------- */
  708. VOID LListEntry (fib)
  709.   struct FileInfoBlock *fib;
  710. {
  711.   LONG i, pmodes;
  712.   LONG blockOFS, blockFFS;
  713.   pmodes = fib->fib_Protection & 0xff;
  714.   (VOID)stpcpy (theprotstr, "chsparwed");
  715.   for (i = 3; i >=  0; i--)
  716.   {
  717.     if ((pmodes & (1 << i)) != 0)
  718.       theprotstr[8 - i] = '-';
  719.     if ((pmodes & (1 << (i + 4))) == 0)
  720.       theprotstr[4 - i] = '-';
  721.   }
  722.  
  723.   if (fib->fib_Comment[0] == 0)
  724.     theprotstr[0] = '-';
  725.  
  726.   FibFileDate (&fib->fib_Date, thedatestr, thetimestr);
  727.  
  728.   if (fib->fib_DirEntryType > 0)
  729.   {
  730.     (VOID)stpcpy (theblksstr, "0");
  731.     (VOID)stpcpy (thesizestr, "Dir");
  732.   }
  733.   else
  734.   {
  735.     if (LSFlags & REALBLOCK)
  736.     {
  737.           blockOFS=(fib->fib_Size + 487)/488;
  738.           blockOFS+=(blockOFS + 71)/72;
  739.           blockFFS=(fib->fib_Size + 511)/512;
  740.           blockFFS+=(blockFFS + 71)/72;
  741.         (VOID) asprintf (theblksstr, LongFmtStr, blockOFS);
  742.         (VOID) asprintf (thebreal,   LongFmtStr, blockFFS);
  743.         (VOID) asprintf (thesizestr, LongFmtStr, fib->fib_Size);
  744.     }
  745.       else 
  746.     {
  747.         (VOID) asprintf (theblksstr, LongFmtStr, fib->fib_NumBlocks);
  748.         (VOID) asprintf (thesizestr, LongFmtStr, fib->fib_Size);
  749.     }
  750.   }
  751.     
  752.   ParseFormatOpts (fib);
  753. }
  754.  
  755. /* --------------------------------------------------------------------
  756.  * List a directory in a verbose informative manner
  757.  * -------------------------------------------------------------------- */
  758. VOID LListDir (fibheadp)
  759.   struct List *fibheadp;
  760. {
  761.   struct FibEntry *tfibp;
  762.   LONG blockOFS, blockFFS;
  763.  
  764.   SetConPen(penstr1);
  765.  
  766.   totblocks = totbytes = 0;
  767.   for (tfibp = (struct FibEntry *)fibheadp->lh_Head;
  768.        tfibp->fe_Node.mln_Succ != 0;
  769.        tfibp = (struct FibEntry *)tfibp->fe_Node.mln_Succ)
  770.   {
  771.     TestBreak();
  772.     if ((LSFlags & BREAKFLAG) != 0)
  773.       return;
  774.     LListEntry (tfibp->fe_Fib);
  775.     if (tfibp->fe_Fib->fib_DirEntryType < 0)
  776.     {
  777.         if (LSFlags & REALBLOCK)
  778.         {
  779.               blockOFS=(tfibp->fe_Fib->fib_Size + 487)/488;
  780.               blockOFS+=(blockOFS + 71)/72;
  781.               blockFFS=(tfibp->fe_Fib->fib_Size + 511)/512;
  782.               blockFFS+=(blockFFS + 71)/72;
  783.               totOFS+=blockOFS;
  784.               totFFS+=blockFFS;
  785.             totbytes += tfibp->fe_Fib->fib_Size;
  786.         }
  787.         else
  788.         {
  789.             totblocks += tfibp->fe_Fib->fib_NumBlocks;
  790.             totbytes += tfibp->fe_Fib->fib_Size;
  791.         }
  792.     }
  793.   }
  794.  
  795.   if ((LSFlags & (BREAKFLAG | NOHEADERS)) == 0)
  796.   {
  797.     if (LSFlags & REALBLOCK) (VOID) asprintf (workstr, totalrealstr, dircount, filecount, totOFS, totFFS, totbytes);
  798.     else (VOID) asprintf (workstr, totalfmtstr, dircount, filecount, totblocks, totbytes);
  799.     WSTR (workstr);
  800.   }
  801.  
  802.   SetConPen(penstr2);
  803. }
  804.  
  805. /* -------------------------------------------------------------------- */
  806. LONG CmpDateBounds (tdate)
  807.   struct DateStamp *tdate;
  808. {
  809.   if ((LSFlags & SHOWNEWERTHAN) != 0)
  810.   {
  811.     if (CompareDateStamps(&thenewdate, tdate) >= 0)
  812.       return (0L);
  813.   }
  814.   if ((LSFlags & SHOWOLDERTHAN) != 0)
  815.   {
  816.     if (CompareDateStamps(tdate, &theolddate) >= 0)
  817.       return (0L);
  818.   }
  819.   return (1L);
  820. }
  821.  
  822. /* --------------------------------------------------------------------
  823.  * Free up memory allocated to a linked list of FibEntrys
  824.  * -------------------------------------------------------------------- */
  825. VOID FreeAllFibs (fibheadp)
  826.   struct List *fibheadp;
  827. {
  828.   struct FibEntry *tfibp;
  829.  
  830.   if (fibheadp != 0)
  831.   {
  832.     while (fibheadp->lh_Head->ln_Succ != 0)
  833.     {
  834.       tfibp = (struct FibEntry *)RemTail(fibheadp);
  835.       myfree (tfibp);
  836.     }
  837.  
  838.   /* Now free the MinList itself */
  839.     myfree (fibheadp);
  840.   }
  841. }
  842.  
  843. /* --------------------------------------------------------------------
  844.  * Allocate and fill a linked list of FileInfoBlocks
  845.  * -------------------------------------------------------------------- */
  846. struct List *GetDir (lockp, fibp)
  847.   struct FileLock *lockp;
  848.   struct FileInfoBlock *fibp;
  849. {
  850.   BYTE *thepat;                /* Pattern pointer to dir or file pattern */
  851.   LONG matchstat;              /* Result of wildmatch() */
  852.   LONG nextstat;               /* Status of ExNext() */
  853.   LONG tempnamlen;             /* Compare length for strings */
  854.   LONG dfcount;
  855.   struct List *fibhead;        /* Temp list of Fibs created */
  856.   struct List *dirhead;        /* Temp list of dirs if SHOWDIRS == 0 */
  857.  
  858.   maxnamlen = dfcount = dircount = filecount = 0L;
  859.  
  860. /* Initialize an exec list of nodes, zero entries */
  861.   if ((fibhead = myalloc ((LONG)sizeof(struct MinList))) == 0)
  862.     return (0L);
  863.   NewList (fibhead);
  864.  
  865. /* Allocate an separate list for directories that don't match specs */
  866.   if ((dirhead = myalloc((LONG)sizeof(struct MinList))) == 0)
  867.     goto BADALLOC;
  868.   NewList (dirhead);
  869.  
  870.   do
  871.   {
  872.     TestBreak ();
  873.     if ((LSFlags & BREAKFLAG) != 0)
  874.       goto GOODRET;
  875.  
  876.   /* If we got something */
  877.     if ((nextstat = ExNext ((BPTR)lockp, fibp)) != 0)
  878.     {
  879.       dfcount++;
  880.  
  881.     /* If the entry is wanted bump count of files or directories */
  882.       if (CmpDateBounds (&fibp->fib_Date) != 0)
  883.       {
  884.         if (fibp->fib_DirEntryType > 0) /* It's a directory */
  885.           thepat = theDirPat;
  886.         else
  887.           thepat = theFilePat;
  888.  
  889.         matchstat = wildmatch (fibp->fib_FileName, thepat);
  890.         if ((LSFlags & ANTIMATCH) != 0 && matchstat != 0)
  891.           matchstat = wildmatch (fibp->fib_FileName, theAntiPat) ^ 1;
  892.  
  893.     if (matchstat == 0)    /* No match? Then move on */
  894.       continue;
  895.  
  896.         if (fibp->fib_DirEntryType > 0) /* It's a directory */
  897.         {
  898.           if (FillFibEntry (dirhead, fibp) == 0)
  899.             goto BADALLOC;
  900.           if ((LSFlags & SHOWDIRS) != 0)
  901.           {
  902.             dircount++;
  903.             gdircount++;
  904.           }
  905.           else
  906.             continue;
  907.         }
  908.         else                            /* It's a file entry */
  909.         {
  910.           if ((LSFlags & SHOWFILES) != 0)
  911.           {
  912.             filecount++;
  913.             gfilecount++;
  914.             gtotblocks += fibp->fib_NumBlocks;
  915.             gtotbytes += fibp->fib_Size;
  916.           }
  917.           else                /* Don't want this file, move on to next entry */
  918.             continue;
  919.         }
  920.  
  921.       /* See if this is the longest filename for later use in listing */
  922.         tempnamlen = strlen (fibp->fib_FileName);
  923.         if (tempnamlen > maxnamlen)
  924.           maxnamlen = tempnamlen;
  925.  
  926.       /* Allocate another FibEntry, put the info inside */
  927.         if (FillFibEntry (fibhead, fibp) == 0)
  928.           goto BADALLOC;
  929.       }
  930.     }
  931.   } while (nextstat != 0);
  932.  
  933. /* No entries found? print message and return FALSE */
  934.   if ((dircount + filecount) == 0)
  935.   {
  936.     if ((LSFlags & NOHEADERS) == 0)
  937.     {
  938.       if (dfcount == 0)
  939.         WSTR (VolEmptyMsg);
  940.       else
  941.         WSTR (NoMatchMsg);
  942.     }
  943.   }
  944.   else
  945.   {
  946.     if ((LSFlags & (SHOWDIRS|SHOWFILES)) != 0)
  947.     {
  948.       if ((LSFlags & LONGLIST) == 0)  /* Short listing wanted */
  949.         SListDir (fibhead);
  950.       else                            /* Full listing */
  951.         LListDir (fibhead);
  952.     }
  953.   }
  954.  
  955. GOODRET:
  956.   FreeAllFibs (fibhead);
  957.   return (dirhead);
  958.  
  959. BADALLOC:
  960.   FreeAllFibs (fibhead);
  961.   FreeAllFibs (dirhead);
  962.   return (0L);
  963. }
  964.  
  965. /* --------------------------------------------------------------------
  966.  * Given a directory name and a lock on that directory, create a list
  967.  * of entries.  Recursively decends into subdirectories if flaged.
  968.  * -------------------------------------------------------------------- */
  969. VOID DirIt (curlock, dirname)
  970.   struct FileLock *curlock;
  971.   BYTE *dirname;
  972. {
  973.   BYTE *subdir;
  974.   LONG dnamlen;
  975.   struct FibEntry *tfibp;
  976.   struct FileLock *sublock;
  977.   struct List *fibheadp;
  978.  
  979. /* Try to fill FileInfoBlock, bomb if not readable for some reason */
  980.   if (Examine ((BPTR)curlock, GFibp) == 0)
  981.   {
  982.     (VOID) asprintf (workstr, NoExamFmtStr, IoErr());
  983.     WSTR (workstr);
  984.     return;
  985.   }
  986.  
  987. /* Put directory header for each listing, if we know the name && recursive */
  988.   if (dirname[0] != 0 && (LSFlags & LISTALL) != 0 && (LSFlags & NOHEADERS) == 0)
  989.   {
  990.     if ((LSFlags & CONSOLE) != 0)
  991.      (VOID)asprintf (workstr, "\23330;41m %s \2330m\n", dirname);
  992.     else
  993.      (VOID)asprintf (workstr, "%s\n", dirname);
  994.     WSTR (workstr);
  995.   }
  996.  
  997. /* If this is a single file list it verbosely */
  998.   if (GFibp->fib_DirEntryType < 0)
  999.   {
  1000.     if ((LSFlags & SHOWFILES) != 0)
  1001.     {
  1002.       (VOID) GetPathString (thePath, thePath);
  1003.       LListEntry (GFibp);
  1004.     }
  1005.   }
  1006.   else /* It is a directory entry */
  1007.   {
  1008.   /* Allocate, fill, and display a dir of entries, check for no ram or abort */
  1009.     if ((fibheadp = GetDir (curlock, GFibp)) != 0)
  1010.     {
  1011.     /* Recursively descend any subdirs in this list, if wanted */
  1012.       if ((LSFlags & LISTALL) != 0)
  1013.       {
  1014.         for (tfibp = (struct FibEntry *)fibheadp->lh_Head;
  1015.               tfibp->fe_Node.mln_Succ != 0;
  1016.               tfibp = (struct FibEntry *)tfibp->fe_Node.mln_Succ)
  1017.         {
  1018.           TestBreak ();
  1019.           if ((LSFlags & BREAKFLAG) != 0)
  1020.             break;
  1021.  
  1022.           if (tfibp->fe_Fib->fib_DirEntryType > 0)
  1023.           {
  1024.           /* Alloc length of path + 1 + newdir name +1 + length of maxname + 1 for NULL
  1025.            * + 1 for null + 3 for rounding.
  1026.            */
  1027.             dnamlen = (LONG)(strlen (dirname) + strlen (tfibp->fe_Fib->fib_FileName) + 36);
  1028.             if ((subdir = myalloc ((LONG)dnamlen)) != 0)
  1029.             {
  1030.               if (dirname[0] != 0)
  1031.               {
  1032.                 (VOID) stpcpy (subdir, dirname);
  1033.                 dnamlen = strlen (dirname) - 1;
  1034.                 if (dirname[dnamlen] != ':' && dirname[dnamlen] != '/')
  1035.                 {
  1036.                   (VOID) strcat (subdir, SlashStr);
  1037.                 }
  1038.               }
  1039.               (VOID) strcat (subdir, tfibp->fe_Fib->fib_FileName);
  1040.  
  1041.               if ((sublock = (struct FileLock *)Lock (subdir, (LONG)ACCESS_READ)) != 0)
  1042.               {
  1043.                 if ((LSFlags & NOHEADERS) == 0)
  1044.                   WCHR (NLine);                 /* Put a blank line between directories */
  1045.         
  1046.         curpath = subdir;
  1047.  
  1048.                 DirIt (sublock, subdir); /* Recurse into this subdirectory */
  1049.  
  1050.                 UnLock ((BPTR)sublock);     /* Unlock our sublock */
  1051.               }
  1052.               myfree (subdir);           /* Free the current namespace */
  1053.             }
  1054.           }
  1055.         }
  1056.       }
  1057.     /* Free up this fib list */
  1058.       FreeAllFibs (fibheadp);
  1059.     }
  1060.   }
  1061. }
  1062.  
  1063. /* -------------------------------------------------------------------- */
  1064. VOID GetCLIArgs (line, argc, argv)
  1065.   BYTE *line;
  1066.   LONG *argc;
  1067.   BYTE **argv;
  1068. {
  1069.   BYTE **pargv, *qarg;
  1070.  
  1071.   *argc = 0;
  1072.   while (*argc < MAXARG)
  1073.   {
  1074.     while (*line == ' ' || *line == '\t' || *line == '\n')
  1075.       line++;
  1076.     if (*line == 0)
  1077.       break;
  1078.     pargv = &argv[*argc];
  1079.     *argc += 1;
  1080.  
  1081.     if (*line == '"')
  1082.     {
  1083.       qarg = line;
  1084.       line += 1;
  1085.       *pargv = line;                 /* ptr inside quoted string */
  1086.       while (*line != 0 && *line != '"')
  1087.         line++;
  1088.       if (*line == 0)                /* Hit end of string without quote! */
  1089.       {
  1090.         *pargv = qarg;               /* Must be okay */
  1091.         break;
  1092.       }
  1093.       else
  1094.         *line++ = 0;                 /* terminate arg ontopof quote */
  1095.     }
  1096.     else                             /* non-quoted arg */
  1097.     {
  1098.       *pargv = line;
  1099.       while (*line != 0 && !(*line == ' ' || *line == '\t' || *line == '\n'))
  1100.         line++;
  1101.       if (*line == 0)
  1102.         break;
  1103.       else
  1104.         *line++ = 0;                 /* terminate arg */
  1105.     }
  1106.   }                                  /* while */
  1107. }
  1108.  
  1109. /* -------------------------------------------------------------------- */
  1110. LONG GetFileDate(name, ptime)
  1111.   char *name;
  1112.   struct DateStamp *ptime;
  1113. {
  1114.   LONG status;
  1115.   struct FileLock *flock;
  1116.   struct FileInfoBlock *fib;
  1117.  
  1118.   status = 0;
  1119.   if ((fib = myalloc ((LONG)sizeof(struct FileInfoBlock))) != 0)
  1120.   {
  1121.     if ((flock = (struct FileLock *)Lock (name, (LONG)ACCESS_READ)) != 0)
  1122.     {
  1123.       if (Examine ((BPTR)flock, fib) != 0)
  1124.       {
  1125.         *ptime = fib->fib_Date;     /* Copy the Date structure */
  1126.         status = 1;
  1127.       }
  1128.       UnLock ((BPTR)flock);
  1129.     }
  1130.     myfree (fib);
  1131.   }
  1132.   return (status);
  1133. }
  1134.  
  1135. /* --------------------------------------------------------------------
  1136.  * Deallocate and close everything
  1137.  * -------------------------------------------------------------------- */
  1138. VOID CleanUp (exit_msg, exit_status, result2)
  1139.   BYTE *exit_msg;
  1140.   LONG exit_status, result2;
  1141. {
  1142.   BYTE workstr[WORKSIZE];
  1143.   struct Process *procp;
  1144.  
  1145. /* Make sure we unlock any locks we created! */
  1146.   if (CurFLock != 0 && (LSFlags & PATHNAMED) != 0)
  1147.     UnLock ((BPTR)CurFLock);
  1148.  
  1149. /* Free our fib */
  1150.   myfree (GFibp);
  1151.  
  1152.   if (*exit_msg != 0)
  1153.   {
  1154.     (VOID) asprintf (workstr, ErrFmtStr, exit_msg, result2);
  1155.     WSTR (workstr);
  1156.   }
  1157.  
  1158. /* Put windowptr back */
  1159.   procp = (struct Process *) FindTask (0L);
  1160.   procp->pr_WindowPtr = OldWindowPtr;
  1161.  
  1162. /* Set return status, exit */
  1163.   procp->pr_Result2 = result2;
  1164.   exit ((int)exit_status);
  1165. }
  1166.  
  1167. /* --------------------------------------------------------------------
  1168.  * Explain how to use.
  1169.  * -------------------------------------------------------------------- */
  1170. VOID Usage ()
  1171. {
  1172.   LONG i;
  1173.  
  1174.   for (i = 0; usagestrs[i] != 0; i++)
  1175.     WSTR (usagestrs[i]);
  1176.   CleanUp (&NLine[1], 20L, 120L);
  1177. }
  1178.  
  1179. /* -------------------------------------------------------------------- */
  1180. LONG ParseCmdOptions (ncnt, argc, argv)
  1181.   LONG ncnt, argc;
  1182.   BYTE **argv;
  1183. {
  1184.   BYTE tmpmsg[4];
  1185.   LONG i, cnt, len;
  1186.   struct Process *procp;
  1187.  
  1188.   cnt = ncnt;      /* Current arg number that contains the options */
  1189.   ncnt += 1;       /* Advance to next arg */
  1190.  
  1191.   for (i = 1, len = strlen (argv[cnt]); i < len; i++)
  1192.   {
  1193.     switch (argv[cnt][i])
  1194.     {
  1195.       case '?':
  1196.       case 'h':
  1197.         Usage ();
  1198.         break;
  1199.       case 'D':
  1200.         LSFlags |= FILESFIRST;
  1201.         break;
  1202.       case 'F':
  1203.         if (argc < (ncnt + 1))     /* Missing required format string */
  1204.           Usage ();
  1205.         thefmtstr = argv[ncnt];    /* Else our format string is the next arg */
  1206.         ncnt += 1;                 /* Bump arg counter */
  1207.         LSFlags |= LONGLIST;
  1208.         break;
  1209.       case 'H':
  1210.         LSFlags |= NOHEADERS;
  1211.         break;
  1212.       case 'I':
  1213.         LSFlags |= NOINTERACT;
  1214.         break;
  1215.       case 'M':
  1216.         LSFlags |= MIXFILESDIRS;
  1217.         break;
  1218.       case 'N':
  1219.         if (argc < (ncnt + 1))     /* Missing required name string */
  1220.           Usage ();
  1221.         else
  1222.         {
  1223.           if (GetFileDate (argv[ncnt], &thenewdate) == 0)
  1224.             NoFileExit (argv[ncnt]);
  1225.           ncnt += 1;                 /* Bump arg counter */
  1226.           LSFlags |= SHOWNEWERTHAN;
  1227.         }
  1228.         break;
  1229.       case 'O':
  1230.         if (argc < (ncnt + 1))     /* Missing required name string */
  1231.         {
  1232.           Usage ();
  1233.         }
  1234.         else
  1235.         {
  1236.           if (GetFileDate (argv[ncnt], &theolddate) == 0)
  1237.             NoFileExit (argv[ncnt]);
  1238.           ncnt += 1;                 /* Bump arg counter */
  1239.           LSFlags |= SHOWOLDERTHAN;
  1240.         }
  1241.         break;
  1242.       case 'P':
  1243.         LSFlags |= (FULLPATHNAMES | LONGLIST);
  1244.         break;
  1245.       case 'R':
  1246.         LSFlags |= LISTALL;
  1247.         break;
  1248.       case 'T':
  1249.         LSFlags |= TOTALIZE;
  1250.         break;
  1251.       case 'X':
  1252.         if (argc < (ncnt + 1))     /* Missing required width number */
  1253.         {
  1254.           Usage ();
  1255.         }
  1256.         else
  1257.         {
  1258.           (VOID) GetDecNum (argv[ncnt], &CurWinCols);
  1259.           ncnt += 1;                 /* Bump arg counter */
  1260.         }
  1261.         break;
  1262.       case 'Y':
  1263.         if (argc < (ncnt + 1))     /* Missing required height number */
  1264.         {
  1265.           Usage ();
  1266.         }
  1267.         else
  1268.         {
  1269.           (VOID) GetDecNum (argv[ncnt], &CurWinRows);
  1270.           ncnt += 1;                 /* Bump arg counter */
  1271.         }
  1272.         break;
  1273.       case 'c':
  1274.         LSFlags |= LONGLIST | NOTEFLAG;
  1275.         break;
  1276.       case 'd':
  1277.         LSFlags &= ~SHOWFILES;
  1278.         break;
  1279.       case 'f':
  1280.         LSFlags &= ~SHOWDIRS;
  1281.         break;
  1282.       case 'k':
  1283.         LSFlags &= ~CONSOLE;
  1284.         break;
  1285.       case 'a':
  1286.         LSFlags |= LONGLIST;
  1287.         LSFlags |= REALBLOCK;
  1288.         strcpy(deffmtstr,deffmtstr2);
  1289.         break;        
  1290.       case 'l':
  1291.         LSFlags |= LONGLIST;
  1292.         break;
  1293.       case 'n':
  1294.         LSFlags |= NOSORTFLAG;
  1295.         break;
  1296.       case 'p':
  1297.         procp = (struct Process *) FindTask (0L);
  1298.         procp->pr_WindowPtr = OldWindowPtr;
  1299.         break;
  1300.       case 'r':
  1301.         LSFlags |= REVFLAG;
  1302.         break;
  1303.       case 's':
  1304.         sortkey = 1;
  1305.         break;
  1306.       case 't':
  1307.         sortkey = 2;
  1308.         break;
  1309.       case 'v':
  1310.         if (argc < (ncnt + 1))         /* Missing required pattern string */
  1311.           Usage ();
  1312.         theAntiPat = argv[ncnt];    /* Point to pattern area */
  1313.         ncnt += 1;            /* Skip over arg */
  1314.         LSFlags |= ANTIMATCH;        /* Set flag to use antipat */
  1315.         break;
  1316.       default:
  1317.         tmpmsg[0] = argv[cnt][i];
  1318.         tmpmsg[1] = 0;
  1319.         (VOID) asprintf (workstr, BadOptFmtStr, tmpmsg);
  1320.         WSTR (workstr);
  1321.         Usage ();
  1322.         break;
  1323.     }
  1324.   }
  1325.   return(ncnt);
  1326. }
  1327.  
  1328. /* -------------------------------------------------------------------- */
  1329. VOID _main (line)
  1330.   BYTE *line;
  1331. {
  1332.   BYTE *argv[MAXARG];             /* arg pointers */
  1333.   LONG argc;                      /* arg count */
  1334.   LONG cnt;
  1335.   struct Process *procp;
  1336.  
  1337. /* Prevent system request from occuring by default */
  1338.   procp = (struct Process *) FindTask (0L);
  1339.   OldWindowPtr = procp->pr_WindowPtr;
  1340.   procp->pr_WindowPtr = (APTR)-1L;
  1341.   
  1342. /* Construct list of args */
  1343.   GetCLIArgs (line, &argc, argv);
  1344.  
  1345. /* Grab FileHandles for input and output to console (or redirection file) */
  1346.   ConIn = (struct FileHandle *)Input ();
  1347.   ConOut = (struct FileHandle *)Output ();
  1348.  
  1349. /* Is this a CLI? Set a flag */
  1350.   if (IsInteractive ((BPTR)ConOut) != 0 && IsInteractive ((BPTR)ConIn) != 0)
  1351.     LSFlags |= CONSOLE;
  1352.  
  1353. /* Allocate a global FileInfoBlock for ExNext() */
  1354.   if ((GFibp = myalloc ((LONG) sizeof (struct FileInfoBlock))) == 0)
  1355.     NoMemExit ();
  1356.  
  1357. /* Initialize arg count, zero grand totals */
  1358.   cnt = 1;
  1359.   gtotblocks = gtotbytes = gdircount = gfilecount = 0;
  1360.  
  1361. /* Parse command line arguments, if any */
  1362.   do
  1363.   {
  1364.     if (cnt < argc && argv[cnt][0] == '-')
  1365.     {
  1366.     /* Reset for next arg */ 
  1367.       LSFlags &= CONSOLE;
  1368.       LSFlags |= SHOWDIRS | SHOWFILES;
  1369.       thefmtstr = deffmtstr;
  1370.  
  1371.       do
  1372.       {
  1373.         cnt = ParseCmdOptions(cnt, argc, argv);
  1374.       } while (cnt < argc && argv[cnt][0] == '-');
  1375.     }
  1376.  
  1377.   /* Is there an named path arg to do? */
  1378.     if (cnt < argc)
  1379.     {
  1380.       LSFlags |= PATHNAMED;                  /* Flag that we have a name */
  1381.       theFilePat[0] = 0;                     /* Terminate pattern strings */
  1382.       theDirPat[0] = 0;
  1383.       (VOID) stpcpy (thePath, argv[cnt]);    /* Copy this arg to work space */
  1384.       cnt++;                                 /* Advance arg counter */
  1385.  
  1386.     /* Wild pathname? Separate into components until we find a non-wild path */
  1387.       if (iswild (thePath) != 0)
  1388.       {
  1389.         (VOID) GetFileString (theFilePat, thePath);
  1390.         (VOID) stpcpy (theDirPat, theFilePat);
  1391.         (VOID) GetPathString (thePath, thePath);
  1392.  
  1393.       /* Still wild?  First part must be wild filename to match */
  1394.         if (iswild (thePath) != 0)
  1395.         {
  1396.           (VOID) GetFileString (theDirPat, thePath);
  1397.           (VOID) GetPathString (thePath, thePath);
  1398.         }
  1399.       }
  1400. #ifdef DEBUGIT
  1401.       asprintf(workstr, "path: %s\n", thePath); WSTR (workstr);
  1402.       asprintf(workstr, " dir: %s\n", theDirPat); WSTR (workstr);
  1403.       asprintf(workstr, "file: %s\n", theFilePat); WSTR (workstr);
  1404. #endif
  1405.  
  1406.     /* No wildcards allowed in the final pathname! */
  1407.       if (iswild (thePath) != 0)
  1408.         CleanUp (NoWildPathMsg, 20L, 120L);
  1409.  
  1410.     /* Now try to lock the dir (or file) */
  1411.       if ((CurFLock = (struct FileLock *)Lock (thePath, (LONG)ACCESS_READ)) == 0)
  1412.       {
  1413.         NoFileExit (thePath);
  1414.       }
  1415.     }
  1416.     else
  1417.     {
  1418.     /*
  1419.      * If no filename was specified, steal Lock on current directory from
  1420.      * CLI process task info.  We durn well better get something useful back;
  1421.      * since we don't do any error checking on the "borrowed" Lock.
  1422.      */
  1423.       CurFLock = (struct FileLock *)procp->pr_CurrentDir;
  1424.     }
  1425.  
  1426.   /* Make a full pathname string from given CurFLock if no colon in path */
  1427.     if (aindex(thePath, ':') == 0)
  1428.       MakePathString (CurFLock, thePath);
  1429.     curpath = thePath;
  1430.  
  1431. #ifdef DEBUGIT
  1432.     asprintf(workstr, "Final path: %s\n", thePath); WSTR (workstr);
  1433. #endif
  1434.  
  1435.   /* If there isn't a dir pattern or file pattern specified, match everything */
  1436.     if (theDirPat[0] == 0)
  1437.     {
  1438.       theDirPat[0] = '*';               /* "*" default matchall dir pattern */
  1439.       theDirPat[1] = 0;                 /* Null terminate string */
  1440.     }
  1441.  
  1442.     if (theFilePat[0] == 0)
  1443.     {
  1444.       theFilePat[0] = '*';
  1445.       theFilePat[1] = 0;
  1446.     }
  1447.  
  1448.   /* Get the directory for this path, display it */
  1449.     DirIt (CurFLock, thePath);
  1450.  
  1451.   /* Release the lock if we locked it */
  1452.     if (CurFLock != 0 && (LSFlags & PATHNAMED) != 0)
  1453.     {
  1454.       UnLock ((BPTR)CurFLock);
  1455.     }
  1456.     CurFLock = 0;
  1457.  
  1458.   /* Still more args? Put a linefeed between listing outputs
  1459.     if (cnt < argc && (LSFlags & NOHEADERS) == 0)
  1460.       WCHR (NLine);
  1461.    */
  1462.     TestBreak();
  1463.     if ((LSFlags & TOTALIZE) != 0)
  1464.     {
  1465.       SetConPen (penstr4);
  1466.       WSTR (TotHeaderMsg);
  1467.       SetConPen (penstr5);
  1468.       (VOID) asprintf (workstr, totalfmtstr, gdircount, gfilecount, gtotblocks, gtotbytes);
  1469.       WSTR (workstr);
  1470.       SetConPen (penstr0);
  1471.     }
  1472.   } while (cnt < argc && (LSFlags & BREAKFLAG) == 0);
  1473.  
  1474. /* All done! Clean exit */
  1475.   CleanUp (&NLine[1], 0L, 0L);
  1476. }
  1477.