home *** CD-ROM | disk | FTP | other *** search
/ The Developer Connection…ice Driver Kit for OS/2 3 / DEV3-D1.ISO / source / util2src / ls_new.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-04-30  |  47.3 KB  |  1,167 lines

  1. /*============================================================================*
  2.  * main() module: ls.c - List directory.                         OS/2 version.
  3.  *
  4.  * (C)Copyright IBM Corporation, 1991, 1992.                     Brian E. Yoder
  5.  *
  6.  * This program is a loose adaptation of the AIX 'ls' command.  See the LS.DOC
  7.  * file for usage information.
  8.  *
  9.  * This program builds a "specification list" using the subroutines in the
  10.  * speclist.c file.  This is done for performance reasons: we will only make
  11.  * one pass through a directory: See the speclist.c file for more information.
  12.  *
  13.  * 04/04/91 - Created.
  14.  * 04/15/91 - Initial version.
  15.  * 04/16/91 - Version 1.1 - Added copyright and version strings.
  16.  * 04/17/91 - Version 1.2 - Internal changes to speclist.c functions.
  17.  * 04/30/91 - Version 1.3 - Ported from DOS to OS/2.  I made showfile()'s
  18.  *            attrib variable a 'ushort' instead of a 'char'.  Also, we
  19.  *            have to check to see that we have at least one argument
  20.  *            before checking for flags.  Also add the c and u flags.
  21.  * 05/06/91 - Version 1.4 - Support -r flag: recursive descent thru subdirectories.
  22.  * 05/09/91 - Version 1.4b - Support new interface to slmake().
  23.  * 06/10/91 - Version 1.5 - Support m, n, and t flags.  Also, allow multiple
  24.  *            flag specifications (i.e. 'ls -t -n' is the same as 'ls -tn').
  25.  * 06/11/91 - Version 1.6 - Updated multicolumn algorithm.
  26.  * 07/02/91 - Version 1.7 - Add o and 1 flags.  The default is changed to
  27.  *            sort by name, and list in multicolumn format if isatty() is
  28.  *            TRUE for standard output.
  29.  * 04/13/92 - Version 1.8 - Don't force HPFS filenames to lowercase anymore.
  30.  * 04/30/92 - Version 1.9 - The flags are now case-sensitive.  Reverse sorting
  31.  *            and sorting by size have been added.
  32.  *============================================================================*/
  33.  
  34. static char copr[] = "(c)Copyright IBM Corporation, 1992.  All rights reserved.";
  35. static char ver[]  = "Version 1.9";
  36.  
  37. #include <stdio.h>
  38. #include <stdlib.h>
  39. #include <string.h>
  40. #include <sys/types.h>
  41. #include <dos.h>
  42.  
  43. #include "util.h"
  44.  
  45. #define BUFFLEN  2048               /* Buffer length */
  46.  
  47. #define MAXWIDTH (BUFFLEN-1)        /* Define the same as BUFFLEN */
  48. #define MINPAD   4                  /* Min. padding between names, for -m */
  49.  
  50. static  char     buff[BUFFLEN+1];   /* Buffer for output filenames */
  51. static  char     fstr[BUFFLEN+1];   /* Buffer for basename portion of filename */
  52.  
  53. /*----------------------------------------------------------------------------*
  54.  * Default argc/argv: If no file specs entered, then assume "*"
  55.  *----------------------------------------------------------------------------*/
  56.  
  57. static  int      dargc = 1;
  58. static  char    *dargv[1] = { "*" };
  59.  
  60. /*----------------------------------------------------------------------------*
  61.  * Definitions for type of output file sorting.  The base values (the first
  62.  * value of each pair) is set by one of the sorting flags.  If a reverse sort
  63.  * is selected, the base value is simply incremented to give the value of the
  64.  * reverse sort.
  65.  *----------------------------------------------------------------------------*/
  66.  
  67. enum SORT_TYPE {  SortNone0,        /* Don't sort */
  68.                   SortNone,         /* Don't sort */
  69.  
  70.                   SortName,         /* Sort by filename */
  71.                   SortRName,        /* Reverse sort by filename */
  72.  
  73.                   SortTime,         /* Sort by date and time */
  74.                   SortRTime,        /* Reverse sort by date and time */
  75.  
  76.                   SortSize,         /* Sort by file size */
  77.                   SortRSize         /* Reverse sort by file size */
  78. };
  79.  
  80. /*----------------------------------------------------------------------------*
  81.  * Structure for flags
  82.  *----------------------------------------------------------------------------*/
  83.  
  84. typedef struct {
  85.  
  86.      int        f_flag;             /* list ordinary files */
  87.      int        s_flag;             /* list system files */
  88.      int        h_flag;             /* list hidden files */
  89.      int        d_flag;             /* list directories */
  90.  
  91.      int        a_flag;             /* list all */
  92.      int        l_flag;             /* list in long format */
  93.      int        U_flag;             /* list in uppercase */
  94.      int        p_flag;             /* Append path separator to directory names */
  95.  
  96.      int        c_flag;             /* Display time of creation */
  97.      int        u_flag;             /* Display time of last access */
  98.  
  99.      int        C_flag;             /* Multicolumn mode setting */
  100.      int        r_flag;             /* Reverse sort flag */
  101.  
  102. } LSFLAGS;
  103.  
  104. static LSFLAGS flags = {            /* Flags and their initial settings */
  105.  
  106.      FALSE, FALSE, FALSE, FALSE,
  107.      FALSE, FALSE, FALSE, FALSE,
  108.      FALSE, FALSE,
  109.      FALSE, FALSE
  110. };
  111.  
  112. /*----------------------------------------------------------------------------*
  113.  * Structures and data used by storefile(), sortfiles(), and mshow()
  114.  *----------------------------------------------------------------------------*/
  115.  
  116. typedef struct stfile {
  117.  
  118.      struct stfile *next;           /* Pointer to next stored file's structure */
  119.      char      *fname;              /* Pointer to file's name */
  120.      ulong      fsize;              /* File's size */
  121.      ushort     fmode;              /* File's mode */
  122.      ushort     fdate;              /* File's date word */
  123.      ushort     ftime;              /* File's time word */
  124.      ulong      fdatetime;          /* Time/date doubleword, for sorting */
  125.  
  126. } STFILE;
  127.  
  128. static STFILE *stfirst = NULL;      /* Linked list of stored file structures */
  129. static STFILE *stlast  = NULL;
  130.  
  131. static ulong   stcnt   = 0L;        /* Number of stored file structures */
  132. static uint    stlongest = 0;       /* Length of longest stored name */
  133. static ulong   stmem   = 0L;        /* Total memory usage by stfile structures */
  134. static STFILE **starray = NULL;     /* Array of 'stcnt' ptrs to stored file structures */
  135.  
  136. static char    MsgStoreMem[] =
  137. "ls: There isn't enough memory to store filenames for multicolumn listing\n\
  138.     and/or sorting.  Try again, but specify the -o1 flags.\n";
  139.  
  140. /*============================================================================*
  141.  * Function prototypes for internal subroutines
  142.  *============================================================================*/
  143.  
  144. static void       syntax       ( void );
  145. static void       showfile     ( char *, ulong, ushort, ushort, ushort, LSFLAGS * );
  146. static int        storefile    ( char *, ulong, ushort, ushort, ushort );
  147. static int        sortfiles    ( int );
  148. static void       showarray    ( LSFLAGS * );
  149.  
  150. static int        qcompName    ( STFILE **, STFILE ** );
  151. static int        qcompRName   ( STFILE **, STFILE ** );
  152. static int        qcompTime    ( STFILE **, STFILE ** );
  153. static int        qcompRTime   ( STFILE **, STFILE ** );
  154. static int        qcompSize    ( STFILE **, STFILE ** );
  155. static int        qcompRSize   ( STFILE **, STFILE ** );
  156.  
  157. static uint       getwidth     ( void );
  158.  
  159. /*============================================================================*
  160.  * Main Program Entry Point
  161.  *============================================================================*/
  162.  
  163. main(argc, argv)
  164.  
  165. int argc;                           /* arg count */
  166. char *argv[];                       /* arg pointers */
  167.  
  168. {
  169.   int     rc;                       /* Return code store area */
  170.   long    lrc;                      /* Long return code */
  171.   char   *flagstr;                  /* Pointer to string of flags */
  172.   char    cf;                       /* Current flag character (if any) */
  173.  
  174.   int     uniq;                     /* 'uniq' flag for slmake() */
  175.   ushort  mflags;                   /* Match flags for slrewind() */
  176.   ushort  recurse;                  /* Match flags for slrewind() */
  177.  
  178.   int     sorttype;                 /* Type of sorting to perform */
  179.   int     store;                    /* Do we need to store filenames first? */
  180.  
  181.   SLPATH *speclist;                 /* Pointer to linked list of path specs */
  182.  
  183.   FINFO  *fdata;                    /* Pointers returned by slmatch() */
  184.   SLPATH *pdata;
  185.   SLNAME *ndata;
  186.  
  187.   ushort f_time;                    /* Time and date values that we will */
  188.   ushort f_date;                    /* display */
  189.  
  190.   uniq = TRUE;                      /* Init: build SLPATHs only for unique paths */
  191.   recurse = FALSE;                  /* Init: don't recursively search dirs */
  192.  
  193.   sorttype = SortName;              /* Default is to sort by name */
  194.   store = FALSE;                    /* We'll adjust this later if necessary */
  195.  
  196.   if (isatty(1))                    /* If standard output is a char device: */
  197.      flags.C_flag = TRUE;                /* Enable multi-column output */
  198.   else                              /* Else: */
  199.      flags.C_flag = FALSE;               /* Set single-column output */
  200.  
  201.  /*---------------------------------------------------------------------------*
  202.   * Process flags, if any
  203.   *---------------------------------------------------------------------------*/
  204.  
  205.   argc--;                           /* Ignore 1st argument (program name) */
  206.   argv++;
  207.  
  208.   while (argc != 0)                 /* For each argument: */
  209.   {
  210.      flagstr = *argv;                  /* Point 'flagstr' to argument */
  211.      if (*flagstr != '-')              /* If it doesn't begin with '-': */
  212.         break;                              /* Then it's the first filespec */
  213.      else                              /* Otherwise: It's a set of flags: */
  214.      {
  215.         flagstr++;                          /* Point past the dash */
  216.  
  217.         if (*flagstr == '?')                /* If help is requested: */
  218.            syntax();                             /* Show help */
  219.  
  220.         while (*flagstr != '\0')            /* For each character in flag string: */
  221.         {
  222.            switch (*flagstr)
  223.            {
  224.               case '?':                          /* If we see ? in the list: */
  225.                  syntax();                       /* then user wants help */
  226.                  return(2);
  227.                  break;
  228.  
  229.               case 'f':
  230.                  flags.f_flag = TRUE;
  231.                  break;
  232.  
  233.               case 's':
  234.                  flags.s_flag = TRUE;
  235.                  break;
  236.  
  237.               case 'h':
  238.                  flags.h_flag = TRUE;
  239.                  break;
  240.  
  241.               case 'd':
  242.                  flags.d_flag = TRUE;
  243.                  break;
  244.  
  245.               case 'a':
  246.                  flags.a_flag = TRUE;
  247.                  break;
  248.  
  249.               case 'c':
  250.                  flags.c_flag = TRUE;
  251.                  break;
  252.  
  253.               case 'u':
  254.               case 'x':
  255.                  flags.u_flag = TRUE;
  256.                  break;
  257.  
  258.               case 'U':
  259.                  flags.U_flag = TRUE;
  260.                  break;
  261.  
  262.               case 'p':
  263.                  flags.p_flag = TRUE;
  264.                  break;
  265.  
  266.               case 'R':
  267.                  recurse = TRUE;
  268.                  break;
  269.  
  270.               case 'g':                          /* 'g' means group in order that */
  271.               case 'G':                          /* filespecs were listed on cmd line */
  272.                  uniq = FALSE;
  273.                  break;
  274.  
  275.              /*--------------------------------------*
  276.               * These flags affect multicolumn output
  277.               *--------------------------------------*/
  278.  
  279.               case 'l':
  280.               case 'L':
  281.                  flags.l_flag = TRUE;
  282.                  flags.C_flag = FALSE;
  283.                  break;
  284.  
  285.               case 'C':                          /* 'C' sets multicolum format */
  286.               case 'm':
  287.                  flags.C_flag = TRUE;
  288.                  flags.l_flag = FALSE;
  289.                  break;
  290.  
  291.               case '1':                          /* '1' forces single-column format */
  292.                  flags.C_flag = FALSE;
  293.                  break;
  294.  
  295.              /*--------------------------------------*
  296.               * These flags affect sorting
  297.               *--------------------------------------*/
  298.  
  299.               case 'n':                          /* 'n' sorts by name */
  300.                  sorttype = SortName;
  301.                  break;
  302.  
  303.               case 't':                          /* 't' sorts by date/time */
  304.                  sorttype = SortTime;
  305.                  break;
  306.  
  307.               case 'z':                          /* 'z' sorts by size */
  308.                  sorttype = SortSize;
  309.                  break;
  310.  
  311.               case 'o':                          /* Disable sorting */
  312.                  sorttype = SortNone0;
  313.                  break;
  314.  
  315.               case 'r':                          /* 'r' reverses the sort */
  316.                  flags.r_flag = TRUE;
  317.                  break;
  318.  
  319.               default:
  320.                  fprintf(stderr, "ls: Invalid flag '%c'.  For help, enter 'ls -?'\n",
  321.                     *flagstr);
  322.                  exit(2);
  323.                  break;
  324.            }
  325.            flagstr++;                            /* Check next character */
  326.         } /* end of while stmt to process all flag characters in current argv */
  327.  
  328.         argc--;                             /* Done with flags: Discard them */
  329.         argv++;
  330.      } /* end of if stmt to process flag string */
  331.   } /* end of while stmt to process all flag strings */
  332.  
  333.   if ( (flags.f_flag == FALSE) &&   /* If all flags having to do with file */
  334.        (flags.s_flag == FALSE) &&   /* types are all (false), then set the */
  335.        (flags.h_flag == FALSE) &&   /* f and d flags to true (default) */
  336.        (flags.d_flag == FALSE) )
  337.   {
  338.      flags.f_flag = TRUE;
  339.      flags.d_flag = TRUE;
  340.   }
  341.  
  342.   if (flags.C_flag ||               /* If we're listing in multi-column format */
  343.      (sorttype > SortNone))         /* or we're sorting the output listing: */
  344.         store = TRUE;                    /* Then we'll need to store all names */
  345.  
  346.   if (flags.r_flag)                 /* If we're sorting in reverse order: */
  347.      sorttype++;                         /* Bump sort type to 2nd value in pair */
  348.  
  349.  /*---------------------------------------------------------------------------*
  350.   * Build the mflags variable: will be passed to slrewind() to restrict
  351.   * the names matched by slmatch().
  352.   *---------------------------------------------------------------------------*/
  353.  
  354.   mflags = 0;                       /* Reset flags, then: */
  355.  
  356.   if (flags.f_flag == TRUE)
  357.      mflags = mflags | SL_NORMAL;
  358.  
  359.   if (flags.s_flag == TRUE)
  360.      mflags = mflags | SL_SYSTEM;
  361.  
  362.   if (flags.h_flag == TRUE)
  363.      mflags = mflags | SL_HIDDEN;
  364.  
  365.   if (flags.d_flag == TRUE)
  366.      mflags = mflags | SL_DIR;
  367.  
  368.   if (flags.a_flag == TRUE)
  369.      mflags = mflags | SL_NORMAL |
  370.                        SL_SYSTEM |
  371.                        SL_HIDDEN |
  372.                        SL_DIR    |
  373.                        SL_DOTDIR |
  374.                        SL_VOLID;
  375.  
  376.  /*---------------------------------------------------------------------------*
  377.   * Process file specifications and build specification list
  378.   *---------------------------------------------------------------------------*/
  379.  
  380.   if (argc != 0)                    /* If there are command line args: */
  381.      rc = slmake(&speclist, uniq, TRUE, argc, argv);     /* Process them */
  382.   else                              /* If there are NO command line args: */
  383.      rc = slmake(&speclist, uniq, TRUE, dargc, dargv);   /* Then assume "*" */
  384.  
  385.   if (rc != 0)                      /* If there was an error: */
  386.   {                                 /* Analyze rc, show msg, and return */
  387.      if (rc == REGX_MEMORY)
  388.         fprintf(stderr, "Out of memory while processing '%s'.\n",
  389.            slerrspec());
  390.      else
  391.         fprintf(stderr, "Invalid filespec: '%s'.\n",
  392.            slerrspec());
  393.  
  394.      return(2);
  395.   }
  396.  
  397.  /*---------------------------------------------------------------------------*
  398.   * Attempt to match files to the specification list
  399.   *---------------------------------------------------------------------------*/
  400.  
  401.   slrewind(speclist, mflags,        /* Initialize slmatch() */
  402.            recurse);
  403.   for (;;)                          /* Loop to find all matching DOS files: */
  404.   {
  405.     /*--------------------------------------------------*
  406.      * Get next matching file
  407.      *--------------------------------------------------*/
  408.  
  409.      fdata = slmatch(&pdata,             /* Get next matching DOS file */
  410.                      &ndata);
  411.      if (fdata == NULL)                  /* If none: */
  412.         break;                                /* Done */
  413.  
  414.     /*--------------------------------------------------*
  415.      * Build d:path\name string and store in buff[]:
  416.      *--------------------------------------------------*/
  417.  
  418.      strcpy(buff, pdata->path);          /* Store path portion of filename */
  419.      pathcat(buff, fdata->Fname);        /* Add name to end of path string */
  420.  
  421.      if (!flags.U_flag)                  /* If we don't want uppercase output: */
  422.      {
  423.         if (!pdata->hpfs)                     /* If not an HPFS name: */
  424.            strlwr(buff);                         /* Make it lowercase */
  425.      }
  426.      else                                /* Otherwise: we want uppercase output: */
  427.      {
  428.         strupr(buff);                         /* Convert it to uppercase */
  429.      }
  430.  
  431.      if (flags.p_flag)                   /* If p flag specified, then: */
  432.         if (isdir(buff))                      /* If name is that of a directory: */
  433.            pathcat(buff, "\\");                    /* Add path separator to it */
  434.  
  435.     /*--------------------------------------------------*
  436.      * Store the appropriate time/date to display
  437.      *--------------------------------------------------*/
  438.  
  439.      f_time = f_date = 0;                /* Initialize */
  440.  
  441.      if ((!flags.c_flag) && (!flags.u_flag))
  442.      {                                   /* If neither c nor u specified: */
  443.         memcpy(&f_time,                  /* Get time of last modification */
  444.                &fdata->ftimeLastWrite,
  445.                sizeof(ushort));
  446.         memcpy(&f_date,
  447.                &fdata->fdateLastWrite,
  448.                sizeof(ushort));
  449.      }
  450.      else if (flags.c_flag)              /* If c specified: */
  451.      {                                   /* Get time of creation */
  452.         memcpy(&f_time,
  453.                &fdata->ftimeCreation,
  454.                sizeof(ushort));
  455.         memcpy(&f_date,
  456.                &fdata->fdateCreation,
  457.                sizeof(ushort));
  458.      }
  459.      else                                /* If u specified: */
  460.      {                                   /* Get time of last access */
  461.         memcpy(&f_time,
  462.                &fdata->ftimeLastAccess,
  463.                sizeof(ushort));
  464.         memcpy(&f_date,
  465.                &fdata->fdateLastAccess,
  466.                sizeof(ushort));
  467.      }
  468.  
  469.     /*--------------------------------------------------*
  470.      * Either show the file or store it
  471.      *--------------------------------------------------*/
  472.  
  473.      if (store == FALSE)                 /* If we're not storing the file: */
  474.      {
  475.         showfile(buff, fdata->Fsize,          /* Show it */
  476.                        fdata->Fmode,
  477.                        f_date, f_time,
  478.                        &flags);
  479.      }
  480.      else                                /* Otherwise: */
  481.      {
  482.         rc = storefile(buff, fdata->Fsize,    /* Store it */
  483.                         fdata->Fmode,
  484.                         f_date, f_time);
  485.         if (rc != 0)                          /* If we can't store it: */
  486.         {
  487.            fprintf(stderr, "%s", MsgStoreMem);     /* Display error message */
  488.            return(2);
  489.         }
  490.      }
  491.   } /* end of for loop for all matching files */
  492.  
  493.  /*---------------------------------------------------------------------------*
  494.   * If we stored the files, then process and list them
  495.   *---------------------------------------------------------------------------*/
  496.  
  497.   if (store == TRUE)                /* If we have stored files: */
  498.   {
  499.      /*
  500.      printf("Total number of stored files:     %lu\n", stcnt);
  501.      printf("Amount of memory used for files:  %lu\n", stmem);
  502.      */
  503.  
  504.      rc = sortfiles(sorttype);           /* Sort them if required */
  505.      if (rc != 0)                        /* Check for errors */
  506.      {
  507.         fprintf(stderr, "%s", MsgStoreMem);
  508.         return(2);
  509.      }
  510.  
  511.      showarray(&flags);                  /* Show the files in the array */
  512.  
  513.   } /* end of: if (store == TRUE) */
  514.  
  515.  /*---------------------------------------------------------------------------*
  516.   * Display (on stderr) all filespecs that had no matching files
  517.   *---------------------------------------------------------------------------*/
  518.  
  519.   lrc = slnotfound(speclist);       /* Display path\names w/no matching files */
  520.   if (lrc == 0L)                    /* If all fspecs were matched: */
  521.      return(0);                          /* Return successfully */
  522.   else                              /* Otherwise:  One or more not found: */
  523.      return(2);                          /* Return with error */
  524.  
  525. } /* end of main() */
  526.  
  527. /*============================================================================*
  528.  * syntax() - Display command syntax and exit to operating system!
  529.  *============================================================================*/
  530. static void syntax()
  531. {
  532.   fprintf(stderr, "%s\n", ver);
  533.   fprintf(stderr, "Usage:  ls [-fshdalcuRpC1ntzrogU] [fspec ...]\n");
  534.   fprintf(stderr, "Flags:\n");
  535.  
  536.   fprintf(stderr, "  f  Match ordinary files.\n");
  537.   fprintf(stderr, "  s  Match system files.\n");
  538.   fprintf(stderr, "  h  Match hidden files.\n");
  539.   fprintf(stderr, "  d  Match subdirectories.\n");
  540.   fprintf(stderr, "  a  Match all: Equivalent to -fshd.\n");
  541.  
  542.   fprintf(stderr, "  l  List in long format.\n");
  543.   fprintf(stderr, "  c  Use time of creation (use with -l or -t).\n");
  544.   fprintf(stderr, "  u  Use time of last access (use with -l or -t).\n");
  545.   fprintf(stderr, "  R  Recursively descend subdirectories.\n");
  546.   fprintf(stderr, "  p  Append path separator to names of directories.\n");
  547.  
  548.   fprintf(stderr, "  C  List files in multiple columns, sorted vertically.\n");
  549.   fprintf(stderr, "  1  List files in one column.\n");
  550.   fprintf(stderr, "  n  Sort listing by filename.\n");
  551.   fprintf(stderr, "  t  Sort listing by file date and time.\n");
  552.   fprintf(stderr, "  z  Sort listing by file size.\n");
  553.   fprintf(stderr, "  r  Reverse the sorting order.\n");
  554.   fprintf(stderr, "  o  Don't sort: list in the order found.\n");
  555.  
  556.   fprintf(stderr, "  g  Group output by 'fspec'.\n");
  557.   fprintf(stderr, "  U  List names in uppercase.\n");
  558.  
  559. //fprintf(stderr, "\n");
  560. //fprintf(stderr, "If no file specifications are present, then \"*\" is assumed.\n");
  561. //fprintf(stderr, "If any fspec is a directory, then \"fspec\\*\" is assumed.\n");
  562. //fprintf(stderr, "If none of the f, s, h, d, and a flags are specified, then 'fd'\n");
  563. //fprintf(stderr, "is assumed: match ordinary files and directories.\n");
  564.  
  565.   exit(2);
  566. }
  567.  
  568. /*============================================================================*
  569.  * showfile() - Show file information.
  570.  *
  571.  * REMARKS:
  572.  *   This subroutine formats the information on the current file entry and
  573.  *   writes it to stdout.
  574.  *
  575.  * RETURNS: Nothing.
  576.  *============================================================================*/
  577.  
  578. static void showfile(fullname, fsize, attrib, fdate, ftime, pflag)
  579.  
  580. char    *fullname;                  /* Full name to display */
  581. ulong    fsize;                     /* File's size */
  582. ushort   attrib;                    /* File's attribute byte */
  583. ushort   fdate;                     /* Date word */
  584. ushort   ftime;                     /* Time word */
  585. LSFLAGS *pflag;                     /* Pointer to flags structure */
  586.  
  587. {
  588.   FTM   ftm;                        /* File time structure */
  589.  
  590.   char  c_dir     = '-';            /* Characters for file mode (long format) */
  591.   char  c_write   = 'w';            /* These are the initial settings: we'll */
  592.   char  c_read    = 'r';            /* be changing them later */
  593.   char  c_system  = '-';
  594.   char  c_hidden  = '-';
  595.   char  c_archive = '-';
  596.  
  597.  /*---------------------------------------------------------------------------*
  598.   * Show information in either long or short format
  599.   *---------------------------------------------------------------------------*/
  600.  
  601.   if (!pflag->l_flag)               /* If short format: */
  602.   {
  603.      printf("%s\n", fullname);           /* Just print name */
  604.      return;
  605.   }
  606.  
  607.  /*---------------------------------------------------------------------------*
  608.   * Show information in long format
  609.   *---------------------------------------------------------------------------*/
  610.  
  611.   if ((attrib & _A_VOLID) != 0)
  612.      c_dir = 'v';
  613.   if ((attrib & _A_SUBDIR) != 0)
  614.      c_dir = 'd';
  615.  
  616.   if ((attrib & _A_RDONLY) != 0)
  617.      c_write = '-';
  618.  
  619.   if ((attrib & _A_SYSTEM) != 0)
  620.      c_system = 's';
  621.   if ((attrib & _A_HIDDEN) != 0)
  622.      c_hidden = 'h';
  623.   if ((attrib & _A_ARCH) != 0)
  624.      c_archive = 'a';
  625.  
  626.   cvtftime(ftime, fdate, &ftm);     /* "Extract" time/date bit fields */
  627.  
  628.   printf("%c%c%c%c%c%c %10lu  %2d-%02d-%4d %2d:%02d:%02d %s\n",
  629.     c_dir,
  630.     c_read,
  631.     c_write,
  632.     c_system,
  633.     c_hidden,
  634.     c_archive,
  635.  
  636.     fsize,                          /* file size */
  637.     ftm.ftm_mon,
  638.     ftm.ftm_day,
  639.     ftm.ftm_year,
  640.     ftm.ftm_hr,
  641.     ftm.ftm_min,
  642.     ftm.ftm_sec,
  643.  
  644.     fullname);
  645.  
  646.   return;
  647. }
  648.  
  649. /*============================================================================*
  650.  * storefile() - Store file's information.
  651.  *
  652.  * REMARKS:
  653.  *   This subroutine stores in information for a file in malloc'd memory.
  654.  *   It allocates a block of memory that is large enough to hold both the
  655.  *   STFILE structure and the file's name, so that we can minimize the
  656.  *   number of calls to malloc.
  657.  *
  658.  *   The static 'stcnt' variable is incremented to reflect the number of
  659.  *   stored file structures.  The static 'stlongest' variable is updated
  660.  *   (if necessary) so that it contains the length of the longest filename
  661.  *   we have stored so far.  The static 'stmem' variable is updated for
  662.  *   debug purposes only: to tell us how much memory we have allocated.
  663.  *
  664.  * RETURNS:
  665.  *   0, if successful.  1, if there isn't enough memory.
  666.  *============================================================================*/
  667.  
  668. static int storefile(fullname, fsize, attrib, fdate, ftime)
  669.  
  670. char    *fullname;                  /* Full name to display */
  671. ulong    fsize;                     /* File's size */
  672. ushort   attrib;                    /* File's attribute byte */
  673. ushort   fdate;                     /* Date word */
  674. ushort   ftime;                     /* Time word */
  675.  
  676. {
  677.   uint    len;                      /* Length */
  678.  
  679.   int     totallen;                 /* Total length needed for memory block */
  680.   char   *memblock;                 /* Pointer to allocated memory */
  681.   STFILE *p_stfile;                 /* Pointer to STFILE structure */
  682.   char   *p_fname;                  /* Pointers to filename string */
  683.  
  684.  /*---------------------------------------------------------------------------*
  685.   * Allocate a single block of memory for data structure and all strings
  686.   *---------------------------------------------------------------------------*/
  687.  
  688.   totallen = sizeof(STFILE) +       /* Calculate total size of memory block */
  689.              strlen(fullname) + 1;  /* needed to hold structure and its strings */
  690.  
  691.   memblock = malloc(totallen);      /* Allocate the memory */
  692.   if (memblock == NULL)
  693.      return(1);
  694.  
  695.   p_stfile = (STFILE *)memblock;    /* Store pointer to block and to where */
  696.   p_fname = memblock + sizeof(STFILE);   /* string will be put */
  697.   strcpy(p_fname, fullname);        /* Store name string in the block */
  698.  
  699.   stmem += totallen;                /* Update total no. of bytes allocated */
  700.  
  701.   p_stfile->next = NULL;            /* Initialize structure link field */
  702.  
  703.   p_stfile->fname = p_fname;        /* Store file information in structure */
  704.   p_stfile->fsize = fsize;
  705.   p_stfile->fmode = attrib;
  706.   p_stfile->fdate = fdate;
  707.   p_stfile->ftime = ftime;
  708.  
  709.  /*---------------------------------------------------------------------------*
  710.   * Store the file's date and time in the 'fdatetime' unsigned long field.
  711.   * This will make it easy sortfiles() to perform a sort based on file
  712.   * date/time if requested.
  713.   *
  714.   * The date is store in the hi-order word of this field, and the time is
  715.   * store in the lo-order word.  We assume that ushort is a word and that
  716.   * ulong is a doubleword.  However, we avoid any dependency on the Intel
  717.   * ordering scheme by not accessing the high and low words directly but
  718.   * by letting the compiler do it for us!
  719.   *---------------------------------------------------------------------------*/
  720.  
  721.   p_stfile->fdatetime = fdate;      /* Store date in lower word of 'fdatetime' */
  722.   p_stfile->fdatetime <<= 16;       /* Shift date into upper word */
  723.   p_stfile->fdatetime |= ftime;     /* OR time into lower word */
  724. /*
  725.   printf("Date:      %04X\n", fdate);
  726.   printf("Time:      %04X\n", ftime);
  727.   printf("Date/time: %08lX\n", p_stfile->fdatetime);
  728. */
  729.  /*---------------------------------------------------------------------------*
  730.   * Add this file structure to the linked list
  731.   *---------------------------------------------------------------------------*/
  732.  
  733.   if (stfirst == NULL)              /* If list is empty: */
  734.      stfirst = p_stfile;                 /* New structure is now first on list */
  735.  
  736.   if (stlast != NULL)               /* If end-of-list ptr is not NULL: */
  737.      stlast->next = p_stfile;            /* Point old end structure to new one */
  738.  
  739.   stlast = p_stfile;                /* New structure is now last on list */
  740.  
  741.  /*---------------------------------------------------------------------------*
  742.   * Update some counts, and we're done
  743.   *---------------------------------------------------------------------------*/
  744.  
  745.   stcnt++;                          /* Update count of file structures stored */
  746.  
  747.   len = strlen(fullname);           /* Get length of file name */
  748.   if (stlongest < len)              /* If longest name so far is shorter: */
  749.      stlongest = len;                    /* We have a new longest name! */
  750.  
  751.   return(0);                        /* Successful: return */
  752. }
  753.  
  754. /*============================================================================*
  755.  * sortfiles() - Sort the stored files.
  756.  *
  757.  * REMARKS:
  758.  *   This subroutine builds an array of pointers to the stored file structures
  759.  *   and stores a pointer to the array in 'starray'.  The linked list of
  760.  *   'stcnt' structures must already have been set up.
  761.  *
  762.  *   This subroutine then sorts the pointers in this array, depending upon
  763.  *   the value in 'sorttype'.
  764.  *
  765.  * RETURNS:
  766.  *   0, if successful.  1, if there isn't enough memory for the array.
  767.  *============================================================================*/
  768.  
  769. static int sortfiles(sorttype)
  770.  
  771. int       sorttype;                 /* Type of sorting: See SORT_TYPE enum */
  772.  
  773. {
  774.   int     rc;                       /* Return code storage */
  775.   STFILE *p_stfile;                 /* Pointer to STFILE structure */
  776.   ulong   totallen;                 /* Total length needed for memory block */
  777.  
  778.   STFILE *source;                   /* Pointer to STFILE structure */
  779.   STFILE **target;                  /* Ptr to loc containing ptr to STFILE */
  780.   uint    cnt;                      /* Count value */
  781.  
  782.  /*---------------------------------------------------------------------------*
  783.   * Allocate a block of memory to hold pointers to all STFILE structures
  784.   *---------------------------------------------------------------------------*/
  785.  
  786.   if (stcnt == 0L)                  /* If there are no STFILE structures: */
  787.      return(0);                          /* Then there's nothing to do! */
  788.  
  789.   totallen = sizeof(char *) *       /* Calulate size of block needed to hold */
  790.              stcnt;                 /* the pointers of all structures */
  791.  
  792.   if (totallen > 0x0000FFFEL)       /* If the size can't fit into a word: */
  793.      return(1);                          /* Then return w/error */
  794.  
  795.   starray = (STFILE **)malloc(totallen);
  796.   if (starray == NULL)              /* Allocate the memory for the array */
  797.      return(1);
  798.  
  799.  /*---------------------------------------------------------------------------*
  800.   * Fill the block of memory with pointers to STFILE structures
  801.   *---------------------------------------------------------------------------*/
  802.  
  803.   source = stfirst;                 /* Point source to first file structure */
  804.   target = starray;                 /* Point target to beginning of array */
  805.  
  806.   cnt = (uint)stcnt;                /* Set loop count: no. of structures */
  807.   while (cnt != 0)                  /* For each STFILE structure: */
  808.   {
  809.      *target = source;                   /* Store structure's ptr in array */
  810.      source = source->next;              /* Point to next structure in list */
  811.      target++;                           /* Point to next location in array */
  812.      cnt--;                              /* Decrement loop counter */
  813.   }
  814.  
  815.  /*---------------------------------------------------------------------------*
  816.   * Sort, depending upon 'sorttype':
  817.   *---------------------------------------------------------------------------*/
  818.  
  819.   switch (sorttype)                 /* Only the following types cause a sort: */
  820.   {
  821.      case SortName:                      /* Sort by name */
  822.         qsort((void *)starray,
  823.               (uint)stcnt,
  824.               sizeof(char *),
  825.               qcompName);
  826.         break;
  827.  
  828.      case SortRName:                     /* Sort by reverse name */
  829.         qsort((void *)starray,
  830.               (uint)stcnt,
  831.               sizeof(char *),
  832.               qcompRName);
  833.         break;
  834.  
  835.      case SortTime:                      /* Sort by date/time stamp */
  836.         qsort((void *)starray,
  837.               (uint)stcnt,
  838.               sizeof(char *),
  839.               qcompTime);
  840.         break;
  841.  
  842.      case SortRTime:                     /* Sort by reverse date/time stamp */
  843.         qsort((void *)starray,
  844.               (uint)stcnt,
  845.               sizeof(char *),
  846.               qcompRTime);
  847.         break;
  848.  
  849.      case SortSize:                      /* Sort by size */
  850.         qsort((void *)starray,
  851.               (uint)stcnt,
  852.               sizeof(char *),
  853.               qcompSize);
  854.         break;
  855.  
  856.      case SortRSize:                     /* Sort by reverse size */
  857.         qsort((void *)starray,
  858.               (uint)stcnt,
  859.               sizeof(char *),
  860.               qcompRSize);
  861.         break;
  862.   }
  863.  
  864.   return(0);                        /* Successful: return */
  865. }
  866.  
  867. /*============================================================================*
  868.  * showarray() - Show the array of stored files.
  869.  *
  870.  * REMARKS:
  871.  *   This subroutine displays the files whose STFILE structure pointers are
  872.  *   stored in the 'starray' array.  There are 'stcnt' pointers in this
  873.  *   array.
  874.  *
  875.  *   This subroutine shows the files in the order that their pointers appear
  876.  *   in the array.
  877.  *
  878.  * RETURNS:
  879.  *   None.
  880.  *============================================================================*/
  881.  
  882. static void showarray(flags)
  883.  
  884. LSFLAGS  *flags;                    /* Pointer to flags structure */
  885.  
  886. {
  887.   STFILE  *p_stfile;                /* Pointer to STFILE structure */
  888.   STFILE **parray;                  /* Ptr to loc inside the array */
  889.   ulong    cnt;                     /* Count value */
  890.  
  891.   uint     dwidth;                  /* Width of display, in characters */
  892.   uint     ncols;                   /* No. of columns of filenames */
  893.   uint     nrows;                   /* No. of rows of filenames */
  894.   STFILE **prow;                    /* Ptr to array position that starts a row */
  895.   STFILE **pst;                     /* Ptr to next array position to display */
  896.   STFILE **endarray;                /* Ptr to last location in array */
  897.  
  898.   uint     coltab;                  /* Tab width: used to put names in buffer */
  899.   char    *pnext;                   /* Pointer inside output buffer */
  900.  
  901.   uint     rowcnt;                  /* Row counter, used inside loop */
  902.   uint     colcnt;                  /* Column counter, used inside loop */
  903.  
  904.   if (stcnt == 0L) return;          /* If no files to show: Quit */
  905.  
  906.  /*---------------------------------------------------------------------------*
  907.   * In case of multi-column format, determine the number of columns of
  908.   * filenames to display.
  909.   *---------------------------------------------------------------------------*/
  910.  
  911.   dwidth = getwidth();                   /* Find width of display */
  912.  
  913.   ncols = (dwidth + MINPAD) /            /* Find out how many columns we need */
  914.           (stlongest + MINPAD);
  915.   if (ncols == 0) ncols = 1;             /* Minimum no. of columns is one */
  916.  
  917. //printf("Display width      = %u\n", dwidth);
  918. //printf("stlongest          = %u\n", stlongest);
  919. //printf("No. of columns     = %u\n", ncols);
  920. //printf("Multi-column mode:   %s\n", flags->C_flag ? "TRUE" : "FALSE");
  921.  
  922.  /*---------------------------------------------------------------------------*
  923.   * If single-column format or if we only have one column to display anyway:
  924.   * just show each structure in the array:
  925.   *---------------------------------------------------------------------------*/
  926.  
  927.   if ((!flags->C_flag) ||           /* If we're not in multi-column mode, */
  928.       (ncols == 1))                 /* or we have only one column anyway: */
  929.   {
  930.      cnt = stcnt;                        /* Set cnt = no. of ptrs in array */
  931.      prow = starray;                     /* Point to beginning of array */
  932.      while (cnt > 0)                     /* For each pointer in the array: */
  933.      {
  934.         showfile((*prow)->fname,              /* Show it */
  935.                  (*prow)->fsize,
  936.                  (*prow)->fmode,
  937.                  (*prow)->fdate,
  938.                  (*prow)->ftime,
  939.                  flags);
  940.         cnt--;                                /* Decrement counter */
  941.         prow++;                               /* And point to next array element */
  942.      }
  943.  
  944.      return;                             /* Done: Return */
  945.   }
  946.  
  947.  /*---------------------------------------------------------------------------*
  948.   * ===> Display filenames in multi-column mode.  If we get here, we are in
  949.   *      multi-column mode and we have two or more columns to display.
  950.   *
  951.   * We want to show the files in the same order that you read a multi-
  952.   * column newpaper or other publication:  You first descend the leftmost
  953.   * column, then the next column over, etc.  But, we have to write an entire
  954.   * row at a time to the display.
  955.   *---------------------------------------------------------------------------*/
  956.  
  957.   coltab = stlongest + MINPAD;      /* Set tab increment inside output buffer */
  958.  
  959.   endarray = starray + stcnt - 1;   /* Point endarray to last array element */
  960.  
  961.  /*---------------------------------------------------------------------------*
  962.   * Determine number of rows to filenames, including partial rows
  963.   *---------------------------------------------------------------------------*/
  964.  
  965.   nrows = stcnt / ncols;            /* Determine no. of full rows of filenames */
  966.   if ( (stcnt % ncols) != 0)        /* If last column is only partially filled: */
  967.      nrows++;                       /* then we need to increment the row count */
  968.  
  969. //printf("No. of rows        = %u\n", nrows);
  970.  
  971.  /*---------------------------------------------------------------------------*
  972.   * Display filenames, one row at a time.  This algorithm assures that
  973.   * all columns, except possibly the last column, are full.  For a small
  974.   * number of filenames, you may not use the full width of the display.
  975.   * This is the same behaviour as that of PS/2 AIX's ls command.
  976.   *---------------------------------------------------------------------------*/
  977.  
  978.   prow = starray;                   /* Pointer to start of first row in array */
  979.   rowcnt = nrows;                   /* Set up row counter */
  980.   while (rowcnt != 0)               /* For each row of filenames: */
  981.   {
  982.      memset(buff, ' ', dwidth);          /* Fill output buffer with spaces */
  983.      buff[dwidth] = '\0';                /* and terminate it */
  984.  
  985.      pst = prow;                         /* Point pst to 1st name in row */
  986.      pnext = buff;                       /* Point pnext to start of output buffer */
  987.      colcnt = ncols;                     /* Set up column counter */
  988.      while (colcnt != 0)                 /* For each column position in the row: */
  989.      {
  990.         if (pst > endarray)                   /* If we're past the end of array: */
  991.            break;                                  /* We're done with this row */
  992.  
  993.         memcpy(pnext, (*pst)->fname,          /* Copy name to output buffer, but */
  994.                strlen((*pst)->fname));        /* don't copy the null terminator! */
  995.  
  996.         pst += nrows;                         /* Point pst to next column within row */
  997.         pnext += coltab;                      /* Point to next spot for name in buff */
  998.         colcnt--;                             /* Decrement column count */
  999.      }                                   /* end of loop for each column position */
  1000.  
  1001.      puts(buff);                         /* Write the row's names to stdout */
  1002.  
  1003.      prow++;                             /* Point prow to start of next row */
  1004.      rowcnt--;                           /* Decrement row count and continue */
  1005.   }                                 /* End of while loop for each row of filenames */
  1006.  
  1007.   return;                           /* Return */
  1008. }
  1009.  
  1010. /*============================================================================*
  1011.  * qcompName() - Compare files by name (case-insensitive)
  1012.  *
  1013.  * REMARKS:
  1014.  *   This subroutine is called by qsort() in response to a request by
  1015.  *   sortfiles() to sort the 'starray' of STFILE structure pointers.
  1016.  *============================================================================*/
  1017.  
  1018. static int qcompName(p1, p2)
  1019.  
  1020. STFILE  **p1;                       /* Pointer to first array element */
  1021. STFILE  **p2;                       /* Pointer to second array element */
  1022.  
  1023. {
  1024.   return(strcmpi( (*p1)->fname,     /* Return with result from comparison */
  1025.                  (*p2)->fname));
  1026. }
  1027.  
  1028.  
  1029. /*============================================================================*
  1030.  * qcompRName() - Compare files by name (case-insensitive)
  1031.  *
  1032.  * REMARKS:
  1033.  *   This subroutine is called by qsort() in response to a request by
  1034.  *   sortfiles() to sort the 'starray' of STFILE structure pointers.
  1035.  *============================================================================*/
  1036.  
  1037. static int qcompRName(p1, p2)
  1038.  
  1039. STFILE  **p1;                       /* Pointer to first array element */
  1040. STFILE  **p2;                       /* Pointer to second array element */
  1041.  
  1042. {
  1043.   return(strcmpi( (*p2)->fname,     /* Return with result from comparison */
  1044.                  (*p1)->fname));
  1045. }
  1046.  
  1047.  
  1048. /*============================================================================*
  1049.  * qcompTime() - Compare files by time/date stamp.
  1050.  *
  1051.  * REMARKS:
  1052.  *   This subroutine is called by qsort() in response to a request by
  1053.  *   sortfiles() to sort the 'starray' of STFILE structure pointers.
  1054.  *============================================================================*/
  1055.  
  1056. static int qcompTime(p1, p2)
  1057.  
  1058. STFILE  **p1;                       /* Pointer to first array element */
  1059. STFILE  **p2;                       /* Pointer to second array element */
  1060.  
  1061. {
  1062.   if ((*p1)->fdatetime == (*p2)->fdatetime) return(0);
  1063.   if ((*p1)->fdatetime >  (*p2)->fdatetime) return(-1);
  1064.                                             return(1);
  1065. }
  1066.  
  1067.  
  1068. /*============================================================================*
  1069.  * qcompRTime() - Compare files by time/date stamp.
  1070.  *
  1071.  * REMARKS:
  1072.  *   This subroutine is called by qsort() in response to a request by
  1073.  *   sortfiles() to sort the 'starray' of STFILE structure pointers.
  1074.  *============================================================================*/
  1075.  
  1076. static int qcompRTime(p1, p2)
  1077.  
  1078. STFILE  **p1;                       /* Pointer to first array element */
  1079. STFILE  **p2;                       /* Pointer to second array element */
  1080.  
  1081. {
  1082.   if ((*p1)->fdatetime == (*p2)->fdatetime) return(0);
  1083.   if ((*p1)->fdatetime <  (*p2)->fdatetime) return(-1);
  1084.                                             return(1);
  1085. }
  1086.  
  1087.  
  1088. /*============================================================================*
  1089.  * qcompSize() - Compare files by size.
  1090.  *
  1091.  * REMARKS:
  1092.  *   This subroutine is called by qsort() in response to a request by
  1093.  *   sortfiles() to sort the 'starray' of STFILE structure pointers.
  1094.  *============================================================================*/
  1095.  
  1096. static int qcompSize(p1, p2)
  1097.  
  1098. STFILE  **p1;                       /* Pointer to first array element */
  1099. STFILE  **p2;                       /* Pointer to second array element */
  1100.  
  1101. {
  1102.   if ((*p1)->fsize == (*p2)->fsize) return(0);
  1103.   if ((*p1)->fsize <  (*p2)->fsize) return(-1);
  1104.                                     return(1);
  1105. }
  1106.  
  1107.  
  1108. /*============================================================================*
  1109.  * qcompRSize() - Compare files by size.
  1110.  *
  1111.  * REMARKS:
  1112.  *   This subroutine is called by qsort() in response to a request by
  1113.  *   sortfiles() to sort the 'starray' of STFILE structure pointers.
  1114.  *============================================================================*/
  1115.  
  1116. static int qcompRSize(p1, p2)
  1117.  
  1118. STFILE  **p1;                       /* Pointer to first array element */
  1119. STFILE  **p2;                       /* Pointer to second array element */
  1120.  
  1121. {
  1122.   if ((*p1)->fsize == (*p2)->fsize) return(0);
  1123.   if ((*p1)->fsize >  (*p2)->fsize) return(-1);
  1124.                                     return(1);
  1125. }
  1126.  
  1127. /*============================================================================*
  1128.  * getwidth() - Gets width of display.
  1129.  *
  1130.  * REMARKS:
  1131.  *   This subroutine gets the width of the current display.  If this width
  1132.  *   is wider than MAXWIDTH, it returns MAXWIDTH.
  1133.  *============================================================================*/
  1134.  
  1135. static uint getwidth()
  1136. {
  1137.   int  rc;                          /* Return code storage */
  1138.   VIOMODEINFO vmi;                  /* Return info from vio */
  1139.   uint dwidth;                      /* Width of display */
  1140.  
  1141.  /*---------------------------------------------------------------------------*
  1142.   * Get width of current display
  1143.   *---------------------------------------------------------------------------*/
  1144.  
  1145.   rc = VioGetMode(&vmi, 0);         /* Get video mode */
  1146.  
  1147.   if (rc != 0)                      /* If error: */
  1148.      dwidth = 80;                      /* Assume 80 cols */
  1149.   else                              /* Else no error: */
  1150.   {                                    /* Extract info from buffer */
  1151.      dwidth = vmi.col;
  1152.   }
  1153.  
  1154.  /*---------------------------------------------------------------------------*
  1155.   * Adjust display width and return it
  1156.   *---------------------------------------------------------------------------*/
  1157.  
  1158.   dwidth--;                         /* Subtract 1, to allow for newline char */
  1159.  
  1160.   if (dwidth == 0)                  /* Just to be sure we never return 0 */
  1161.      dwidth = 1;
  1162.  
  1163.   if (dwidth > MAXWIDTH)            /* If larger than our internal maximum: */
  1164.      dwidth = MAXWIDTH;                  /* Then chop it down to size */
  1165.   return(dwidth);                   /* Return the width */
  1166. }
  1167.