treeD --- Unix file system diagrammer

Istvan Mohos istvan at hhb.UUCP
Fri May 4 04:56:24 AEST 1990



The utmost conceptual simplicity of file system trees belies the
difficulty of memorizing actual nodal layouts of specific trees.
In spite a plain topological essence of TREE no two trees are
the same; and most users would have an equal chance of being able
to recall the exact twig arrangement of last year's Christmas tree
as they would in describing a 30 Meg Unix subdirectory.

Recursive listers such as 'find' obscure the tree structure by the
one-dimensionality of their output: standard output mediums of
pinfeed paper or the scrollable screen rigidly limit the width,
while allowing infinitely long lists.

The treeD file system diagrammer erases the width limitation of
the output medium, and constructs true two-dimensional maps of
file systems.  To realize a hardcopy of a map, the internal diagram
is output as equal-sized pages, each page bearing a letter/number
coordinate analogous to lettered, numbered grids on a street map.
Command line options fine tune the format of the output, control
non-default grid sizes and the depth of the directory search.

-DREALUNIX should be enabled in the makefile when compiling under
System V.  The 'man page' is in the file treeD.tex, and should be
processed with "tex treeD" (assuming TeX capability at the site).
The Bourne script treeX converts treeD's character-graphics
depicting branching, to bold lines under plain TeX.

===============================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".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# globals.h i.h ilib.h
# goaway.c iego.c ierror.c illistn.c init.c iopt.c iread.c
# listfiles.c mapper.c package.c split.c tile.c treeD.c
# makefile treeX treeD.tex README

echo x - globals.h
cat > "globals.h" << '//E*O*F globals.h//'
/* globals.h */

#include "ilib.h"
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>

#define BLANK     0
#define SINGLE    1
#define DOUBLE    2
#define TRIPLE    3
#define LEAF      4
#define BUD       5
#define JOINT     6
#define TWIG      7
#define SPAN      8
#define TAP       9
#define TIP       10

#define MAXFN     15      /* fourteen name chars, 1 type char */
#define SPACE     32      /* ASCII value */

#define ZERO      ((char *)NULL)
#define TSIZ      (sizeof (struct tile))

#define BAILOUT   sprintf (Msg, "error at line %d of %s",\
				  __LINE__, __FILE__), goaway()

int  goaway();
int  fstat();
int  stat();
char *malloc();
char *gets();

struct tile {
	int ver;
	int hor;
	int linkc;
	char line1[17];
	char line2[17];
	char line3[17];
};

#ifdef MAIN

char Listfile[IQUARTK];   /* /tmp file for listing of current dir */
char Tiles[IQUARTK];      /* /tmp filename for sequential output */
char Msg[IQUARTK];        /* buffer for reporting errors to user */
struct tile **Mp;         /* base of tile pointer matrix */
int  Td;                  /* Tile descriptor */
int  Tx;                  /* horizontal tile position */
int  Ty = -1;             /* vertical tile position */
int  Deepest;             /* greatest vertical depth reached */
int  Wid = 72;            /* max column width of printed page */
int  Lin = 66;            /* max lines on printed page */
int  Widemod;             /* number of tiles/page horizontally */
int  Deepmod;             /* number of tiles/page vertically */
int  Depth;               /* max. recursion depth; 0=infty */
int  Fstop;               /* max. file list page; 0=infty */
int  Printblank;          /* print page even if no tiles on it */
unsigned int Mcount;      /* width*height of rectangular tile matrix */

struct tile Blank;        /* one of each tile, initialized in init.c */
struct tile Single;
struct tile Double;
struct tile Triple;
struct tile Leaf;
struct tile Bud;
struct tile Joint;
struct tile Twig;
struct tile Span;
struct tile Tap;
struct tile Tip;

#else
extern char Listfile[];
extern char Tiles[];
extern char Msg[];
extern struct tile **Mp;
extern int  Td;
extern int  Tx;
extern int  Ty;
extern int  Deepest;
extern int  Lin;
extern int  Wid;
extern int  Widemod;
extern int  Deepmod;
extern int  Depth;
extern int  Fstop;
extern int  Printblank;
extern unsigned int Mcount;

extern struct tile Blank;
extern struct tile Single;
extern struct tile Double;
extern struct tile Triple;
extern struct tile Leaf;
extern struct tile Bud;
extern struct tile Joint;
extern struct tile Twig;
extern struct tile Span;
extern struct tile Tap;
extern struct tile Tip;

#endif
//E*O*F globals.h//

echo x - i.h
cat > "i.h" << '//E*O*F i.h//'
/* i.h */
/**************************************
* local include file for ilib functions
* Istvan Mohos, 1987
***************************************/

#ifdef pyr
#include <sys/time.h>
#else
#include <time.h>
#endif

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

#ifndef X_OK
#    ifdef REALUNIX
#        define F_OK 0
#        define X_OK 1
#        define W_OK 2
#        define R_OK 4
#        include <fcntl.h>
#    else
#        include <sys/file.h>
#    endif
#endif

#define NUL          0    /* the ASCII 0 byte */
#define MAXSTR       1892
#define BIGBUFSIZ    4096
#define SHORTSTR     256
#define IFOURK       4096
#define ITWOK        2048
#define IONEK        1024
#define IHALFK       512
#define IQUARTK      256
#define BADCHARP(p)  ((p) == (char *)NULL || *(p) == '\0')
#define NULCHARP(p)  ((p) == (char *)NULL)
#define WHITE(c)     ((c) < 33)
#define BLACK(c)     ((c) > 32)
#define SMALL(c)     ((c) < 32)

#define INITOKF      1    /* setup forward parsing */
#define INITOKR     -1    /* setup reverse parsing */
#define ITOKF        2    /* forward parse */
#define ITOKR       -2    /* reverse parse */

int  fstat();
int  stat();
char *malloc();
char *calloc();
long lseek();
long time();

struct tm *_igetdate();

char *ctime();
char *iwhich();
char *getenv();
char *ilast();
char *ianytok();
char *ialntok();
char *ictok();
int  ierror();
int  ifamily();
int  ilongest();
int  itexrect();
int  iread();

extern int errno, sys_nerr;
extern char *sys_errlist[];

#ifndef IAMIERROR
extern char ierbuf[];
extern int  ierflag;
#endif
//E*O*F i.h//

echo x - ilib.h
cat > "ilib.h" << '//E*O*F ilib.h//'
/* ilib.h */
/*********************************************************************
* This is the client's #include file for accessing functions in ilib.a
* Istvan Mohos, 1987 --- in the Public Domain
**********************************************************************/

/* functions archived in ilib.a: */
char * ialntok();
char * ianymatch();
char * ianytok();
int    ibcmp();
void   ibcopy();
void   iblank();
int    ibreakl();
char * ictok();
char * icopy();
int    icount();
void   icue();
int    idamage();
char * idate();
void   idump();
int    iego();
int    ierror();
int    iexpect();
int    ifamily();
int    ifilter();
int    igroup();
int    ihash();
int    ihasharg();
char * ihms();
int    iinput();
char * ilast();
int    iline();
int    ilist();
int    ilistn();
int    illistn();
int    ilongest();
int    ilower();
char * imatch();
int    imode();
int    imonth();
int    inest();
char * inl();
char * inull();
int    inumsearch();
void   inumsort();
int    inumstrcmp();
char * inextl();
int    ioctal();
int    iopt();
int    iread();
int    irotate();
int    iround();
int    isearch();
void   isort();
char * istartl();
int    istripcom();
int    istripdq();
int    istripsq();
int    istripstr();
int    iswap();
int    itexrect();
int    itoday();
int    itohour();
int    itok();
int    itomin();
int    itomonth();
int    itosec();
int    itoyear();
int    itran();
void   itwin();
int    iuniq();
int    iuniqa();
int    iupper();
char * iwhich();
int    iwrite();
int    iwritopn();
char * ixmatch();
int    ixsearch();
int    ixswap();

