about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLeah Neukirchen <leah@vuxu.org>2017-07-04 14:01:44 +0200
committerLeah Neukirchen <leah@vuxu.org>2017-07-04 14:01:44 +0200
commit42b6dbbbde03ac9134658fc531a1294a96f1d33d (patch)
tree680286c2b2fe4511059791f27103f1237a5de35b
downloadxdu-42b6dbbbde03ac9134658fc531a1294a96f1d33d.tar.gz
xdu-42b6dbbbde03ac9134658fc531a1294a96f1d33d.tar.xz
xdu-42b6dbbbde03ac9134658fc531a1294a96f1d33d.zip
Initial import of xdu-3.0.orig
-rw-r--r--Imakefile12
-rw-r--r--README94
-rw-r--r--XDu.ad19
-rw-r--r--patchlevel.h1
-rw-r--r--version.h1
-rw-r--r--xdu.c770
-rw-r--r--xdu.man204
-rw-r--r--xwin.c496
8 files changed, 1597 insertions, 0 deletions
diff --git a/Imakefile b/Imakefile
new file mode 100644
index 0000000..a40e57b
--- /dev/null
+++ b/Imakefile
@@ -0,0 +1,12 @@
+XCOMM
+XCOMM  Imakefile for xdu : X11 display of du output
+XCOMM
+XCOMM  Phillip Dykstra, phil@arl.mil
+XCOMM
+        DEPLIBS = XawClientDepLibs
+LOCAL_LIBRARIES = XawClientLibs
+           SRCS = xdu.c xwin.c 
+           OBJS = xdu.o xwin.o
+
+ComplexProgramTarget(xdu)
+InstallAppDefaults(XDu)
diff --git a/README b/README
new file mode 100644
index 0000000..84b306e
--- /dev/null
+++ b/README
@@ -0,0 +1,94 @@
+================================================================
+XDU - Display the output of "du" in an X window
+================================================================
+
+XDU is a program for displaying a graphical tree of disk space
+utilization as reported by the UNIX utility "du".  You can
+navigate up and down in the tree, sort things, and print out
+information.  See the manual page for details.
+
+This program can be found by anonymous ftp at
+
+	ftp.arl.mil:pub/xdu-3.0.tar.Z
+
+and should appear in the X11R6 contrib files.
+
+================================================================
+Compilation
+================================================================
+
+Use "xmkmf" to build a Makefile from the Imakefile.
+Then "make", "make install", "make install.man".
+But if for some reason you can't do that, try:
+
+	cc -o xdu xdu.c xwin.c -lXaw -lXt -lXext -lX11
+
+See the XDu.ad file if e.g. you have problems with
+  the selected font.
+
+This release was tested against X11R6 patch level 1 on an
+SGI running Irix 5.2.  It has been tested against X11R5 on
+SunOS 4.1.3, SunOS 5.2 (Solaris), SGI Irix 4.0.5, Gould
+UTX 2.1.
+
+================================================================
+Revision History
+================================================================
+
+Version 3.0	5 Jun 94
+  X11R6 contrib release
+  Popup help window
+  Now uses Athena widgets, but no menus or buttons yet
+
+Version 2.1	22 Jul 93
+  Fixed a bug in the sorting code where traversal back up a
+    sorted tree could land you in the wrong parent directory.
+
+Version 2.0	21 Jul 93
+  Added sorting.
+  Command line options.
+  More resources.
+  Better redraw behavior.
+  Bug fixes (to handle trailing slashes and directories
+     with no or zero size information).
+
+Version 1.1	5 Oct 91
+  Added resource control
+  Display of size information
+  More accurate label positioning
+
+Version 1.0	4 Sep 91
+  First public release
+
+================================================================
+Remaining Bug?
+================================================================
+
+  On startup on a Sun (but not on an SGI), keyboard input
+  may not be received by the application until you move the
+  mouse out of and back into the window.  Button presses are
+  fine.  Does anyone know what's going on there?
+  Mark Evans pointed out a fix - now in the BUG section of
+  the manual page, but I would still like to hear how/why
+  this happens.
+
+================================================================
+Acknowledgements
+================================================================
+Thanks for bug reports and code fixes from:
+
+Casey Leedom <casey@gauss.llnl.gov>
+Stephen Gildea <gildea@expo.lcs.mit.edu>
+Nelson Minar <nelson@reed.edu>
+Don Tiessen <dtiessen@silver.cs.umanitoba.ca>
+Gerry.Tomlinson@newcastle.ac.uk
+Mark Evans <mre1@itri.bton.ac.uk>
+Juha Takala <jta@piuha.sah.vtt.fi>
+
+And the many others who told me what they thought about it.
+
+Send any bugs/comments to:
+
+Phil Dykstra
+<phil@arl.mil>
+http://info.arl.mil/~phil/
diff --git a/XDu.ad b/XDu.ad
new file mode 100644
index 0000000..62f60c0
--- /dev/null
+++ b/XDu.ad
@@ -0,0 +1,19 @@
+!
+!  XDu Resources
+!
+!  I recommend the "-*-helvetica-bold-r-normal--14-*" font, but if
+!    you don't have it, or want your server default, then change it
+!    or comment it out respectively.
+!  Order choices: first, last, alpha, ralpha, size, rsize
+!  Color to taste.
+!
+!XDu*foreground:	yellow
+!XDu*background:	blue4
+!XDu*help*foreground:	green
+!XDu*help*background:	red4
+XDu*window.width:	600
+XDu*window.height:	480
+XDu.font:		-*-helvetica-bold-r-normal--14-*
+XDu.ncol:		5
+XDu.showsize:		true
+XDu.order:		first
diff --git a/patchlevel.h b/patchlevel.h
new file mode 100644
index 0000000..935ec35
--- /dev/null
+++ b/patchlevel.h
@@ -0,0 +1 @@
+#define PATCHLEVEL 0
diff --git a/version.h b/version.h
new file mode 100644
index 0000000..a650768
--- /dev/null
+++ b/version.h
@@ -0,0 +1 @@
+#define	XDU_VERSION	"3.0"
diff --git a/xdu.c b/xdu.c
new file mode 100644
index 0000000..fe05843
--- /dev/null
+++ b/xdu.c
@@ -0,0 +1,770 @@
+/*
+ *			X D U . C
+ *
+ * Display the output of "du" in an X window.
+ *
+ * Phillip C. Dykstra
+ * <phil@arl.mil>
+ * 4 Sep 1991.
+ * 
+ * Copyright (c)	Phillip C. Dykstra	1991, 1993, 1994
+ * The X Consortium, and any party obtaining a copy of these files from
+ * the X Consortium, directly or indirectly, is granted, free of charge, a
+ * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
+ * nonexclusive right and license to deal in this software and
+ * documentation files (the "Software"), including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons who receive
+ * copies from any such party to do so.  This license includes without
+ * limitation a license to do the foregoing actions under any patents of
+ * the party supplying this software to the X Consortium.
+ */
+#include <stdio.h>
+#include "version.h"
+
+extern char *malloc(), *calloc();
+
+#define	MAXDEPTH	80	/* max elements in a path */
+#define	MAXNAME		1024	/* max pathname element length */
+#define	MAXPATH		4096	/* max total pathname length */
+#define	NCOLS		5	/* default number of columns in display */
+
+/* What we IMPORT from xwin.c */
+extern int xsetup(), xmainloop(), xdrawrect(), xrepaint();
+
+/* What we EXPORT to xwin.c */
+extern int press(), reset(), repaint(), setorder(), reorder();
+extern nodeinfo(), helpinfo();
+int ncols = NCOLS;
+
+/* internal routines */
+char *strdup();
+void addtree();
+void parse_file();
+void parse_entry();
+void dumptree();
+void clearrects();
+void sorttree();
+
+/* order to sort paths by */
+#define	ORD_FIRST	1
+#define	ORD_LAST	2
+#define	ORD_ALPHA	3
+#define	ORD_SIZE	4
+#define	ORD_RALPHA	5
+#define	ORD_RSIZE	6
+#define	ORD_DEFAULT	ORD_FIRST
+int order = ORD_DEFAULT;
+
+/*
+ * Rectangle Structure
+ * Stores window coordinates of a displayed rectangle
+ * so that we can "find" it again on key presses.
+ */
+struct rect {
+	int	left;
+	int	top;
+	int	width;
+	int	height;
+};
+
+/*
+ * Node Structure
+ * Each node in the path tree is linked in with one of these.
+ */
+struct node {
+	char	*name;
+	long	size;		/* from here down in the tree */
+	long	num;		/* entry number - for resorting */
+	struct	rect rect;	/* last drawn screen rectangle */
+	struct	node *peer;	/* siblings */
+	struct	node *child;	/* list of children if !NULL */
+	struct	node *parent;	/* backpointer to parent */
+} top;
+struct node *topp = &top;
+#define	NODE_NULL ((struct node *)0)
+long nnodes = 0;
+
+/*
+ * create a new node with the given name and size info
+ */
+struct node *
+makenode(name,size)
+char *name;
+int size;
+{
+	struct	node	*np;
+
+	np = (struct node *)calloc(1,sizeof(struct node));
+	np->name = strdup(name);
+	np->size = size;
+	np->num = nnodes;
+	nnodes++;
+
+	return	np;
+}
+
+/*
+ * Return the node (if any) which has a draw rectangle containing
+ * the given x,y point.
+ */
+struct node *
+findnode(treep, x, y)
+struct	node *treep;
+int	x, y;
+{
+	struct	node	*np;
+	struct	node	*np2;
+
+	if (treep == NODE_NULL)
+		return	NODE_NULL;
+
+	if (x >= treep->rect.left && x < treep->rect.left+treep->rect.width
+	 && y >= treep->rect.top && y < treep->rect.top+treep->rect.height) {
+		/*printf("found %s\n", treep->name);*/
+		return	treep;	/* found */
+	}
+
+	/* for each child */
+	for (np = treep->child; np != NULL; np = np->peer) {
+		if ((np2 = findnode(np,x,y)) != NODE_NULL)
+			return	np2;
+	}
+	return	NODE_NULL;
+}
+
+/*
+ * return a count of the number of children of a given node
+ */
+int
+numchildren(nodep)
+struct node *nodep;
+{
+	int	n;
+
+	if (nodep == NODE_NULL)
+		return	0;
+
+	n = 0;
+	for (nodep = nodep->child; nodep != NODE_NULL; nodep=nodep->peer)
+		n++;
+
+	return	n;
+}
+
+/*
+ * fix_tree - This function repairs the tree when certain nodes haven't
+ * 	      had their sizes initialized. [DPT911113]
+ *	      * * * This function is recursive * * *
+ */
+long
+fix_tree(top)
+struct node *top;
+{
+	struct node *nd;
+
+	if (top == NODE_NULL)		/* recursion end conditions */
+		return 0;
+	if (top->size >= 0) 		/* also halt recursion on valid size */
+		return top->size;	/* (remember: sizes init. to -1) */
+
+	top->size = 0;
+	for (nd = top->child; nd != NODE_NULL; nd = nd->peer)
+		top->size += fix_tree(nd);
+
+	return top->size;
+}
+
+static char usage[] = "\
+Usage: xdu [-options ...] filename\n\
+   or  xdu [-options ...] < du.out\n\
+\n\
+Graphically displays the output of du in an X window\n\
+  options include:\n\
+  -s          Don't display size information\n\
+  +s          Display size information (default)\n\
+  -n          Sort in numerical order (largest first)\n\
+  -rn         Sort in reverse numerical order\n\
+  -a          Sort in alphabetical order\n\
+  -ra         Sort in reverse alphabetical order\n\
+  -c num      Set number of columns to num\n\
+  Toolkit options: -fg, -bg, -rv, -display, -geometry, etc.\n\
+";
+
+main(argc,argv)
+int argc;
+char **argv;
+{
+	top.name = strdup("[root]");
+	top.size = -1;
+
+	xsetup(&argc,argv);
+	if (argc == 1) {
+		if (isatty(fileno(stdin))) {
+			fprintf(stderr, usage);
+			exit(1);
+		} else {
+			parse_file("-");
+		}
+	} else if (argc == 2 && strcmp(argv[1],"-help") != 0) {
+		parse_file(argv[1]);
+	} else {
+		fprintf(stderr, usage);
+		exit(1);
+	}
+	top.size = fix_tree(&top);
+
+	/*dumptree(&top,0);*/
+	if (order != ORD_DEFAULT)
+		sorttree(&top, order);
+
+	topp = &top;
+	/* don't display root if only one child */
+	if (numchildren(topp) == 1)
+		topp = topp->child;
+
+	xmainloop();
+	exit(0);
+}
+
+void
+parse_file(filename)
+char *filename;
+{
+	char	buf[4096];
+	char	name[4096];
+	int	size;
+	FILE	*fp;
+
+	if (strcmp(filename, "-") == 0) {
+		fp = stdin;
+	} else {
+		if ((fp = fopen(filename, "r")) == 0) {
+			fprintf(stderr, "xdu: can't open \"%s\"\n", filename);
+			exit(1);
+		}
+	}
+	while (fgets(buf,sizeof(buf),fp) != NULL) {
+		sscanf(buf, "%d %s\n", &size, name);
+		/*printf("%d %s\n", size, name);*/
+		parse_entry(name,size);
+	}
+	fclose(fp);
+}
+
+/* bust up a path string and link it into the tree */
+void
+parse_entry(name,size)
+char *name;
+int size;
+{
+	char	*path[MAXDEPTH]; /* break up path into this list */
+	char	buf[MAXNAME];	 /* temp space for path element name */
+	int	arg, indx;
+	int	length;		/* nelson@reed.edu - trailing / fix */
+
+	if (*name == '/')
+		name++;		/* skip leading / */
+
+	length = strlen(name);
+	if ((length > 0) && (name[length-1] == '/')) {
+		/* strip off trailing / (e.g. GNU du) */
+		name[length-1] = 0;
+	}
+
+	arg = 0; indx = 0;
+	bzero(path,sizeof(path));
+	bzero(buf,sizeof(buf));
+	while (*name != NULL) {
+		if (*name == '/') {
+			buf[indx] = 0;
+			path[arg++] = strdup(buf);
+			indx = 0;
+			if (arg >= MAXDEPTH)
+				break;
+		} else {
+			buf[indx++] = *name;
+			if (indx >= MAXNAME)
+				break;
+		}
+		name++;
+	}
+	buf[indx] = 0;
+	path[arg++] = strdup(buf);
+	path[arg] = NULL;
+
+	addtree(&top,path,size);
+}
+
+/*
+ *  Determine where n1 should go compared to n2
+ *    based on the current sorting order.
+ *  Return -1 if is should be before.
+ *          0 if it is a toss up.
+ *	    1 if it should go after.
+ */
+int
+compare(n1,n2,order)
+struct node *n1, *n2;
+int order;
+{
+	int	ret;
+
+	switch (order) {
+	case ORD_SIZE:
+		ret = n2->size - n1->size;
+		if (ret == 0)
+			return strcmp(n1->name,n2->name);
+		else
+			return ret;
+		break;
+	case ORD_RSIZE:
+		ret = n1->size - n2->size;
+		if (ret == 0)
+			return strcmp(n1->name,n2->name);
+		else
+			return ret;
+		break;
+	case ORD_ALPHA:
+		return strcmp(n1->name,n2->name);
+		break;
+	case ORD_RALPHA:
+		return strcmp(n2->name,n1->name);
+		break;
+	case ORD_FIRST:
+		/*return -1;*/
+		return (n1->num - n2->num);
+		break;
+	case ORD_LAST:
+		/*return 1;*/
+		return (n2->num - n1->num);
+		break;
+	}
+
+	/* shouldn't get here */
+	fprintf(stderr,"xdu: bad insertion order\n");
+	return	0;
+}
+
+void
+insertchild(nodep,childp,order)
+struct node *nodep;	/* parent */
+struct node *childp;	/* child to be added */
+int order;		/* FIRST, LAST, ALPHA, SIZE */
+{
+	struct node *np, *np1;
+
+	if (nodep == NODE_NULL || childp == NODE_NULL)
+		return;
+	if (childp->peer != NODE_NULL) {
+		fprintf(stderr, "xdu: can't insert child with peers\n");
+		return;
+	}
+
+	childp->parent = nodep;
+	if (nodep->child == NODE_NULL) {
+		/* no children, order doesn't matter */
+		nodep->child = childp;
+		return;
+	}
+	/* nodep has at least one child already */
+	if (compare(childp,nodep->child,order) < 0) {
+		/* new first child */
+		childp->peer = nodep->child;
+		nodep->child = childp;
+		return;
+	}
+	np1 = nodep->child;
+	for (np = np1->peer; np != NODE_NULL; np = np->peer) {
+		if (compare(childp,np,order) < 0) {
+			/* insert between np1 and np */
+			childp->peer = np;
+			np1->peer = childp;
+			return;
+		}
+		np1 = np;
+	}
+	/* at end, link new child on */
+	np1->peer = childp;
+}
+
+/* add path as a child of top - recursively */
+void
+addtree(top, path, size)
+struct node *top;
+char *path[];
+int size;
+{
+	struct	node *np;
+
+	/*printf("addtree(\"%s\",\"%s\",%d)\n", top->name, path[0], size);*/
+
+	/* check all children for a match */
+	for (np = top->child; np != NULL; np = np->peer) {
+		if (strcmp(path[0],np->name) == 0) {
+			/* name matches */
+			if (path[1] == NULL) {
+				/* end of the chain, save size */
+				np->size = size;
+				return;
+			}
+			/* recurse */
+			addtree(np,&path[1],size);
+			return;
+		}
+	}
+	/* no child matched, add a new child */
+	np = makenode(path[0],-1);
+	insertchild(top,np,order);
+
+	if (path[1] == NULL) {
+		/* end of the chain, save size */
+		np->size = size;
+		return;
+	}
+	/* recurse */
+	addtree(np,&path[1],size);
+	return;
+}
+
+/* debug tree print */
+void
+dumptree(np,level)
+struct node *np;
+int level;
+{
+	int	i;
+	struct	node *subnp;
+
+	for (i = 0; i < level; i++)
+		printf("   ");
+
+	printf("%s %d\n", np->name, np->size);
+	for (subnp = np->child; subnp != NULL; subnp = subnp->peer) {
+		dumptree(subnp,level+1);
+	}
+}
+
+void
+sorttree(np, order)
+struct node *np;
+int order;
+{
+	struct	node *subnp;
+	struct	node *np0, *np1, *np2, *np3;
+
+	/* sort the trees of each of this nodes children */
+	for (subnp = np->child; subnp != NODE_NULL; subnp = subnp->peer) {
+		sorttree(subnp, order);
+	}
+	/* then put the given nodes children in order */
+	np0 = np;	/* np0 points to node before np1 */
+	for (np1 = np->child; np1 != NODE_NULL; np1 = np1->peer) {
+		np2 = np1;	/* np2 points to node before np3 */
+		for (np3 = np1->peer; np3 != NODE_NULL; np3 = np3->peer) {
+			if (compare(np3,np1,order) < 0) {
+				/* swap links */
+				if (np0 == np)
+					np0->child = np3;
+				else
+					np0->peer = np3;
+				np2->peer = np3->peer;
+				np3->peer = np1;
+
+				/* adjust pointers */
+				np1 = np3;
+				np3 = np2;
+			}
+			np2 = np3;
+		}
+		np0 = np1;
+	}
+}
+
+/*
+ * Draws a node in the given rectangle, and all of its children
+ * to the "right" of the given rectangle.
+ */
+drawnode(nodep, rect)
+struct node *nodep;	/* node whose children we should draw */
+struct rect rect;	/* rectangle to draw all children in */
+{
+	struct rect subrect;
+
+	/*printf("Drawing \"%s\" %d\n", nodep->name, nodep->size);*/
+
+	xdrawrect(nodep->name, nodep->size,
+		rect.left,rect.top,rect.width,rect.height);
+
+	/* save current screen rectangle for lookups */
+	nodep->rect.left = rect.left;
+	nodep->rect.top = rect.top;
+	nodep->rect.width = rect.width;
+	nodep->rect.height = rect.height;
+
+	/* draw children in subrectangle */
+	subrect.left = rect.left+rect.width;
+	subrect.top = rect.top;
+	subrect.width = rect.width;
+	subrect.height = rect.height;
+	drawchildren(nodep, subrect);
+}
+
+/*
+ * Draws all children of a node within the given rectangle.
+ * Recurses on children.
+ */
+drawchildren(nodep, rect)
+struct node *nodep;	/* node whose children we should draw */
+struct rect rect;	/* rectangle to draw all children in */
+{
+	int	totalsize;
+	int	totalheight;
+	struct	node	*np;
+	double	fractsize;
+	int	height;
+	int	top;
+
+	/*printf("Drawing children of \"%s\", %d\n", nodep->name, nodep->size);*/
+	/*printf("In [%d,%d,%d,%d]\n", rect.left,rect.top,rect.width,rect.height);*/
+
+	top = rect.top;
+	totalheight = rect.height;
+	totalsize = nodep->size;
+	if (totalsize == 0) {
+		/* total the sizes of the children */
+		totalsize = 0;
+		for (np = nodep->child; np != NULL; np = np->peer)
+			totalsize += np->size;
+		nodep->size = totalsize;
+	}
+
+	/* for each child */
+	for (np = nodep->child; np != NULL; np = np->peer) {
+		fractsize = np->size / (double)totalsize;
+		height = fractsize * totalheight + 0.5;
+		if (height > 1) {
+			struct rect subrect;
+			/*printf("%s, drawrect[%d,%d,%d,%d]\n", np->name,
+				rect.left,top,rect.width,height);*/
+			xdrawrect(np->name, np->size,
+				rect.left,top,rect.width,height);
+
+			/* save current screen rectangle for lookups */
+			np->rect.left = rect.left;
+			np->rect.top = top;
+			np->rect.width = rect.width;
+			np->rect.height = height;
+
+			/* draw children in subrectangle */
+			subrect.left = rect.left+rect.width;
+			subrect.top = top;
+			subrect.width = rect.width;
+			subrect.height = height;
+			drawchildren(np, subrect);
+
+			top += height;
+		}
+	}
+}
+
+/*
+ * clear the rectangle information of a given node
+ * and all of its decendents
+ */
+void
+clearrects(nodep)
+struct	node *nodep;
+{
+	struct	node	*np;
+
+	if (nodep == NODE_NULL)
+		return;
+
+	nodep->rect.left = 0;
+	nodep->rect.top = 0;
+	nodep->rect.width = 0;
+	nodep->rect.height = 0;
+
+	/* for each child */
+	for (np = nodep->child; np != NULL; np = np->peer) {
+		clearrects(np);
+	}
+}
+
+pwd()
+{
+	struct node *np;
+	struct node *stack[MAXDEPTH];
+	int num = 0;
+	struct node *rootp;
+	char path[MAXPATH];
+
+	rootp = &top;
+	if (numchildren(rootp) == 1)
+		rootp = rootp->child;
+
+	np = topp;
+	while (np != NODE_NULL) {
+		stack[num++] = np;
+		if (np == rootp)
+			break;
+		np = np->parent;
+	}
+
+	path[0] = '\0';
+	while (--num >= 0) {
+		strcat(path,stack[num]->name);
+		if (num != 0)
+			strcat(path,"/");
+	}
+	printf("%s %d (%.2f%%)\n", path, topp->size,
+		100.0*topp->size/rootp->size);
+}
+
+char *
+strdup(s)
+char *s;
+{
+	int	n;
+	char	*cp;
+
+	n = strlen(s);
+	cp = malloc(n+1);
+	strcpy(cp,s);
+
+	return	cp;
+}
+
+/**************** External Entry Points ****************/
+
+int
+press(x,y)
+int x, y;
+{
+	struct node *np;
+
+	/*printf("press(%d,%d)...\n",x,y);*/
+	np = findnode(&top,x,y);
+	/*printf("Found \"%s\"\n", np?np->name:"(null)");*/
+	if (np == topp) {
+		/* already top, go up if possible */
+		if (np->parent != &top || numchildren(&top) != 1)
+			np = np->parent;
+		/*printf("Already top, parent = \"%s\"\n", np?np->name:"(null)");*/
+	}
+	if (np != NODE_NULL) {
+		topp = np;
+		xrepaint();
+	}
+}
+
+int
+reset()
+{
+	topp = &top;
+	if (numchildren(topp) == 1)
+		topp = topp->child;
+	xrepaint();
+}
+
+int
+repaint(width,height)
+int width, height;
+{
+	struct	rect rect;
+
+	/* define a rectangle to draw into */
+	rect.top = 0;
+	rect.left = 0;
+	rect.width = width/ncols;
+	rect.height = height;
+
+	clearrects(&top);	/* clear current rectangle info */
+	drawnode(topp,rect);	/* draw tree into given rectangle */
+#if 0
+	pwd();			/* display current path */
+#endif
+}
+
+int
+setorder(op)
+char *op;
+{
+	if (strcmp(op, "size") == 0) {
+		order = ORD_SIZE;
+	} else if (strcmp(op, "rsize") == 0) {
+		order = ORD_RSIZE;
+	} else if (strcmp(op, "alpha") == 0) {
+		order = ORD_ALPHA;
+	} else if (strcmp(op, "ralpha") == 0) {
+		order = ORD_RALPHA;
+	} else if (strcmp(op, "first") == 0) {
+		order = ORD_FIRST;
+	} else if (strcmp(op, "last") == 0) {
+		order = ORD_LAST;
+	} else if (strcmp(op, "reverse") == 0) {
+		switch (order) {
+		case ORD_ALPHA:
+			order = ORD_RALPHA;
+			break;
+		case ORD_RALPHA:
+			order = ORD_ALPHA;
+			break;
+		case ORD_SIZE:
+			order = ORD_RSIZE;
+			break;
+		case ORD_RSIZE:
+			order = ORD_SIZE;
+			break;
+		case ORD_FIRST:
+			order = ORD_LAST;
+			break;
+		case ORD_LAST:
+			order = ORD_FIRST;
+			break;
+		}
+	} else {
+		fprintf(stderr, "xdu: bad order \"%s\"\n", op);
+	}
+}
+
+int
+reorder(op)
+char *op;	/* order name */
+{
+	setorder(op);
+	sorttree(topp, order);
+	xrepaint();
+}
+
+int
+nodeinfo()
+{
+	struct node *np;
+
+	/* display current root path */
+	pwd();
+
+	/* display each child of this node */
+	for (np = topp->child; np != NULL; np = np->peer) {
+		printf("%-8d %s\n", np->size, np->name);
+	}
+}
+
+int
+helpinfo()
+{
+	fprintf(stdout, "\n\
+XDU Version %s - Keyboard Commands\n\
+  a  sort alphabetically\n\
+  n  sort numerically (largest first)\n\
+  f  sort first-in-first-out\n\
+  l  sort last-in-first-out\n\
+  r  reverse sort\n\
+  /  goto the root\n\
+  q  quit (also Escape)\n\
+  i  info to standard out\n\
+0-9  set number of columns (0=10)\n\
+", XDU_VERSION);
+}
diff --git a/xdu.man b/xdu.man
new file mode 100644
index 0000000..1f5da2e
--- /dev/null
+++ b/xdu.man
@@ -0,0 +1,204 @@
+.TH XDU 1 X11
+.SH NAME
+xdu \- display the output of "du" in an X window
+.SH SYNOPSIS
+.B du \|| xdu [options]
+.SH DESCRIPTION
+.I Xdu
+is a program for displaying a graphical tree of disk space
+utilization as reported by the UNIX utility "du".  The
+user can navigate through the tree structure and change
+the order of the displayed information.  The window
+is divided up into several columns, each of which is one level
+deeper in the directory hierarchy (from left to right).  Boxes
+are drawn for each directory.  The amount of vertical space
+occupied by each box is directly proportional to the amount of
+disk space consumed by it and all of its children.  The name of
+each directory and the amount of data are displayed provided
+that there is enough space within its box.  Any space at the
+"bottom" of a box not covered by its children to the right
+represents the space consumed by files \fIin\fR that directory
+(as opposed to space from its children).
+.PP
+There are several command line options available.  Equivalent
+resources (if any) are shown with each option.
+.TP
+.B \+s
+(.showsize: true)
+display sizes (the default).
+.TP
+.B \-s
+(.showsize: false)
+don't display sizes.
+.TP
+.BI \-c " num"
+display \fInum\fR columns.
+.TP
+.B \-a
+(.order: alpha)
+sorts display alphabetically.
+.TP
+.B \-ra
+(.order: ralpha)
+sorts display reverse alphabetically.
+.TP
+.B \-n
+(.order: size)
+sorts display numerically (largest first).
+.TP
+.B \-rn
+(.order: rsize)
+sorts display reverse numerically (smallest first).
+.TP
+.BI \-fg " color"
+(.foregound)
+determines the color of the text and lines.
+.TP
+.BI \-bg " color"
+(.background)
+determines the color of the background.
+.TP
+.B \-rv
+reverse video (for monochrome displays)
+.PP
+In addition to these the usual toolkit options such as
+\-rv, \-font, \-display, \-geometry, are supported.
+.SH MOUSE ACTIONS
+The user can move up or down the tree by clicking the left mouse on
+a directory box.  If the left most box is selected, the display will
+move up one level (assuming you are not already at the root).  If any
+other box is selected, it will be placed against the left edge of the
+window and the display will be rescaled appropriately.
+At any time the middle mouse will bring you back to the root.
+Clicking the right mouse will exit the program.
+.SH KEYSTROKES
+Several keystroke commands are supported.  Note that all sorting happens
+from the current root node down, so it is possible to sort one subtree
+differently than another by clicking into it, doing a sort, and going
+back up to the parent.
+.TP
+.B 1-9,0
+sets the number of columns in the display (0 = 10).
+.TP
+.B a
+alphabetical sort.
+.TP
+.B n
+numerical sort (largest first).
+.TP
+.B f
+first-in-first-out sort (this is the order the
+data was read into the program).
+.TP
+.B l
+last-in-first-out sort.
+.TP
+.B r
+reverse sense of sort.
+.TP
+.B s
+toggle size display.
+.TP
+.B h
+display a popup help window.
+.TP
+.B i
+display information about the current root node to standard out.
+The first line shows the path within the tree, the total size
+from this node on down, and the precentage that total represents
+of all of the data given to \fIxdu\fR.  Subsequent lines show the
+size and name information for all children of this node in the
+order they are currently sorted in.  This allows tiny directories
+to be seen that otherwise could not be labled on the display,
+and also allows for cutting and pasting of the information.
+.TP
+.B /
+goto the root.
+.TP
+.B q
+(or Escape)
+exit the program.
+.SH ACTIONS
+All mouse and keyboard events trigger actions so they can all
+be rebound by translations to suit the individual.  The action
+functions are:
+.TP
+.B reset()
+goes to the root node.
+.TP
+.B goto()
+moves down into the directory currently under the mouse
+(and is thus probably only useful bound to a mouse button).
+.TP
+.B quit()
+exits the program.
+.TP
+.B reorder(type)
+sorts the display from the current root node down according to
+one of: alpha, ralpha (reverse alphabetical), size (largest to
+smallest), rsize (smallest to largest), first (as originally read
+in), last (opposite of original data), reverse (reverse whatever
+sort mode is currently selected).
+.TP
+.B size()
+toggles size display on/off
+.TP
+.B ncol(num)
+sets the number of columns to num.
+.TP
+.B info()
+displays directory information as described in the KEYBOARD section.
+.TP
+.B help()
+displays a popup help window.
+.PP
+As an example of rebinding these, you could put the following
+in your resources file:
+.sp
+.nf
+XDu*translations: #override\\n\\
+          <Key>X: reorder(reverse)\\n\\
+      <Btn3Down>: info()
+.fi
+.sp
+to make the 'x' key reverse the current sort order and the right
+mouse button print the directory information message.
+.SH RESOURCES
+The following resources are supported with some sample values.
+.PP
+.nf
+XDu*foreground:		yellow
+XDu*background:		blue4
+XDu*window.width:	600
+XDu*window.height:	480
+XDu*help*foreground:	green
+XDu*help*background:	red4
+XDu.ncol:		5
+XDu.font:		-*-helvetica-bold-r-normal--14-*
+XDu.showsize:		False
+XDu.order:		size
+.fi
+.SH EXAMPLE
+.I
+cd /usr/src
+.br
+.I
+du > /tmp/du.out
+.br
+.I
+xdu \-n /tmp/du.out
+.SH BUGS
+On some machines keyboard input may not be accepted by xdu until
+the mouse is moved out of and back into the window.  I have been
+told that putting the following in your resources file may fix
+this.
+.sp
+.nf
+<window manager name>.FocusLenience: True
+.fi
+.SH "SEE ALSO"
+du(1)
+.SH AUTHOR
+Phillip C. Dykstra
+.br
+<phil@arl.army.mil>
diff --git a/xwin.c b/xwin.c
new file mode 100644
index 0000000..dbecb5d
--- /dev/null
+++ b/xwin.c
@@ -0,0 +1,496 @@
+/*
+ * XDU - X Window System Interface.
+ *
+ * We hide all of the X hieroglyphics inside of this module.
+ *
+ * Phillip C. Dykstra
+ * <phil@arl.mil>
+ * 4 Sep 1991.
+ * 
+ * Copyright (c)	Phillip C. Dykstra	1991, 1993, 1994
+ * The X Consortium, and any party obtaining a copy of these files from
+ * the X Consortium, directly or indirectly, is granted, free of charge, a
+ * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
+ * nonexclusive right and license to deal in this software and
+ * documentation files (the "Software"), including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons who receive
+ * copies from any such party to do so.  This license includes without
+ * limitation a license to do the foregoing actions under any patents of
+ * the party supplying this software to the X Consortium.
+ */
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+
+#include <X11/Xaw/AsciiText.h>
+#include <X11/Xaw/AsciiSrc.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/Label.h>
+
+#include <stdio.h>
+
+#ifndef X_NOT_STDC_ENV
+#include <stdlib.h>	/* for exit() */
+#endif
+
+/* IMPORTS: routines that this module vectors out to */
+extern int press();
+extern int reset();
+extern int repaint();
+extern int reorder();
+extern int setorder();
+extern int nodeinfo();
+extern int helpinfo();
+extern int ncols;
+
+/* EXPORTS: routines that this module exports outside */
+extern int xsetup();
+extern int xmainloop();
+extern int xclear();
+extern int xrepaint();
+extern int xrepaint_noclear();
+extern int xdrawrect();
+
+/* internal routines */
+static void help_popup();
+static void help_popdown();
+
+static String fallback_resources[] = {
+"*window.width:		600",
+"*window.height:	480",
+"*help.width:		500",
+"*help.height:		330",
+"*order:		first",
+NULL
+};
+
+/* Application Resources */
+typedef struct {
+	Pixel	foreground;
+	Pixel	background;
+	XFontStruct *font;
+	int	ncol;
+	Boolean	showsize;
+	char	*order;
+} res_data, *res_data_ptr;
+static res_data res;
+
+static XtResource application_resources[] = {
+	{ XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
+		XtOffset(res_data_ptr,foreground), XtRString, XtDefaultForeground},
+	{ XtNbackground, XtCBackground, XtRPixel, sizeof(Pixel),
+		XtOffset(res_data_ptr,background), XtRString, XtDefaultBackground},
+	{ XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
+		XtOffset(res_data_ptr,font), XtRString, XtDefaultFont },
+	{ "ncol", "Ncol", XtRInt, sizeof(int),
+		XtOffset(res_data_ptr,ncol), XtRString, "5"},
+	{ "showsize", "ShowSize", XtRBoolean, sizeof(Boolean),
+		XtOffset(res_data_ptr,showsize), XtRString, "True"},
+	{ "order", "Order", XtRString, sizeof(String),
+		XtOffset(res_data_ptr,order), XtRString, "first"}
+};
+
+/* Command Line Options */
+static XrmOptionDescRec options[] = {
+	{"-c",		"*ncol",	XrmoptionSepArg,	NULL},
+	{"+s",		"*showsize",	XrmoptionNoArg,		"True"},
+	{"-s",		"*showsize",	XrmoptionNoArg,		"False"},
+	{"-n",		"*order",	XrmoptionNoArg,		"size"},
+	{"-rn",		"*order",	XrmoptionNoArg,		"rsize"},
+	{"-a",		"*order",	XrmoptionNoArg,		"alpha"},
+	{"-ra",		"*order",	XrmoptionNoArg,		"ralpha"}
+};
+
+/* action routines */
+static void a_goto();
+static void a_reset();
+static void a_quit();
+static void a_reorder();
+static void a_size();
+static void a_ncol();
+static void a_info();
+static void a_help();
+static void a_removehelp();
+
+static XtActionsRec actionsTable[] = {
+	{ "reset",	a_reset },
+	{ "goto",	a_goto },
+	{ "quit",	a_quit },
+	{ "reorder",	a_reorder },
+	{ "size",	a_size },
+	{ "ncol",	a_ncol },
+	{ "info",	a_info },
+	{ "help",	a_help },
+	{ "RemoveHelp",	a_removehelp }
+};
+
+static char defaultTranslations[] = "\
+<Key>Q:	quit()\n\
+<Key>Escape: quit()\n\
+:<Key>/: reset()\n\
+<Key>S:	size()\n\
+<Key>I:	info()\n\
+<Key>H: help()\n\
+<Key>Help: help()\n\
+:<Key>?: help()\n\
+<Key>A:	reorder(alpha)\n\
+<Key>N:	reorder(size)\n\
+<Key>F:	reorder(first)\n\
+<Key>L:	reorder(last)\n\
+<Key>R:	reorder(reverse)\n\
+<Key>1:	ncol(1)\n\
+<Key>2:	ncol(2)\n\
+<Key>3:	ncol(3)\n\
+<Key>4:	ncol(4)\n\
+<Key>5:	ncol(5)\n\
+<Key>6:	ncol(6)\n\
+<Key>7:	ncol(7)\n\
+<Key>8:	ncol(8)\n\
+<Key>9:	ncol(9)\n\
+<Key>0:	ncol(10)\n\
+<Btn1Down>: goto()\n\
+<Btn2Down>: reset()\n\
+<Btn3Down>: quit()\n\
+";
+
+/*  action routines  */
+
+static void a_quit(w, event, params, num_params)
+Widget w;
+XEvent *event;
+String *params;
+Cardinal *num_params;
+{
+	XtDestroyApplicationContext(XtWidgetToApplicationContext(w));
+	exit(0);
+}
+
+static void a_goto(w, event, params, num_params)
+Widget w;
+XEvent *event;
+String *params;
+Cardinal *num_params;
+{
+	press(event->xbutton.x, event->xbutton.y);
+}
+
+static void a_reset(w, event, params, num_params)
+Widget w;
+XEvent *event;
+String *params;
+Cardinal *num_params;
+{
+	reset();
+}
+
+static void a_reorder(w, event, params, num_params)
+Widget w;
+XEvent *event;
+String *params;
+Cardinal *num_params;
+{
+	if (*num_params != 1) {
+		fprintf(stderr, "xdu: bad number of params to reorder action\n");
+	} else {
+		reorder(*params);
+	}
+}
+
+static void a_size(w, event, params, num_params)
+Widget w;
+XEvent *event;
+String *params;
+Cardinal *num_params;
+{
+	if (res.showsize)
+		res.showsize = 0;
+	else
+		res.showsize = 1;
+	xrepaint();
+}
+
+static void a_ncol(w, event, params, num_params)
+Widget w;
+XEvent *event;
+String *params;
+Cardinal *num_params;
+{
+	int	n;
+
+	if (*num_params != 1) {
+		fprintf(stderr, "xdu: bad number of params to ncol action\n");
+		return;
+	}
+	n = atoi(*params);
+	if (n < 1 || n > 1000) {
+		fprintf(stderr, "xdu: bad value to ncol action\n");
+		return;
+	}
+	ncols = res.ncol = n;
+	xrepaint();
+}
+
+static void a_info(w, event, params, num_params)
+Widget w;
+XEvent *event;
+String *params;
+Cardinal *num_params;
+{
+	nodeinfo();
+}
+
+static void a_help(w, event, params, num_params)
+Widget w;
+XEvent *event;
+String *params;
+Cardinal *num_params;
+{
+	/*helpinfo();*/
+	help_popup();
+}
+
+static void a_removehelp(w, event, params, num_params)
+Widget w;
+XEvent *event;
+String *params;
+Cardinal *num_params;
+{
+	help_popdown();
+}
+
+/* callback routines */
+
+static void c_resize(w, data, event, continue_to_dispatch)
+Widget w;
+XtPointer data;
+XEvent *event;
+Boolean *continue_to_dispatch;
+{
+	/*printf("Resize\n");*/
+	xrepaint();
+}
+
+static void c_repaint(w, data, event, continue_to_dispatch)
+Widget w;
+XtPointer data;
+XEvent *event;
+Boolean *continue_to_dispatch;
+{
+	/*printf("Expose\n");*/
+	xrepaint_noclear();
+}
+
+/* X Window related variables */
+static Cursor WorkingCursor;
+static Display *dpy;
+static int screen;
+static Visual *vis;
+static Window win;
+static GC gc;
+static GC cleargc;
+static XtAppContext app_con;
+
+Widget toplevel;
+
+/*  External Functions  */
+
+int
+xsetup(argcp, argv)
+int *argcp;
+char **argv;
+{
+	XtTranslations trans_table;
+	Widget w;
+	XGCValues gcv;
+	int n;
+	Arg args[5];
+
+	/* Create the top level Widget */
+	n = 0;
+	XtSetArg(args[n], XtNtitle, "XDU Disk Usage Display ('h' for help)\n"); n++;
+	toplevel = XtAppInitialize(&app_con, "XDu",
+			options, XtNumber(options),
+			argcp, argv,
+			fallback_resources, args, n);
+
+	XtGetApplicationResources(toplevel, (XtPointer)&res,
+		application_resources, XtNumber(application_resources),
+		NULL, 0 );
+
+	XtAppAddActions(app_con, actionsTable, XtNumber(actionsTable));
+	trans_table = XtParseTranslationTable(defaultTranslations);
+
+	/* Create a simple Label class widget to draw in */
+	n = 0;
+	XtSetArg(args[n], XtNlabel, ""); n++;
+	w = XtCreateManagedWidget("window", labelWidgetClass, toplevel,
+		args, n);
+
+	/* events */
+	XtAddEventHandler(w, ExposureMask, False, c_repaint, NULL);
+	XtAddEventHandler(w, StructureNotifyMask, False, c_resize, NULL);
+	XtAugmentTranslations(w, trans_table);
+
+	XtRealizeWidget(toplevel);
+
+	/* We need these for the raw Xlib calls */
+	win = XtWindow(w);
+	dpy = XtDisplay(w);
+	screen = DefaultScreen(dpy);
+	vis = DefaultVisual(dpy,screen);
+
+	gcv.foreground = res.foreground;
+	gcv.background = res.background;
+	gcv.font = res.font->fid;
+	gc = XCreateGC(dpy, win, (GCFont|GCForeground|GCBackground), &gcv);
+
+	setorder(res.order);
+	ncols = res.ncol;
+}
+
+xmainloop()
+{
+	XtAppMainLoop(app_con);
+	return(0);
+}
+
+xclear()
+{
+	XClearWindow(dpy, win);
+}
+
+xrepaint()
+{
+	XWindowAttributes xwa;
+
+	XClearWindow(dpy, win);
+	XGetWindowAttributes(dpy, win, &xwa);
+	repaint(xwa.width, xwa.height);
+}
+
+xrepaint_noclear()
+{
+	XWindowAttributes xwa;
+
+	XGetWindowAttributes(dpy, win, &xwa);
+	repaint(xwa.width, xwa.height);
+}
+
+xdrawrect(name, size, x, y, width, height)
+char *name;
+int size;
+int x, y, width, height;
+{
+	int	textx, texty;
+	char	label[1024];
+	XCharStruct overall;
+	int	ascent, descent, direction;
+	int	cheight;
+
+	/*printf("draw(%d,%d,%d,%d)\n", x, y, width, height );*/
+	XDrawRectangle(dpy, win, gc, x, y, width, height);
+
+	if (res.showsize) {
+		sprintf(label,"%s (%d)", name, size);
+		name = label;
+	}
+
+	XTextExtents(res.font, name, strlen(name), &direction,
+		&ascent, &descent, &overall);
+	cheight = overall.ascent + overall.descent;
+	if (height < (cheight + 2))
+		return;
+
+	/* print label */
+	textx = x + 4;
+	texty = y + height/2.0 + (overall.ascent - overall.descent)/2.0 + 1.5;
+	XDrawString(dpy, win, gc, textx, texty, name, strlen(name));
+}
+
+static Widget popup;
+
+static void
+help_popup()
+{
+	Widget form, text, src;
+	Arg args[15];
+	int n;
+	Atom wm_delete_window;
+	XtTranslations trans_table;
+
+	if (popup != NULL) {
+		XtPopup(popup, XtGrabNone);
+		return;
+	}
+
+	/* popup shell */
+	n = 0;
+	XtSetArg(args[n], XtNtitle, "XDU Help"); n++;
+	popup = XtCreatePopupShell("helpPopup", transientShellWidgetClass,
+		toplevel, args, n);
+
+	/* form container */
+	n = 0;
+	XtSetArg(args[n], XtNborderWidth, 0); n++;
+	XtSetArg(args[n], XtNdefaultDistance, 0); n++;
+	form = XtCreateManagedWidget("form", formWidgetClass,
+		popup, args, n);
+
+	/* text widget in form */
+	n = 0;
+	XtSetArg(args[n], XtNborderWidth, 0); n++;
+	XtSetArg(args[n], XtNresize, XawtextResizeBoth); n++;
+	/* fallback resources weren't working here on the Sun */
+	XtSetArg(args[n], XtNwidth, 500); n++;
+	XtSetArg(args[n], XtNheight, 330); n++;
+	text = XtCreateManagedWidget("help", asciiTextWidgetClass,
+		form, args, n);
+
+	/* create text source */
+	n = 0;
+	XtSetArg(args[n], XtNtype, XawAsciiString); n++;
+	XtSetArg(args[n], XtNeditType, XawtextRead); n++;
+	XtSetArg(args[n], XtNstring, "\
+XDU Version 3.0 - Phil Dykstra <phil@arl.mil>\n\
+\n\
+Keyboard Commands\n\
+  a  sort alphabetically\n\
+  n  sort numerically (largest first)\n\
+  f  sort first-in-first-out\n\
+  l  sort last-in-first-out\n\
+  r  reverse sort\n\
+  s  toggle size display\n\
+  /  goto the root\n\
+  i  node info to standard out\n\
+  h  this help message\n\
+  q  quit (also Escape)\n\
+0-9  set number of columns (0=10)\n\
+\n\
+Mouse Commands\n\
+  Left   Goto node (goto parent if leftmost box)\n\
+  Middle Back to root\n\
+  Right  Quit\n\
+"); n++;
+	src = XtCreateWidget("textSource", asciiSrcObjectClass,
+		text, args, n);
+	/* set text source */
+	XawTextSetSource(text, src, 0);
+
+	XtRealizeWidget(popup);
+	XtPopup(popup, XtGrabNone);
+
+	trans_table = XtParseTranslationTable("<Key>Q: RemoveHelp()");
+	XtAugmentTranslations(form, trans_table);
+
+	/* Set up ICCCM delete window */
+	wm_delete_window = XInternAtom(XtDisplay(popup), "WM_DELETE_WINDOW", False);
+	XtOverrideTranslations(popup, XtParseTranslationTable("<Message>WM_PROTOCOLS: RemoveHelp()"));
+	XSetWMProtocols(XtDisplay(popup), XtWindow(popup), &wm_delete_window, 1);
+}
+
+static void
+help_popdown()
+{
+	XtPopdown(popup);
+}