/* "AList" 0.06 ... Disk Directory (catalog) Utility by Ed Kivi */
  
/* This command is designed to get an AmigaDOS directory and display the
   full contents to the screen.  Later it will create an AmigaDOS file
   of PathNames to be used as a Library of files on disk storage.    */
  
#include <exec/types.h>   /* order is important, else "NULL" */
#include <stdio.h>
#include <ctype.h>        /* will be 'redefined' in compiler's eye */
#include <time.h>
#include <libraries/dosextens.h>
#include <exec/memory.h>

/* #define AEKfilenote       -* long version of aekDC desired */
#undef  AEKfilenote       /* short version of aekDC desired */
#undef  DEBUG             /* [dis|en]able de-bugging printf statements */

#ifndef  HIGHVALUE
#define  HIGHVALUE     127
#endif

#define  AmigaFIB      struct FileInfoBlock
#define  aekDC         struct DCatalog      /* A. E. Kivi Disk Catalog */
#define  volsize       31     /* maximum length of volume name (31) */
#ifndef  AEKfilenote
#define  pathsize      36     /* maximum length of path name */
#else
#define  pathsize      61     /* maximum length of path name */
#endif
#define  filesize      31     /* maximum length of file name (31) */
#define  extsize       11     /* maximum length of file name extension */
  
extern struct FileLock *Lock();

/* preliminary disk catalog record layout, growing from Ram: to file */
   aekDC
   {
   aekDC    *DC_next;            /* pointer to next record */
   aekDC    *DC_prior;           /* pointer to prior record */
#ifdef   AEKfilenote
   aekDC    *DC_altnext;         /* pointer to next record (alt seq) */
   aekDC    *DC_altprior;        /* pointer to prior record (alt seq) */
#endif
   long      DC_key;             /* Ram: address, Disk: track */
   long      DC_size;            /* 0, or size in bytes of a file */
   long      DC_blocks;          /* number of blocks required */
   char      DC_type;            /* V= Volume, D= Directory, F= File */
   char      DC_volume[volsize]; /* disk name, as "WorkBench_1.2" */
   char      DC_path[pathsize];  /* first 'n' chars of the Path Name */
   char      DC_file[filesize];  /* file name, as "DLibrary" */
   char      DC_ext[extsize];    /* extension, as ".c" (without .) */
   char      DC_icon;            /* Y = has .info, N = no .info */
   char      DC_dmy[12];         /* Date, as "05-Sep-1987 " */
   char      DC_time[9];         /* Time-of-day, as "14:45:13" */
#ifdef   AEKfilenote
   char      DC_filenote[71];    /* Comments about the file */
#endif
   } ;

/* This is probably unnecessary, but nothing obvious in AmigaDOS */
   struct Calendar
   {
   int  yearday;          /* number of days in prior months, as "243" */
   int  lastday;          /* last day in the month, as "30" */
   char monname[4];       /* month name, as "Sep " */
   } ;

/* This is the format used by gmtime() and localtime() */
   struct DateTime
   {
   char DT_wday[4];       /* Day-of-the-week, as "Sat" */
   char DT_month[4];      /* month name, as "Sep " */
   char DT_mday[3];       /* Day-of-the-month, as "05" */
   char DT_time[9];       /* Time-of-day, as "14:45:13" */
   char DT_year[5];       /* Year, as "1987" */
   char DT_filler[1];     /* NewLine */
   } ;
