xref: /plan9/sys/src/cmd/fossil/view.c (revision 0c6300e705c776baceaa75d1b64fc63b9564225a)
15e96a66cSDavid du Colombier #include "stdinc.h"
25e96a66cSDavid du Colombier #include "dat.h"
35e96a66cSDavid du Colombier #include "fns.h"
45e96a66cSDavid du Colombier #include <draw.h>
55e96a66cSDavid du Colombier #include <event.h>
65e96a66cSDavid du Colombier 
75e96a66cSDavid du Colombier /* --- tree.h */
85e96a66cSDavid du Colombier typedef struct Tree Tree;
95e96a66cSDavid du Colombier typedef struct Tnode Tnode;
105e96a66cSDavid du Colombier 
115e96a66cSDavid du Colombier struct Tree
125e96a66cSDavid du Colombier {
135e96a66cSDavid du Colombier 	Tnode *root;
145e96a66cSDavid du Colombier 	Point offset;
155e96a66cSDavid du Colombier 	Image *clipr;
165e96a66cSDavid du Colombier };
175e96a66cSDavid du Colombier 
185e96a66cSDavid du Colombier struct Tnode
195e96a66cSDavid du Colombier {
205e96a66cSDavid du Colombier 	Point offset;
215e96a66cSDavid du Colombier 
225e96a66cSDavid du Colombier 	char *str;
235e96a66cSDavid du Colombier //	char *(*strfn)(Tnode*);
245e96a66cSDavid du Colombier //	uint (*draw)(Tnode*, Image*, Image*, Point);
255e96a66cSDavid du Colombier 	void (*expand)(Tnode*);
265e96a66cSDavid du Colombier 	void (*collapse)(Tnode*);
275e96a66cSDavid du Colombier 
285e96a66cSDavid du Colombier 	uint expanded;
295e96a66cSDavid du Colombier 	Tnode **kid;
305e96a66cSDavid du Colombier 	int nkid;
315e96a66cSDavid du Colombier 	void *aux;
325e96a66cSDavid du Colombier };
335e96a66cSDavid du Colombier 
345e96a66cSDavid du Colombier typedef struct Atree Atree;
355e96a66cSDavid du Colombier struct Atree
365e96a66cSDavid du Colombier {
375e96a66cSDavid du Colombier 	int resizefd;
385e96a66cSDavid du Colombier 	Tnode *root;
395e96a66cSDavid du Colombier };
405e96a66cSDavid du Colombier 
415e96a66cSDavid du Colombier Atree *atreeinit(char*);
425e96a66cSDavid du Colombier 
435e96a66cSDavid du Colombier /* --- visfossil.c */
445e96a66cSDavid du Colombier Tnode *initxheader(void);
455e96a66cSDavid du Colombier Tnode *initxcache(char *name);
465e96a66cSDavid du Colombier Tnode *initxsuper(void);
475e96a66cSDavid du Colombier Tnode *initxlocalroot(char *name, u32int addr);
485e96a66cSDavid du Colombier Tnode *initxentry(Entry);
495e96a66cSDavid du Colombier Tnode *initxsource(Entry, int);
505e96a66cSDavid du Colombier Tnode *initxentryblock(Block*, Entry*);
515e96a66cSDavid du Colombier Tnode *initxdatablock(Block*, uint);
525e96a66cSDavid du Colombier Tnode *initxroot(char *name, uchar[VtScoreSize]);
535e96a66cSDavid du Colombier 
545e96a66cSDavid du Colombier int fd;
555e96a66cSDavid du Colombier Header h;
565e96a66cSDavid du Colombier Super super;
575e96a66cSDavid du Colombier VtSession *z;
585e96a66cSDavid du Colombier VtRoot vac;
595e96a66cSDavid du Colombier int showinactive;
605e96a66cSDavid du Colombier 
615e96a66cSDavid du Colombier /*
625e96a66cSDavid du Colombier  * dumbed down versions of fossil routines
635e96a66cSDavid du Colombier  */
645e96a66cSDavid du Colombier char*
bsStr(int state)655e96a66cSDavid du Colombier bsStr(int state)
665e96a66cSDavid du Colombier {
675e96a66cSDavid du Colombier 	static char s[100];
685e96a66cSDavid du Colombier 
695e96a66cSDavid du Colombier 	if(state == BsFree)
705e96a66cSDavid du Colombier 		return "Free";
715e96a66cSDavid du Colombier 	if(state == BsBad)
725e96a66cSDavid du Colombier 		return "Bad";
735e96a66cSDavid du Colombier 
745e96a66cSDavid du Colombier 	sprint(s, "%x", state);
755e96a66cSDavid du Colombier 	if(!(state&BsAlloc))
765e96a66cSDavid du Colombier 		strcat(s, ",Free");	/* should not happen */
775e96a66cSDavid du Colombier 	if(state&BsVenti)
785e96a66cSDavid du Colombier 		strcat(s, ",Venti");
795e96a66cSDavid du Colombier 	if(state&BsClosed)
805e96a66cSDavid du Colombier 		strcat(s, ",Closed");
815e96a66cSDavid du Colombier 	return s;
825e96a66cSDavid du Colombier }
835e96a66cSDavid du Colombier 
845e96a66cSDavid du Colombier char *bttab[] = {
855e96a66cSDavid du Colombier 	"BtData",
865e96a66cSDavid du Colombier 	"BtData+1",
875e96a66cSDavid du Colombier 	"BtData+2",
885e96a66cSDavid du Colombier 	"BtData+3",
895e96a66cSDavid du Colombier 	"BtData+4",
905e96a66cSDavid du Colombier 	"BtData+5",
915e96a66cSDavid du Colombier 	"BtData+6",
925e96a66cSDavid du Colombier 	"BtData+7",
935e96a66cSDavid du Colombier 	"BtDir",
945e96a66cSDavid du Colombier 	"BtDir+1",
955e96a66cSDavid du Colombier 	"BtDir+2",
965e96a66cSDavid du Colombier 	"BtDir+3",
975e96a66cSDavid du Colombier 	"BtDir+4",
985e96a66cSDavid du Colombier 	"BtDir+5",
995e96a66cSDavid du Colombier 	"BtDir+6",
1005e96a66cSDavid du Colombier 	"BtDir+7",
1015e96a66cSDavid du Colombier };
1025e96a66cSDavid du Colombier 
1035e96a66cSDavid du Colombier char*
btStr(int type)1045e96a66cSDavid du Colombier btStr(int type)
1055e96a66cSDavid du Colombier {
1065e96a66cSDavid du Colombier 	if(type < nelem(bttab))
1075e96a66cSDavid du Colombier 		return bttab[type];
1085e96a66cSDavid du Colombier 	return "unknown";
1095e96a66cSDavid du Colombier }
1105e96a66cSDavid du Colombier #pragma varargck argpos stringnode 1
1115e96a66cSDavid du Colombier 
1125e96a66cSDavid du Colombier Block*
allocBlock(void)1135e96a66cSDavid du Colombier allocBlock(void)
1145e96a66cSDavid du Colombier {
1155e96a66cSDavid du Colombier 	Block *b;
1165e96a66cSDavid du Colombier 
1175e96a66cSDavid du Colombier 	b = mallocz(sizeof(Block)+h.blockSize, 1);
1185e96a66cSDavid du Colombier 	b->data = (void*)&b[1];
1195e96a66cSDavid du Colombier 	return b;
1205e96a66cSDavid du Colombier }
1215e96a66cSDavid du Colombier 
1225e96a66cSDavid du Colombier void
blockPut(Block * b)1235e96a66cSDavid du Colombier blockPut(Block *b)
1245e96a66cSDavid du Colombier {
1255e96a66cSDavid du Colombier 	free(b);
1265e96a66cSDavid du Colombier }
1275e96a66cSDavid du Colombier 
1285e96a66cSDavid du Colombier static u32int
partStart(int part)1295e96a66cSDavid du Colombier partStart(int part)
1305e96a66cSDavid du Colombier {
1315e96a66cSDavid du Colombier 	switch(part){
1325e96a66cSDavid du Colombier 	default:
1335e96a66cSDavid du Colombier 		assert(0);
1345e96a66cSDavid du Colombier 	case PartSuper:
1355e96a66cSDavid du Colombier 		return h.super;
1365e96a66cSDavid du Colombier 	case PartLabel:
1375e96a66cSDavid du Colombier 		return h.label;
1385e96a66cSDavid du Colombier 	case PartData:
1395e96a66cSDavid du Colombier 		return h.data;
1405e96a66cSDavid du Colombier 	}
1415e96a66cSDavid du Colombier }
1425e96a66cSDavid du Colombier 
1435e96a66cSDavid du Colombier 
1445e96a66cSDavid du Colombier static u32int
partEnd(int part)1455e96a66cSDavid du Colombier partEnd(int part)
1465e96a66cSDavid du Colombier {
1475e96a66cSDavid du Colombier 	switch(part){
1485e96a66cSDavid du Colombier 	default:
1495e96a66cSDavid du Colombier 		assert(0);
1505e96a66cSDavid du Colombier 	case PartSuper:
1515e96a66cSDavid du Colombier 		return h.super+1;
1525e96a66cSDavid du Colombier 	case PartLabel:
1535e96a66cSDavid du Colombier 		return h.data;
1545e96a66cSDavid du Colombier 	case PartData:
1555e96a66cSDavid du Colombier 		return h.end;
1565e96a66cSDavid du Colombier 	}
1575e96a66cSDavid du Colombier }
1585e96a66cSDavid du Colombier 
1595e96a66cSDavid du Colombier Block*
readBlock(int part,u32int addr)1605e96a66cSDavid du Colombier readBlock(int part, u32int addr)
1615e96a66cSDavid du Colombier {
1625e96a66cSDavid du Colombier 	u32int start, end;
1635e96a66cSDavid du Colombier 	u64int offset;
1645e96a66cSDavid du Colombier 	int n, nn;
1655e96a66cSDavid du Colombier 	Block *b;
1665e96a66cSDavid du Colombier 	uchar *buf;
1675e96a66cSDavid du Colombier 
1685e96a66cSDavid du Colombier 	start = partStart(part);
1695e96a66cSDavid du Colombier 	end = partEnd(part);
1705e96a66cSDavid du Colombier 	if(addr >= end-start){
1715e96a66cSDavid du Colombier 		werrstr("bad addr 0x%.8ux; wanted 0x%.8ux - 0x%.8ux", addr, start, end);
1725e96a66cSDavid du Colombier 		return nil;
1735e96a66cSDavid du Colombier 	}
1745e96a66cSDavid du Colombier 
1755e96a66cSDavid du Colombier 	b = allocBlock();
1765e96a66cSDavid du Colombier 	b->addr = addr;
1775e96a66cSDavid du Colombier 	buf = b->data;
1785e96a66cSDavid du Colombier 	offset = ((u64int)(addr+start))*h.blockSize;
1795e96a66cSDavid du Colombier 	n = h.blockSize;
1805e96a66cSDavid du Colombier 	while(n > 0){
1815e96a66cSDavid du Colombier 		nn = pread(fd, buf, n, offset);
1825e96a66cSDavid du Colombier 		if(nn < 0){
1835e96a66cSDavid du Colombier 			blockPut(b);
1845e96a66cSDavid du Colombier 			return nil;
1855e96a66cSDavid du Colombier 		}
1865e96a66cSDavid du Colombier 		if(nn == 0){
1875e96a66cSDavid du Colombier 			werrstr("short read");
1885e96a66cSDavid du Colombier 			blockPut(b);
1895e96a66cSDavid du Colombier 			return nil;
1905e96a66cSDavid du Colombier 		}
1915e96a66cSDavid du Colombier 		n -= nn;
1925e96a66cSDavid du Colombier 		offset += nn;
1935e96a66cSDavid du Colombier 		buf += nn;
1945e96a66cSDavid du Colombier 	}
1955e96a66cSDavid du Colombier 	return b;
1965e96a66cSDavid du Colombier }
1975e96a66cSDavid du Colombier 
1985e96a66cSDavid du Colombier int vtType[BtMax] = {
1995e96a66cSDavid du Colombier 	VtDataType,		/* BtData | 0  */
2005e96a66cSDavid du Colombier 	VtPointerType0,		/* BtData | 1  */
2015e96a66cSDavid du Colombier 	VtPointerType1,		/* BtData | 2  */
2025e96a66cSDavid du Colombier 	VtPointerType2,		/* BtData | 3  */
2035e96a66cSDavid du Colombier 	VtPointerType3,		/* BtData | 4  */
2045e96a66cSDavid du Colombier 	VtPointerType4,		/* BtData | 5  */
2055e96a66cSDavid du Colombier 	VtPointerType5,		/* BtData | 6  */
2065e96a66cSDavid du Colombier 	VtPointerType6,		/* BtData | 7  */
2075e96a66cSDavid du Colombier 	VtDirType,		/* BtDir | 0  */
2085e96a66cSDavid du Colombier 	VtPointerType0,		/* BtDir | 1  */
2095e96a66cSDavid du Colombier 	VtPointerType1,		/* BtDir | 2  */
2105e96a66cSDavid du Colombier 	VtPointerType2,		/* BtDir | 3  */
2115e96a66cSDavid du Colombier 	VtPointerType3,		/* BtDir | 4  */
2125e96a66cSDavid du Colombier 	VtPointerType4,		/* BtDir | 5  */
2135e96a66cSDavid du Colombier 	VtPointerType5,		/* BtDir | 6  */
2145e96a66cSDavid du Colombier 	VtPointerType6,		/* BtDir | 7  */
2155e96a66cSDavid du Colombier };
2165e96a66cSDavid du Colombier 
2175e96a66cSDavid du Colombier Block*
ventiBlock(uchar score[VtScoreSize],uint type)2185e96a66cSDavid du Colombier ventiBlock(uchar score[VtScoreSize], uint type)
2195e96a66cSDavid du Colombier {
2205e96a66cSDavid du Colombier 	int n;
2215e96a66cSDavid du Colombier 	Block *b;
2225e96a66cSDavid du Colombier 
2235e96a66cSDavid du Colombier 	b = allocBlock();
2245e96a66cSDavid du Colombier 	memmove(b->score, score, VtScoreSize);
2255e96a66cSDavid du Colombier 	b->addr = NilBlock;
2265e96a66cSDavid du Colombier 
2275e96a66cSDavid du Colombier 	n = vtRead(z, b->score, vtType[type], b->data, h.blockSize);
2285e96a66cSDavid du Colombier 	if(n < 0){
2295e96a66cSDavid du Colombier 		fprint(2, "vtRead returns %d: %R\n", n);
2305e96a66cSDavid du Colombier 		blockPut(b);
2315e96a66cSDavid du Colombier 		return nil;
2325e96a66cSDavid du Colombier 	}
2335e96a66cSDavid du Colombier 	vtZeroExtend(vtType[type], b->data, n, h.blockSize);
2345e96a66cSDavid du Colombier 	b->l.type = type;
2355e96a66cSDavid du Colombier 	b->l.state = 0;
2365e96a66cSDavid du Colombier 	b->l.tag = 0;
2375e96a66cSDavid du Colombier 	b->l.epoch = 0;
2385e96a66cSDavid du Colombier 	return b;
2395e96a66cSDavid du Colombier }
2405e96a66cSDavid du Colombier 
2415e96a66cSDavid du Colombier Block*
dataBlock(uchar score[VtScoreSize],uint type,uint tag)2425e96a66cSDavid du Colombier dataBlock(uchar score[VtScoreSize], uint type, uint tag)
2435e96a66cSDavid du Colombier {
2445e96a66cSDavid du Colombier 	Block *b, *bl;
2455e96a66cSDavid du Colombier 	int lpb;
2465e96a66cSDavid du Colombier 	Label l;
2475e96a66cSDavid du Colombier 	u32int addr;
2485e96a66cSDavid du Colombier 
2495e96a66cSDavid du Colombier 	addr = globalToLocal(score);
2505e96a66cSDavid du Colombier 	if(addr == NilBlock)
2515e96a66cSDavid du Colombier 		return ventiBlock(score, type);
2525e96a66cSDavid du Colombier 
2535e96a66cSDavid du Colombier 	lpb = h.blockSize/LabelSize;
2545e96a66cSDavid du Colombier 	bl = readBlock(PartLabel, addr/lpb);
2555e96a66cSDavid du Colombier 	if(bl == nil)
2565e96a66cSDavid du Colombier 		return nil;
2575e96a66cSDavid du Colombier 	if(!labelUnpack(&l, bl->data, addr%lpb)){
2585e96a66cSDavid du Colombier 		werrstr("%R");
2595e96a66cSDavid du Colombier 		blockPut(bl);
2605e96a66cSDavid du Colombier 		return nil;
2615e96a66cSDavid du Colombier 	}
2625e96a66cSDavid du Colombier 	blockPut(bl);
2635e96a66cSDavid du Colombier 	if(l.type != type){
2645e96a66cSDavid du Colombier 		werrstr("type mismatch; got %d (%s) wanted %d (%s)",
2655e96a66cSDavid du Colombier 			l.type, btStr(l.type), type, btStr(type));
2665e96a66cSDavid du Colombier 		return nil;
2675e96a66cSDavid du Colombier 	}
2685e96a66cSDavid du Colombier 	if(tag && l.tag != tag){
2695e96a66cSDavid du Colombier 		werrstr("tag mismatch; got 0x%.8ux wanted 0x%.8ux",
2705e96a66cSDavid du Colombier 			l.tag, tag);
2715e96a66cSDavid du Colombier 		return nil;
2725e96a66cSDavid du Colombier 	}
2735e96a66cSDavid du Colombier 	b = readBlock(PartData, addr);
2745e96a66cSDavid du Colombier 	if(b == nil)
2755e96a66cSDavid du Colombier 		return nil;
2765e96a66cSDavid du Colombier 	b->l = l;
2775e96a66cSDavid du Colombier 	return b;
2785e96a66cSDavid du Colombier }
2795e96a66cSDavid du Colombier 
2805e96a66cSDavid du Colombier Entry*
copyEntry(Entry e)2815e96a66cSDavid du Colombier copyEntry(Entry e)
2825e96a66cSDavid du Colombier {
2835e96a66cSDavid du Colombier 	Entry *p;
2845e96a66cSDavid du Colombier 
2855e96a66cSDavid du Colombier 	p = mallocz(sizeof *p, 1);
2865e96a66cSDavid du Colombier 	*p = e;
2875e96a66cSDavid du Colombier 	return p;
2885e96a66cSDavid du Colombier }
2895e96a66cSDavid du Colombier 
2905e96a66cSDavid du Colombier MetaBlock*
copyMetaBlock(MetaBlock mb)2915e96a66cSDavid du Colombier copyMetaBlock(MetaBlock mb)
2925e96a66cSDavid du Colombier {
2935e96a66cSDavid du Colombier 	MetaBlock *p;
2945e96a66cSDavid du Colombier 
2955e96a66cSDavid du Colombier 	p = mallocz(sizeof mb, 1);
2965e96a66cSDavid du Colombier 	*p = mb;
2975e96a66cSDavid du Colombier 	return p;
2985e96a66cSDavid du Colombier }
2995e96a66cSDavid du Colombier 
3005e96a66cSDavid du Colombier /*
3015e96a66cSDavid du Colombier  * visualizer
3025e96a66cSDavid du Colombier  */
3035e96a66cSDavid du Colombier 
304*0c6300e7SDavid du Colombier #pragma	varargck	argpos	stringnode	1
305*0c6300e7SDavid du Colombier 
3065e96a66cSDavid du Colombier Tnode*
stringnode(char * fmt,...)3075e96a66cSDavid du Colombier stringnode(char *fmt, ...)
3085e96a66cSDavid du Colombier {
3095e96a66cSDavid du Colombier 	va_list arg;
3105e96a66cSDavid du Colombier 	Tnode *t;
3115e96a66cSDavid du Colombier 
3125e96a66cSDavid du Colombier 	t = mallocz(sizeof(Tnode), 1);
3135e96a66cSDavid du Colombier 	va_start(arg, fmt);
3145e96a66cSDavid du Colombier 	t->str = vsmprint(fmt, arg);
3155e96a66cSDavid du Colombier 	va_end(arg);
3165e96a66cSDavid du Colombier 	t->nkid = -1;
3175e96a66cSDavid du Colombier 	return t;
3185e96a66cSDavid du Colombier }
3195e96a66cSDavid du Colombier 
3205e96a66cSDavid du Colombier void
xcacheexpand(Tnode * t)3215e96a66cSDavid du Colombier xcacheexpand(Tnode *t)
3225e96a66cSDavid du Colombier {
3235e96a66cSDavid du Colombier 	if(t->nkid >= 0)
3245e96a66cSDavid du Colombier 		return;
3255e96a66cSDavid du Colombier 
3265e96a66cSDavid du Colombier 	t->nkid = 1;
3275e96a66cSDavid du Colombier 	t->kid = mallocz(sizeof(t->kid[0])*t->nkid, 1);
3285e96a66cSDavid du Colombier 	t->kid[0] = initxheader();
3295e96a66cSDavid du Colombier }
3305e96a66cSDavid du Colombier 
3315e96a66cSDavid du Colombier Tnode*
initxcache(char * name)3325e96a66cSDavid du Colombier initxcache(char *name)
3335e96a66cSDavid du Colombier {
3345e96a66cSDavid du Colombier 	Tnode *t;
3355e96a66cSDavid du Colombier 
3365e96a66cSDavid du Colombier 	if((fd = open(name, OREAD)) < 0)
3375e96a66cSDavid du Colombier 		sysfatal("cannot open %s: %r", name);
3385e96a66cSDavid du Colombier 
3395e96a66cSDavid du Colombier 	t = stringnode("%s", name);
3405e96a66cSDavid du Colombier 	t->expand = xcacheexpand;
3415e96a66cSDavid du Colombier 	return t;
3425e96a66cSDavid du Colombier }
3435e96a66cSDavid du Colombier 
3445e96a66cSDavid du Colombier void
xheaderexpand(Tnode * t)3455e96a66cSDavid du Colombier xheaderexpand(Tnode *t)
3465e96a66cSDavid du Colombier {
3475e96a66cSDavid du Colombier 	if(t->nkid >= 0)
3485e96a66cSDavid du Colombier 		return;
3495e96a66cSDavid du Colombier 
3505e96a66cSDavid du Colombier 	t->nkid = 1;
3515e96a66cSDavid du Colombier 	t->kid = mallocz(sizeof(t->kid[0])*t->nkid, 1);
3525e96a66cSDavid du Colombier 	t->kid[0] = initxsuper();
3535e96a66cSDavid du Colombier 	//t->kid[1] = initxlabel(h.label);
3545e96a66cSDavid du Colombier 	//t->kid[2] = initxdata(h.data);
3555e96a66cSDavid du Colombier }
3565e96a66cSDavid du Colombier 
3575e96a66cSDavid du Colombier Tnode*
initxheader(void)3585e96a66cSDavid du Colombier initxheader(void)
3595e96a66cSDavid du Colombier {
3605e96a66cSDavid du Colombier 	u8int buf[HeaderSize];
3615e96a66cSDavid du Colombier 	Tnode *t;
3625e96a66cSDavid du Colombier 
3635e96a66cSDavid du Colombier 	if(pread(fd, buf, HeaderSize, HeaderOffset) < HeaderSize)
3645e96a66cSDavid du Colombier 		return stringnode("error reading header: %r");
3655e96a66cSDavid du Colombier 	if(!headerUnpack(&h, buf))
3665e96a66cSDavid du Colombier 		return stringnode("error unpacking header: %R");
3675e96a66cSDavid du Colombier 
3685e96a66cSDavid du Colombier 	t = stringnode("header "
3695e96a66cSDavid du Colombier 		"version=%#ux (%d) "
3705e96a66cSDavid du Colombier 		"blockSize=%#ux (%d) "
3715e96a66cSDavid du Colombier 		"super=%#lux (%ld) "
3725e96a66cSDavid du Colombier 		"label=%#lux (%ld) "
3735e96a66cSDavid du Colombier 		"data=%#lux (%ld) "
3745e96a66cSDavid du Colombier 		"end=%#lux (%ld)",
3755e96a66cSDavid du Colombier 		h.version, h.version, h.blockSize, h.blockSize,
3765e96a66cSDavid du Colombier 		h.super, h.super,
3775e96a66cSDavid du Colombier 		h.label, h.label, h.data, h.data, h.end, h.end);
3785e96a66cSDavid du Colombier 	t->expand = xheaderexpand;
3795e96a66cSDavid du Colombier 	return t;
3805e96a66cSDavid du Colombier }
3815e96a66cSDavid du Colombier 
3825e96a66cSDavid du Colombier void
xsuperexpand(Tnode * t)3835e96a66cSDavid du Colombier xsuperexpand(Tnode *t)
3845e96a66cSDavid du Colombier {
3855e96a66cSDavid du Colombier 	if(t->nkid >= 0)
3865e96a66cSDavid du Colombier 		return;
3875e96a66cSDavid du Colombier 
3885e96a66cSDavid du Colombier 	t->nkid = 1;
3895e96a66cSDavid du Colombier 	t->kid = mallocz(sizeof(t->kid[0])*t->nkid, 1);
3905e96a66cSDavid du Colombier 	t->kid[0] = initxlocalroot("active", super.active);
3915e96a66cSDavid du Colombier //	t->kid[1] = initxlocalroot("next", super.next);
3925e96a66cSDavid du Colombier //	t->kid[2] = initxlocalroot("current", super.current);
3935e96a66cSDavid du Colombier }
3945e96a66cSDavid du Colombier 
3955e96a66cSDavid du Colombier Tnode*
initxsuper(void)3965e96a66cSDavid du Colombier initxsuper(void)
3975e96a66cSDavid du Colombier {
3985e96a66cSDavid du Colombier 	Block *b;
3995e96a66cSDavid du Colombier 	Tnode *t;
4005e96a66cSDavid du Colombier 
4015e96a66cSDavid du Colombier 	b = readBlock(PartSuper, 0);
4025e96a66cSDavid du Colombier 	if(b == nil)
4035e96a66cSDavid du Colombier 		return stringnode("reading super: %r");
4045e96a66cSDavid du Colombier 	if(!superUnpack(&super, b->data)){
4055e96a66cSDavid du Colombier 		blockPut(b);
4065e96a66cSDavid du Colombier 		return stringnode("unpacking super: %R");
4075e96a66cSDavid du Colombier 	}
4085e96a66cSDavid du Colombier 	blockPut(b);
4095e96a66cSDavid du Colombier 	t = stringnode("super "
4105e96a66cSDavid du Colombier 		"version=%#ux "
4115e96a66cSDavid du Colombier 		"epoch=[%#ux,%#ux) "
4125e96a66cSDavid du Colombier 		"qid=%#llux "
4135e96a66cSDavid du Colombier 		"active=%#x "
4145e96a66cSDavid du Colombier 		"next=%#x "
4155e96a66cSDavid du Colombier 		"current=%#x "
4165e96a66cSDavid du Colombier 		"last=%V "
4175e96a66cSDavid du Colombier 		"name=%s",
4185e96a66cSDavid du Colombier 		super.version, super.epochLow, super.epochHigh,
4195e96a66cSDavid du Colombier 		super.qid, super.active, super.next, super.current,
4205e96a66cSDavid du Colombier 		super.last, super.name);
4215e96a66cSDavid du Colombier 	t->expand = xsuperexpand;
4225e96a66cSDavid du Colombier 	return t;
4235e96a66cSDavid du Colombier }
4245e96a66cSDavid du Colombier 
4255e96a66cSDavid du Colombier void
xvacrootexpand(Tnode * t)4265e96a66cSDavid du Colombier xvacrootexpand(Tnode *t)
4275e96a66cSDavid du Colombier {
4285e96a66cSDavid du Colombier 	if(t->nkid >= 0)
4295e96a66cSDavid du Colombier 		return;
4305e96a66cSDavid du Colombier 
4315e96a66cSDavid du Colombier 	t->nkid = 1;
4325e96a66cSDavid du Colombier 	t->kid = mallocz(sizeof(t->kid[0])*t->nkid, 1);
4335e96a66cSDavid du Colombier 	t->kid[0] = initxroot("root", vac.score);
4345e96a66cSDavid du Colombier }
4355e96a66cSDavid du Colombier 
4365e96a66cSDavid du Colombier Tnode*
initxvacroot(uchar score[VtScoreSize])4375e96a66cSDavid du Colombier initxvacroot(uchar score[VtScoreSize])
4385e96a66cSDavid du Colombier {
4395e96a66cSDavid du Colombier 	Tnode *t;
4405e96a66cSDavid du Colombier 	uchar buf[VtRootSize];
4415e96a66cSDavid du Colombier 	int n;
4425e96a66cSDavid du Colombier 
4435e96a66cSDavid du Colombier 	if((n = vtRead(z, score, VtRootType, buf, VtRootSize)) < 0)
4445e96a66cSDavid du Colombier 		return stringnode("reading root %V: %R", score);
4455e96a66cSDavid du Colombier 
4465e96a66cSDavid du Colombier 	if(!vtRootUnpack(&vac, buf))
4475e96a66cSDavid du Colombier 		return stringnode("unpack %d-byte root: %R", n);
4485e96a66cSDavid du Colombier 
4495e96a66cSDavid du Colombier 	h.blockSize = vac.blockSize;
4505e96a66cSDavid du Colombier 	t = stringnode("vac version=%#ux name=%s type=%s blockSize=%ud score=%V prev=%V",
4515e96a66cSDavid du Colombier 		vac.version, vac.name, vac.type, vac.blockSize, vac.score, vac.prev);
4525e96a66cSDavid du Colombier 	t->expand = xvacrootexpand;
4535e96a66cSDavid du Colombier 	return t;
4545e96a66cSDavid du Colombier }
4555e96a66cSDavid du Colombier 
4565e96a66cSDavid du Colombier Tnode*
initxlabel(Label l)4575e96a66cSDavid du Colombier initxlabel(Label l)
4585e96a66cSDavid du Colombier {
4595e96a66cSDavid du Colombier 	return stringnode("label type=%s state=%s epoch=%#ux tag=%#ux",
4605e96a66cSDavid du Colombier 		btStr(l.type), bsStr(l.state), l.epoch, l.tag);
4615e96a66cSDavid du Colombier }
4625e96a66cSDavid du Colombier 
4635e96a66cSDavid du Colombier typedef struct Xblock Xblock;
4645e96a66cSDavid du Colombier struct Xblock
4655e96a66cSDavid du Colombier {
4665e96a66cSDavid du Colombier 	Tnode;
4675e96a66cSDavid du Colombier 	Block *b;
4685e96a66cSDavid du Colombier 	int (*gen)(void*, Block*, int, Tnode**);
4695e96a66cSDavid du Colombier 	void *arg;
4705e96a66cSDavid du Colombier 	int printlabel;
4715e96a66cSDavid du Colombier };
4725e96a66cSDavid du Colombier 
4735e96a66cSDavid du Colombier void
xblockexpand(Tnode * tt)4745e96a66cSDavid du Colombier xblockexpand(Tnode *tt)
4755e96a66cSDavid du Colombier {
4765e96a66cSDavid du Colombier 	int i, j;
4775e96a66cSDavid du Colombier 	enum { Q = 32 };
4785e96a66cSDavid du Colombier 	Xblock *t = (Xblock*)tt;
4795e96a66cSDavid du Colombier 	Tnode *nn;
4805e96a66cSDavid du Colombier 
4815e96a66cSDavid du Colombier 	if(t->nkid >= 0)
4825e96a66cSDavid du Colombier 		return;
4835e96a66cSDavid du Colombier 
4845e96a66cSDavid du Colombier 	j = 0;
4855e96a66cSDavid du Colombier 	if(t->printlabel){
4865e96a66cSDavid du Colombier 		t->kid = mallocz(Q*sizeof(t->kid[0]), 1);
4875e96a66cSDavid du Colombier 		t->kid[0] = initxlabel(t->b->l);
4885e96a66cSDavid du Colombier 		j = 1;
4895e96a66cSDavid du Colombier 	}
4905e96a66cSDavid du Colombier 
4915e96a66cSDavid du Colombier 	for(i=0;; i++){
4925e96a66cSDavid du Colombier 		switch((*t->gen)(t->arg, t->b, i, &nn)){
4935e96a66cSDavid du Colombier 		case -1:
4945e96a66cSDavid du Colombier 			t->nkid = j;
4955e96a66cSDavid du Colombier 			return;
4965e96a66cSDavid du Colombier 		case 0:
4975e96a66cSDavid du Colombier 			break;
4985e96a66cSDavid du Colombier 		case 1:
4995e96a66cSDavid du Colombier 			if(j%Q == 0)
5005e96a66cSDavid du Colombier 				t->kid = realloc(t->kid, (j+Q)*sizeof(t->kid[0]));
5015e96a66cSDavid du Colombier 			t->kid[j++] = nn;
5025e96a66cSDavid du Colombier 			break;
5035e96a66cSDavid du Colombier 		}
5045e96a66cSDavid du Colombier 	}
5055e96a66cSDavid du Colombier }
5065e96a66cSDavid du Colombier 
5075e96a66cSDavid du Colombier int
nilgen(void *,Block *,int,Tnode **)5085e96a66cSDavid du Colombier nilgen(void*, Block*, int, Tnode**)
5095e96a66cSDavid du Colombier {
5105e96a66cSDavid du Colombier 	return -1;
5115e96a66cSDavid du Colombier }
5125e96a66cSDavid du Colombier 
5135e96a66cSDavid du Colombier Tnode*
initxblock(Block * b,char * s,int (* gen)(void *,Block *,int,Tnode **),void * arg)5145e96a66cSDavid du Colombier initxblock(Block *b, char *s, int (*gen)(void*, Block*, int, Tnode**), void *arg)
5155e96a66cSDavid du Colombier {
5165e96a66cSDavid du Colombier 	Xblock *t;
5175e96a66cSDavid du Colombier 
5185e96a66cSDavid du Colombier 	if(gen == nil)
5195e96a66cSDavid du Colombier 		gen = nilgen;
5205e96a66cSDavid du Colombier 	t = mallocz(sizeof(Xblock), 1);
5215e96a66cSDavid du Colombier 	t->b = b;
5225e96a66cSDavid du Colombier 	t->gen = gen;
5235e96a66cSDavid du Colombier 	t->arg = arg;
5245e96a66cSDavid du Colombier 	if(b->addr == NilBlock)
5255e96a66cSDavid du Colombier 		t->str = smprint("Block %V: %s", b->score, s);
5265e96a66cSDavid du Colombier 	else
5275e96a66cSDavid du Colombier 		t->str = smprint("Block %#ux: %s", b->addr, s);
5285e96a66cSDavid du Colombier 	t->printlabel = 1;
5295e96a66cSDavid du Colombier 	t->nkid = -1;
5305e96a66cSDavid du Colombier 	t->expand = xblockexpand;
5315e96a66cSDavid du Colombier 	return t;
5325e96a66cSDavid du Colombier }
5335e96a66cSDavid du Colombier 
5345e96a66cSDavid du Colombier int
xentrygen(void * v,Block * b,int o,Tnode ** tp)5355e96a66cSDavid du Colombier xentrygen(void *v, Block *b, int o, Tnode **tp)
5365e96a66cSDavid du Colombier {
5375e96a66cSDavid du Colombier 	Entry e;
5385e96a66cSDavid du Colombier 	Entry *ed;
5395e96a66cSDavid du Colombier 
5405e96a66cSDavid du Colombier 	ed = v;
5415e96a66cSDavid du Colombier 	if(o >= ed->dsize/VtEntrySize)
5425e96a66cSDavid du Colombier 		return -1;
5435e96a66cSDavid du Colombier 
5445e96a66cSDavid du Colombier 	entryUnpack(&e, b->data, o);
5455e96a66cSDavid du Colombier 	if(!showinactive && !(e.flags & VtEntryActive))
5465e96a66cSDavid du Colombier 		return 0;
5475e96a66cSDavid du Colombier 	*tp = initxentry(e);
5485e96a66cSDavid du Colombier 	return 1;
5495e96a66cSDavid du Colombier }
5505e96a66cSDavid du Colombier 
5515e96a66cSDavid du Colombier Tnode*
initxentryblock(Block * b,Entry * ed)5525e96a66cSDavid du Colombier initxentryblock(Block *b, Entry *ed)
5535e96a66cSDavid du Colombier {
5545e96a66cSDavid du Colombier 	return initxblock(b, "entry", xentrygen, ed);
5555e96a66cSDavid du Colombier }
5565e96a66cSDavid du Colombier 
5575e96a66cSDavid du Colombier typedef struct Xentry Xentry;
5585e96a66cSDavid du Colombier struct Xentry
5595e96a66cSDavid du Colombier {
5605e96a66cSDavid du Colombier 	Tnode;
5615e96a66cSDavid du Colombier 	Entry e;
5625e96a66cSDavid du Colombier };
5635e96a66cSDavid du Colombier 
5645e96a66cSDavid du Colombier void
xentryexpand(Tnode * tt)5655e96a66cSDavid du Colombier xentryexpand(Tnode *tt)
5665e96a66cSDavid du Colombier {
5675e96a66cSDavid du Colombier 	Xentry *t = (Xentry*)tt;
5685e96a66cSDavid du Colombier 
5695e96a66cSDavid du Colombier 	if(t->nkid >= 0)
5705e96a66cSDavid du Colombier 		return;
5715e96a66cSDavid du Colombier 
5725e96a66cSDavid du Colombier 	t->nkid = 1;
5735e96a66cSDavid du Colombier 	t->kid = mallocz(sizeof(t->kid[0])*t->nkid, 1);
5745e96a66cSDavid du Colombier 	t->kid[0] = initxsource(t->e, 1);
5755e96a66cSDavid du Colombier }
5765e96a66cSDavid du Colombier 
5775e96a66cSDavid du Colombier Tnode*
initxentry(Entry e)5785e96a66cSDavid du Colombier initxentry(Entry e)
5795e96a66cSDavid du Colombier {
5805e96a66cSDavid du Colombier 	Xentry *t;
5815e96a66cSDavid du Colombier 
5825e96a66cSDavid du Colombier 	t = mallocz(sizeof *t, 1);
5835e96a66cSDavid du Colombier 	t->nkid = -1;
5845e96a66cSDavid du Colombier 	t->str = smprint("Entry gen=%#ux psize=%d dsize=%d depth=%d flags=%#ux size=%lld score=%V",
5855e96a66cSDavid du Colombier 		e.gen, e.psize, e.dsize, e.depth, e.flags, e.size, e.score);
5865e96a66cSDavid du Colombier 	if(e.flags & VtEntryLocal)
5875e96a66cSDavid du Colombier 		t->str = smprint("%s archive=%d snap=%d tag=%#ux", t->str, e.archive, e.snap, e.tag);
5885e96a66cSDavid du Colombier 	t->expand = xentryexpand;
5895e96a66cSDavid du Colombier 	t->e = e;
5905e96a66cSDavid du Colombier 	return t;
5915e96a66cSDavid du Colombier }
5925e96a66cSDavid du Colombier 
5935e96a66cSDavid du Colombier int
ptrgen(void * v,Block * b,int o,Tnode ** tp)5945e96a66cSDavid du Colombier ptrgen(void *v, Block *b, int o, Tnode **tp)
5955e96a66cSDavid du Colombier {
5965e96a66cSDavid du Colombier 	Entry *ed;
5975e96a66cSDavid du Colombier 	Entry e;
5985e96a66cSDavid du Colombier 
5995e96a66cSDavid du Colombier 	ed = v;
6005e96a66cSDavid du Colombier 	if(o >= ed->psize/VtScoreSize)
6015e96a66cSDavid du Colombier 		return -1;
6025e96a66cSDavid du Colombier 
6035e96a66cSDavid du Colombier 	e = *ed;
6045e96a66cSDavid du Colombier 	e.depth--;
6055e96a66cSDavid du Colombier 	memmove(e.score, b->data+o*VtScoreSize, VtScoreSize);
6065e96a66cSDavid du Colombier 	if(memcmp(e.score, vtZeroScore, VtScoreSize) == 0)
6075e96a66cSDavid du Colombier 		return 0;
6085e96a66cSDavid du Colombier 	*tp = initxsource(e, 0);
6095e96a66cSDavid du Colombier 	return 1;
6105e96a66cSDavid du Colombier }
6115e96a66cSDavid du Colombier 
6125e96a66cSDavid du Colombier static int
etype(int flags,int depth)6135e96a66cSDavid du Colombier etype(int flags, int depth)
6145e96a66cSDavid du Colombier {
6155e96a66cSDavid du Colombier 	uint t;
6165e96a66cSDavid du Colombier 
6175e96a66cSDavid du Colombier 	if(flags&VtEntryDir)
6185e96a66cSDavid du Colombier 		t = BtDir;
6195e96a66cSDavid du Colombier 	else
6205e96a66cSDavid du Colombier 		t = BtData;
6215e96a66cSDavid du Colombier 	return t+depth;
6225e96a66cSDavid du Colombier }
6235e96a66cSDavid du Colombier 
6245e96a66cSDavid du Colombier Tnode*
initxsource(Entry e,int dowrap)6255e96a66cSDavid du Colombier initxsource(Entry e, int dowrap)
6265e96a66cSDavid du Colombier {
6275e96a66cSDavid du Colombier 	Block *b;
6285e96a66cSDavid du Colombier 	Tnode *t, *tt;
6295e96a66cSDavid du Colombier 
6305e96a66cSDavid du Colombier 	b = dataBlock(e.score, etype(e.flags, e.depth), e.tag);
6315e96a66cSDavid du Colombier 	if(b == nil)
6325e96a66cSDavid du Colombier 		return stringnode("dataBlock: %r");
6335e96a66cSDavid du Colombier 
6345e96a66cSDavid du Colombier 	if((e.flags & VtEntryActive) == 0)
6355e96a66cSDavid du Colombier 		return stringnode("inactive Entry");
6365e96a66cSDavid du Colombier 
6375e96a66cSDavid du Colombier 	if(e.depth == 0){
6385e96a66cSDavid du Colombier 		if(e.flags & VtEntryDir)
6395e96a66cSDavid du Colombier 			tt = initxentryblock(b, copyEntry(e));
6405e96a66cSDavid du Colombier 		else
6415e96a66cSDavid du Colombier 			tt = initxdatablock(b, e.dsize);
6425e96a66cSDavid du Colombier 	}else{
6435e96a66cSDavid du Colombier 		tt = initxblock(b, smprint("%s+%d pointer", (e.flags & VtEntryDir) ? "BtDir" : "BtData", e.depth),
6445e96a66cSDavid du Colombier 			ptrgen, copyEntry(e));
6455e96a66cSDavid du Colombier 	}
6465e96a66cSDavid du Colombier 
6475e96a66cSDavid du Colombier 	/*
6485e96a66cSDavid du Colombier 	 * wrap the contents of the Source in a Source node,
6495e96a66cSDavid du Colombier 	 * just so it's closer to what you see in the code.
6505e96a66cSDavid du Colombier 	 */
6515e96a66cSDavid du Colombier 	if(dowrap){
6525e96a66cSDavid du Colombier 		t = stringnode("Source");
6535e96a66cSDavid du Colombier 		t->nkid = 1;
6545e96a66cSDavid du Colombier 		t->kid = mallocz(sizeof(Tnode*)*1, 1);
6555e96a66cSDavid du Colombier 		t->kid[0] = tt;
6565e96a66cSDavid du Colombier 		tt = t;
6575e96a66cSDavid du Colombier 	}
6585e96a66cSDavid du Colombier 	return tt;
6595e96a66cSDavid du Colombier }
6605e96a66cSDavid du Colombier 
6615e96a66cSDavid du Colombier int
xlocalrootgen(void *,Block * b,int o,Tnode ** tp)6625e96a66cSDavid du Colombier xlocalrootgen(void*, Block *b, int o, Tnode **tp)
6635e96a66cSDavid du Colombier {
6645e96a66cSDavid du Colombier 	Entry e;
6655e96a66cSDavid du Colombier 
6665e96a66cSDavid du Colombier 	if(o >= 1)
6675e96a66cSDavid du Colombier 		return -1;
6685e96a66cSDavid du Colombier 	entryUnpack(&e, b->data, o);
6695e96a66cSDavid du Colombier 	*tp = initxentry(e);
6705e96a66cSDavid du Colombier 	return 1;
6715e96a66cSDavid du Colombier }
6725e96a66cSDavid du Colombier 
6735e96a66cSDavid du Colombier Tnode*
initxlocalroot(char * name,u32int addr)6745e96a66cSDavid du Colombier initxlocalroot(char *name, u32int addr)
6755e96a66cSDavid du Colombier {
6765e96a66cSDavid du Colombier 	uchar score[VtScoreSize];
6775e96a66cSDavid du Colombier 	Block *b;
6785e96a66cSDavid du Colombier 
6795e96a66cSDavid du Colombier 	localToGlobal(addr, score);
6805e96a66cSDavid du Colombier 	b = dataBlock(score, BtDir, RootTag);
6815e96a66cSDavid du Colombier 	if(b == nil)
6825e96a66cSDavid du Colombier 		return stringnode("read data block %#ux: %R", addr);
6835e96a66cSDavid du Colombier 	return initxblock(b, smprint("'%s' fs root", name), xlocalrootgen, nil);
6845e96a66cSDavid du Colombier }
6855e96a66cSDavid du Colombier 
6865e96a66cSDavid du Colombier int
xvacrootgen(void *,Block * b,int o,Tnode ** tp)6875e96a66cSDavid du Colombier xvacrootgen(void*, Block *b, int o, Tnode **tp)
6885e96a66cSDavid du Colombier {
6895e96a66cSDavid du Colombier 	Entry e;
6905e96a66cSDavid du Colombier 
6915e96a66cSDavid du Colombier 	if(o >= 3)
6925e96a66cSDavid du Colombier 		return -1;
6935e96a66cSDavid du Colombier 	entryUnpack(&e, b->data, o);
6945e96a66cSDavid du Colombier 	*tp = initxentry(e);
6955e96a66cSDavid du Colombier 	return 1;
6965e96a66cSDavid du Colombier }
6975e96a66cSDavid du Colombier 
6985e96a66cSDavid du Colombier Tnode*
initxroot(char * name,uchar score[VtScoreSize])6995e96a66cSDavid du Colombier initxroot(char *name, uchar score[VtScoreSize])
7005e96a66cSDavid du Colombier {
7015e96a66cSDavid du Colombier 	Block *b;
7025e96a66cSDavid du Colombier 
7035e96a66cSDavid du Colombier 	b = dataBlock(score, BtDir, RootTag);
7045e96a66cSDavid du Colombier 	if(b == nil)
7055e96a66cSDavid du Colombier 		return stringnode("read data block %V: %R", score);
7065e96a66cSDavid du Colombier 	return initxblock(b, smprint("'%s' fs root", name), xvacrootgen, nil);
7075e96a66cSDavid du Colombier }
7085e96a66cSDavid du Colombier Tnode*
initxdirentry(MetaEntry * me)7095e96a66cSDavid du Colombier initxdirentry(MetaEntry *me)
7105e96a66cSDavid du Colombier {
7115e96a66cSDavid du Colombier 	DirEntry dir;
7125e96a66cSDavid du Colombier 	Tnode *t;
7135e96a66cSDavid du Colombier 
7145e96a66cSDavid du Colombier 	if(!deUnpack(&dir, me))
7155e96a66cSDavid du Colombier 		return stringnode("deUnpack: %R");
7165e96a66cSDavid du Colombier 
7175e96a66cSDavid du Colombier 	t = stringnode("dirEntry elem=%s size=%llud data=%#lux/%#lux meta=%#lux/%#lux", dir.elem, dir.size, dir.entry, dir.gen, dir.mentry, dir.mgen);
7185e96a66cSDavid du Colombier 	t->nkid = 1;
7195e96a66cSDavid du Colombier 	t->kid = mallocz(sizeof(t->kid[0])*1, 1);
7205e96a66cSDavid du Colombier 	t->kid[0] = stringnode(
7215e96a66cSDavid du Colombier 		"qid=%#llux\n"
7225e96a66cSDavid du Colombier 		"uid=%s gid=%s mid=%s\n"
7235e96a66cSDavid du Colombier 		"mtime=%lud mcount=%lud ctime=%lud atime=%lud\n"
7245e96a66cSDavid du Colombier 		"mode=%luo\n"
7255e96a66cSDavid du Colombier 		"plan9 %d p9path %#llux p9version %lud\n"
7265e96a66cSDavid du Colombier 		"qidSpace %d offset %#llux max %#llux",
7275e96a66cSDavid du Colombier 		dir.qid,
7285e96a66cSDavid du Colombier 		dir.uid, dir.gid, dir.mid,
7295e96a66cSDavid du Colombier 		dir.mtime, dir.mcount, dir.ctime, dir.atime,
7305e96a66cSDavid du Colombier 		dir.mode,
7315e96a66cSDavid du Colombier 		dir.plan9, dir.p9path, dir.p9version,
7325e96a66cSDavid du Colombier 		dir.qidSpace, dir.qidOffset, dir.qidMax);
7335e96a66cSDavid du Colombier 	return t;
7345e96a66cSDavid du Colombier }
7355e96a66cSDavid du Colombier 
7365e96a66cSDavid du Colombier int
metaentrygen(void * v,Block *,int o,Tnode ** tp)7375e96a66cSDavid du Colombier metaentrygen(void *v, Block*, int o, Tnode **tp)
7385e96a66cSDavid du Colombier {
7395e96a66cSDavid du Colombier 	Tnode *t;
7405e96a66cSDavid du Colombier 	MetaBlock *mb;
7415e96a66cSDavid du Colombier 	MetaEntry me;
7425e96a66cSDavid du Colombier 
7435e96a66cSDavid du Colombier 	mb = v;
7445e96a66cSDavid du Colombier 	if(o >= mb->nindex)
7455e96a66cSDavid du Colombier 		return -1;
7465e96a66cSDavid du Colombier 	meUnpack(&me, mb, o);
7475e96a66cSDavid du Colombier 
7485e96a66cSDavid du Colombier 	t = stringnode("MetaEntry %d bytes", mb->size);
7495e96a66cSDavid du Colombier 	t->kid = mallocz(sizeof(t->kid[0])*1, 1);
7505e96a66cSDavid du Colombier 	t->kid[0] = initxdirentry(&me);
7515e96a66cSDavid du Colombier 	t->nkid = 1;
7525e96a66cSDavid du Colombier 	*tp = t;
7535e96a66cSDavid du Colombier 	return 1;
7545e96a66cSDavid du Colombier }
7555e96a66cSDavid du Colombier 
7565e96a66cSDavid du Colombier int
metablockgen(void * v,Block * b,int o,Tnode ** tp)7575e96a66cSDavid du Colombier metablockgen(void *v, Block *b, int o, Tnode **tp)
7585e96a66cSDavid du Colombier {
7595e96a66cSDavid du Colombier 	Xblock *t;
7605e96a66cSDavid du Colombier 	MetaBlock *mb;
7615e96a66cSDavid du Colombier 
7625e96a66cSDavid du Colombier 	if(o >= 1)
7635e96a66cSDavid du Colombier 		return -1;
7645e96a66cSDavid du Colombier 
7655e96a66cSDavid du Colombier 	/* hack: reuse initxblock as a generic iterator */
7665e96a66cSDavid du Colombier 	mb = v;
7675e96a66cSDavid du Colombier 	t = (Xblock*)initxblock(b, "", metaentrygen, mb);
7685e96a66cSDavid du Colombier 	t->str = smprint("MetaBlock %d/%d space used, %d add'l free %d/%d table used%s",
7695e96a66cSDavid du Colombier 		mb->size, mb->maxsize, mb->free, mb->nindex, mb->maxindex,
7705e96a66cSDavid du Colombier 		mb->botch ? " [BOTCH]" : "");
7715e96a66cSDavid du Colombier 	t->printlabel = 0;
7725e96a66cSDavid du Colombier 	*tp = t;
7735e96a66cSDavid du Colombier 	return 1;
7745e96a66cSDavid du Colombier }
7755e96a66cSDavid du Colombier 
7765e96a66cSDavid du Colombier /*
7775e96a66cSDavid du Colombier  * attempt to guess at the type of data in the block.
7785e96a66cSDavid du Colombier  * it could just be data from a file, but we're hoping it's MetaBlocks.
7795e96a66cSDavid du Colombier  */
7805e96a66cSDavid du Colombier Tnode*
initxdatablock(Block * b,uint n)7815e96a66cSDavid du Colombier initxdatablock(Block *b, uint n)
7825e96a66cSDavid du Colombier {
7835e96a66cSDavid du Colombier 	MetaBlock mb;
7845e96a66cSDavid du Colombier 
7855e96a66cSDavid du Colombier 	if(n > h.blockSize)
7865e96a66cSDavid du Colombier 		n = h.blockSize;
7875e96a66cSDavid du Colombier 
7885e96a66cSDavid du Colombier 	if(mbUnpack(&mb, b->data, n))
7895e96a66cSDavid du Colombier 		return initxblock(b, "metadata", metablockgen, copyMetaBlock(mb));
7905e96a66cSDavid du Colombier 
7915e96a66cSDavid du Colombier 	return initxblock(b, "data", nil, nil);
7925e96a66cSDavid du Colombier }
7935e96a66cSDavid du Colombier 
7945e96a66cSDavid du Colombier int
parseScore(uchar * score,char * buf,int n)7955e96a66cSDavid du Colombier parseScore(uchar *score, char *buf, int n)
7965e96a66cSDavid du Colombier {
7975e96a66cSDavid du Colombier 	int i, c;
7985e96a66cSDavid du Colombier 
7995e96a66cSDavid du Colombier 	memset(score, 0, VtScoreSize);
8005e96a66cSDavid du Colombier 
8015e96a66cSDavid du Colombier 	if(n < VtScoreSize*2)
8025e96a66cSDavid du Colombier 		return 0;
8035e96a66cSDavid du Colombier 	for(i=0; i<VtScoreSize*2; i++){
8045e96a66cSDavid du Colombier 		if(buf[i] >= '0' && buf[i] <= '9')
8055e96a66cSDavid du Colombier 			c = buf[i] - '0';
8065e96a66cSDavid du Colombier 		else if(buf[i] >= 'a' && buf[i] <= 'f')
8075e96a66cSDavid du Colombier 			c = buf[i] - 'a' + 10;
8085e96a66cSDavid du Colombier 		else if(buf[i] >= 'A' && buf[i] <= 'F')
8095e96a66cSDavid du Colombier 			c = buf[i] - 'A' + 10;
8105e96a66cSDavid du Colombier 		else{
8115e96a66cSDavid du Colombier 			return 0;
8125e96a66cSDavid du Colombier 		}
8135e96a66cSDavid du Colombier 
8145e96a66cSDavid du Colombier 		if((i & 1) == 0)
8155e96a66cSDavid du Colombier 			c <<= 4;
8165e96a66cSDavid du Colombier 
8175e96a66cSDavid du Colombier 		score[i>>1] |= c;
8185e96a66cSDavid du Colombier 	}
8195e96a66cSDavid du Colombier 	return 1;
8205e96a66cSDavid du Colombier }
8215e96a66cSDavid du Colombier 
8225e96a66cSDavid du Colombier int
scoreFmt(Fmt * f)8235e96a66cSDavid du Colombier scoreFmt(Fmt *f)
8245e96a66cSDavid du Colombier {
8255e96a66cSDavid du Colombier 	uchar *v;
8265e96a66cSDavid du Colombier 	int i;
8275e96a66cSDavid du Colombier 	u32int addr;
8285e96a66cSDavid du Colombier 
8295e96a66cSDavid du Colombier 	v = va_arg(f->args, uchar*);
8305e96a66cSDavid du Colombier 	if(v == nil){
8315e96a66cSDavid du Colombier 		fmtprint(f, "*");
8325e96a66cSDavid du Colombier 	}else if((addr = globalToLocal(v)) != NilBlock)
8335e96a66cSDavid du Colombier 		fmtprint(f, "0x%.8ux", addr);
8345e96a66cSDavid du Colombier 	else{
8355e96a66cSDavid du Colombier 		for(i = 0; i < VtScoreSize; i++)
8365e96a66cSDavid du Colombier 			fmtprint(f, "%2.2ux", v[i]);
8375e96a66cSDavid du Colombier 	}
8385e96a66cSDavid du Colombier 
8395e96a66cSDavid du Colombier 	return 0;
8405e96a66cSDavid du Colombier }
8415e96a66cSDavid du Colombier 
8425e96a66cSDavid du Colombier Atree*
atreeinit(char * arg)8435e96a66cSDavid du Colombier atreeinit(char *arg)
8445e96a66cSDavid du Colombier {
8455e96a66cSDavid du Colombier 	Atree *a;
8465e96a66cSDavid du Colombier 	uchar score[VtScoreSize];
8475e96a66cSDavid du Colombier 
8485e96a66cSDavid du Colombier 	vtAttach();
8495e96a66cSDavid du Colombier 
8505e96a66cSDavid du Colombier 	fmtinstall('V', scoreFmt);
8515e96a66cSDavid du Colombier 	fmtinstall('R', vtErrFmt);
8525e96a66cSDavid du Colombier 
8535e96a66cSDavid du Colombier 	z = vtDial(nil, 1);
8545e96a66cSDavid du Colombier 	if(z == nil)
8555e96a66cSDavid du Colombier 		fprint(2, "warning: cannot dial venti: %R\n");
8565e96a66cSDavid du Colombier 	if(!vtConnect(z, 0)){
8575e96a66cSDavid du Colombier 		fprint(2, "warning: cannot connect to venti: %R\n");
8585e96a66cSDavid du Colombier 		z = nil;
8595e96a66cSDavid du Colombier 	}
8605e96a66cSDavid du Colombier 	a = mallocz(sizeof(Atree), 1);
8615e96a66cSDavid du Colombier 	if(strncmp(arg, "vac:", 4) == 0){
8625e96a66cSDavid du Colombier 		if(!parseScore(score, arg+4, strlen(arg+4))){
8635e96a66cSDavid du Colombier 			fprint(2, "cannot parse score\n");
8645e96a66cSDavid du Colombier 			return nil;
8655e96a66cSDavid du Colombier 		}
8665e96a66cSDavid du Colombier 		a->root = initxvacroot(score);
8675e96a66cSDavid du Colombier 	}else
8685e96a66cSDavid du Colombier 		a->root = initxcache(arg);
8695e96a66cSDavid du Colombier 	a->resizefd = -1;
8705e96a66cSDavid du Colombier 	return a;
8715e96a66cSDavid du Colombier }
8725e96a66cSDavid du Colombier 
8735e96a66cSDavid du Colombier /* --- tree.c */
8745e96a66cSDavid du Colombier enum
8755e96a66cSDavid du Colombier {
8765e96a66cSDavid du Colombier 	Nubwidth = 11,
8775e96a66cSDavid du Colombier 	Nubheight = 11,
8785e96a66cSDavid du Colombier 	Linewidth = Nubwidth*2+4,
8795e96a66cSDavid du Colombier };
8805e96a66cSDavid du Colombier 
8815e96a66cSDavid du Colombier uint
drawtext(char * s,Image * m,Image * clipr,Point o)8825e96a66cSDavid du Colombier drawtext(char *s, Image *m, Image *clipr, Point o)
8835e96a66cSDavid du Colombier {
8845e96a66cSDavid du Colombier 	char *t, *nt, *e;
8855e96a66cSDavid du Colombier 	uint dy;
8865e96a66cSDavid du Colombier 
8875e96a66cSDavid du Colombier 	if(s == nil)
8885e96a66cSDavid du Colombier 		s = "???";
8895e96a66cSDavid du Colombier 
8905e96a66cSDavid du Colombier 	dy = 0;
8915e96a66cSDavid du Colombier 	for(t=s; t&&*t; t=nt){
8925e96a66cSDavid du Colombier 		if(nt = strchr(t, '\n')){
8935e96a66cSDavid du Colombier 			e = nt;
8945e96a66cSDavid du Colombier 			nt++;
8955e96a66cSDavid du Colombier 		}else
8965e96a66cSDavid du Colombier 			e = t+strlen(t);
8975e96a66cSDavid du Colombier 
8985e96a66cSDavid du Colombier 		_string(m, Pt(o.x, o.y+dy), display->black, ZP, display->defaultfont,
8995e96a66cSDavid du Colombier 			t, nil, e-t, clipr->clipr, nil, ZP, SoverD);
9005e96a66cSDavid du Colombier 		dy += display->defaultfont->height;
9015e96a66cSDavid du Colombier 	}
9025e96a66cSDavid du Colombier 	return dy;
9035e96a66cSDavid du Colombier }
9045e96a66cSDavid du Colombier 
9055e96a66cSDavid du Colombier void
drawnub(Image * m,Image * clipr,Point o,Tnode * t)9065e96a66cSDavid du Colombier drawnub(Image *m, Image *clipr, Point o, Tnode *t)
9075e96a66cSDavid du Colombier {
9085e96a66cSDavid du Colombier 	clipr = nil;
9095e96a66cSDavid du Colombier 
9105e96a66cSDavid du Colombier 	if(t->nkid == 0)
9115e96a66cSDavid du Colombier 		return;
9125e96a66cSDavid du Colombier 	if(t->nkid == -1 && t->expand == nil)
9135e96a66cSDavid du Colombier 		return;
9145e96a66cSDavid du Colombier 
9155e96a66cSDavid du Colombier 	o.y += (display->defaultfont->height-Nubheight)/2;
9165e96a66cSDavid du Colombier 	draw(m, rectaddpt(Rect(0,0,1,Nubheight), o), display->black, clipr, ZP);
9175e96a66cSDavid du Colombier 	draw(m, rectaddpt(Rect(0,0,Nubwidth,1), o), display->black, clipr, o);
9185e96a66cSDavid du Colombier 	draw(m, rectaddpt(Rect(Nubwidth-1,0,Nubwidth,Nubheight), o),
9195e96a66cSDavid du Colombier 		display->black, clipr, addpt(o, Pt(Nubwidth-1, 0)));
9205e96a66cSDavid du Colombier 	draw(m, rectaddpt(Rect(0, Nubheight-1, Nubwidth, Nubheight), o),
9215e96a66cSDavid du Colombier 		display->black, clipr, addpt(o, Pt(0, Nubheight-1)));
9225e96a66cSDavid du Colombier 
9235e96a66cSDavid du Colombier 	draw(m, rectaddpt(Rect(0, Nubheight/2, Nubwidth, Nubheight/2+1), o),
9245e96a66cSDavid du Colombier 		display->black, clipr, addpt(o, Pt(0, Nubheight/2)));
9255e96a66cSDavid du Colombier 	if(!t->expanded)
9265e96a66cSDavid du Colombier 		draw(m, rectaddpt(Rect(Nubwidth/2, 0, Nubwidth/2+1, Nubheight), o),
9275e96a66cSDavid du Colombier 			display->black, clipr, addpt(o, Pt(Nubwidth/2, 0)));
9285e96a66cSDavid du Colombier 
9295e96a66cSDavid du Colombier }
9305e96a66cSDavid du Colombier 
9315e96a66cSDavid du Colombier uint
drawnode(Tnode * t,Image * m,Image * clipr,Point o)9325e96a66cSDavid du Colombier drawnode(Tnode *t, Image *m, Image *clipr, Point o)
9335e96a66cSDavid du Colombier {
9345e96a66cSDavid du Colombier 	int i;
9355e96a66cSDavid du Colombier 	char *fs, *s;
9365e96a66cSDavid du Colombier 	uint dy;
9375e96a66cSDavid du Colombier 	Point oo;
9385e96a66cSDavid du Colombier 
9395e96a66cSDavid du Colombier 	if(t == nil)
9405e96a66cSDavid du Colombier 		return 0;
9415e96a66cSDavid du Colombier 
9425e96a66cSDavid du Colombier 	t->offset = o;
9435e96a66cSDavid du Colombier 
9445e96a66cSDavid du Colombier 	oo = Pt(o.x+Nubwidth+2, o.y);
9455e96a66cSDavid du Colombier //	if(t->draw)
9465e96a66cSDavid du Colombier //		dy = (*t->draw)(t, m, clipr, oo);
9475e96a66cSDavid du Colombier //	else{
9485e96a66cSDavid du Colombier 		fs = nil;
9495e96a66cSDavid du Colombier 		if(t->str)
9505e96a66cSDavid du Colombier 			s = t->str;
9515e96a66cSDavid du Colombier 	//	else if(t->strfn)
9525e96a66cSDavid du Colombier 	//		fs = s = (*t->strfn)(t);
9535e96a66cSDavid du Colombier 		else
9545e96a66cSDavid du Colombier 			s = "???";
9555e96a66cSDavid du Colombier 		dy = drawtext(s, m, clipr, oo);
9565e96a66cSDavid du Colombier 		free(fs);
9575e96a66cSDavid du Colombier //	}
9585e96a66cSDavid du Colombier 
9595e96a66cSDavid du Colombier 	if(t->expanded){
9605e96a66cSDavid du Colombier 		if(t->nkid == -1 && t->expand)
9615e96a66cSDavid du Colombier 			(*t->expand)(t);
9625e96a66cSDavid du Colombier 		oo = Pt(o.x+Nubwidth+(Linewidth-Nubwidth)/2, o.y+dy);
9635e96a66cSDavid du Colombier 		for(i=0; i<t->nkid; i++)
9645e96a66cSDavid du Colombier 			oo.y += drawnode(t->kid[i], m, clipr, oo);
9655e96a66cSDavid du Colombier 		dy = oo.y - o.y;
9665e96a66cSDavid du Colombier 	}
9675e96a66cSDavid du Colombier 	drawnub(m, clipr, o, t);
9685e96a66cSDavid du Colombier 	return dy;
9695e96a66cSDavid du Colombier }
9705e96a66cSDavid du Colombier 
9715e96a66cSDavid du Colombier void
drawtree(Tree * t,Image * m,Rectangle r)9725e96a66cSDavid du Colombier drawtree(Tree *t, Image *m, Rectangle r)
9735e96a66cSDavid du Colombier {
9745e96a66cSDavid du Colombier 	Point p;
9755e96a66cSDavid du Colombier 
9765e96a66cSDavid du Colombier 	draw(m, r, display->white, nil, ZP);
9775e96a66cSDavid du Colombier 
9785e96a66cSDavid du Colombier 	replclipr(t->clipr, 1, r);
9795e96a66cSDavid du Colombier 	p = addpt(t->offset, r.min);
9805e96a66cSDavid du Colombier 	drawnode(t->root, m, t->clipr, p);
9815e96a66cSDavid du Colombier }
9825e96a66cSDavid du Colombier 
9835e96a66cSDavid du Colombier Tnode*
findnode(Tnode * t,Point p)9845e96a66cSDavid du Colombier findnode(Tnode *t, Point p)
9855e96a66cSDavid du Colombier {
9865e96a66cSDavid du Colombier 	int i;
9875e96a66cSDavid du Colombier 	Tnode *tt;
9885e96a66cSDavid du Colombier 
9895e96a66cSDavid du Colombier 	if(ptinrect(p, rectaddpt(Rect(0,0,Nubwidth, Nubheight), t->offset)))
9905e96a66cSDavid du Colombier 		return t;
9915e96a66cSDavid du Colombier 	if(!t->expanded)
9925e96a66cSDavid du Colombier 		return nil;
9935e96a66cSDavid du Colombier 	for(i=0; i<t->nkid; i++)
9945e96a66cSDavid du Colombier 		if(tt = findnode(t->kid[i], p))
9955e96a66cSDavid du Colombier 			return tt;
9965e96a66cSDavid du Colombier 	return nil;
9975e96a66cSDavid du Colombier }
9985e96a66cSDavid du Colombier 
9995e96a66cSDavid du Colombier void
usage(void)10005e96a66cSDavid du Colombier usage(void)
10015e96a66cSDavid du Colombier {
10025e96a66cSDavid du Colombier 	fprint(2, "usage: vtree /dev/sdC0/fossil\n");
10035e96a66cSDavid du Colombier 	exits("usage");
10045e96a66cSDavid du Colombier }
10055e96a66cSDavid du Colombier 
10065e96a66cSDavid du Colombier Tree t;
10075e96a66cSDavid du Colombier 
10085e96a66cSDavid du Colombier void
eresized(int new)10095e96a66cSDavid du Colombier eresized(int new)
10105e96a66cSDavid du Colombier {
10115e96a66cSDavid du Colombier 	Rectangle r;
10125e96a66cSDavid du Colombier 	r = screen->r;
10135e96a66cSDavid du Colombier 	if(new && getwindow(display, Refnone) < 0)
10145e96a66cSDavid du Colombier 		fprint(2,"can't reattach to window");
10155e96a66cSDavid du Colombier 	drawtree(&t, screen, screen->r);
10165e96a66cSDavid du Colombier }
10175e96a66cSDavid du Colombier 
10185e96a66cSDavid du Colombier enum
10195e96a66cSDavid du Colombier {
10205e96a66cSDavid du Colombier 	Left = 1<<0,
10215e96a66cSDavid du Colombier 	Middle = 1<<1,
10225e96a66cSDavid du Colombier 	Right = 1<<2,
10235e96a66cSDavid du Colombier 
10245e96a66cSDavid du Colombier 	MMenu = 2,
10255e96a66cSDavid du Colombier };
10265e96a66cSDavid du Colombier 
10275e96a66cSDavid du Colombier char *items[] = { "exit", 0 };
10285e96a66cSDavid du Colombier enum { IExit, };
10295e96a66cSDavid du Colombier 
10305e96a66cSDavid du Colombier Menu menu;
10315e96a66cSDavid du Colombier 
10325e96a66cSDavid du Colombier void
main(int argc,char ** argv)10335e96a66cSDavid du Colombier main(int argc, char **argv)
10345e96a66cSDavid du Colombier {
10355e96a66cSDavid du Colombier 	int n;
10365e96a66cSDavid du Colombier 	char *dir;
10375e96a66cSDavid du Colombier 	Event e;
10385e96a66cSDavid du Colombier 	Point op, p;
10395e96a66cSDavid du Colombier 	Tnode *tn;
10405e96a66cSDavid du Colombier 	Mouse m;
10415e96a66cSDavid du Colombier 	int Eready;
10425e96a66cSDavid du Colombier 	Atree *fs;
10435e96a66cSDavid du Colombier 
10445e96a66cSDavid du Colombier 	ARGBEGIN{
10455e96a66cSDavid du Colombier 	case 'a':
10465e96a66cSDavid du Colombier 		showinactive = 1;
10475e96a66cSDavid du Colombier 		break;
10485e96a66cSDavid du Colombier 	default:
10495e96a66cSDavid du Colombier 		usage();
10505e96a66cSDavid du Colombier 	}ARGEND
10515e96a66cSDavid du Colombier 
10525e96a66cSDavid du Colombier 	switch(argc){
10535e96a66cSDavid du Colombier 	default:
10545e96a66cSDavid du Colombier 		usage();
10555e96a66cSDavid du Colombier 	case 1:
10565e96a66cSDavid du Colombier 		dir = argv[0];
10575e96a66cSDavid du Colombier 		break;
10585e96a66cSDavid du Colombier 	}
10595e96a66cSDavid du Colombier 
10605e96a66cSDavid du Colombier 	fs = atreeinit(dir);
10615e96a66cSDavid du Colombier 	initdraw(0, "/lib/font/bit/lucidasans/unicode.8.font", "tree");
10625e96a66cSDavid du Colombier 	t.root = fs->root;
10635e96a66cSDavid du Colombier 	t.offset = ZP;
10645e96a66cSDavid du Colombier 	t.clipr = allocimage(display, Rect(0,0,1,1), GREY1, 1, DOpaque);
10655e96a66cSDavid du Colombier 
10665e96a66cSDavid du Colombier 	eresized(0);
10675e96a66cSDavid du Colombier 	flushimage(display, 1);
10685e96a66cSDavid du Colombier 
10695e96a66cSDavid du Colombier 	einit(Emouse);
10705e96a66cSDavid du Colombier 
10715e96a66cSDavid du Colombier 	menu.item = items;
10725e96a66cSDavid du Colombier 	menu.gen = 0;
10735e96a66cSDavid du Colombier 	menu.lasthit = 0;
10745e96a66cSDavid du Colombier 	if(fs->resizefd > 0){
10755e96a66cSDavid du Colombier 		Eready = 1<<3;
10765e96a66cSDavid du Colombier 		estart(Eready, fs->resizefd, 1);
10775e96a66cSDavid du Colombier 	}else
10785e96a66cSDavid du Colombier 		Eready = 0;
10795e96a66cSDavid du Colombier 
10805e96a66cSDavid du Colombier 	for(;;){
10815e96a66cSDavid du Colombier 		switch(n=eread(Emouse|Eready, &e)){
10825e96a66cSDavid du Colombier 		default:
10835e96a66cSDavid du Colombier 			if(Eready && n==Eready)
10845e96a66cSDavid du Colombier 				eresized(0);
10855e96a66cSDavid du Colombier 			break;
10865e96a66cSDavid du Colombier 		case Emouse:
10875e96a66cSDavid du Colombier 			m = e.mouse;
10885e96a66cSDavid du Colombier 			switch(m.buttons){
10895e96a66cSDavid du Colombier 			case Left:
10905e96a66cSDavid du Colombier 				op = t.offset;
10915e96a66cSDavid du Colombier 				p = m.xy;
10925e96a66cSDavid du Colombier 				do {
10935e96a66cSDavid du Colombier 					t.offset = addpt(t.offset, subpt(m.xy, p));
10945e96a66cSDavid du Colombier 					p = m.xy;
10955e96a66cSDavid du Colombier 					eresized(0);
10965e96a66cSDavid du Colombier 					m = emouse();
10975e96a66cSDavid du Colombier 				}while(m.buttons == Left);
10985e96a66cSDavid du Colombier 				if(m.buttons){
10995e96a66cSDavid du Colombier 					t.offset = op;
11005e96a66cSDavid du Colombier 					eresized(0);
11015e96a66cSDavid du Colombier 				}
11025e96a66cSDavid du Colombier 				break;
11035e96a66cSDavid du Colombier 			case Middle:
11045e96a66cSDavid du Colombier 				n = emenuhit(MMenu, &m, &menu);
11055e96a66cSDavid du Colombier 				if(n == -1)
11065e96a66cSDavid du Colombier 					break;
11075e96a66cSDavid du Colombier 				switch(n){
11085e96a66cSDavid du Colombier 				case IExit:
11095e96a66cSDavid du Colombier 					exits(nil);
11105e96a66cSDavid du Colombier 				}
11115e96a66cSDavid du Colombier 				break;
11125e96a66cSDavid du Colombier 			case Right:
11135e96a66cSDavid du Colombier 				do
11145e96a66cSDavid du Colombier 					m = emouse();
11155e96a66cSDavid du Colombier 				while(m.buttons == Right);
11165e96a66cSDavid du Colombier 				if(m.buttons)
11175e96a66cSDavid du Colombier 					break;
11185e96a66cSDavid du Colombier 				tn = findnode(t.root, m.xy);
11195e96a66cSDavid du Colombier 				if(tn){
11205e96a66cSDavid du Colombier 					tn->expanded = !tn->expanded;
11215e96a66cSDavid du Colombier 					eresized(0);
11225e96a66cSDavid du Colombier 				}
11235e96a66cSDavid du Colombier 				break;
11245e96a66cSDavid du Colombier 			}
11255e96a66cSDavid du Colombier 		}
11265e96a66cSDavid du Colombier 	}
11275e96a66cSDavid du Colombier }
1128