home *** CD-ROM | disk | FTP | other *** search
/ The CDPD Public Domain Collection for CDTV 3 / CDPDIII.bin / fish / 771-780 / ff773 / ls / ls.c < prev    next >
C/C++ Source or Header  |  1992-12-05  |  37KB  |  1,531 lines

  1. /*
  2.  *  LS 4.7ljr
  3.  *
  4.  *  This software is Copyright © 1989, 1990, 1991, 1992  Loren J. Rittle.
  5.  *  This software is Copyright for the sole purpose of protecting
  6.  *  the fact that this is indeed free software.
  7.  *
  8.  *  This software is wholly, but at this point loosely, based upon
  9.  *  Justin V. McCormick's PD LS ``V3.1  July 29, 1989'' with
  10.  *  enhancements and bug fixes by the copyright holder, Loren J. Rittle.
  11.  */
  12.  
  13. /* This program is free software; you can redistribute it and/or modify
  14. it under the terms of the GNU General Public License as published by
  15. the Free Software Foundation; either version 2, or (at your option)
  16. any later version.
  17.  
  18. This program is distributed in the hope that it will be useful,
  19. but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21. GNU General Public License for more details.
  22.  
  23. You should have received a copy of the GNU General Public License
  24. along with this program; if not, write to the Free Software
  25. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  26.  
  27. See COPYING and ls.doc for copyright and copying information. */
  28.  
  29. /* If you generate a new version for release to the public,
  30. please replace my initials 'ljr' with your own (so the public
  31. knows who to blame... :-).  Your version should be marked
  32. (at least in the source and documentation) as having been based
  33. upon the 'ljr' strain version <current version number>.
  34.  
  35. On the other hand, you can send bug reports and patches to
  36. rittle@comm.mot.com for me to incorporate into my next major
  37. release.
  38.  
  39. Loren J. Rittle
  40. rittle@comm.mot.com */
  41.  
  42. #include "ls.h"
  43.  
  44. #define MAXARG 100
  45. #define PADTABSIZE 256
  46. #define WORKSIZE 1024
  47.  
  48. #if !defined(VERSION_STRING) || !defined(SHORT_DATE_STRING) || !defined(DATE_STRING)
  49. #error VERSION_STRING, SHORT_DATE_STRING and DATE_STRING must be defined.
  50. #error Use smake to compile.
  51. #endif
  52.  
  53. void _CXBRK (int sig);
  54. void _CXBRK (int sig) {}
  55.  
  56. BYTE version[]=
  57. "\0$VER: ls-(ljr_strain) " VERSION_STRING " (" SHORT_DATE_STRING ")";
  58. BYTE shortusage[]= "LS Version " VERSION_STRING "ljr, " DATE_STRING "\n\
  59. Usage: ls [-h?acdefklnrstw1ADFHIMNOPRTX <args>] [path] ...\n";
  60. BYTE fullusage[]= "\
  61.     a  List all entries    A  Display entries across line\n\
  62.     c  Show filenotes    D  Show dirs last\n\
  63.     d  About dirs        F <format> Format output\n\
  64.     e  Sort by extension    H  No headings and subtotals\n\
  65.     f  Only show files    I <pattern> Ignore pattern\n\
  66.     k  Kill ANSI codes    M  Mixed output files and dirs\n\
  67.     l  Long listing        N <date> Show newer than date\n\
  68.     n  No sort        O <date> Show older than date\n\
  69.     r  Reverse sort        P  Show full pathnames\n\
  70.     s  Sort by size        R  Recursive listing\n\
  71.     t  Sort by date        T  Total all subtotals\n\
  72.     w <string> Set %%w    X <wide> Set output columns\n\
  73.     1  One entry per line\n";
  74. BYTE deffmtstr[]= "%p %5b %8s %d %n%w%C\\n";
  75. BYTE deffullstr[]= "%n\\n";
  76. BYTE LongFmtStr[]= "%ld";
  77. BYTE totalfmtstr[]= "Dirs:%-4ld Files:%-4ld Blocks:%-5ld Bytes:%-8ld\n";
  78. BYTE TotHeaderMsg[]= "Totals:\n";
  79. BYTE ErrFmtStr[]= "ls: %s\n";
  80. BYTE BadOptFmtStr[]= "ls: Unknown option -%lc\n";
  81. BYTE OptNeedsArgStr[]= "ls: Option -%lc needs argument\n";
  82. BYTE NoExamFmtStr[]= "ls: Cannot examine file or directory, Error #%ld\n";
  83. BYTE NoFindFmtStr[]= "ls: '%s' not found\n";
  84. BYTE NotGoodDateStr[]= "ls: '%s' not a date\n";
  85. BYTE NoWildPathMsg[]= "Unable to pattern match paths";
  86. BYTE NoRAMMsg[]= "No RAM";
  87. BYTE ParseErrorMsg[]= "ParsePattern Error";
  88. BYTE NameFromLockMsg[]= "NameFromLock Error";
  89. BYTE ExNextMsg[]= "ExNext Error";
  90. BYTE NoCurrentDirMsg[]= "No Current Directory";
  91.  
  92. /* these are basically related to fib_DirEntryType after being
  93.    remaped by RemapDirEntryType (). */
  94. struct highlight highlight_tabx[13] =
  95. {
  96.  /* -7 FILE_DEFAULT */ {"", "", 0},
  97.  /* -6 LINKFILE_EXE */ {"\x9b" "1m", "\x9b" "22m", 0},
  98.  /* -5 FILE_EXE     */ {"", "", 0},
  99.  /* -4 LINKFILE     */ {"\x9b" "1m", "\x9b" "22m", 0},
  100.  /* -3 FILE         */ {"", "", 0},
  101.  /* -2 SOFTLINK_EXE */ {"\x9b" "3m", "\x9b" "23m", 0},
  102.  /* -1 SOFTLINK     */ {"\x9b" "3m", "\x9b" "23m", 0},
  103.  /*  0 COMMENT      */ {"\x9b" "2m/* ", " */\x9b" "22m", 6},
  104.  /*  1 ROOT         */ {"\x9b" "2m", "\x9b" "22m", 0},
  105.  /*  2 USERDIR      */ {"\x9b" "2m", "\x9b" "22m", 0},
  106.  /*  3 LABEL        */ {"\x9b" "2m", "\x9b" "22m", 0},
  107.  /*  4 LINKDIR      */ {"\x9b" "2;1m", "\x9b" "22m", 0},
  108.  /*  5 DIR_DEFAULT  */ {"", "", 0},
  109. }, *highlight_tab = &highlight_tabx[7];
  110. #define HIGHLIGHT_MAX 5
  111. #define HIGHLIGHT_MIN -7
  112.  
  113. #define HI_FILE_DEFAULT -7
  114. #define HI_LINKFILE_EXE -6
  115. #define HI_FILE_EXE -5
  116. #define HI_LINKFILE -4
  117. #define HI_FILE -3
  118. #define HI_SOFTLINK_EXE -2
  119. #define HI_SOFTLINK -1
  120. #define HI_COMMENT 0
  121. #define HI_ROOT 1
  122. #define HI_USERDIR 2
  123. #define HI_LABEL 3
  124. #define HI_LINKDIR 4
  125. #define HI_DIR_DEFAULT 5
  126.  
  127. struct highlight highlight_null = {"", "", 0};
  128. struct highlight highlight_cursor = {"\x9b" " p", "\x9b" "0 p", 0};
  129.  
  130. struct DateStamp theolddate;
  131. struct DateStamp thenewdate;
  132. struct FileLock *CurFLock;
  133.  
  134. /* Don't change order of next four entries.  Order needs to be preserved
  135.    for VPrintf calls. */
  136. LONG gdircount;
  137. LONG gfilecount;
  138. LONG gtotblocks;
  139. LONG gtotbytes;
  140. /* end of important order */
  141.  
  142. /* Don't change order of next four entries.  Order needs to be preserved
  143.    for VPrintf calls. */
  144. LONG dircount;
  145. LONG filecount;
  146. LONG totblocks;
  147. LONG totbytes;
  148. /* end of important order */
  149.  
  150. LONG maxnamlen;
  151. enum sortkey
  152. {
  153.   sort_alpha, sort_size, sort_date, sort_extension, sort_none
  154. } sortkey;
  155. char *wflag = "\n";
  156. BYTE padtab[PADTABSIZE];
  157. BYTE thePath[WORKSIZE * 2 + 4];
  158. BYTE theDirPat[WORKSIZE];
  159. BYTE theFilePat[WORKSIZE];
  160. BYTE theDirPatParsed[WORKSIZE * 2 + 2];
  161. BYTE theFilePatParsed[WORKSIZE * 2 + 2];
  162. BYTE workstr[WORKSIZE + 64];
  163. BYTE theblksstr[64];
  164. BYTE thedatestr[64];
  165. BYTE theprotstr[64];
  166. BYTE thesizestr[64];
  167. BYTE thehexsizestr[64];
  168. BYTE thetimesizestr[64];
  169. LONG CurWinCols;
  170. BYTE *curpath;
  171. BYTE *thefmtstr = deffmtstr;
  172. struct AnAntiPattern *TheAntiPatterns;
  173.  
  174. int BREAKFLAG;
  175. int CONSOLE;
  176. int HIDEDIRS;
  177. int LISTALL;
  178. int LONGLIST;
  179. int NOTEFLAG;
  180. int PATHNAMED;
  181. int REVFLAG;
  182. int ABOUTDIRS;
  183. int FULLPATHNAMES;
  184. int TOTALIZE;
  185. int NOHEADERS;
  186. int FILESFIRST;
  187. int MIXFILESDIRS;
  188. int SHOWOLDERTHAN;
  189. int SHOWNEWERTHAN;
  190. int SHOWHIDDEN;
  191. int ACROSSLIST;
  192.  
  193. static int CompareExtensions (char *, char *);
  194. static int CmpDateBounds (struct DateStamp *);
  195. static int CompFibs (enum sortkey, struct FileInfoBlock *, struct FileInfoBlock *);
  196. static void WriteErrorString (BYTE *,...);
  197. static void TestBreak (void);
  198. static void DateStr (BYTE *, struct DateStamp *);
  199. static int StrDate (BYTE *, struct DateStamp *);
  200. static void CleanUp (BYTE *, LONG, LONG);
  201. static void AddAntiPattern (BYTE *);
  202. static void Usage (int);
  203. void kput1 (void);
  204. static void GetWinBounds (LONG *, LONG *, LONG);
  205. static int FillFibEntry (struct MinList *, struct FileInfoBlock *);
  206. static struct FibEntry *ModNextFib (struct FibEntry *, LONG);
  207. static void SListDir (struct MinList *);
  208. static void LListEntry (struct FileInfoBlock *);
  209. static void LListDir (struct MinList *);
  210. static void FreeAllFibs (struct MinList *);
  211. static struct MinList *GetDir (struct FileLock *, struct FileInfoBlock *);
  212. static void DirIt (struct FileLock *, BYTE *);
  213. static void GetCLIArgs (BYTE *, LONG *, BYTE **);
  214. static LONG ParseCmdOptions (LONG, LONG, BYTE **);
  215. static char *stpcpy (char *, char *);
  216. int stricmp (char *, char *);
  217. static void RemapDirEntryType (struct FileInfoBlock *a);
  218.  
  219. static void RemapDirEntryType (struct FileInfoBlock *a)
  220. {
  221.   /* Remap fib_DirEntryType to allow for easier usage later. */
  222.   /* See order in highlight_tab for more details. */
  223.   switch (a->fib_DirEntryType)
  224.     {
  225.       case ST_LINKFILE:
  226.       case ST_FILE:
  227.       if ((a->fib_Protection & FIBF_SCRIPT) ||
  228.       (~a->fib_Protection & FIBF_EXECUTE))
  229.     a->fib_DirEntryType -= 2;
  230.       break;
  231.     case ST_ROOT:
  232.     case ST_USERDIR:
  233.     case ST_LINKDIR:
  234.       break;
  235.     case ST_SOFTLINK:
  236.       if ((a->fib_Protection & FIBF_SCRIPT) ||
  237.       (~a->fib_Protection & FIBF_EXECUTE))
  238.     a->fib_DirEntryType = HI_SOFTLINK_EXE;
  239.       else
  240.     a->fib_DirEntryType = HI_SOFTLINK;
  241.       break;
  242.     default:
  243.       if (a->fib_DirEntryType >= 0)
  244.     a->fib_DirEntryType = HI_DIR_DEFAULT;
  245.       else
  246.     a->fib_DirEntryType = HI_FILE_DEFAULT;
  247.       break;
  248.     }
  249. }
  250.  
  251. static char *stpcpy (char *a, char *b)
  252. {
  253.   while (*a++ = *b++)
  254.     ;
  255.   return --a;
  256. }
  257.  
  258. static int CompareExtensions (char *namea, char *nameb)
  259. {
  260.   char *taila = strrchr (namea, '.');
  261.   char *tailb = strrchr (nameb, '.');
  262.  
  263.   if (taila)
  264.     return tailb ? stricmp (taila, tailb) : 1;
  265.   else
  266.     return tailb ? -1 : 0;
  267. }
  268.  
  269. static int CmpDateBounds (struct DateStamp *tdate)
  270. {
  271.   if ((SHOWNEWERTHAN) && (CompareDates (&thenewdate, tdate) <= 0))
  272.     return 0;
  273.   if ((SHOWOLDERTHAN) && (CompareDates (tdate, &theolddate) <= 0))
  274.     return 0;
  275.   return 1;
  276. }
  277.  
  278. static int CompFibs (enum sortkey keytype, struct FileInfoBlock *a, struct FileInfoBlock *b)
  279. {
  280.   int rc;
  281.  
  282.   if (((a->fib_DirEntryType < 0) && (b->fib_DirEntryType > 0)) ||
  283.       ((a->fib_DirEntryType > 0) && (b->fib_DirEntryType < 0)))
  284.     if (!(MIXFILESDIRS))
  285.       return (FILESFIRST) ?
  286.     (a->fib_DirEntryType < 0) :
  287.     (a->fib_DirEntryType > 0);
  288.  
  289.   switch (keytype)
  290.     {
  291.     case sort_size:
  292.       rc = b->fib_Size - a->fib_Size;
  293.       goto like;
  294.     case sort_date:
  295.       rc = CompareDates (&b->fib_Date, &a->fib_Date);
  296.       goto like;
  297.     case sort_extension:
  298.       rc = CompareExtensions (b->fib_FileName, a->fib_FileName);
  299.     like:
  300.       if (rc > 0)
  301.     return !REVFLAG;
  302.       if (rc < 0)
  303.     return REVFLAG;
  304.     case sort_alpha:
  305.       if (stricmp (b->fib_FileName, a->fib_FileName) > 0)
  306.     return !REVFLAG;
  307.       else
  308.     return REVFLAG;
  309.     case sort_none:
  310.       return REVFLAG;
  311.     default:
  312.       return REVFLAG;
  313.     }
  314. }
  315.  
  316. static void WriteErrorString (BYTE *tstring, ...)
  317. {
  318.   struct Process *procp = (struct Process *) FindTask (0L);
  319.   BPTR StdErr = procp->pr_CES;
  320.  
  321.   VFPrintf (StdErr ? StdErr : Output (), tstring, (LONG *) & tstring + 1);
  322. }
  323.  
  324. static void TestBreak (void)
  325. {
  326.   if (CheckSignal (SIGBREAKF_CTRL_C))
  327.     {
  328.       PutStr ("*** Break: ls\n");
  329.       BREAKFLAG = 1;
  330.     }
  331. }
  332.  
  333. static void DateStr (BYTE *s, struct DateStamp *ds)
  334. {
  335.   char StrDate[LEN_DATSTRING], StrTime[LEN_DATSTRING];
  336.   struct DateTime dt;
  337.   struct DateStamp cds;
  338.  
  339.   dt.dat_Stamp = *ds;
  340.   dt.dat_Format = FORMAT_DOS;
  341.   dt.dat_Flags = 0;
  342.   dt.dat_StrDay = NULL;
  343.   dt.dat_StrDate = StrDate;
  344.   dt.dat_StrTime = StrTime;
  345.  
  346.   if (!DateToStr (&dt))
  347.     {
  348.       strcpy (s, "Bad  1  Date");
  349.       return;
  350.     }
  351.  
  352.   s[0] = StrDate[3];
  353.   s[1] = StrDate[4];
  354.   s[2] = StrDate[5];
  355.   s[3] = ' ';
  356.   s[4] = (StrDate[0] == '0') ? ' ' : StrDate[0];
  357.   s[5] = StrDate[1];
  358.   s[6] = ' ';
  359.   DateStamp (&cds);
  360.   if (((cds.ds_Days - ds->ds_Days) > 180) || ((cds.ds_Days - ds->ds_Days) < 0))
  361.     {
  362.       s[7] = ' ';
  363.       if (ds->ds_Days < 8035)
  364.     {
  365.       s[8] = '1';
  366.       s[9] = '9';
  367.     }
  368.       else
  369.     {
  370.       s[8] = '2';
  371.       s[9] = '0';
  372.     }
  373.       s[10] = StrDate[7];
  374.       s[11] = StrDate[8];
  375.     }
  376.   else
  377.     {
  378.       s[7] = StrTime[0];
  379.       s[8] = StrTime[1];
  380.       s[9] = ':';
  381.       s[10] = StrTime[3];
  382.       s[11] = StrTime[4];
  383.     }
  384.   s[12] = '\0';
  385. }
  386.  
  387. static int StrDate (BYTE *s, struct DateStamp *ds)
  388. {
  389.   char StrDate[LEN_DATSTRING], StrTime[LEN_DATSTRING];
  390.   struct DateTime dt;
  391.  
  392.   switch (strlen (s))
  393.     {
  394.     case 17:
  395.       /* Standard "MMM DD HH:MM YYYY" 'ls' format */
  396.       if ((StrDate[0] = s[4]) == ' ')
  397.     StrDate[0] = '0';
  398.       StrDate[1] = s[5];
  399.       StrDate[2] = '-';
  400.       StrDate[3] = s[0];
  401.       StrDate[4] = s[1];
  402.       StrDate[5] = s[2];
  403.       StrDate[6] = '-';
  404.       StrDate[7] = s[15];
  405.       StrDate[8] = s[16];
  406.       StrDate[9] = '\0';
  407.  
  408.       StrTime[0] = s[7];
  409.       StrTime[1] = s[8];
  410.       StrTime[2] = s[9];
  411.       StrTime[3] = s[10];
  412.       StrTime[4] = s[11];
  413.       StrTime[5] = ':';
  414.       StrTime[6] = '0';
  415.       StrTime[7] = '0';
  416.       StrTime[8] = '\0';
  417.       break;
  418.     case 18:
  419.       /* Standard "DD-MMM-YY HH:MM:SS" AmigaDOS 'date' and internal format */
  420.       StrDate[0] = s[0];
  421.       StrDate[1] = s[1];
  422.       StrDate[2] = s[2];
  423.       StrDate[3] = s[3];
  424.       StrDate[4] = s[4];
  425.       StrDate[5] = s[5];
  426.       StrDate[6] = s[6];
  427.       StrDate[7] = s[7];
  428.       StrDate[8] = s[8];
  429.       StrDate[9] = '\0';
  430.  
  431.       StrTime[0] = s[10];
  432.       StrTime[1] = s[11];
  433.       StrTime[2] = s[12];
  434.       StrTime[3] = s[13];
  435.       StrTime[4] = s[14];
  436.       StrTime[5] = s[15];
  437.       StrTime[6] = s[16];
  438.       StrTime[7] = s[17];
  439.       StrTime[8] = '\0';
  440.       break;
  441.     default:
  442.       return 1;
  443.     }
  444.  
  445.   dt.dat_Format = FORMAT_DOS;
  446.   dt.dat_Flags = 0;
  447.   dt.dat_StrDay = NULL;
  448.   dt.dat_StrDate = StrDate;
  449.   dt.dat_StrTime = StrTime;
  450.  
  451.   if (!StrToDate (&dt))
  452.     return 1;
  453.  
  454.   *ds = dt.dat_Stamp;
  455.   return 0;
  456. }
  457.  
  458. static void CleanUp (BYTE *exit_msg, LONG exit_status, LONG result2)
  459. {
  460.   if (CurFLock)
  461.     UnLock ((BPTR) CurFLock);
  462.  
  463.   if (*exit_msg)
  464.     WriteErrorString (ErrFmtStr, exit_msg);
  465.  
  466.   PutStr (highlight_cursor.on);
  467.   Flush (Output ());
  468.  
  469.   SetIoErr (result2);
  470.  
  471.   /* this cleanup routine assumes that the C library will free all
  472.      malloc'd memory.  This is true with SAS/C with standard exit(). */
  473.   exit ((int) exit_status);
  474. }
  475.  
  476. static void AddAntiPattern (BYTE *pattern)
  477. {
  478.   struct AnAntiPattern *AnAntiPattern = malloc (sizeof (struct AnAntiPattern));
  479.   int len = strlen (pattern) * 2 + 2;
  480.  
  481.   if (!AnAntiPattern)
  482.     CleanUp (NoRAMMsg, RETURN_FAIL, ERROR_NO_FREE_STORE);
  483.   AnAntiPattern->next = TheAntiPatterns;
  484.   AnAntiPattern->pattern = pattern;
  485.   if (!(AnAntiPattern->parsedpattern = malloc (len)))
  486.     CleanUp (NoRAMMsg, RETURN_FAIL, ERROR_NO_FREE_STORE);
  487.   if (ParsePatternNoCase (pattern, AnAntiPattern->parsedpattern, len) == -1)
  488.     CleanUp (ParseErrorMsg, RETURN_FAIL, ERROR_NO_MORE_ENTRIES);
  489.   TheAntiPatterns = AnAntiPattern;
  490. }
  491.  
  492. static void Usage (int full)
  493. {
  494.   PutStr (shortusage);
  495.   if (full)
  496.     PutStr (fullusage);
  497.   CleanUp ("", full ? RETURN_WARN : RETURN_FAIL, ERROR_BAD_TEMPLATE);
  498. }
  499.  
  500. #ifdef __SASC
  501. void kput1 (void)
  502. {
  503.   void __builtin_emit (int);
  504.  
  505.   __builtin_emit (0x16C0);    /* MOVE.B  D0,(A3)+ */
  506. }
  507.  
  508. #else
  509. #error Fix kput1() for your compiler.
  510. #endif
  511.  
  512. static void GetWinBounds (LONG *width, LONG *height, LONG have_console)
  513. {
  514.   BPTR output = Output ();
  515.   char buffer[16];
  516.  
  517.   if (have_console && output && IsInteractive (output) &&
  518.       SetMode (output, 1) && (Write (output, "\x9b" "0 q", 4) == 4) &&
  519.       WaitForChar (output, 10000L) &&
  520.       (Read (output, buffer, sizeof (buffer)) > 9) &&
  521.       (buffer[0] == '\x9b'))
  522.     {
  523.       int y = StrToLong (buffer + 5, height);
  524.       int x = StrToLong (buffer + 5 + y + 1, width);
  525.       if ((x == -1) || (y == -1))
  526.     goto TRY_ENV;
  527.     }
  528.   else
  529.     {
  530.     TRY_ENV:
  531.       if ((GetVar ("console_width", buffer, sizeof (buffer), LV_VAR | GVF_LOCAL_ONLY) == -1) ||
  532.       (StrToLong (buffer, width) == -1))
  533.     *width = 77;
  534.       if ((GetVar ("console_height", buffer, sizeof (buffer), LV_VAR | GVF_LOCAL_ONLY) == -1) ||
  535.       (StrToLong (buffer, height) == -1))
  536.     *height = 23;
  537.     }
  538.   if (have_console && output && IsInteractive (output))
  539.     SetMode (output, 0);
  540. }
  541.  
  542. static int FillFibEntry (struct MinList *headfib, struct FileInfoBlock *fibp)
  543. {
  544.   struct FibEntry *afib, *tfibp = malloc (sizeof (struct FibEntry));
  545.  
  546.   if (!tfibp)
  547.     {
  548.       BREAKFLAG = 1;
  549.       return 0;
  550.     }
  551.  
  552.   tfibp->fe_Fib = *fibp;
  553.  
  554.   RemapDirEntryType (&(tfibp->fe_Fib));
  555.  
  556.   for (afib = (struct FibEntry *) headfib->mlh_Head; afib->fe_Node.mln_Succ;
  557.        afib = (struct FibEntry *) afib->fe_Node.mln_Succ)
  558.     if (CompFibs (sortkey, &(tfibp->fe_Fib), &(afib->fe_Fib)))
  559.       break;
  560.   Insert ((struct List *) headfib, (struct Node *) tfibp, (struct Node *) afib->fe_Node.mln_Pred);
  561.  
  562.   return 1;
  563. }
  564.  
  565. static struct FibEntry *ModNextFib (struct FibEntry *tfibp, LONG rows)
  566. {
  567.   LONG i;
  568.  
  569.   for (i = 0; i < rows && tfibp->fe_Node.mln_Succ; i++)
  570.     tfibp = (struct FibEntry *) tfibp->fe_Node.mln_Succ;
  571.   return (tfibp);
  572. }
  573.  
  574. static void SListDir (struct MinList *fibheadp)
  575. {
  576.   LONG avglen;
  577.   LONG colcnt;
  578.   LONG dfcount;
  579.   LONG i, j, wlen;
  580.   LONG maxcol;
  581.   LONG maxrow;
  582.   LONG rowcnt;
  583.   LONG tlen;
  584.   LONG totlen;
  585.   struct FibEntry *hfibp, *tfibp;
  586.  
  587.   for (totlen = dfcount = 0, hfibp = (struct FibEntry *) fibheadp->mlh_Head;
  588.        hfibp->fe_Node.mln_Succ;
  589.        hfibp = (struct FibEntry *) hfibp->fe_Node.mln_Succ)
  590.     {
  591.       totlen += strlen (hfibp->fe_Fib.fib_FileName) +
  592.     highlight_tab[hfibp->fe_Fib.fib_DirEntryType].printable_len;
  593.       dfcount++;
  594.     }
  595.  
  596.   avglen = totlen / dfcount;
  597.   if (totlen % dfcount)
  598.     avglen++;
  599.  
  600.   if ((CurWinCols) <= maxnamlen)
  601.     maxcol = 1;
  602.   else
  603.     for (maxcol = 0, colcnt = CurWinCols; colcnt >= avglen; maxcol++)
  604.       colcnt -= avglen + 2;
  605.  
  606.   for (;;)
  607.     {
  608.       memset (padtab, 0, PADTABSIZE);
  609.  
  610.       if (!(ACROSSLIST))
  611.     {
  612.       maxrow = dfcount / maxcol;
  613.       if (dfcount % maxcol)
  614.         maxrow++;
  615.       for (rowcnt = 0, hfibp = (struct FibEntry *) fibheadp->mlh_Head;
  616.            rowcnt < maxrow && hfibp->fe_Node.mln_Succ;
  617.            rowcnt++, hfibp = (struct FibEntry *) hfibp->fe_Node.mln_Succ)
  618.         {
  619.           for (colcnt = 0, tfibp = hfibp;
  620.            colcnt < maxcol && tfibp->fe_Node.mln_Succ;
  621.            colcnt++, tfibp = ModNextFib (tfibp, maxrow))
  622.         {
  623.           tlen = strlen (tfibp->fe_Fib.fib_FileName) +
  624.             highlight_tab[tfibp->fe_Fib.fib_DirEntryType].printable_len;
  625.           if (tlen > padtab[colcnt])
  626.             padtab[colcnt] = tlen;
  627.         }
  628.  
  629.           if (!rowcnt)
  630.         {
  631.           maxcol = colcnt;
  632.           maxrow = dfcount / maxcol;
  633.           if (dfcount % maxcol)
  634.             maxrow++;
  635.         }
  636.         }
  637.     }
  638.       else
  639.     {
  640.       maxrow = dfcount / maxcol;
  641.       if (dfcount % maxcol)
  642.         maxrow++;
  643.  
  644.       for (rowcnt = 0, tfibp = (struct FibEntry *) fibheadp->mlh_Head;
  645.            rowcnt < maxrow && tfibp->fe_Node.mln_Succ;
  646.            rowcnt++)
  647.         {
  648.           for (colcnt = 0;
  649.            colcnt < maxcol && tfibp->fe_Node.mln_Succ;
  650.           colcnt++, tfibp = (struct FibEntry *) tfibp->fe_Node.mln_Succ)
  651.         {
  652.           tlen = strlen (tfibp->fe_Fib.fib_FileName) +
  653.             highlight_tab[tfibp->fe_Fib.fib_DirEntryType].printable_len;
  654.           if (tlen > padtab[colcnt])
  655.             padtab[colcnt] = tlen;
  656.         }
  657.         }
  658.     }
  659.  
  660.       for (colcnt = totlen = 0; (colcnt + 1) < maxcol; colcnt++)
  661.     totlen += (LONG) padtab[colcnt] + 2;
  662.       totlen += (LONG) padtab[colcnt];
  663.  
  664.       if (maxcol > 1 && totlen > CurWinCols)
  665.     maxcol--;
  666.       else
  667.     break;
  668.     }
  669.  
  670.   if (!(ACROSSLIST))
  671.     {
  672.       for (rowcnt = 0, hfibp = (struct FibEntry *) fibheadp->mlh_Head;
  673.        !(BREAKFLAG) && rowcnt < maxrow && hfibp->fe_Node.mln_Succ;
  674.        TestBreak (), rowcnt++, hfibp = (struct FibEntry *) hfibp->fe_Node.mln_Succ)
  675.     {
  676.       for (colcnt = 0, tfibp = hfibp;
  677.            colcnt < maxcol && tfibp->fe_Node.mln_Succ;
  678.            colcnt++)
  679.         {
  680.           workstr[0] = 0;
  681.           wlen = strlen (tfibp->fe_Fib.fib_FileName) +
  682.         highlight_tab[tfibp->fe_Fib.fib_DirEntryType].printable_len;
  683.           strcat (workstr, highlight_tab[tfibp->fe_Fib.fib_DirEntryType].on);
  684.           strcat (workstr, tfibp->fe_Fib.fib_FileName);
  685.           strcat (workstr, highlight_tab[tfibp->fe_Fib.fib_DirEntryType].off);
  686.  
  687.           tfibp = ModNextFib (tfibp, maxrow);
  688.  
  689.           if ((colcnt + 1) < maxcol && tfibp->fe_Node.mln_Succ)
  690.         {
  691.           for (i = (LONG) padtab[colcnt] + 1, j = strlen (workstr); i >= wlen; i--, j++)
  692.             workstr[j] = ' ';
  693.           workstr[j] = 0;
  694.         }
  695.           PutStr (workstr);
  696.         }
  697.  
  698.       PutStr ("\n");
  699.     }
  700.     }
  701.   else
  702.     {
  703.       for (rowcnt = 0, tfibp = (struct FibEntry *) fibheadp->mlh_Head;
  704.        !(BREAKFLAG) && rowcnt < maxrow && tfibp->fe_Node.mln_Succ;
  705.        TestBreak (), rowcnt++)
  706.     {
  707.       for (colcnt = 0;
  708.            colcnt < maxcol && tfibp->fe_Node.mln_Succ;
  709.            colcnt++)
  710.         {
  711.           workstr[0] = 0;
  712.           wlen = strlen (tfibp->fe_Fib.fib_FileName) +
  713.         highlight_tab[tfibp->fe_Fib.fib_DirEntryType].printable_len;
  714.           strcat (workstr, highlight_tab[tfibp->fe_Fib.fib_DirEntryType].on);
  715.           strcat (workstr, tfibp->fe_Fib.fib_FileName);
  716.           strcat (workstr, highlight_tab[tfibp->fe_Fib.fib_DirEntryType].off);
  717.  
  718.           tfibp = (struct FibEntry *) tfibp->fe_Node.mln_Succ;
  719.  
  720.           if ((colcnt + 1) < maxcol && tfibp->fe_Node.mln_Succ)
  721.         {
  722.           for (i = (LONG) padtab[colcnt] + 1, j = strlen (workstr); i >= wlen; i--, j++)
  723.             workstr[j] = ' ';
  724.           workstr[j] = 0;
  725.         }
  726.           PutStr (workstr);
  727.         }
  728.  
  729.       PutStr ("\n");
  730.     }
  731.     }
  732. }
  733.  
  734. static void LListEntry (struct FileInfoBlock *fib)
  735. {
  736.   BYTE *cp1, *cp2, *reps;
  737.   BYTE *pathend, *thenamestr;
  738.   LONG i, spccnt;
  739.  
  740.   theprotstr[0] = "sL-L-ll-dd-DS"[fib->fib_DirEntryType + 7];
  741.   theprotstr[1] = (fib->fib_Protection & 128 /*FIBF_HIDDEN*/ )? 'h' : '-';
  742.   theprotstr[2] = (fib->fib_Protection & FIBF_SCRIPT) ? 's' : '-';
  743.   theprotstr[3] = (fib->fib_Protection & FIBF_PURE) ? 'p' : '-';
  744.   theprotstr[4] = (fib->fib_Protection & FIBF_ARCHIVE) ? 'a' : '-';
  745.   theprotstr[5] = (fib->fib_Protection & FIBF_READ) ? '-' : 'r';
  746.   theprotstr[6] = (fib->fib_Protection & FIBF_WRITE) ? '-' : 'w';
  747.   theprotstr[7] = (fib->fib_Protection & FIBF_EXECUTE) ? '-' : 'e';
  748.   theprotstr[8] = (fib->fib_Protection & FIBF_DELETE) ? '-' : 'd';
  749.   theprotstr[9] = ' ';
  750.   theprotstr[10] = fib->fib_Comment[0] ? 'c' : ' ';
  751.  
  752.   DateStr (thedatestr, &(fib->fib_Date));
  753.  
  754.   RawDoFmt (LongFmtStr, &fib->fib_NumBlocks, kput1, theblksstr);
  755.   RawDoFmt (LongFmtStr, &fib->fib_Size, kput1, thesizestr);
  756.  
  757.   i = strlen (curpath);
  758.   pathend = curpath + i;
  759.   if (i > 1 && *(curpath + i - 1) != ':')
  760.     {
  761.       *(curpath + i) = '/';
  762.       i++;
  763.       *(curpath + i) = 0;
  764.     }
  765.   thenamestr = curpath + strlen (curpath);
  766.   cp2 = thenamestr;
  767.   if ((fib->fib_DirEntryType == HI_SOFTLINK) ||
  768.       (fib->fib_DirEntryType == HI_SOFTLINK_EXE))
  769.     {
  770.       struct DevProc *devproc = GetDeviceProc (curpath, NULL);
  771.       UBYTE buffer[512];
  772.       BOOL success;
  773.  
  774.       cp2 = stpcpy (cp2, fib->fib_FileName);
  775.  
  776.       if (devproc)
  777.     {
  778.       cp2 = stpcpy (cp2, " -> ");
  779.       success = ReadLink (devproc->dvp_Port, devproc->dvp_Lock,
  780.                   fib->fib_FileName, buffer, 512);
  781.       if (success)
  782.         strcpy (cp2, buffer);
  783.       else
  784.         strcpy (cp2, "!ERROR READING LINK!");
  785.       FreeDeviceProc (devproc);
  786.     }
  787.       else
  788.     strcpy (cp2, "!ERROR LOCKING LINK!");
  789.     }
  790.   else if ((fib->fib_DirEntryType == HI_LINKDIR) ||
  791.        (fib->fib_DirEntryType == HI_LINKFILE) ||
  792.        (fib->fib_DirEntryType == HI_LINKFILE_EXE))
  793.     {
  794.       UBYTE buffer[512];
  795.       BPTR plock, lock;
  796.  
  797.       plock = Lock (curpath, ACCESS_READ);
  798.       plock = CurrentDir (plock);
  799.       cp2 = stpcpy (cp2, fib->fib_FileName);
  800.       cp2 = stpcpy (cp2, " -> ");
  801.       lock = Lock (fib->fib_FileName, ACCESS_READ);
  802.       if (lock)
  803.     {
  804.       NameFromLock (lock, buffer, 512);
  805.       UnLock (lock);
  806.       strcpy (cp2, buffer);
  807.     }
  808.       else
  809.     strcpy (cp2, "!ERROR LOCKING LINK!");
  810.       plock = CurrentDir (plock);
  811.       UnLock (plock);
  812.     }
  813.   else
  814.     strcpy (cp2, fib->fib_FileName);
  815.  
  816.   for (cp1 = workstr, cp2 = thefmtstr; *cp2; cp2++)
  817.     {
  818.       if (*cp2 != '%' && *cp2 != '\\')
  819.     *cp1++ = *cp2;
  820.       else
  821.     {
  822.       if (*cp2++ == '%')
  823.         {
  824.           if ((*cp2 >= '0') && (*cp2 <= '9'))
  825.         {
  826.           cp2 += StrToLong (cp2, &spccnt);
  827.           if (spccnt > 99)
  828.             spccnt = 99;
  829.         }
  830.           else
  831.         spccnt = 0;
  832.  
  833.           switch (*cp2)
  834.         {
  835.         case 'p':
  836.           reps = theprotstr;
  837.           break;
  838.         case 'd':
  839.           reps = thedatestr;
  840.           break;
  841.         case 'b':
  842.           reps = theblksstr;
  843.           break;
  844.         case 's':
  845.           reps = thesizestr;
  846.           break;
  847.         case 'C':
  848.           if ((NOTEFLAG) && fib->fib_Comment[0])
  849.             {
  850.               cp1 = stpcpy (cp1, highlight_tab[HI_COMMENT].on);
  851.               cp1 = stpcpy (cp1, fib->fib_Comment);
  852.               cp1 = stpcpy (cp1, highlight_tab[HI_COMMENT].off);
  853.             }
  854.           reps = "";
  855.           break;
  856.         case 'w':
  857.           if ((NOTEFLAG) && fib->fib_Comment[0])
  858.             reps = wflag;
  859.           else
  860.             reps = "";
  861.           break;
  862.         case 'h':
  863.           RawDoFmt ("0x%lx", &fib->fib_Size, kput1, thehexsizestr);
  864.           reps = thehexsizestr;
  865.           break;
  866.         case 'S':
  867.           {
  868.             long xfer_rate;
  869.             int time[2];
  870.  
  871.             cp2++;
  872.             if ((*cp2 >= '0') && (*cp2 <= '9'))
  873.               cp2 += StrToLong (cp2, &xfer_rate);
  874.             else
  875.               xfer_rate = 0;
  876.             cp2--;
  877.             if (!xfer_rate)
  878.               xfer_rate = 230;
  879.  
  880.             time[0] = fib->fib_Size / xfer_rate / 60;
  881.             time[1] = fib->fib_Size / xfer_rate % 60;
  882.             RawDoFmt ("%ld:%02ld", time, kput1, thetimesizestr);
  883.             reps = thetimesizestr;
  884.             break;
  885.           }
  886.         case 'n':
  887.           if (FULLPATHNAMES)
  888.             reps = curpath;
  889.           else
  890.             reps = thenamestr;
  891.           break;
  892.         case '%':
  893.           *cp1++ = '%';
  894.           *cp1++ = 0;
  895.         default:
  896.           reps = "";
  897.           break;
  898.         }
  899.           for (i = strlen (reps); i < spccnt; i++)
  900.         *cp1++ = ' ';
  901.           cp1 = stpcpy (cp1, reps);
  902.         }
  903.       else
  904.         {
  905.           switch (*cp2)
  906.         {
  907.         case 'n':
  908.           *cp1++ = '\n';
  909.           break;
  910.         case '"':
  911.           *cp1++ = '"';
  912.           break;
  913.         case 't':
  914.           *cp1++ = '\t';
  915.           break;
  916.         case '\\':
  917.           *cp1++ = '\\';
  918.           break;
  919.         default:
  920.           break;
  921.         }
  922.         }
  923.     }
  924.     }
  925.   *cp1 = 0;
  926.   PutStr (workstr);
  927.  
  928.   *pathend = 0;
  929. }
  930.  
  931. static void LListDir (struct MinList *fibheadp)
  932. {
  933.   struct FibEntry *tfibp;
  934.  
  935.   totblocks = totbytes = 0;
  936.   for (tfibp = (struct FibEntry *) fibheadp->mlh_Head;
  937.        tfibp->fe_Node.mln_Succ;
  938.        tfibp = (struct FibEntry *) tfibp->fe_Node.mln_Succ)
  939.     {
  940.       TestBreak ();
  941.       if (BREAKFLAG)
  942.     return;
  943.       LListEntry (&(tfibp->fe_Fib));
  944.       if (tfibp->fe_Fib.fib_DirEntryType < 0)
  945.     {
  946.       totblocks += tfibp->fe_Fib.fib_NumBlocks;
  947.       totbytes += tfibp->fe_Fib.fib_Size;
  948.     }
  949.     }
  950.  
  951.   if (!(BREAKFLAG || NOHEADERS))
  952.     VPrintf (totalfmtstr, &dircount);    /* note that parameters are in order in memory */
  953. }
  954.  
  955. static void FreeAllFibs (struct MinList *fibheadp)
  956. {
  957.   struct FibEntry *tfibp;
  958.  
  959.   if (fibheadp)
  960.     {
  961.       while (fibheadp->mlh_Head->mln_Succ)
  962.     {
  963.       tfibp = (struct FibEntry *) RemTail ((struct List *) fibheadp);
  964.       free (tfibp);
  965.     }
  966.     }
  967. }
  968.  
  969. static struct MinList *GetDir (struct FileLock *lockp, struct FileInfoBlock *fibp)
  970. {
  971.   struct AnAntiPattern *theanti;
  972.   LONG matchstat;
  973.   LONG nextstat;
  974.   LONG tempnamlen;
  975.   struct MinList *fibhead;
  976.   struct MinList *dirhead;
  977.  
  978.   maxnamlen = dircount = filecount = 0L;
  979.  
  980.   if (!(fibhead = malloc (sizeof (struct MinList))))
  981.       return (0L);
  982.   NewList ((struct List *) fibhead);
  983.  
  984.   if (!(dirhead = malloc (sizeof (struct MinList))))
  985.       goto BADALLOC;
  986.   NewList ((struct List *) dirhead);
  987.  
  988.   do
  989.     {
  990.       TestBreak ();
  991.       if (BREAKFLAG)
  992.     goto GOODRET;
  993.  
  994.       if (nextstat = ExNext ((BPTR) lockp, fibp))
  995.     {
  996.       if (CmpDateBounds (&fibp->fib_Date))
  997.         {
  998.           if ((fibp->fib_DirEntryType >= 0) &&
  999.           (!(fibp->fib_DirEntryType == ST_SOFTLINK)))
  1000.         matchstat = MatchPatternNoCase (theDirPatParsed, fibp->fib_FileName);
  1001.           else
  1002.         matchstat = MatchPatternNoCase (theFilePatParsed, fibp->fib_FileName);
  1003.  
  1004.           if (!(SHOWHIDDEN) && matchstat)
  1005.         matchstat = !(fibp->fib_Protection & 128 /*FIBF_HIDDEN*/ );
  1006.           theanti = TheAntiPatterns;
  1007.           while (matchstat && theanti)
  1008.         {
  1009.           matchstat = !MatchPatternNoCase (theanti->parsedpattern, fibp->fib_FileName);
  1010.           theanti = theanti->next;
  1011.         }
  1012.  
  1013.           if (!matchstat)
  1014.         continue;
  1015.  
  1016.           if ((fibp->fib_DirEntryType >= 0) &&
  1017.           (!(fibp->fib_DirEntryType == ST_SOFTLINK)))
  1018.         {
  1019.           if (!FillFibEntry (dirhead, fibp))
  1020.             goto BADALLOC;
  1021.           if (HIDEDIRS)
  1022.             continue;
  1023.           dircount++;
  1024.           gdircount++;
  1025.         }
  1026.           else
  1027.         {
  1028.           filecount++;
  1029.           gfilecount++;
  1030.           gtotblocks += fibp->fib_NumBlocks;
  1031.           gtotbytes += fibp->fib_Size;
  1032.         }
  1033.  
  1034.           tempnamlen = strlen (fibp->fib_FileName);
  1035.           if (tempnamlen > maxnamlen)
  1036.         maxnamlen = tempnamlen;
  1037.  
  1038.           if (!FillFibEntry (fibhead, fibp))
  1039.         goto BADALLOC;
  1040.         }
  1041.     }
  1042.     }
  1043.   while (nextstat);
  1044.  
  1045.   if (IoErr ()!= ERROR_NO_MORE_ENTRIES)
  1046.     CleanUp (ExNextMsg, RETURN_FAIL, IoErr ());
  1047.  
  1048.   if (dircount + filecount)
  1049.     {
  1050.       if (!(LONGLIST))
  1051.     SListDir (fibhead);
  1052.       else
  1053.     LListDir (fibhead);
  1054.     }
  1055.  
  1056. GOODRET:
  1057.   FreeAllFibs (fibhead);
  1058.   return (dirhead);
  1059.  
  1060. BADALLOC:
  1061.   FreeAllFibs (fibhead);
  1062.   FreeAllFibs (dirhead);
  1063.   return (0L);
  1064. }
  1065.  
  1066. static void DirIt (struct FileLock *curlock, BYTE *dirname)
  1067. {
  1068.   BYTE *subdir;
  1069.   size_t dnamlen;
  1070.   struct FibEntry *tfibp;
  1071.   struct FileLock *sublock;
  1072.   struct MinList *fibheadp;
  1073.   struct FileInfoBlock __aligned GFibp;
  1074.  
  1075.   if (!Examine ((BPTR) curlock, &GFibp))
  1076.     {
  1077.       WriteErrorString (NoExamFmtStr, IoErr ());
  1078.       return;
  1079.     }
  1080.  
  1081.   if (dirname[0] && (LISTALL) && !(NOHEADERS))
  1082.     {
  1083.       PutStr (highlight_tab[HI_LABEL].on);
  1084.       PutStr (dirname);
  1085.       PutStr ("\n");
  1086.       PutStr (highlight_tab[HI_LABEL].off);
  1087.     }
  1088.  
  1089.   if ((GFibp.fib_DirEntryType < 0) ||
  1090.       (GFibp.fib_DirEntryType == ST_SOFTLINK) || (ABOUTDIRS))
  1091.     {
  1092.       RemapDirEntryType (&GFibp);
  1093.  
  1094.       *(PathPart (thePath)) = '\0';
  1095.       LListEntry (&GFibp);
  1096.       filecount++;
  1097.       gfilecount++;
  1098.       gtotblocks += GFibp.fib_NumBlocks;
  1099.       gtotbytes += GFibp.fib_Size;
  1100.     }
  1101.   else
  1102.     {
  1103.       if (fibheadp = GetDir (curlock, &GFibp))
  1104.     {
  1105.       if (LISTALL)
  1106.         {
  1107.           for (tfibp = (struct FibEntry *) fibheadp->mlh_Head;
  1108.            tfibp->fe_Node.mln_Succ;
  1109.            tfibp = (struct FibEntry *) tfibp->fe_Node.mln_Succ)
  1110.         {
  1111.           TestBreak ();
  1112.           if (BREAKFLAG)
  1113.             break;
  1114.  
  1115.           dnamlen = (strlen (dirname) + strlen (tfibp->fe_Fib.fib_FileName) + 36);
  1116.           if (subdir = malloc (dnamlen))
  1117.             {
  1118.               if (dirname[0])
  1119.             {
  1120.               strcpy (subdir, dirname);
  1121.               dnamlen = strlen (dirname) - 1;
  1122.               if (dirname[dnamlen] != ':' && dirname[dnamlen] != '/')
  1123.                 strcat (subdir, "/");
  1124.             }
  1125.               strcat (subdir, tfibp->fe_Fib.fib_FileName);
  1126.  
  1127.               if (sublock = (struct FileLock *) Lock (subdir, (LONG) ACCESS_READ))
  1128.             {
  1129.               if (!(NOHEADERS))
  1130.                 PutStr ("\n");
  1131.  
  1132.               curpath = subdir;
  1133.  
  1134.               DirIt (sublock, subdir);
  1135.  
  1136.               UnLock ((BPTR) sublock);
  1137.             }
  1138.               free (subdir);
  1139.             }
  1140.         }
  1141.         }
  1142.       FreeAllFibs (fibheadp);
  1143.     }
  1144.     }
  1145. }
  1146.  
  1147. static void GetCLIArgs (BYTE *line, LONG *argc, BYTE **argv)
  1148. {
  1149.   BYTE **pargv, *qarg;
  1150.  
  1151.   *argc = 0;
  1152.   while (*argc < MAXARG)
  1153.     {
  1154.       while (*line == ' ' || *line == '\t' || *line == '\n')
  1155.     line++;
  1156.       if (!(*line))
  1157.     break;
  1158.       pargv = &argv[*argc];
  1159.       *argc += 1;
  1160.  
  1161.       if (*line == '"')
  1162.     {
  1163.       qarg = line;
  1164.       line += 1;
  1165.       *pargv = line;
  1166.       while (*line && *line != '"')
  1167.         if (*line == '\\' && *(line + 1))
  1168.           line += 2;
  1169.         else
  1170.           line++;
  1171.       if (!(*line))
  1172.         {
  1173.           *pargv = qarg;
  1174.           break;
  1175.         }
  1176.       else
  1177.         *line++ = 0;
  1178.     }
  1179.       else
  1180.     {
  1181.       *pargv = line;
  1182.       while (*line && !(*line == ' ' || *line == '\t' || *line == '\n'))
  1183.         line++;
  1184.       if (*line)
  1185.         *line++ = 0;
  1186.       else
  1187.         break;
  1188.     }
  1189.     }
  1190. }
  1191.  
  1192. static LONG ParseCmdOptions (LONG ncnt, LONG argc, BYTE **argv)
  1193. {
  1194.   LONG i, cnt, len;
  1195.  
  1196.   cnt = ncnt;
  1197.   ncnt += 1;
  1198.  
  1199.   for (i = 1, len = strlen (argv[cnt]); i < len; i++)
  1200.     {
  1201.       switch (argv[cnt][i])
  1202.     {
  1203.     case '?':
  1204.     case 'h':
  1205.       Usage (1);
  1206.       break;
  1207.     case 'D':
  1208.       FILESFIRST = 1;
  1209.       break;
  1210.     case 'F':
  1211.       if (argc < (ncnt + 1))
  1212.         goto missing_arg_error;
  1213.       thefmtstr = argv[ncnt];
  1214.       ncnt += 1;
  1215.       LONGLIST = 1;
  1216.       break;
  1217.     case 'H':
  1218.       NOHEADERS = 1;
  1219.       break;
  1220.     case 'A':
  1221.       ACROSSLIST = 1;
  1222.       break;
  1223.     case 'M':
  1224.       MIXFILESDIRS = 1;
  1225.       break;
  1226.     case 'N':
  1227.       if (argc < (ncnt + 1))
  1228.         goto missing_arg_error;
  1229.       if (StrDate (argv[ncnt], &thenewdate))
  1230.         goto date_arg_error;
  1231.       ncnt += 1;
  1232.       SHOWNEWERTHAN = 1;
  1233.       break;
  1234.     case 'O':
  1235.       if (argc < (ncnt + 1))
  1236.         goto missing_arg_error;
  1237.       if (StrDate (argv[ncnt], &theolddate))
  1238.         goto date_arg_error;
  1239.       ncnt += 1;
  1240.       SHOWOLDERTHAN = 1;
  1241.       break;
  1242.     date_arg_error:
  1243.       WriteErrorString (NotGoodDateStr, argv[ncnt]);
  1244.       CleanUp ("", RETURN_FAIL, ERROR_BAD_TEMPLATE);
  1245.     case 'P':
  1246.       if ((thefmtstr == deffmtstr) && !(LONGLIST))
  1247.         thefmtstr = deffullstr;
  1248.       FULLPATHNAMES = 1;
  1249.       LONGLIST = 1;
  1250.       NOHEADERS = 1;
  1251.       break;
  1252.     case 'R':
  1253.       LISTALL = 1;
  1254.       break;
  1255.     case 'T':
  1256.       TOTALIZE = 1;
  1257.       break;
  1258.     case 'X':
  1259.       if (argc < (ncnt + 1))
  1260.         goto missing_arg_error;
  1261.       StrToLong (argv[ncnt], &CurWinCols);
  1262.       ncnt += 1;
  1263.       break;
  1264.     case 'w':
  1265.       if (argc < (ncnt + 1))
  1266.         goto missing_arg_error;
  1267.       wflag = argv[ncnt];
  1268.       ncnt += 1;
  1269.       break;
  1270.     case '1':
  1271.       CurWinCols = 1;
  1272.       break;
  1273.     case 'Y':
  1274.       if (argc < (ncnt + 1))
  1275.         goto missing_arg_error;
  1276.       ncnt += 1;
  1277.       break;
  1278.     case 'a':
  1279.       SHOWHIDDEN = 1;
  1280.       break;
  1281.     case 'c':
  1282.       LONGLIST = 1;
  1283.       NOTEFLAG = 1;
  1284.       break;
  1285.     case 'd':
  1286.       ABOUTDIRS = 1;
  1287.       break;
  1288.     case 'e':
  1289.       sortkey = sort_extension;
  1290.       break;
  1291.     case 'f':
  1292.       HIDEDIRS = 1;
  1293.       break;
  1294.     case 'k':
  1295.       CONSOLE = 0;
  1296.       break;
  1297.     case 'l':
  1298.       if ((thefmtstr == deffullstr) && (FULLPATHNAMES))
  1299.         thefmtstr = deffmtstr;
  1300.       LONGLIST = 1;
  1301.       break;
  1302.     case 'n':
  1303.       sortkey = sort_none;
  1304.       break;
  1305.     case 'r':
  1306.       REVFLAG = 1;
  1307.       break;
  1308.     case 's':
  1309.       sortkey = sort_size;
  1310.       break;
  1311.     case 't':
  1312.       sortkey = sort_date;
  1313.       break;
  1314.     case 'I':
  1315.       if (argc < (ncnt + 1))
  1316.         goto missing_arg_error;
  1317.       AddAntiPattern (argv[ncnt]);
  1318.       ncnt += 1;
  1319.       break;
  1320.     default:
  1321.       WriteErrorString (BadOptFmtStr, argv[cnt][i]);
  1322.       Usage (0);
  1323.     missing_arg_error:
  1324.       WriteErrorString (OptNeedsArgStr, argv[cnt][i]);
  1325.       Usage (1);
  1326.     }
  1327.     }
  1328.   return (ncnt);
  1329. }
  1330.  
  1331. void __stdargs __main (char *line)
  1332. {
  1333.   BYTE *argv[MAXARG];
  1334.   LONG argc;
  1335.   LONG cnt = 1;
  1336.   LONG Columns, Rows;
  1337.   SHORT namedPath = FALSE;
  1338.  
  1339.   if (DOSBase->dl_lib.lib_Version < 37)
  1340.     {
  1341.       Write (Output (), "ls: Need AmigaOS Release 2.04\n", 30);
  1342.       exit (20);
  1343.     }
  1344.  
  1345.   GetCLIArgs (line, &argc, argv);
  1346.  
  1347.   if (IsInteractive (Output ()))
  1348.     CONSOLE = 1;
  1349.  
  1350.   while (cnt < argc && argv[cnt][0] == '-')
  1351.     {
  1352.       if (argv[cnt][1] == '-' || argv[cnt][1] == '\0')
  1353.     {
  1354.       cnt++;
  1355.       break;
  1356.     }
  1357.       else
  1358.     {
  1359.       cnt = ParseCmdOptions (cnt, argc, argv);
  1360.       continue;
  1361.     }
  1362.     }
  1363.  
  1364.   {
  1365.     int i;
  1366.     char a[32], *b;
  1367.  
  1368.     if (!(b = malloc (32)))
  1369.       CleanUp (NoRAMMsg, RETURN_FAIL, ERROR_NO_FREE_STORE);
  1370.     for (i = HIGHLIGHT_MIN; i <= HIGHLIGHT_MAX; i++)
  1371.       {
  1372.     RawDoFmt ("ls_highlight%ld.on", &i, kput1, a);
  1373.     if (GetVar (a, b, 32, LV_VAR | GVF_LOCAL_ONLY) != -1)
  1374.       {
  1375.         highlight_tab[i].on = b;
  1376.         highlight_tab[i].printable_len = 0;
  1377.         for (; *b; b++)
  1378.           {
  1379.         if ((*b == '\x9b') || (*b == '\x1b'))
  1380.           {
  1381.             while (*++b)
  1382.               if (*b == 'm')
  1383.             break;
  1384.           }
  1385.         else
  1386.           highlight_tab[i].printable_len++;
  1387.           }
  1388.         if (!(b = malloc (32)))
  1389.           CleanUp (NoRAMMsg, RETURN_FAIL, ERROR_NO_FREE_STORE);
  1390.         RawDoFmt ("ls_highlight%ld.off", &i, kput1, a);
  1391.         if (GetVar (a, b, 32, LV_VAR | GVF_LOCAL_ONLY) != -1)
  1392.           {
  1393.         highlight_tab[i].off = b;
  1394.         for (; *b; b++)
  1395.           {
  1396.             if ((*b == '\x9b') || (*b == '\x1b'))
  1397.               {
  1398.             while (*++b)
  1399.               if (*b == 'm')
  1400.                 break;
  1401.               }
  1402.             else
  1403.               highlight_tab[i].printable_len++;
  1404.           }
  1405.         if (!(b = malloc (32)))
  1406.           CleanUp (NoRAMMsg, RETURN_FAIL, ERROR_NO_FREE_STORE);
  1407.           }
  1408.       }
  1409.       }
  1410.     free (b);
  1411.   }
  1412.  
  1413.   if (!(CONSOLE))
  1414.     {
  1415.       int i;
  1416.  
  1417.       for (i = HIGHLIGHT_MIN; i <= HIGHLIGHT_MAX; i++)
  1418.     if ((*highlight_tab[i].on == '\x9b') ||
  1419.         (*highlight_tab[i].on == '\x1b') ||
  1420.         (*highlight_tab[i].off == '\x9b') ||
  1421.         (*highlight_tab[i].off == '\x1b'))
  1422.       highlight_tab[i] = highlight_null;
  1423.  
  1424.       highlight_cursor = highlight_null;
  1425.       highlight_tab[HI_COMMENT].on = "/* ";
  1426.       highlight_tab[HI_COMMENT].off = " */";
  1427.     }
  1428.  
  1429.   {
  1430.     char b[32];
  1431.  
  1432.     if (!(SHOWHIDDEN))
  1433.       if (GetVar ("ls_hidepattern", b, sizeof b, LV_VAR | GVF_LOCAL_ONLY) != -1)
  1434.     AddAntiPattern (b);
  1435.       else
  1436.     AddAntiPattern ("(#?.(info|bak)|.#?)");
  1437.   }
  1438.  
  1439.   if (!CurWinCols)
  1440.     {
  1441.       GetWinBounds (&Columns, &Rows, (CONSOLE));
  1442.       CurWinCols = Columns;
  1443.     }
  1444.  
  1445.   PutStr (highlight_cursor.off);
  1446.  
  1447.   while (!(BREAKFLAG))
  1448.     {
  1449.       if (cnt < argc)
  1450.     {
  1451.       namedPath = TRUE;
  1452.  
  1453.       theFilePat[0] = 0;
  1454.       theDirPat[0] = 0;
  1455.       strcpy (thePath, argv[cnt]);
  1456.       cnt++;
  1457.  
  1458.       if (ParsePatternNoCase (thePath, theDirPatParsed, sizeof (theDirPatParsed)))
  1459.         {
  1460.           strcpy (theFilePat, FilePart (thePath));
  1461.           strcpy (theDirPat, theFilePat);
  1462.           *(PathPart (thePath)) = '\0';
  1463.  
  1464.           if (ParsePatternNoCase (thePath, theDirPatParsed, sizeof (theDirPatParsed)))
  1465.         {
  1466.           strcpy (theDirPat, FilePart (thePath));
  1467.           *(PathPart (thePath)) = '\0';
  1468.         }
  1469.         }
  1470.  
  1471.       if (ParsePatternNoCase (thePath, theDirPatParsed, sizeof (theDirPatParsed)))
  1472.         CleanUp (NoWildPathMsg, RETURN_WARN, ERROR_NO_MORE_ENTRIES);
  1473.  
  1474.       if (!(CurFLock = (struct FileLock *) Lock (thePath, (LONG) ACCESS_READ)))
  1475.         {
  1476.           WriteErrorString (NoFindFmtStr, thePath);
  1477.           CleanUp ("", RETURN_FAIL, IoErr ());
  1478.         }
  1479.     }
  1480.       else if ((!namedPath) && (cnt == argc))
  1481.     {
  1482.       struct Process *procp = (struct Process *) FindTask (0L);
  1483.  
  1484.       cnt++;
  1485.  
  1486.       if (procp->pr_CurrentDir)
  1487.         CurFLock = (struct FileLock *) DupLock (procp->pr_CurrentDir);
  1488.       else
  1489.         CurFLock = (struct FileLock *) Lock ("sys:", ACCESS_READ);
  1490.       if (!CurFLock)
  1491.         CleanUp (NoCurrentDirMsg, RETURN_FAIL, ERROR_NO_DEFAULT_DIR);
  1492.     }
  1493.       else
  1494.     break;
  1495.  
  1496.       if (!strchr (thePath, ':'))
  1497.     if (!NameFromLock ((BPTR) CurFLock, thePath, sizeof (thePath)))
  1498.       CleanUp (NameFromLockMsg, RETURN_FAIL, IoErr ());
  1499.       curpath = thePath;
  1500.  
  1501.       if (!theDirPat[0])
  1502.     strcpy (theDirPat, "#?");
  1503.  
  1504.       if (!theFilePat[0])
  1505.     strcpy (theFilePat, "#?");
  1506.  
  1507.       if (ParsePatternNoCase (theDirPat, theDirPatParsed, sizeof (theDirPatParsed)) == -1)
  1508.     CleanUp (ParseErrorMsg, RETURN_FAIL, ERROR_NO_MORE_ENTRIES);
  1509.       if (ParsePatternNoCase (theFilePat, theFilePatParsed, sizeof (theFilePatParsed)) == -1)
  1510.     CleanUp (ParseErrorMsg, RETURN_FAIL, ERROR_NO_MORE_ENTRIES);
  1511.  
  1512.       DirIt (CurFLock, thePath);
  1513.  
  1514.       if (CurFLock)
  1515.     UnLock ((BPTR) CurFLock);
  1516.       CurFLock = NULL;
  1517.  
  1518.       TestBreak ();
  1519.     }
  1520.  
  1521.   if ((TOTALIZE))
  1522.     {
  1523.       PutStr (highlight_tab[HI_LABEL].on);
  1524.       PutStr (TotHeaderMsg);
  1525.       PutStr (highlight_tab[HI_LABEL].off);
  1526.       VPrintf (totalfmtstr, &gdircount);    /* note that parameters are in order in memory */
  1527.     }
  1528.  
  1529.   CleanUp ("", RETURN_OK, 0L);
  1530. }
  1531.