home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume18 / vtree / vtree.c < prev   
C/C++ Source or Header  |  1989-04-19  |  15KB  |  631 lines

  1. /* vtree
  2.   
  3.    +=======================================+
  4.    | This program is in the public domain. |
  5.    +=======================================+
  6.   
  7.    This program shows the directory structure of a filesystem or 
  8.    part of one.  It also shows the amount of space taken up by files
  9.    in each subdirectory. 
  10.   
  11.    Call via
  12.   
  13.     vtree fn1 fn2 fn3 ...
  14.   
  15.    If any of the given filenames is a directory (the usual case),
  16.    vtree will recursively descend into it, and the output line will
  17.    reflect the accumulated totals for all files in the directory.
  18.    
  19.    This program is based upon "agef" written by David S. Hayes at the 
  20.    Army Artificial Intelligence Center at the Pentagon.
  21.    
  22.    This program is dependent upon the new directory routines written by
  23.    Douglas A. Gwyn at the US Army Ballistic Research Laboratory at the
  24.    Aberdeen Proving Ground in Maryland.
  25. */
  26. /*
  27. ** Patches were received from the following people:
  28. **
  29. **    1.    Mike Howard, (...!uunet!milhow1!how)
  30. **        Mike's patches included changes to the Makefile to
  31. **        customize vtree to SCO Xenix for the 286 as well as the
  32. **        386.  He also added external definitions to hash.c
  33. **
  34. **    2.    Andrew Weeks, (...!uunet!mcvax!doc.ic.ac.uk!aw)
  35. **        Andrew sent me diffs to make vtree work properly under BSD
  36. **        He also pointed out that you will need one of the PD getopt
  37. **        packages for BSD.
  38. **
  39. **    3.    Ralph Chapman, (...uunet!ihnp4!ihuxy!chapman)
  40. **        Ralph sent me changes (not diffs unfortunately) to make
  41. **        vtree work properly under the SYS_III option.  His changes
  42. **        were in direct.c and vtree.c
  43. **
  44. **    4.    David Eckelkamp notified me of a bug when printing the
  45. **        visual tree.  The bug occured when a directory name
  46. **        was too long.  It caused vtree to mess up the tree
  47. **        being printed.
  48. */
  49.  
  50. #include "patchlevel.h"
  51.  
  52. #include <ctype.h>
  53. #include <sys/types.h>
  54. #include <sys/stat.h>
  55. #include <sys/param.h>
  56. #include <stdio.h>
  57. #ifdef    BSD
  58. #include <strings.h>
  59. #else
  60. #include <string.h>
  61. #endif
  62.  
  63. #include "customize.h"
  64. #include "hash.h"
  65.  
  66.  
  67. #ifdef    SYS_III
  68.     #define    rewinddir(fp)    rewind(fp)
  69. #endif
  70.  
  71. #define SAME        0    /* for strcmp */
  72. #define BLOCKSIZE    512    /* size of a disk block */
  73.  
  74. #define K(x)        ((x + 1023)/1024)    /* convert stat(2) blocks into
  75.                      * k's.  On my machine, a block
  76.                      * is 512 bytes. */
  77.  
  78. #define    TRUE    1
  79. #define    FALSE    0
  80. #define    V_CHAR    "|"    /*    Vertical character    */
  81. #define    H_CHAR    "-"    /*    Horizontal character    */
  82. #define    A_CHAR    ">"    /*    Arrow char        */
  83. #define    T_CHAR    "+"    /*    Tee char        */
  84. #define    L_CHAR    "\\"    /*    L char, bottom of a branch    */
  85.  
  86. #define    MAX_COL_WIDTH    15
  87. #define    MAX_V_DEPTH    256        /* max depth for visual display */
  88.  
  89. #ifdef    MEMORY_BASED
  90. struct RD_list {
  91.     READ        entry;
  92.     struct RD_list    *fptr;
  93.     struct RD_list    *bptr;
  94. };
  95. #endif
  96.  
  97.  
  98.  
  99. int        indent = 0,        /* current indent */
  100.         depth = 9999,        /* max depth */
  101.         cur_depth = 0,    
  102.         sum = FALSE,        /* sum the subdirectories */
  103.         dup = FALSE,        /* use duplicate inodes */
  104.         floating = FALSE,    /* floating column widths */
  105.         sort = FALSE,
  106.         cnt_inodes = FALSE,    /* count inodes */
  107.         quick = FALSE,        /* quick display */
  108.         visual = FALSE,        /* visual display */
  109.         version = 0,        /* = 1 display version, = 2 show options */
  110.         sub_dirs[MAX_V_DEPTH],
  111.         sub_dirs_indents[MAX_V_DEPTH];
  112.  
  113. struct    stat    stb;            /* Normally not a good idea, but */
  114.                     /* this structure is used through- */
  115.                     /* out the program           */
  116.  
  117. extern char    *optarg;            /* from getopt(3) */
  118. extern int      optind,
  119.                 opterr;
  120.  
  121.  
  122. char           *Program;        /* our name */
  123. short           sw_follow_links = 1;    /* follow symbolic links */
  124. short           sw_summary;        /* print Grand Total line */
  125.  
  126. int             total_inodes, inodes;    /* inode count */
  127. long            total_sizes, sizes;    /* block count */
  128.  
  129. char            topdir[NAMELEN];    /* our starting directory */
  130.  
  131.  
  132.  
  133. /*
  134. ** Find the last field of a string.
  135. */
  136. char *lastfield(p,c)
  137. char *p;    /* Null-terminated string to scan */
  138. int   c;    /* Separator char, usually '/' */
  139. {
  140. char *r;
  141.  
  142.     r = p;
  143.     while (*p)            /* Find the last field of the name */
  144.         if (*p++ == c)
  145.             r = p;
  146.     return r;
  147. } /* lastfield */
  148.  
  149.  
  150.  
  151.  
  152.  /*
  153.   * We ran into a subdirectory.  Go down into it, and read everything
  154.   * in there. 
  155.   */
  156. int    indented = FALSE;    /* These had to be global since they */
  157. int    last_indent = 0;    /* determine what gets displayed during */
  158. int    last_subdir = FALSE;    /* the visual display */
  159.  
  160.  
  161.  
  162. down(subdir)
  163. char    *subdir;
  164. {
  165. OPEN    *dp;            /* stream from a directory */
  166. OPEN    *opendir ();
  167. char    cwd[NAMELEN], tmp[NAMELEN];
  168. char    *sptr;
  169. READ    *file;            /* directory entry */
  170. READ    *readdir ();
  171. int    i, x;
  172. struct    stat    stb;
  173.  
  174. #ifdef    MEMORY_BASED
  175. struct RD_list    *head = NULL, *tail, *tmp_RD, *tmp1_RD;        /* head and tail of directory list */
  176. struct RD_list    sz;
  177. READ        tmp_entry;
  178. #endif
  179.  
  180.     if ( (cur_depth == depth) && (!sum) )
  181.         return;
  182.  
  183. /* display the tree */
  184.  
  185.     if (cur_depth < depth) {
  186.         if (visual) {
  187.             if (!indented) {
  188.                 for (i = 1; i <cur_depth; i++) {
  189.                     if (floating) x = sub_dirs_indents[i] + 1;
  190.                         else x = MAX_COL_WIDTH - 3;
  191.                     if (sub_dirs[i]) {
  192.                         printf("%*s%s   ",x - 1," ",V_CHAR);
  193.                     } else printf("%*s   ",x," ");
  194.                 }
  195.                 if (cur_depth>0) {
  196.                     if (floating) x = sub_dirs_indents[cur_depth] + 1;
  197.                         else x = MAX_COL_WIDTH - 3;
  198.                     if (sub_dirs[cur_depth] == 0) {
  199.                         printf("%*s%s%s%s ",x - 1," ",L_CHAR,H_CHAR,A_CHAR);
  200.                         last_subdir = cur_depth;
  201.                     }
  202.                     else printf("%*s%s%s%s ",x - 1," ",T_CHAR,H_CHAR,A_CHAR);
  203.                 }
  204.             } else {
  205.                 if (!floating)
  206.                     for (i = 1; i<MAX_COL_WIDTH-last_indent-3; i++)
  207.                         printf("%s",H_CHAR);
  208.                 printf("%s%s%s ",T_CHAR,H_CHAR,A_CHAR);
  209.             }
  210.  
  211.     /* This is in case a subdir name is too big.  It is then displayed on
  212.     ** two lines, the first line is the full name, the second line is
  213.     ** truncated.  Any subdirs displayed for the current subdir will be
  214.     ** appended to the second line.  This keeps the columns in order
  215.     */
  216.  
  217. #ifndef    ONEPERLINE
  218.             if (  ( strlen(subdir) > MAX_COL_WIDTH - 3 && !floating )    ) {
  219. #else
  220.             if (  ( strlen(subdir) > MAX_COL_WIDTH - 3 && !floating ) ||
  221.                 lastfield(subdir,'/') != subdir) {
  222. #endif
  223.  
  224.                 printf("%s\n",subdir);
  225.                 for (i = 1; i <=cur_depth; i++) {
  226.                     if (sub_dirs[i]) {
  227.                         printf("%*s%s   ",MAX_COL_WIDTH-4," ",V_CHAR);
  228.                     }
  229.                     else printf("%*s   ",MAX_COL_WIDTH-3," ");
  230.                 }
  231.                 strcpy(tmp,lastfield(subdir,'/'));
  232.                 tmp[MAX_COL_WIDTH - 4] = 0;
  233.                 printf("%s",tmp);
  234. #ifdef    ONEPERLINE
  235.                 if (floating || strlen(tmp) < MAX_COL_WIDTH - 4) printf(" ");
  236. #endif
  237.                 sub_dirs_indents[cur_depth + 1] = last_indent = strlen(tmp) + 1;
  238.             }
  239.             else {
  240.                 printf("%s",subdir);
  241.                 sub_dirs_indents[cur_depth + 1] = last_indent = strlen(subdir)+1;
  242.                 if (floating || strlen(subdir) < MAX_COL_WIDTH - 4) 
  243.                     printf(" ");
  244.             }
  245.             indented = TRUE;
  246.         }
  247.         else printf("%*s%s",indent," ",subdir);
  248.     }
  249.  
  250. /* open subdirectory */
  251.  
  252.     if ((dp = opendir(subdir)) == NULL) {
  253.         printf(" - can't read %s\n", subdir);
  254.         indented = FALSE;
  255.         return;
  256.     }
  257.  
  258.     cur_depth++;
  259.     indent+=3;
  260.  
  261. #ifdef BSD
  262.     getwd(cwd);                /* remember where we are */
  263. #else
  264.     getcwd(cwd, sizeof(cwd));        /* remember where we are */
  265. #endif
  266.     chdir(subdir);                /* go into subdir */
  267.  
  268.  
  269. #ifdef    MEMORY_BASED
  270.  
  271.     for (file = readdir(dp); file != NULL; file = readdir(dp)) {
  272.         if ((!quick && !visual ) ||
  273.              ( strcmp(NAME(*file), "..") != SAME &&
  274.              strcmp(NAME(*file), ".") != SAME &&
  275.              chk_4_dir(NAME(*file)) ) ) {
  276.             tmp_RD = (struct RD_list *) malloc(sizeof(sz));
  277.             memcpy(&tmp_RD->entry, file, sizeof(tmp_entry));
  278.             tmp_RD->bptr = head;
  279.             tmp_RD->fptr = NULL;
  280.             if (head == NULL) head = tmp_RD;
  281.                 else tail->fptr = tmp_RD;
  282.             tail = tmp_RD;
  283.         }
  284.     }
  285.  
  286.                 /* screwy, inefficient, bubble sort    */
  287.                 /* but it works                */
  288.     if (sort) {
  289.         tmp_RD = head;
  290.         while (tmp_RD) {
  291.             tmp1_RD = tmp_RD->fptr;
  292.             while (tmp1_RD) {
  293.                 if (strcmp(NAME(tmp_RD->entry), NAME(tmp1_RD->entry)) >0) {
  294.                     /* swap the two */
  295.                     memcpy(&tmp_entry, &tmp_RD->entry, sizeof(tmp_entry));
  296.                     memcpy(&tmp_RD->entry, &tmp1_RD->entry, sizeof(tmp_entry));
  297.                     memcpy(&tmp1_RD->entry, &tmp_entry, sizeof(tmp_entry));
  298.                 }
  299.                 tmp1_RD = tmp1_RD->fptr;
  300.             }
  301.             tmp_RD = tmp_RD->fptr;
  302.         }
  303.     }
  304.  
  305. #endif
  306.  
  307.     if ( (!quick) && (!visual) ) {
  308.  
  309.         /* accumulate total sizes and inodes in current directory */
  310.  
  311.  
  312. #ifdef    MEMORY_BASED
  313.         tmp_RD = head;
  314.         while (tmp_RD) {
  315.             file = &tmp_RD->entry;
  316.             tmp_RD = tmp_RD->fptr;
  317. #else
  318.         
  319.         for (file = readdir(dp); file != NULL; file = readdir(dp)) {
  320. #endif
  321.             if (strcmp(NAME(*file), "..") != SAME) 
  322.                 get_data(NAME(*file),FALSE);
  323.         }
  324.  
  325.         if (cur_depth<depth) {
  326.             if (cnt_inodes) printf("   %d",inodes);
  327.             printf(" : %ld\n",sizes);
  328.             total_sizes += sizes;
  329.             total_inodes += inodes;
  330.             sizes = 0;
  331.             inodes = 0;
  332.         }
  333. #ifndef    MEMORY_BASED
  334.         rewinddir(dp);
  335. #endif
  336.     } else if (!visual) printf("\n");
  337.  
  338.     if (visual) {
  339.  
  340. /* count subdirectories */
  341.  
  342.  
  343. #ifdef    MEMORY_BASED
  344.         tmp_RD = head;
  345.         while (tmp_RD) {
  346.             file = &tmp_RD->entry;
  347.             tmp_RD = tmp_RD->fptr;
  348. #else
  349.         for (file = readdir(dp); file != NULL; file = readdir(dp)) {
  350.             if ( (strcmp(NAME(*file), "..") != SAME) &&
  351.                  (strcmp(NAME(*file), ".") != SAME) ) {
  352.                 if (chk_4_dir(NAME(*file))) {
  353. #endif
  354.                     sub_dirs[cur_depth]++;
  355. #ifndef    MEMORY_BASED
  356.                 }
  357.             }
  358. #endif
  359.         }
  360. #ifndef    MEMORY_BASED
  361.         rewinddir(dp);
  362. #endif
  363.     }
  364.     
  365. /* go down into the subdirectory */
  366.  
  367. #ifdef    MEMORY_BASED
  368.     tmp_RD = head;
  369.     while (tmp_RD) {
  370.         file = &tmp_RD->entry;
  371.         tmp_RD = tmp_RD->fptr;
  372. #else
  373.     for (file = readdir(dp); file != NULL; file = readdir(dp)) {
  374. #endif
  375.         if ( (strcmp(NAME(*file), "..") != SAME) &&
  376.              (strcmp(NAME(*file), ".") != SAME) ) {
  377.             if (chk_4_dir(NAME(*file))) 
  378.                 sub_dirs[cur_depth]--;
  379.             get_data(NAME(*file),TRUE);
  380.         }
  381.     }
  382.  
  383.     if ( (!quick) && (!visual) ) {
  384.  
  385. /* print totals */
  386.  
  387.         if (cur_depth == depth) {
  388.             if (cnt_inodes) printf("   %d",inodes);
  389.             printf(" : %ld\n",sizes);
  390.             total_sizes += sizes;
  391.             total_inodes += inodes;
  392.             sizes = 0;
  393.             inodes = 0;
  394.         }
  395.     }
  396.  
  397. #ifdef    MEMORY_BASED
  398.                 /* free the allocated memory */
  399.     tmp_RD = head;
  400.     while (tmp_RD) {
  401.         tmp_RD = tmp_RD->fptr;
  402.         free(head);
  403.         head = tmp_RD;
  404.     }
  405. #endif    
  406.  
  407.     if (visual && indented) {
  408.         printf("\n");
  409.         indented = FALSE;
  410.         if (last_subdir>=cur_depth-1) {
  411.             for (i = 1; i <cur_depth; i++) {
  412.                 if (sub_dirs[i]) {
  413.                     if (floating)
  414.                         printf("%*s%s   ",sub_dirs_indents[i]," ",V_CHAR);
  415.                     else printf("%*s%s   ",MAX_COL_WIDTH-4," ",V_CHAR);
  416.                 } else {
  417.                     if (floating)
  418. /*ZZZ*/                        printf("%*s   ",sub_dirs_indents[i] + 1," ");
  419.                      else printf("%*s   ",MAX_COL_WIDTH-3," ");
  420.                  }
  421.             }
  422.             printf("\n");
  423.             last_subdir = FALSE;
  424.         }
  425.     }
  426.     indent-=3;
  427.     sub_dirs[cur_depth] = 0;
  428.     cur_depth--;
  429.  
  430.     chdir(cwd);            /* go back where we were */
  431.     closedir(dp);            /* shut down the directory */
  432.  
  433.  
  434. } /* down */
  435.  
  436.  
  437.  
  438. int    chk_4_dir(path)
  439. char    *path;
  440. {
  441.     if (is_directory(path)) return TRUE;
  442.     else return FALSE;
  443.         
  444. } /* chk_4_dir */
  445.  
  446.  
  447.  
  448. /* Is the specified path a directory ? */
  449.  
  450. int    is_directory(path)
  451. char           *path;
  452. {
  453.  
  454. #ifdef LSTAT
  455.     if (sw_follow_links)
  456.         stat(path, &stb);    /* follows symbolic links */
  457.     else
  458.         lstat(path, &stb);    /* doesn't follow symbolic links */
  459. #else
  460.     stat(path, &stb);
  461. #endif
  462.  
  463.     if ((stb.st_mode & S_IFMT) == S_IFDIR)
  464.         return TRUE;
  465.     else return FALSE;
  466. } /* is_directory */
  467.  
  468.  
  469.  
  470.  /*
  471.   * Get the aged data on a file whose name is given.  If the file is a
  472.   * directory, go down into it, and get the data from all files inside. 
  473.   */
  474.  
  475. get_data(path,cont)
  476. char           *path;
  477. int        cont;    
  478. {
  479. /* struct    stat    stb; */
  480. int        i;
  481.  
  482.     if (cont) { 
  483.         if (is_directory(path)) 
  484.             down(path);
  485.     }
  486.     else {
  487.         if (is_directory(path)) return;
  488.  
  489.             /* Don't do it again if we've already done it once. */
  490.  
  491.         if ( (h_enter(stb.st_dev, stb.st_ino) == OLD) && (!dup) )
  492.             return;
  493.         inodes++;
  494.         sizes+= K(stb.st_size);
  495.     }
  496. } /* get_data */
  497.  
  498.  
  499.  
  500. main(argc, argv)
  501. int    argc;
  502. char    *argv[];
  503. {
  504. int    i,
  505.     j,
  506.     err = FALSE;
  507. int    option;
  508. int    user_file_list_supplied = 0;
  509.  
  510.     Program = *argv;        /* save our name for error messages */
  511.  
  512.     /* Pick up options from command line */
  513.  
  514.     while ((option = getopt(argc, argv, "dfh:iostqvV")) != EOF) {
  515.         switch (option) {
  516.             case 'f':    floating = TRUE; break;
  517.             case 'h':    depth = atoi(optarg);
  518.                     while (*optarg) {
  519.                         if (!isdigit(*optarg)) {
  520.                             err = TRUE;
  521.                             break;
  522.                         }
  523.                         optarg++;
  524.                     }
  525.                     break;
  526.             case 'd':    dup = TRUE;
  527.                     break;    
  528.             case 'i':    cnt_inodes = TRUE;
  529.                     break;
  530.             case 'o':    sort = TRUE; break;    
  531.             case 's':    sum = TRUE;
  532.                     break;
  533.             case 't':    sw_summary = TRUE;
  534.                     break;
  535.             case 'q':    quick = TRUE;
  536.                     dup = FALSE;
  537.                     sum = FALSE;
  538.                     cnt_inodes = FALSE;
  539.                     break;
  540.             case 'v':    visual = TRUE;
  541.                     break;
  542.             case 'V':    version++;
  543.                     break;
  544.             default:    err = TRUE;
  545.         }
  546.         if (err) {
  547.             fprintf(stderr,"%s: [ -d ] [ -h # ] [ -i ] [ -o ] [ -s ] [ -q ] [ -v ] [ -V ]\n",Program);
  548.             fprintf(stderr,"    -d    count duplicate inodes\n");
  549.             fprintf(stderr,"    -f    floating column widths\n");
  550.             fprintf(stderr,"    -h #    height of tree to look at\n");
  551.             fprintf(stderr,"    -i    count inodes\n");
  552.             fprintf(stderr,"    -o    sort directories before processing\n");
  553.             fprintf(stderr,"    -s    include subdirectories not shown due to -h option\n");
  554.             fprintf(stderr,"    -t    totals at the end\n");
  555.             fprintf(stderr,"    -q    quick display, no counts\n");
  556.             fprintf(stderr,"    -v    visual display\n");
  557.             fprintf(stderr,"    -V    show current version\n");
  558.             fprintf(stderr,"        (2 Vs shows specified options)\n");
  559.             exit(-1);
  560.         }
  561.     
  562.     }
  563.  
  564.     if (version > 0 ) {
  565.  
  566. #ifdef    MEMORY_BASED
  567.         printf("%s memory based\n",VERSION);
  568. #else
  569.         printf("%s disk based\n",VERSION);
  570. #endif
  571.  
  572.         if (version>1) {
  573.             printf("Tree height:    %d\n",depth);
  574.             if (dup) printf("Include duplicate inodes\n");
  575.             if (cnt_inodes) printf("Count inodes\n");
  576.             if (sum) printf("Include unseen subdirectories in totals\n");
  577.             if (sw_summary) printf("Print totals at end\n");
  578.             if (quick) printf("Quick display only\n");
  579.             if (visual) printf("Visual tree\n");
  580.             if (sort) printf("Sort directories before processing\n");
  581.         }
  582.     }
  583.     
  584.     /* If user didn't specify targets, inspect current directory. */
  585.  
  586.     if (optind >= argc) {
  587.         user_file_list_supplied = 0;
  588.     } else {
  589.         user_file_list_supplied = 1;
  590.     }
  591.  
  592. #ifdef BSD
  593.     getwd(topdir);                /* find out where we are */
  594. #else
  595.     getcwd(topdir, sizeof (topdir));    /* find out where we are */
  596. #endif
  597.  
  598.     /* Zero out grand totals */
  599.     total_inodes = total_sizes = 0;
  600.     /* Zero out sub_dirs */
  601.     for (i=0; i<=MAX_V_DEPTH; i++) {
  602.         sub_dirs[i] = 0;
  603.         sub_dirs_indents[i] = 0;
  604.     }
  605.         
  606.     /* Inspect each argument */
  607.     for (i = optind; i < argc || (!user_file_list_supplied && i == argc); i++) {
  608.         cur_depth = inodes = sizes = 0;
  609.  
  610.         chdir(topdir);        /* be sure to start from the same place */
  611.         get_data(user_file_list_supplied?argv[i] : topdir, TRUE);/* this may change our cwd */
  612.  
  613.         total_inodes += inodes;
  614.         total_sizes += sizes;
  615.     }
  616.  
  617.     if (sw_summary) {
  618.         printf("\n\nTotal space used: %ld\n",total_sizes);
  619.         if (cnt_inodes) printf("Total inodes: %d\n",inodes);
  620.     }
  621.     
  622. #ifdef HSTATS
  623.     fflush(stdout);
  624.     h_stats();
  625. #endif
  626.  
  627.     exit(0);
  628. } /* main */
  629.  
  630.  
  631.