home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume28 / xtree / part01 / xtree.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-05-29  |  10.4 KB  |  383 lines

  1. /******************************************************************************
  2.  *
  3.  *  xtree - a graphical tree of your directories
  4.  *
  5.  *  Mark Livingston  -  mark@acpub.duke.edu -  Aug '92
  6.  *
  7.  *  USAGE
  8.  *         xtree [ <dirname>+ ]
  9.  *
  10.  *  DESCRIPTION
  11.  *         Gives a graphical tree of the given directory, with the default
  12.  *         being the current directory.  Uses xterm escape codes to generate
  13.  *         the tree characters.
  14.  *
  15.  *  OPTIONS
  16.  *         I have tried to make them as similar to Unix's 'ls' as possible
  17.  *
  18.  *         -[aA]  -  show hidden ("dot") files, but it never shows . or ..
  19.  *                   unless they are explicity given on the command line
  20.  *
  21.  *         -F     -  place a trailing '/' after directories, '@' after links,
  22.  *                   '=' after sockets, and '*' after executables
  23.  *
  24.  *         -L     -  follow symbolic links as if they were hard links, by
  25.  *                   default, it simply lists the link, a la ls
  26.  *
  27.  *         -d [n] -  instead of traverse down the directory structure as far
  28.  *                   as it goes or until ABSMAX_DEPTH is reached, traverse
  29.  *                   for n levels, or 1 if n is not given - however, I must
  30.  *                   n = 1 is NOT real interesting.  Then again, neither is
  31.  *                   'ls -d'
  32.  *
  33.  *         -e     -  interpret all following arguments as directory names
  34.  *  NOTES
  35.  *         so far works on Sun4, RS6000, DEC
  36.  *
  37.  *  BUGS
  38.  *         - using xterm escape codes is neither pretty nor reliable
  39.  *         - messes up the tree when the last thing in a given dir is
  40.  *             a subdir
  41.  *
  42.  *****************************************************************************/
  43.  
  44. #include <stdio.h>
  45. #include <dirent.h>
  46. #include <sys/types.h>
  47. #include <sys/stat.h>
  48. #include <errno.h>
  49. #include "xtree.h"
  50.  
  51. #define INTERPRET(errno, fname) \
  52.   switch( (errno) ) \
  53.     { \
  54.       case EACCES: \
  55.         fprintf(stderr, "%s: %s: permission denied\n", Progname, (fname)); \
  56.         break; \
  57.       case ENOENT: \
  58.         fprintf(stderr, "%s: %s: no such file or directory\n", \
  59.                 Progname, (fname)); \
  60.         break; \
  61.       case ENOTDIR: \
  62.         fprintf(stderr, "%s: %s: not a directory\n", Progname, (fname)); \
  63.         break; \
  64.     }
  65.                                        
  66. int max_depth = MAX_DEPTH;
  67. int islast[ABSMAX_DEPTH];
  68. char *Progname;
  69.  
  70. #ifdef PROTOTYPE
  71. void usage(void)
  72. #else
  73. void usage()
  74. #endif /* PROTOTYPE */
  75. {
  76.     fprintf(stderr, "Usage: %s -[aAFL] [-d [depth]] [-e] [<dir-name>+]\n",
  77.             Progname);
  78.     exit(-1);
  79. }
  80.  
  81. #ifdef PROTOTYPE
  82. void xtree(char *name, int depth, int flags)
  83. #else
  84. void xtree(name, depth, flags)
  85. char *name;
  86. int depth, flags;
  87. #endif /* PROTOTYPE */
  88. {
  89.     int i, err, go_up;
  90.     char *last, *next, *c;
  91.     struct stat filestat;
  92.     DIR *mydir;
  93.     struct dirent *mydirent;
  94.  
  95.     /* we know this won't fail, tested above */
  96.     mydir = opendir(name);
  97.  
  98.     /*
  99.      *  read in all directory entries, add all names
  100.      *  that will be shown to the name table
  101.      */
  102.     while((mydirent = readdir(mydir)) != NULL)
  103.       if(strcmp(mydirent->d_name, ".") && strcmp(mydirent->d_name, ".."))
  104.         if(*mydirent->d_name != '.' || flags & SHOWALL)
  105.           addname(mydirent->d_name, depth);
  106.  
  107.     if(closedir(mydir))
  108.       fprintf(stderr, "%s: couldn't close directory %s\n", Progname, name);
  109.  
  110.     /* now change to this directory so that stat can use just the name */
  111.     if(chdir(name))
  112.       {
  113.         INTERPRET(errno, name)
  114.         return;
  115.       }
  116.  
  117.     last = getname(depth);
  118.     while((next = getname(depth)) != NULL)
  119.       {
  120.         err = FALSE;
  121.         if(flags & GETLINK)
  122.           {
  123.             if(stat(last, &filestat))
  124.               {
  125.                 INTERPRET(errno, name)
  126.                   err = TRUE;
  127.               }
  128.           }
  129.         else
  130.           if(lstat(last, &filestat))
  131.             {
  132.               INTERPRET(errno, name)
  133.               err = TRUE;
  134.             }
  135.  
  136.         if(!err)
  137.           {
  138.             for(i = 0; i < depth - 1; i++)
  139.               printf("(0%c(B       ", (islast[i]) ? ' ' : 'x');
  140.             printf("(0%c(B       %s", 'x', last);
  141.  
  142.             if(flags & TRAILER)
  143.               {
  144.                 if(S_ISLNK(filestat.st_mode))
  145.                   printf("@");
  146.                 else if(S_ISDIR(filestat.st_mode))
  147.                   printf("/");
  148.                 else if(S_ISSOCK(filestat.st_mode))
  149.                   printf("=");
  150.                 else if(filestat.st_mode & S_IXUSR)
  151.                   printf("*");
  152.               }
  153.  
  154.             printf("\n");
  155.  
  156.             if(S_ISDIR(filestat.st_mode) && depth < max_depth - 1)
  157.               xtree(last, depth + 1, flags);
  158.           }
  159.         free(last);
  160.         last = (char *) malloc(strlen(next) + 1);
  161.         strcpy(last, next);
  162.       }
  163.  
  164.     err = FALSE;
  165.     if(last != NULL)
  166.       {
  167.         if(flags & GETLINK)
  168.           {
  169.             if(stat(last, &filestat))
  170.               {
  171.                 INTERPRET(errno, name)
  172.                 err = TRUE;
  173.               }
  174.           }
  175.         else
  176.           if(lstat(last, &filestat))
  177.             {
  178.               INTERPRET(errno, name)
  179.               err = TRUE;
  180.             }
  181.  
  182.         if(!err)
  183.           {
  184.             for (i = 0; i < depth - 1; i++)
  185.               printf("(0%c(B       ", (islast[i]) ? ' ' : 'x');
  186.             printf("(0%c(B       ", 'm');
  187.             printf("%s", last);
  188.  
  189.             if(flags & TRAILER)
  190.               {
  191.                 if(S_ISLNK(filestat.st_mode))
  192.                   printf("@");
  193.                 else if(S_ISDIR(filestat.st_mode))
  194.                   printf("/");
  195.                 else if(S_ISSOCK(filestat.st_mode))
  196.                   printf("=");
  197.                 else if(filestat.st_mode & S_IXUSR)
  198.                   printf("*");
  199.               }
  200.  
  201.             printf("\n");
  202.  
  203.             if(S_ISDIR(filestat.st_mode) && depth < max_depth - 1)
  204.           {
  205.         islast[depth - 1] = TRUE;
  206.         xtree(last, depth + 1, flags);
  207.         islast[depth - 1] = FALSE;
  208.           }
  209.           }
  210.       }
  211.     else
  212.       { /* valid directory, but it is empty */
  213.         for(i = 0; i < depth - 1; i++)
  214.           printf("(0%c(B       ", (islast[i]) ? ' ' : 'x');
  215.         printf("(0%c(B\n", 'm');
  216.       }
  217.  
  218.     /* count how many subdirectories - needed for 'xtree dir/subdir dir2' */
  219.     for(go_up = 1, c = name; *c; c++)
  220.       if(*c == '/')
  221.         go_up++;
  222.  
  223.     for(i = 0; i < go_up; i++)
  224.       if(chdir(".."))
  225.         {
  226.           fprintf(stderr, "%s: cannot move back up directory tree from %s\n",
  227.                   Progname, name);
  228.           exit(-1);
  229.         }
  230.  
  231.     return;
  232. }
  233.  
  234. #ifdef PROTOTYPE
  235. void main(int argc, char *argv[])
  236. #else    
  237. void main(argc, argv)
  238. int argc;
  239. char *argv[];
  240. #endif /* PROTOTYPE */
  241. {
  242.     int i, index, done, flags = 0;
  243.     char *next, *c, *cwdname, *shortname, *s;
  244.     struct stat filestat;
  245.  
  246.     Progname = (char *) malloc(strlen(argv[0] + 1));
  247.     strcpy(Progname, argv[0]);
  248.     for(i = 0; i < ABSMAX_DEPTH; i++) islast[i] = 0;
  249.  
  250.     /* process command-line options */
  251.     for(done = FALSE, index = 1;
  252.         index < argc && *argv[index] == '-' && !done;
  253.         index++)
  254.       for(c = argv[index] + 1; *c; c++)
  255.         switch(*c)
  256.           {
  257.             case 'a':
  258.             case 'A':
  259.               flags |= SHOWALL;
  260.               break;
  261.  
  262.             case 'F':
  263.               flags |= TRAILER;
  264.               break;
  265.  
  266.             case 'L':
  267.               flags |= GETLINK;
  268.               break;
  269.  
  270.             case 'd':
  271.               if(strcmp(argv[index], "-d")) /* can't give in combo */
  272.                 {
  273.                   fprintf(stderr, "%s: can't give -d ", Progname);
  274.                   fprintf(stderr, "option in a group\n");
  275.                   usage();
  276.                 }
  277.  
  278.               if((index == argc - 1) || !isdigit(*argv[index + 1]))
  279.                 max_depth = 1;
  280.               else
  281.                 {
  282.                   index++;
  283.                   max_depth = atoi(argv[index]);
  284.                   if(max_depth > ABSMAX_DEPTH)
  285.                     {
  286.                       fprintf(stderr, "%s: cannot exceed depth of %d\n",
  287.                               Progname, ABSMAX_DEPTH);
  288.                       max_depth = ABSMAX_DEPTH;
  289.                     }
  290.                 } /* else: isdigit(*argv[index]) */
  291.  
  292.               break;
  293.  
  294.             case 'e':
  295.               if(strcmp(argv[index], "-e")) /* can't give in combo */
  296.                 {
  297.                   fprintf(stderr, "%s, can't give -e option ", Progname);
  298.                   fprintf(stderr, "in a group\n");
  299.                   usage();
  300.                 }
  301.               done = TRUE;
  302.               break;
  303.  
  304.             default :
  305.               fprintf(stderr, "%s: unknown option '-%c'\n", Progname, *c);
  306.               usage();
  307.               break;
  308.           } /* end switch */
  309.  
  310.     init();
  311.     /* getcwd returns the full pathname, but we only want the local name */
  312.     cwdname = (char *) malloc(256);
  313.     cwdname = getcwd(cwdname, 256);
  314.     shortname = (char *) malloc(strlen(cwdname) + 1);
  315.     for(c = cwdname, s = shortname; *c; c++, s++)
  316.       {
  317.     if(*c == '/')
  318.       {
  319.         c++;
  320.         s = shortname;  /* start over */
  321.       }
  322.     *s = *c;
  323.       }
  324.     *s = '\0'; /* make it NULL-terminated */
  325.  
  326.     if(!strcmp(shortname, "")) /* at root */
  327.       strcpy(shortname, "/");
  328.  
  329.     if(index == argc)
  330.       {
  331.         printf("%s\n", shortname);
  332.         xtree(".", 1, flags);
  333.       }
  334.     else
  335.       {
  336.         for(i = index; i < argc; i++)
  337.           {
  338.             if(stat(argv[i], &filestat))
  339.               INTERPRET(errno, argv[i])
  340.             else
  341.               {
  342.                 if(S_ISDIR(filestat.st_mode))
  343.                   addname(argv[i], 0);
  344.                 else
  345.                   fprintf(stderr, "%s: %s not a directory\n",
  346.                           argv[0], argv[i]);
  347.               }
  348.           }
  349.     
  350.         while((next = getname(0)) != NULL)
  351.           {
  352.             printf("%s", next);
  353.             if(stat(next, &filestat))
  354.               INTERPRET(errno, next)
  355.             else
  356.               if(flags & TRAILER)
  357.                 {
  358.                   if(S_ISLNK(filestat.st_mode))
  359.                     printf("@");
  360.                   else if(S_ISDIR(filestat.st_mode))
  361.                     printf("/");
  362.                   else if(S_ISSOCK(filestat.st_mode))
  363.                     printf("=");
  364.                   else if(filestat.st_mode & S_IXUSR)
  365.                     printf("*");
  366.                 }
  367.  
  368.             printf("\n");
  369.             xtree(next, 1, flags);
  370.         if(chdir(cwdname))
  371.           {
  372.         fprintf(stderr, "%s: Unable to return to initial directory\n",
  373.             Progname);
  374.         exit(-1);
  375.           }
  376.           }
  377.       }
  378.     free(cwdname);
  379.     free(shortname);
  380.  
  381.     exit(0);
  382. }
  383.