/* replace/rename above when (if?) found in AmigaDOS */

   AmigaFIB         *fb;         /* File Info from locked directory */
   struct FileLock  *dir;        /* Locked AmigaDOS directory */
   struct FileLock  *olddir;     /* AmigaDOS directory to be restored */
   struct DateTime  *ud;         /* Date stamp info from FileInfoBlock */
   aekDC            *pdc;        /* Disk Catalog record to be inserted */
   aekDC            *pdc1;       /* Root (Volume) Disk Catalog record */
   aekDC            *pdEnd;      /* End-of-List Disk Catalog record */
   aekDC            *pdTop;      /* Directory record #1 (Volume Name only) */
   int               nestedDir;  /* number of sub-directories found */
   aekDC            *pdirn;      /* last Directory record (Path only) */
   aekDC            *pdir;       /* Directory record (Path Name only) */
   aekDC            *getDCmem(aekDC *); /* storage for Disk Catalog record */

   char   *append( char *, char *, int );  /* string concatenation */
   char   *copyn( char *, char *, int );   /* Copy n chars. */
   char   *copy( char *, char * );         /* Copy string of chars. */
   char   *fill_area( char *, unsigned char, int ); /* string (area) fill */
   char   *ign;                /* throw-away pointer from memset */
   char    DiskName[volsize];  /* name of the disk from FileInfoBlock */
   char    dirname[volsize];   /* name of the disk: from FileInfoBlock */
   char    work[108];          /* volume/path/file name work area */    

   int     compareci( char *, char * );    /* string compare, case insensitive */
   static int        keyed    = 0;  /* return from 'getchar' */
   static int        calls    = 0;  /* number of entries in core */
   static int        passno   = 0;  /* number of directory passes made */
   static int        fullpage = 22; /* number of lines displayed per page */
   static int        thispage = 0;  /* number of lines displayed so far */
   static int        bytes    = 0;  /* number of bytes stored on disk */
   static int        blocks   = 0;  /* number of blocks on disk used */
   static int        mask     = 0;  /* clean-up requirement indicators */
#define   allocud    0x00000001     /* allocated storage for DateTime */
#define   allocfb    0x00000002     /* allocated storage for FileInfoBlock */
#define   svolddir   0x00000004     /* saved old directory */
#define   allocpdTop 0x00000010     /* allocated storage for Head-of-List */
#define   allocpdEnd 0x00000020     /* allocated storage for Tail-of-List */
#define   allocpdc   0x00000040     /* allocated storage for at least 1 record */

   BOOL              fail;          /* Directory Access Failure */
   BOOL              noargs   = FALSE;   /* TRUE if argument(s) supplied */
   BOOL              color    = FALSE;   /* TRUE if color display desired */
   BOOL              verbose  = FALSE;   /* TRUE if Version, count, etc. */
   BOOL              redirect = FALSE;   /* TRUE if >prt: being used */

   void              close_things(); /* clean-up memory, etc. */
   void              Delay();        /* delay for de-bugging */
   void              usage();        /* invalid argument(s), display usage */
   void              options( char *, int );  /* parse options */
   void              Traverse( aekDC *, int, struct FileLock * );
   void              MergeDC( aekDC * , aekDC * , aekDC * ); /* Filer */
   void              print_headings();     /* headings over column data */

void
main(argc,argv)

int argc;
char *argv[];

