home *** CD-ROM | disk | FTP | other *** search
/ The Developer Connection…ice Driver Kit for OS/2 3 / DEV3-D1.ISO / source / util2src / ls.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-09-28  |  48.2 KB  |  1,187 lines

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