[net.sources] dtree

chad@anasazi.UUCP (Chad R. Larson) (04/21/86)

Here's the results of my request for "dtree".  I recieved about 8
copies of various versions, plus offers for about a dozen more.
Thanks for the active response!  Shortly after the sources, requests
for the best one started drifting in.  When the list of requests got
over a dozen, I decided there might be enough general interest in a
reposting.  (I'm not sure what the level should be to swing the
post/e-mail scale.)  Anyway, if you are happy with your copy of dtree,
hit "n" now.  Otherwise, I have run diff on the copies I got and
sorted out the one that seems to be the most complete.  It is reputed
to run on System V and 4.2 depending on #ifdef options, however I
haven't tested it yet.  Enjoy!
	-crl

-----------------------------cut here---------------------------------------

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by chad on Mon Apr 21 11:22:50 MST 1986
# Contents:  Header dtree.c dtree.1 dtree.man
 
echo x - Header
sed 's/^@//' > "Header" <<'@//E*O*F Header//'
>From utai!utcsri!utzoo!decvax!genrad!panda!sources-request
Subject: dtree for 4.2
Date: Thu, 3-Oct-85 09:14:13 EDT
Lines: 947

Mod.sources:  Volume 3, Issue 20
Submitted by: Mike Meyer <ucbvax!ucbjade!ucbopal:mwm>


@//E*O*F Header//
chmod u=rw,g=rw,o=rw Header
 
echo x - dtree.c
sed 's/^@//' > "dtree.c" <<'@//E*O*F dtree.c//'
/*
 *	DTREE - Print the tree structure of a directory
 *	4/7/83 name was changed from TREE to DTREE
 *	9/7/83 mods for 4.1c and 4.2 dirctory structure added
 *
 *	Dave Borman, Digital Unix Engineering Group
 *		decvax!borman
 *	Originally written at St. Olaf College, Northfield MN.
 *	Copyright (c) 1983 by Dave Borman
 *	All rights reserved
 *	This program may not be sold, but may be distributed
 *	provided this header is included.
 *
 *	Compile: PDP V7 w/split i&d    cc -O dtree.c -i -n -o dtree
 *		 VAX 4.1bsd	       cc -O dtree.c -n -o dtree
 *		 VAX 4.2bsd	       cc -O -DNEWDIR -DLINK dtree.c -n -o dtree
 *
 *	Usage:	dtree -[adfglnpsvx] [-b filenamesize] [-c linelength] [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
 *		-g) same as l, but use group name instead of user name
 *		-l) print stats with each listing
 *		    if both g & l flags are given, both owner and
 *		    group will be printed
 *		-n) do not sort the tree
 *		-p) include files starting with a '.' (except "." & "..")
 *		-s) use shorter stats. Implies -l if -g isn't given.
 *		-v) variable length columns off
 *		-x) do not cross mounted file systems.
 *              -b length) big directory names (define the max. name length)
 *		-c length) set max column length to "length" (if col.
 *                         length > file name length, this implies -b also)
 */

 /*     Modified by      Ed Arnold      CSU-CS   (csu-cs!arnold)     3-5-84
  *
  *     Allows symbolic links to both directories and individual files.
  *     With a '-l' or '-al' option, links are denoted with a 'l' in front of 
  *     file or directory permissions. In all other instances both links to 
  *     directories and files are represented just as files are. Contents of
  *     linked directories are not printed due to the possibility of 
  *     recursively linked directories.
  *
  *     Big directory name option added by:
  *                      Mike Vevea      CSU-CS (csu-cs!vevea)      3-22-84
  *	Toggle sense of -v (running 4.2), and eliminate some extraneous
  *		print info	Mike Meyer Energy Analysts (mwm@ea)	4/17/84
  *	Fix the exit status to correctly indicate what happened.
  *				Mike Meyer Energy Analysts (mwm@ea)	4/23/84
 */

#define STATS	/* comment out to remove stats, giving more core space	*/
		/* and thus the ability to tree larger tree structures	*/
		/* on PDP 11/70s */

/* #define	NEWDIR	/* directory structure ala Berkeley 4.1c or 4.2 */
 

