/* $Id: nodeop.c,v 3.1 2005/02/19 08:29:53 mac Exp $
 *
 * nodeop.c
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and that
 * both that copyright notice and this permission notice appear in
 * supporting documentation, and that the authors name not be used in
 * advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.  The author makes no
 * representations about the suitability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 */
#include "du2ps.h"
unsigned dhue;

Node *top;
int (*cmp)();

static void
drawrect(Node *nodep, double y, double height, int depth,
		unsigned h, unsigned s, unsigned b)
{
	printf("%d (%s \\(%d\\)) %.2f %.2f %f %f %f\n",
        depth, nodep->name, nodep->size, height, y,
	(double) h / 0x8000 , (double) s, (double) b);
}

/*
 * create a new node with the given name and set other initial values
 */
static Node *
makenode(char *name)
{
	Node *nodep;

	if (NULL == (nodep = (Node *) malloc(
		(unsigned) (sizeof(Node) + strlen(name) + 1)))) {
		fprintf(stderr, "malloc error\n");
		exit (ENOMEM);
	}
	nodep->child = nodep->peer = NODE_NULL;
	nodep->nchild = nodep->size = 0;
	nodep->name = (char *) &nodep[1];
	strcpy(nodep->name, name);
	return nodep;
}


static void
addtree(Node *nodep, char **path, int size)
{
	Node *child;

	/* check all children for a match */
	for(child = nodep->child; NODE_NULL != child; child = child->peer){
		if(!strcmp(path[0], child->name)) break;
	}

	if (NODE_NULL == child) {
		child = nodep->child;
		nodep->child = makenode(path[0]);
		nodep->child->peer = child;
		child = nodep->child;
		nodep->nchild++;
	}

	if (NULL == path[1]) {
		child->size = size;
	}
	else {
		addtree(child, &path[1], size);
	}
	return;
}

int
cmp_alph(Node **p, Node **q)
{
	return strcmp((*p)->name, (*q)->name);
}

int
cmp_size(Node **p, Node **q)
{
	return ((*q)->size - (*p)->size);
}

static void
sorttree(Node *nodep)
{
	Node *child;
	Node **nodtbl;
	int i, ind;

	if (NODE_NULL == nodep->child) return;

	if (NULL == (nodtbl = (Node **) malloc(
		sizeof(Node *) * nodep->nchild))) {
		fprintf(stderr, "malloc error\n");
		exit (ENOMEM);
	}

	for(child = nodep->child, ind = 0;
		NODE_NULL != child; child = child->peer, ind++){
		nodtbl[ind] = child;
		if (NODE_NULL != child->child) {
			sorttree(child);
		}
	}

	if (2 <= nodep->nchild) {
		qsort((char *) nodtbl, nodep->nchild, sizeof(Node *), cmp);

		nodep->child = nodtbl[0];
		for (i = 1; i < nodep->nchild; ++i) {
			nodtbl[i - 1]->peer = nodtbl[i];
		}
		nodtbl[i - 1]->peer = NODE_NULL;
	}

	free(nodtbl);

	return;
}


static void
drawchildren(Node *nodep, double y, double h, int depth)
             /* node whose children we should draw */
            
          
{
	Node *np;
	double height;
	unsigned hue;
	int s, b;

    /* for each child */
	hue = 0;
	s = b = 1;
	for(np = nodep->child; NODE_NULL != np; np = np->peer){
		if (nodep->size == 0) {
			return;
		}
		height = h * np->size / nodep->size;
		if (dhue != 0) {
			drawrect(np, y, height, depth, hue, s, b);
			hue += dhue;
			hue %= 0x8000;
		}
		else {
			drawrect(np, y, height, depth, 0, 0, 1);
		}
		/* draw children in subrectangle */
		drawchildren(np, y, height, depth + 1);
		y -= height;
    }
}

/*
 * reads du output,
 * builds hierarchy structure,
 * returns the top directory name.
 */
#define MAXLL MAXPATH+80
char *
parse(void)
{
	char buf[MAXLL], name[MAXPATH+2], *n;
	char *path[MAXDEPTH]; /* break up path into this list */
	int depth, size;
	int i;

	top = makenode("..");
	while(fgets(buf, MAXLL, stdin)  != 0){
		sscanf(buf, "%d %s\n", &size, n = name);
		if('/' == *n) {
			if ( strlen(name) > MAXPATH ) {
				fprintf(stderr, "too long path\n");
				exit(ENAMETOOLONG);
			}
			for(i = strlen(name)+1; i >= 0; i--) {
				name[i+1] = name[i];
			}
			name[0] = '.';
		}
		if ( name[0] == '.' && name[1] == '/' && name[2] == EOS ) {
			name[1] = EOS;
		}
		path[depth = 0] = n;
			
		for(; *n != EOS; n++){
			if('/' == *n){
				*n = EOS;
				path[++depth] = n + 1;
				if(depth > MAXDEPTH) break;
			}
		}
		path[++depth] = NULL;
		addtree(top, path, size);
	}
	sorttree(top);

	/*
	 * descend hierarchy until the number of children is not 1.
	 * if the directory size is 0, then total the sizes of the children.
	 */
	for(n = name; 0 == top->size;){
		Node *np;

		if (1 == top->nchild) {
			top = top->child;
			while (EOS != *n) n++;
			*n = '/';
		}
		else if(1 < top->nchild){
			for(np = top->child; NODE_NULL != np; np = np->peer){
				top->size = top->size + np->size;
			}
			break;
		}
		else break;
	}
	*n = 0;
	return strdup(name);
}

void
drawnode(double canvas_height)
{
	/* draw the top directory */
	drawrect(top, canvas_height, canvas_height, 0, 0, 0, 1);
	/* draw children recursively */ 
	drawchildren(top, canvas_height, canvas_height, 1);
}


syntax highlighted by Code2HTML, v. 0.9.1