plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

view.c (19812B)


      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*
     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*
    105 btStr(int type)
    106 {
    107 	if(type < nelem(bttab))
    108 		return bttab[type];
    109 	return "unknown";
    110 }
    111 
    112 Block*
    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
    123 blockPut(Block *b)
    124 {
    125 	free(b);
    126 }
    127 
    128 static u32int
    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
    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*
    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*
    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*
    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*
    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*
    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*
    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
    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*
    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
    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*
    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
    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*
    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
    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*
    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*
    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
    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
    508 nilgen(void *v, Block *b, int o, Tnode **tp)
    509 {
    510 	return -1;
    511 }
    512 
    513 Tnode*
    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
    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*
    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
    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*
    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
    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
    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*
    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
    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*
    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
    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*
    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*
    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
    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
    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*
    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
    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
    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*
    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
    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
    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
    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
    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*
    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
    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
   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
   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 }