char * malloc();
char * calloc();
long   lseek();

#include <stdio.h>
#ifndef X_OK
#    ifdef REALUNIX
#        define F_OK 0
#        define X_OK 1
#        define W_OK 2
#        define R_OK 4
#        include <fcntl.h>
#    else
#        include <sys/file.h>
#    endif
#endif

extern char ierbuf[];
extern int  ierflag;

/* imode symbolic constants, modeled after stat.h list */
#define ISSOCK   0140000   /* socket */
#define ISLNK    0120000   /* symbolic link */
#define ISREG    0100000   /* regular */
#define ISBLK    0060000   /* block special */
#define ISDIR    0040000   /* directory */
#define ISCHR    0020000   /* character special */
#define ISFIFO   0010000   /* named pipe */
#define ISUID    0004000   /* set uid on execution */
#define ISGID    0002000   /* set gid on execution */
#define ISSTICK  0001000   /* keep text in memory (sticky bit) */
#define ISROWN   0000400   /* read, owner */
#define ISWOWN   0000200   /* write, owner */
#define ISXOWN   0000100   /* execute/search, owner */
#define ISRGRP   0000040   /* read, group */
#define ISWGRP   0000020   /* write, group */
#define ISXGRP   0000010   /* execute/search, group */
#define ISRALL   0000004   /* read, others */
#define ISWALL   0000002   /* write, others */
#define ISXALL   0000001   /* execute/search, others */

#define INITOKF       1    /* setup forward parsing */
#define INITOKR      -1    /* setup reverse parsing */
#define ITOKF         2    /* forward parse */
#define ITOKR        -2    /* reverse parse */

#define DOUNCOUN(x,y) (y) = (x); --(y) >= 0
#define BADCHARP(p)   ((p) == (char *)NULL || *(p) == '\0')
#define NULCHARP(p)   ((p) == (char *)NULL)
#define ZPT           (char *)NULL

#define IANYTOK       0
#define IALNTOK       1
#define ICTOK         2

#define SPACE_LINE   -1
#define LINE_ONLY     0
#define LINE_SPACE    1

#define IROTR         1 /* rotate 90 deg. to right */
#define IROTL        -1 /* rotate 90 deg. to left */
#define IROTOR        3 /* rotate 180 deg. over and 90 deg. to right */
#define IROTOL       -3 /* rotate 180 deg. over and 90 deg. to left */

#define SHORTMO       0 /* idate format: Jun 23 1988 */
#define SHORTUPMO     1 /* idate format: JUN 23 1988 */
#define LONGMO        2 /* idate format: June 23, 1988 */
#define LONGUPMO      3 /* idate format: JUNE 23, 1988 */

#define WHITE(c)      ((c) < 33)
#define BLACK(c)      ((c) > 32)
#define TONEXWHITE(p) while (*(p) && (*(p)>32)) (p)++
#define TONEXBLACK(p) while (*(p) && (*(p)<33)) (p)++

#define IFOURK        4096
#define ITWOK         2048
#define IONEK         1024
#define IHALFK        512
#define IQUARTK       256
//E*O*F ilib.h//

echo x - goaway.c
cat > "goaway.c" << '//E*O*F goaway.c//'
/* goaway.c */

#include "globals.h"

goaway ()
{
	signal(SIGINT, SIG_IGN);
	signal(SIGHUP, SIG_IGN);
	signal(SIGTERM, SIG_IGN);
	signal(SIGQUIT, SIG_IGN);

	unlink (Tiles);
	unlink (Listfile);
	if (*Msg)
		fprintf(stderr, "%s\n", Msg), exit (1);

	exit (0);
}

//E*O*F goaway.c//

echo x - iego.c
cat > "iego.c" << '//E*O*F iego.c//'
/* iego.c */
/*******************************************************
* return file name in buf without path or .ext component
* Istvan Mohos, 1987 --- in the Public Domain
*******************************************************/

#include "i.h"

int
iego (ptr, wbuf, delim, ext)
char *ptr, *wbuf, delim, ext;
{
	char *fr, *to, *mark;

	if (BADCHARP(ptr))
		return (ierror ("iego: invalid name pointer"));
	if (delim == 0)
		delim = '/';

	to = wbuf;
	*to = '\0';
	for (fr = ptr; *fr++;);
	for (--fr; --fr > ptr;) {
		if (*fr == delim) {
			++fr;
			break;
		}
	}
	if (*fr == delim)
		++fr;
	
	if (ext == 0)
		for (mark = ptr; mark < fr; *to++ = *mark++);
	else
		for (;*fr && *fr != ext;)
			*to++ = *fr++;
	*to = '\0';

	return (strlen (wbuf));
}
//E*O*F iego.c//

echo x - ierror.c
cat > "ierror.c" << '//E*O*F ierror.c//'
/* ierror.c */
/****************************************
* report error via "ierbuf" and "ierflag"
* Istvan Mohos, 1987 --- in the Public Domain
****************************************/

#define IAMIERROR
#include "i.h"

char ierbuf[IHALFK];
int  ierflag;

int
ierror (ustr)
char *ustr;
{
	if (errno > 0 && errno < sys_nerr) { /* system error */
		ierflag = -errno;
		if (NULCHARP (ustr))
			strcpy(ierbuf, sys_errlist[errno]);
		else
			sprintf(ierbuf, "%s --- %.*s", sys_errlist[errno],
				IHALFK - strlen (sys_errlist[errno]) - 6, ustr);
		errno = 0;
		return (ierflag);
	}

	ierflag = -sys_nerr; /* user error */
	if (NULCHARP (ustr))
		strcpy (ierbuf, "Error");
	else
		strncpy (ierbuf, ustr, IHALFK-1);
	errno = 0;
	return (ierflag);
}
//E*O*F ierror.c//

echo x - illistn.c
cat > "illistn.c" << '//E*O*F illistn.c//'
/* illistn.c */
/********************************************
* create array of pointers to lines in buffer
* Istvan Mohos, 1987 --- in the Public Domain
*********************************************/

#include "i.h"

int
illistn (start, end, ptrlist)
char *start;
char *end;
char **ptrlist;
{
	int ri;
	char *rp;
	char *savp, **work;

	if (end <= start || NULCHARP (start))
		return (ierror ("illistn: zero-size or invalid buffer"));
	*(end -1) = '\n'; /* just in case */
	for (ri = 0, rp = end; --rp >= start;)
		if (*rp == '\n')
			++ri;

	if (NULCHARP(savp=malloc((unsigned int)(ri*sizeof(char *)))))
		return(ierror("illistn: can't allocate pointer array"));

	/* start one character to left of last byte in buffer */
	*(end -1) = 0;
	for (work=(char **)savp+ri, rp=end-1; --rp >= start;)
		if (*rp == '\n')
			*rp = 0, *--work = rp+1;

	/* rp == start-1 */
	*--work = ++rp;

	*ptrlist = savp;
	return(ri);
}
//E*O*F illistn.c//

echo x - init.c
cat > "init.c" << '//E*O*F init.c//'
/* init.c */

#include "globals.h"

