xref: /plan9-contrib/sys/src/cmd/fossil/view.c (revision 5e96a66c77eb9140492ca53f857cbbf108e128ed)
1*5e96a66cSDavid du Colombier #include "stdinc.h"
2*5e96a66cSDavid du Colombier #include "dat.h"
3*5e96a66cSDavid du Colombier #include "fns.h"
4*5e96a66cSDavid du Colombier #include <draw.h>
5*5e96a66cSDavid du Colombier #include <event.h>
6*5e96a66cSDavid du Colombier 
7*5e96a66cSDavid du Colombier /* --- tree.h */
8*5e96a66cSDavid du Colombier typedef struct Tree Tree;
9*5e96a66cSDavid du Colombier typedef struct Tnode Tnode;
10*5e96a66cSDavid du Colombier 
11*5e96a66cSDavid du Colombier struct Tree
12*5e96a66cSDavid du Colombier {
13*5e96a66cSDavid du Colombier 	Tnode *root;
14*5e96a66cSDavid du Colombier 	Point offset;
15*5e96a66cSDavid du Colombier 	Image *clipr;
16*5e96a66cSDavid du Colombier };
17*5e96a66cSDavid du Colombier 
18*5e96a66cSDavid du Colombier struct Tnode
19*5e96a66cSDavid du Colombier {
20*5e96a66cSDavid du Colombier 	Point offset;
21*5e96a66cSDavid du Colombier 
22*5e96a66cSDavid du Colombier 	char *str;
23*5e96a66cSDavid du Colombier //	char *(*strfn)(Tnode*);
24*5e96a66cSDavid du Colombier //	uint (*draw)(Tnode*, Image*, Image*, Point);
25*5e96a66cSDavid du Colombier 	void (*expand)(Tnode*);
26*5e96a66cSDavid du Colombier 	void (*collapse)(Tnode*);
27*5e96a66cSDavid du Colombier 
28*5e96a66cSDavid du Colombier 	uint expanded;
29*5e96a66cSDavid du Colombier 	Tnode **kid;
30*5e96a66cSDavid du Colombier 	int nkid;
31*5e96a66cSDavid du Colombier 	void *aux;
32*5e96a66cSDavid du Colombier };
33*5e96a66cSDavid du Colombier 
34*5e96a66cSDavid du Colombier typedef struct Atree Atree;
35*5e96a66cSDavid du Colombier struct Atree
36*5e96a66cSDavid du Colombier {
37*5e96a66cSDavid du Colombier 	int resizefd;
38*5e96a66cSDavid du Colombier 	Tnode *root;
39*5e96a66cSDavid du Colombier };
40*5e96a66cSDavid du Colombier 
41*5e96a66cSDavid du Colombier Atree *atreeinit(char*);
42*5e96a66cSDavid du Colombier 
43*5e96a66cSDavid du Colombier /* --- visfossil.c */
44*5e96a66cSDavid du Colombier Tnode *initxheader(void);
45*5e96a66cSDavid du Colombier Tnode *initxcache(char *name);
46*5e96a66cSDavid du Colombier Tnode *initxsuper(void);
47*5e96a66cSDavid du Colombier Tnode *initxlocalroot(char *name, u32int addr);
48*5e96a66cSDavid du Colombier Tnode *initxentry(Entry);
49*5e96a66cSDavid du Colombier Tnode *initxsource(Entry, int);
50*5e96a66cSDavid du Colombier Tnode *initxentryblock(Block*, Entry*);
51*5e96a66cSDavid du Colombier Tnode *initxdatablock(Block*, uint);
52*5e96a66cSDavid du Colombier Tnode *initxroot(char *name, uchar[VtScoreSize]);
53*5e96a66cSDavid du Colombier 
54*5e96a66cSDavid du Colombier int fd;
55*5e96a66cSDavid du Colombier Header h;
56*5e96a66cSDavid du Colombier Super super;
57*5e96a66cSDavid du Colombier VtSession *z;
58*5e96a66cSDavid du Colombier VtRoot vac;
59*5e96a66cSDavid du Colombier int showinactive;
60*5e96a66cSDavid du Colombier 
61*5e96a66cSDavid du Colombier /*
62*5e96a66cSDavid du Colombier  * dumbed down versions of fossil routines
63*5e96a66cSDavid du Colombier  */
64*5e96a66cSDavid du Colombier char*
65*5e96a66cSDavid du Colombier bsStr(int state)
66*5e96a66cSDavid du Colombier {
67*5e96a66cSDavid du Colombier 	static char s[100];
68*5e96a66cSDavid du Colombier 
69*5e96a66cSDavid du Colombier 	if(state == BsFree)
70*5e96a66cSDavid du Colombier 		return "Free";
71*5e96a66cSDavid du Colombier 	if(state == BsBad)
72*5e96a66cSDavid du Colombier 		return "Bad";
73*5e96a66cSDavid du Colombier 
74*5e96a66cSDavid du Colombier 	sprint(s, "%x", state);
75*5e96a66cSDavid du Colombier 	if(!(state&BsAlloc))
76*5e96a66cSDavid du Colombier 		strcat(s, ",Free");	/* should not happen */
77*5e96a66cSDavid du Colombier 	if(state&BsCopied)
78*5e96a66cSDavid du Colombier 		strcat(s, ",Copied");
79*5e96a66cSDavid du Colombier 	if(state&BsVenti)
80*5e96a66cSDavid du Colombier 		strcat(s, ",Venti");
81*5e96a66cSDavid du Colombier 	if(state&BsClosed)
82*5e96a66cSDavid du Colombier 		strcat(s, ",Closed");
83*5e96a66cSDavid du Colombier 	return s;
84*5e96a66cSDavid du Colombier }
85*5e96a66cSDavid du Colombier 
86*5e96a66cSDavid du Colombier char *bttab[] = {
87*5e96a66cSDavid du Colombier 	"BtData",
88*5e96a66cSDavid du Colombier 	"BtData+1",
89*5e96a66cSDavid du Colombier 	"BtData+2",
90*5e96a66cSDavid du Colombier 	"BtData+3",
91*5e96a66cSDavid du Colombier 	"BtData+4",
92*5e96a66cSDavid du Colombier 	"BtData+5",
93*5e96a66cSDavid du Colombier 	"BtData+6",
94*5e96a66cSDavid du Colombier 	"BtData+7",
95*5e96a66cSDavid du Colombier 	"BtDir",
96*5e96a66cSDavid du Colombier 	"BtDir+1",
97*5e96a66cSDavid du Colombier 	"BtDir+2",
98*5e96a66cSDavid du Colombier 	"BtDir+3",
99*5e96a66cSDavid du Colombier 	"BtDir+4",
100*5e96a66cSDavid du Colombier 	"BtDir+5",
101*5e96a66cSDavid du Colombier 	"BtDir+6",
102*5e96a66cSDavid du Colombier 	"BtDir+7",
103*5e96a66cSDavid du Colombier };
104*5e96a66cSDavid du Colombier 
105*5e96a66cSDavid du Colombier char*
106*5e96a66cSDavid du Colombier btStr(int type)
107*5e96a66cSDavid du Colombier {
108*5e96a66cSDavid du Colombier 	if(type < nelem(bttab))
109*5e96a66cSDavid du Colombier 		return bttab[type];
110*5e96a66cSDavid du Colombier 	return "unknown";
111*5e96a66cSDavid du Colombier }
112*5e96a66cSDavid du Colombier #pragma varargck argpos stringnode 1
113*5e96a66cSDavid du Colombier 
114*5e96a66cSDavid du Colombier Block*
115*5e96a66cSDavid du Colombier allocBlock(void)
116*5e96a66cSDavid du Colombier {
117*5e96a66cSDavid du Colombier 	Block *b;
118*5e96a66cSDavid du Colombier 
119*5e96a66cSDavid du Colombier 	b = mallocz(sizeof(Block)+h.blockSize, 1);
120*5e96a66cSDavid du Colombier 	b->data = (void*)&b[1];
121*5e96a66cSDavid du Colombier 	return b;
122*5e96a66cSDavid du Colombier }
123*5e96a66cSDavid du Colombier 
124*5e96a66cSDavid du Colombier void
125*5e96a66cSDavid du Colombier blockPut(Block *b)
126*5e96a66cSDavid du Colombier {
127*5e96a66cSDavid du Colombier 	free(b);
128*5e96a66cSDavid du Colombier }
129*5e96a66cSDavid du Colombier 
130*5e96a66cSDavid du Colombier static u32int
131*5e96a66cSDavid du Colombier partStart(int part)
132*5e96a66cSDavid du Colombier {
133*5e96a66cSDavid du Colombier 	switch(part){
134*5e96a66cSDavid du Colombier 	default:
135*5e96a66cSDavid du Colombier 		assert(0);
136*5e96a66cSDavid du Colombier 	case PartSuper:
137*5e96a66cSDavid du Colombier 		return h.super;
138*5e96a66cSDavid du Colombier 	case PartLabel:
139*5e96a66cSDavid du Colombier 		return h.label;
140*5e96a66cSDavid du Colombier 	case PartData:
141*5e96a66cSDavid du Colombier 		return h.data;
142*5e96a66cSDavid du Colombier 	}
143*5e96a66cSDavid du Colombier }
144*5e96a66cSDavid du Colombier 
145*5e96a66cSDavid du Colombier 
146*5e96a66cSDavid du Colombier static u32int
147*5e96a66cSDavid du Colombier partEnd(int part)
148*5e96a66cSDavid du Colombier {
149*5e96a66cSDavid du Colombier 	switch(part){
150*5e96a66cSDavid du Colombier 	default:
151*5e96a66cSDavid du Colombier 		assert(0);
152*5e96a66cSDavid du Colombier 	case PartSuper:
153*5e96a66cSDavid du Colombier 		return h.super+1;
154*5e96a66cSDavid du Colombier 	case PartLabel:
155*5e96a66cSDavid du Colombier 		return h.data;
156*5e96a66cSDavid du Colombier 	case PartData:
157*5e96a66cSDavid du Colombier 		return h.end;
158*5e96a66cSDavid du Colombier 	}
159*5e96a66cSDavid du Colombier }
160*5e96a66cSDavid du Colombier 
161*5e96a66cSDavid du Colombier Block*
162*5e96a66cSDavid du Colombier readBlock(int part, u32int addr)
163*5e96a66cSDavid du Colombier {
164*5e96a66cSDavid du Colombier 	u32int start, end;
165*5e96a66cSDavid du Colombier 	u64int offset;
166*5e96a66cSDavid du Colombier 	int n, nn;
167*5e96a66cSDavid du Colombier 	Block *b;
168*5e96a66cSDavid du Colombier 	uchar *buf;
169*5e96a66cSDavid du Colombier 
170*5e96a66cSDavid du Colombier 	start = partStart(part);
171*5e96a66cSDavid du Colombier 	end = partEnd(part);
172*5e96a66cSDavid du Colombier 	if(addr >= end-start){
173*5e96a66cSDavid du Colombier 		werrstr("bad addr 0x%.8ux; wanted 0x%.8ux - 0x%.8ux", addr, start, end);
174*5e96a66cSDavid du Colombier 		return nil;
175*5e96a66cSDavid du Colombier 	}
176*5e96a66cSDavid du Colombier 
177*5e96a66cSDavid du Colombier 	b = allocBlock();
178*5e96a66cSDavid du Colombier 	b->addr = addr;
179*5e96a66cSDavid du Colombier 	buf = b->data;
180*5e96a66cSDavid du Colombier 	offset = ((u64int)(addr+start))*h.blockSize;
181*5e96a66cSDavid du Colombier 	n = h.blockSize;
182*5e96a66cSDavid du Colombier 	while(n > 0){
183*5e96a66cSDavid du Colombier 		nn = pread(fd, buf, n, offset);
184*5e96a66cSDavid du Colombier 		if(nn < 0){
185*5e96a66cSDavid du Colombier 			blockPut(b);
186*5e96a66cSDavid du Colombier 			return nil;
187*5e96a66cSDavid du Colombier 		}
188*5e96a66cSDavid du Colombier 		if(nn == 0){
189*5e96a66cSDavid du Colombier 			werrstr("short read");
190*5e96a66cSDavid du Colombier 			blockPut(b);
191*5e96a66cSDavid du Colombier 			return nil;
192*5e96a66cSDavid du Colombier 		}
193*5e96a66cSDavid du Colombier 		n -= nn;
194*5e96a66cSDavid du Colombier 		offset += nn;
195*5e96a66cSDavid du Colombier 		buf += nn;
196*5e96a66cSDavid du Colombier 	}
197*5e96a66cSDavid du Colombier 	return b;
198*5e96a66cSDavid du Colombier }
199*5e96a66cSDavid du Colombier 
200*5e96a66cSDavid du Colombier int vtType[BtMax] = {
201*5e96a66cSDavid du Colombier 	VtDataType,		/* BtData | 0  */
202*5e96a66cSDavid du Colombier 	VtPointerType0,		/* BtData | 1  */
203*5e96a66cSDavid du Colombier 	VtPointerType1,		/* BtData | 2  */
204*5e96a66cSDavid du Colombier 	VtPointerType2,		/* BtData | 3  */
205*5e96a66cSDavid du Colombier 	VtPointerType3,		/* BtData | 4  */
206*5e96a66cSDavid du Colombier 	VtPointerType4,		/* BtData | 5  */
207*5e96a66cSDavid du Colombier 	VtPointerType5,		/* BtData | 6  */
208*5e96a66cSDavid du Colombier 	VtPointerType6,		/* BtData | 7  */
209*5e96a66cSDavid du Colombier 	VtDirType,		/* BtDir | 0  */
210*5e96a66cSDavid du Colombier 	VtPointerType0,		/* BtDir | 1  */
211*5e96a66cSDavid du Colombier 	VtPointerType1,		/* BtDir | 2  */
212*5e96a66cSDavid du Colombier 	VtPointerType2,		/* BtDir | 3  */
213*5e96a66cSDavid du Colombier 	VtPointerType3,		/* BtDir | 4  */
214*5e96a66cSDavid du Colombier 	VtPointerType4,		/* BtDir | 5  */
215*5e96a66cSDavid du Colombier 	VtPointerType5,		/* BtDir | 6  */
216*5e96a66cSDavid du Colombier 	VtPointerType6,		/* BtDir | 7  */
217*5e96a66cSDavid du Colombier };
218*5e96a66cSDavid du Colombier 
219*5e96a66cSDavid du Colombier Block*
220*5e96a66cSDavid du Colombier ventiBlock(uchar score[VtScoreSize], uint type)
221*5e96a66cSDavid du Colombier {
222*5e96a66cSDavid du Colombier 	int n;
223*5e96a66cSDavid du Colombier 	Block *b;
224*5e96a66cSDavid du Colombier 
225*5e96a66cSDavid du Colombier 	b = allocBlock();
226*5e96a66cSDavid du Colombier 	memmove(b->score, score, VtScoreSize);
227*5e96a66cSDavid du Colombier 	b->addr = NilBlock;
228*5e96a66cSDavid du Colombier 
229*5e96a66cSDavid du Colombier 	n = vtRead(z, b->score, vtType[type], b->data, h.blockSize);
230*5e96a66cSDavid du Colombier 	if(n < 0){
231*5e96a66cSDavid du Colombier 		fprint(2, "vtRead returns %d: %R\n", n);
232*5e96a66cSDavid du Colombier 		blockPut(b);
233*5e96a66cSDavid du Colombier 		return nil;
234*5e96a66cSDavid du Colombier 	}
235*5e96a66cSDavid du Colombier 	vtZeroExtend(vtType[type], b->data, n, h.blockSize);
236*5e96a66cSDavid du Colombier 	b->l.type = type;
237*5e96a66cSDavid du Colombier 	b->l.state = 0;
238*5e96a66cSDavid du Colombier 	b->l.tag = 0;
239*5e96a66cSDavid du Colombier 	b->l.epoch = 0;
240*5e96a66cSDavid du Colombier 	return b;
241*5e96a66cSDavid du Colombier }
242*5e96a66cSDavid du Colombier 
243*5e96a66cSDavid du Colombier Block*
244*5e96a66cSDavid du Colombier dataBlock(uchar score[VtScoreSize], uint type, uint tag)
245*5e96a66cSDavid du Colombier {
246*5e96a66cSDavid du Colombier 	Block *b, *bl;
247*5e96a66cSDavid du Colombier 	int lpb;
248*5e96a66cSDavid du Colombier 	Label l;
249*5e96a66cSDavid du Colombier 	u32int addr;
250*5e96a66cSDavid du Colombier 
251*5e96a66cSDavid du Colombier 	addr = globalToLocal(score);
252*5e96a66cSDavid du Colombier 	if(addr == NilBlock)
253*5e96a66cSDavid du Colombier 		return ventiBlock(score, type);
254*5e96a66cSDavid du Colombier 
255*5e96a66cSDavid du Colombier 	lpb = h.blockSize/LabelSize;
256*5e96a66cSDavid du Colombier 	bl = readBlock(PartLabel, addr/lpb);
257*5e96a66cSDavid du Colombier 	if(bl == nil)
258*5e96a66cSDavid du Colombier 		return nil;
259*5e96a66cSDavid du Colombier 	if(!labelUnpack(&l, bl->data, addr%lpb)){
260*5e96a66cSDavid du Colombier 		werrstr("%R");
261*5e96a66cSDavid du Colombier 		blockPut(bl);
262*5e96a66cSDavid du Colombier 		return nil;
263*5e96a66cSDavid du Colombier 	}
264*5e96a66cSDavid du Colombier 	blockPut(bl);
265*5e96a66cSDavid du Colombier 	if(l.type != type){
266*5e96a66cSDavid du Colombier 		werrstr("type mismatch; got %d (%s) wanted %d (%s)",
267*5e96a66cSDavid du Colombier 			l.type, btStr(l.type), type, btStr(type));
268*5e96a66cSDavid du Colombier 		return nil;
269*5e96a66cSDavid du Colombier 	}
270*5e96a66cSDavid du Colombier 	if(tag && l.tag != tag){
271*5e96a66cSDavid du Colombier 		werrstr("tag mismatch; got 0x%.8ux wanted 0x%.8ux",
272*5e96a66cSDavid du Colombier 			l.tag, tag);
273*5e96a66cSDavid du Colombier 		return nil;
274*5e96a66cSDavid du Colombier 	}
275*5e96a66cSDavid du Colombier 	b = readBlock(PartData, addr);
276*5e96a66cSDavid du Colombier 	if(b == nil)
277*5e96a66cSDavid du Colombier 		return nil;
278*5e96a66cSDavid du Colombier 	b->l = l;
279*5e96a66cSDavid du Colombier 	return b;
280*5e96a66cSDavid du Colombier }
281*5e96a66cSDavid du Colombier 
282*5e96a66cSDavid du Colombier Entry*
283*5e96a66cSDavid du Colombier copyEntry(Entry e)
284*5e96a66cSDavid du Colombier {
285*5e96a66cSDavid du Colombier 	Entry *p;
286*5e96a66cSDavid du Colombier 
287*5e96a66cSDavid du Colombier 	p = mallocz(sizeof *p, 1);
288*5e96a66cSDavid du Colombier 	*p = e;
289*5e96a66cSDavid du Colombier 	return p;
290*5e96a66cSDavid du Colombier }
291*5e96a66cSDavid du Colombier 
292*5e96a66cSDavid du Colombier MetaBlock*
293*5e96a66cSDavid du Colombier copyMetaBlock(MetaBlock mb)
294*5e96a66cSDavid du Colombier {
295*5e96a66cSDavid du Colombier 	MetaBlock *p;
296*5e96a66cSDavid du Colombier 
297*5e96a66cSDavid du Colombier 	p = mallocz(sizeof mb, 1);
298*5e96a66cSDavid du Colombier 	*p = mb;
299*5e96a66cSDavid du Colombier 	return p;
300*5e96a66cSDavid du Colombier }
301*5e96a66cSDavid du Colombier 
302*5e96a66cSDavid du Colombier /*
303*5e96a66cSDavid du Colombier  * visualizer
304*5e96a66cSDavid du Colombier  */
305*5e96a66cSDavid du Colombier 
306*5e96a66cSDavid du Colombier Tnode*
307*5e96a66cSDavid du Colombier stringnode(char *fmt, ...)
308*5e96a66cSDavid du Colombier {
309*5e96a66cSDavid du Colombier 	va_list arg;
310*5e96a66cSDavid du Colombier 	Tnode *t;
311*5e96a66cSDavid du Colombier 
312*5e96a66cSDavid du Colombier 	t = mallocz(sizeof(Tnode), 1);
313*5e96a66cSDavid du Colombier 	va_start(arg, fmt);
314*5e96a66cSDavid du Colombier 	t->str = vsmprint(fmt, arg);
315*5e96a66cSDavid du Colombier 	va_end(arg);
316*5e96a66cSDavid du Colombier 	t->nkid = -1;
317*5e96a66cSDavid du Colombier 	return t;
318*5e96a66cSDavid du Colombier }
319*5e96a66cSDavid du Colombier 
320*5e96a66cSDavid du Colombier void
321*5e96a66cSDavid du Colombier xcacheexpand(Tnode *t)
322*5e96a66cSDavid du Colombier {
323*5e96a66cSDavid du Colombier 	if(t->nkid >= 0)
324*5e96a66cSDavid du Colombier 		return;
325*5e96a66cSDavid du Colombier 
326*5e96a66cSDavid du Colombier 	t->nkid = 1;
327*5e96a66cSDavid du Colombier 	t->kid = mallocz(sizeof(t->kid[0])*t->nkid, 1);
328*5e96a66cSDavid du Colombier 	t->kid[0] = initxheader();
329*5e96a66cSDavid du Colombier }
330*5e96a66cSDavid du Colombier 
331*5e96a66cSDavid du Colombier Tnode*
332*5e96a66cSDavid du Colombier initxcache(char *name)
333*5e96a66cSDavid du Colombier {
334*5e96a66cSDavid du Colombier 	Tnode *t;
335*5e96a66cSDavid du Colombier 
336*5e96a66cSDavid du Colombier 	if((fd = open(name, OREAD)) < 0)
337*5e96a66cSDavid du Colombier 		sysfatal("cannot open %s: %r", name);
338*5e96a66cSDavid du Colombier 
339*5e96a66cSDavid du Colombier 	t = stringnode("%s", name);
340*5e96a66cSDavid du Colombier 	t->expand = xcacheexpand;
341*5e96a66cSDavid du Colombier 	return t;
342*5e96a66cSDavid du Colombier }
343*5e96a66cSDavid du Colombier 
344*5e96a66cSDavid du Colombier void
345*5e96a66cSDavid du Colombier xheaderexpand(Tnode *t)
346*5e96a66cSDavid du Colombier {
347*5e96a66cSDavid du Colombier 	if(t->nkid >= 0)
348*5e96a66cSDavid du Colombier 		return;
349*5e96a66cSDavid du Colombier 
350*5e96a66cSDavid du Colombier 	t->nkid = 1;
351*5e96a66cSDavid du Colombier 	t->kid = mallocz(sizeof(t->kid[0])*t->nkid, 1);
352*5e96a66cSDavid du Colombier 	t->kid[0] = initxsuper();
353*5e96a66cSDavid du Colombier 	//t->kid[1] = initxlabel(h.label);
354*5e96a66cSDavid du Colombier 	//t->kid[2] = initxdata(h.data);
355*5e96a66cSDavid du Colombier }
356*5e96a66cSDavid du Colombier 
357*5e96a66cSDavid du Colombier Tnode*
358*5e96a66cSDavid du Colombier initxheader(void)
359*5e96a66cSDavid du Colombier {
360*5e96a66cSDavid du Colombier 	u8int buf[HeaderSize];
361*5e96a66cSDavid du Colombier 	Tnode *t;
362*5e96a66cSDavid du Colombier 
363*5e96a66cSDavid du Colombier 	if(pread(fd, buf, HeaderSize, HeaderOffset) < HeaderSize)
364*5e96a66cSDavid du Colombier 		return stringnode("error reading header: %r");
365*5e96a66cSDavid du Colombier 	if(!headerUnpack(&h, buf))
366*5e96a66cSDavid du Colombier 		return stringnode("error unpacking header: %R");
367*5e96a66cSDavid du Colombier 
368*5e96a66cSDavid du Colombier 	t = stringnode("header "
369*5e96a66cSDavid du Colombier 		"version=%#ux (%d) "
370*5e96a66cSDavid du Colombier 		"blockSize=%#ux (%d) "
371*5e96a66cSDavid du Colombier 		"super=%#lux (%ld) "
372*5e96a66cSDavid du Colombier 		"label=%#lux (%ld) "
373*5e96a66cSDavid du Colombier 		"data=%#lux (%ld) "
374*5e96a66cSDavid du Colombier 		"end=%#lux (%ld)",
375*5e96a66cSDavid du Colombier 		h.version, h.version, h.blockSize, h.blockSize,
376*5e96a66cSDavid du Colombier 		h.super, h.super,
377*5e96a66cSDavid du Colombier 		h.label, h.label, h.data, h.data, h.end, h.end);
378*5e96a66cSDavid du Colombier 	t->expand = xheaderexpand;
379*5e96a66cSDavid du Colombier 	return t;
380*5e96a66cSDavid du Colombier }
381*5e96a66cSDavid du Colombier 
382*5e96a66cSDavid du Colombier void
383*5e96a66cSDavid du Colombier xsuperexpand(Tnode *t)
384*5e96a66cSDavid du Colombier {
385*5e96a66cSDavid du Colombier 	if(t->nkid >= 0)
386*5e96a66cSDavid du Colombier 		return;
387*5e96a66cSDavid du Colombier 
388*5e96a66cSDavid du Colombier 	t->nkid = 1;
389*5e96a66cSDavid du Colombier 	t->kid = mallocz(sizeof(t->kid[0])*t->nkid, 1);
390*5e96a66cSDavid du Colombier 	t->kid[0] = initxlocalroot("active", super.active);
391*5e96a66cSDavid du Colombier //	t->kid[1] = initxlocalroot("next", super.next);
392*5e96a66cSDavid du Colombier //	t->kid[2] = initxlocalroot("current", super.current);
393*5e96a66cSDavid du Colombier }
394*5e96a66cSDavid du Colombier 
395*5e96a66cSDavid du Colombier Tnode*
396*5e96a66cSDavid du Colombier initxsuper(void)
397*5e96a66cSDavid du Colombier {
398*5e96a66cSDavid du Colombier 	Block *b;
399*5e96a66cSDavid du Colombier 	Tnode *t;
400*5e96a66cSDavid du Colombier 
401*5e96a66cSDavid du Colombier 	b = readBlock(PartSuper, 0);
402*5e96a66cSDavid du Colombier 	if(b == nil)
403*5e96a66cSDavid du Colombier 		return stringnode("reading super: %r");
404*5e96a66cSDavid du Colombier 	if(!superUnpack(&super, b->data)){
405*5e96a66cSDavid du Colombier 		blockPut(b);
406*5e96a66cSDavid du Colombier 		return stringnode("unpacking super: %R");
407*5e96a66cSDavid du Colombier 	}
408*5e96a66cSDavid du Colombier 	blockPut(b);
409*5e96a66cSDavid du Colombier 	t = stringnode("super "
410*5e96a66cSDavid du Colombier 		"version=%#ux "
411*5e96a66cSDavid du Colombier 		"epoch=[%#ux,%#ux) "
412*5e96a66cSDavid du Colombier 		"qid=%#llux "
413*5e96a66cSDavid du Colombier 		"active=%#x "
414*5e96a66cSDavid du Colombier 		"next=%#x "
415*5e96a66cSDavid du Colombier 		"current=%#x "
416*5e96a66cSDavid du Colombier 		"last=%V "
417*5e96a66cSDavid du Colombier 		"name=%s",
418*5e96a66cSDavid du Colombier 		super.version, super.epochLow, super.epochHigh,
419*5e96a66cSDavid du Colombier 		super.qid, super.active, super.next, super.current,
420*5e96a66cSDavid du Colombier 		super.last, super.name);
421*5e96a66cSDavid du Colombier 	t->expand = xsuperexpand;
422*5e96a66cSDavid du Colombier 	return t;
423*5e96a66cSDavid du Colombier }
424*5e96a66cSDavid du Colombier 
425*5e96a66cSDavid du Colombier void
426*5e96a66cSDavid du Colombier xvacrootexpand(Tnode *t)
427*5e96a66cSDavid du Colombier {
428*5e96a66cSDavid du Colombier 	if(t->nkid >= 0)
429*5e96a66cSDavid du Colombier 		return;
430*5e96a66cSDavid du Colombier 
431*5e96a66cSDavid du Colombier 	t->nkid = 1;
432*5e96a66cSDavid du Colombier 	t->kid = mallocz(sizeof(t->kid[0])*t->nkid, 1);
433*5e96a66cSDavid du Colombier 	t->kid[0] = initxroot("root", vac.score);
434*5e96a66cSDavid du Colombier }
435*5e96a66cSDavid du Colombier 
436*5e96a66cSDavid du Colombier Tnode*
437*5e96a66cSDavid du Colombier initxvacroot(uchar score[VtScoreSize])
438*5e96a66cSDavid du Colombier {
439*5e96a66cSDavid du Colombier 	Tnode *t;
440*5e96a66cSDavid du Colombier 	uchar buf[VtRootSize];
441*5e96a66cSDavid du Colombier 	int n;
442*5e96a66cSDavid du Colombier 
443*5e96a66cSDavid du Colombier 	if((n = vtRead(z, score, VtRootType, buf, VtRootSize)) < 0)
444*5e96a66cSDavid du Colombier 		return stringnode("reading root %V: %R", score);
445*5e96a66cSDavid du Colombier 
446*5e96a66cSDavid du Colombier 	if(!vtRootUnpack(&vac, buf))
447*5e96a66cSDavid du Colombier 		return stringnode("unpack %d-byte root: %R", n);
448*5e96a66cSDavid du Colombier 
449*5e96a66cSDavid du Colombier 	h.blockSize = vac.blockSize;
450*5e96a66cSDavid du Colombier 	t = stringnode("vac version=%#ux name=%s type=%s blockSize=%ud score=%V prev=%V",
451*5e96a66cSDavid du Colombier 		vac.version, vac.name, vac.type, vac.blockSize, vac.score, vac.prev);
452*5e96a66cSDavid du Colombier 	t->expand = xvacrootexpand;
453*5e96a66cSDavid du Colombier 	return t;
454*5e96a66cSDavid du Colombier }
455*5e96a66cSDavid du Colombier 
456*5e96a66cSDavid du Colombier Tnode*
457*5e96a66cSDavid du Colombier initxlabel(Label l)
458*5e96a66cSDavid du Colombier {
459*5e96a66cSDavid du Colombier 	return stringnode("label type=%s state=%s epoch=%#ux tag=%#ux",
460*5e96a66cSDavid du Colombier 		btStr(l.type), bsStr(l.state), l.epoch, l.tag);
461*5e96a66cSDavid du Colombier }
462*5e96a66cSDavid du Colombier 
463*5e96a66cSDavid du Colombier typedef struct Xblock Xblock;
464*5e96a66cSDavid du Colombier struct Xblock
465*5e96a66cSDavid du Colombier {
466*5e96a66cSDavid du Colombier 	Tnode;
467*5e96a66cSDavid du Colombier 	Block *b;
468*5e96a66cSDavid du Colombier 	int (*gen)(void*, Block*, int, Tnode**);
469*5e96a66cSDavid du Colombier 	void *arg;
470*5e96a66cSDavid du Colombier 	int printlabel;
471*5e96a66cSDavid du Colombier };
472*5e96a66cSDavid du Colombier 
473*5e96a66cSDavid du Colombier void
474*5e96a66cSDavid du Colombier xblockexpand(Tnode *tt)
475*5e96a66cSDavid du Colombier {
476*5e96a66cSDavid du Colombier 	int i, j;
477*5e96a66cSDavid du Colombier 	enum { Q = 32 };
478*5e96a66cSDavid du Colombier 	Xblock *t = (Xblock*)tt;
479*5e96a66cSDavid du Colombier 	Tnode *nn;
480*5e96a66cSDavid du Colombier 
481*5e96a66cSDavid du Colombier 	if(t->nkid >= 0)
482*5e96a66cSDavid du Colombier 		return;
483*5e96a66cSDavid du Colombier 
484*5e96a66cSDavid du Colombier 	j = 0;
485*5e96a66cSDavid du Colombier 	if(t->printlabel){
486*5e96a66cSDavid du Colombier 		t->kid = mallocz(Q*sizeof(t->kid[0]), 1);
487*5e96a66cSDavid du Colombier 		t->kid[0] = initxlabel(t->b->l);
488*5e96a66cSDavid du Colombier 		j = 1;
489*5e96a66cSDavid du Colombier 	}
490*5e96a66cSDavid du Colombier 
491*5e96a66cSDavid du Colombier 	for(i=0;; i++){
492*5e96a66cSDavid du Colombier 		switch((*t->gen)(t->arg, t->b, i, &nn)){
493*5e96a66cSDavid du Colombier 		case -1:
494*5e96a66cSDavid du Colombier 			t->nkid = j;
495*5e96a66cSDavid du Colombier 			return;
496*5e96a66cSDavid du Colombier 		case 0:
497*5e96a66cSDavid du Colombier 			break;
498*5e96a66cSDavid du Colombier 		case 1:
499*5e96a66cSDavid du Colombier 			if(j%Q == 0)
500*5e96a66cSDavid du Colombier 				t->kid = realloc(t->kid, (j+Q)*sizeof(t->kid[0]));
501*5e96a66cSDavid du Colombier 			t->kid[j++] = nn;
502*5e96a66cSDavid du Colombier 			break;
503*5e96a66cSDavid du Colombier 		}
504*5e96a66cSDavid du Colombier 	}
505*5e96a66cSDavid du Colombier }
506*5e96a66cSDavid du Colombier 
507*5e96a66cSDavid du Colombier int
508*5e96a66cSDavid du Colombier nilgen(void*, Block*, int, Tnode**)
509*5e96a66cSDavid du Colombier {
510*5e96a66cSDavid du Colombier 	return -1;
511*5e96a66cSDavid du Colombier }
512*5e96a66cSDavid du Colombier 
513*5e96a66cSDavid du Colombier Tnode*
514*5e96a66cSDavid du Colombier initxblock(Block *b, char *s, int (*gen)(void*, Block*, int, Tnode**), void *arg)
515*5e96a66cSDavid du Colombier {
516*5e96a66cSDavid du Colombier 	Xblock *t;
517*5e96a66cSDavid du Colombier 
518*5e96a66cSDavid du Colombier 	if(gen == nil)
519*5e96a66cSDavid du Colombier 		gen = nilgen;
520*5e96a66cSDavid du Colombier 	t = mallocz(sizeof(Xblock), 1);
521*5e96a66cSDavid du Colombier 	t->b = b;
522*5e96a66cSDavid du Colombier 	t->gen = gen;
523*5e96a66cSDavid du Colombier 	t->arg = arg;
524*5e96a66cSDavid du Colombier 	if(b->addr == NilBlock)
525*5e96a66cSDavid du Colombier 		t->str = smprint("Block %V: %s", b->score, s);
526*5e96a66cSDavid du Colombier 	else
527*5e96a66cSDavid du Colombier 		t->str = smprint("Block %#ux: %s", b->addr, s);
528*5e96a66cSDavid du Colombier 	t->printlabel = 1;
529*5e96a66cSDavid du Colombier 	t->nkid = -1;
530*5e96a66cSDavid du Colombier 	t->expand = xblockexpand;
531*5e96a66cSDavid du Colombier 	return t;
532*5e96a66cSDavid du Colombier }
533*5e96a66cSDavid du Colombier 
534*5e96a66cSDavid du Colombier int
535*5e96a66cSDavid du Colombier xentrygen(void *v, Block *b, int o, Tnode **tp)
536*5e96a66cSDavid du Colombier {
537*5e96a66cSDavid du Colombier 	Entry e;
538*5e96a66cSDavid du Colombier 	Entry *ed;
539*5e96a66cSDavid du Colombier 
540*5e96a66cSDavid du Colombier 	ed = v;
541*5e96a66cSDavid du Colombier 	if(o >= ed->dsize/VtEntrySize)
542*5e96a66cSDavid du Colombier 		return -1;
543*5e96a66cSDavid du Colombier 
544*5e96a66cSDavid du Colombier 	entryUnpack(&e, b->data, o);
545*5e96a66cSDavid du Colombier 	if(!showinactive && !(e.flags & VtEntryActive))
546*5e96a66cSDavid du Colombier 		return 0;
547*5e96a66cSDavid du Colombier 	*tp = initxentry(e);
548*5e96a66cSDavid du Colombier 	return 1;
549*5e96a66cSDavid du Colombier }
550*5e96a66cSDavid du Colombier 
551*5e96a66cSDavid du Colombier Tnode*
552*5e96a66cSDavid du Colombier initxentryblock(Block *b, Entry *ed)
553*5e96a66cSDavid du Colombier {
554*5e96a66cSDavid du Colombier 	return initxblock(b, "entry", xentrygen, ed);
555*5e96a66cSDavid du Colombier }
556*5e96a66cSDavid du Colombier 
557*5e96a66cSDavid du Colombier typedef struct Xentry Xentry;
558*5e96a66cSDavid du Colombier struct Xentry
559*5e96a66cSDavid du Colombier {
560*5e96a66cSDavid du Colombier 	Tnode;
561*5e96a66cSDavid du Colombier 	Entry e;
562*5e96a66cSDavid du Colombier };
563*5e96a66cSDavid du Colombier 
564*5e96a66cSDavid du Colombier void
565*5e96a66cSDavid du Colombier xentryexpand(Tnode *tt)
566*5e96a66cSDavid du Colombier {
567*5e96a66cSDavid du Colombier 	Xentry *t = (Xentry*)tt;
568*5e96a66cSDavid du Colombier 
569*5e96a66cSDavid du Colombier 	if(t->nkid >= 0)
570*5e96a66cSDavid du Colombier 		return;
571*5e96a66cSDavid du Colombier 
572*5e96a66cSDavid du Colombier 	t->nkid = 1;
573*5e96a66cSDavid du Colombier 	t->kid = mallocz(sizeof(t->kid[0])*t->nkid, 1);
574*5e96a66cSDavid du Colombier 	t->kid[0] = initxsource(t->e, 1);
575*5e96a66cSDavid du Colombier }
576*5e96a66cSDavid du Colombier 
577*5e96a66cSDavid du Colombier Tnode*
578*5e96a66cSDavid du Colombier initxentry(Entry e)
579*5e96a66cSDavid du Colombier {
580*5e96a66cSDavid du Colombier 	Xentry *t;
581*5e96a66cSDavid du Colombier 
582*5e96a66cSDavid du Colombier 	t = mallocz(sizeof *t, 1);
583*5e96a66cSDavid du Colombier 	t->nkid = -1;
584*5e96a66cSDavid du Colombier 	t->str = smprint("Entry gen=%#ux psize=%d dsize=%d depth=%d flags=%#ux size=%lld score=%V",
585*5e96a66cSDavid du Colombier 		e.gen, e.psize, e.dsize, e.depth, e.flags, e.size, e.score);
586*5e96a66cSDavid du Colombier 	if(e.flags & VtEntryLocal)
587*5e96a66cSDavid du Colombier 		t->str = smprint("%s archive=%d snap=%d tag=%#ux", t->str, e.archive, e.snap, e.tag);
588*5e96a66cSDavid du Colombier 	t->expand = xentryexpand;
589*5e96a66cSDavid du Colombier 	t->e = e;
590*5e96a66cSDavid du Colombier 	return t;
591*5e96a66cSDavid du Colombier }
592*5e96a66cSDavid du Colombier 
593*5e96a66cSDavid du Colombier int
594*5e96a66cSDavid du Colombier ptrgen(void *v, Block *b, int o, Tnode **tp)
595*5e96a66cSDavid du Colombier {
596*5e96a66cSDavid du Colombier 	Entry *ed;
597*5e96a66cSDavid du Colombier 	Entry e;
598*5e96a66cSDavid du Colombier 
599*5e96a66cSDavid du Colombier 	ed = v;
600*5e96a66cSDavid du Colombier 	if(o >= ed->psize/VtScoreSize)
601*5e96a66cSDavid du Colombier 		return -1;
602*5e96a66cSDavid du Colombier 
603*5e96a66cSDavid du Colombier 	e = *ed;
604*5e96a66cSDavid du Colombier 	e.depth--;
605*5e96a66cSDavid du Colombier 	memmove(e.score, b->data+o*VtScoreSize, VtScoreSize);
606*5e96a66cSDavid du Colombier 	if(memcmp(e.score, vtZeroScore, VtScoreSize) == 0)
607*5e96a66cSDavid du Colombier 		return 0;
608*5e96a66cSDavid du Colombier 	*tp = initxsource(e, 0);
609*5e96a66cSDavid du Colombier 	return 1;
610*5e96a66cSDavid du Colombier }
611*5e96a66cSDavid du Colombier 
612*5e96a66cSDavid du Colombier static int
613*5e96a66cSDavid du Colombier etype(int flags, int depth)
614*5e96a66cSDavid du Colombier {
615*5e96a66cSDavid du Colombier 	uint t;
616*5e96a66cSDavid du Colombier 
617*5e96a66cSDavid du Colombier 	if(flags&VtEntryDir)
618*5e96a66cSDavid du Colombier 		t = BtDir;
619*5e96a66cSDavid du Colombier 	else
620*5e96a66cSDavid du Colombier 		t = BtData;
621*5e96a66cSDavid du Colombier 	return t+depth;
622*5e96a66cSDavid du Colombier }
623*5e96a66cSDavid du Colombier 
624*5e96a66cSDavid du Colombier Tnode*
625*5e96a66cSDavid du Colombier initxsource(Entry e, int dowrap)
626*5e96a66cSDavid du Colombier {
627*5e96a66cSDavid du Colombier 	Block *b;
628*5e96a66cSDavid du Colombier 	Tnode *t, *tt;
629*5e96a66cSDavid du Colombier 
630*5e96a66cSDavid du Colombier 	b = dataBlock(e.score, etype(e.flags, e.depth), e.tag);
631*5e96a66cSDavid du Colombier 	if(b == nil)
632*5e96a66cSDavid du Colombier 		return stringnode("dataBlock: %r");
633*5e96a66cSDavid du Colombier 
634*5e96a66cSDavid du Colombier 	if((e.flags & VtEntryActive) == 0)
635*5e96a66cSDavid du Colombier 		return stringnode("inactive Entry");
636*5e96a66cSDavid du Colombier 
637*5e96a66cSDavid du Colombier 	if(e.depth == 0){
638*5e96a66cSDavid du Colombier 		if(e.flags & VtEntryDir)
639*5e96a66cSDavid du Colombier 			tt = initxentryblock(b, copyEntry(e));
640*5e96a66cSDavid du Colombier 		else
641*5e96a66cSDavid du Colombier 			tt = initxdatablock(b, e.dsize);
642*5e96a66cSDavid du Colombier 	}else{
643*5e96a66cSDavid du Colombier 		tt = initxblock(b, smprint("%s+%d pointer", (e.flags & VtEntryDir) ? "BtDir" : "BtData", e.depth),
644*5e96a66cSDavid du Colombier 			ptrgen, copyEntry(e));
645*5e96a66cSDavid du Colombier 	}
646*5e96a66cSDavid du Colombier 
647*5e96a66cSDavid du Colombier 	/*
648*5e96a66cSDavid du Colombier 	 * wrap the contents of the Source in a Source node,
649*5e96a66cSDavid du Colombier 	 * just so it's closer to what you see in the code.
650*5e96a66cSDavid du Colombier 	 */
651*5e96a66cSDavid du Colombier 	if(dowrap){
652*5e96a66cSDavid du Colombier 		t = stringnode("Source");
653*5e96a66cSDavid du Colombier 		t->nkid = 1;
654*5e96a66cSDavid du Colombier 		t->kid = mallocz(sizeof(Tnode*)*1, 1);
655*5e96a66cSDavid du Colombier 		t->kid[0] = tt;
656*5e96a66cSDavid du Colombier 		tt = t;
657*5e96a66cSDavid du Colombier 	}
658*5e96a66cSDavid du Colombier 	return tt;
659*5e96a66cSDavid du Colombier }
660*5e96a66cSDavid du Colombier 
661*5e96a66cSDavid du Colombier int
662*5e96a66cSDavid du Colombier xlocalrootgen(void*, Block *b, int o, Tnode **tp)
663*5e96a66cSDavid du Colombier {
664*5e96a66cSDavid du Colombier 	Entry e;
665*5e96a66cSDavid du Colombier 
666*5e96a66cSDavid du Colombier 	if(o >= 1)
667*5e96a66cSDavid du Colombier 		return -1;
668*5e96a66cSDavid du Colombier 	entryUnpack(&e, b->data, o);
669*5e96a66cSDavid du Colombier 	*tp = initxentry(e);
670*5e96a66cSDavid du Colombier 	return 1;
671*5e96a66cSDavid du Colombier }
672*5e96a66cSDavid du Colombier 
673*5e96a66cSDavid du Colombier Tnode*
674*5e96a66cSDavid du Colombier initxlocalroot(char *name, u32int addr)
675*5e96a66cSDavid du Colombier {
676*5e96a66cSDavid du Colombier 	uchar score[VtScoreSize];
677*5e96a66cSDavid du Colombier 	Block *b;
678*5e96a66cSDavid du Colombier 
679*5e96a66cSDavid du Colombier 	localToGlobal(addr, score);
680*5e96a66cSDavid du Colombier 	b = dataBlock(score, BtDir, RootTag);
681*5e96a66cSDavid du Colombier 	if(b == nil)
682*5e96a66cSDavid du Colombier 		return stringnode("read data block %#ux: %R", addr);
683*5e96a66cSDavid du Colombier 	return initxblock(b, smprint("'%s' fs root", name), xlocalrootgen, nil);
684*5e96a66cSDavid du Colombier }
685*5e96a66cSDavid du Colombier 
686*5e96a66cSDavid du Colombier int
687*5e96a66cSDavid du Colombier xvacrootgen(void*, Block *b, int o, Tnode **tp)
688*5e96a66cSDavid du Colombier {
689*5e96a66cSDavid du Colombier 	Entry e;
690*5e96a66cSDavid du Colombier 
691*5e96a66cSDavid du Colombier 	if(o >= 3)
692*5e96a66cSDavid du Colombier 		return -1;
693*5e96a66cSDavid du Colombier 	entryUnpack(&e, b->data, o);
694*5e96a66cSDavid du Colombier 	*tp = initxentry(e);
695*5e96a66cSDavid du Colombier 	return 1;
696*5e96a66cSDavid du Colombier }
697*5e96a66cSDavid du Colombier 
698*5e96a66cSDavid du Colombier Tnode*
699*5e96a66cSDavid du Colombier initxroot(char *name, uchar score[VtScoreSize])
700*5e96a66cSDavid du Colombier {
701*5e96a66cSDavid du Colombier 	Block *b;
702*5e96a66cSDavid du Colombier 
703*5e96a66cSDavid du Colombier 	b = dataBlock(score, BtDir, RootTag);
704*5e96a66cSDavid du Colombier 	if(b == nil)
705*5e96a66cSDavid du Colombier 		return stringnode("read data block %V: %R", score);
706*5e96a66cSDavid du Colombier 	return initxblock(b, smprint("'%s' fs root", name), xvacrootgen, nil);
707*5e96a66cSDavid du Colombier }
708*5e96a66cSDavid du Colombier Tnode*
709*5e96a66cSDavid du Colombier initxdirentry(MetaEntry *me)
710*5e96a66cSDavid du Colombier {
711*5e96a66cSDavid du Colombier 	DirEntry dir;
712*5e96a66cSDavid du Colombier 	Tnode *t;
713*5e96a66cSDavid du Colombier 
714*5e96a66cSDavid du Colombier 	if(!deUnpack(&dir, me))
715*5e96a66cSDavid du Colombier 		return stringnode("deUnpack: %R");
716*5e96a66cSDavid du Colombier 
717*5e96a66cSDavid 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);
718*5e96a66cSDavid du Colombier 	t->nkid = 1;
719*5e96a66cSDavid du Colombier 	t->kid = mallocz(sizeof(t->kid[0])*1, 1);
720*5e96a66cSDavid du Colombier 	t->kid[0] = stringnode(
721*5e96a66cSDavid du Colombier 		"qid=%#llux\n"
722*5e96a66cSDavid du Colombier 		"uid=%s gid=%s mid=%s\n"
723*5e96a66cSDavid du Colombier 		"mtime=%lud mcount=%lud ctime=%lud atime=%lud\n"
724*5e96a66cSDavid du Colombier 		"mode=%luo\n"
725*5e96a66cSDavid du Colombier 		"plan9 %d p9path %#llux p9version %lud\n"
726*5e96a66cSDavid du Colombier 		"qidSpace %d offset %#llux max %#llux",
727*5e96a66cSDavid du Colombier 		dir.qid,
728*5e96a66cSDavid du Colombier 		dir.uid, dir.gid, dir.mid,
729*5e96a66cSDavid du Colombier 		dir.mtime, dir.mcount, dir.ctime, dir.atime,
730*5e96a66cSDavid du Colombier 		dir.mode,
731*5e96a66cSDavid du Colombier 		dir.plan9, dir.p9path, dir.p9version,
732*5e96a66cSDavid du Colombier 		dir.qidSpace, dir.qidOffset, dir.qidMax);
733*5e96a66cSDavid du Colombier 	return t;
734*5e96a66cSDavid du Colombier }
735*5e96a66cSDavid du Colombier 
736*5e96a66cSDavid du Colombier int
737*5e96a66cSDavid du Colombier metaentrygen(void *v, Block*, int o, Tnode **tp)
738*5e96a66cSDavid du Colombier {
739*5e96a66cSDavid du Colombier 	Tnode *t;
740*5e96a66cSDavid du Colombier 	MetaBlock *mb;
741*5e96a66cSDavid du Colombier 	MetaEntry me;
742*5e96a66cSDavid du Colombier 
743*5e96a66cSDavid du Colombier 	mb = v;
744*5e96a66cSDavid du Colombier 	if(o >= mb->nindex)
745*5e96a66cSDavid du Colombier 		return -1;
746*5e96a66cSDavid du Colombier 	meUnpack(&me, mb, o);
747*5e96a66cSDavid du Colombier 
748*5e96a66cSDavid du Colombier 	t = stringnode("MetaEntry %d bytes", mb->size);
749*5e96a66cSDavid du Colombier 	t->kid = mallocz(sizeof(t->kid[0])*1, 1);
750*5e96a66cSDavid du Colombier 	t->kid[0] = initxdirentry(&me);
751*5e96a66cSDavid du Colombier 	t->nkid = 1;
752*5e96a66cSDavid du Colombier 	*tp = t;
753*5e96a66cSDavid du Colombier 	return 1;
754*5e96a66cSDavid du Colombier }
755*5e96a66cSDavid du Colombier 
756*5e96a66cSDavid du Colombier int
757*5e96a66cSDavid du Colombier metablockgen(void *v, Block *b, int o, Tnode **tp)
758*5e96a66cSDavid du Colombier {
759*5e96a66cSDavid du Colombier 	Xblock *t;
760*5e96a66cSDavid du Colombier 	MetaBlock *mb;
761*5e96a66cSDavid du Colombier 
762*5e96a66cSDavid du Colombier 	if(o >= 1)
763*5e96a66cSDavid du Colombier 		return -1;
764*5e96a66cSDavid du Colombier 
765*5e96a66cSDavid du Colombier 	/* hack: reuse initxblock as a generic iterator */
766*5e96a66cSDavid du Colombier 	mb = v;
767*5e96a66cSDavid du Colombier 	t = (Xblock*)initxblock(b, "", metaentrygen, mb);
768*5e96a66cSDavid du Colombier 	t->str = smprint("MetaBlock %d/%d space used, %d add'l free %d/%d table used%s",
769*5e96a66cSDavid du Colombier 		mb->size, mb->maxsize, mb->free, mb->nindex, mb->maxindex,
770*5e96a66cSDavid du Colombier 		mb->botch ? " [BOTCH]" : "");
771*5e96a66cSDavid du Colombier 	t->printlabel = 0;
772*5e96a66cSDavid du Colombier 	*tp = t;
773*5e96a66cSDavid du Colombier 	return 1;
774*5e96a66cSDavid du Colombier }
775*5e96a66cSDavid du Colombier 
776*5e96a66cSDavid du Colombier /*
777*5e96a66cSDavid du Colombier  * attempt to guess at the type of data in the block.
778*5e96a66cSDavid du Colombier  * it could just be data from a file, but we're hoping it's MetaBlocks.
779*5e96a66cSDavid du Colombier  */
780*5e96a66cSDavid du Colombier Tnode*
781*5e96a66cSDavid du Colombier initxdatablock(Block *b, uint n)
782*5e96a66cSDavid du Colombier {
783*5e96a66cSDavid du Colombier 	MetaBlock mb;
784*5e96a66cSDavid du Colombier 
785*5e96a66cSDavid du Colombier 	if(n > h.blockSize)
786*5e96a66cSDavid du Colombier 		n = h.blockSize;
787*5e96a66cSDavid du Colombier 
788*5e96a66cSDavid du Colombier 	if(mbUnpack(&mb, b->data, n))
789*5e96a66cSDavid du Colombier 		return initxblock(b, "metadata", metablockgen, copyMetaBlock(mb));
790*5e96a66cSDavid du Colombier 
791*5e96a66cSDavid du Colombier 	return initxblock(b, "data", nil, nil);
792*5e96a66cSDavid du Colombier }
793*5e96a66cSDavid du Colombier 
794*5e96a66cSDavid du Colombier int
795*5e96a66cSDavid du Colombier parseScore(uchar *score, char *buf, int n)
796*5e96a66cSDavid du Colombier {
797*5e96a66cSDavid du Colombier 	int i, c;
798*5e96a66cSDavid du Colombier 
799*5e96a66cSDavid du Colombier 	memset(score, 0, VtScoreSize);
800*5e96a66cSDavid du Colombier 
801*5e96a66cSDavid du Colombier 	if(n < VtScoreSize*2)
802*5e96a66cSDavid du Colombier 		return 0;
803*5e96a66cSDavid du Colombier 	for(i=0; i<VtScoreSize*2; i++){
804*5e96a66cSDavid du Colombier 		if(buf[i] >= '0' && buf[i] <= '9')
805*5e96a66cSDavid du Colombier 			c = buf[i] - '0';
806*5e96a66cSDavid du Colombier 		else if(buf[i] >= 'a' && buf[i] <= 'f')
807*5e96a66cSDavid du Colombier 			c = buf[i] - 'a' + 10;
808*5e96a66cSDavid du Colombier 		else if(buf[i] >= 'A' && buf[i] <= 'F')
809*5e96a66cSDavid du Colombier 			c = buf[i] - 'A' + 10;
810*5e96a66cSDavid du Colombier 		else{
811*5e96a66cSDavid du Colombier 			return 0;
812*5e96a66cSDavid du Colombier 		}
813*5e96a66cSDavid du Colombier 
814*5e96a66cSDavid du Colombier 		if((i & 1) == 0)
815*5e96a66cSDavid du Colombier 			c <<= 4;
816*5e96a66cSDavid du Colombier 
817*5e96a66cSDavid du Colombier 		score[i>>1] |= c;
818*5e96a66cSDavid du Colombier 	}
819*5e96a66cSDavid du Colombier 	return 1;
820*5e96a66cSDavid du Colombier }
821*5e96a66cSDavid du Colombier 
822*5e96a66cSDavid du Colombier int
823*5e96a66cSDavid du Colombier scoreFmt(Fmt *f)
824*5e96a66cSDavid du Colombier {
825*5e96a66cSDavid du Colombier 	uchar *v;
826*5e96a66cSDavid du Colombier 	int i;
827*5e96a66cSDavid du Colombier 	u32int addr;
828*5e96a66cSDavid du Colombier 
829*5e96a66cSDavid du Colombier 	v = va_arg(f->args, uchar*);
830*5e96a66cSDavid du Colombier 	if(v == nil){
831*5e96a66cSDavid du Colombier 		fmtprint(f, "*");
832*5e96a66cSDavid du Colombier 	}else if((addr = globalToLocal(v)) != NilBlock)
833*5e96a66cSDavid du Colombier 		fmtprint(f, "0x%.8ux", addr);
834*5e96a66cSDavid du Colombier 	else{
835*5e96a66cSDavid du Colombier 		for(i = 0; i < VtScoreSize; i++)
836*5e96a66cSDavid du Colombier 			fmtprint(f, "%2.2ux", v[i]);
837*5e96a66cSDavid du Colombier 	}
838*5e96a66cSDavid du Colombier 
839*5e96a66cSDavid du Colombier 	return 0;
840*5e96a66cSDavid du Colombier }
841*5e96a66cSDavid du Colombier 
842*5e96a66cSDavid du Colombier Atree*
843*5e96a66cSDavid du Colombier atreeinit(char *arg)
844*5e96a66cSDavid du Colombier {
845*5e96a66cSDavid du Colombier 	Atree *a;
846*5e96a66cSDavid du Colombier 	uchar score[VtScoreSize];
847*5e96a66cSDavid du Colombier 
848*5e96a66cSDavid du Colombier 	vtAttach();
849*5e96a66cSDavid du Colombier 
850*5e96a66cSDavid du Colombier 	fmtinstall('V', scoreFmt);
851*5e96a66cSDavid du Colombier 	fmtinstall('R', vtErrFmt);
852*5e96a66cSDavid du Colombier 
853*5e96a66cSDavid du Colombier 	z = vtDial(nil, 1);
854*5e96a66cSDavid du Colombier 	if(z == nil)
855*5e96a66cSDavid du Colombier 		fprint(2, "warning: cannot dial venti: %R\n");
856*5e96a66cSDavid du Colombier 	if(!vtConnect(z, 0)){
857*5e96a66cSDavid du Colombier 		fprint(2, "warning: cannot connect to venti: %R\n");
858*5e96a66cSDavid du Colombier 		z = nil;
859*5e96a66cSDavid du Colombier 	}
860*5e96a66cSDavid du Colombier 	a = mallocz(sizeof(Atree), 1);
861*5e96a66cSDavid du Colombier 	if(strncmp(arg, "vac:", 4) == 0){
862*5e96a66cSDavid du Colombier 		if(!parseScore(score, arg+4, strlen(arg+4))){
863*5e96a66cSDavid du Colombier 			fprint(2, "cannot parse score\n");
864*5e96a66cSDavid du Colombier 			return nil;
865*5e96a66cSDavid du Colombier 		}
866*5e96a66cSDavid du Colombier 		a->root = initxvacroot(score);
867*5e96a66cSDavid du Colombier 	}else
868*5e96a66cSDavid du Colombier 		a->root = initxcache(arg);
869*5e96a66cSDavid du Colombier 	a->resizefd = -1;
870*5e96a66cSDavid du Colombier 	return a;
871*5e96a66cSDavid du Colombier }
872*5e96a66cSDavid du Colombier 
873*5e96a66cSDavid du Colombier /* --- tree.c */
874*5e96a66cSDavid du Colombier enum
875*5e96a66cSDavid du Colombier {
876*5e96a66cSDavid du Colombier 	Nubwidth = 11,
877*5e96a66cSDavid du Colombier 	Nubheight = 11,
878*5e96a66cSDavid du Colombier 	Linewidth = Nubwidth*2+4,
879*5e96a66cSDavid du Colombier };
880*5e96a66cSDavid du Colombier 
881*5e96a66cSDavid du Colombier uint
882*5e96a66cSDavid du Colombier drawtext(char *s, Image *m, Image *clipr, Point o)
883*5e96a66cSDavid du Colombier {
884*5e96a66cSDavid du Colombier 	char *t, *nt, *e;
885*5e96a66cSDavid du Colombier 	uint dy;
886*5e96a66cSDavid du Colombier 
887*5e96a66cSDavid du Colombier 	if(s == nil)
888*5e96a66cSDavid du Colombier 		s = "???";
889*5e96a66cSDavid du Colombier 
890*5e96a66cSDavid du Colombier 	dy = 0;
891*5e96a66cSDavid du Colombier 	for(t=s; t&&*t; t=nt){
892*5e96a66cSDavid du Colombier 		if(nt = strchr(t, '\n')){
893*5e96a66cSDavid du Colombier 			e = nt;
894*5e96a66cSDavid du Colombier 			nt++;
895*5e96a66cSDavid du Colombier 		}else
896*5e96a66cSDavid du Colombier 			e = t+strlen(t);
897*5e96a66cSDavid du Colombier 
898*5e96a66cSDavid du Colombier 		_string(m, Pt(o.x, o.y+dy), display->black, ZP, display->defaultfont,
899*5e96a66cSDavid du Colombier 			t, nil, e-t, clipr->clipr, nil, ZP, SoverD);
900*5e96a66cSDavid du Colombier 		dy += display->defaultfont->height;
901*5e96a66cSDavid du Colombier 	}
902*5e96a66cSDavid du Colombier 	return dy;
903*5e96a66cSDavid du Colombier }
904*5e96a66cSDavid du Colombier 
905*5e96a66cSDavid du Colombier void
906*5e96a66cSDavid du Colombier drawnub(Image *m, Image *clipr, Point o, Tnode *t)
907*5e96a66cSDavid du Colombier {
908*5e96a66cSDavid du Colombier 	clipr = nil;
909*5e96a66cSDavid du Colombier 
910*5e96a66cSDavid du Colombier 	if(t->nkid == 0)
911*5e96a66cSDavid du Colombier 		return;
912*5e96a66cSDavid du Colombier 	if(t->nkid == -1 && t->expand == nil)
913*5e96a66cSDavid du Colombier 		return;
914*5e96a66cSDavid du Colombier 
915*5e96a66cSDavid du Colombier 	o.y += (display->defaultfont->height-Nubheight)/2;
916*5e96a66cSDavid du Colombier 	draw(m, rectaddpt(Rect(0,0,1,Nubheight), o), display->black, clipr, ZP);
917*5e96a66cSDavid du Colombier 	draw(m, rectaddpt(Rect(0,0,Nubwidth,1), o), display->black, clipr, o);
918*5e96a66cSDavid du Colombier 	draw(m, rectaddpt(Rect(Nubwidth-1,0,Nubwidth,Nubheight), o),
919*5e96a66cSDavid du Colombier 		display->black, clipr, addpt(o, Pt(Nubwidth-1, 0)));
920*5e96a66cSDavid du Colombier 	draw(m, rectaddpt(Rect(0, Nubheight-1, Nubwidth, Nubheight), o),
921*5e96a66cSDavid du Colombier 		display->black, clipr, addpt(o, Pt(0, Nubheight-1)));
922*5e96a66cSDavid du Colombier 
923*5e96a66cSDavid du Colombier 	draw(m, rectaddpt(Rect(0, Nubheight/2, Nubwidth, Nubheight/2+1), o),
924*5e96a66cSDavid du Colombier 		display->black, clipr, addpt(o, Pt(0, Nubheight/2)));
925*5e96a66cSDavid du Colombier 	if(!t->expanded)
926*5e96a66cSDavid du Colombier 		draw(m, rectaddpt(Rect(Nubwidth/2, 0, Nubwidth/2+1, Nubheight), o),
927*5e96a66cSDavid du Colombier 			display->black, clipr, addpt(o, Pt(Nubwidth/2, 0)));
928*5e96a66cSDavid du Colombier 
929*5e96a66cSDavid du Colombier }
930*5e96a66cSDavid du Colombier 
931*5e96a66cSDavid du Colombier uint
932*5e96a66cSDavid du Colombier drawnode(Tnode *t, Image *m, Image *clipr, Point o)
933*5e96a66cSDavid du Colombier {
934*5e96a66cSDavid du Colombier 	int i;
935*5e96a66cSDavid du Colombier 	char *fs, *s;
936*5e96a66cSDavid du Colombier 	uint dy;
937*5e96a66cSDavid du Colombier 	Point oo;
938*5e96a66cSDavid du Colombier 
939*5e96a66cSDavid du Colombier 	if(t == nil)
940*5e96a66cSDavid du Colombier 		return 0;
941*5e96a66cSDavid du Colombier 
942*5e96a66cSDavid du Colombier 	t->offset = o;
943*5e96a66cSDavid du Colombier 
944*5e96a66cSDavid du Colombier 	oo = Pt(o.x+Nubwidth+2, o.y);
945*5e96a66cSDavid du Colombier //	if(t->draw)
946*5e96a66cSDavid du Colombier //		dy = (*t->draw)(t, m, clipr, oo);
947*5e96a66cSDavid du Colombier //	else{
948*5e96a66cSDavid du Colombier 		fs = nil;
949*5e96a66cSDavid du Colombier 		if(t->str)
950*5e96a66cSDavid du Colombier 			s = t->str;
951*5e96a66cSDavid du Colombier 	//	else if(t->strfn)
952*5e96a66cSDavid du Colombier 	//		fs = s = (*t->strfn)(t);
953*5e96a66cSDavid du Colombier 		else
954*5e96a66cSDavid du Colombier 			s = "???";
955*5e96a66cSDavid du Colombier 		dy = drawtext(s, m, clipr, oo);
956*5e96a66cSDavid du Colombier 		free(fs);
957*5e96a66cSDavid du Colombier //	}
958*5e96a66cSDavid du Colombier 
959*5e96a66cSDavid du Colombier 	if(t->expanded){
960*5e96a66cSDavid du Colombier 		if(t->nkid == -1 && t->expand)
961*5e96a66cSDavid du Colombier 			(*t->expand)(t);
962*5e96a66cSDavid du Colombier 		oo = Pt(o.x+Nubwidth+(Linewidth-Nubwidth)/2, o.y+dy);
963*5e96a66cSDavid du Colombier 		for(i=0; i<t->nkid; i++)
964*5e96a66cSDavid du Colombier 			oo.y += drawnode(t->kid[i], m, clipr, oo);
965*5e96a66cSDavid du Colombier 		dy = oo.y - o.y;
966*5e96a66cSDavid du Colombier 	}
967*5e96a66cSDavid du Colombier 	drawnub(m, clipr, o, t);
968*5e96a66cSDavid du Colombier 	return dy;
969*5e96a66cSDavid du Colombier }
970*5e96a66cSDavid du Colombier 
971*5e96a66cSDavid du Colombier void
972*5e96a66cSDavid du Colombier drawtree(Tree *t, Image *m, Rectangle r)
973*5e96a66cSDavid du Colombier {
974*5e96a66cSDavid du Colombier 	Point p;
975*5e96a66cSDavid du Colombier 
976*5e96a66cSDavid du Colombier 	draw(m, r, display->white, nil, ZP);
977*5e96a66cSDavid du Colombier 
978*5e96a66cSDavid du Colombier 	replclipr(t->clipr, 1, r);
979*5e96a66cSDavid du Colombier 	p = addpt(t->offset, r.min);
980*5e96a66cSDavid du Colombier 	drawnode(t->root, m, t->clipr, p);
981*5e96a66cSDavid du Colombier }
982*5e96a66cSDavid du Colombier 
983*5e96a66cSDavid du Colombier Tnode*
984*5e96a66cSDavid du Colombier findnode(Tnode *t, Point p)
985*5e96a66cSDavid du Colombier {
986*5e96a66cSDavid du Colombier 	int i;
987*5e96a66cSDavid du Colombier 	Tnode *tt;
988*5e96a66cSDavid du Colombier 
989*5e96a66cSDavid du Colombier 	if(ptinrect(p, rectaddpt(Rect(0,0,Nubwidth, Nubheight), t->offset)))
990*5e96a66cSDavid du Colombier 		return t;
991*5e96a66cSDavid du Colombier 	if(!t->expanded)
992*5e96a66cSDavid du Colombier 		return nil;
993*5e96a66cSDavid du Colombier 	for(i=0; i<t->nkid; i++)
994*5e96a66cSDavid du Colombier 		if(tt = findnode(t->kid[i], p))
995*5e96a66cSDavid du Colombier 			return tt;
996*5e96a66cSDavid du Colombier 	return nil;
997*5e96a66cSDavid du Colombier }
998*5e96a66cSDavid du Colombier 
999*5e96a66cSDavid du Colombier void
1000*5e96a66cSDavid du Colombier usage(void)
1001*5e96a66cSDavid du Colombier {
1002*5e96a66cSDavid du Colombier 	fprint(2, "usage: vtree /dev/sdC0/fossil\n");
1003*5e96a66cSDavid du Colombier 	exits("usage");
1004*5e96a66cSDavid du Colombier }
1005*5e96a66cSDavid du Colombier 
1006*5e96a66cSDavid du Colombier Tree t;
1007*5e96a66cSDavid du Colombier 
1008*5e96a66cSDavid du Colombier void
1009*5e96a66cSDavid du Colombier eresized(int new)
1010*5e96a66cSDavid du Colombier {
1011*5e96a66cSDavid du Colombier 	Rectangle r;
1012*5e96a66cSDavid du Colombier 	r = screen->r;
1013*5e96a66cSDavid du Colombier 	if(new && getwindow(display, Refnone) < 0)
1014*5e96a66cSDavid du Colombier 		fprint(2,"can't reattach to window");
1015*5e96a66cSDavid du Colombier 	drawtree(&t, screen, screen->r);
1016*5e96a66cSDavid du Colombier }
1017*5e96a66cSDavid du Colombier 
1018*5e96a66cSDavid du Colombier enum
1019*5e96a66cSDavid du Colombier {
1020*5e96a66cSDavid du Colombier 	Left = 1<<0,
1021*5e96a66cSDavid du Colombier 	Middle = 1<<1,
1022*5e96a66cSDavid du Colombier 	Right = 1<<2,
1023*5e96a66cSDavid du Colombier 
1024*5e96a66cSDavid du Colombier 	MMenu = 2,
1025*5e96a66cSDavid du Colombier };
1026*5e96a66cSDavid du Colombier 
1027*5e96a66cSDavid du Colombier char *items[] = { "exit", 0 };
1028*5e96a66cSDavid du Colombier enum { IExit, };
1029*5e96a66cSDavid du Colombier 
1030*5e96a66cSDavid du Colombier Menu menu;
1031*5e96a66cSDavid du Colombier 
1032*5e96a66cSDavid du Colombier void
1033*5e96a66cSDavid du Colombier main(int argc, char **argv)
1034*5e96a66cSDavid du Colombier {
1035*5e96a66cSDavid du Colombier 	int n;
1036*5e96a66cSDavid du Colombier 	char *dir;
1037*5e96a66cSDavid du Colombier 	Event e;
1038*5e96a66cSDavid du Colombier 	Point op, p;
1039*5e96a66cSDavid du Colombier 	Tnode *tn;
1040*5e96a66cSDavid du Colombier 	Mouse m;
1041*5e96a66cSDavid du Colombier 	int Eready;
1042*5e96a66cSDavid du Colombier 	Atree *fs;
1043*5e96a66cSDavid du Colombier 
1044*5e96a66cSDavid du Colombier 	ARGBEGIN{
1045*5e96a66cSDavid du Colombier 	case 'a':
1046*5e96a66cSDavid du Colombier 		showinactive = 1;
1047*5e96a66cSDavid du Colombier 		break;
1048*5e96a66cSDavid du Colombier 	default:
1049*5e96a66cSDavid du Colombier 		usage();
1050*5e96a66cSDavid du Colombier 	}ARGEND
1051*5e96a66cSDavid du Colombier 
1052*5e96a66cSDavid du Colombier 	switch(argc){
1053*5e96a66cSDavid du Colombier 	default:
1054*5e96a66cSDavid du Colombier 		usage();
1055*5e96a66cSDavid du Colombier 	case 1:
1056*5e96a66cSDavid du Colombier 		dir = argv[0];
1057*5e96a66cSDavid du Colombier 		break;
1058*5e96a66cSDavid du Colombier 	}
1059*5e96a66cSDavid du Colombier 
1060*5e96a66cSDavid du Colombier 	fs = atreeinit(dir);
1061*5e96a66cSDavid du Colombier 	initdraw(0, "/lib/font/bit/lucidasans/unicode.8.font", "tree");
1062*5e96a66cSDavid du Colombier 	t.root = fs->root;
1063*5e96a66cSDavid du Colombier 	t.offset = ZP;
1064*5e96a66cSDavid du Colombier 	t.clipr = allocimage(display, Rect(0,0,1,1), GREY1, 1, DOpaque);
1065*5e96a66cSDavid du Colombier 
1066*5e96a66cSDavid du Colombier 	eresized(0);
1067*5e96a66cSDavid du Colombier 	flushimage(display, 1);
1068*5e96a66cSDavid du Colombier 
1069*5e96a66cSDavid du Colombier 	einit(Emouse);
1070*5e96a66cSDavid du Colombier 
1071*5e96a66cSDavid du Colombier 	menu.item = items;
1072*5e96a66cSDavid du Colombier 	menu.gen = 0;
1073*5e96a66cSDavid du Colombier 	menu.lasthit = 0;
1074*5e96a66cSDavid du Colombier 	if(fs->resizefd > 0){
1075*5e96a66cSDavid du Colombier 		Eready = 1<<3;
1076*5e96a66cSDavid du Colombier 		estart(Eready, fs->resizefd, 1);
1077*5e96a66cSDavid du Colombier 	}else
1078*5e96a66cSDavid du Colombier 		Eready = 0;
1079*5e96a66cSDavid du Colombier 
1080*5e96a66cSDavid du Colombier 	for(;;){
1081*5e96a66cSDavid du Colombier 		switch(n=eread(Emouse|Eready, &e)){
1082*5e96a66cSDavid du Colombier 		default:
1083*5e96a66cSDavid du Colombier 			if(Eready && n==Eready)
1084*5e96a66cSDavid du Colombier 				eresized(0);
1085*5e96a66cSDavid du Colombier 			break;
1086*5e96a66cSDavid du Colombier 		case Emouse:
1087*5e96a66cSDavid du Colombier 			m = e.mouse;
1088*5e96a66cSDavid du Colombier 			switch(m.buttons){
1089*5e96a66cSDavid du Colombier 			case Left:
1090*5e96a66cSDavid du Colombier 				op = t.offset;
1091*5e96a66cSDavid du Colombier 				p = m.xy;
1092*5e96a66cSDavid du Colombier 				do {
1093*5e96a66cSDavid du Colombier 					t.offset = addpt(t.offset, subpt(m.xy, p));
1094*5e96a66cSDavid du Colombier 					p = m.xy;
1095*5e96a66cSDavid du Colombier 					eresized(0);
1096*5e96a66cSDavid du Colombier 					m = emouse();
1097*5e96a66cSDavid du Colombier 				}while(m.buttons == Left);
1098*5e96a66cSDavid du Colombier 				if(m.buttons){
1099*5e96a66cSDavid du Colombier 					t.offset = op;
1100*5e96a66cSDavid du Colombier 					eresized(0);
1101*5e96a66cSDavid du Colombier 				}
1102*5e96a66cSDavid du Colombier 				break;
1103*5e96a66cSDavid du Colombier 			case Middle:
1104*5e96a66cSDavid du Colombier 				n = emenuhit(MMenu, &m, &menu);
1105*5e96a66cSDavid du Colombier 				if(n == -1)
1106*5e96a66cSDavid du Colombier 					break;
1107*5e96a66cSDavid du Colombier 				switch(n){
1108*5e96a66cSDavid du Colombier 				case IExit:
1109*5e96a66cSDavid du Colombier 					exits(nil);
1110*5e96a66cSDavid du Colombier 				}
1111*5e96a66cSDavid du Colombier 				break;
1112*5e96a66cSDavid du Colombier 			case Right:
1113*5e96a66cSDavid du Colombier 				do
1114*5e96a66cSDavid du Colombier 					m = emouse();
1115*5e96a66cSDavid du Colombier 				while(m.buttons == Right);
1116*5e96a66cSDavid du Colombier 				if(m.buttons)
1117*5e96a66cSDavid du Colombier 					break;
1118*5e96a66cSDavid du Colombier 				tn = findnode(t.root, m.xy);
1119*5e96a66cSDavid du Colombier 				if(tn){
1120*5e96a66cSDavid du Colombier 					tn->expanded = !tn->expanded;
1121*5e96a66cSDavid du Colombier 					eresized(0);
1122*5e96a66cSDavid du Colombier 				}
1123*5e96a66cSDavid du Colombier 				break;
1124*5e96a66cSDavid du Colombier 			}
1125*5e96a66cSDavid du Colombier 		}
1126*5e96a66cSDavid du Colombier 	}
1127*5e96a66cSDavid du Colombier }
1128