[comp.sources.misc] v09i085: dtree

djm@abyss.eng.umd.edu (David J. MacKenzie) (12/21/89)

Posting-number: Volume 9, Issue 85
Submitted-by: djm@abyss.eng.umd.edu (David J. MacKenzie)
Archive-name: dtree

This is a new revision of the dtree Unix directory tree printing
program.  Its output has a different, and in my opinion more
attractive, format from that of the vtree program that appeared in
comp.sources.unix volume 18.

This version of dtree should work on all Unixes from version 7 onward,
with some tweaking of the Makefile.

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README dtree.1 dtree.c getcwd.c Makefile
# Wrapped by djm@abyss on Wed Dec 20 15:14:15 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(1014 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
Xdtree pretty-prints directory structures like this:
X
X      |-df---------
X      |-ftw--------
X      |
X      |            |-bin-src-
X      |            |-date----
X      |-gnu--------|-install-
X      |            |-lib-----
X|-src-|            |-libc----
X      |            |-tput----
X      |-misc-------
X      |-patches----
X      |-printf-----
X      |-qterm------
X
XIt was originally written for v7 and 4.1BSD; a version that had been
Xpartially adapted to the Berkeley Fast File System and portable
Xdirectory package appeared in mod.sources several years ago, but it
Xhad several bugs (among the more severe, it always dumps core on some
Xsystems).  This version fixes those and also has improved performance
Xand more helpful error messages.  It also works on System V; for the
Xbenefit of System V users, I have packaged with dtree Doug Gwyn's free
Xreimplementation of the getcwd function that doesn't open a pipe to
X/bin/pwd but determines the current directory directly; this should
Xhelp performance on those systems.
END_OF_FILE
if test 1014 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'dtree.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'dtree.1'\"
else
echo shar: Extracting \"'dtree.1'\" \(2365 characters\)
sed "s/^X//" >'dtree.1' <<'END_OF_FILE'
X.TH DTREE 1L
X.SH NAME
Xdtree \- display directory tree structures
X.SH SYNOPSIS
X.B dtree
X[
X.I \-adfgHlnpsvx
X] [
X.I \-c linelength
X] [
X.BR directory ...
X]
X.SH DESCRIPTION
X.I dtree
Xdisplays a graphic representation of the directory structure of each given
X.B directory 
Xand its children. If no directories are specified, the current
Xdirectory is used.
XBy default, only
Xdirectories, not regular files, are shown, and only their
Xfilenames are given.  Various options add additional
Xinformation to the tree.
X.SS OPTIONS
X.TP
X.I \-a
XInclude files in the listing (excluding entries beginning with '.').
X.TP
X.I \-c linelength
XMake
X.I linelength
Xthe length of each
Xcolumn of the printout.  By default, this is 14.
XAny entries longer than
Xthe column length are truncated accordingly, and the last character that
Xfits into the column is replaced by an asterisk.
XThis option only has an effect if the
X.I -v 
Xoption is specified.
X.TP
X.I \-d
XList directories first.  For each directory, its subdirectories
Xwill be listed first, and then all of its other entries.
X.TP
X.I \-f
XList files first.  The reverse of
X.IR  \-d .
X.TP
X.I \-l
XLong listing.  Display useful information to the right of
Xeach entry: the name of the file's owner, its size in blocks, and its mode.
X.TP
X.I \-g
XSame as the
X.I \-l
Xoption, except that the group name is used instead of
Xthe owner name.  If both the
X.I \-l
Xand
X.I \-g
Xoptions are used, both the
Xowner and group will be displayed.
X.TP
X.I \-H
XDisplay a header at the top of the printout that gives the time and
Xdate that the printout was made and a summary of the type of
Xinformation contained in the tree.
X.TP
X.I \-n
XNo sort.  Entries are listed in the order they are read
Xfrom the directories.
X.TP
X.I \-p
XInclude entries beginning with '.' (except '.' and '..').
X.TP
X.I \-s
XSimplify the long listing: display the user id, size in blocks, and
Xoctal mode of the file.  This option implies the
X.I \-l
Xoption unless the
X.I \-g
Xoption is specified.
X.TP
X.I \-v
XDo not let column lengths vary; use the same
Xwidth for each column of output.  The width defaults to 14
Xbut can be set with the
X.I \-c
Xoption.
X.TP
X.I \-x
XDo not cross file systems.  
X.I dtree
Xwill not cross over to a
Xsubdirectory if it is on a different file system.
X.SH AUTHOR
XDave Borman, Digital Unix Engineering Group
X.br
Xdecvax!borman
X.br
XOriginally written at St. Olaf College, Northfield, MN.
END_OF_FILE
if test 2365 -ne `wc -c <'dtree.1'`; then
    echo shar: \"'dtree.1'\" unpacked with wrong size!