init ()
{
	char  *mktemp();
	struct tile *tp;

	/******************
	 *                *
	 *                *
	 *                *
	 ******************/
	tp = &Blank;
	strcpy (tp->line1, "                ");
	strcpy (tp->line2, "                ");
	strcpy (tp->line3, "                ");


	/******************
	 *string1         *
	 *                *
	 *                *
	 ******************/
	tp = &Single;
	strcpy (tp->line2, "                ");
	strcpy (tp->line3, "                ");


	/******************
	 *string1         *
	 *string2         *
	 *                *
	 ******************/
	tp = &Double;
	strcpy (tp->line3, "                ");


	/******************
	 *string1         *
	 *string2         *     Triple
	 *string3         *
	 ******************/


	/******************
	 *  |             *
	 *string1         *
	 *string2         *
	 ******************/
	tp = &Leaf;
	strcpy (tp->line1, "  |             ");


	/******************
	 *  |             *
	 *string1         *
	 *                *
	 ******************/
	tp = &Bud;
	strcpy (tp->line1, "  |             ");
	strcpy (tp->line3, "                ");


	/******************
	 *  |             *
	 *string1         *
	 *  |             *
	 ******************/
	tp = &Joint;
	strcpy (tp->line1, "  |             ");
	strcpy (tp->line3, "  |             ");


	/******************
	 *  |             *
	 *string1~~~~~~~~~*
	 *  |             *
	 ******************/
	tp = &Twig;
	strcpy (tp->line1, "  |             ");
	strcpy (tp->line3, "  |             ");


	/******************
	 *                *
	 *~~~~~~~~~~~~~~~~*
	 *                *
	 ******************/
	tp = &Span;
	strcpy (tp->line1, "                ");
	strcpy (tp->line2, "~~~~~~~~~~~~~~~~");
	strcpy (tp->line3, "                ");


	/******************
	 *                *
	 *~~~~~~~~~~~~~~~~*
	 *  |             *
	 ******************/
	tp = &Tap;
	strcpy (tp->line1, "                ");
	strcpy (tp->line2, "~~~~~~~~~~~~~~~~");
	strcpy (tp->line3, "  |             ");


	/******************
	 *                *
	 *~~>             *
	 *  |             *
	 ******************/
	tp = &Tip;
	strcpy (tp->line1, "                ");
	strcpy (tp->line2, "~~>             ");
	strcpy (tp->line3, "  |             ");


	strcpy (Tiles, "/tmp/");
	strcat (Tiles, mktemp ("treeDTXXXXXX"));
	if ((Td = open (Tiles, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1)
		fprintf (stderr, "can't write to %s\n", Tiles), exit (1);

	strcpy (Listfile, "/tmp/");
	strcat (Listfile, mktemp ("treeDLXXXXXX"));

	if (signal(SIGHUP, SIG_IGN) == SIG_DFL)
		signal(SIGHUP, goaway);
	if (signal(SIGINT, SIG_IGN) == SIG_DFL)
		signal(SIGINT, goaway);
	if (signal(SIGTERM, SIG_IGN) == SIG_DFL)
		signal(SIGTERM, goaway);
	if (signal(SIGQUIT, SIG_IGN) == SIG_DFL)
		signal(SIGQUIT, goaway);

	Widemod = (Wid-3)/16;
	Deepmod = (Lin-3)/3;
}
//E*O*F init.c//

echo x - iopt.c
cat > "iopt.c" << '//E*O*F iopt.c//'
/* iopt.c */
/*********************************************
* command line option manager
* Istvan Mohos, 1987 --- in the Public Domain
*********************************************/

#include "i.h"

int
iopt (ptr)
char ***ptr;
{
	char *rp;
	static char **olist;
	static int first = 1;
	int optlen;

	if (first) {
		olist = *ptr;
		first = 0;
	}

	rp = *olist;
	if (rp == NULL || *rp != '-') { /* no more options */
		*ptr = olist;               /* set to first non-option */
		first = 1;                  /* automatically re-init */
		return (0);
	}

	optlen = strlen (rp);
	if (optlen > 2) {               /* flag, value combined */
		rp += 2;
		*olist = rp;                /* right past '-c' flag */
		*ptr = olist;
		++olist;                    /* pre-increment for next time */
		return (*--rp);
	}

	if (optlen == 1) {              /* '-' by itself */
		*ptr = olist;
		++olist;                    /* pre-increment for next time */
		return (*rp);
	}

	/* else (optlen == 2): normal '-c' flag */
	++rp;
	++olist;
	if (*olist == NULL || **olist == 0) { /* no option value */
		--olist;
		*ptr = olist;               /* cough up entire option flag */
		first = 1;                  /* automatically re-init */
		return (0);
	}

	*ptr = olist;
	++olist;                        /* pre-increment for next time */
	return (*rp);
}
//E*O*F iopt.c//

echo x - iread.c
cat > "iread.c" << '//E*O*F iread.c//'
/* iread.c */
/*************************************************
* read file into malloc'd buffer, return file size
* Istvan Mohos, 1987 --- in the Public Domain
**************************************************/

#include "i.h"

int
iread (fname, mallocp)
char *fname;
char **mallocp;
{
	struct stat sbuf;
	int checkval, fd;
	int count;

	if (BADCHARP (fname))
		return (ierror ("iread: invalid file name"));

	if (mallocp == (char **) NULL) {
		if (access (fname, R_OK) == -1)
			return (-1); /* can't read it */
		return (0);
	}

	if ((fd = open (fname, 0)) == -1)
		return (ierror ("iread: no file access"));
		
	if ((checkval = fstat (fd, &sbuf)) == -1)
		return (ierror ("iread: fstat read error"));

	if ((count = (int)sbuf.st_size) == 0)
		return (ierror ("iread: zero length file"));

	if (NULCHARP (*mallocp = malloc ((unsigned int) count+1)))
		return (ierror ("iread: can't allocate read buffer"));

	if ((checkval = read (fd, *mallocp, count)) != count) {
		sprintf (ierbuf+200,
			"iread: expected: %d, read: %d", count, checkval);
		return (ierror (ierbuf+200));
	}
	close (fd);
	*(*mallocp + count) = 0;
	return (checkval);
}
//E*O*F iread.c//

echo x - listfiles.c
cat > "listfiles.c" << '//E*O*F listfiles.c//'
/* listfiles.c */

#include "globals.h"

listfiles (ptr, ptrcount, fcount)
char **ptr;
int ptrcount, fcount;
{
	int fdeep;
	int maxpgdeep;
	int qi;
	char *s1, *s2, *s3;
	static char *more = "--- more ---";

	if ((fdeep = Ty + 1) > Deepest)
		Deepest = fdeep;

	if (Fstop) {
		maxpgdeep = Deepest / Deepmod +1; /* 1, 2... */
		if (maxpgdeep < Fstop)
			maxpgdeep = Fstop;
	}

	/* list files only, skip directories; null out file names */
	if (fcount == 1) {
		for (DOUNCOUN (ptrcount, qi); ptr++) {
			if (*(*ptr + strlen (*ptr) -1) != '/') {
				tile (fdeep, BUD, 0, *ptr, ZERO, ZERO);
				**ptr = 0;
				return;
			}
		}
	}

	qi = ptrcount;
	while (qi--) {
		if (*(*ptr + strlen (*ptr) -1) != '/') {
			--fcount;
			s1 = *ptr;
			ptr++;
			break;
		}
		ptr++;
	}
	while (qi--) {
		if (*(*ptr + strlen (*ptr) -1) != '/') {
			--fcount;
			if (Fstop && fcount && ((fdeep+1)/Deepmod >= maxpgdeep)) {
				s2 = more;
				tile (fdeep, LEAF, 0, s1, s2, ZERO);
				*s1 = 0;
				**ptr = 0;
				while (qi--) {
					++ptr;
					if (*(*ptr + strlen (*ptr) -1) != '/')
						**ptr = 0;
				}
				return;
			}
			s2 = *ptr;
			ptr++;
			break;
		}
		ptr++;
	}
	tile (fdeep, LEAF, 0, s1, s2, ZERO);
	*s1 = *s2 = 0;

	while (fcount >= 3) {
		if (++fdeep > Deepest)
			Deepest = fdeep;
		while (qi--) {
			if (*(*ptr + strlen (*ptr) -1) != '/') {
				--fcount;
				s1 = *ptr;
				ptr++;
				break;
			}
			ptr++;
		}
		while (qi--) {
			if (*(*ptr + strlen (*ptr) -1) != '/') {
				--fcount;
				s2 = *ptr;
				ptr++;
				break;
			}
			ptr++;
		}
		while (qi--) {
			if (*(*ptr + strlen (*ptr) -1) != '/') {
				--fcount;
				if (Fstop && fcount && ((fdeep+1)/Deepmod >= maxpgdeep)) {
					s3 = more;
					tile (fdeep, TRIPLE, 0, s1, s2, s3);
					*s1 = *s2 = 0;
					**ptr = 0;
					while (qi--) {
						++ptr;
						if (*(*ptr + strlen (*ptr) -1) != '/')
							**ptr = 0;
					}
					return;
				}
				s3 = *ptr;
				ptr++;
				break;
			}
			ptr++;
		}
		tile (fdeep, TRIPLE, 0, s1, s2, s3);
		*s1 = *s2 = *s3 = 0;
	}
	if (fcount == 2) {
		if (++fdeep > Deepest)
			Deepest = fdeep;
		while (qi--) {
			if (*(*ptr + strlen (*ptr) -1) != '/') {
				--fcount;
				s1 = *ptr;
				ptr++;
				break;
			}
			ptr++;
		}
		while (qi--) {
			if (*(*ptr + strlen (*ptr) -1) != '/') {
				--fcount;
				s2 = *ptr;
				ptr++;
				break;
			}
			ptr++;
		}
		tile (fdeep, DOUBLE, 0, s1, s2, ZERO);
		*s1 = *s2 = 0;
	}
	else if (fcount == 1) {
		if (++fdeep > Deepest)
			Deepest = fdeep;
		while (qi--) {
			if (*(*ptr + strlen (*ptr) -1) != '/') {
				--fcount;
				s1 = *ptr;
				ptr++;
				break;
			}
			ptr++;
		}
		tile (fdeep, SINGLE, 0, s1, ZERO, ZERO);
		*s1 = 0;
	}
}
//E*O*F listfiles.c//

echo x - mapper.c
cat > "mapper.c" << '//E*O*F mapper.c//'
/* mapper.c */

#include "globals.h"

extern int errno;

mapper (dirname)
char *dirname;
{
	char dirego[IQUARTK];
	char cmd[IHALFK];
	char newpath[IONEK];
	char *pathptr;
	char *filebuf;
	char *fileend;
	char *ptrbuf;
	char **ptr;
	int  ccount;
	int  fcount;
	int  dcount;
	int  ptrcount;
	int  qi;
	int  flush;

	if (++Ty > Deepest)
		Deepest = Ty;
	sprintf (cmd, "/bin/ls -aFq %s > %s",dirname, Listfile);
	errno = 0;
	system (cmd);
	if (errno)
		BAILOUT;

	iego (dirname, dirego, '/', '/');

	if ((ccount = iread (Listfile, &filebuf)) < 1)
		goaway();
	fileend = filebuf + ccount;
	if ((ptrcount = illistn (filebuf, fileend, &ptrbuf)) < 1)
		BAILOUT;

	/* count files and directories */
	ptr = (char **)ptrbuf;
	for (dcount = fcount = 0, DOUNCOUN (ptrcount, qi); ptr++)
		(*(*ptr + strlen (*ptr) -1) == '/') ? ++dcount : ++fcount;

	if (dcount < 2) {
		strcat (dirego, "!");
		tile (Ty, BUD, 0, dirego, ZERO, ZERO);
		free (filebuf);
		return;
	}

	if (dcount == 2 && !fcount) {
		strcat (dirego, "/");
		tile (Ty, BUD, 0, dirego, ZERO, ZERO);
		free (filebuf);
		return;
	}

	/* At this point, the next tile is a Twig if dirname contains
	   both files and directories, or no files and multiple dirs.
	   The next tile is a Joint if dirname contains files only, or
	   a single directory.
	*/
	if (dcount == 2) {
		strcat (dirego, "/");
		tile (Ty, JOINT, 0, dirego, ZERO, ZERO);
		ptr = (char **)ptrbuf;
		listfiles (ptr, ptrcount, fcount);
		free (filebuf);
		return;
	}
	if (dcount == 3 && !fcount) {
		strcat (dirego, "/");
		tile (Ty, JOINT, 0, dirego, ZERO, ZERO);
		strcpy (newpath, dirname);
		pathptr = newpath + strlen (newpath);
		*pathptr++ = '/';
		ptr = (char **)ptrbuf;
		for (DOUNCOUN (ptrcount, qi); ptr++) {
			if (strcmp (*ptr, "./") && strcmp (*ptr, "../")) {
				if (!Depth || Ty < (Depth-1)) {
					*(*ptr + strlen (*ptr) -1) = 0;
					strcpy (pathptr, *ptr);
					mapper (newpath);
					Ty--;
				}
				else {
					*(*ptr + strlen (*ptr) -1) = '!';
					tile (Ty+1, BUD, 0, *ptr, ZERO, ZERO);
					if (Ty+1 > Deepest)
						Deepest = Ty+1;
				}
				free (filebuf);
				return;
			}
		}
	}
	if (fcount) { /* and dcount > 2 */
		strcat (dirego, "/");
		tile (Ty, TWIG, dcount-2, dirego, ZERO, ZERO);
		ptr = (char **)ptrbuf;
		listfiles (ptr, ptrcount, fcount);

		strcpy (newpath, dirname);
		pathptr = newpath + strlen (newpath);
		*pathptr++ = '/';
		ptr = (char **)ptrbuf;
		for (DOUNCOUN (ptrcount, qi); ptr++) {
			if (**ptr) { /* not a file */
				if (strcmp (*ptr, "./") && strcmp (*ptr, "../")) {
					++Tx;
					if (!Depth || Ty < (Depth-1)) {
						*(*ptr + strlen (*ptr) -1) = 0;
						strcpy (pathptr, *ptr);
						mapper (newpath);
						Ty--;
					}
					else {
						*(*ptr + strlen (*ptr) -1) = '!';
						tile (Ty+1, BUD, 0, *ptr, ZERO, ZERO);
						if (Ty+1 > Deepest)
							Deepest = Ty+1;
					}
				}
			}
		}
		free (filebuf);
		return;
	}

	/* no files, multiple dirs */
	strcat (dirego, "/");
	tile (Ty, TWIG, dcount-3, dirego, ZERO, ZERO);

	flush = 1;
	strcpy (newpath, dirname);
	pathptr = newpath + strlen (newpath);
	*pathptr++ = '/';
	ptr = (char **)ptrbuf;
	for (DOUNCOUN (ptrcount, qi); ptr++) {
		if (strcmp (*ptr, "./") && strcmp (*ptr, "../")) {
			flush ? (flush = 0) : ++Tx;
			if (!Depth || Ty < (Depth-1)) {
				*(*ptr + strlen (*ptr) -1) = 0;
				strcpy (pathptr, *ptr);
				mapper (newpath);
				Ty--;
			}
			else {
				*(*ptr + strlen (*ptr) -1) = '!';
				tile (Ty+1, BUD, 0, *ptr, ZERO, ZERO);
				if (Ty+1 > Deepest)
					Deepest = Ty+1;
			}
		}
	}
	free (filebuf);
	return;
}

//E*O*F mapper.c//

echo x - package.c
cat > "package.c" << '//E*O*F package.c//'
/* package.c */

#include "globals.h"

package ()
{
	char *buf;
	struct tile *tp;
	struct tile **tmp;
	int tcount;
	int linkc;
	int depth = Deepest +1;
	int qi, qj;

	close (Td);
	if ((tcount = iread (Tiles, &buf)) < 1)
		BAILOUT;
	tcount /= TSIZ;

	Mcount = depth * (Tx+1);
	if ((Mp = (struct tile **)malloc (Mcount * sizeof (struct tile **)))
	== (struct tile **) NULL)
		BAILOUT;

	/* init all locations to Blank */
	for (tmp = Mp, DOUNCOUN (Mcount, qi); tmp++)
		*tmp = &Blank;

	/* remap tcount tiles in buf into a rectangular array, Y first:
	   line 0:   0,0     /0,1     /0,2
	   line 1:   1,0    / 1,1    / 1,2
	   (6 deep)  2,0   /  2,1   /  2,2
	   line 3:   3,0  /   3,1  /   3,2
	   line 4:   4,0 /    4,1 /    4,2
	   Deepest:  5,0/     5,1/     5,2
	*/
	tp = (struct tile *)buf;
	for (DOUNCOUN (tcount, qi); tp++)
		Mp[tp->hor * depth + tp->ver] = tp;

	/* blank out leader to first tile (root) only */
	tp = Mp[0];
	strcpy (tp->line1, "                ");

	/* Find and complete every Twig, line-by-line left-to-right.
	   Do not look for Twig in bottom line or at right edge.
	   To right of each Twig, substitute Twig->linkc number of Blank
	   tiles with Tap tiles (or Tip at the terminal tile).
	   Tap or Tip tiles must be installed directly above non-blank
	   tiles only: if next horizontal position is located above a Blank,
	   a Span tile is laid instead.
	*/
	for (qi = 0; qi < Deepest; qi++) {              /* Y loop */
		for (qj = 0; qj < Tx; qj++) {               /* X loop */
			if (linkc = (Mp[qj*depth+qi])->linkc) { /* a Twig? */
				++qj;                               /* next hor. tile */
				for (--linkc; linkc; qj++) {        /* not incl. Tip */
					if (Mp[qj*depth+qi+1] == &Blank) {
						Mp[qj*depth+qi] = &Span;
						continue;
					}
					Mp[qj*depth+qi] = &Tap;
					linkc--;
				}
				while (Mp[qj*depth+qi+1] == &Blank) {
					Mp[qj*depth+qi] = &Span;
					qj++;
				}
				Mp[qj*depth+qi] = &Tip;
			}
		}
	}

/***** To print matrix data:
	for (tmp = Mp, DOUNCOUN (Mcount, qi); tmp++)
		printf ("%s]\n%s]\n%s]\n................\n",
		(*tmp)->line1, (*tmp)->line2, (*tmp)->line3);
*/

/***** To print file data:
	tp = (struct tile *)buf;
	for (qi = tcount; qi--; tp++)
		printf ("Y:%d X:%d B:%d\n%s]\n%s]\n%s]\n................\n",
		tp->ver, tp->hor, tp->linkc, tp->line1, tp->line2, tp->line3);
*/


}
//E*O*F package.c//

echo x - split.c
cat > "split.c" << '//E*O*F split.c//'
/* split.c */

#include "globals.h"

split ()
{
	static char alpha[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
	char   counter[32];        /* horiz. numerical id of page */
	char   pad[4];             /* 0, 1, or 2 newlines at end of page */
	char   verid[4];           /* vertical, alpha id of page */
	char   topline[IONEK];     /* top border of page */
	char   botline[IONEK];     /* bottom border of page */
	char   blankline[IONEK];
	char   strip[3*IONEK];     /* 3 lines: a horiz. strip of tiles */
	struct tile *tp;
	char   *s1, *s2, *s3;
	char   *pp;
	int    depth = Deepest +1;
	int    width = Tx +1;
	int    darktile;           /* any tile except Blank */
	int    skip = 0;           /* count of blank pages skipped */
	int    thishor;            /* number of tiles horiz. on this page */
	int    thisver;            /* number of tiles vert. on this page */
	int    horpcnt;            /* number of horizontal pages of data */
	int    verpcnt;            /* number of vertical pages of data */
	int    padline;            /* pad page at bottom with blank lines */
	int    realcols;           /* actual number of cols printed/page */
	int    stripsiz;           /* characters printed per strip */
	int    qy, qx;
	int    ty, tx;

	padline = Lin % 3;
	horpcnt = Tx/Widemod +1;
	verpcnt = Deepest/Deepmod +1;
	realcols = Widemod * 16 +3 +1;  /* newline at end */
	stripsiz = realcols * 3;

	fprintf (stderr, "Matrix of %d horizontal %s @ %d %s per list.\n",
		verpcnt, (verpcnt > 1) ? "lists" : "list",
		horpcnt, (horpcnt > 1) ? "pages" : "page");

	pp = topline;
	*pp++ = SPACE;
	*pp++ = SPACE;
	for (DOUNCOUN (realcols-4, qx); *pp++ = ':');
	*pp = '\n';

	pp = botline;
	*pp++ = SPACE;
	*pp++ = SPACE;
	for (DOUNCOUN (realcols-4, qx); *pp++ = ';');
	*pp = '\n';

	pp = blankline;
	*pp++ = SPACE;
	*pp++ = '(';
	for (DOUNCOUN (realcols-4, qx); *pp++ = SPACE);
	*pp++ = ')';
	*pp = '\n';

	if (padline)
	   strcpy (pad, ((padline == 1) ? "\n" : "\n\n"));

	/* Output pages with number X coordinates and letter Y coordinates.
	   Output pages in horizontal order first.
	*/
	for (thisver = Deepmod, qy = 0; qy < verpcnt; qy++) {
		if (verpcnt < 26) {       /* one-character alpha coordinate */
			verid[0] = alpha[qy];
			verid[1] = SPACE;
			verid[2] = SPACE;
		}
		else if (verpcnt < 676) { /* two-character alpha coordinate */
			ty = qy % 676;        /* a number between 0 and 675 */
			tx = ty / 26;         /* a number between 0 and 25 */
			verid[0] = alpha[tx];
			tx = qy % 26;         /* a number between 0 and 25 */
			verid[1] = alpha[tx];
			verid[2] = SPACE;
		}
		else {                    /* three-character alpha coordinate */
			qx = qy % 17576;      /* a number between 0 and 17575 */
			tx = qx / 676;        /* a number between 0 and 25 */
			verid[0] = alpha[tx];
			ty = qx % 676;        /* a number between 0 and 675 */
			tx = ty / 26;         /* a number between 0 and 25 */
			verid[1] = alpha[tx];
			tx = qx % 26;         /* a number between 0 and 25 */
			verid[2] = alpha[tx];
		}

		/* Each horizontal pagelist already output contained
		   width * Deepmod tiles.  When on the last pagelist, the
		   remaining tilecount divided by the width of the tile matrix
		   gives the count of tiles to print vertically.
		*/
		if (qy+1 == verpcnt)
			thisver = (Mcount - qy * width * Deepmod) / width;

		for (thishor = Widemod, qx = 0; qx < horpcnt; qx++) {
			/* When on the last page of the horizontal pagelist,
			   the number of horizontal tiles to print on the page is
			   width % Widemod (if there is a remainder), or
			   Widemod (if there is no remainder).
			*/
			if (qx+1 == horpcnt)
				if (!(thishor = width % Widemod))
					thishor = Widemod;

			for (darktile = ty = 0; ty < thisver; ty++) {
				for (tx = 0; tx < thishor; tx++) {
					tp = Mp[qx*depth*Widemod+tx*depth+qy*Deepmod+ty];
					if (tp != &Blank)
						darktile = 1;
					if (darktile)
						break;
				}
				if (darktile)
					break;
			}
			if (!Printblank && !darktile) {
				++skip;
				goto skippage;
			}

			sprintf (counter, "  %d\n", qx +1);
			write (1, counter, strlen (counter));
			write (1, topline, realcols-1);
			for (ty = 0; ty < thisver; ty++) {
				s1 = strip;
				s2 = s1 + realcols;
				s3 = s2 + realcols;
				if (ty == 0) {
					*s1++ = verid[0];
					*s2++ = verid[1];
					*s3++ = verid[2];
				}
				else {
					*s1++ = SPACE;
					*s2++ = SPACE;
					*s3++ = SPACE;
				}
				*s1++ = '(';
				*s2++ = '(';
				*s3++ = '(';
				for (tx = 0; tx < thishor; tx++) {
					/* Each previous column (qx) of pages advanced the
					   Mp index of the current tile column by
					   depth * Widemod.  Each previous tile column on
					   this page advanced the Mp index by depth.  The
					   current pointer is the offset from the current
					   tile column start, by the current depth.
					*/
					tp = Mp[qx*depth*Widemod+tx*depth+qy*Deepmod+ty];
					strcpy (s1, tp->line1);
					s1 += 16;
					strcpy (s2, tp->line2);
					s2 += 16;
					strcpy (s3, tp->line3);
					s3 += 16;
				}
				if (thishor < Widemod) {
					for  (tp = &Blank; tx < Widemod; tx++) {
						strcpy (s1, tp->line1);
						s1 += 16;
						strcpy (s2, tp->line2);
						s2 += 16;
						strcpy (s3, tp->line3);
						s3 += 16;
					}
				}
				*s1++ = ')';
				*s2++ = ')';
				*s3++ = ')';
				*s1 = '\n';
				*s2 = '\n';
				*s3 = '\n';
				write (1, strip, stripsiz);
			}
			if (thisver < Deepmod)
				for (ty = 3 * (Deepmod-thisver); ty; ty--)
					write (1, blankline, realcols);

			write (1, botline, realcols-1);
			if (padline)
				write (1, pad, padline);
skippage:;
		}
	}
	if (skip)
		fprintf (stderr, "Skipped %d blank pages.\n", skip);
}
//E*O*F split.c//

echo x - tile.c
cat > "tile.c" << '//E*O*F tile.c//'
/* tile.c */

#include "globals.h"

tile (ver, kind, linkc, str1, str2, str3)
int kind, linkc;
char *str1, *str2, *str3;
{
	char *sp, *lim;
	struct tile *tp;
	int q1, q2, q3;

	if (!NULCHARP (str1) && ((q1 = strlen (str1)) > MAXFN)) {
		*(str1 + MAXFN -1) = '<';
		*(str1 + MAXFN) = 0;
		q1 = MAXFN;
	}
	if (!NULCHARP (str2) && ((q2 = strlen (str2)) > MAXFN)) {
		*(str2 + MAXFN -1) = '<';
		*(str2 + MAXFN) = 0;
		q2 = MAXFN;
	}
	if (!NULCHARP (str3) && ((q3 = strlen (str3)) > MAXFN)) {
		*(str3 + MAXFN -1) = '<';
		*(str3 + MAXFN) = 0;
		q3 = MAXFN;
	}

	switch (kind) {
		default:
		case BLANK:
			tp = &Blank;
		break;

		case SINGLE:
			tp = &Single;
			strcpy (tp->line1 , str1);
			lim = tp->line1 + MAXFN +1;
			sp = tp->line1 + q1;
			while (sp < lim)
				*sp++ = SPACE;
		break;

		case DOUBLE:
			tp = &Double;
			strcpy (tp->line1 , str1);
			lim = tp->line1 + MAXFN +1;
			sp = tp->line1 + q1;
			while (sp < lim)
				*sp++ = SPACE;
			strcpy (tp->line2 , str2);
			lim = tp->line2 + MAXFN +1;
			sp = tp->line2 + q2;
			while (sp < lim)
				*sp++ = SPACE;
		break;

		case TRIPLE:
			tp = &Triple;
			strcpy (tp->line1 , str1);
			lim = tp->line1 + MAXFN +1;
			sp = tp->line1 + q1;
			while (sp < lim)
				*sp++ = SPACE;
			strcpy (tp->line2 , str2);
			lim = tp->line2 + MAXFN +1;
			sp = tp->line2 + q2;
			while (sp < lim)
				*sp++ = SPACE;
			strcpy (tp->line3 , str3);
			lim = tp->line3 + MAXFN +1;
			sp = tp->line3 + q3;
			while (sp < lim)
				*sp++ = SPACE;
		break;

		case LEAF:
			tp = &Leaf;
			strcpy (tp->line2 , str1);
			lim = tp->line2 + MAXFN +1;
			sp = tp->line2 + q1;
			while (sp < lim)
				*sp++ = SPACE;
			strcpy (tp->line3 , str2);
			lim = tp->line3 + MAXFN +1;
			sp = tp->line3 + q2;
			while (sp < lim)
				*sp++ = SPACE;
		break;

		case BUD:
			tp = &Bud;
			strcpy (tp->line2 , str1);
			lim = tp->line2 + MAXFN +1;
			sp = tp->line2 + q1;
			while (sp < lim)
				*sp++ = SPACE;
		break;

		case JOINT:
			tp = &Joint;
			strcpy (tp->line2 , str1);
			lim = tp->line2 + MAXFN +1;
			sp = tp->line2 + q1;
			while (sp < lim)
				*sp++ = SPACE;
		break;

		case TWIG:
			tp = &Joint;
			strcpy (tp->line2 , str1);
			lim = tp->line2 + MAXFN +1;
			sp = tp->line2 + q1;
			while (sp < lim)
				*sp++ = '~';
		break;
	}

	tp->ver = ver;
	tp->hor = Tx;
	tp->linkc = linkc;

	if (write (Td, tp, TSIZ) != TSIZ)
		 BAILOUT;
}
//E*O*F tile.c//

echo x - treeD.c
cat > "treeD.c" << '//E*O*F treeD.c//'
/* treeD.c --- Unix file system diagrammer */
/******************************
* Author: Istvan Mohos
* March 1990
******************************/

#define MAIN
#include "globals.h"

main (argc, argv)
int argc;
char *argv[];
{
	int  optchar;
	char **opt;

	if (argc == 1)
cmderr:
		fprintf(stderr,
"Usage: %s [-wN] [-lN] [-dN] [-fN] [-blank] dir\n%s%s%s%s%s", argv[0],
"\t-w N   maximum printable column width of page (min.19, max.1023)\n",
"\t-l N   maximum count of lines per page (min.6)\n",
"\t-d N   limit directory recursion to N depth\n",
"\t-f N   truncate long file lists beyond the Nth vertical page\n",
"\t-blank force the printing of blank pages (suppressed by default)\n"),
		exit(1);

	opt = &(argv[1]);
	while (optchar = iopt (&opt)) {
		switch (optchar) {
			default:
				goto cmderr;
			case 'w':
				if ((Wid = atoi (*opt)) < 19 || Wid > 1023)
					fprintf(stderr,
					"page width: 19 -- 1023 columns\n"), exit(1);
				break;
			case 'l':
				if ((Lin = atoi (*opt)) < 6)
					fprintf(stderr,
					"page length: minimum 6 lines\n"), exit(1);
				break;
			case 'd':
				if ((Depth = atoi (*opt)) < 1)
					fprintf(stderr,
					"minimum depth: 1 level\n"), exit(1);
				break;
			case 'f':
				if ((Fstop = atoi (*opt)) < 1)
					fprintf(stderr,
					"minimum depth: 1 page\n"), exit(1);
				break;
			case 'b':
				Printblank = 1;
				break;
		}
	}

	init();
	mapper (*opt);
	package();
	split();
	goaway();
}
//E*O*F treeD.c//

echo x - makefile
cat > "makefile" << '//E*O*F makefile//'
#
CC=/bin/cc

# PC6300+ SysV
#CFLAGS=-O -DREALUNIX -DPLUS6300

# BSD
CFLAGS=-O

#.SILENT:

IFILES= iopt.o iego.o ierror.o iread.o illistn.o
FILES= treeD.o init.o tile.o listfiles.o mapper.o package.o goaway.o split.o

treeD: ${FILES} ${IFILES}
	$(CC) -o $@ ${FILES} ${IFILES}

${IFILES}:i.h
${FILES}: globals.h
//E*O*F makefile//

echo x - treeX
cat > "treeX" << '//E*O*F treeX//'
#!/bin/sh
# Istvan Mohos, May 1990
if test $# -eq 0
then echo "Usage: $0 dirname"
    exit 1
fi
name=`basename $0`
treeD -l79 -w115 -f1 $* > /tmp/$$.tex
tail -50 $0 > $name.tex
echo \\listing\{/tmp/$$.tex\} >> $name.tex
echo \\bye >> $name.tex
tex $name.tex
/bin/rm /tmp/$$.tex
exit 0

% TeX input begins here
\vsize 9.9in
\hsize 7.5in
\font\stt=cmtt8
\raggedbottom \nopagenumbers \stt
\baselineskip 9pt
% verbatim macros from Appendix D (pages 381, 391) of The TeXbook
% redefined ( ) : ; ~ > | for char graphics
\def\uncatcodespecials{\def\do##1{\catcode`##1=12 }\dospecials}
\def\setupverbatim{\stt
  \def\par{\leavevmode\egroup\box0\endgraf}
  \obeylines \uncatcodespecials \obeyspaces
  \catcode`\`=\active \catcode`\^^I=\active
  \catcode`(=\active \catcode`)=\active
  \catcode`:=\active \catcode`;=\active
  \catcode`\~=\active \catcode`>=\active \catcode`|=\active
  \everypar{\startbox}}
\newdimen\w \setbox0=\hbox{\stt\space} \w=4\wd0 % width of tab
\def\startbox{\setbox0=\hbox\bgroup}
{\catcode`\`=\active \gdef`{\relax\lq}}
{\catcode`\^^I=\active
  \gdef^^I{\leavevmode\egroup
    \dimen0=\wd0          % the width so far, or since the previous tab
    \divide\dimen0 by\w
    \multiply\dimen0 by\w % compute previous multiple of \w
    \advance\dimen0 by\w  % advance to next multiple of \w
    \wd0=\dimen0 \box0 \startbox}}
{\obeyspaces\global\let =\ } % let active space become control space
\def\listing#1{\par\begingroup\setupverbatim\input#1 \endgroup}
%
\newdimen\vsz % \vsz dimensions are relative to \baselineskip
\vsz=9pt
\newdimen\hsz % \hsz dimensions are a function of char width
\setbox0=\hbox{\stt m}
\hsz=\wd0
%
{\catcode`(=\active  % vertical left border
\gdef({\kern.6\hsz\vrule width.1\hsz height.8\vsz depth.2\vsz\kern.3\hsz}}
{\catcode`)=\active  % vertical right border
\gdef){\kern.2\hsz\vrule width.1\hsz height.8\vsz depth.2\vsz\kern.7\hsz}}
{\catcode`:=\active  % horizontal upper border
\gdef:{\kern-.4\hsz\vrule width1.7\hsz height-.17\vsz depth.22\vsz\kern-.3\hsz}}
{\catcode`;=\active  % horizontal lower border
\gdef;{\kern-.4\hsz\vrule width1.7\hsz height.78\vsz depth-.73\vsz\kern-.3\hsz}}
{\catcode`\~=\active % horizontal branch
\gdef~{\vrule width1\hsz height0\vsz depth.2\vsz}}
{\catcode`>=\active  % elbow (knob) moving horizontal branch to vertical
\gdef>{\vrule width.7\hsz height0\vsz depth.2\vsz\kern.3\hsz}}
{\catcode`|=\active  % vertical branch
\gdef|{\kern.3\hsz\vrule width.4\hsz height.8\vsz depth.2\vsz\kern.3\hsz}}
//E*O*F treeX//

echo x - treeD.tex
cat > "treeD.tex" << '//E*O*F treeD.tex//'
% XREF treeD treeX
\font\myit=cmti9
\font\mytt=cmtt9
\font\myletr=cmssq8
\font\bb=cmssbx10

\def\date{{\myletr May 4, 1990}}
\def\vers{{\myletr Version 1.1}}
\def\mansection{USER (1)}
\def\name{treeD}
\def\IT{{\bb treeD}}
\def\ALT{{\bb treeX}}

\hsize 5.6in
\vsize 8in
\raggedbottom
\parskip 0pt
\parindent 0pt
\myletr

% to enclose a horizontal upper-case string in a rectangle
\def\dx#1{\kern -.15em\setbox0=\hbox{\thinspace{#1}\thinspace}
\dimen0=\ht0 \advance\dimen0 by 2pt
\dimen1=\dimen0 \advance\dimen1 by -.3pt
\dimen3=\dp0 \advance\dimen3 by 2.3pt
\dimen4=\dimen3 \advance\dimen4 by -.3pt
\vrule height \dimen0 depth \dimen3 width .3pt
\vrule height \dimen0 depth -\dimen1 width \wd0
\vrule width -\wd0
\vrule height -\dimen4 depth \dimen3 width \wd0
\vrule width -\wd0
\box0
\vrule height \dimen0 depth \dimen3 width .3pt
}
% to enclose a single \tt key in a square
\def\key#1{\setbox0=\hbox{\thinspace{\mytt#1}\thinspace}
\dimen0=2ex \dimen1=\dimen0 \advance\dimen1 by -.3pt
\dimen3=.5ex \dimen4=\dimen3 \advance\dimen4 by -.3pt
\dimen5=\wd0
\vrule height \dimen0 depth \dimen3 width .3pt    % left vertical
\vrule height \dimen0 depth -\dimen1 width 1.05em % top horiz
\vrule width -1.05em                              % backup to left
\box0                                             % install text
\vrule width -\dimen5                             % backup to left
\vrule height -\dimen4 depth \dimen3 width 1.05em % bottom horiz
\vrule height \dimen0 depth \dimen3 width .3pt    % right vertical
\thinspace
}
\def\bs{{\char92}}       % \
\def\ul{{\char95}}       % _
\def\pipe{{\char124}}    % |
\def\lc{{\char123}}      % {
\def\rc{{\char125}}      % }
\def\lbrac{{\char91}}    % [
\def\rbrac{{\char93}}    % ]
\def\langle{{\char60}}   % <
\def\rangle{{\char62}}   % >
\def\circum{{\char94}}   % ^
\def\caret{{\char94}}    % ^
\def\tilde{{\char126}}   % ~
\def\dollar{{\char36}}   % $
\def\at{{\char64}}       % @

\headline={{\myletr\name\hfil\mansection\hfil\name}}
\footline={\ifodd\pageno\rightfoot\else\leftfoot\fi}
\def\leftfoot{\rlap{\myletr\folio}\hfil{\vers\ --- \date}\hfil}
\def\rightfoot{\hfil{\vers\ --- \date}\hfil\llap{\myletr\folio}}
\def\S#1{\leftline{}\par\leftskip=-20pt{\bb#1}\smallskip\leftskip=0pt}
\def\SU#1{\par\leftskip=-20pt{\bb#1}\smallskip\leftskip=0pt}
\def\L{\par\leftline{}\par}
\def\I#1{\leftskip=20pt{#1}\par\leftskip=0pt}

\S{NAME}
\IT\ --- Unix file system diagrammer

\ALT\ --- produce a file system diagram in \TeX

\S{SYNOPSIS}
\IT\
\lbrac --w N\rbrac\
\lbrac --l N\rbrac\
\lbrac --d N\rbrac\
\lbrac --f N\rbrac\
\lbrac --blank\rbrac\
dirname

\ALT\ dirname

\S{DESCRIPTION}
The \IT\ file system diagrammer bypasses text width limitations imposed
by fan-fold paper or scrollable screen output mediums, and
constructs a true two-dimensional map of the file
system ``{\myit dirname\/}'' specified on the command line.
To realize a hardcopy of this map, the internal diagram is
output in the form of
equal-sized pages, each page bearing a letter/number
coordinate analogous to lettered, numbered grids on a street map.
Command line options fine tune the format of the output, control
non-default grid sizes and the depth of the directory search
as follows:
\L
\item{{\bb --w} N}
specifies that the maximum printing width of a physical page
is N character columns.  The internal width of individual
grid rectangles will be expanded or reduced to the
nearest number of columns not greater
than N that will permit printing
full 16-column vertical file lists in parallel.  Each page must
be wide enough to contain at least one fixed width vertical file list,
two additional bytes per page for the vertical
grid boundary markers, and one leftmost byte for the vertical character
coordinate stamp of the grid: 19 bytes minimum.  The default page width
is nominally 72 bytes, 67 bytes of which is used to print a grid
rectangle.
\L
\item{{\bb --l} N}
specifies the maximum number of lines N, that can be printed on
a physical page.  Vertical lists of the map are arranged in
{\myit tiles\/}, each tile three lines in depth.  The smallest page
must be able to contain one tile, plus two lines for the top and bottom
horizontal grid markers, and a topmost line for the numerical
coordinate stamp of the grid: a minimum of 6 lines.  The default
page length is 66 lines, containing 21 tiles.
\L
\item{{\bb --d} N}
instructs \IT\ to halt directory recursion at N depth.  That is,
the command
\dx{treeD --d1 ../usr}\ results in diagramming the file structure of
{\myit ../usr\/}, but without descending into or listing the contents
of any of the
subdirectories.  The default N depth (zero) specifies infinite descent.
\L
\item{{\bb --f} N}
results in truncating long file lists beyond the Nth vertical page.
The default depth page can display 21 directory levels or
63 plain files.  The structure of even complex file systems can often
be contained on a single horizontal grid strip, branches using up
map width much more readily than depth.  One or two directories with
many plain files may necessitate vertical expansion to show
excessively deep file lists; and can double or triple the total size of
the map while contributing little to the structural information.  The
{\myit --f\/} option truncates such long (plain) file lists beyond the
specified vertical page.
\L
\item{{\bb --blank}}
forces \IT\ to output blank ``filler'' pages.  If both the width and
the depth of the map is more than one grid rectangle,
the map may contain blank grids.
By default, such pages are not output.  With the {\myit --blank\/}
option any blank grids are also printed, each otherwise blank page
containing the number and letter coordinates and the outline
of the grid.
\L
The \IT\ process lists the files of the examined directories via the
\dx{ls -aFq}\ command, producing also the names of ``hidden files'',
and appending a single ``file type'' character to names of
directories, executable files, and a number of special files
(differently between System~V and BSD).  \IT\ does not display the
\key{.} and \key{..}
special directory links.  File names longer than
14 characters are truncated in the map at 14 bytes, and the
``name too long''
file type marker \key{\langle} is added as the fifteenth
character.  The file type marker of inaccessible directories
is changed from \key{/} to \key{!}, also in the cases when the
directory was made unreadable by the {\myit --d\/} option limiting
the descent.  The sixteenth byte of a printed plain filename is
always a space.
\L
The \ALT\ Bourne shell script first executes the
\smallskip
\I{\mytt treeD \ -l 79 \ -w 115 \ -f1 \ \dollar *}
\smallskip
command to diagram the directory specified on its command line,
then converts the output of the \IT\ command to
plain \TeX\ input.  Grid borders originally
marked by vertical \key{(} \key{)} and
horizontal \key{:} \key{;} characters are converted to
thin vertical and horizontal lines; file branch markers
\key{\tilde} \key{\pipe} \key{\rangle} are converted to
bold lines.  The {\myit treeX.tex\/} file thus produced, is then input
to \TeX, and yields the device independent output
{\myit treeX.dvi\/}, ready for printing.  The \IT\ command line
options embedded within \ALT\ can be overridden by specifying new
options on \ALT's command line before the final ``{\myit dirname\/}'',
however the \TeX\ format is expecting the exact 115-byte width and
79-line pages shown above.
\bye
//E*O*F treeD.tex//

echo x - README
cat > "README" << '//E*O*F README//'
treeD --- Unix file system diagrammer

The utmost conceptual simplicity of file system trees belies the
difficulty of memorizing actual nodal layouts of specific trees.
In spite a plain topological essence of TREE no two trees are
the same; and most users would have an equal chance of being able
to recall the exact twig arrangement of last year's Christmas tree
as they would in describing a 30 Meg Unix subdirectory.

Recursive listers such as 'find' obscure the tree structure by the
one-dimensionality of their output: standard output mediums of
pinfeed paper or the scrollable screen rigidly limit the width,
while allowing infinitely long lists.

The treeD file system diagrammer erases the width limitation of
the output medium, and constructs true two-dimensional maps of
file systems.  To realize a hardcopy of a map, the internal diagram
is output as equal-sized pages, each page bearing a letter/number
coordinate analogous to lettered, numbered grids on a street map.
Command line options fine tune the format of the output, control
non-default grid sizes and the depth of the directory search.

-DREALUNIX should be enabled in the makefile when compiling under
System V.  The 'man page' is in the file treeD.tex, and should be
processed with "tex treeD" (assuming TeX capability at the site).
The Bourne script treeX converts treeD's character-graphics
depicting branching, to bold lines under plain TeX.
//E*O*F README//

echo Possible errors detected by \'wc\' [hopefully none]:
temp=/tmp/shar$$
trap "rm -f $temp; exit" 0 1 2 3 15
cat > $temp <<\!!!
     105     398    2671 globals.h
      78     213    1596 i.h
     160     591    4205 ilib.h
      19      30     262 goaway.c
      40     120     749 iego.c
      35      99     744 ierror.c
      40     137     914 illistn.c
     138     298    2905 init.c
      58     206    1330 iopt.c
      47     162    1143 iread.c
     153     457    2655 listfiles.c
     161     556    3553 mapper.c
      88     364    2351 package.c
     192     918    5652 split.c
     125     421    2371 tile.c
      64     198    1428 treeD.c
      19      38     311 makefile
      65     249    2480 treeX
     188    1047    7408 treeD.tex
      27     225    1419 README
    1802    6727   46147 total
!!!
wc  globals.h i.h ilib.h \
goaway.c iego.c ierror.c illistn.c init.c iopt.c iread.c \
listfiles.c mapper.c package.c split.c tile.c treeD.c \
makefile treeX treeD.tex README | sed 's=[^ ]*/==' | diff -b $temp -
exit 0
-- 
        Istvan Mohos
        ...uunet!pyrdc!pyrnj!hhb!istvan
        RACAL-REDAC/HHB 1000 Wyckoff Ave. Mahwah NJ 07430 201-848-8000
======================================================================



More information about the Alt.sources mailing list