{

   int    ai = 0;

   if ( argc >= 7 )
        usage();
   if (argc == 1)
     {
      ign = copy( dirname, "" );
      noargs = TRUE;
     }
   else
        {
         while ( argv [ai + 1] && ai++ <= argc - 1 )
               {
                 ign = copy( work, argv [ai] );
                 if ( work[0] == '?' )
                      usage();
                 if ( ( work[0] == 'o' || work[0] == 'O') && (work[1] == 'p' || work[1] == 'P') && ( work[2] == 't' || work[2] == 'T') )
                     {
                      ign = copy( work, argv [++ai] );
                      options( work, 0);
                     }
                 else
                      {
                       if ( work[0] == '-' )
                            options( work, 1);
                       else
                           {
                            ign = copy( dirname, work );
                            if ( dirname == '\0' )
                                 usage();
                           }
                      }    
               }
        }

   if ( fullpage < 3 )
        fullpage = 22;

   ud  = (struct DateTime *) AllocMem( sizeof( struct DateTime ), MEMF_CLEAR | MEMF_PUBLIC );
   mask |= allocud;
   fb  = (AmigaFIB *) AllocMem( sizeof( AmigaFIB ), MEMF_CLEAR | MEMF_PUBLIC );
   mask |= allocfb;
   dir = Lock(dirname, ACCESS_READ);
   if ( !dir )
      {
       printf( "Could not Lock '%s'.\n", dirname );
       close_things();
      }
   olddir = CurrentDir( dir );
   mask |= svolddir;

/*       Read "Root" directory FileInfoBlock */

   fail = (dir == NULL) | !Examine(dir,fb);
   if (fail) {
      printf("Sorry, '%s' is not a directory\n", dirname);
      close_things();
   }
   ign =   copy( work, fb->fib_FileName );
   ign = append( work, ":", sizeof(work) );
   if ( work[0] == ':' )
       ign = copy( work, dirname );
   work[volsize-1] = '\0';
   ign = copy( DiskName, work );

/*       Build specified Root directory name and information */

   pdTop            = getDCmem( NULL );  /* get memory for top-of-list */
   if ( !pdTop )
        close_things();
   mask |= allocpdTop;
   pdTop->DC_key    = fb->fib_DiskKey;
   pdTop->DC_size   = 0;
   pdTop->DC_blocks = fb->fib_NumBlocks;
   pdTop->DC_type   = 'V' ;
   ign = copy( pdc->DC_volume, DiskName );
   ign = fill_area( pdTop->DC_path, (unsigned char) '\0', sizeof(pdTop->DC_path) );
   ign = fill_area( pdTop->DC_file, (unsigned char) '\0', sizeof(pdTop->DC_file) );
   ign = fill_area( pdTop->DC_ext,  (unsigned char) '\0', sizeof(pdTop->DC_ext) );
   pdTop->DC_icon = 'N' ;

/* Root entry has been initialized */

   pdEnd           = getDCmem( pdTop );  /* get memory for end-of-list */
   if ( !pdEnd )
        close_things();
   mask |= allocpdTop;
   pdTop->DC_prior = NULL;
   pdEnd->DC_next  = NULL;
   pdTop->DC_next  = pdEnd;
   pdEnd->DC_prior = pdTop;
   ign = fill_area( pdEnd->DC_volume, (unsigned char) HIGHVALUE, sizeof(pdEnd->DC_volume) );
   ign = fill_area( pdEnd->DC_path,   (unsigned char) HIGHVALUE, sizeof(pdEnd->DC_path) );
   ign = fill_area( pdEnd->DC_file,   (unsigned char) HIGHVALUE, sizeof(pdEnd->DC_file) );
   ign = fill_area( pdEnd->DC_ext,    (unsigned char) HIGHVALUE, sizeof(pdEnd->DC_ext) );

/* End-of-list entry has been initialized */

   pdc1  = pdTop;           /* This one gets changed */
   pdc   = pdc1;            /* set generic pointer to root */
   calls--;                 /* do not count end-of-list */

/*      Build File and Directory records, Print from record */

   if ( redirect )          /* form feed and indent for printer */
        printf( "\f      " );
   if ( verbose )
      {
        printf( "\033[1mAList V0.06\033[0m compiled at 14:45:13 on Sat 05-Sep-1987.\n" );
        thispage = 2;
      }
   Traverse( pdc1, passno, dir );

   print_headings();
   pdc = pdTop;

/*       Print specified directory name and information */

   while ( pdc != pdEnd)
     {
      if ( !redirect && verbose && thispage >= fullpage )
         {
           printf( "Press \033[37m\033[3menter\033[0m to continue." );
           keyed = getchar();
           thispage = 0;
           print_headings();
         }
      if ( redirect )
           printf( "      " );         /* indent for printer */
      if ( pdc->DC_type == 'V' )
         printf( "\033[1m%-30s ", pdc->DC_volume );
      else
          {
           if ( pdc->DC_type == 'D' )
              {
                if ( color )
                     printf( "\033[37m" );
                printf( "\033[3m%-30s ", pdc->DC_path );
              }
           else
               {
                if ( pdc->DC_path[0] == '\0' )
                    ign = copy( work, pdc->DC_file );
                else
                    {
                      ign = copy( work, "  " );
                      ign = append( work, pdc->DC_file, sizeof(work) );
                    }
                work[volsize-1] = '\0';
                printf( "%-30s ", work);
               }
          }
      printf( "%7d ", pdc->DC_key );
      if ( pdc->DC_type == 'V' )
           printf( "  Root ");
      else if ( pdc->DC_type == 'D' )
                printf( "   Dir " );
           else
              {
               bytes += pdc->DC_size;
               printf( "%6d ", pdc->DC_size );
              }
      blocks += pdc->DC_blocks;
      printf( "%4d ", pdc->DC_blocks );
      printf( "%s ", pdc->DC_dmy );
      printf( "%s\033[0m\n", pdc->DC_time );
      thispage++;
      pdc1 = pdc;
      pdc = pdc->DC_next;
      pdTop->DC_next = pdc;    /* remove record from the linked-list */
      if ( pdc1 != pdTop )
           FreeMem( pdc1, sizeof(aekDC) ); /* Free memory for 2 -> n-1 */
     }
   mask ^= allocpdc;           /* freed all file/directory records */
   if ( redirect )
        printf( "      " );         /* indent for printer */
   printf( "%d members in %d blocks, storing %d bytes.\n", calls, blocks, bytes );
   close_things();
}                     /* end of main-line of program "DLibrary" */

