[net.sources] dirtree

ordy (01/12/83)

	Here is the dirtree source and manual page. It is set up for
4.1 BSD. If you want to run on other Version 7 systems, you MUST edit
line 136 in the source and change the name of the directory structure
to match what you have in <dir.h>. On 4.1 it is 'dir', but on 2.8,
and perhaps vanilla V7 it is 'direct'.

	The program should use the new directory examination functions
so that it can work under 4.2, but it does not. If anybody makes those
changes, please let me know. Other good extensions are in the manual page.

	Greg Ordy

-----------------------------dirtree.c-----------------------------
#
/*
 * dirtree - display a directory tree.
 *
 * Greg Ordy, 1/8/83
 * Case Western Reserve University
 * Copyright (c) 1983
 * all rights reserved
 * Permission is granted for use by
 * valid Unix* licensees
 * (*) Unix is a trademark of Bell Labs
 *
 * options: -t: truncate at 80 columns
 *          -T: truncate at 132 columns
 *          -ox-y : show column x to column y
*/

#include	<stdio.h>
#include	<sys/types.h>
#include	<dir.h>
#include	<stat.h>

#define	MAXLEVEL 64		/* maximum depth possible */

struct	dentry			/* internal form of a directory entry */
{
	struct dentry *d_down;	/* lower level */
	struct dentry *d_f;	/* links on same level */
	char   *d_dname;	/* name of the directory */
	int	d_child;	/* number of children directories */
	int	d_offset;	/* offset until first character */
	short	d_left;		/* left column number */
	short	d_length;	/* length of string */
	short	d_arrow;	/* column of arrow coming into name */
};

struct dentry dhead;		/* head of the tree */
char rootname[256];		/* initial entry */

char	*levels[MAXLEVEL*3];	/* levels deep maximum, contains char */
				/* pointer per level for printing functions */
int	maxlevel;		/* maximum depth */
int	column;			/* global column count for output generation */
int	clipstart;		/* clipping bounds */
int	clipstop = {78};
int	clipped;		/* we did a clip option */

extern char *xalloc ();		/* for type matching */

main (argc, argv)
int argc;
char *argv[];
{
	init (argc, argv);
	build_tree (&dhead, 1);
	show_tree ();
}

init (c, v)
int c;
char *v[];
{
	register int i, j;

	for (i = 1; i < c; i++) {
		if (v[i][0] == '-') {
			for (j = 1; v[i][j]; j++) {
				switch (v[i][j]) {
					case 't': clipstop = 78; clipped++; break;
					case 'T': clipstop = 131; clipped++; break;
					case 'o': getclip (v[i]); goto xit;
					default: fprintf (stderr, "Unknown option: %c\n", v[i][j]);
						 break;
				}
			}
		    xit:
			i = i;	/* ugly but functional */
		}
		else {
			if (rootname[0]) {
				fprintf (stderr,"Too many directory names.\n");
				exit (-4);
			}
			strcpy (rootname, v[i]);

		}
	}
	if (rootname[0] == 0)
		strcpy (rootname, ".");

	dhead.d_dname = rootname;
	dhead.d_length = strlen (rootname);
}

getclip (sp)
register char *sp;
{
	register char *p;

	sp++;	/* pass up initial '-' */
	clipped++;
	for (p = sp; *p && (*p != '-'); p++);
	if (*p == 0) {
		clipstart = atoi (sp);
		if (clipstart < 0) {
			fprintf (stderr, "Invalid lower clip value. (%d)\n",
				clipstart);
			exit (-5);
		}
		clipstop = 79;
		return;
	}
	*p = ' ';
	sp++;
	clipstart = atoi (sp);
	clipstop = atoi (p);
	if (clipstop <= clipstart) {
		fprintf (stderr, "clip values must have '<' relationship.\n");
		exit (-6);
	}
	if (clipstart < 0 || clipstop < 0) {
		fprintf (stderr, "clip values must be postive.\n");
		exit (-6);
	}
}