/* #define LINK    /* allows links to be processed in Berkeley 4.2 version 
                      NEWDIR must be defined as well as LINK */
static	char Sccsid[]="@(#)dtree.c	2.3	2/14/84";

#ifdef LINK
static  char Rcsid[] ="$Header: /mnt/ntape/RCS/dtree.c,v 1.2 84/04/23 10:33:41 root Exp $";
#endif LINK

#include	<stdio.h>
#include	<sys/types.h>
#include	<sys/stat.h>
#ifdef	STATS
# include	<pwd.h>
# include	<grp.h>
#endif	STATS
#ifdef	NEWDIR
# include	<sys/dir.h>
#else
# include	<sys/dir.h>
#endif	NEWDIR
#define	DEFDIRSIZ	255
#define DEFCOLWID       14
#ifndef MAXNAMLEN
# define MAXNAMLEN      DEFCOLWID
#endif
/*
 * TWIDDLE is a fudge factor.  It should be declared so that
 * sizeof(struct entry) is on a nice boundry.
 */
#define	TWIDDLE	4

#define	addr(b,o) ((struct entry *)\
			((int)(b) + (o)*(sizeof(struct entry)+Length-TWIDDLE)))
#define	SIZEOFentry	(sizeof(struct entry) + Length - TWIDDLE)

#define	DEPTH	10	/* maximum depth that dtree will go */
#define	MAX	100	/* initial # of elements for list of files */
#define	PWD	"/bin/pwd"	/* program to get name of current dir */
#define	DATE	"/bin/date"	/* program to print current date */

#define	FFIRST	2	/* sort files first */
#define DFIRST	1	/* sort directories first */
#define	FAIL	-1	/* failure return status of sys calls */
#define	GREATER	1	/* return value of strcmp if arg1 > arg2 */
#define	LESSTHAN -1	/* return value of strcmp if arg1 < arg2 */
#define	SAME	0	/* return value of strcmp if arg1 == arg2 */

int	Index = 0;		/* current element of list[]	*/
int	Length = DEFDIRSIZ;	/* max length of a dir. name    */
int     CLength = DEFDIRSIZ;    /* 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 = 1;		/* set to allow compaction of column width */
#ifdef	STATS
int	Gflag = 0;		/* set for group stats instead of owner */
int	Longflg = 0;		/* set for long listing */
int	Compact = 0;		/* set for shortened long listing */
#endif	STATS
struct	stat Status;
#ifdef  LINK
struct  stat Lstat;   /* stat of link, if there is one */
#endif  LINK

struct entry {
	int next;			/* index to next element in list */
					/* could be a ptr, but realloc() */
					/* might screw us then */
#ifdef	STATS
	off_t	e_size;			/* size in blocks */
	unsigned short	e_mode;		/* file mode */
	short	e_uid;			/* uid of owner */
	short	e_gid;			/* gid of owner */
#endif	STATS
	short unsigned dir : 1;		/* entry is a directory */
	short unsigned last : 1;	/* last entry in the dir. */
	short unsigned dev : 1;		/* set if same device as top */
	short unsigned end : 13;	/* index of last subdir entry*/
	char	e_name[TWIDDLE];	/* name from directory entry */
					/* it will actually be larger */
} *List, *SaveList;

unsigned Size;				/* how big of space we've malloced */

char	*Spaces;	/* used for output */
char	Buf1[BUFSIZ];	/* buffers for stdio stuff.  We don't want	*/
char	Buf2[BUFSIZ];	/* anyone calling malloc, because then		*/
char	Buf3[BUFSIZ];	/* realloc() will have to move the whole list	*/