/*       Append a string to the end of a string up to 'limit' characters. */

char *append( to, from, limit )

char    *to;
char    *from;
int      limit;

{
    char    *temp = to;

    if ( !to )
        return( temp );

    while ( *to && limit > 1 )   /* find current end-of-string */
          {
           to++;
           limit--;
          }

    while ( *from && limit > 1 ) /* extend string */
          {
           *to = *from++;
           to++;
           limit--;
          }

    *to++ = '\0';                /* ensure NULL terminated */
    return( temp );
}

/*        Close off (as friendly as possible) */

void close_things()

{
  if ( mask & allocud )                 /* FreeMem for date work area */
       FreeMem( ud, sizeof(struct DateTime) );
  if ( mask & allocfb )                 /* FreeMem for FileInfoBlock */
       FreeMem( fb, sizeof(AmigaFIB) );
  if ( mask & allocpdTop )              /* at least 1 record allocated */
     {
       if ( mask & allocpdc )           /* FreeMem for 2 -> n-1 */
          {
            pdc = pdTop->DC_next;
            while ( pdc != pdEnd)
              {
               pdc1 = pdc;
               pdc = pdc->DC_next;
               pdTop->DC_next = pdc;    /* maintain the linked-list */
               if ( pdc1 != pdTop )
                    FreeMem( pdc1, sizeof(aekDC) );
              }
          }
       FreeMem( pdTop, sizeof(aekDC) ); /* FreeMem for record 1 */
     }
  if ( mask & allocpdEnd )
       FreeMem( pdEnd, sizeof(aekDC) ); /* FreeMem for record n */
  if ( mask & svolddir )
       dir = CurrentDir( olddir );
  Exit();
}

/*        Compares string to string (case insensitive). */

int  compareci( to, from )

char    *to;
char    *from;

{
    char    a;
    char    b;
    int     c;

    for ( ; ; *to++, *from++ )
        {
          a = *to;
          b = *from;
          if ( a >= 'A' && a <= 'Z' )
               a = a - 'A' + 'a';
          if ( b >= 'A' && b <= 'Z' )
               b = b - 'A' + 'a';
          c = ( int ) a - ( int) b;
          if ( c || !a || !b )
              return( c );
        }
}

/*     Copy string of characters (ending with NULL terminator) */

char    *copy( to, from )

char    *to;      /* destination string */
char    *from;    /* source string */

{
    char    *temp = to;

    while ( to && *from )         /* no zero destination allowed */
           *to++ = *from++;
    *to++ = '\0';

    return(temp);
}

/*     Copy exactly "n" characters (regardless of NULL terminator) */

char    *copyn(to, from, n)

char    *to;      /* destination string */
char    *from;    /* source string */
int     n;        /* number of characters to be copied */

{
    char    *temp = to;

    while ( n-- && to )           /* no zero destination allowed */
           ( *to++ = *from++ );

    return(temp);
}

/*       Fills a string with the specified character. */

char *fill_area( to, what, howmany)

         char    *to;
unsigned char     what;
         int      howmany;

{
    char    *temp = to;

    while ( howmany-- > 1 )
       {
        *to++ = what;
       }

    *to = '\0';

    return(temp);
}

/*     get memory to use as Disk Catalog record */

aekDC *getDCmem(source)

aekDC *source;

{

   pdc = (aekDC *) AllocMem( sizeof( aekDC ), MEMF_CLEAR | MEMF_PUBLIC );

   if ( !pdc )
      {
       printf( "Out of memory large enough for another record.\n" );
       return( pdc );
      }
   calls++;
   if ( source )
       ign = copyn( (char *) pdc, (char *) source, sizeof(aekDC) );
   UnDateStamp(ud, fb);
   return(pdc);

}

/*     Find proper location for "New", given ("Top", "End", "New"). */

void   MergeDC(top, end, new)

aekDC *top;
aekDC *end;
aekDC *new;