build_tree (dp, flag)
register struct dentry *dp;
int flag;
{
	register int i;
	register char *pp;
	register FILE *fp;
	register struct dentry *ddp;
	long ftemp;
	struct dir de;
	struct stat sbuf;
	struct dentry **nextp;
	extern long ftell ();
	char ddname[20];

	if (chdir (dp->d_dname) < 0) {
		if (flag) {
			fprintf (stderr, "Cannot chdir to %s\n", dp->d_dname);
			exit (-1);
		}
		pp = xalloc (strlen (dp->d_dname) + 3);
		sprintf (pp, "*%s*", dp->d_dname);
		dp->d_dname = pp;
		dp->d_length = strlen (pp);
		return;
	}

	if ((fp = fopen (".", "r")) == NULL) {
		pp = xalloc (strlen (dp->d_dname) + 3);
		sprintf (pp, "-%s-", dp->d_dname);
		dp->d_dname = pp;
		dp->d_length = strlen (pp);
		return;
	}

	nextp = &dp->d_down;
	fseek (fp, (long) (2 * (sizeof (de))), 0);	/* pass up . and .. */

	while (1) {
		if (fread (&de, sizeof (de), 1, fp) != 1) {
			fclose (fp);
			for (i = 0, ddp = dp->d_down; ddp; ddp = ddp->d_f)
				i++;
			dp->d_child = i;
			chdir ("..");
			return;
		}
		if (de.d_ino == 0)
			continue;
		strncpy (ddname, de.d_name, 14);
		ddname[14] = 0;
		if (stat (ddname, &sbuf) < 0)
			continue;
		if ((sbuf.st_mode & S_IFDIR) == 0)
			continue;
		ddp = (struct dentry *) xalloc (sizeof (*ddp));
		ddp->d_length = strlen (ddname);
		ddp->d_dname = xalloc (ddp->d_length+2);
		strcpy (ddp->d_dname, ddname);
		*nextp = ddp;
		nextp = &ddp->d_f;
		ftemp = ftell (fp);
		fclose (fp);
		build_tree (ddp, 0);
		fp = fopen (".", "r");
		fseek (fp, ftemp, 0);
	}
}

char *xalloc (n)
register int n;
{
	extern char *calloc ();
	register char *pp;

	pp = calloc (1, n);
	if (pp == NULL) {
		fprintf (stderr, "Out of Memory!\n");
		exit (-2);
	}
	return (pp);
}

char *strget (s)
register char *s;
{
	register int n;
	register char *pp;

	n = strlen (s) + 1;
	pp = xalloc (n);
	strcpy (pp, s);
	return (pp);
}

stradd (pp, s)
register char **pp, *s;
{
	register int n;
	register char *p;

	if (*pp)
		n = strlen (*pp);
	else
		n = 0;
	n += strlen (s);
	p = xalloc (n + 2);
	if (*pp) {
		strcpy (p, *pp);
		free (*pp);
	}
	strcat (p, s);
	strcat (p, " ");
	*pp = p;
}

/*
 * Here is where the fun is, displaying the directory structure
 * built off of dhead. In most all cases we will overflow the normal
 * CRT display, and even 132 column jobbies. We must place the
 * directory names and connect them together. Placement follows
 * the following basic algorithm:
 * 1) If no children, mark left of name at current column, advance
 * column to right + 1. Mark upper line above middle of name.
 * Mark horizontal line between upper line limits.
 * 2) If children, determine span of children, which has been
 * previously done by step #1. From span, place parent in middle,
 * locate parent descender.
*/
show_tree ()
{
	register int i, j;
	register char *p;

	getdown (&dhead, 0);

	if (maxlevel > MAXLEVEL) {
		fprintf (stderr, "Sorry, directories too deep, increase MAXLEVELand recompile.\n");
		exit (-3);
	}
	for (i = 0; i != ((maxlevel*3)+1); i++) {
		levels[i] = xalloc (column+2);
		for (p = levels[i], j = 0; j != (column+1); j++)
			*p++ = ' ';
		*p = 0;
	}

	fillin (&dhead, 0);

	display ();
}

getdown (dp, ll)
register struct dentry *dp;
register int ll;
{
	register struct dentry *ddp, *xp;
	int left, right, middle, lcol;

	if (dp == 0)		/* no longer necessary, but no harm */
		return;
	lcol = column;
	for (ddp = dp; ddp; ddp = ddp->d_f) {
		if (ddp->d_down == 0) {
			ddp->d_left = column;
			column += (ddp->d_length+1);
			if (ll > maxlevel)
				maxlevel = ll;
		}
		else {
			lcol = column;
			getdown (ddp->d_down, ll+1); /* determine children */
			left = ddp->d_down->d_left + (ddp->d_down->d_length)/2;
			for (xp = ddp->d_down; xp; xp = xp->d_f)
				right = xp->d_left + (xp->d_length)/2;
			middle = (left+right)/2;
			ddp->d_left = middle - (ddp->d_length)/2;
			if (ddp->d_left < lcol)
				ddp->d_left = lcol;
			ddp->d_arrow = middle;
			if ((lcol = (ddp->d_left + ddp->d_length+1)) > column)
				column = lcol;
		}
	}
}