main(argc, argv)
char	**argv;
int	argc;
{
	register int i, j = 0;
	int	flag = 0;	/* used to jump over 'c' argument */
	char	top[128];	/* array for treetop name */
	char	home[128];	/* starting dir for multiple trees */
	char	*ptr;
	char	*malloc();
	char	*rindex();
	FILE	*istr, *popen();

	setbuf(stdout, Buf1);

	for (j=1; j<argc; j++) {
		if (flag) {	/* saw a 'c' or 'b', so jump over value */
			if (flag > 1) j += flag-1;
			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 'b':
					Length = atoi(argv[j+1+flag]);
					if (Length > MAXNAMLEN)
						Length = MAXNAMLEN;
					else if (Length < 1)
						Length = DEFCOLWID;
					flag += 1;
					break;
				case 'c':
					CLength = atoi(argv[j+1+flag]);
					if (CLength > MAXNAMLEN)
						CLength = MAXNAMLEN;
					else if (CLength < 1)
						CLength = DEFCOLWID;
					if (Length < CLength) Length = CLength;
					flag += 1;
					break;
				case 'd':
					File_dir = DFIRST;
					break;
				case 'f':
					File_dir = FFIRST;
					break;
				case 'n':
					Sort = 0;
					break;
				case 'p':
					Point = 0;
					break;
				case 'v':
					Varspaces = 0;
					break;
				case 'x':
					Xdev = 0;
					break;
#ifdef	STATS
				case 'g':
					Gflag = 1;
					break;
				case 'l':
					Longflg = 1;
					break;
				case 's':
					Compact = 1;
					break;
#endif	STATS
				default:
					fprintf(stderr,
						"Bad flag: %c\n", argv[j][i]);
					fprintf(stderr,
#ifdef	STATS
		"Usage: dtree -[adfglnpsvx] [-b filenamesize] [-c linelength] [directory ... ]\n");
#else	STATS
		"Usage: dtree -[adfglnpsvx] [-b filenamesize] [-c linelength] [directory ... ]\n");
#endif	STATS
					exit(FAIL);
				}
			}
		} else
			break;
	}
#ifdef	STATS
	if (Compact && !Gflag)
		Longflg = 1;
#endif	STATS

	/* Establish where we are (our home base...) */
	if ((istr = popen(PWD, "r")) == NULL) {
		fprintf(stderr,"dtree: %s failed\n", PWD);
		exit(1);
	} else {
		setbuf(istr, Buf2);
		if (fgets(home, 128, istr) == NULL) {
			fprintf(stderr, "dtree: EOF from %s\n", PWD);
			exit(1);
		}
	}
	pclose(istr);
	if (home[strlen(home)-1] == '\n')
		home[strlen(home)-1] = '\0';

	Spaces = malloc(Length+2);
	for(i = 0; i < Length + 1; i++)
		Spaces[i] = ' ';
	Spaces[i] = '\0';

	/* Get initial Storage space */
	Size = SIZEOFentry * MAX;
	SaveList = (struct entry *)malloc(Size);

	/* adjust for no specified directory */
	if (j == argc)
		argv[--j] = ".\0";

/*	system(DATE); Why in gods name did he do this? */	
/*	printf("\n");*/
	/* walk down the rest of the args, treeing them one at at time */
	for (; j < argc; j++) {
		if (chdir(home) == -1) {
			fprintf(stderr, "Can't chdir back to %s\n", home);
			exit(1);
		}
		sprintf(top, "%s", argv[j]);

		if (chdir(top) == FAIL) {
			fprintf(stderr, "Can't chdir to %s\n", top);
			continue;
		} else if ((istr = popen(PWD, "r")) == NULL) {
			fprintf(stderr,"dtree: %s failed\n", PWD);
			continue;
		} else {
			setbuf(istr, Buf2);
			if (fgets(top, 128, istr) == NULL) {
				fprintf(stderr, "dtree: EOF from %s\n", PWD);
				pclose(istr);
				continue;
			}
			ptr = top;
			while (*ptr && (*ptr != '\n'))
				ptr++;
			*ptr = '\0';
			pclose(istr);
		}

		List = SaveList; Index = 0;
		ptr = rindex(top, '/');

		if (!ptr || *++ptr == '\0')
			sprintf(addr(List,Index)->e_name, "%.*s", Length, top);
		else
			sprintf(addr(List,Index)->e_name, "%.*s", Length, ptr);

		if(stat(top, &Status) == FAIL) {
			fprintf(stderr, "Can't stat %s\n", top);
			continue;
		}
		Device = Status.st_dev;
		addr(List,0)->dir = 1;
		addr(List,0)->last = 1;
		addr(List,0)->next = 1;
#ifdef	STATS
		addr(List,0)->e_mode = Status.st_mode;
		addr(List,0)->e_uid = Status.st_uid;
		addr(List,0)->e_gid = Status.st_gid;
		addr(List,0)->e_size = Status.st_size;
#endif	STATS
		Index = 1;
		for (i = 1; i < DEPTH; i++)
			Maxes[i] = 0;
		Maxes[0] = stln(addr(List,0)->e_name);
		Level = 1;

		/* search the tree */
		addr(List,0)->end = t_search(top, addr(List,0));

		if (Index == 1)				/* empty tree */
			addr(List,0)->next = 0;

/*		if (All)
		    printf("\nDirectory structure and contents of %s\n", top);
		else
		    printf("\nDirectory structure of %s\n", top);
		if (Point)
			printf("(excluding entries that begin with '.')\n");
*/
		pt_tree();				/* print the tree */
	}
	exit(0) ;
}


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 */
#ifdef	NEWDIR
	DIR	*dirp;			/* pointer to directory */
