From: utzoo!decvax!duke!harpo!npoiv!npois!houxm!houxa!houxi!houxz!ihnp4!stolaf!borman Newsgroups: net.sources Title: tree.c Article-I.D.: stolaf.769 Posted: Wed Jan 19 21:50:48 1983 Received: Fri Jan 21 07:46:05 1983 /* * TREE - Print the tree structure of a directory * Version 2.0 * * Dave Borman, St. Olaf College. * {ihnp4|harpo|minn-ua}!stolaf!{borman|sys} * Copyright (c) 1983 by Dave Borman * All rights reserved * Permission is hereby given for use by valid UNIX(TM) licencees. * This program may not be sold, but may be distributed * provided this header is included. * * This is a preliminary relese. If you make any changes, please * notify me at the above address of your mods, especialy of * changes to allow it to run under flavors of UNIX other than V7. * * Compile: cc -O tree.c -i -n -o tree * Usage: tree -[adflnpsvx] [-c length] [top] * Flags: -a) include non-directory entries in listing * -d) sort tree with directories at the top * -f) sort tree with files at the top * -l) print stats with each listing * -n) do not sort the tree * -p) include files starting with a '.' (except "." & "..") * -s) use shorter stats. Implies -l * -v) variable length columns * -x) do not cross mounted file systems. * -c length) set max column length to "length" */ #define STATS /* comment out to remove stats, giving more core space */ /* and thus the ability to tree larger tree structures */ #define STO /* allows for local mod to nami. "name:" gets mapped */ /* to "/user/name" or "/libe/name". */ #include#include #include #ifdef STATS #include #endif STATS #define DEPTH 10 /* maximum depth that tree will go */ #define MAX 100 /* initial # of elements for list of files */ #define FFIRST 2 /* sort files first */ #define DFIRST 1 /* sort directories first */ int index = 0; /* current element of list[] */ int length = DIRSIZ; /* max length of a column */ int all = 0; /* all != 0; list non-directory entries */ int file_dir = 0; /* flag for how to sort */ int sort = 1; /* flag to cause sorting of entries */ int point = 1; /* skip point files if set */ int maxes[DEPTH]; /* array keeps track of max length in columns */ int level = 0; /* counter for how deep we are */ int device; /* device that we are starting tree on */ int xdev = 1; /* set to allow crossing of devices */ int varspaces = 0; /* set to allow compaction of column width */ #ifdef STATS int longflg = 0; /* set for long listing */ int compact = 0; /* set for shortened long listing */ #endif STATS struct stat status; struct entry { union { ino_t e_ino; struct { unsigned dir : 1; /* entry is a directory */ unsigned last : 1; /* last entry in the dir. */ unsigned dev : 1; /* set if same device as top */ unsigned end : 13; /* index of last subdir entry */ } flags; } u; char e_name[DIRSIZ]; /* name from directory entry */ int next; /* index to next element in list */ #ifdef STATS unsigned short e_mode; /* file mode */ short e_uid; /* uid of owner */ off_t e_size; /* size in blocks */ #endif STATS } *list; unsigned size = sizeof(struct entry)*MAX; char *spaces = " "; /* used for output */ main(argc, argv) char **argv; int argc; { int j = 0; int flag = 0; /* used to jump over 'c' argument */ int i; char top[128]; /* array for treetop name */ char *malloc(); char *rindex(); char *slash; /* used for rindexing for a / in treetop */ #ifdef STO char *colon; #endif strcpy(top, ".\0"); for (j=1; j DIRSIZ || length < 1) length = DIRSIZ; flag = 1; break; case 'd': file_dir = DFIRST; break; case 'f': file_dir = FFIRST; break; #ifdef STATS case 'l': longflg = 1; break; #endif STATS case 'n': sort = 0; break; case 'p': point = 0; break; #ifdef STATS case 's': longflg = 1; compact = 1; break; #endif STATS case 'v': varspaces = 1; break; case 'x': xdev = 0; break; default: printf("Bad flag: %c\n", argv[j][i]); exit(-1); } } } else if (j < argc-1) { #ifdef STATS printf("Usage: tree -[adflnpsvx] [-c length] [top]\n"); #else STATS printf("Usage: tree -[adfnpvx] [-c length] [top]\n"); #endif STATS exit(-1); } else strcpy(top, argv[j]); } list = (struct entry *)malloc(size); /* get initial storage space */ slash = rindex(top, '/'); #ifdef STO colon = rindex(top, ':'); if (slash) { if (colon && colon > slash && *(colon+1)) strncpy(list[index].e_name, ++colon, DIRSIZ); else strncpy(list[index].e_name, ++slash, DIRSIZ); } else { if (colon && *(colon+1)) strncpy(list[index].e_name, ++colon, DIRSIZ); else strncpy(list[index].e_name, top, DIRSIZ); } #else if (slash) /* strip off begining of name */ strncpy(list[index].e_name, ++slash, DIRSIZ); #endif STO if (stat(top,&status) == -1) { printf("Can't stat %s\n", top); exit(1); } device = status.st_dev; list[0].u.flags.dir = 1; list[0].u.flags.last = 1; list[0].next = 1; index = 1; for (i = 1; i < DEPTH; i++) maxes[i] = 0; maxes[0] = stln(list[0].e_name); level = 1; list[0].u.flags.end = t_search(top, &list[0]); /* search the tree */ if (index == 1) /* empty tree */ list[0].next = 0; pt_tree(); /* print the tree */ } t_search(dir, addrs) char *dir; struct entry *addrs; { int bsort; /* index to begin sort */ int stmp; /* save temporary index value */ struct entry *sstep; /* saved step in list */ int nitems; /* # of items in this directory */ int fd; /* file descriptor for directory */ char sub[DEPTH*15]; /* used to create subdirectory name */ int i; int compar(); /* comparison routine for qsort */ int n_subs = 0; int err(); int tmp = 0; extern qsort(); char *realloc(); if ((fd = open(dir, 0)) == -1) { printf("Cannot open %s\n", dir); return(0); } bsort = index; sstep = &list[bsort]; /* initialize sstep for for loop later on */ nitems = index; /* get the entries of the directory that we are interested in */ while (read(fd, (char *)&list[index], sizeof(struct direct)) != 0) { if (!list[index].u.e_ino || !(strcmp(list[index].e_name, ".")) || !(strcmp(list[index].e_name, "..")) || (point && list[index].e_name[0] == '.')) continue; strcpy(sub, dir); strcat(sub, "/"); strncat(sub, list[index].e_name, DIRSIZ); tmp = stat(sub, &status); if (tmp == -1) { printf("%s:stat can't find\n", sub); continue; } if ((status.st_mode & S_IFMT) == S_IFDIR) list[index].u.flags.dir = 1; else if (all) list[index].u.flags.dir = 0; else continue; list[index].u.flags.last = 0; list[index].u.flags.end = 0; list[index].u.flags.dev = (device == status.st_dev); #ifdef STATS list[index].e_mode = status.st_mode; list[index].e_uid = status.st_uid; list[index].e_size = status.st_size; #endif STATS if (stln(list[index].e_name) > maxes[level]) maxes[level] = stln(list[index].e_name); ++index; if (index*sizeof(struct entry) >= size) { size += 20*(sizeof(struct entry)); if (!(list = (struct entry *)realloc((char *)list, size))) { printf("Out of space\n"); break; } } } close(fd); nitems = index - nitems; /* nitems now contains the # of */ /* items in this dir, rather than */ /* # total items before this dir */ if (sort) qsort(&list[bsort], nitems, sizeof(struct entry), compar); list[index-1].u.flags.last = 1; /* mark last item for this dir */ n_subs = nitems; stmp = index; /* now walk through, and recurse on directory entries */ /* sstep was initialized above */ for (i = 0; i < nitems; sstep = &list[stmp - nitems+(++i)]) { if (sstep->u.flags.dir && (xdev || sstep->u.flags.dev)) { sstep->next = index; strcpy(sub, dir); strcat(sub, "/"); strncat(sub, sstep->e_name, DIRSIZ); tmp = n_subs; level++; n_subs += t_search(sub, sstep); --level; if (!(n_subs - tmp)) sstep->next = 0; } else sstep->next = 0; } addrs->u.flags.end = (unsigned)n_subs; return(n_subs); } /* * comparison routine for qsort */ compar(a, b) struct entry *a; struct entry *b; { if (!file_dir) /* straight alphabetical */ return(strncmp(a->e_name, b->e_name, DIRSIZ)); /* sort alphabetically if both dirs or both not dirs */ if ((a->u.flags.dir && b->u.flags.dir) || (!a->u.flags.dir && !b->u.flags.dir)) return(strncmp(a->e_name, b->e_name, DIRSIZ)); if (file_dir == FFIRST) { /* sort by files first */ if (a->u.flags.dir) return(1); else return(-1); } if (a->u.dir) /* sort by dir first */ return(-1); else return(1); } pt_tree() { struct entry *l; struct entry *hdr[DEPTH]; int posit[DEPTH]; /* array of positions to print dirs */ int con[DEPTH]; /* flags for connecting up tree */ char flag; /* flag to leave blank line after dir */ int i,j; struct entry *stack[DEPTH]; /* save positions for changing levels */ int top = 0; /* index to top of stack */ int count = 0; /* count of line of output */ #ifdef STATS char *getmode(); extern struct passwd *getpwuid(); #endif STATS level = 0; /* initialize level */ /* this loop appends each entry with dashes or spaces, for */ /* directories or files respectively */ for (i = 0; i < index; i++) { for (j = 0; j < DIRSIZ; j++) { if (!list[i].e_name[j]) break; } if (list[i].u.flags.dir) { for (; j < DIRSIZ; j++) list[i].e_name[j] = '-'; } else { for (; j < DIRSIZ; j++) list[i].e_name[j] = ' '; } } /* adjust the maxes array according to the flags */ for (i = 0; i < DEPTH; i++) { if (varspaces) { if (maxes[i] > length ) maxes[i] = length; } else maxes[i] = length; } /* clear the connective and position flags */ for (i = 0; i < DEPTH; i++) con[i] = posit[i] = 0; /* this is the main loop to print the tree structure. */ l = list; for (;;) { flag = 0; /* directory entry, save it for later printing */ if (l->u.flags.dir != 0 && l->u.flags.next != 0) { hdr[level] = l; posit[level] = count + l->u.flags.end/2 + 1; flag = 1; } #ifdef STATS do_it_again: #endif STATS /* print columns up to our entry */ for (j = 0; j < level; j++) { if (posit[j] == count) { /* time to print it */ printf("|-%.*s",maxes[j],hdr[j]->e_name); posit[j] = 0; if (hdr[j]->last != 0) con[j] = 0; else con[j] = 1; #ifdef STATS if (longflg) { for (++j; j <= level; j++) printf("| %.*s",maxes[j],spaces); if (!compact) { printf("%s %8.8s %7D\n", getmode(l->e_mode), getpwuid(l->e_uid)->pw_name, (l->e_size+511L)/512L); } else { printf(" %04o %5d %7D\n", l->e_mode & 07777, l->e_uid, (l->e_size+511L)/512L); } goto do_it_again; } #endif STATS } else if (con[j]) printf("| %.*s",maxes[j],spaces); else printf(" %.*s",maxes[j],spaces); } if (flag) { /* directory just gotten, don't print it */ if (con[j]) printf("|"); } else { /* normal file name, print it out */ printf("|-%.*s",maxes[level],l->e_name); if (l->u.flags.last) { con[j] = 0; } else { con[j] = 1; } #ifdef STATS if (longflg) { if (compact) { printf(" %04o %5d %7D", l->e_mode & 07777, l->e_uid, (l->e_size+511L)/512L); } else { printf("%s %8.8s %7D", getmode(l->e_mode), getpwuid(l->e_uid)->pw_name, (l->e_size+511L)/512L); } } #endif STATS } printf("\n"); if (l->next) { /* redirect to sub-directory */ stack[top++] = l; l = &list[l->next]; ++level; } else { if (l->u.flags.last) /* walk back up */ while (l->u.flags.last) { --level; if (--top <= 0) return; l = stack[top]; } ++l; } ++count; } } #ifdef STATS /* take the mode and make it into a nice character string */ char * getmode(p_mode) unsigned short p_mode; { static char a_mode[16]; unsigned short i = 0; unsigned short j = 0; a_mode[j++] = ' '; if (p_mode & S_ISVTX) a_mode[j] = 't'; else a_mode[j] = ' '; switch (p_mode & S_IFMT) { case S_IFDIR: a_mode[j++] = 'd'; break; case S_IFMPC: a_mode[j-1] = 'm'; /* FALL THROUGH */ case S_IFCHR: a_mode[j++] = 'c'; break; case S_IFMPB: a_mode[j-1] = 'm'; /* FALL THROUGH */ case S_IFBLK: a_mode[j++] = 'b'; break; case S_IFREG: default: j++; break; } a_mode[j++] = ' '; for( i = 0;i<3;i++ ) { if(p_mode<<(3*i) & S_IREAD) a_mode[j++] = 'r'; else a_mode[j++] = '-'; if(p_mode<<(3*i) & S_IWRITE) a_mode[j++] = 'w'; else a_mode[j++] = '-'; if( i<2 && (p_mode<