Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
xdu/xdu.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
1175 lines (1049 sloc)
27.4 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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. | |
* | |
* Slightly modifications Wed Apr 9 09:16:16 MET DST 1997 by | |
* Peter Marquardt, marquardt_p@mpimg-berlin-dahlem.mpg.de | |
* | |
* added ability to print postscriptfiles Thr Oct 1 12:17 MET DST 1998 by | |
* Marius Tolzmann, tolzmann@mpimg-berlin-dahlem.mpg.de | |
* (new option -f and new key p !!!) | |
* | |
* reworked Mon Jul 10 10:55:44 CEST 2017 | |
* Peter Marquardt, marquardt_p@molgen.mpg.de | |
* - 'worksforme' Makefile | |
* - -Wall -Werror -Wextra -pedantic | |
* | |
* added ability to print svg-files Fri Jun 08 2018 01:04:45 PM CEST | |
* Niclas Hofmann, niclas@molgen.mpg.de | |
* (new key v) | |
* - 'worksforme' Makefile | |
* - -Wall -Werror -Wextra -pedantic | |
* | |
* changed mouse actions Wed Aug 15 2018 11:25:00 PM CEST | |
* Niclas Hofmann, niclas@molgen.mpg.de | |
* - middle mouse button: print path of node that had been clicked | |
* - right mouse button: reset (former middle button action) | |
* - 'worksforme' Makefile | |
* - -Wall -Werror -Wextra -pedantic | |
* | |
*/ | |
#define _GNU_SOURCE | |
#include "version.h" | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.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 */ | |
char* root_prefix = "[root]"; | |
/* | |
* 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(); | |
static void addtree(); | |
static void parse_file(); | |
static void parse_entry(); | |
void dumptree(); | |
static void clearrects(); | |
static 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 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 | |
*/ | |
static struct node* | |
makenode(char* name, long long size) | |
{ | |
struct node* np; | |
np = (struct node*)calloc(1, sizeof(struct node)); | |
np->name = 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. | |
*/ | |
static struct node* | |
findnode(struct node* treep, int x, int 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 | |
*/ | |
static int numchildren(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 * * * | |
*/ | |
static long fix_tree(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\ | |
-f Set filename for postscript print !\n\ | |
Toolkit options: -fg, -bg, -rv, -display, -geometry, etc.\n\ | |
"; | |
int main(int argc, char** argv) | |
{ | |
top.name = root_prefix; | |
top.size = -1; | |
xsetup(&argc, argv); | |
if (argc == 1) { | |
if (isatty(fileno(stdin))) { | |
fprintf(stderr, "%s", usage); | |
exit(1); | |
} else { | |
parse_file("-"); | |
} | |
} else if (argc == 2 && strcmp(argv[1], "-help") != 0) { | |
parse_file(argv[1]); | |
} else { | |
fprintf(stderr, "%s", usage); | |
exit(1); | |
} | |
top.size = fix_tree(&top); | |
/* | |
* dumptree(&top,0); | |
*/ | |
sorttree(&top, order); | |
topp = ⊤ | |
/* | |
* don't display root if only one child | |
*/ | |
if (numchildren(topp) == 1) | |
topp = topp->child; | |
xmainloop(); | |
exit(0); | |
} | |
static void parse_file(char* filename) | |
{ | |
char buf[4096]; | |
char* name; | |
int spos = 0; | |
long long 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) { | |
buf[strlen(buf) - 1] = '\0'; /* remove the trailing newline */ | |
sscanf(buf, "%lld %n", &size, &spos); | |
name = buf + spos; | |
/* | |
* printf("%d %s\n", size, name); | |
*/ | |
parse_entry(name, size); | |
} | |
fclose(fp); | |
} | |
/* | |
* bust up a path string and link it into the tree | |
*/ | |
static void parse_entry(char* name, long long 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((char*)path, sizeof(path)); | |
bzero(buf, sizeof(buf)); | |
while (*name) { | |
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. | |
*/ | |
static int compare(struct node* n1, struct node* n2, int order) | |
{ | |
switch (order) { | |
case ORD_SIZE: | |
if (n2->size > n1->size) { | |
return 1; | |
} else if (n2->size < n1->size) { | |
return -1; | |
} else { | |
return strcmp(n1->name, n2->name); | |
} | |
/* | |
* not reached | |
*/ | |
case ORD_RSIZE: | |
if (n1->size > n2->size) { | |
return 1; | |
} else if (n1->size < n2->size) { | |
return -1; | |
} else { | |
return strcmp(n1->name, n2->name); | |
} | |
/* | |
* not reached | |
*/ | |
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; | |
} | |
/* | |
* add path as a child of top - recursively | |
*/ | |
static void addtree(struct node* top, char* path[], long long 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); | |
np->parent = top; | |
np->peer = top->child; | |
top->child=np; | |
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(struct node* np, int level) | |
{ | |
int i; | |
struct node* subnp; | |
for (i = 0; i < level; i++) | |
printf(" "); | |
printf("%s %lld\n", np->name, np->size); | |
for (subnp = np->child; subnp != NULL; subnp = subnp->peer) { | |
dumptree(subnp, level + 1); | |
} | |
} | |
static void sorttree(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 all children of a node within the given rectangle. | |
* Recurses on children. | |
*/ | |
static void drawchildren(struct node* nodep, struct rect rect) | |
{ | |
long long totalsize; | |
int totalheight; | |
struct node* np; | |
double fractsize; | |
int height; | |
int top; | |
long long size; | |
/* | |
* 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; | |
size = 0; | |
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 = totalsize ? np->size / (double)totalsize : 0; | |
height = fractsize * totalheight + 0.5; | |
{ | |
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); | |
size += np->size; | |
top = rect.top + (totalsize ? (double)size / totalsize * rect.height + 0.5 : 0); | |
} | |
} | |
} | |
/* | |
* Draws a node in the given rectangle, and all of its children | |
* to the "right" of the given rectangle. | |
*/ | |
static void drawnode(struct node* nodep, struct rect rect) | |
{ | |
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); | |
} | |
/* | |
* clear the rectangle information of a given node | |
* and all of its decendents | |
*/ | |
static void clearrects(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); | |
} | |
} | |
static void pwd(void) | |
{ | |
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 %lld (%.2f%%)\n", path, topp->size, | |
100.0 * topp->size / rootp->size); | |
} | |
/**************** External Entry Points ****************/ | |
void press(int x, int 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(); | |
} | |
} | |
void printpath(int x, int y) | |
{ | |
struct node* np; | |
char* path; | |
char* tmp_path; | |
np = findnode(&top, x, y); | |
if (np == NULL) | |
return; | |
asprintf(&path, "%s", np->name); | |
while (np->parent != NULL) { | |
tmp_path = path; | |
asprintf(&path, "%s/%s", np->parent->name, path); | |
np = np->parent; | |
free(tmp_path); | |
} | |
tmp_path = path + strlen(root_prefix); | |
printf("%s\n", tmp_path); | |
free(path); | |
} | |
void reset(void) | |
{ | |
topp = ⊤ | |
if (numchildren(topp) == 1) | |
topp = topp->child; | |
xrepaint(); | |
} | |
void repaint(int width, int 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 | |
} | |
void setorder(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); | |
} | |
} | |
void reorder(char* op) | |
{ /* order name */ | |
setorder(op); | |
sorttree(topp, order); | |
xrepaint(); | |
} | |
void nodeinfo(void) | |
{ | |
struct node* np; | |
/* | |
* display current root path | |
*/ | |
pwd(); | |
/* | |
* display each child of this node | |
*/ | |
for (np = topp->child; np != NULL; np = np->peer) { | |
printf("%-12lld %s\n", np->size, np->name); | |
} | |
} | |
void helpinfo(void) | |
{ | |
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\ | |
p print to file (as postscript)\n\ | |
i info to standard out\n\ | |
0-9 set number of columns (0=10)\n\ | |
", | |
XDU_VERSION); | |
} | |
static void fprintpsstart(FILE* fp) | |
{ | |
fprintf(fp, | |
"%%!\n" | |
"%%%%BoundingBox: 26 693 85 808\n" | |
"%%%%Title: xdu output\n" | |
"%%%%Orientation: Portrait\n" | |
"%%%%Pages: 1\n" | |
"%%%%DocumentFonts: (atend)\n" | |
"%%%%EndComments\n" | |
"%%%%BeginProlog\n\n" | |
"/xdudict 2 dict def\n" | |
"xdudict begin\n\n" | |
"end\n\n" | |
"%%%%EndProlog\n" | |
"%%%%Page: 1 1\n\n" | |
"xdudict begin\n" | |
"/xdusavedpage save def\n\n" | |
"1 setmiterlimit\n" | |
"1 setlinewidth\n" | |
"0 setgray\n" | |
"72 0 mul 72 11.60 mul translate\n" | |
"72 128 div 100.000 mul 100 div dup neg scale\n" | |
"gsave\n" | |
"/xduorigctm matrix currentmatrix def\n\n"); | |
} | |
static void fprintpsend(FILE* fp) | |
{ | |
fprintf(fp, | |
"grestore\n" | |
"xdusavedpage restore\n" | |
"end\n" | |
"showpage\n" | |
"\n" | |
"%%%%Trailer\n" | |
"%%%%DocumentFonts: fixed\n" | |
"%%%%EOF\n"); | |
} | |
static void fprintpsbox(FILE* fp, int x1, int y1, int x2, int y2) | |
{ | |
fprintf(fp, | |
"%%BOX\n" | |
"0 setgray\n" | |
"gsave\n" | |
" 10 setmiterlimit\n" | |
" gsave\n" | |
" newpath\n" | |
" %d %d moveto %d %d lineto %d %d lineto %d %d lineto\n" | |
" closepath\n" | |
" stroke\n" | |
" grestore\n" | |
"grestore\n" | |
"\n", | |
x1, y1, x2 + x1, y1, x2 + x1, y2 + y1, x1, y2 + y1); | |
} | |
static void fprintpstext(FILE* fp, int x, int y, char* s) | |
{ | |
fprintf(fp, | |
"%%TEXT\n" | |
"0 setgray\n" | |
"/fixed findfont [10 0 0 -10 0 0] makefont setfont\n" | |
" gsave\n" | |
" %i %i moveto (%s) show\n" | |
" grestore\n" | |
"\n", | |
x, y, s); | |
} | |
static void savepschildren(FILE* fp, struct node* nodep, struct rect rect, int showsize) | |
{ | |
long long size, totalsize; | |
int totalheight, | |
height, | |
top; | |
struct node* np; | |
char *name="n/a", | |
label[1024]; | |
struct rect subrect; | |
top = rect.top; | |
size = 0; | |
totalheight = rect.height; | |
totalsize = nodep->size; | |
if (totalsize == 0) { | |
for (np = nodep->child; np != NULL; np = np->peer) | |
totalsize += np->size; | |
nodep->size = totalsize; | |
} | |
for (np = nodep->child; np != NULL; np = np->peer) { | |
height = (totalsize ? (np->size / (double)totalsize) : 0) * totalheight + 0.5; | |
switch (showsize) { | |
case 0: | |
name = np->name; | |
break; | |
case 1: | |
sprintf(label, "%s \\(%lldk\\)", np->name, np->size); | |
name = label; | |
break; | |
case 2: | |
sprintf(label, "%s \\(%.2fM\\)", np->name, | |
(double)np->size / (double)1024); | |
name = label; | |
break; | |
case 3: | |
sprintf(label, "%s \\(%.2fG\\)", np->name, | |
(double)np->size / (double)(1024 * 1024)); | |
name = label; | |
break; | |
case 4: | |
sprintf(label, "%s \\(%.2fT\\)", np->name, | |
(double)np->size / (double)(1024 * 1024 * 1024)); | |
name = label; | |
break; | |
default: | |
printf("arghhhhh!"); | |
break; | |
} | |
fprintpsbox(fp, rect.left, top, rect.width, height); | |
if (height > 10) | |
fprintpstext(fp, rect.left + 4, top + (height) / 2.0 + 3.0, | |
name); | |
subrect.left = rect.left + rect.width; | |
subrect.top = top; | |
subrect.width = rect.width; | |
subrect.height = height; | |
savepschildren(fp, np, subrect, showsize); | |
size += np->size; | |
top = rect.top + (int)(totalsize ? (double)size / (double)totalsize * rect.height + 0.5 : 0); | |
} | |
} | |
static void savepsnode(FILE* fp, struct node* nodep, struct rect rect, int showsize) | |
{ | |
struct rect subrect; | |
char label[1024], | |
*name; | |
switch (showsize) { | |
case 0: | |
name = nodep->name; | |
break; | |
case 1: | |
sprintf(label, "%s \\(%lldk\\)", nodep->name, nodep->size); | |
name = label; | |
break; | |
case 2: | |
sprintf(label, "%s \\(%.2fM\\)", nodep->name, | |
(double)nodep->size / (double)1024); | |
name = label; | |
break; | |
case 3: | |
sprintf(label, "%s \\(%.2fG\\)", nodep->name, | |
(double)nodep->size / (double)(1024 * 1024)); | |
name = label; | |
break; | |
case 4: | |
sprintf(label, "%s \\(%.2fT\\)", nodep->name, | |
(double)nodep->size / (double)(1024 * 1024 * 1024)); | |
name = label; | |
break; | |
default: | |
name = NULL; | |
break; | |
} | |
fprintpsbox(fp, rect.left, rect.top, rect.width, rect.height); | |
fprintpstext(fp, rect.left + 4, | |
rect.top + (rect.height - rect.top) / 2, name); | |
subrect.left = rect.left + rect.width; | |
subrect.top = rect.top; | |
subrect.width = rect.width; | |
subrect.height = rect.height; | |
savepschildren(fp, nodep, subrect, showsize); | |
} | |
void savetops(char* fname, int showsize) | |
{ | |
FILE* fp; | |
struct rect rect; | |
if ((fp = fopen(fname, "w")) != NULL) { | |
fprintpsstart(fp); | |
rect.top = 40; | |
rect.left = 40; | |
rect.width = 960 / ncols; | |
rect.height = 1400; | |
savepsnode(fp, topp, rect, showsize); | |
fprintpsend(fp); | |
fclose(fp); | |
} | |
} | |
static void fprintsvgstart(FILE* fp, int width, int height) | |
{ | |
fprintf(fp, "<?xml version=\"1.0\"?>\n<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"%d\" height=\"%d\">\n", width, height); | |
} | |
static void fprintsvgend(FILE* fp) | |
{ | |
fprintf(fp, "</svg>"); | |
} | |
static void fprintsvgnode(FILE* fp, struct rect rect) | |
{ | |
fprintf(fp, | |
" <rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" stroke=\"black\" stroke-width=\"1px\" fill=\"white\"/>\n", | |
rect.left, rect.top, rect.width, rect.height); | |
} | |
static void fprintsvgnodetext(FILE* fp, int x, int y, long long size, char* name, int showsize) | |
{ | |
char buffer[1024], | |
*text="n/a"; | |
switch (showsize) { | |
case 0: | |
text = name; | |
break; | |
case 1: | |
sprintf(buffer, "%s (%lldk)", name, size); | |
text = buffer; | |
break; | |
case 2: | |
sprintf(buffer, "%s (%.2fM)", name, (double)size / 1024.0); | |
text = buffer; | |
break; | |
case 3: | |
sprintf(buffer, "%s (%.2fG)", name, | |
(double)size / (1024.0 * 1024.0)); | |
text = buffer; | |
break; | |
case 4: | |
sprintf(buffer, "%s (%.2fT)", name, | |
(double)size / (1024.0 * 1024.0 * 1024.0)); | |
text = buffer; | |
break; | |
default: | |
printf("arghhhhh"); | |
break; | |
} | |
fprintf(fp, | |
" <text x=\"%d\" y=\"%d\" alignment-baseline=\"middle\" text-anchor=\"left\" style=\"font-size:12\">%s</text>\n", | |
x, y, text); | |
} | |
static void savesvgnode(FILE* fp, struct node* nodep, int showsize, int depth) | |
{ | |
struct node* np; | |
if (depth >= ncols) | |
return; | |
else | |
depth++; | |
for (np = nodep; np != NULL; np = np->peer) { | |
fprintsvgnode(fp, np->rect); | |
if (np->rect.height > 13) | |
fprintsvgnodetext(fp, np->rect.left + 4, | |
np->rect.top + np->rect.height / 2 + 6, | |
np->size, np->name, showsize); | |
savesvgnode(fp, np->child, showsize, depth); | |
} | |
} | |
void savetosvg(char* fname, int showsize, int width, int height) | |
{ | |
FILE* fp; | |
if ((fp = fopen(fname, "w")) != NULL) { | |
fprintsvgstart(fp, width, height); | |
savesvgnode(fp, topp, showsize, 0); | |
fprintsvgend(fp); | |
fclose(fp); | |
} | |
} | |
void uponechild(void) | |
{ | |
struct node *np, | |
*parent = NULL; | |
np = topp; | |
if (np->parent != &top || numchildren(&top) != 1) { | |
parent = np->parent; | |
} | |
if (parent != NODE_NULL) { | |
np = parent->child; | |
if (topp != np) { | |
for (; np->peer; np = np->peer) { | |
if (np->peer == topp) { | |
topp = np; | |
xrepaint(); | |
return; | |
} | |
} | |
} | |
} | |
} | |
void downonechild(void) | |
{ | |
if (topp->peer != NODE_NULL) { | |
topp = topp->peer; | |
xrepaint(); | |
} | |
} |