#else
	FILE	*dirp;
#endif
	char	sub[MAXNAMLEN+1];	/* used for subdirectory names */
	int	i;
#ifdef	NEWDIR
	struct direct *dp;
#else
	struct direct dirent;
	struct direct *dp = &dirent;
#endif	NEWDIR
	int	n_subs = 0;
	int	tmp = 0;
	extern	qsort();
	int	compar();	/* comparison routine for qsort */
	char	*realloc();


#ifdef	NEWDIR
	dirp = opendir(".");
#else
	dirp = fopen(".", "r");
#endif	NEWDIR
	if (dirp == NULL) {
		fprintf(stderr, "Cannot open %s\n", dir);
		return(0);
	}
#ifndef	NEWDIR
	setbuf(dirp, Buf3);
#endif	NEWDIR

	bsort = Index;
	sstep = addr(List,bsort); /* initialize sstep for for loop later on */
	nitems = Index;
	/* get the entries of the directory that we are interested in */
#ifndef	NEWDIR
	while (fread((char *)(dp), sizeof(struct direct), 1, dirp) == 1) {
#else
	while ((dp = readdir(dirp)) != NULL) {
#endif	NEWDIR

		if (!dp->d_ino
		    || (strcmp(dp->d_name, ".") == SAME)
		    || (strcmp(dp->d_name, "..") == SAME)
		    || (Point && dp->d_name[0] == '.'))
				continue;

		sprintf(sub, "%s", dp->d_name);
#ifdef LINK
		if (lstat (sub,&Lstat) == FAIL) {
			fprintf(stderr, "%s:lstat can't find\n", sub);
			continue;
                }
#endif LINK
		if ((tmp = stat(sub, &Status)) == FAIL) {
			fprintf(stderr, "%s:stat can't find\n", sub);
			continue;
		}
#ifdef LINK
		if (((Lstat.st_mode & S_IFMT) == S_IFLNK)  &&
		    ((Status.st_mode & S_IFMT) == S_IFDIR)) 
		        addr(List,Index)->dir = 0;	
		else if ((((Lstat.st_mode & S_IFMT) == S_IFLNK) &&
			((Status.st_mode & S_IFMT) != S_IFDIR)) && (All)) 
                        addr(List,Index)->dir = 0;
#endif LINK
		else if ((Status.st_mode & S_IFMT) == S_IFDIR)
			addr(List,Index)->dir = 1;
		else if (All)
			addr(List,Index)->dir = 0;
		else
			continue;
		sprintf(addr(List,Index)->e_name, "%.*s", Length, dp->d_name);
		addr(List,Index)->last = 0;
		addr(List,Index)->end = 0;
#ifdef LINK
		if ((Lstat.st_mode & S_IFMT) == S_IFLNK) {
		     addr(List,Index)->dev = (Device == Lstat.st_dev);
		     addr(List,Index)->e_mode = Lstat.st_mode;
		     addr(List,Index)->e_uid = Lstat.st_uid;
		     addr(List,Index)->e_gid = Lstat.st_gid;
		     addr(List,Index)->e_size = Lstat.st_size;
                }
                else {
#endif LINK
		     addr(List,Index)->dev = (Device == Status.st_dev);
#ifdef STATS
		     addr(List,Index)->e_mode = Status.st_mode;
		     addr(List,Index)->e_uid = Status.st_uid;
		     addr(List,Index)->e_gid = Status.st_gid;
		     addr(List,Index)->e_size = Status.st_size;
#endif STATS
#ifdef LINK
                }
#endif LINK
		if (stln(addr(List,Index)->e_name) > Maxes[Level])
			Maxes[Level] = stln(addr(List,Index)->e_name);
		++Index;
		if (Index*SIZEOFentry >= Size) {
			Size += 20*SIZEOFentry;
			if (!(List =
			    (struct entry *)realloc((char *)List, Size))) {
				fprintf(stderr, "Out of space\n");
				break;
			}
		}
	}
#ifdef	NEWDIR
	closedir(dirp);
#else
	fclose(dirp);
#endif	NEWDIR

	nitems = Index - nitems;	/* nitems now contains the # of */
					/* items in this dir, rather than */
					/* # total items before this dir */

	if (Sort)
		qsort(addr(List,bsort), nitems, SIZEOFentry, compar);

	addr(List,Index-1)->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 = addr(List,stmp - nitems+(++i))) {
		if (sstep->dir && (Xdev || sstep->dev)) {
			sstep->next = Index;
			sprintf(sub, "%.*s", Length, sstep->e_name);
			tmp = n_subs;
			Level++;
			if (chdir(sub) == FAIL)
				fprintf(stderr,
					"Can't chdir to %s/%s\n", dir, sub);
			else {
				n_subs += t_search(sub, sstep);
				if (chdir("..") == FAIL) {
					fprintf(stderr,
						"No '..' in %s/%s!\n",dir, sub);
					exit(1);
				}
			}
			--Level;
			if (n_subs - tmp <= 0)
				sstep->next = 0;
			else
				--n_subs;
		}
		else
			sstep->next = 0;
	}
	addrs->end = (unsigned)n_subs;
	return(n_subs);
}

