home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume8 / hier / hier.c < prev    next >
C/C++ Source or Header  |  1987-02-11  |  8KB  |  343 lines

  1. /*
  2.  * Show file system hierarchy.
  3.  *
  4.  * Usage: see below.
  5.  *
  6.  * Unlike ls(1), it sorts files across lines rather than down columns.
  7.  * Fixing this would be non-trivial, involving saving filenames until it
  8.  * was time to dump them.
  9.  *
  10.  * Also, due to the behavior of sftw() (like ftw()), it never lists "." and
  11.  * ".." files.
  12.  *
  13.  * Warning:  If you use ftw() instead of sftw(), -a option will stop working.
  14.  *
  15.  * Warning:  If you use ftw() instead of sftw(), a bug will appear.  This is
  16.  * because two calls in a row from ftw() to OneFile() may be made for ordinary
  17.  * files, where the second is in a directory a level above the first one.
  18.  * OneFile() would have to check to see if each ordinary file's path is
  19.  * different than the previous one's, indicating a change of directory level.
  20.  */
  21.  
  22. #include <sys/types.h>
  23. #include <sys/stat.h>
  24. #include <stdio.h>
  25. #include <ftw.h>
  26. #ifdef    BSD
  27. #include <sys/dir.h>            /* for DIRSIZ */
  28. #include <strings.h>
  29. #else
  30. #include <sys/ndir.h>            /* for DIRSIZ */
  31. #include <string.h>
  32. #endif    /* BSD */
  33.  
  34.  
  35. /*********************************************************************
  36.  * MISCELLANEOUS GLOBAL VALUES:
  37.  */
  38.  
  39. #define    PROC                /* null; easy to find procs */
  40. #define    FALSE    0
  41. #define    TRUE    1
  42. #define    CHNULL    ('\0')
  43. #define    CPNULL    ((char *) NULL)
  44. #define    REG    register
  45.  
  46. char *usage[] = {
  47.     "usage: %s [-adp] [-c columns] [-i indent] [directories...]",
  48.     "-a include directories and files whose names start with \".\"",
  49.     "-d show directories only",
  50.     "-p print filenames packed onto lines, not aligned",
  51.     "-c set width of display (or use COLUMNS env variable; default = 80)",
  52.     "-i set indentation per level; default = 4",
  53.     "Does current directory by default.",
  54.     CPNULL,
  55. };
  56.  
  57. char    *myname;            /* how program was invoked    */
  58. int    aflag    = FALSE;        /* -a (all files) option    */
  59. int    dflag    = FALSE;        /* -d (directories) option    */
  60. int    pflag    = FALSE;        /* -p (packed filenames) option    */
  61. int    columns    = 0;            /* from -c option or env    */
  62. int    indent    = 0;            /* from -i option or default    */
  63.  
  64. #define    COLUMNS    80            /* width of display        */
  65. #define    INDENT     4            /* per directory level        */
  66.  
  67. int    startlen;            /* of current arg (filename)    */
  68. int    nextcol = 0;            /* column in output line    */
  69.  
  70.  
  71. /************************************************************************
  72.  * M A I N
  73.  *
  74.  * Initialize, then call sftw() for each given filename after clearing
  75.  * global startlen to indicate a new starting file.  When done, if global
  76.  * nextcol != 0 (in the middle of an output line), finish the last line.
  77.  */
  78.  
  79. PROC main (argc, argv)
  80.     int    argc;
  81.     char    **argv;
  82. {
  83. extern    int    optind;            /* from getopt()    */
  84. extern    char    *optarg;        /* from getopt()    */
  85. REG    int    option;            /* option "letter"    */
  86.     char    *argdef = ".";        /* default argument    */
  87.     char    *colstr;        /* from environment    */
  88.  
  89.     char    *getenv();
  90.     int    OneFile();
  91.  
  92. /* #define DEPTH (_NFILE - 5)        /* for ftw(), but not sftw() */
  93.  
  94. /*
  95.  * PARSE ARGUMENTS:
  96.  */
  97.  
  98.     myname = *argv;
  99.  
  100.     while ((option = getopt (argc, argv, "adpc:i:")) != EOF)
  101.     {
  102.         switch (option)
  103.         {
  104.         case 'a':    aflag    = TRUE;            break;
  105.         case 'd':    dflag    = TRUE;            break;
  106.         case 'p':    pflag    = TRUE;            break;
  107.         case 'c':    columns    = atoi (optarg);    break;
  108.         case 'i':    indent    = atoi (optarg);    break;
  109.         default:    Usage();
  110.         }
  111.     }
  112.  
  113.     if (dflag && pflag)
  114.         Error ("-d and -p don't make sense together");
  115.  
  116. /*
  117.  * FINISH INITIALIZING:
  118.  */
  119.  
  120.     if ((columns == 0)                /* no value given */
  121.      && (((colstr = getenv ("COLUMNS")) == CPNULL)    /* undefined      */
  122.       || ((columns = atoi (colstr)) == 0)))      /* defined null or zero */
  123.     {
  124.         columns = COLUMNS;        /* use default */
  125.     }
  126.  
  127.     if (indent == 0)        /* no value given */
  128.         indent = INDENT;        /* use default      */
  129.  
  130.     argc -= optind;            /* skip options    */
  131.     argv += optind;
  132.  
  133.     if (argc == 0)            /* no filenames given */
  134.     {
  135.         argc = 1;
  136.         argv = & argdef;        /* use default */
  137.     }
  138.  
  139. /*
  140.  * WALK EACH FILE TREE:
  141.  */
  142.  
  143.     while (argc-- > 0)
  144.     {
  145.         startlen = 0;
  146.  
  147.         if (sftw (*argv, OneFile, aflag))
  148.         Error ("file tree walk failed for file \"%s\"", *argv);
  149.  
  150.         argv++;
  151.     }
  152.  
  153.     if (nextcol)            /* last line not finished */
  154.         putchar ('\n');
  155.  
  156.     exit (0);
  157.  
  158. } /* main */
  159.  
  160.  
  161. /************************************************************************
  162.  * O N E   F I L E
  163.  *
  164.  * Called from sftw() to handle (print) one file, given a filename (starting
  165.  * file plus sub-file part) and ftw() file type.  Always returns zero (all
  166.  * is well, keep going).
  167.  *
  168.  * It's messy because of the need to print multiple non-directory basenames
  169.  * on one line.  Uses global startlen to save time figuring depth beyond
  170.  * starting file.  If currently zero, this is the starting file; print the
  171.  * fullname, on a line alone, with no indent.
  172.  *
  173.  * Use globals startlen, indent, columns, and nextcol.
  174.  */
  175.  
  176. PROC int OneFile (filename, statp, type)
  177.     char    *filename;    /* name        */
  178.     struct    stat *statp;    /* info, unused    */
  179.     int    type;        /* ftw() type    */
  180. {
  181. REG    char    *basename;    /* part of filename */
  182.  
  183. /*
  184.  * PRINTING FORMATS (matching ftw() types):
  185.  */
  186.  
  187. static    char    *FMT_D   = "%s/\n";
  188. static    char    *FMT_DNR = "%s/ (not readable)\n";
  189. static    char    *FMT_NS  = "%s (not stat'able)\n";
  190. static    char    *FMT_F1     = "%s\n";        /* for starting file */
  191. static    char    *FMT_F2     = "%-*s";        /* for sub-file         */
  192.  
  193. #ifdef    BSD
  194. #define    FILEWIDTH MAXNAMLEN            /* for FMT_F2 */
  195. #else
  196. #define    FILEWIDTH (DIRSIZ + 1)            /* for FMT_F2 */
  197. #endif    /* BSD */
  198. REG    int    filewidth = FILEWIDTH;        /* if ! pflag */
  199.  
  200. #define    NEWLINE     { putchar ('\n'); nextcol = 0; }  /* for speed and clarity */
  201.  
  202. /*
  203.  * OPTIONALLY IGNORE NON-DIRECTORY (even if named as an input argument):
  204.  */
  205.  
  206.     if (dflag && (type == FTW_F))
  207.         return (0);
  208.  
  209. /*
  210.  * HANDLE STARTING FILE:
  211.  */
  212.  
  213.     if (startlen == 0)
  214.     {
  215.         startlen = strlen (filename);    /* set for later */
  216.  
  217.         if (nextcol)        /* end previous line */
  218.         NEWLINE;        /* sets nextcol == 0 */
  219.  
  220.         printf ((type == FTW_D)   ? FMT_D    :
  221.             (type == FTW_DNR) ? FMT_DNR    :
  222.             (type == FTW_NS)  ? FMT_NS    : FMT_F1, filename);
  223.  
  224.         return (0);
  225.     }
  226.  
  227. /*
  228.  * SET BASENAME FOR ALL OTHER TYPES:
  229.  */
  230.  
  231.     basename = ((basename = strrchr (filename, '/')) == CPNULL) ?
  232.            filename : (basename + 1);    /* past "/" if any */
  233.  
  234. /*
  235.  * HANDLE NON-DIRECTORY SUB-FILE (print multiple per line):
  236.  */
  237.  
  238.     if (type == FTW_F)
  239.     {
  240.         if (pflag)                /* else use preset value */
  241.         filewidth = strlen (basename) + 1;
  242.  
  243.         if (nextcol    && (nextcol + filewidth >= columns))    /* overflow */
  244.         NEWLINE;            /* sets nextcol == 0 */
  245.  
  246.         if (nextcol == 0)        /* start new line with indent */
  247.         nextcol = PrintIndent (filename);
  248.  
  249.         printf (FMT_F2, filewidth, basename);
  250.         nextcol += filewidth;
  251.         return (0);
  252.     }
  253.  
  254. /*
  255.  * HANDLE DIRECTORY OR OTHER SUB-FILE (print on line by itself):
  256.  */
  257.  
  258.     if (nextcol)            /* end previous line */
  259.         NEWLINE;            /* sets nextcol == 0 */
  260.  
  261.     PrintIndent (filename);
  262.  
  263.     printf ((type == FTW_D)   ? FMT_D   :
  264.         (type == FTW_DNR) ? FMT_DNR : FMT_NS, basename);
  265.  
  266.     return (0);
  267.  
  268. } /* OneFile */
  269.  
  270.  
  271. /************************************************************************
  272.  * P R I N T   I N D E N T
  273.  *
  274.  * Given a filename and globals startlen and indent, print the total
  275.  * indentation needed before the name, which is indent times the number of
  276.  * slashes past startlen (which should be >= 1).  Return the indent value.
  277.  */
  278.  
  279. PROC int PrintIndent (filename)
  280. REG    char    *filename;
  281. {
  282. REG    int    depth = 0;
  283. REG    int    totind;
  284.     int    retval;
  285.  
  286.     filename += startlen;        /* start of sub-part */
  287.  
  288.     while (*filename != CHNULL)
  289.         if (*filename++ == '/')
  290.         depth++;
  291.  
  292.     retval = totind = indent * depth;
  293.  
  294.     while (totind-- > 0)
  295.         putchar (' ');
  296.  
  297.     return (retval);
  298.  
  299. } /* PrintIndent */
  300.  
  301.  
  302. /************************************************************************
  303.  * U S A G E
  304.  *
  305.  * Print usage messages (char *usage[]) to stderr and exit nonzero.
  306.  * Each message is followed by a newline.
  307.  */
  308.  
  309. PROC Usage()
  310. {
  311. REG    int    which = 0;        /* current line */
  312.  
  313.     while (usage [which] != CPNULL)
  314.     {
  315.         fprintf (stderr, usage [which++], myname);
  316.         putc ('\n', stderr);
  317.     }
  318.  
  319.     exit (1);
  320.  
  321. } /* Usage */
  322.  
  323.  
  324. /************************************************************************
  325.  * E R R O R
  326.  *
  327.  * Print an error message to stderr and exit nonzero.  Message is preceded
  328.  * by "<myname>: " using global char *myname, and followed by a newline.
  329.  */
  330.  
  331. /* VARARGS */
  332. PROC Error (message, arg1, arg2, arg3, arg4)
  333.     char    *message;
  334.     long    arg1, arg2, arg3, arg4;
  335. {
  336.     fprintf (stderr, "%s: ", myname);
  337.     fprintf (stderr, message, arg1, arg2, arg3, arg4);
  338.     putc ('\n', stderr);
  339.  
  340.     exit (1);
  341.  
  342. } /* Error */
  343.