{

  int    HiLo;

  while (top != end)   /* someone created a highvalues disk/path/file */
     {
      HiLo = compareci( top->DC_volume, new->DC_volume );
      if (HiLo > 0)
          break;
        if (!HiLo)
         {
          HiLo = compareci( top->DC_path, new->DC_path );
          if (HiLo > 0)
              break;
            if (!HiLo)
             {
              HiLo = compareci( top->DC_file, new->DC_file );
              if (HiLo > 0)
                  break;
                if (!HiLo)
                 {
                  HiLo = compareci( top->DC_ext, new->DC_ext );
                  if (HiLo > 0)
                      break;
                 }
             }
         }
      top = top->DC_next;
     }

      top           = top->DC_prior;

      end           = top->DC_next;
      new->DC_next  = top->DC_next;
      new->DC_prior = top;
      top->DC_next  = new;
      end->DC_prior = new;
}

/*       Parse option string(s) and set indicators */

void options( array, oi )

char   array[];
int    oi;

{

  int   i = 0, j = 0;

  while ( array[ oi + i ] )
  {
  if ( array[ oi + i ] == 'c' || array[ oi + i ] == 'C' )
     {
       color = TRUE;
       i++;
     }
  else if ( array[ oi + i ] == 'v' || array[ oi + i ] == 'V' )
          {
            verbose = TRUE;
            i++;
          }
  else if ( array[ oi + i ] == 'p' || array[ oi + i ] == 'P' )
          {
            redirect = TRUE;
            i++;
          }
       else
          {
          if ( array[ oi + i ] == 'l' || array[ oi + i ] == 'L' )
              {
               fullpage = 0;
               for ( j = 0; j = 2; j++ )
                  {
                    i++;
                    if ( array[ oi + i ] >= '0' && array[ oi + i ] <= '9' )
                       {
                         fullpage *= 10;
                         fullpage += array[ oi + i ] - '0';
                       }
                    else
                         break;
                  }
             }
          else
               usage();
          }
  }
}

/*       Print "Headings" for information display */

void print_headings()

{
   printf( "\n" );
   if ( redirect )
        printf( "      " );         /* indent for printer */
   printf("F_i_l_e___N_a_m_e_____________ ");
   printf("__Keys_ ");
   printf("_Size_ ");
   printf("Blks ");
   printf("DD-MMM-YYYY ");
   printf("__Time__\n");
   thispage++;

}

/* Traverse a Directory until ERROR_NO_MORE_ENTRIES

*    parent points to the directory record in core
*    level is the number of ancestors in core        */

void Traverse( parent, Level, pasdir )

aekDC            *parent; /* Parent record to be cloned */
int               Level;  /* Number of branches from the trunk */
struct FileLock  *pasdir; /* AmigaDOS directory I am examining */
 
{

   struct FileLock  *mydir;  /* AmigaDOS directory my child will examine */

   char    mydirname[volsize+pathsize];  /* Selected AmigaDOS directory */

   int     subs = 0;         /* number of sub-directories I found */

   Level++;
   while ( ExNext( dir, fb) != 0 || IoErr() != ERROR_NO_MORE_ENTRIES )
   {
      pdc = getDCmem( parent );
      if ( pdc )
           mask |= allocpdc;
      else
           break;
      if ( verbose && !redirect )
           printf( "%02d Files/Directories in Level %02d.\r", calls, Level);

      pdc->DC_key = fb->fib_DiskKey;
      pdc->DC_size = 0;
      pdc->DC_blocks = fb->fib_NumBlocks;
      if ( fb->fib_DirEntryType > 0 )
        {
         subs++;
         pdc->DC_type = 'D' ;
         pdc->DC_size = ( long ) Level;
         if ( pdc->DC_path[0] == ' ' || !pdc->DC_path[0] )
             {
              ign = copy( pdc->DC_path, "" );
             }
         else
             {
              ign = append( pdc->DC_path, "/", sizeof(pdc->DC_path) );
             }
         ign = append( pdc->DC_path, fb->fib_FileName, sizeof(pdc->DC_path) );
        }
      else
        {
         pdc->DC_size = fb->fib_Size;
         pdc->DC_type = 'F' ;
         ign = copy( pdc->DC_file, fb->fib_FileName );
        }
      MergeDC( parent, pdEnd, pdc );
   }
   UnLock( pasdir );            /* release parent directory */
   while ( subs-- > 0 )
     {
      pdc = parent;
      while ( pdc->DC_type != 'D' || pdc->DC_size == 0 )
             { if ( pdc->DC_volume[0] == HIGHVALUE )
                    break;
               else
                    pdc = pdc->DC_next;
             }
      pdc->DC_size = 0;
      ign = copy( mydirname, pdc->DC_volume );
      ign       = append( mydirname, pdc->DC_path, sizeof(mydirname) );
      mydir     = Lock( mydirname, ACCESS_READ );
      if ( !mydir | !Examine( mydir, fb ) )
         {
          printf( "Program error, '%s' is not a directory\n", mydirname );
          close_things();
         }
      Traverse( pdc, Level, mydir ); /* follow limb thru branches to leaves */
     }
}