/*
 *	comparison routine for qsort
 */

compar(a, b)
struct entry *a, *b;
{
	if (!File_dir)		/* straight alphabetical */
		return(strncmp(a->e_name, b->e_name, Length));

	/* sort alphabetically if both dirs or both not dirs */

	if ((a->dir && b->dir) || (!a->dir && !b->dir))
		return(strncmp(a->e_name, b->e_name, Length));

	if (File_dir == FFIRST) {	/* sort by files first */
		if (a->dir)
			return(GREATER);
		else
			return(LESSTHAN);
	}

	if (a->dir)			/* sort by dir first */
		return(LESSTHAN);
	else
		return(GREATER);
}


pt_tree()
{
	register int	i,j;
	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 */
	struct	entry *stack[DEPTH];	/* save positions for changing levels */
	int	top = 0;		/* index to top of stack */
	int	count = 1;		/* count of line of output */
#ifdef	STATS
	char	*getmode();
	char	*guid(), *ggid();
#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 < Length; j++) {
			if (!addr(List,i)->e_name[j])
				break;
		}
		if (addr(List,i)->dir) {
			for (; j < Length; j++)
				addr(List,i)->e_name[j] = '-';
		} else {
			for (; j < Length; j++)
				addr(List,i)->e_name[j] = ' ';
		}
	}

	/* adjust the Maxes array according to the flags */

	for (i = 0; i < DEPTH; i++) {
		if (Varspaces) {
			if (Maxes[i] > CLength )
				Maxes[i] = CLength;
		} else
			Maxes[i] = CLength;
	}

	/* 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 = addr(List,0);
	j = 0;
	for (;;) {
		/* directory entry, save it for later printing */
		if (l->dir != 0 && l->next != 0) {
			hdr[Level] = l;
			posit[Level] = count + (l->end + 1)/2 - 1;
			flag = 1;
			stack[top++] = l;
			l = addr(List,l->next);
			++Level;
			continue;
		}

