home *** CD-ROM | disk | FTP | other *** search
- /******************************************************************************
- *
- * xtree - a graphical tree of your directories
- *
- * Mark Livingston - mark@acpub.duke.edu - Aug '92
- *
- * USAGE
- * xtree [ <dirname>+ ]
- *
- * DESCRIPTION
- * Gives a graphical tree of the given directory, with the default
- * being the current directory. Uses xterm escape codes to generate
- * the tree characters.
- *
- * OPTIONS
- * I have tried to make them as similar to Unix's 'ls' as possible
- *
- * -[aA] - show hidden ("dot") files, but it never shows . or ..
- * unless they are explicity given on the command line
- *
- * -F - place a trailing '/' after directories, '@' after links,
- * '=' after sockets, and '*' after executables
- *
- * -L - follow symbolic links as if they were hard links, by
- * default, it simply lists the link, a la ls
- *
- * -d [n] - instead of traverse down the directory structure as far
- * as it goes or until ABSMAX_DEPTH is reached, traverse
- * for n levels, or 1 if n is not given - however, I must
- * n = 1 is NOT real interesting. Then again, neither is
- * 'ls -d'
- *
- * -e - interpret all following arguments as directory names
- * NOTES
- * so far works on Sun4, RS6000, DEC
- *
- * BUGS
- * - using xterm escape codes is neither pretty nor reliable
- * - messes up the tree when the last thing in a given dir is
- * a subdir
- *
- *****************************************************************************/
-
- #include <stdio.h>
- #include <dirent.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <errno.h>
- #include "xtree.h"
-
- #define INTERPRET(errno, fname) \
- switch( (errno) ) \
- { \
- case EACCES: \
- fprintf(stderr, "%s: %s: permission denied\n", Progname, (fname)); \
- break; \
- case ENOENT: \
- fprintf(stderr, "%s: %s: no such file or directory\n", \
- Progname, (fname)); \
- break; \
- case ENOTDIR: \
- fprintf(stderr, "%s: %s: not a directory\n", Progname, (fname)); \
- break; \
- }
-
- int max_depth = MAX_DEPTH;
- int islast[ABSMAX_DEPTH];
- char *Progname;
-
- #ifdef PROTOTYPE
- void usage(void)
- #else
- void usage()
- #endif /* PROTOTYPE */
- {
- fprintf(stderr, "Usage: %s -[aAFL] [-d [depth]] [-e] [<dir-name>+]\n",
- Progname);
- exit(-1);
- }
-
- #ifdef PROTOTYPE
- void xtree(char *name, int depth, int flags)
- #else
- void xtree(name, depth, flags)
- char *name;
- int depth, flags;
- #endif /* PROTOTYPE */
- {
- int i, err, go_up;
- char *last, *next, *c;
- struct stat filestat;
- DIR *mydir;
- struct dirent *mydirent;
-
- /* we know this won't fail, tested above */
- mydir = opendir(name);
-
- /*
- * read in all directory entries, add all names
- * that will be shown to the name table
- */
- while((mydirent = readdir(mydir)) != NULL)
- if(strcmp(mydirent->d_name, ".") && strcmp(mydirent->d_name, ".."))
- if(*mydirent->d_name != '.' || flags & SHOWALL)
- addname(mydirent->d_name, depth);
-
- if(closedir(mydir))
- fprintf(stderr, "%s: couldn't close directory %s\n", Progname, name);
-
- /* now change to this directory so that stat can use just the name */
- if(chdir(name))
- {
- INTERPRET(errno, name)
- return;
- }
-
- last = getname(depth);
- while((next = getname(depth)) != NULL)
- {
- err = FALSE;
- if(flags & GETLINK)
- {
- if(stat(last, &filestat))
- {
- INTERPRET(errno, name)
- err = TRUE;
- }
- }
- else
- if(lstat(last, &filestat))
- {
- INTERPRET(errno, name)
- err = TRUE;
- }
-
- if(!err)
- {
- for(i = 0; i < depth - 1; i++)
- printf("(0%c(B ", (islast[i]) ? ' ' : 'x');
- printf("(0%c(B %s", 'x', last);
-
- if(flags & TRAILER)
- {
- if(S_ISLNK(filestat.st_mode))
- printf("@");
- else if(S_ISDIR(filestat.st_mode))
- printf("/");
- else if(S_ISSOCK(filestat.st_mode))
- printf("=");
- else if(filestat.st_mode & S_IXUSR)
- printf("*");
- }
-
- printf("\n");
-
- if(S_ISDIR(filestat.st_mode) && depth < max_depth - 1)
- xtree(last, depth + 1, flags);
- }
- free(last);
- last = (char *) malloc(strlen(next) + 1);
- strcpy(last, next);
- }
-
- err = FALSE;
- if(last != NULL)
- {
- if(flags & GETLINK)
- {
- if(stat(last, &filestat))
- {
- INTERPRET(errno, name)
- err = TRUE;
- }
- }
- else
- if(lstat(last, &filestat))
- {
- INTERPRET(errno, name)
- err = TRUE;
- }
-
- if(!err)
- {
- for (i = 0; i < depth - 1; i++)
- printf("(0%c(B ", (islast[i]) ? ' ' : 'x');
- printf("(0%c(B ", 'm');
- printf("%s", last);
-
- if(flags & TRAILER)
- {
- if(S_ISLNK(filestat.st_mode))
- printf("@");
- else if(S_ISDIR(filestat.st_mode))
- printf("/");
- else if(S_ISSOCK(filestat.st_mode))
- printf("=");
- else if(filestat.st_mode & S_IXUSR)
- printf("*");
- }
-
- printf("\n");
-
- if(S_ISDIR(filestat.st_mode) && depth < max_depth - 1)
- {
- islast[depth - 1] = TRUE;
- xtree(last, depth + 1, flags);
- islast[depth - 1] = FALSE;
- }
- }
- }
- else
- { /* valid directory, but it is empty */
- for(i = 0; i < depth - 1; i++)
- printf("(0%c(B ", (islast[i]) ? ' ' : 'x');
- printf("(0%c(B\n", 'm');
- }
-
- /* count how many subdirectories - needed for 'xtree dir/subdir dir2' */
- for(go_up = 1, c = name; *c; c++)
- if(*c == '/')
- go_up++;
-
- for(i = 0; i < go_up; i++)
- if(chdir(".."))
- {
- fprintf(stderr, "%s: cannot move back up directory tree from %s\n",
- Progname, name);
- exit(-1);
- }
-
- return;
- }
-
- #ifdef PROTOTYPE
- void main(int argc, char *argv[])
- #else
- void main(argc, argv)
- int argc;
- char *argv[];
- #endif /* PROTOTYPE */
- {
- int i, index, done, flags = 0;
- char *next, *c, *cwdname, *shortname, *s;
- struct stat filestat;
-
- Progname = (char *) malloc(strlen(argv[0] + 1));
- strcpy(Progname, argv[0]);
- for(i = 0; i < ABSMAX_DEPTH; i++) islast[i] = 0;
-
- /* process command-line options */
- for(done = FALSE, index = 1;
- index < argc && *argv[index] == '-' && !done;
- index++)
- for(c = argv[index] + 1; *c; c++)
- switch(*c)
- {
- case 'a':
- case 'A':
- flags |= SHOWALL;
- break;
-
- case 'F':
- flags |= TRAILER;
- break;
-
- case 'L':
- flags |= GETLINK;
- break;
-
- case 'd':
- if(strcmp(argv[index], "-d")) /* can't give in combo */
- {
- fprintf(stderr, "%s: can't give -d ", Progname);
- fprintf(stderr, "option in a group\n");
- usage();
- }
-
- if((index == argc - 1) || !isdigit(*argv[index + 1]))
- max_depth = 1;
- else
- {
- index++;
- max_depth = atoi(argv[index]);
- if(max_depth > ABSMAX_DEPTH)
- {
- fprintf(stderr, "%s: cannot exceed depth of %d\n",
- Progname, ABSMAX_DEPTH);
- max_depth = ABSMAX_DEPTH;
- }
- } /* else: isdigit(*argv[index]) */
-
- break;
-
- case 'e':
- if(strcmp(argv[index], "-e")) /* can't give in combo */
- {
- fprintf(stderr, "%s, can't give -e option ", Progname);
- fprintf(stderr, "in a group\n");
- usage();
- }
- done = TRUE;
- break;
-
- default :
- fprintf(stderr, "%s: unknown option '-%c'\n", Progname, *c);
- usage();
- break;
- } /* end switch */
-
- init();
- /* getcwd returns the full pathname, but we only want the local name */
- cwdname = (char *) malloc(256);
- cwdname = getcwd(cwdname, 256);
- shortname = (char *) malloc(strlen(cwdname) + 1);
- for(c = cwdname, s = shortname; *c; c++, s++)
- {
- if(*c == '/')
- {
- c++;
- s = shortname; /* start over */
- }
- *s = *c;
- }
- *s = '\0'; /* make it NULL-terminated */
-
- if(!strcmp(shortname, "")) /* at root */
- strcpy(shortname, "/");
-
- if(index == argc)
- {
- printf("%s\n", shortname);
- xtree(".", 1, flags);
- }
- else
- {
- for(i = index; i < argc; i++)
- {
- if(stat(argv[i], &filestat))
- INTERPRET(errno, argv[i])
- else
- {
- if(S_ISDIR(filestat.st_mode))
- addname(argv[i], 0);
- else
- fprintf(stderr, "%s: %s not a directory\n",
- argv[0], argv[i]);
- }
- }
-
- while((next = getname(0)) != NULL)
- {
- printf("%s", next);
- if(stat(next, &filestat))
- INTERPRET(errno, next)
- else
- if(flags & TRAILER)
- {
- if(S_ISLNK(filestat.st_mode))
- printf("@");
- else if(S_ISDIR(filestat.st_mode))
- printf("/");
- else if(S_ISSOCK(filestat.st_mode))
- printf("=");
- else if(filestat.st_mode & S_IXUSR)
- printf("*");
- }
-
- printf("\n");
- xtree(next, 1, flags);
- if(chdir(cwdname))
- {
- fprintf(stderr, "%s: Unable to return to initial directory\n",
- Progname);
- exit(-1);
- }
- }
- }
- free(cwdname);
- free(shortname);
-
- exit(0);
- }
-