fillin (dp, ll)
register struct dentry *dp;
register int ll;
{
	register struct dentry *ddp;
	register int line;
	register char *p;
	int i, left, right;

	if (dp == 0)
		return;
	line = ll * 3;
	if (ll != 0) {
		p = levels[line-1];
		p[dp->d_left+(dp->d_length/2)] = '|';
	}
	if (dp->d_arrow)
		levels[line+1][dp->d_arrow] = '^';
	p = levels[line];
	munge (dp, p);

	for (ddp = dp->d_down; ddp; ddp = ddp->d_f)
		fillin (ddp, ll+1);
	if (dp->d_down && dp->d_down->d_f) {
		left = dp->d_down->d_left + (dp->d_down->d_length)/2;
		for (ddp = dp->d_down; ddp; ddp = ddp->d_f)
			right = ddp->d_left + (ddp->d_length)/2;
		p = levels[line+1];
		for (i = left; i != (right+1); i++)
			if (p[i] != '^')
				p[i] = '_';
	}
}

display ()
{
	register int i;
	register char *p;

	for (i = 0; i != (maxlevel*3+1); i++) {
		for (p = levels[i]; *p; p++);
		p--;
		while (*(p--) == ' ');
		p++;
		p++;
		*p = 0;
	}

	if (clipstart || (column > clipstop)) {
		fprintf (stderr, "Truncated structure. Columns %d to %d shown. Max is %d\n", clipstart, clipstop, column);
	}

	if ((clipstop < column) && clipped) {
		for (i = 0; i !=((maxlevel*3)+1); i++)
			levels[i][clipstop+1] = 0;
	}

	for (i = 0; i != ((maxlevel*3)+1); i++)
		printf ("%s\n", &levels[i][clipstart]);
}

munge (dp, p)
register struct dentry *dp;
register char *p;
{
	register char *pp;

	p = &p[dp->d_left];
	for (pp = dp->d_dname; *pp;)
		*p++ = *pp++;
}
------------------dirtree.l---------------------------------------
.TH DIRTREE l
.UC 4
dirtree \- graphically display Unix directory structures
.SH SYNOPSIS
.B dirtree
[-t -T -ox-y] [directory]
.SH DESCRIPTION
.I Dirtree
descends the specified directory substructure and produces a graph
representation of it on the standard output.
In almost all cases, the resulting structure is wider than a CRT screen
(80 columns), or even a printer (132 columns).
.I Dirtree
therefore has options which clip the displayed output to some portion of
total output. A composite picture can be made from butting separate
vertical strips together.
.PP
The '-t' option displays columns 0 to 79, and is designed for CRTs.
The '-T' option displays columns 0 to 131, and is designed for 132
column devices. The '-o' option contains a range specification, which
defines the generated output. The two values represent the value
of the left column and right column in the virtual output which is
mapped to the displayed output. The format of the '-o' option
is: '-ox-y', where the number 'x' is the left clip column and the
number 'y' is the right clip number. The two values are separated
with a minus (-) sign.
.PP
Only directories are displayed by
.I dirtree.
.PP
If a starting directory is not specified, '.' (current directory) is
chosen.
.SH DIAGNOSTICS
.I Dirtree
must be able to chdir to the specified
directory. Once running, there are two classes of directories
which
.I dirtree
cannot process, those which it cannot read and those for which
it only has execute permission. In these cases, the underlying
structure cannot be determined. If these cases are found, the name
of the directory involved will be surrounded by minus (-) and
asterisk (*) characters respectively.
On small memory systems, dirtree may run out of dynamically allocated
memory.
.SH AUTHOR
Greg Ordy, CWRU.
.SH BUGS
Should use the directory search functions to be portable between all
planned Unix versions. Should sort the entries in a directory.
The representation can be horizontally compressed, but is not.
----------------------------Last Line-----------------------------