home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_200 / 264_01 / ls.c < prev    next >
Text File  |  1979-12-31  |  16KB  |  661 lines

  1. /*
  2.  * ls - list contents of directory
  3.  *
  4.  * Usage: ls [-alrstxzAR1] [path...]
  5.  *
  6.  * Flags:
  7.  * -a   list all files, including hidden and system files, ".", and ".."
  8.  * -l   long listing form (extra information)
  9.  * -r   reverse order of sorting
  10.  * -s   display size of each file in kilobytes, and total for each directory
  11.  * -t   sort by time/date (latest first)
  12.  * -x    sort by extension
  13.  * -z    sort by size
  14.  * -A   list all files except "." and ".."
  15.  * -R    list subdirectories recursively
  16.  * -1    display 1 entry per line of short form
  17.  *
  18.  * This program is in the public domain.
  19.  * David MacKenzie
  20.  * 6522 Elgin Lane
  21.  * Bethesda, MD 20817
  22.  *
  23.  * Latest revision: 05/07/88
  24.  */
  25.  
  26. #include <ctype.h>
  27. #ifndef tolower
  28. #define tolower(s) _tolower(s)
  29. #endif
  30. #include "getdir.h"
  31.  
  32. /* Arbitrary internal limit on filename path length. */
  33. #define MAXPATH 125
  34.  
  35. /* For dynamically allocating space to store contents of each directory. */
  36. #define ENTRIES_INCR 25
  37.  
  38. /*
  39.  * isdir(e)
  40.  *     struct sablk *e;
  41.  */
  42. #define isdir(e) ((e)->sa_attrib & FA_DIREC)
  43.  
  44. /*
  45.  * kbytes(b)
  46.  *     int b;
  47.  * Macro to return the number of kilobytes (rounded up) taken by a given
  48.  * number of bytes.
  49.  */
  50. #define kbytes(b) ((b) / 1024 + ((b) % 1024 != 0))
  51.  
  52. /* Shortened format for array in memory, to save space. */
  53. struct sablk {
  54.     char    sa_attrib;
  55.     unsigned sa_ftime;
  56.     unsigned sa_fdate;
  57.     unsigned long sa_fsize;
  58.     char   *sa_fname;
  59. };
  60.  
  61. /*
  62.  * Linked list of names of directories to list recursively (automatically
  63.  * initialized to NULL).
  64.  */
  65. struct dirs {
  66.     char   *dirs_name;
  67.     struct dirs *dirs_next;
  68. }      *dirlist;
  69.  
  70. char    dirbuf[512];        /* Buffer for disk input. */
  71.  
  72. char   *monthtab[] = {
  73.     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  74.     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  75. };
  76.  
  77. int     aflag = 0;        /* Display . and .. */
  78. int     lflag = 0;        /* Long listing form. */
  79. int     rflag = 0;        /* Reverse order of sort. */
  80. int     sflag = 0;        /* Display file sizes. */
  81. int     tflag = 0;        /* Sort by time/date. */
  82. int     xflag = 0;        /* Sort by extension. */
  83. int     zflag = 0;        /* Sort by size. */
  84. int     Aflag = 0;        /* Display hidden and system files. */
  85. int     Rflag = 0;        /* Display subdirectories recursively. */
  86. int     oneflag = 0;        /* One entry per line in short listing. */
  87.  
  88. int     listingargs;        /* Boolean for sort comparison function. */
  89.  
  90. char   *
  91. Malloc(), *Realloc();
  92.  
  93. main(argc, argv)
  94.     int     argc;
  95.     char  **argv;
  96. {
  97.     void    listfiles(), listdir();
  98.     int     entcmp();
  99.     struct sablk *initarray(), *saveent();
  100.     char   *basename(), *index();
  101.     struct dirs *dirp;
  102.     struct ffblk *dir = (struct ffblk *) dirbuf;
  103.     struct sablk *save_array;    /* Array of dir entries for sorting. */
  104.     int     last_entry = -1;    /* Last valid entry of save_array. */
  105.     int     last_normal;    /* Last non-directory entry. */
  106.     char    pathbuf[MAXPATH];    /* Path of globbed arguments. */
  107.     char   *tail;        /* Tail component of pathbuf. */
  108.     int     optind;        /* Loop index. */
  109.  
  110.     for (optind = 1; optind < argc && *argv[optind] == '-'; ++optind) {
  111.     while (*++argv[optind]) {
  112.         switch (*argv[optind]) {
  113.         case 'a':
  114.         aflag = Aflag = 1;
  115.         break;
  116.         case 'l':
  117.         lflag = 1;
  118.         break;
  119.         case 'r':
  120.         rflag = 1;
  121.         break;
  122.         case 's':
  123.         sflag = 1;
  124.         break;
  125.         case 't':
  126.         tflag = 1;
  127.         break;
  128.         case 'x':
  129.         xflag = 1;
  130.         break;
  131.         case 'z':
  132.         zflag = 1;
  133.         break;
  134.         case 'A':
  135.         Aflag = 1;
  136.         break;
  137.         case 'R':
  138.         Rflag = 1;
  139.         break;
  140.         case '1':
  141.         oneflag = 1;
  142.         break;
  143.         default:
  144.         printf("Usage: ls [-alrstxzAR1] [path...]\n");
  145.         exit(1);
  146.         break;
  147.         }
  148.     }
  149.     }
  150.  
  151.     setdta(dirbuf);        /* Set disk transfer area. */
  152.  
  153.     if (optind == argc)
  154.     argv[argc++] = ".";
  155.  
  156.     save_array = initarray();
  157.     for (; optind < argc; ++optind) {
  158.     /*
  159.      * Normally, when we say getfirst("dir") or getnext("dir"), where
  160.      * "dir" is the name of a directory, they return "dir" back to us
  161.      * along with its attributes, by which we discover that it has the
  162.      * FA_DIREC attribute.  However, for certain pseudo-directories
  163.      * (which are real, regular directories on Unix), if we say, for
  164.      * instance, getfirst("\"), we get nothing back - no matches. To work
  165.      * around that problem, we have this special test for the problem
  166.      * pseudo-directories. 
  167.      */
  168.     if (isroot(argv[optind]) || isdot(argv[optind])) {
  169.         (void) strcpy(dir->ff_fname, argv[optind]);
  170.         dir->ff_attrib = FA_DIREC;
  171.         dir->ff_ftime = dir->ff_fdate = 0;
  172.         dir->ff_fsize = 0L;
  173.         save_array = saveent(save_array, dir, ++last_entry);
  174.     } else if (!getfirst(argv[optind], FA_ALL)) {
  175.         /*
  176.          * Copy any leading drive and/or path into pathbuf because the
  177.          * MS-DOS globbing routines return only the base of the
  178.          * filenames. 
  179.          */
  180.         (void) strcpy(pathbuf, argv[optind]);
  181.         tail = basename(pathbuf);
  182.  
  183.         do {
  184.         if (dir->ff_fname[0] != '.' &&
  185.             (Aflag ||
  186.             (dir->ff_attrib & (FA_HIDDEN | FA_SYSTEM)) == 0)) {
  187.             (void) strcpy(tail, dir->ff_fname);
  188.             (void) strcpy(dir->ff_fname, pathbuf);
  189.             save_array = saveent(save_array, dir, ++last_entry);
  190.         }
  191.         } while (!getnext(argv[optind], FA_ALL));
  192.     } else if (index(argv[optind], '*') || index(argv[optind], '?')) {
  193.         printf("No match.\n");
  194.         exit(1);
  195.     }
  196.     }
  197.     listingargs = 1;
  198.     qsort((char *) save_array, last_entry + 1, sizeof(struct sablk),
  199.     entcmp);
  200.  
  201.     /* Find last non-directory entry. */
  202.     for (last_normal = last_entry;
  203.     last_normal >= 0 && isdir(&save_array[last_normal]);
  204.     --last_normal);
  205.  
  206.     /* First the regular listing... */
  207.     listfiles(save_array, last_normal);
  208.  
  209.     /* ...then the directories. */
  210.     if (last_normal < last_entry) {
  211.     if (last_normal++ >= 0)
  212.         putchar('\n');
  213.     for (;;) {
  214.         listdir(save_array[last_normal].sa_fname, last_entry > 0);
  215.         /*
  216.          * We avoid a lot of memory waste by not using recursive function
  217.          * calls to implement recursive listing. 
  218.          */
  219.         while (dirlist) {
  220.         /* Chop head of list off and show it. */
  221.         dirp = dirlist;
  222.         dirlist = dirp->dirs_next;
  223.         putchar('\n');
  224.         /* This call might add a new head to the list! */
  225.         listdir(dirp->dirs_name, 1);
  226.         free(dirp->dirs_name);
  227.         free((char *) dirp);
  228.         }
  229.         if (++last_normal > last_entry)
  230.         break;
  231.         putchar('\n');
  232.     }
  233.     }
  234.     exit(0);
  235. }
  236.  
  237. /*
  238.  * Display contents of one directory.
  239.  */
  240. void
  241. listdir(name, label)
  242.     char   *name;        /* Name of directory to display. */
  243.     int     label;        /* Diplay the directory's name:? */
  244. {
  245.     int     entcmp();
  246.     void    listfiles(), addsubdir();
  247.     char   *concat(), *savestr();
  248.     struct sablk *save_array;
  249.     int     last_entry;
  250.     int     total_kbytes;
  251.     int     i;            /* Loop index. */
  252.  
  253.     total_kbytes = getdir(name, &save_array, &last_entry);
  254.  
  255.     if (label)
  256.     printf("%s:\n", name);
  257.     if (lflag || sflag)
  258.     printf("total %d\n", total_kbytes);
  259.     if (last_entry == -1)
  260.     return;
  261.  
  262.     listingargs = 0;
  263.     qsort((char *) save_array, last_entry + 1, sizeof(struct sablk),
  264.     entcmp);
  265.     listfiles(save_array, last_entry);
  266.  
  267.     if (Rflag)
  268.     for (i = last_entry; i >= 0; --i)
  269.         if (isdir(&save_array[i]) && save_array[i].sa_fname[0] != '.')
  270.         /* Add entry to list of subdirectories. */
  271.         addsubdir(concat(name, save_array[i].sa_fname));
  272.     for (i = 0; i <= last_entry; ++i)
  273.     free(save_array[i].sa_fname);
  274.     free(save_array);
  275. }
  276.  
  277. /*
  278.  * Read contents of directory into array and return size in kilobytes.
  279.  */
  280. getdir(name, psave_array, plast_entry)
  281.     char   *name;
  282.     struct sablk **psave_array;
  283.     int    *plast_entry;
  284. {
  285.     char   *concat();
  286.     struct sablk *initarray(), *saveent();
  287.     struct ffblk *dir = (struct ffblk *) dirbuf;
  288.     struct sablk *save_array;    /* Array of dir entries for sorting. */
  289.     int     last_entry = -1;    /* Last valid entry of save_array. */
  290.     int     total_kbytes = 0;    /* Ignored if sflag and lflag not given. */
  291.     char   *nametmp;
  292.     int     i;            /* Temporary. */
  293.  
  294.     save_array = initarray();
  295.     nametmp = concat(name, "*.*");
  296.  
  297.     for (i = getfirst(nametmp, FA_ALL); !i; i = getnext(nametmp, FA_ALL))
  298.     if ((aflag || dir->ff_fname[0] != '.') &&
  299.         (Aflag || (dir->ff_attrib & (FA_HIDDEN | FA_SYSTEM)) == 0)) {
  300.         save_array = saveent(save_array, dir, ++last_entry);
  301.         if (sflag || lflag)
  302.         total_kbytes += kbytes(dir->ff_fsize);
  303.     }
  304.     if (last_entry == -1) {
  305.     (void) free(save_array);
  306.     *psave_array = (void *) 0;
  307.     } else
  308.     *psave_array = save_array;
  309.     *plast_entry = last_entry;
  310.     return total_kbytes;
  311. }
  312.  
  313. void
  314. listfiles(save_array, last_entry)
  315.     struct sablk *save_array;
  316.     int     last_entry;
  317. {
  318.     if (last_entry < 0)
  319.     return;
  320.  
  321.     if (lflag)
  322.     longlist(save_array, last_entry);
  323.     else
  324.     shortlist(save_array, last_entry);
  325. }
  326.  
  327. /*
  328.  * List files in verbose format.
  329.  */
  330. longlist(save_array, last_entry)
  331.     struct sablk *save_array;
  332.     int     last_entry;
  333. {
  334.     int     i;
  335.  
  336.     for (i = 0; i <= last_entry; ++i)
  337.     longent(&save_array[i]);
  338. }
  339.  
  340. /*
  341.  * List files in table format.
  342.  */
  343. shortlist(save_array, last_entry)
  344.     struct sablk *save_array;
  345.     int     last_entry;
  346. {
  347.     int     width = 0;        /* Length of longest filename. */
  348.     int     columns = 0;    /* # of columns to show. */
  349.     int     lines = 0;        /* # of rows to show. */
  350.     int     w;            /* Temporary. */
  351.     int     x;            /* Temporary. */
  352.     int     i;            /* Loop index. */
  353.     int     j;            /* Loop index. */
  354.  
  355.     if (oneflag)
  356.     columns = 1;
  357.     else {
  358.     /*
  359.      * Find the length of the longest filename.  Add one character for
  360.      * the '\' at the ends of directories. 
  361.      */
  362.     for (i = 0; i <= last_entry; ++i)
  363.         if ((w = strlen(save_array[i].sa_fname) +
  364.             (isdir(&save_array[i]) != 0)) > width)
  365.         width = w;
  366.     /* Round off to next tab stop and adjust for kbytes field. */
  367.     width = (width & ~7) + 8 + sflag * 5;
  368.     if (width > 80)
  369.         columns = 1;
  370.     else
  371.         columns = 80 / width;
  372.     }
  373.  
  374.     lines = (last_entry + columns) / columns;
  375.     for (i = 0; i < lines; ++i) {
  376.     for (j = 0; j < columns; ++j) {
  377.         x = j * lines + i;
  378.         shortent(&save_array[x]);
  379.  
  380.         if (x + lines > last_entry) {
  381.         putchar('\n');
  382.         break;
  383.         }
  384.         w = strlen(save_array[x].sa_fname) + sflag * 5 +
  385.         (isdir(&save_array[x]) != 0);
  386.         while (w < width) {
  387.         w = (w & ~7) + 8;
  388.         putchar('\t');
  389.         }
  390.     }
  391.     }
  392. }
  393.  
  394. /*
  395.  * Long listing of one file.
  396.  */
  397. longent(ent)
  398.     struct sablk *ent;
  399. {
  400.     int     month;
  401.  
  402.     if (sflag)
  403.     printf("%4d ", kbytes(ent->sa_fsize));
  404.  
  405.     month = ((ent->sa_fdate >> 5) & 15) - 1;
  406.     if (month < 0 || month > 11)
  407.     month = 11;
  408.  
  409.     /* Aztec C scrambles the minutes if this is 1 printf. */
  410.     printf("%c%c%c%c%c %9lu %s %02u 19%02u ",
  411.     ent->sa_attrib & FA_DIREC  ? 'd' : '-',
  412.     ent->sa_attrib & FA_SYSTEM ? 's' : '-',
  413.     ent->sa_attrib & FA_HIDDEN ? 'h' : '-',
  414.     ent->sa_attrib & FA_RDONLY ? 'r' : '-',
  415.     ent->sa_attrib & FA_ARCH   ? 'a' : '-',
  416.     ent->sa_fsize,
  417.     monthtab[month],    /* Month. */
  418.     ent->sa_fdate & 31,    /* Day. */
  419.     ((ent->sa_fdate >> 9) & 127) + 80);    /* Year. */
  420.     printf("%2u:%02u %-13s\n",
  421.     (ent->sa_ftime >> 11) & 31,    /* Hour. */
  422.     (ent->sa_ftime >> 5) & 63,    /* Minute. */
  423.     ent->sa_fname);
  424. }
  425.  
  426. /*
  427.  * Display short form of file.
  428.  */
  429. shortent(ent)
  430.     struct sablk *ent;
  431. {
  432.     if (sflag)
  433.     printf("%4d ", kbytes(ent->sa_fsize));
  434.  
  435.     printf("%s", ent->sa_fname);
  436.     if (isdir(ent))
  437.     putchar('\\');
  438. }
  439.  
  440. struct sablk *
  441. initarray()
  442. {
  443.     return (struct sablk *)
  444.     Malloc(sizeof(struct sablk) * ENTRIES_INCR);
  445. }
  446.  
  447. struct sablk *
  448. saveent(save_array, dir, last_entry)
  449.     struct sablk *save_array;
  450.     struct ffblk *dir;
  451.     int     last_entry;
  452. {
  453.     char   *normalize(), *savestr();
  454.  
  455.     if (last_entry > 0 && last_entry % ENTRIES_INCR == 0)
  456.     save_array = (struct sablk *)
  457.         Realloc(save_array, sizeof(struct sablk) *
  458.         (last_entry + ENTRIES_INCR));
  459.  
  460.     save_array[last_entry].sa_attrib = dir->ff_attrib;
  461.     save_array[last_entry].sa_ftime = dir->ff_ftime;
  462.     save_array[last_entry].sa_fdate = dir->ff_fdate;
  463.     save_array[last_entry].sa_fsize = dir->ff_fsize;
  464.     save_array[last_entry].sa_fname = savestr(normalize(dir->ff_fname));
  465.     return save_array;
  466. }
  467.  
  468. /*
  469.  * Return file stuck on to the end of dir.
  470.  */
  471. char   *
  472. concat(dir, file)
  473.     char   *dir, *file;
  474. {
  475.     static char catbuf[MAXPATH];
  476.     int     i;
  477.  
  478.     if (strlen(dir) + strlen(file) + 2 > sizeof(catbuf)) {
  479.     printf("ls: filename too long\n");
  480.     exit(1);
  481.     }
  482.     if (!strcmp(dir, "") || !strcmp(dir, ".")) {
  483.     (void) strcpy(catbuf, file);
  484.     } else {
  485.     (void) strcpy(catbuf, dir);
  486.     i = strlen(dir) - 1;
  487.     if (dir[i] != '\\' && dir[i] != ':' && file[0] != '\\')
  488.         (void) strcat(catbuf, "\\");
  489.     strcat(catbuf, file);
  490.     }
  491.     return catbuf;
  492. }
  493.  
  494. char   *
  495. savestr(s)
  496.     char   *s;
  497. {
  498.     char   *t;
  499.  
  500.     t = Malloc(strlen(s) + 1);
  501.     (void) strcpy(t, s);
  502.     return t;
  503. }
  504.  
  505. /*
  506.  * Convert uppercase letters to lowercase, and non-graphic characters to '?'.
  507.  * Return the argument.
  508.  */
  509.  
  510. char   *
  511. normalize(s)
  512.     char   *s;
  513. {
  514.     char   *t;
  515.  
  516.     for (t = s; *t; ++t)
  517.     if (!isascii(*t) || !isgraph(*t))
  518.         *t = '?';
  519.     else if (isupper(*t) && *t != '_')
  520.         /* Aztec C's ctype thinks that isupper('_') is true . . . */
  521.         *t = tolower(*t);
  522.     return s;
  523. }
  524.  
  525. /*
  526.  * Comparison routine for qsort().
  527.  */
  528.  
  529. entcmp(e1, e2)
  530.     struct sablk *e1, *e2;
  531. {
  532.     char   *index();
  533.     int     result;
  534.  
  535.     /* Command line argument directories are sorted to the end. */
  536.     if (listingargs) {
  537.     if (isdir(e1) && !isdir(e2))
  538.         return 1;
  539.     else if (!isdir(e1) && isdir(e2))
  540.         return -1;
  541.     /*
  542.      * Either both or neither are command line directories; do regular
  543.      * sort. 
  544.      */
  545.     }
  546.     if (zflag)
  547.     result = e1->sa_fsize < e2->sa_fsize ? -1 :
  548.         e1->sa_fsize > e2->sa_fsize ? 1 :
  549.         strcmp(e1->sa_fname, e2->sa_fname);
  550.     else if (tflag)
  551.     result = e1->sa_fdate < e2->sa_fdate ? 1 :
  552.         e1->sa_fdate > e2->sa_fdate ? -1 :
  553.         e1->sa_ftime < e2->sa_ftime ? 1 :
  554.         e1->sa_ftime > e2->sa_ftime ? -1 :
  555.         strcmp(e1->sa_fname, e2->sa_fname);
  556.     else if (xflag) {
  557.     char   *ex1 = index(e1->sa_fname, '.');
  558.     char   *ex2 = index(e2->sa_fname, '.');
  559.  
  560.     result = !ex1 && ex2 ? -1 :
  561.         ex1 && !ex2 ? 1 :
  562.         ex1 && ex2 ? strcmp(ex1, ex2) :
  563.         strcmp(e1->sa_fname, e2->sa_fname);
  564.     } else
  565.     result = strcmp(e1->sa_fname, e2->sa_fname);
  566.     return rflag ? -result : result;
  567. }
  568.  
  569. /*
  570.  * Return true if the path has the form "d:" or "\" or "d:\".
  571.  */
  572. isroot(p)
  573.     char   *p;
  574. {
  575.     int     i = strlen(p);
  576.  
  577.     return i == 2 && p[1] == ':' ||
  578.     !strcmp(p, "\\") ||
  579.     i == 3 && !strcmp(p + 1, ":\\");
  580. }
  581.  
  582. /*
  583.  * Return true if the path has the form:
  584.  * ".." or "d:.." or "." or "d:." or "\." or "d:\.".
  585.  */
  586. isdot(p)
  587.     char   *p;
  588. {
  589.     int     i = strlen(p);
  590.  
  591.     return !strcmp(p, "..") ||
  592.     i == 4 && !strcmp(p + 1, ":..") ||
  593.     !strcmp(p, ".") ||
  594.     i == 3 && !strcmp(p + 1, ":.") ||
  595.     !strcmp(p, "\\.") ||
  596.     i == 4 && !strcmp(p + 1, ":\\.");
  597. }
  598.  
  599. /*
  600.  * Return a pointer to the base of path p, e.g. with any drive
  601.  * and leading path removed.
  602.  */
  603. char   *
  604. basename(p)
  605.     char   *p;
  606. {
  607.     char   *tail;
  608.  
  609.     for (tail = p; *p; ++p)
  610.     if (*p == ':' || *p == '\\')
  611.         tail = p + 1;
  612.     return tail;
  613. }
  614.  
  615. char   *
  616. Malloc(n)
  617.     unsigned n;
  618. {
  619.     char   *malloc();
  620.     char   *p;
  621.  
  622.     p = malloc(n);
  623.     if (!p) {
  624.     perror("malloc");
  625.     exit(1);
  626.     }
  627.     return p;
  628. }
  629.  
  630. char   *
  631. Realloc(p, n)
  632.     char   *p;
  633.     unsigned n;
  634. {
  635.     char   *realloc();
  636.  
  637.     p = realloc(p, n);
  638.     if (!p) {
  639.     perror("realloc");
  640.     exit(1);
  641.     }
  642.     return p;
  643. }
  644.  
  645. /*
  646.  * Add directory d to list of subdirectories to list recursively.
  647.  */
  648. void
  649. addsubdir(d)
  650.     char   *d;
  651. {
  652.     char   *savestr();
  653.     struct dirs *dirp;
  654.  
  655.     /* Make the new entry the head of the list. */
  656.     dirp = (struct dirs *) Malloc(sizeof(struct dirs));
  657.     dirp->dirs_name = savestr(d);
  658.     dirp->dirs_next = dirlist;
  659.     dirlist = dirp;
  660. }
  661.