/*     Translate DateStamp to "Sat Sep 05 14:45:13 1987 !" */

UnDateStamp( o, i )

struct DateTime  *o;
AmigaFIB         *i;

{

   static struct Calendar Mon_table[] =
    {
          0,  0, "   ",   /* null entry for month number "00" */
          0, 31, "Jan",   /* non-leap year calendar */
         31, 28, "Feb",
         59, 31, "Mar",
         90, 30, "Apr",
        120, 31, "May",
        151, 30, "Jun",
        181, 31, "Jul",
        212, 31, "Aug",
        243, 30, "Sep",
        273, 31, "Oct",
        304, 30, "Nov",
        334, 31, "Dec",
          0, 31, "Jan",   /* leap year calendar */
         31, 29, "Feb",
         60, 31, "Mar",
         91, 30, "Apr",
        121, 31, "May",
        152, 30, "Jun",
        182, 31, "Jul",
        213, 31, "Aug",
        244, 30, "Sep",
        274, 31, "Oct",
        305, 30, "Nov",
        335, 31, "Dec"
    } ;

/*             Day-of-the-week array        */
   static char WeekDays[][4] =
       {
        "Sun",
        "Mon",
        "Tue",
        "Wed",
        "Thu",
        "Fri",
        "Sat"
       } ;

   int h, m, s, d, mm, y, ly, wd, j;       /* work fields */

   d = i->fib_Date.ds_Days - 1;            /* days since 12/31/1977 */
  wd = i->fib_Date.ds_Days % 7;            /* day-of-the-week */
   y = 1978;                               /* years (base was 1978) */
   for (;;)
      {
       h = y % 4;
       if (h = 0)
            ly = 12;
       else
            ly = 0;
       j = Mon_table[12+ly].yearday + Mon_table[12+ly].lastday;
       if (d >= j)
          {
           y++;
           d = d - j;
          }
       else
           break;
      }
   for (mm = 1; mm < 13; mm++)
       if (d > Mon_table[mm+ly].lastday)
              d = d - Mon_table[mm+ly].lastday;
       else break;
   h = i->fib_Date.ds_Minute / 60;              /* hours */
   m = i->fib_Date.ds_Minute % 60;              /* minutes */
   s = i->fib_Date.ds_Tick / TICKS_PER_SECOND;  /* seconds */
   ign = copy(o->DT_wday, WeekDays[wd]);
   ign = copy(o->DT_month, Mon_table[mm].monname);
   sprintf(o->DT_mday, "%02d", d);
   sprintf(o->DT_time, "%02d:%02d:%02d", h, m, s);
   sprintf(o->DT_year, "%04d", y);
   ign = copy(   pdc->DC_dmy, o->DT_mday );
   ign = append( pdc->DC_dmy, "-", sizeof(pdc->DC_dmy) );
   ign = append( pdc->DC_dmy, o->DT_month, sizeof(pdc->DC_dmy) );
   ign = append( pdc->DC_dmy, "-", sizeof(pdc->DC_dmy) );
   ign = append( pdc->DC_dmy, o->DT_year, sizeof(pdc->DC_dmy) );
   ign = copy( pdc->DC_time, o->DT_time );
   return(0); 

}

/*       Display format of AList command */

void usage()
{
   printf("Usage: AList [>prt:] [dirname] [opt pcvl19 | -c -v -l45 -p]\n");
   printf( " where: c=color display\n" );
   printf( "        p=printer re-direction [>prt:] used\n" );
   printf( "        v=verbose\n" );
   printf( "    and l=lines/'page' (22 = full med-res CLI)\n");
   Exit();
}