fi
# end of 'dtree.1'
fi
if test -f 'dtree.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'dtree.c'\"
else
echo shar: Extracting \"'dtree.c'\" \(21819 characters\)
sed "s/^X//" >'dtree.c' <<'END_OF_FILE'
X/*
X *	DTREE - Print the tree structure of a directory
X *	4/7/83 name was changed from TREE to DTREE
X *	9/7/83 mods for 4.1c and 4.2 dirctory structure added
X *
X *	Dave Borman, Digital Unix Engineering Group
X *		decvax!borman
X *	Originally written at St. Olaf College, Northfield MN.
X *	Copyright (c) 1983 by Dave Borman
X *	All rights reserved
X *	This program may not be sold, but may be distributed
X *	provided this header is included.
X *
X *	Usage:	dtree [-adfgHlnpsvx] [-c line-length] [directory...]
X *	Flags:	-a) include non-directory entries in listing
X *		-d) sort tree with directories at the top
X *		-f) sort tree with files at the top
X *		-g) same as l, but use group name instead of user name
X *		-H) display a header at top
X *		-l) print stats with each listing
X *		    if both g & l flags are given, both owner and
X *		    group will be printed
X *		-n) do not sort the tree
X *		-p) include files starting with a '.' (except "." & "..")
X *		-s) use shorter stats. Implies -l if -g isn't given.
X *		-v) variable length columns off
X *		-x) do not cross mounted file systems.
X *		-c length) set max column length to "length"
X */
X
X /*     Modified by      Ed Arnold      CSU-CS   (csu-cs!arnold)     3-5-84
X  *
X  *     Allows symbolic links to both directories and individual files.
X  *     With a '-l' or '-al' option, links are denoted with a 'l' in front of 
X  *     file or directory permissions. In all other instances both links to 
X  *     directories and files are represented just as files are. Contents of
X  *     linked directories are not printed due to the possibility of 
X  *     recursively linked directories.
X  *
X  *     Big directory name option added by:
X  *                      Mike Vevea      CSU-CS (csu-cs!vevea)      3-22-84
X  *
X  *	Toggle sense of -v (running 4.2), and eliminate some extraneous
X  *		print info	Mike Meyer Energy Analysts (mwm@ea)	4/17/84
X  *
X  *	Fix the exit status to correctly indicate what happened.
X  *				Mike Meyer Energy Analysts (mwm@ea)	4/23/84
X  *
X  *	Change MAX to INITIAL_ELEM to fix conflict on Suns,
X  *	fix incorrect default value for Clength when -v not given,
X  *	add -H option to print header with date and description of tree,
X  *	remove -b option (useless) and simplify array access,
X  *	use getopt to parse options, fix usage message,
X  *	use getwd instead of opening a pipe to pwd,
X  *	make error messages more informative,
X  *	use strncpy instead of sprintf for speed,
X  *	move function declarations to top of file,
X  *	comment out junk after #else and #endif,
X  *	make symbolic link configuring automatic,
X  *	check all error returns from malloc and realloc,
X  *	add System V/Xenix/Unos compatibility,
X  *	remove definition of rindex.
X  *		David MacKenzie <djm@eng.umd.edu> 12/20/89
X  */
X
X/* Compile-time options:
X *
X * STATS	leave undefined to remove stats, giving more core space
X * and thus the ability to tree larger tree structures on PDP 11/70s.
X *
X * NEWDIR	directory structure a la Berkeley 4.1c or 4.2
X *
X * NDIR		use <sys/ndir.h> instead of <sys/dir.h>
X * NEWDIR must be defined as well.
X *
X * DIRENT	use Posix directory library.
X * NEWDIR must be defined as well.
X *
X * SYSV		use getcwd instead of getwd, strrchr instead of rindex.
X */
X
Xstatic	char Sccsid[]="@(#)dtree.c	2.3	2/14/84";
X
X#ifdef S_IFLNK
Xstatic  char Rcsid[] ="$Header: /mnt/ntape/RCS/dtree.c,v 1.2 84/04/23 10:33:41 root Exp $";
X#endif /* S_IFLNK */
X
X#include	<stdio.h>
X#include	<sys/types.h>
X#include	<sys/stat.h>
X#include	<time.h>
X
X#ifdef	STATS
X# include	<pwd.h>
X# include	<grp.h>
X#endif	/* STATS */
X
X#ifdef	NEWDIR
X# if	DIRENT
X#  include	<dirent.h>
X#  define	direct	dirent
X# else
X#  if	NDIR
X#   include	<sys/ndir.h>
X#  else
X#   include	<sys/dir.h>
X#  endif
X# endif /* DIRENT */
X#else
X# include	<sys/dir.h>
X#endif	/* NEWDIR */
X
X/* default column length when -v is given */
X#ifdef unos
X#define DEFCOLWID	30
X#else
X#define DEFCOLWID       14
X#endif
X
X#include	<sys/param.h>	/* for MAXPATHLEN */
X#ifndef MAXPATHLEN
X# ifdef LPNMAX		/* Xenix from stdio.h */
X#  define MAXPATHLEN (LPNMAX - 1)
X# else
X#  include <limits.h>	/* try somewhere else */
X#  define MAXPATHLEN PATH_MAX
X# endif
X#endif
X
X#ifndef MAXNAMLEN	/* defined with NEWDIR routines */
X# ifdef LFNMAX		/* Xenix again */
X#  define MAXNAMLEN (LFNMAX - 1)
X# else
X#  define MAXNAMLEN      DEFCOLWID
X# endif
X#endif
X
X#define	DEPTH	10	/* maximum depth that dtree will go */
X#define	INITIAL_ELEM	100	/* initial # of elements for list of files */
X
X#define	FFIRST	2	/* sort files first */
X#define DFIRST	1	/* sort directories first */
X#define	FAIL	-1	/* failure return status of sys calls */
X#define	GREATER	1	/* return value of strcmp if arg1 > arg2 */
X#define	LESSTHAN -1	/* return value of strcmp if arg1 < arg2 */
X#define	SAME	0	/* return value of strcmp if arg1 == arg2 */
X
X#ifdef	STATS
Xchar *getmode();
Xchar *guid();
Xchar *ggid();
Xstruct passwd *getpwuid();
Xstruct group *getgrgid();
X#endif /* STATS */
X
X#ifdef	SYSV
X#define	rindex	strrchr
X#endif /* SYSV */
X
Xchar *getwd();
Xchar *malloc();
Xchar *realloc();
Xchar *rindex();
Xint qsort();
Xlong time();
X
Xint compar();			/* comparison routine for qsort */
Xchar *xmalloc();
Xchar *xrealloc();
X
X#ifdef	SYSV
X#define getwd(buf) getcwd((buf), MAXPATHLEN + 1)
X#endif /* SYSV */
X
Xint	Index = 0;		/* current element of list[]	*/
Xint     CLength = 0;		/* max length of a column       */
Xint	All = 0;		/* all != 0; list non-directory entries */
Xint	File_dir = 0;		/* flag for how to sort */
Xint	Sort = 1;		/* flag to cause sorting of entries */
Xint	Point = 1;		/* skip point files if set	*/
Xint	Header = 0;		/* print verbose header */
Xint	Maxes[DEPTH];		/* array keeps track of max length in columns */
Xint	Level = 0;		/* counter for how deep we are	*/
Xint	Device;			/* device that we are starting tree on */
Xint	Xdev = 1;		/* set to allow crossing of devices */
Xint	Varspaces = 1;		/* set to allow compaction of column width */
X#ifdef	STATS
Xint	Gflag = 0;		/* set for group stats instead of owner */
Xint	Longflg = 0;		/* set for long listing */
Xint	Compact = 0;		/* set for shortened long listing */
X#endif	/* STATS */
Xstruct	stat Status;
X#ifdef  S_IFLNK
Xstruct  stat Lstat;   /* stat of link, if there is one */
X#endif  /* S_IFLNK */
X
Xstruct entry {
X	int next;			/* index to next element in list */
X					/* could be a ptr, but realloc() */
X					/* might screw us then */
X#ifdef	STATS
X	off_t	e_size;			/* size in blocks */
X	unsigned short	e_mode;		/* file mode */
X	short	e_uid;			/* uid of owner */
X	short	e_gid;			/* gid of owner */
X#endif	/* STATS */
X	unsigned short dir : 1;		/* entry is a directory */
X	unsigned short last : 1;	/* last entry in the dir. */
X	unsigned short dev : 1;		/* set if same device as top */
X	unsigned short end : 13;	/* index of last subdir entry*/
X	char	e_name[MAXNAMLEN + 1];	/* name from directory entry */
X} *List, *SaveList;
X
Xunsigned Size;				/* how big of space we've malloced */
X
Xchar	*Spaces;	/* used for output */
Xchar	Buf1[BUFSIZ];	/* buffers for stdio stuff.  We don't want	*/
X#ifndef NEWDIR
Xchar	Buf2[BUFSIZ];	/* anyone calling malloc, because then		*/
X			/* realloc() will have to move the whole list	*/
X#endif
X
Xmain(argc, argv)
Xchar	**argv;
Xint	argc;
X{
X	extern int optind;
X	extern char *optarg;
X	register int i;
X	char	top[MAXPATHLEN + 1];	/* array for treetop name */
X	char	home[MAXPATHLEN + 1];	/* starting dir for multiple trees */
X	char	*ptr;
X
X	setbuf(stdout, Buf1);
X
X	while ((i = getopt (argc, argv,
X#ifdef STATS
X			    "adfgHlnpsvxc:"
X#else
X			    "adfHnpvxc:"
X#endif /* STATS */
X			    )) != EOF) {
X		switch (i) {
X				case 'a':
X					All = 1;
X					break;
X				case 'c':
X					CLength = atoi(optarg);
X					if (CLength > MAXNAMLEN)
X						CLength = MAXNAMLEN;
X					else if (CLength < 1)
X						CLength = DEFCOLWID;
X					break;
X				case 'd':
X					File_dir = DFIRST;
X					break;
X				case 'f':
X					File_dir = FFIRST;
X					break;
X				case 'H':
X					Header = 1;
X					break;
X				case 'n':
X					Sort = 0;
X					break;
X				case 'p':
X					Point = 0;
X					break;
X				case 'v':
X					Varspaces = 0;
X					break;
X				case 'x':
X					Xdev = 0;
X					break;
X#ifdef	STATS
X				case 'g':
X					Gflag = 1;
X					break;
X				case 'l':
X					Longflg = 1;
X					break;
X				case 's':
X					Compact = 1;
X					break;
X#endif	/* STATS */
X				default:
X					fprintf(stderr,
X#ifdef	STATS
X		"Usage: dtree [-adfgHlnpsvx] [-c linelength] [directory ... ]\n"
X#else	/* STATS */
X		"Usage: dtree [-adfHnpvx] [-c linelength] [directory ... ]\n"
X#endif	/* STATS */
X						);
X					exit(FAIL);
X				}
X	}
X#ifdef	STATS
X	if (Compact && !Gflag)
X		Longflg = 1;
X#endif	/* STATS */
X	if (CLength == 0)
X		CLength = Varspaces ? MAXNAMLEN : DEFCOLWID;
X
X	/* Establish where we are (our home base...) */
X	if (getwd(home) == 0) {
X		fprintf(stderr,
X			"dtree: Cannot get initial directory: %s\n", home);
X		exit(1);
X	}
X
X	Spaces = xmalloc(MAXNAMLEN+2);
X	for(i = 0; i <= MAXNAMLEN; i++)
X		Spaces[i] = ' ';
X	Spaces[i] = '\0';
X
X	/* Get initial Storage space */
X	Size = sizeof(struct entry) * INITIAL_ELEM;
X	SaveList = (struct entry *)xmalloc(Size);
X
X	/* adjust for no specified directory */
X	if (optind == argc)
X		argv[--optind] = ".";
X
X	if (Header)
X		print_date();
X
X	/* walk down the rest of the args, treeing them one at at time */
X	for (; optind < argc; optind++) {
X		if (chdir(home) == -1) {
X			fprintf(stderr, "dtree: Cannot change to initial directory ");
X			perror(home);
X			exit(1);
X		}
X		strncpy (top, argv[optind], MAXPATHLEN);
X
X		if (chdir(top) == FAIL) {
X			fprintf(stderr, "dtree: Cannot change to top directory ");
X			perror(top);
X			continue;
X		} else if (getwd(top) == 0) {
X			fprintf(stderr,"dtree: Cannot get current directory: %s\n", top);
X			continue;
X		}
X
X		List = SaveList; Index = 0;
X		ptr = rindex(top, '/');
X
X		if (!ptr || *++ptr == '\0')
X			strncpy(List[Index].e_name, top, MAXNAMLEN);
X		else
X			strncpy(List[Index].e_name, ptr, MAXNAMLEN);
X
X		if(stat(top, &Status) == FAIL) {
X			fprintf(stderr, "dtree: Cannot stat directory ");
X			perror(top);
X			continue;
X		}
X		Device = Status.st_dev;
X		List[0].dir = 1;
X		List[0].last = 1;
X		List[0].next = 1;
X#ifdef	STATS
X		List[0].e_mode = Status.st_mode;
X		List[0].e_uid = Status.st_uid;
X		List[0].e_gid = Status.st_gid;
X		List[0].e_size = Status.st_size;
X#endif	/* STATS */
X		Index = 1;
X		for (i = 1; i < DEPTH; i++)
X			Maxes[i] = 0;
X		Maxes[0] = stln(List[0].e_name);
X		Level = 1;
X
X		/* search the tree */
X		List[0].end = t_search(top, &List[0]);
X
X		if (Index == 1)				/* empty tree */
X			List[0].next = 0;
X
X		if (Header) {
X		if (All)
X		    printf("\nDirectory structure and contents of %s\n", top);
X		else
X		    printf("\nDirectory structure of %s\n", top);
X		if (Point)
X			printf("(excluding entries that begin with '.')\n");
X		}
X
X		pt_tree();				/* print the tree */
X	}
X	exit(0) ;
X}
X
X
Xt_search(dir, addrs)
Xchar *dir;
Xstruct	entry *addrs;
X{
X	int	bsort;			/* index to begin sort */
X	int	stmp;			/* save temporary index value */
X	struct entry *sstep;		/* saved step in list */
X	int	nitems;			/* # of items in this directory */
X#ifdef	NEWDIR
X	DIR	*dirp;			/* pointer to directory */
X#else
X	FILE	*dirp;
X#endif
X	char	sub[MAXNAMLEN+1];	/* used for subdirectory names */
X	int	i;
X#ifdef	NEWDIR
X	struct direct *dp;
X#else
X	struct direct dirent;
X	struct direct *dp = &dirent;
X#endif	/* NEWDIR */
X	int	n_subs = 0;
X	int	tmp = 0;
X
X#ifdef	NEWDIR
X	dirp = opendir(".");
X#else
X	dirp = fopen(".", "r");
X#endif	/* NEWDIR */
X	if (dirp == NULL) {
X		fprintf(stderr, "dtree: Cannot open directory ");
X		perror(dir);
X		return(0);
X	}
X#ifndef	NEWDIR
X	setbuf(dirp, Buf2);
X#endif	/* NEWDIR */
X
X	bsort = Index;
X	sstep = &List[bsort]; /* initialize sstep for for loop later on */
X	nitems = Index;
X	/* get the entries of the directory that we are interested in */
X#ifndef	NEWDIR
X	while (fread((char *)(dp), sizeof(struct direct), 1, dirp) == 1) {
X#else
X	while ((dp = readdir(dirp)) != NULL) {
X#endif	/* NEWDIR */
X
X		if (dp->d_ino
X#ifdef unos
X		    == -1
X#else
X		    == 0
X#endif /* unos */
X		    || (strcmp(dp->d_name, ".") == SAME)
X		    || (strcmp(dp->d_name, "..") == SAME)
X		    || (Point && dp->d_name[0] == '.'))
X				continue;
X
X		strncpy(sub, dp->d_name, MAXNAMLEN);
X#ifdef S_IFLNK
X		if (lstat(sub,&Lstat) == FAIL) {
X			fprintf(stderr, "dtree: In directory %s, cannot lstat entry ", dir);
X			perror(sub);
X			continue;
X                }
X#endif /* S_IFLNK */
X		if (stat(sub, &Status) == FAIL) {
X			fprintf(stderr, "dtree: In directory %s, cannot stat entry ", dir);
X			perror(sub);
X			continue;
X		}
X#ifdef S_IFLNK
X		if (((Lstat.st_mode & S_IFMT) == S_IFLNK)  &&
X		    ((Status.st_mode & S_IFMT) == S_IFDIR)) 
X		        List[Index].dir = 0;	
X		else if ((((Lstat.st_mode & S_IFMT) == S_IFLNK) &&
X			((Status.st_mode & S_IFMT) != S_IFDIR)) && (All)) 
X                        List[Index].dir = 0;
X#endif /* S_IFLNK */
X		else if ((Status.st_mode & S_IFMT) == S_IFDIR)
X			List[Index].dir = 1;
X		else if (All)
X			List[Index].dir = 0;
X		else
X			continue;
X		strncpy(List[Index].e_name, dp->d_name, MAXNAMLEN);
X		List[Index].last = 0;
X		List[Index].end = 0;
X#ifdef S_IFLNK
X		if ((Lstat.st_mode & S_IFMT) == S_IFLNK) {
X		     List[Index].dev = (Device == Lstat.st_dev);
X		     List[Index].e_mode = Lstat.st_mode;
X		     List[Index].e_uid = Lstat.st_uid;
X		     List[Index].e_gid = Lstat.st_gid;
X		     List[Index].e_size = Lstat.st_size;
X                }
X                else {
X#endif /* S_IFLNK */
X		     List[Index].dev = (Device == Status.st_dev);
X#ifdef STATS
X		     List[Index].e_mode = Status.st_mode;
X		     List[Index].e_uid = Status.st_uid;
X		     List[Index].e_gid = Status.st_gid;
X		     List[Index].e_size = Status.st_size;
X#endif /* STATS */
X#ifdef S_IFLNK
X                }
X#endif /* S_IFLNK */
X		if (stln(List[Index].e_name) > Maxes[Level])
X			Maxes[Level] = stln(List[Index].e_name);
X		++Index;
X		if (Index*sizeof(struct entry) >= Size) {
X			Size += 20*sizeof(struct entry);
X			List = (struct entry *)xrealloc((char *)List, Size);
X		}
X	}
X#ifdef	NEWDIR
X	closedir(dirp);
X#else
X	fclose(dirp);
X#endif	/* NEWDIR */
X
X	nitems = Index - nitems;	/* nitems now contains the # of */
X					/* items in this dir, rather than */
X					/* # total items before this dir */
X
X	if (Sort)
X		qsort(&List[bsort], nitems, sizeof(struct entry), compar);
X
X	List[Index-1].last = 1;	/* mark last item for this dir */
X	n_subs = nitems;
X	stmp = Index;
X
X	/* now walk through, and recurse on directory entries */
X	/* sstep was initialized above */
X
X	for (i = 0; i < nitems; sstep = &List[stmp - nitems+(++i)]) {
X		if (sstep->dir && (Xdev || sstep->dev)) {
X			sstep->next = Index;
X			strncpy(sub, sstep->e_name, MAXNAMLEN);
X			tmp = n_subs;
X			Level++;
X			if (chdir(sub) == FAIL) {
X				fprintf(stderr,
X					"dtree: Cannot change to directory %s/", dir);
X				perror(sub);
X			} else {
X				n_subs += t_search(sub, sstep);
X				if (chdir("..") == FAIL) {
X					fprintf(stderr,
X						"dtree: %s/%s lacks '..' entry\n",dir, sub);
X					exit(1);
X				}
X			}
X			--Level;
X			if (n_subs - tmp <= 0)
X				sstep->next = 0;
X			else
X				--n_subs;
X		}
X		else
X			sstep->next = 0;
X	}
X	addrs->end = (unsigned)n_subs;
X	return(n_subs);
X}
X
X/*
X *	comparison routine for qsort
X */
X
Xcompar(a, b)
Xstruct entry *a, *b;
X{
X	if (!File_dir)		/* straight alphabetical */
X		return(strncmp(a->e_name, b->e_name, MAXNAMLEN));
X
X	/* sort alphabetically if both dirs or both not dirs */
X
X	if ((a->dir && b->dir) || (!a->dir && !b->dir))
X		return(strncmp(a->e_name, b->e_name, MAXNAMLEN));
X
X	if (File_dir == FFIRST) {	/* sort by files first */
X		if (a->dir)
X			return(GREATER);
X		else
X			return(LESSTHAN);
X	}
X
X	if (a->dir)			/* sort by dir first */
X		return(LESSTHAN);
X	else
X		return(GREATER);
X}
X
X
Xpt_tree()
X{
X	register int	i,j;
X	struct entry *l;
X	struct entry *hdr[DEPTH];
X	int posit[DEPTH];		/* array of positions to print dirs */
X	int con[DEPTH];			/* flags for connecting up tree */
X	char	flag = 0;		/* flag to leave blank line after dir */
X	struct	entry *stack[DEPTH];	/* save positions for changing levels */
X	int	top = 0;		/* index to top of stack */
X	int	count = 1;		/* count of line of output */
X
X	Level = 0;			/* initialize Level */
X
X	/* this loop appends each entry with dashes or spaces, for */
X	/* directories or files respectively */
X
X	for (i = 0; i < Index; i++) {
X		for (j = 0; j < MAXNAMLEN; j++) {
X			if (!List[i].e_name[j])
X				break;
X		}
X		if (List[i].dir) {
X			for (; j < MAXNAMLEN; j++)
X				List[i].e_name[j] = '-';
X		} else {
X			for (; j < MAXNAMLEN; j++)
X				List[i].e_name[j] = ' ';
X		}
X	}
X
X	/* adjust the Maxes array according to the flags */
X
X	for (i = 0; i < DEPTH; i++) {
X		if (Varspaces) {
X			if (Maxes[i] > CLength )
X				Maxes[i] = CLength;
X		} else
X			Maxes[i] = CLength;
X	}
X
X	/* clear the connective and position flags */
X
X	for (i = 0; i < DEPTH; i++)
X		con[i] = posit[i] = 0;
X
X	/* this is the main loop to print the tree structure. */
X	l = &List[0];
X	j = 0;
X	for (;;) {
X		/* directory entry, save it for later printing */
X		if (l->dir != 0 && l->next != 0) {
X			hdr[Level] = l;
X			posit[Level] = count + (l->end + 1)/2 - 1;
X			flag = 1;
X			stack[top++] = l;
X			l = &List[l->next];
X			++Level;
X			continue;
X		}
X
X#ifdef	STATS
X	do_it_again:
X#endif	/* STATS */
X		/* print columns up to our entry */
X		for (j = 0; j < (flag ? Level-1 : Level); j++) {
X			if (!flag && posit[j] && posit[j] <= count) {
X				/* time to print it */
X				if (hdr[j]->e_name[CLength-1] != '-')
X					hdr[j]->e_name[CLength-1] = '*';
X				printf("|-%.*s",Maxes[j],hdr[j]->e_name);
X				posit[j] = 0;
X				if (hdr[j]->last != 0)
X				    con[j] = 0;
X				else
X				    con[j] = 1;
X#ifdef	STATS
X				if (Gflag || Longflg) {
X				    if ((i = j+1) <= Level)
X					printf("| %.*s", Maxes[i], Spaces);
X				    for (i++; i <= Level; i++) {
X					printf("%c %.*s",
X					    (con[i] ? '|' : ' '),
X					    Maxes[i], Spaces);
X				    }
X				    if (!Compact) {
X					printf("%s ", getmode(hdr[j]->e_mode));
X					if (Longflg)
X					   printf("%8.8s ",guid(hdr[j]->e_uid));
X					if (Gflag)
X					   printf("%8.8s ",ggid(hdr[j]->e_gid));
X					printf("%7ld\n",
X					    (hdr[j]->e_size+511L)/512L);
X				    } else {
X					printf(" %04o ",hdr[j]->e_mode & 07777);
X					if (Longflg)
X					    printf("%5u ", hdr[j]->e_uid);
X					if (Gflag)
X					    printf("%5u ", hdr[j]->e_gid);
X					printf("%7ld\n",
X					    (hdr[j]->e_size+511L)/512L);
X				    }
X				    goto do_it_again;
X				}
X#endif	/* STATS */
X			} else
X				printf("%c %.*s", (con[j] ? '|' : ' '),
X					Maxes[j], Spaces);
X		}
X		if (flag) {	/* start of directory, so leave a blank line */
X			printf(con[j] ? "|\n" : "\n");
X			flag = 0;
X			continue;
X		} else {
X				/* normal file name, print it out */
X			if (l->e_name[CLength-1] != '-' &&
X			    l->e_name[CLength-1] != ' ')
X			    l->e_name[CLength-1] = '*';
X			printf("|-%.*s",Maxes[Level],l->e_name);
X			if (l->last) {
X				con[j] = 0;
X			} else {
X				con[j] = 1;
X			}
X#ifdef	STATS
X			if (Gflag || Longflg) {
X				if (Compact) {
X					printf(" %04o ", l->e_mode & 07777);
X					if (Longflg)
X					    printf("%5u ", l->e_uid);
X					if (Gflag)
X					    printf("%5u ", l->e_gid);
X					printf("%7ld",
X					    (l->e_size+511L)/512L);
X				} else {
X					printf("%s ", getmode(l->e_mode));
X					if (Longflg)
X					    printf("%8.8s ",guid(l->e_uid));
X					if (Gflag)
X					    printf("%8.8s ",ggid(l->e_gid));
X					printf("%7ld",
X					    (l->e_size+511L)/512L);
X				}
X			}
X#endif	/* STATS */
X		}
X		printf("\n");
X
X		if (l->last) {
X			/* walk back up */
X			while (l->last) {
X				--Level;
X				if (--top <= 0)
X					return;
X				l = stack[top];
X			}
X		}
X		l = &l[1];
X		++count;
X	}
X}
X
X#ifdef	STATS
X
Xchar *
Xguid(uid)
Xshort uid;
X{
X	static char tb[10];
X	struct passwd *pswd;
X
X	pswd = getpwuid(uid);
X	if (pswd == NULL)
X		sprintf(tb,"%u", uid);
X	else
X		sprintf(tb, "%8s", pswd->pw_name);
X	return(tb);
X}
X
Xchar *
Xggid(gid)
Xshort gid;
X{
X	static char tb[10];
X	struct group *grp;
X
X	grp = getgrgid(gid);
X	if (grp == NULL)
X		sprintf(tb,"%u", gid);
X	else
X		sprintf(tb, "%8s", grp->gr_name);
X	return(tb);
X}
X
X/* take the mode and make it into a nice character string */
X
Xchar	*
Xgetmode(p_mode)
Xunsigned short p_mode;
X{
X	static char a_mode[16];
X	register int	i = 0, j = 0;
X
X	a_mode[j++] = ' ';
X
X	switch (p_mode & S_IFMT) {
X#ifdef S_IFLNK
X	case S_IFLNK:
X		a_mode[j++] = 'l';
X		break;
X#endif /* S_IFLNK */
X	case S_IFDIR:
X		a_mode[j++] = 'd';
X		break;
X#ifdef	S_IFMPC		/* defined in stat.h if you have MPX files */
X	case S_IFMPC:
X		a_mode[j-1] = 'm';
X		/* FALL THROUGH */
X#endif	/* S_IFMPC */
X	case S_IFCHR:
X		a_mode[j++] = 'c';
X		break;
X#ifdef	S_IFMPB		/* defined in stat.h if you have MPX files */
X	case S_IFMPB:
X		a_mode[j-1] = 'm';
X		/* FALL THROUGH */
X#endif	/* S_IFMPB */
X	case S_IFBLK:
X		a_mode[j++] = 'b';
X		break;
X	case S_IFREG:
X	default:
X		a_mode[j++] = (p_mode & S_ISVTX) ? 't' : ' ';
X		break;
X	}
X	a_mode[j++] = ' ';
X	for( i = 0;i<3;i++ ) {
X		a_mode[j++] = (p_mode<<(3*i) & S_IREAD) ? 'r' : '-';
X		a_mode[j++] = (p_mode<<(3*i) & S_IWRITE) ? 'w' : '-';
X		a_mode[j++] = (i<2 && (p_mode<<i & S_ISUID)) ? 's' :
X			      ((p_mode<<(3*i) & S_IEXEC ) ? 'x' : '-');
X		a_mode[j++] = ' ';
X	}
X	a_mode[j] = '\0';
X	return(a_mode);
X}
X#endif
X
X/* like strlen, but returns length up to MAXNAMLEN-1 */
Xstln(st)
Xregister char *st;
X{
X	register int t;
X
X	for (t=0; t<MAXNAMLEN-1; ++t)
X		if (!st[t])
X			return (++t);
X	return (++t);
X}
X
Xprint_date()
X{
X	long now;
X
X	time(&now);
X	printf ("%s", ctime(&now));
X}
X
Xvoid
Xmemory_out()
X{
X	fprintf(stderr, "dtree: Virtual memory exhausted\n");
X	exit(1);
X}
X
X/* Allocate `size' bytes of memory dynamically, with error checking.  */
X
Xchar *
Xxmalloc (size)
Xunsigned size;
X{
X	char *ptr;
X
X	ptr = malloc (size);
X	if (ptr == 0 && size != 0)
X		memory_out ();
X	return ptr;
X}
X
X/* Change the size of an allocated block of memory `ptr' to `size' bytes,
X   with error checking.
X   If `ptr' is NULL, run xmalloc.
X   If `size' is 0, run free and return NULL.  */
X
Xchar *
Xxrealloc (ptr, size)
Xchar *ptr;
Xunsigned size;
X{
X	if (ptr == 0)
X		return xmalloc (size);
X	if (size == 0) {
X		free (ptr);
X		return 0;
X	}
X	ptr = realloc (ptr, size);
X	if (ptr == 0 && size != 0)
X		memory_out ();
X	return ptr;
X}
END_OF_FILE
if test 21819 -ne `wc -c <'dtree.c'`; then
    echo shar: \"'dtree.c'\" unpacked with wrong size!
fi
# end of 'dtree.c'
fi
if test -f 'getcwd.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'getcwd.c'\"
else
echo shar: Extracting \"'getcwd.c'\" \(5247 characters\)
sed "s/^X//" >'getcwd.c' <<'END_OF_FILE'
X/*
X	getcwd -- get current working directory name (POSIX and SVID compatible)
X
X	last edit:	21-Sep-1987	D A Gwyn
X
X	This public-domain getcwd() routine can be used to replace the UNIX
X	System V library routine (which uses popen() to capture the output of
X	the "pwd" command).  Once that is done, "pwd" can be reimplemented as
X	just puts(getcwd()).
X
X	This implementation depends on every directory having entries for
X	"." and "..".  It also depends on the internals of the <dirent.h>
X	data structures to some degree.
X
X	I considered using chdir() to ascend the hierarchy, followed by a
X	final chdir() to the path being returned by getcwd() to restore the
X	location, but decided that error recovery was too difficult that way.
X	The algorithm I settled on was inspired by my rewrite of the "pwd"
X	utility, combined with the dotdots[] array trick from the SVR2 shell.
X*/
X#include	<sys/types.h>
X#include	<sys/stat.h>
X#include	<string.h>
X#include	<dirent.h>
X#include	<errno.h>
X
Xtypedef char	*pointer;		/* (void *) if you have it */
X
Xextern void	free();
Xextern pointer	malloc();
Xextern int	fstat(), stat();
X
Xextern int	errno;			/* normally done by <errno.h> */
X
X#ifndef NULL
X#define	NULL	0			/* amorphous null pointer constant */
X#endif
X
X#ifndef NAME_MAX
X#define	NAME_MAX	255		/* maximum directory entry size */
X#endif
X
Xchar	*
Xgetcwd( buf, size )			/* returns pointer to CWD pathname */
X	char		*buf;		/* where to put name (NULL to malloc) */
X	int		size;		/* size of buf[] or malloc()ed memory */
X	{
X	static char	dotdots[] =
X"../../../../../../../../../../../../../../../../../../../../../../../../../..";
X	char 		*dotdot;	/* -> dotdots[.], right to left */
X	DIR		*dirp;		/* -> parent directory stream */
X	struct dirent	*dir;		/* -> directory entry */
X	struct stat	stat1, stat2;	/* info from stat() */
X	struct stat	*d = &stat1;	/* -> info about "." */
X	struct stat	*dd = &stat2;	/* -> info about ".." */
X	register char	*buffer;	/* local copy of buf, or malloc()ed */
X	char		*bufend;	/* -> buffer[size] */
X	register char	*endp;		/* -> end of reversed string */
X	register char	*dname;		/* entry name ("" for root) */
X	int		serrno = errno;	/* save entry errno */
X
X	if ( size == 0 )
X		{
X		errno = EINVAL;		/* invalid argument */
X		return NULL;
X		}
X
X	if ( (buffer = buf) == NULL	/* wants us to malloc() the string */
X	  && (buffer = (char *)malloc( (unsigned)size )) == NULL
X	   )	{
X		errno = ENOMEM;		/* cannot malloc() specified size */
X		return NULL;
X		}
X
X	if ( stat( ".", dd ) != 0 )	/* prime the pump */
X		goto error;		/* errno already set */
X
X	endp = buffer;			/* initially, empty string */
X	bufend = &buffer[size];
X
X	for ( dotdot = &dotdots[sizeof(dotdots)]; dotdot != dotdots; )
X		{
X		dotdot -= 3;		/* include one more "/.." section */
X					/* (first time is actually "..") */
X
X		/* swap stat() info buffers */
X		{
X		register struct stat	*temp = d;
X
X		d = dd;			/* new current dir is old parent dir */
X		dd = temp;
X		}
X
X		if ( (dirp = opendir( dotdot )) == NULL )	/* new parent */
X			goto error;	/* errno already set */
X
X		if ( fstat( dirp->dd_fd, dd ) != 0 )
X			{
X			serrno = errno;	/* set by fstat() */
X			(void)closedir( dirp );
X			errno = serrno;	/* in case closedir() clobbered it */
X			goto error;
X			}
X
X		if ( d->st_dev == dd->st_dev )
X			{		/* not crossing a mount point */
X			if ( d->st_ino == dd->st_ino )
X				{	/* root directory */
X				dname = "";
X				goto append;
X				}
X
X			do
X				if ( (dir = readdir( dirp )) == NULL )
X					{
X					(void)closedir( dirp );
X					errno = ENOENT;	/* missing entry */
X					goto error;
X					}
X			while ( dir->d_ino != d->st_ino );
X			}
X		else	{		/* crossing a mount point */
X			struct stat	t;	/* info re. test entry */
X			char		name[sizeof(dotdots) + 1 + NAME_MAX];
X
X			(void)strcpy( name, dotdot );
X			dname = &name[strlen( name )];
X			*dname++ = '/';
X
X			do	{
X				if ( (dir = readdir( dirp )) == NULL )
X					{
X					(void)closedir( dirp );
X					errno = ENOENT;	/* missing entry */
X					goto error;
X					}
X
X				(void)strcpy( dname, dir->d_name );
X				/* must fit if NAME_MAX is not a lie */
X				}
X			while ( stat( name, &t ) != 0
X			     || t.st_ino != d->st_ino
X			     || t.st_dev != d->st_dev
X			      );
X			}
X
X		dname = dir->d_name;
X
X		/* append "/" and reversed dname string onto buffer */
X    append:
X		if ( endp != buffer	/* avoid trailing / in final name */
X		  || dname[0] == '\0'	/* but allow "/" when CWD is root */
X		   )
X			*endp++ = '/';
X
X		{
X		register char	*app;	/* traverses dname string */
X
X		for ( app = dname; *app != '\0'; ++app )
X			;
X
X		if ( app - dname >= bufend - endp )
X			{
X			(void)closedir( dirp );
X			errno = ERANGE;	/* won't fit allotted space */
X			goto error;
X			}
X
X		while ( app != dname )
X			*endp++ = *--app;
X		}
X
X		(void)closedir( dirp );
X
X		if ( dname[0] == '\0' )	/* reached root; wrap it up */
X			{
X			register char	*startp;	/* -> buffer[.] */
X
X			*endp = '\0';	/* plant null terminator */
X
X			/* straighten out reversed pathname string */
X			for ( startp = buffer; --endp > startp; ++startp )
X				{
X				char	temp = *endp;
X
X				*endp = *startp;
X				*startp = temp;
X				}
X
X			errno = serrno;	/* restore entry errno */
X			return buffer;
X			}
X		}
X
X	errno = ENOMEM;			/* actually, algorithm failure */
X
X    error:
X	if ( buf == NULL )
X		free( (pointer)buffer );
X
X	return NULL;
X	}
X
END_OF_FILE
if test 5247 -ne `wc -c <'getcwd.c'`; then
    echo shar: \"'getcwd.c'\" unpacked with wrong size!
fi
# end of 'getcwd.c'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(728 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X# Makefile for dtree
X
X# Things that might go in DEFS:
X# -DSTATS -DNEWDIR -DDIRENT -DNDIR -DSYSV
XDEFS = -DSTATS -DNEWDIR -DDIRENT
XCFLAGS = $(DEFS) -O
XLDFLAGS = -s
XLIBS = # For Xenix use -lx with -DNDIR
X
XBINDIR = /usr/local/bin
XMANDIR = /usr/local/man/man1
X
XOBJECTS = dtree.o #getcwd.o
X
XARCH_FILES = README dtree.1 dtree.c getcwd.c Makefile
X
Xall: dtree
X
Xdtree: $(OBJECTS)
X	$(CC) -o dtree $(LDFLAGS) $(OBJECTS) $(LIBS)
X
Xinstall: dtree dtree.1
X	cp dtree $(BINDIR)
X	cp dtree.1 $(MANDIR)
X
Xlint: dtree.c
X	lint $(DEFS) dtree.c
X
Xshar: $(ARCH_FILES)
X	shar $(ARCH_FILES) > dtree.shar
X
Xdist: dtree.tar.Z
X
Xtar: dtree.tar.Z
X
Xdtree.tar.Z: $(ARCH_FILES)
X	tar cf - $(ARCH_FILES) | compress > dtree.tar.Z
X
Xclean:
X	rm -f dtree *.o core tags a.out
END_OF_FILE
if test 728 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
echo shar: End of shell archive.
exit 0