home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume32 / tbtree / part01 / tbtree.c < prev   
C/C++ Source or Header  |  1992-10-15  |  10KB  |  312 lines

  1. /*- tbtree - Graphical directory display. */
  2. /*
  3.  *  Usage: tbtree [directory]
  4.  *
  5.  *  That command will give a "tree" drawing of the Unix sub-directories
  6.  *  below.  If no directory is specified, the current directory, ".",
  7.  *  is used.
  8.  */
  9. /*
  10.  * This software is released to the public domain.  Please don't
  11.  * sell it for a million dollars profit without giving me some.
  12.  * Please leave my name and this credit in the code, or everyone
  13.  * on the planet will forget I exist, even before I die.  Besides,
  14.  * I know where you live.
  15.  *
  16.  * If you improve this program, please send me a copy.
  17.  * tombaker@world.std.com || tabaker@aol.com || BIX as tombaker  Oct 2, 1992
  18.  */
  19. /*
  20.  * Install: Cut where it says.
  21.  *          Compile as
  22.  *                     % cc -o tbtree tbtree.c
  23.  */
  24. /*
  25.  * 14-JUL-89 Original.
  26.  * 10-FEB-90 Changed the 'bugs' comment to a 'features' comment.
  27.  * 26-JUN-90 Fixed bug in which link names are not terminated correctly.
  28.  * 08-NOV-90 Prune of XYZZY hardwired into alternate version.
  29.  * 02-OCT-92 Cleaned up and posted in response to call for help over
  30.  *           the Net.
  31.  * 05-OCT-92 stat() and lstat() now properly report errors ("You have no
  32.  *           permission", "file no longer exists") verbosely,
  33.  *           AND stat() ignores symbolic links which point at nonexistent
  34.  *           files.
  35.  *           (Get it? I fix a bug, which I probably have known about since
  36.  *           the 1980's, three days AFTER I broadcast the source code to
  37.  *           the world.)
  38.  *
  39.  * Features:
  40.  *       Follows links.
  41.  *           It indicates links with a "->".
  42.  *           It DOES distinguish between a link to a file and one
  43.  *               to a subdirectory; only the latter get displayed.
  44.  *           It seems to follow circular links.  Eg., "xxx -> ./"
  45.  *       System errors in stat() and lstat() result in calls to
  46.  *            perror(), so a useful error message is provided.
  47.  *
  48.  * Bugs:
  49.  *       The opendir() call still has trouble with files which
  50.  *       are not directories; the "*UNREADABLE*" pruning results.
  51.  *
  52.  * Suggestions:
  53.  *       Some pruning is indicated:
  54.  *           Go only a specified number of levels down.
  55.  *       It is easy to lose track of what the lines point back UP to.
  56.  *       An interactive version would be appreciated.
  57.  *
  58.  *       The quick-and-dirty use of big, big arrays, instead of using
  59.  *       dynamic allocations or the system defines, was okay because
  60.  *       the swapper/pager can handle it and this is not that big a
  61.  *       deal of a program.  Guess I was tired that day, but it works.
  62.  *       (pleasenoflames: you don't like it, fix it)
  63.  *
  64.  * Notes:
  65.  *    STAT - In the case of a symbolic link that points to a file that
  66.  *        does not exist (e.g., you create a linkfile "foo" that points
  67.  *        to a file named "foobar", but then delete "foobar", leaving
  68.  *        "foo"), stat() returns an error flag, and sets the "errno" global
  69.  *        to ENOENT.  This is normal, so we ignore this as a special case.
  70.  *
  71.  *        Other system errors will thwart tbtree, and are reported.
  72.  *
  73.  *    This works on Sun workstations.  Try it on your version of Unix.
  74.  *    (If I could guarantee it would work, I'd charge you.)
  75.  *
  76.  *    The call to readlink() returns -1, and error = EINVAL if the
  77.  *    file being tested is not a link.  It returns the contents of
  78.  *    symlink in nonterminated string, and the return value is the
  79.  *    number of characters of the link name.
  80.  *
  81.  *    If a link is a hard link, that means there is no symbolic link.
  82.  *    This program just skips down the hard links like they aren't
  83.  *    there.
  84.  */
  85. #include <stdio.h>
  86. #include <sys/types.h>
  87. #include <sys/stat.h>
  88. #include <errno.h>
  89. #include <string.h>
  90. #include <dirent.h>
  91.  
  92. #define FALSE        (0)
  93. #define TRUE         (1)
  94.  
  95. #define MAX_SUBS     (1000)
  96. #define MAX_COMPS    (100)
  97. #define MAX_COMP_LEN (100)
  98. #define MAX_PATH     (MAX_COMPS*(MAX_COMP_LEN+1))
  99.  
  100. /*-   DATA. */
  101. int   debug = FALSE;
  102. int   last_line_had_no_file = FALSE;
  103.  
  104. /*-   MAIN() - Process args and start top directory. */
  105. main(argc, argv)
  106. int    argc;
  107. char **argv;
  108. {
  109.     switch( argc )
  110.     {
  111.     case 1:
  112.         show( ".",     " " );
  113.         break;
  114.     case 2:
  115.         show( argv[1], " " );
  116.         break; 
  117.     default:
  118.         fprintf(stderr,"Usage: %s [directory].\n", argv[0]);
  119.         break;
  120.     }
  121. }
  122.  
  123. /*-   SHOW() - Display a directory and its subdirectories.  RECURSIVE. */
  124. show( path,  prefix )
  125. char *path, *prefix;
  126. {
  127.     char           *lastp;
  128.     DIR            *dirp;
  129.     struct dirent  *dp;
  130.     struct stat     buf;
  131.     char            newpath[MAX_PATH+1];
  132.     char            newprefix[MAX_COMPS*4+1];
  133.     int             num_subs = 0;
  134.     char            darray[MAX_SUBS][MAX_COMP_LEN+1];
  135.     int             i;
  136.     int             symsize;
  137.     char            sympath[MAX_PATH+1];
  138.  
  139.     /*- Get the last component of directory name. */
  140.     lastp=strrchr( path, '/' );
  141.     if( lastp == NULL )
  142.         lastp = path;
  143.     else
  144.         lastp++;
  145.  
  146.     /*- Show last component of directory name. */
  147.     printf("%s", lastp);
  148.     last_line_had_no_file = FALSE;
  149.  
  150.     /*- Prune dirs ONE TWO and THREE.
  151.      * if(  (!strcmp(lastp,"ONE"))
  152.      *   || (!strcmp(lastp,"TWO"))
  153.      *   || (!strcmp(lastp,"THREE"))
  154.      *   )
  155.      * {
  156.      *     if( strcmp(lastp, "THREE"))
  157.      *         printf("  ");
  158.      *     printf("\t*PRUNED*\n");
  159.      *     last_line_had_no_file = FALSE;
  160.      *     return;
  161.      * }
  162.      */
  163.  
  164.     /*- Open directory. */
  165.     if ((dirp = opendir(path)) == NULL)
  166.     {
  167.         /*- If can't (even after the above testing), yell and abort. */
  168.         /*
  169.          * The block below puts the message below the name.
  170.          *            printf("\n");
  171.          *            printf("%s|\n",prefix);
  172.          *            printf("%s*UNREADABLE*\n", prefix );
  173.          *            printf("%s\n", prefix );
  174.          *            last_line_had_no_file = TRUE;
  175.          * The block in use shoves it to the right, giving one line
  176.          *     per unreadable directory, just like directories with no
  177.          *     subdirectories.
  178.          */
  179.         printf("\t*UNREADABLE*\n");
  180.         last_line_had_no_file = FALSE;
  181.         return;
  182.     }
  183.     printf("\n");
  184.  
  185.     /*- Read all entries. */
  186.     for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
  187.     {
  188.         /*- Skip directory entries '.' and '..' . */
  189.         if (!strcmp(dp->d_name, "."))
  190.             continue;
  191.         if (!strcmp(dp->d_name, ".."))
  192.             continue;
  193.  
  194.         /*- If entry is a directory, */
  195.         strcpy( newpath, path );
  196.         strcat( newpath, "/"  );
  197.         strcat( newpath, dp->d_name  );
  198.                                      /* See note on "STAT" in file header. */
  199.         if( stat(newpath,&buf) && errno!=ENOENT )
  200.         {
  201.             fprintf(stderr,"Error running stat() on \"%s\")",newpath);
  202.             perror("");
  203.         }
  204.         if( (buf.st_mode&S_IFDIR) == S_IFDIR )
  205.         {
  206.             /*- Add it to array. */
  207.             strcpy( darray[num_subs], dp->d_name );
  208.             num_subs++;
  209.         }
  210.     }
  211.  
  212.     /*- Close directory. */
  213.     closedir(dirp);
  214.  
  215.     /*- Call sub to sort the array, in place. */
  216.     sort( num_subs, darray );
  217.  
  218.     /*- Line above sub-directory. */
  219.     if( num_subs )
  220.         if( !last_line_had_no_file )
  221.         {
  222.             printf("%s|\n",prefix);
  223.             last_line_had_no_file = TRUE;
  224.         }
  225.  
  226.     /*- For each sub-directory ... */
  227.     for( i=0; i<num_subs; i++ )
  228.     {
  229.         /*- Prepare sub-directory's path. */
  230.         strcpy( newpath, path );
  231.         strcat( newpath, "/"  );
  232.         strcat( newpath, darray[i] );
  233.  
  234.         /*- Sub-direcory's prefix string depends on ... */
  235.         if( (i+1) == num_subs )
  236.         {
  237.             /*- ... if sub-directory is last one ... */
  238.             strcpy( newprefix, prefix );
  239.             strcat( newprefix, "    "   );
  240.         }
  241.         else
  242.         {
  243.             /*- ... or not. */
  244.             strcpy( newprefix, prefix );
  245.             strcat( newprefix, "|   "   );
  246.         }
  247.  
  248.         /*- If sub-directory is a symbolic link, */
  249.                                      /* See note on "STAT" in file header. */
  250.         if( lstat(newpath,&buf) && errno!=ENOENT )
  251.         {
  252.             fprintf(stderr,"Error running lstat() on \"%s\")",newpath);
  253.             perror("");
  254.         }
  255.         if( (buf.st_mode&S_IFLNK) == S_IFLNK )
  256.         {
  257.             /*- Get symbolic name as well. */
  258.             if( (symsize=readlink(newpath,sympath,MAX_PATH)) < 0 )
  259.             {
  260.                 fprintf(stderr,"Error on readlink.\n");
  261.             }
  262.  
  263.             if( (lastp=strchr(sympath, (int) ' ')) != NULL )
  264.                 *lastp = '\0';
  265.  
  266.             sympath[symsize] = '\0';
  267.             printf("%s+- %s -> %s\n",prefix,darray[i],sympath);
  268.         }
  269.         /*- Else, */
  270.         else
  271.         {
  272.             /*- Recurse to show sub-directory. */
  273.             printf("%s+- ",prefix);
  274.             show( newpath, newprefix );
  275.         }
  276.     }
  277.  
  278.     /*- Space below each directory. */
  279.     if( num_subs )
  280.         if( !last_line_had_no_file )
  281.         {
  282.             printf("%s\n",prefix);
  283.             last_line_had_no_file = TRUE;
  284.         }
  285.  
  286.     /*- Done. */
  287. }
  288.  
  289. /*-   SORT() - Sort array in place. */
  290. sort( n, darray )
  291. int   n;
  292. char     darray[MAX_SUBS][MAX_COMP_LEN+1];
  293. {
  294.     register int i, j;
  295.     char         scratch[MAX_COMP_LEN+1];
  296.  
  297.     /*- For each of the n-1 passes. */
  298.     for( i=0; i<(n-1); i++ )
  299.  
  300.         /*- For each of the lowest (n-1-pass) pairs. */
  301.         for( j=0; j<(n-1-i); j++ )
  302.  
  303.             /*- If the two are out of order. */
  304.             if( strcmp( darray[j], darray[j+1] ) > 0 )
  305.             {
  306.                 /*- Swap them. */
  307.                 strcpy( scratch,     darray[j]   );
  308.                 strcpy( darray[j],   darray[j+1] );
  309.                 strcpy( darray[j+1], scratch     );
  310.             }
  311. }
  312.