#ifdef	STATS
	do_it_again:
#endif	STATS
		/* print columns up to our entry */
		for (j = 0; j < (flag ? Level-1 : Level); j++) {
			if (!flag && posit[j] && posit[j] <= count) {
				/* time to print it */
				if (hdr[j]->e_name[CLength-1] != '-')
					hdr[j]->e_name[CLength-1] = '*';
				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 (Gflag || Longflg) {
				    if ((i = j+1) <= Level)
					printf("| %.*s", Maxes[i], Spaces);
				    for (i++; i <= Level; i++) {
					printf("%c %.*s",
					    (con[i] ? '|' : ' '),
					    Maxes[i], Spaces);
				    }
				    if (!Compact) {
					printf("%s ", getmode(hdr[j]->e_mode));
					if (Longflg)
					   printf("%8.8s ",guid(hdr[j]->e_uid));
					if (Gflag)
					   printf("%8.8s ",ggid(hdr[j]->e_gid));
					printf("%7ld\n",
					    (hdr[j]->e_size+511L)/512L);
				    } else {
					printf(" %04o ",hdr[j]->e_mode & 07777);
					if (Longflg)
					    printf("%5u ", hdr[j]->e_uid);
					if (Gflag)
					    printf("%5u ", hdr[j]->e_gid);
					printf("%7ld\n",
					    (hdr[j]->e_size+511L)/512L);
				    }
				    goto do_it_again;
				}
#endif	STATS
			} else
				printf("%c %.*s", (con[j] ? '|' : ' '),
					Maxes[j], Spaces);
		}
		if (flag) {	/* start of directory, so leave a blank line */
			printf(con[j] ? "|\n" : "\n");
			flag = 0;
			continue;
		} else {
				/* normal file name, print it out */
			if (l->e_name[CLength-1] != '-' &&
			    l->e_name[CLength-1] != ' ')
			    l->e_name[CLength-1] = '*';
			printf("|-%.*s",Maxes[Level],l->e_name);
			if (l->last) {
				con[j] = 0;
			} else {
				con[j] = 1;
			}
#ifdef	STATS
			if (Gflag || Longflg) {
				if (Compact) {
					printf(" %04o ", l->e_mode & 07777);
					if (Longflg)
					    printf("%5u ", l->e_uid);
					if (Gflag)
					    printf("%5u ", l->e_gid);
					printf("%7ld",
					    (l->e_size+511L)/512L);
				} else {
					printf("%s ", getmode(l->e_mode));
					if (Longflg)
					    printf("%8.8s ",guid(l->e_uid));
					if (Gflag)
					    printf("%8.8s ",ggid(l->e_gid));
					printf("%7ld",
					    (l->e_size+511L)/512L);
				}
			}
#endif	STATS
		}
		printf("\n");

		if (l->last) {
			/* walk back up */
			while (l->last) {
				--Level;
				if (--top <= 0)
					return;
				l = stack[top];
			}
		}
		l = addr(l,1);
		++count;
	}
}

#ifdef	STATS

char *
guid(uid)
short uid;
{
	static char tb[10];
	extern struct passwd *getpwuid();
	struct passwd *pswd;

	pswd = getpwuid(uid);
	if (pswd == NULL)
		sprintf(tb,"%u", uid);
	else
		sprintf(tb, "%8s", pswd->pw_name);
	return(tb);
}

char *
ggid(gid)
short gid;
{
	static char tb[10];
	extern struct group *getgrgid();
	struct group *grp;

	grp = getgrgid(gid);
	if (grp == NULL)
		sprintf(tb,"%u", gid);
	else
		sprintf(tb, "%8s", grp->gr_name);
	return(tb);
}

/* take the mode and make it into a nice character string */

