home *** CD-ROM | disk | FTP | other *** search
- /* vtree
-
- +=======================================+
- | This program is in the public domain. |
- +=======================================+
-
- This program shows the directory structure of a filesystem or
- part of one. It also shows the amount of space taken up by files
- in each subdirectory.
-
- Call via
-
- vtree fn1 fn2 fn3 ...
-
- If any of the given filenames is a directory (the usual case),
- vtree will recursively descend into it, and the output line will
- reflect the accumulated totals for all files in the directory.
-
- This program is based upon "agef" written by David S. Hayes at the
- Army Artificial Intelligence Center at the Pentagon.
-
- This program is dependent upon the new directory routines written by
- Douglas A. Gwyn at the US Army Ballistic Research Laboratory at the
- Aberdeen Proving Ground in Maryland.
- */
- /*
- ** Patches were received from the following people:
- **
- ** 1. Mike Howard, (...!uunet!milhow1!how)
- ** Mike's patches included changes to the Makefile to
- ** customize vtree to SCO Xenix for the 286 as well as the
- ** 386. He also added external definitions to hash.c
- **
- ** 2. Andrew Weeks, (...!uunet!mcvax!doc.ic.ac.uk!aw)
- ** Andrew sent me diffs to make vtree work properly under BSD
- ** He also pointed out that you will need one of the PD getopt
- ** packages for BSD.
- **
- ** 3. Ralph Chapman, (...uunet!ihnp4!ihuxy!chapman)
- ** Ralph sent me changes (not diffs unfortunately) to make
- ** vtree work properly under the SYS_III option. His changes
- ** were in direct.c and vtree.c
- **
- ** 4. David Eckelkamp notified me of a bug when printing the
- ** visual tree. The bug occured when a directory name
- ** was too long. It caused vtree to mess up the tree
- ** being printed.
- */
-
- #include "patchlevel.h"
-
- #include <ctype.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <sys/param.h>
- #include <stdio.h>
- #ifdef BSD
- #include <strings.h>
- #else
- #include <string.h>
- #endif
-
- #include "customize.h"
- #include "hash.h"
-
-
- #ifdef SYS_III
- #define rewinddir(fp) rewind(fp)
- #endif
-
- #define SAME 0 /* for strcmp */
- #define BLOCKSIZE 512 /* size of a disk block */
-
- #define K(x) ((x + 1023)/1024) /* convert stat(2) blocks into
- * k's. On my machine, a block
- * is 512 bytes. */
-
- #define TRUE 1
- #define FALSE 0
- #define V_CHAR "|" /* Vertical character */
- #define H_CHAR "-" /* Horizontal character */
- #define A_CHAR ">" /* Arrow char */
- #define T_CHAR "+" /* Tee char */
- #define L_CHAR "\\" /* L char, bottom of a branch */
-
- #define MAX_COL_WIDTH 15
- #define MAX_V_DEPTH 256 /* max depth for visual display */
-
- #ifdef MEMORY_BASED
- struct RD_list {
- READ entry;
- struct RD_list *fptr;
- struct RD_list *bptr;
- };
- #endif
-
-
-
- int indent = 0, /* current indent */
- depth = 9999, /* max depth */
- cur_depth = 0,
- sum = FALSE, /* sum the subdirectories */
- dup = FALSE, /* use duplicate inodes */
- floating = FALSE, /* floating column widths */
- sort = FALSE,
- cnt_inodes = FALSE, /* count inodes */
- quick = FALSE, /* quick display */
- visual = FALSE, /* visual display */
- version = 0, /* = 1 display version, = 2 show options */
- sub_dirs[MAX_V_DEPTH],
- sub_dirs_indents[MAX_V_DEPTH];
-
- struct stat stb; /* Normally not a good idea, but */
- /* this structure is used through- */
- /* out the program */
-
- extern char *optarg; /* from getopt(3) */
- extern int optind,
- opterr;
-
-
- char *Program; /* our name */
- short sw_follow_links = 1; /* follow symbolic links */
- short sw_summary; /* print Grand Total line */
-
- int total_inodes, inodes; /* inode count */
- long total_sizes, sizes; /* block count */
-
- char topdir[NAMELEN]; /* our starting directory */
-
-
-
- /*
- ** Find the last field of a string.
- */
- char *lastfield(p,c)
- char *p; /* Null-terminated string to scan */
- int c; /* Separator char, usually '/' */
- {
- char *r;
-
- r = p;
- while (*p) /* Find the last field of the name */
- if (*p++ == c)
- r = p;
- return r;
- } /* lastfield */
-
-
-
-
- /*
- * We ran into a subdirectory. Go down into it, and read everything
- * in there.
- */
- int indented = FALSE; /* These had to be global since they */
- int last_indent = 0; /* determine what gets displayed during */
- int last_subdir = FALSE; /* the visual display */
-
-
-
- down(subdir)
- char *subdir;
- {
- OPEN *dp; /* stream from a directory */
- OPEN *opendir ();
- char cwd[NAMELEN], tmp[NAMELEN];
- char *sptr;
- READ *file; /* directory entry */
- READ *readdir ();
- int i, x;
- struct stat stb;
-
- #ifdef MEMORY_BASED
- struct RD_list *head = NULL, *tail, *tmp_RD, *tmp1_RD; /* head and tail of directory list */
- struct RD_list sz;
- READ tmp_entry;
- #endif
-
- if ( (cur_depth == depth) && (!sum) )
- return;
-
- /* display the tree */
-
- if (cur_depth < depth) {
- if (visual) {
- if (!indented) {
- for (i = 1; i <cur_depth; i++) {
- if (floating) x = sub_dirs_indents[i] + 1;
- else x = MAX_COL_WIDTH - 3;
- if (sub_dirs[i]) {
- printf("%*s%s ",x - 1," ",V_CHAR);
- } else printf("%*s ",x," ");
- }
- if (cur_depth>0) {
- if (floating) x = sub_dirs_indents[cur_depth] + 1;
- else x = MAX_COL_WIDTH - 3;
- if (sub_dirs[cur_depth] == 0) {
- printf("%*s%s%s%s ",x - 1," ",L_CHAR,H_CHAR,A_CHAR);
- last_subdir = cur_depth;
- }
- else printf("%*s%s%s%s ",x - 1," ",T_CHAR,H_CHAR,A_CHAR);
- }
- } else {
- if (!floating)
- for (i = 1; i<MAX_COL_WIDTH-last_indent-3; i++)
- printf("%s",H_CHAR);
- printf("%s%s%s ",T_CHAR,H_CHAR,A_CHAR);
- }
-
- /* This is in case a subdir name is too big. It is then displayed on
- ** two lines, the first line is the full name, the second line is
- ** truncated. Any subdirs displayed for the current subdir will be
- ** appended to the second line. This keeps the columns in order
- */
-
- #ifndef ONEPERLINE
- if ( ( strlen(subdir) > MAX_COL_WIDTH - 3 && !floating ) ) {
- #else
- if ( ( strlen(subdir) > MAX_COL_WIDTH - 3 && !floating ) ||
- lastfield(subdir,'/') != subdir) {
- #endif
-
- printf("%s\n",subdir);
- for (i = 1; i <=cur_depth; i++) {
- if (sub_dirs[i]) {
- printf("%*s%s ",MAX_COL_WIDTH-4," ",V_CHAR);
- }
- else printf("%*s ",MAX_COL_WIDTH-3," ");
- }
- strcpy(tmp,lastfield(subdir,'/'));
- tmp[MAX_COL_WIDTH - 4] = 0;
- printf("%s",tmp);
- #ifdef ONEPERLINE
- if (floating || strlen(tmp) < MAX_COL_WIDTH - 4) printf(" ");
- #endif
- sub_dirs_indents[cur_depth + 1] = last_indent = strlen(tmp) + 1;
- }
- else {
- printf("%s",subdir);
- sub_dirs_indents[cur_depth + 1] = last_indent = strlen(subdir)+1;
- if (floating || strlen(subdir) < MAX_COL_WIDTH - 4)
- printf(" ");
- }
- indented = TRUE;
- }
- else printf("%*s%s",indent," ",subdir);
- }
-
- /* open subdirectory */
-
- if ((dp = opendir(subdir)) == NULL) {
- printf(" - can't read %s\n", subdir);
- indented = FALSE;
- return;
- }
-
- cur_depth++;
- indent+=3;
-
- #ifdef BSD
- getwd(cwd); /* remember where we are */
- #else
- getcwd(cwd, sizeof(cwd)); /* remember where we are */
- #endif
- chdir(subdir); /* go into subdir */
-
-
- #ifdef MEMORY_BASED
-
- for (file = readdir(dp); file != NULL; file = readdir(dp)) {
- if ((!quick && !visual ) ||
- ( strcmp(NAME(*file), "..") != SAME &&
- strcmp(NAME(*file), ".") != SAME &&
- chk_4_dir(NAME(*file)) ) ) {
- tmp_RD = (struct RD_list *) malloc(sizeof(sz));
- memcpy(&tmp_RD->entry, file, sizeof(tmp_entry));
- tmp_RD->bptr = head;
- tmp_RD->fptr = NULL;
- if (head == NULL) head = tmp_RD;
- else tail->fptr = tmp_RD;
- tail = tmp_RD;
- }
- }
-
- /* screwy, inefficient, bubble sort */
- /* but it works */
- if (sort) {
- tmp_RD = head;
- while (tmp_RD) {
- tmp1_RD = tmp_RD->fptr;
- while (tmp1_RD) {
- if (strcmp(NAME(tmp_RD->entry), NAME(tmp1_RD->entry)) >0) {
- /* swap the two */
- memcpy(&tmp_entry, &tmp_RD->entry, sizeof(tmp_entry));
- memcpy(&tmp_RD->entry, &tmp1_RD->entry, sizeof(tmp_entry));
- memcpy(&tmp1_RD->entry, &tmp_entry, sizeof(tmp_entry));
- }
- tmp1_RD = tmp1_RD->fptr;
- }
- tmp_RD = tmp_RD->fptr;
- }
- }
-
- #endif
-
- if ( (!quick) && (!visual) ) {
-
- /* accumulate total sizes and inodes in current directory */
-
-
- #ifdef MEMORY_BASED
- tmp_RD = head;
- while (tmp_RD) {
- file = &tmp_RD->entry;
- tmp_RD = tmp_RD->fptr;
- #else
-
- for (file = readdir(dp); file != NULL; file = readdir(dp)) {
- #endif
- if (strcmp(NAME(*file), "..") != SAME)
- get_data(NAME(*file),FALSE);
- }
-
- if (cur_depth<depth) {
- if (cnt_inodes) printf(" %d",inodes);
- printf(" : %ld\n",sizes);
- total_sizes += sizes;
- total_inodes += inodes;
- sizes = 0;
- inodes = 0;
- }
- #ifndef MEMORY_BASED
- rewinddir(dp);
- #endif
- } else if (!visual) printf("\n");
-
- if (visual) {
-
- /* count subdirectories */
-
-
- #ifdef MEMORY_BASED
- tmp_RD = head;
- while (tmp_RD) {
- file = &tmp_RD->entry;
- tmp_RD = tmp_RD->fptr;
- #else
- for (file = readdir(dp); file != NULL; file = readdir(dp)) {
- if ( (strcmp(NAME(*file), "..") != SAME) &&
- (strcmp(NAME(*file), ".") != SAME) ) {
- if (chk_4_dir(NAME(*file))) {
- #endif
- sub_dirs[cur_depth]++;
- #ifndef MEMORY_BASED
- }
- }
- #endif
- }
- #ifndef MEMORY_BASED
- rewinddir(dp);
- #endif
- }
-
- /* go down into the subdirectory */
-
- #ifdef MEMORY_BASED
- tmp_RD = head;
- while (tmp_RD) {
- file = &tmp_RD->entry;
- tmp_RD = tmp_RD->fptr;
- #else
- for (file = readdir(dp); file != NULL; file = readdir(dp)) {
- #endif
- if ( (strcmp(NAME(*file), "..") != SAME) &&
- (strcmp(NAME(*file), ".") != SAME) ) {
- if (chk_4_dir(NAME(*file)))
- sub_dirs[cur_depth]--;
- get_data(NAME(*file),TRUE);
- }
- }
-
- if ( (!quick) && (!visual) ) {
-
- /* print totals */
-
- if (cur_depth == depth) {
- if (cnt_inodes) printf(" %d",inodes);
- printf(" : %ld\n",sizes);
- total_sizes += sizes;
- total_inodes += inodes;
- sizes = 0;
- inodes = 0;
- }
- }
-
- #ifdef MEMORY_BASED
- /* free the allocated memory */
- tmp_RD = head;
- while (tmp_RD) {
- tmp_RD = tmp_RD->fptr;
- free(head);
- head = tmp_RD;
- }
- #endif
-
- if (visual && indented) {
- printf("\n");
- indented = FALSE;
- if (last_subdir>=cur_depth-1) {
- for (i = 1; i <cur_depth; i++) {
- if (sub_dirs[i]) {
- if (floating)
- printf("%*s%s ",sub_dirs_indents[i]," ",V_CHAR);
- else printf("%*s%s ",MAX_COL_WIDTH-4," ",V_CHAR);
- } else {
- if (floating)
- /*ZZZ*/ printf("%*s ",sub_dirs_indents[i] + 1," ");
- else printf("%*s ",MAX_COL_WIDTH-3," ");
- }
- }
- printf("\n");
- last_subdir = FALSE;
- }
- }
- indent-=3;
- sub_dirs[cur_depth] = 0;
- cur_depth--;
-
- chdir(cwd); /* go back where we were */
- closedir(dp); /* shut down the directory */
-
-
- } /* down */
-
-
-
- int chk_4_dir(path)
- char *path;
- {
- if (is_directory(path)) return TRUE;
- else return FALSE;
-
- } /* chk_4_dir */
-
-
-
- /* Is the specified path a directory ? */
-
- int is_directory(path)
- char *path;
- {
-
- #ifdef LSTAT
- if (sw_follow_links)
- stat(path, &stb); /* follows symbolic links */
- else
- lstat(path, &stb); /* doesn't follow symbolic links */
- #else
- stat(path, &stb);
- #endif
-
- if ((stb.st_mode & S_IFMT) == S_IFDIR)
- return TRUE;
- else return FALSE;
- } /* is_directory */
-
-
-
- /*
- * Get the aged data on a file whose name is given. If the file is a
- * directory, go down into it, and get the data from all files inside.
- */
-
- get_data(path,cont)
- char *path;
- int cont;
- {
- /* struct stat stb; */
- int i;
-
- if (cont) {
- if (is_directory(path))
- down(path);
- }
- else {
- if (is_directory(path)) return;
-
- /* Don't do it again if we've already done it once. */
-
- if ( (h_enter(stb.st_dev, stb.st_ino) == OLD) && (!dup) )
- return;
- inodes++;
- sizes+= K(stb.st_size);
- }
- } /* get_data */
-
-
-
- main(argc, argv)
- int argc;
- char *argv[];
- {
- int i,
- j,
- err = FALSE;
- int option;
- int user_file_list_supplied = 0;
-
- Program = *argv; /* save our name for error messages */
-
- /* Pick up options from command line */
-
- while ((option = getopt(argc, argv, "dfh:iostqvV")) != EOF) {
- switch (option) {
- case 'f': floating = TRUE; break;
- case 'h': depth = atoi(optarg);
- while (*optarg) {
- if (!isdigit(*optarg)) {
- err = TRUE;
- break;
- }
- optarg++;
- }
- break;
- case 'd': dup = TRUE;
- break;
- case 'i': cnt_inodes = TRUE;
- break;
- case 'o': sort = TRUE; break;
- case 's': sum = TRUE;
- break;
- case 't': sw_summary = TRUE;
- break;
- case 'q': quick = TRUE;
- dup = FALSE;
- sum = FALSE;
- cnt_inodes = FALSE;
- break;
- case 'v': visual = TRUE;
- break;
- case 'V': version++;
- break;
- default: err = TRUE;
- }
- if (err) {
- fprintf(stderr,"%s: [ -d ] [ -h # ] [ -i ] [ -o ] [ -s ] [ -q ] [ -v ] [ -V ]\n",Program);
- fprintf(stderr," -d count duplicate inodes\n");
- fprintf(stderr," -f floating column widths\n");
- fprintf(stderr," -h # height of tree to look at\n");
- fprintf(stderr," -i count inodes\n");
- fprintf(stderr," -o sort directories before processing\n");
- fprintf(stderr," -s include subdirectories not shown due to -h option\n");
- fprintf(stderr," -t totals at the end\n");
- fprintf(stderr," -q quick display, no counts\n");
- fprintf(stderr," -v visual display\n");
- fprintf(stderr," -V show current version\n");
- fprintf(stderr," (2 Vs shows specified options)\n");
- exit(-1);
- }
-
- }
-
- if (version > 0 ) {
-
- #ifdef MEMORY_BASED
- printf("%s memory based\n",VERSION);
- #else
- printf("%s disk based\n",VERSION);
- #endif
-
- if (version>1) {
- printf("Tree height: %d\n",depth);
- if (dup) printf("Include duplicate inodes\n");
- if (cnt_inodes) printf("Count inodes\n");
- if (sum) printf("Include unseen subdirectories in totals\n");
- if (sw_summary) printf("Print totals at end\n");
- if (quick) printf("Quick display only\n");
- if (visual) printf("Visual tree\n");
- if (sort) printf("Sort directories before processing\n");
- }
- }
-
- /* If user didn't specify targets, inspect current directory. */
-
- if (optind >= argc) {
- user_file_list_supplied = 0;
- } else {
- user_file_list_supplied = 1;
- }
-
- #ifdef BSD
- getwd(topdir); /* find out where we are */
- #else
- getcwd(topdir, sizeof (topdir)); /* find out where we are */
- #endif
-
- /* Zero out grand totals */
- total_inodes = total_sizes = 0;
- /* Zero out sub_dirs */
- for (i=0; i<=MAX_V_DEPTH; i++) {
- sub_dirs[i] = 0;
- sub_dirs_indents[i] = 0;
- }
-
- /* Inspect each argument */
- for (i = optind; i < argc || (!user_file_list_supplied && i == argc); i++) {
- cur_depth = inodes = sizes = 0;
-
- chdir(topdir); /* be sure to start from the same place */
- get_data(user_file_list_supplied?argv[i] : topdir, TRUE);/* this may change our cwd */
-
- total_inodes += inodes;
- total_sizes += sizes;
- }
-
- if (sw_summary) {
- printf("\n\nTotal space used: %ld\n",total_sizes);
- if (cnt_inodes) printf("Total inodes: %d\n",inodes);
- }
-
- #ifdef HSTATS
- fflush(stdout);
- h_stats();
- #endif
-
- exit(0);
- } /* main */
-
-
-