tree.c
utzoo!decvax!duke!harpo!npoiv!npois!houxm!houxa!houxi!houxz!ihnp4!stolaf!borman
utzoo!decvax!duke!harpo!npoiv!npois!houxm!houxa!houxi!houxz!ihnp4!stolaf!borman
Wed Jan 19 21:50:48 AEST 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 <sys/types.h>
#include <sys/dir.h>
#include <sys/stat.h>
#ifdef STATS
#include <pwd.h>
#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<argc; j++) {
if (flag) { /* saw a 'c', so jump over value */
flag = 0;
continue;
}
if (argv[j][0] == '-') {
for (i = 1; i < strlen(argv[j]); i++) {
switch (argv[j][i]) {
case 'a':
all = 1;
break;
case 'c':
length = atoi(argv[j+1]);
if (length > 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<<i & S_ISUID) )
a_mode[j++] = 's';
else if (p_mode<<(3*i) & S_IEXEC )
a_mode[j++] = 'x';
else
a_mode[j++] = '-';
a_mode[j++] = ' ';
}
a_mode[j] = '\0';
return(a_mode);
}
#endif
/* stln - sortof like strlen, returns length up to DIRSIZE-1 */
stln(st)
register char *st;
{
register int t;
for (t=0; t<DIRSIZ-1; ++t)
if (!st[t])
return (++t);
return (++t);
}
More information about the Comp.sources.unix
mailing list