char	*
getmode(p_mode)
unsigned short p_mode;
{
	static char a_mode[16];
	register int	i = 0, j = 0;

	a_mode[j++] = ' ';

	switch (p_mode & S_IFMT) {
#ifdef LINK
	case S_IFLNK:
		a_mode[j++] = 'l';
		break;
#endif LINK
	case S_IFDIR:
		a_mode[j++] = 'd';
		break;
#ifdef	S_IFMPC		/* defined in stats.h if you have MPX files */
	case S_IFMPC:
		a_mode[j-1] = 'm';
		/* FALL THROUGH */
#endif	S_IFMPC
	case S_IFCHR:
		a_mode[j++] = 'c';
		break;
#ifdef	S_IFMPB		/* defined in stats.h if you have MPX files */
	case S_IFMPB:
		a_mode[j-1] = 'm';
		/* FALL THROUGH */
#endif	S_IFMPB
	case S_IFBLK:
		a_mode[j++] = 'b';
		break;
	case S_IFREG:
	default:
		a_mode[j++] = (p_mode & S_ISVTX) ? 't' : ' ';
		break;
	}
	a_mode[j++] = ' ';
	for( i = 0;i<3;i++ ) {
		a_mode[j++] = (p_mode<<(3*i) & S_IREAD) ? 'r' : '-';
		a_mode[j++] = (p_mode<<(3*i) & S_IWRITE) ? 'w' : '-';
		a_mode[j++] = (i<2 && (p_mode<<i & S_ISUID)) ? 's' :
			      ((p_mode<<(3*i) & S_IEXEC ) ? 'x' : '-');
		a_mode[j++] = ' ';
	}
	a_mode[j] = '\0';
	return(a_mode);
}
#endif

/* stln - sortof like strlen, returns length up to Length-1 */
stln(st)
register char *st;
{
	register int t;

	for (t=0; t<Length-1; ++t)
		if (!st[t])
			return (++t);
	return (++t);
}

/*
 * Return a pointer into str at which the character
 * c last appears; NULL if not found.
*/

char *
rindex(str, c)
register char *str, c;
{
	register char *ptr;

	ptr = NULL;
	do {
		if (*str == c)
			ptr = str;
	} while (*str++);
	return(ptr);
}
@//E*O*F dtree.c//
chmod u=rw,g=rw,o=rw dtree.c
 
echo x - dtree.1
sed 's/^@//' > "dtree.1" <<'@//E*O*F dtree.1//'
@.TH DTREE l
@.SH NAME
dtree \- print directory tree structures
@.SH SYNOPSIS
@.B dtree
[
@.B \-adfglnpsvx
]
[
@.B \-b filenamesize
]
[
@.B \-c linelength
] [ directory1 ...]
@.SH DESCRIPTION
@.I Dtree
is a program to print out the tree structure of directories
and their children. If no directories are specified,
@.I dtree
takes the current working directory to be the top of the tree
structure.  It prints out just the directory structure by default.
If no flags are specified,
@.I dtree
prints out just the directory structures.
Recognized options are as follows:
@.TP
-a
Include files in printout (excluding entries beginning with '.')
@.TP
-b
Take the next argument to be the maximum length of a directory name; default
is 14 characters, or the value associated with a -c argument, if any.  
any directories with names longer than this length will not be searched, thus
any files and directories within them will not be included in the output.
@.TP
-c
Take the next argument to be the length of each
column of the printout. (By default, this is 14, the
maximum filename length.  Any lengths greater than
the column width are truncated accordingly, and the last character which
fits into the column is replaced by an asterisk.)
@.TP
-d
List directories first. For each directory, its subdirectories
will be listed first, and then all its other entries.
@.TP
-f
List files first. Reverse of -d.
@.TP
-l
Long listing. Useful information is printed to the right of
each entry. The name of the owner, its size in blocks, and its mode are printed.
@.TP
-g
Same as the -l flag, except that the group name is used instead of
the owner name. If both the -l and -g flags are used, both the
owner and group will be printed.
@.TP
-n
No sort. Names are listed in the order they are read
from the directory.
@.TP
-p
Include entries beginning with '.' (excluding "." and "..").
@.TP
-s
Simplify the long listing. Prints uid, size in blocks, and octal mode of the
file. This flag implies the -l flag unless the -g flag is specified.
@.TP
-v
Disable variable length columns.  Normally columns are of variable length.
Rather than using the same
width for each column of output, each column is shortened as much as
possible without truncating any names.
@.TP
-x
Do not cross file systems.  Dtree will not cross over to a
subdirectory if it is on a different file system.
@.SH AUTHOR
Dave Borman, Digital Unix Engineering Group
@.br
decvax!borman
@.br
Originally written at St. Olaf College, Northfield, MN.
@//E*O*F dtree.1//
chmod u=rw,g=rw,o=rw dtree.1
 
