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