home *** CD-ROM | disk | FTP | other *** search
- /* @(#) dusort.c 1.7 93/08/18 00:09:05
- *
- * disk usage sorter
- *
- * Copyright 1990-1992, Unicom Systems Development. All rights reserved.
- * See accompanying README file for terms of distribution and use.
- *
- * Edit at tabstops=4.
- *
- * This filter reads the output from "du", builds a tree representing
- * the directories scanned, sorts the nodes in descending order, and
- * traverses the tree, thus yielding a printout of disk usage sorted
- * within directories.
- */
-
- #include "config.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <ctype.h>
- #include <string.h>
-
- #define USAGE "usage: %s [-t threshold] [file]\n"
- #define INDENT 4
- #define LISTINCR 16
- #define TRUE 1
- #define FALSE 0
-
- /*
- * The disk usage information is loaded into a tree structure. Each node
- * of the tree is a (struct duentry), and corresponds to one component
- * in a pathname. If the node represents a directory, its contents (both
- * files and subdirectories) will be stored in the "child[]" list. The
- * disk usage at this entry will be saved into "size", or set to -1 if
- * no disk usage info exists at this entry. Only those entries with a
- * nonnegative "size" value will be printed in the final report.
- */
- struct duentry {
- char *name; /* pathname component at this node */
- long size; /* usage size info, or -1 if not available */
- struct duentry **child; /* directory contents */
- int ch_alloc; /* allocated size of "child" array */
- int ch_size; /* number of elements used in "child" array */
- short leading_slash; /* is there a "/" before the name component */
- };
-
- /*
- * Only save off entries whose disk usage exceeds this threshold.
- */
- long Threshold;
-
-
- struct duentry *make_entry(); /* allocate a node to be placed in the tree */
- struct duentry *find_entry(); /* search directory for an entry */
- struct duentry *attach_entry(); /* add entry as child to current directory */
- void sort_tree(); /* sort tree in descending order */
- void print_tree(); /* pre-order traversal of the tree */
- char *xstrdup();
- PTRTYPE *xmalloc();
- PTRTYPE *xrealloc();
-
-
- main(argc, argv)
- int argc;
- char *argv[];
- {
- char buf[1024], *fname, *last_field, *bufp, *s;
- int lineno, leading_slash, i;
- long size;
- struct duentry *du_root, *p, *p1;
- extern int optind;
- extern char *optarg;
-
- /*
- * Initialize.
- */
- Threshold = 0L;
- du_root = make_entry("", FALSE);
-
- /*
- * Crack command line.
- */
- while ((i = getopt(argc, argv, "t:")) != EOF) {
- switch (i) {
- case 't':
- if ((Threshold=atol(optarg)) <= 0L && strcmp(optarg, "0") != 0) {
- fprintf(stderr, "%s: bad threshold value \"%s\"\n",
- argv[0], optarg);
- exit(1);
- }
- break;
- default:
- fprintf(stderr, USAGE, argv[0]);
- exit(1);
- }
- }
-
- /*
- * Figure out where input is coming from.
- */
- switch (argc-optind) {
- case 1:
- fname = argv[optind];
- if (freopen(fname, "r", stdin) == NULL) {
- perror(fname);
- exit(1);
- }
- break;
- case 0:
- /* read from stdin */
- fname = "<stdin>";
- break;
- default:
- fprintf(stderr, USAGE, argv[0]);
- exit(1);
- }
-
- /*
- * Read in the input data. It is expected to be in a format:
- *
- * size .... pathname
- */
- lineno = 0;
- while (++lineno, fgets(buf, sizeof(buf), stdin) != NULL) {
-
- /*
- * Break the line up into fields. Save the first field as the size.
- */
- s = NULL;
- bufp = buf;
- i = 0;
- while (last_field = s, (s = strtok(bufp, " \t\n")) != NULL) {
- bufp = NULL;
- if (i++ == 0)
- size = atol(s);
- }
- if (i < 2) {
- fprintf(stderr, "%s(%d): line ignored\n", fname, lineno);
- continue;
- }
-
- /*
- * Ignore this entry if it is smaller than what we want.
- */
- if (size < Threshold)
- continue;
-
- /*
- * Break the pathname into components, and descend down
- * into the tree, creating nodes as required.
- */
- leading_slash = (*last_field == '/');
- p = du_root;
- while ((s = strtok(last_field, "/")) != NULL) {
- if ((p1 = find_entry(p, s)) == NULL)
- p1 = attach_entry(p, make_entry(s, leading_slash));
- last_field = NULL;
- leading_slash = TRUE;
- p = p1;
- }
-
- /*
- * Save disk usage for this entry in last component of the path.
- */
- p->size = size;
-
- }
-
- /*
- * Sort and traverse the tree.
- */
- sort_tree(du_root);
- buf[0] = '\0';
- print_tree(du_root, buf, buf, 0);
-
- exit(0);
- /*NOTREACHED*/
- }
-
-
- /*
- * Allocate a new node for the tree.
- */
- struct duentry *make_entry(name, leading_slash)
- register char *name;
- int leading_slash;
- {
- register struct duentry *p;
- p = (struct duentry *) xmalloc(sizeof(struct duentry));
- p->name = xstrdup(name);
- p->size = -1L;
- p->child = (struct duentry **) NULL;
- p->ch_alloc = 0;
- p->ch_size = 0;
- p->leading_slash = (short) leading_slash;
- return p;
- }
-
-
- /*
- * Search a directory entry for a member entry and return a pointer to
- * the located entry. If not found, add this member to the directory.
- */
- struct duentry *find_entry(p, name)
- register struct duentry *p;
- register char *name;
- {
- register int i;
-
- for (i = 0 ; i < p->ch_size ; ++i) {
- if (strcmp(name, p->child[i]->name) == 0)
- return p->child[i];
- }
- return (struct duentry *) NULL;
- }
-
-
- /*
- * Add a new child at the current node.
- */
- struct duentry *attach_entry(p, pchild)
- register struct duentry *p, *pchild;
- {
- if (p->ch_size >= p->ch_alloc) {
- p->ch_alloc += LISTINCR;
- p->child = (struct duentry **) xrealloc((PTRTYPE *)p->child,
- p->ch_alloc*sizeof(struct duentry *));
- }
- p->child[p->ch_size++] = pchild;
- return pchild;
- }
-
-
- /*
- * Recursively sort the tree so that the nodes in "child[]" are in
- * descending order by disk usage. This is done as a selection sort.
- */
- void sort_tree(p)
- struct duentry *p;
- {
- int i, j, m;
- long msize;
- struct duentry *t;
-
- /*
- * Selection sort.
- */
- for (i = 0 ; i < p->ch_size ; ++i) {
- /*
- * INVARIANT: child[0:i-1] is sorted in descending order.
- * child[x] > child[y] for all x < y && x < i.
- */
- m = i;
- msize = p->child[i]->size;
- for (j = i+1 ; j < p->ch_size ; ++j) {
- /*
- * INVARIANT: child[m] is the maximum in the range child[i:j-1]
- */
- if (p->child[j]->size > msize) {
- m = j;
- msize = p->child[j]->size;
- }
- }
- /*
- * RESULT: child[m] is the maximum in the range [i:ch_size-1]
- */
- if (m != i) {
- t = p->child[m];
- p->child[m] = p->child[i];
- p->child[i] = t;
- }
- /*
- * RESULT: child[0:i] is sorted,
- * child[x] > child[y] for all x < y && x < i+1.
- */
- sort_tree(p->child[i]);
- }
- }
-
-
- /*
- * Perform a preorder traversal of the tree to get a sorted report.
- */
- void print_tree(p, buf, bufend, lvl)
- struct duentry *p;
- char *buf, *bufend;
- int lvl;
- {
- int i;
- char *s;
-
- /*
- * Append this entry to the pathname.
- */
- if (p->leading_slash)
- *bufend++ = '/';
- for (s = p->name ; *s != '\0' ; )
- *bufend++ = *s++;
- *bufend = '\0';
-
- /*
- * If there is a size at this entry then display it.
- */
- if (p->size >= 0) {
- printf("%ld\t%*s%s\n", p->size, INDENT*lvl, "",
- (buf[0] == '\0' ? "/" : buf));
- ++lvl;
- }
-
- /*
- * Recurse into the children of this entry.
- */
- for (i = 0 ; i < p->ch_size ; ++i)
- print_tree(p->child[i], buf, bufend, lvl);
- }
-
-
- static char malloc_error[] = "error - out of memory [malloc failed]\n";
-
- char *xstrdup(s)
- register char *s;
- {
- register char *s1;
- if ((s1 = (char *) malloc((unsigned)strlen(s)+1)) == NULL) {
- fputs(malloc_error, stderr);
- exit(1);
- }
- return strcpy(s1, s);
- }
-
- PTRTYPE *xmalloc(n)
- register unsigned n;
- {
- register PTRTYPE *s;
- if ((s = malloc(n)) == NULL) {
- fputs(malloc_error, stderr);
- exit(1);
- }
- return s;
- }
-
- PTRTYPE *xrealloc(s, n)
- register PTRTYPE *s;
- register unsigned n;
- {
- if ((s = (s == NULL ? malloc(n) : realloc(s, n))) == NULL) {
- fputs(malloc_error, stderr);
- exit(1);
- }
- return s;
- }
-
-