diff options
Diffstat (limited to 'xdu.c')
-rw-r--r-- | xdu.c | 770 |
1 files changed, 770 insertions, 0 deletions
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 = ⊤ +#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 = ⊤ + /* 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 = ⊤ + 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 = ⊤ + 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); +} |