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