echo x - dtree.man
sed 's/^@//' > "dtree.man" <<'@//E*O*F dtree.man//'



DTREE(l)            UNIX Programmer's Manual             DTREE(l)



NAME
     dtree - print directory tree structures

SYNOPSIS
     dtree [ -adfglnpsvx ] [ -b filenamesize ] [ -c linelength ]
     [ directory1 ...]

DESCRIPTION
     _D_t_r_e_e is a program to print out the tree structure of direc-
     tories and their children. If no directories are specified,
     _d_t_r_e_e takes the current working directory to be the top of
     the tree structure.  If no flags are specified, _d_t_r_e_e prints
     out just the directory structures.  Recognized options are
     as follows:

     -a   Include files in printout (excluding entries beginning
          with '.')

     -b   Take the next argument to be the maximum length of a
          directory name; default is 14 characters, or the value
          associated with a -c argument, if any. Any directories
          with names longer than this length will not be
          searched, thus any files and directories within them
          will not be included in the output.  This flag only has
          an effect if the -v flag is specified.

     -c   Take the next argument to be the length of each column
          of the printout. (By default, this is 14, the maximum
          filename length.  Any lengths greater than the column
          width are truncated accordingly, and the last character
          which fits into the column is replaced by an asterisk.)
          This flag only has an effect if the -v flag is speci-
          fied.

     -d   List directories first. For each directory, its sub-
          directories will be listed first, and then all its
          other entries.

     -f   List files first. Reverse of -d.

     -l   Long listing. Useful information is printed to the
          right of each entry. The name of the owner, its size in
          blocks, and its mode are printed.

     -g   Same as the -l flag, except that the group name is used
          instead of the owner name. If both the -l and -g flags
          are used, both the owner and group will be printed.

     -n   No sort. Names are listed in the order they are read
          from the directory.

     -p   Include entries beginning with '.' (excluding "." and



Printed 10/8/85                                                 1






DTREE(l)            UNIX Programmer's Manual             DTREE(l)



          "..").

     -s   Simplify the long listing. Prints uid, size in blocks,
          and octal mode of the file. This flag implies the -l
          flag unless the -g flag is specified.

     -v   Don't let columns be of variable length. Use the same
          width for each column of output. The width defaults to
          14 (the normal Unix maximum file length), but can be
          set with either -c or -b.

     -x   Do not cross file systems.  Dtree will not cross over
          to a subdirectory if it is on a different file system.

AUTHOR
     Dave Borman, Digital Unix Engineering Group
     decvax!borman
     Originally written at St. Olaf College, Northfield, MN.





































Printed 10/8/85                                                 2



@//E*O*F dtree.man//
chmod u=rw,g=rw,o=rw dtree.man
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
      9     23    217 Header
    831   3183  21040 dtree.c
     84    437   2468 dtree.1
    132    446   3185 dtree.man
   1056   4089  26910 total
!!!
wc  Header dtree.c dtree.1 dtree.man | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0

-------------------uncut here--------------------------------------------------
-- 
"You can't have everything--where would you put it?"
	-Steven Wright
_____________________________________________________________________
UUCP:    {mot!terak}!anasazi!chad               Voice: Hey, Chad!
Ma Bell: (602) 870-3330                         ICBM:  N33deg,33min
Surface: International Anasazi, Inc.                   W112deg,03min
         7500 North Dreamy Draw Drive
         Suit 120
         Phoenix, AZ 85020

ryan@cray.UUCP (Mayer) (05/06/86)

*** REPLACE THIS LINE WITH YOUR MESSAGE ***
	The copy of dtree we got was truncated.  Could somebody out
there send me a copy?