Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
Header h;
56
Super super;
57
VtSession *z;
58
VtRoot vac;
59
int showinactive;
60
 
61
/*
62
 * dumbed down versions of fossil routines
63
 */
64
char*
65
bsStr(int state)
66
{
67
	static char s[100];
68
 
69
	if(state == BsFree)
70
		return "Free";
71
	if(state == BsBad)
72
		return "Bad";
73
 
74
	sprint(s, "%x", state);
75
	if(!(state&BsAlloc))
76
		strcat(s, ",Free");	/* should not happen */
77
	if(state&BsVenti)
78
		strcat(s, ",Venti");
79
	if(state&BsClosed)
80
		strcat(s, ",Closed");
81
	return s;
82
}
83
 
84
char *bttab[] = {
85
	"BtData",
86
	"BtData+1",
87
	"BtData+2",
88
	"BtData+3",
89
	"BtData+4",
90
	"BtData+5",
91
	"BtData+6",
92
	"BtData+7",
93
	"BtDir",
94
	"BtDir+1",
95
	"BtDir+2",
96
	"BtDir+3",
97
	"BtDir+4",
98
	"BtDir+5",
99
	"BtDir+6",
100
	"BtDir+7",
101
};
102
 
103
char*
104
btStr(int type)
105
{
106
	if(type < nelem(bttab))
107
		return bttab[type];
108
	return "unknown";
109
}
110
#pragma varargck argpos stringnode 1
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
	VtPointerType0,		/* BtData | 1  */
201
	VtPointerType1,		/* BtData | 2  */
202
	VtPointerType2,		/* BtData | 3  */
203
	VtPointerType3,		/* BtData | 4  */
204
	VtPointerType4,		/* BtData | 5  */
205
	VtPointerType5,		/* BtData | 6  */
206
	VtPointerType6,		/* BtData | 7  */
207
	VtDirType,		/* BtDir | 0  */
208
	VtPointerType0,		/* BtDir | 1  */
209
	VtPointerType1,		/* BtDir | 2  */
210
	VtPointerType2,		/* BtDir | 3  */
211
	VtPointerType3,		/* BtDir | 4  */
212
	VtPointerType4,		/* BtDir | 5  */
213
	VtPointerType5,		/* BtDir | 6  */
214
	VtPointerType6,		/* 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))
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=%ud score=%V prev=%V",
451
		vac.version, 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;
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->nkid >= 0)
482
		return;
483
 
484
	j = 0;
485
	if(t->printlabel){
486
		t->kid = mallocz(Q*sizeof(t->kid[0]), 1);
487
		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->nkid = j;
495
			return;
496
		case 0:
497
			break;
498
		case 1:
499
			if(j%Q == 0)
500
				t->kid = realloc(t->kid, (j+Q)*sizeof(t->kid[0]));
501
			t->kid[j++] = nn;
502
			break;
503
		}
504
	}
505
}
506
 
507
int
508
nilgen(void*, Block*, int, Tnode**)
509
{
510
	return -1;
511
}
512
 
513
Tnode*
514
initxblock(Block *b, char *s, int (*gen)(void*, Block*, int, Tnode**), 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->str = smprint("Block %V: %s", b->score, s);
526
	else
527
		t->str = smprint("Block %#ux: %s", b->addr, s);
528
	t->printlabel = 1;
529
	t->nkid = -1;
530
	t->expand = xblockexpand;
531
	return 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;
561
	Entry e;
562
};
563
 
564
void
565
xentryexpand(Tnode *tt)
566
{
567
	Xentry *t = (Xentry*)tt;
568
 
569
	if(t->nkid >= 0)
570
		return;
571
 
572
	t->nkid = 1;
573
	t->kid = mallocz(sizeof(t->kid[0])*t->nkid, 1);
574
	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->nkid = -1;
584
	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->str = smprint("%s archive=%d snap=%d tag=%#ux", t->str, e.archive, e.snap, e.tag);
588
	t->expand = xentryexpand;
589
	t->e = e;
590
	return 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*, 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*, 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*, 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->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 = 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
	vtAttach();
849
 
850
	fmtinstall('V', scoreFmt);
851
	fmtinstall('R', vtErrFmt);
852
 
853
	z = vtDial(nil, 1);
854
	if(z == nil)
855
		fprint(2, "warning: cannot dial venti: %R\n");
856
	if(!vtConnect(z, 0)){
857
		fprint(2, "warning: cannot connect to venti: %R\n");
858
		z = nil;
859
	}
860
	a = mallocz(sizeof(Atree), 1);
861
	if(strncmp(arg, "vac:", 4) == 0){
862
		if(!parseScore(score, arg+4, strlen(arg+4))){
863
			fprint(2, "cannot parse score\n");
864
			return nil;
865
		}
866
		a->root = initxvacroot(score);
867
	}else
868
		a->root = initxcache(arg);
869
	a->resizefd = -1;
870
	return a;
871
}
872
 
873
/* --- tree.c */
874
enum
875
{
876
	Nubwidth = 11,
877
	Nubheight = 11,
878
	Linewidth = Nubwidth*2+4,
879
};
880
 
881
uint
882
drawtext(char *s, Image *m, Image *clipr, Point o)
883
{
884
	char *t, *nt, *e;
885
	uint dy;
886
 
887
	if(s == nil)
888
		s = "???";
889
 
890
	dy = 0;
891
	for(t=s; t&&*t; t=nt){
892
		if(nt = strchr(t, '\n')){
893
			e = nt;
894
			nt++;
895
		}else
896
			e = t+strlen(t);
897
 
898
		_string(m, Pt(o.x, o.y+dy), display->black, ZP, display->defaultfont,
899
			t, nil, e-t, clipr->clipr, nil, ZP, SoverD);
900
		dy += display->defaultfont->height;
901
	}
902
	return dy;
903
}
904
 
905
void
906
drawnub(Image *m, Image *clipr, Point o, Tnode *t)
907
{
908
	clipr = nil;
909
 
910
	if(t->nkid == 0)
911
		return;
912
	if(t->nkid == -1 && t->expand == nil)
913
		return;
914
 
915
	o.y += (display->defaultfont->height-Nubheight)/2;
916
	draw(m, rectaddpt(Rect(0,0,1,Nubheight), o), display->black, clipr, ZP);
917
	draw(m, rectaddpt(Rect(0,0,Nubwidth,1), o), display->black, clipr, o);
918
	draw(m, rectaddpt(Rect(Nubwidth-1,0,Nubwidth,Nubheight), o), 
919
		display->black, clipr, addpt(o, Pt(Nubwidth-1, 0)));
920
	draw(m, rectaddpt(Rect(0, Nubheight-1, Nubwidth, Nubheight), o),
921
		display->black, clipr, addpt(o, Pt(0, Nubheight-1)));
922
 
923
	draw(m, rectaddpt(Rect(0, Nubheight/2, Nubwidth, Nubheight/2+1), o),
924
		display->black, clipr, addpt(o, Pt(0, Nubheight/2)));
925
	if(!t->expanded)
926
		draw(m, rectaddpt(Rect(Nubwidth/2, 0, Nubwidth/2+1, Nubheight), o),
927
			display->black, clipr, addpt(o, Pt(Nubwidth/2, 0)));
928
 
929
}
930
 
931
uint
932
drawnode(Tnode *t, Image *m, Image *clipr, Point o)
933
{
934
	int i;
935
	char *fs, *s;
936
	uint dy;
937
	Point oo;
938
 
939
	if(t == nil)
940
		return 0;
941
 
942
	t->offset = o;
943
 
944
	oo = Pt(o.x+Nubwidth+2, o.y);
945
//	if(t->draw)
946
//		dy = (*t->draw)(t, m, clipr, oo);
947
//	else{
948
		fs = nil;
949
		if(t->str)
950
			s = t->str;
951
	//	else if(t->strfn)
952
	//		fs = s = (*t->strfn)(t);
953
		else
954
			s = "???";
955
		dy = drawtext(s, m, clipr, oo);
956
		free(fs);
957
//	}
958
 
959
	if(t->expanded){
960
		if(t->nkid == -1 && t->expand)
961
			(*t->expand)(t);
962
		oo = Pt(o.x+Nubwidth+(Linewidth-Nubwidth)/2, o.y+dy);
963
		for(i=0; i<t->nkid; i++)
964
			oo.y += drawnode(t->kid[i], m, clipr, oo);
965
		dy = oo.y - o.y;
966
	}
967
	drawnub(m, clipr, o, t);
968
	return dy;
969
}
970
 
971
void
972
drawtree(Tree *t, Image *m, Rectangle r)
973
{
974
	Point p;
975
 
976
	draw(m, r, display->white, nil, ZP);
977
 
978
	replclipr(t->clipr, 1, r);
979
	p = addpt(t->offset, r.min);
980
	drawnode(t->root, m, t->clipr, p);
981
}
982
 
983
Tnode*
984
findnode(Tnode *t, Point p)
985
{
986
	int i;
987
	Tnode *tt;
988
 
989
	if(ptinrect(p, rectaddpt(Rect(0,0,Nubwidth, Nubheight), t->offset)))
990
		return t;
991
	if(!t->expanded)
992
		return nil;
993
	for(i=0; i<t->nkid; i++)
994
		if(tt = findnode(t->kid[i], p))
995
			return tt;
996
	return nil;
997
}
998
 
999
void
1000
usage(void)
1001
{
1002
	fprint(2, "usage: vtree /dev/sdC0/fossil\n");
1003
	exits("usage");
1004
}
1005
 
1006
Tree t;
1007
 
1008
void
1009
eresized(int new)
1010
{
1011
	Rectangle r;
1012
	r = screen->r;
1013
	if(new && getwindow(display, Refnone) < 0)
1014
		fprint(2,"can't reattach to window");
1015
	drawtree(&t, screen, screen->r);
1016
}
1017
 
1018
enum
1019
{
1020
	Left = 1<<0,
1021
	Middle = 1<<1,
1022
	Right = 1<<2,
1023
 
1024
	MMenu = 2,
1025
};
1026
 
1027
char *items[] = { "exit", 0 };
1028
enum { IExit, };
1029
 
1030
Menu menu;
1031
 
1032
void
1033
main(int argc, char **argv)
1034
{
1035
	int n;
1036
	char *dir;
1037
	Event e;
1038
	Point op, p;
1039
	Tnode *tn;
1040
	Mouse m;
1041
	int Eready;
1042
	Atree *fs;
1043
 
1044
	ARGBEGIN{
1045
	case 'a':
1046
		showinactive = 1;
1047
		break;
1048
	default:
1049
		usage();
1050
	}ARGEND
1051
 
1052
	switch(argc){
1053
	default:
1054
		usage();
1055
	case 1:
1056
		dir = argv[0];
1057
		break;
1058
	}
1059
 
1060
	fs = atreeinit(dir);
1061
	initdraw(0, "/lib/font/bit/lucidasans/unicode.8.font", "tree");
1062
	t.root = fs->root;
1063
	t.offset = ZP;
1064
	t.clipr = allocimage(display, Rect(0,0,1,1), GREY1, 1, DOpaque);
1065
 
1066
	eresized(0);
1067
	flushimage(display, 1);
1068
 
1069
	einit(Emouse);
1070
 
1071
	menu.item = items;
1072
	menu.gen = 0;
1073
	menu.lasthit = 0;
1074
	if(fs->resizefd > 0){
1075
		Eready = 1<<3;
1076
		estart(Eready, fs->resizefd, 1);
1077
	}else
1078
		Eready = 0;
1079
 
1080
	for(;;){
1081
		switch(n=eread(Emouse|Eready, &e)){
1082
		default:
1083
			if(Eready && n==Eready)
1084
				eresized(0);
1085
			break;
1086
		case Emouse:
1087
			m = e.mouse;
1088
			switch(m.buttons){
1089
			case Left:
1090
				op = t.offset;
1091
				p = m.xy;
1092
				do {
1093
					t.offset = addpt(t.offset, subpt(m.xy, p));
1094
					p = m.xy;
1095
					eresized(0);
1096
					m = emouse();
1097
				}while(m.buttons == Left);
1098
				if(m.buttons){
1099
					t.offset = op;
1100
					eresized(0);
1101
				}
1102
				break;
1103
			case Middle:
1104
				n = emenuhit(MMenu, &m, &menu);
1105
				if(n == -1)
1106
					break;
1107
				switch(n){
1108
				case IExit:
1109
					exits(nil);
1110
				}
1111
				break;
1112
			case Right:
1113
				do
1114
					m = emouse();
1115
				while(m.buttons == Right);
1116
				if(m.buttons)
1117
					break;
1118
				tn = findnode(t.root, m.xy);
1119
				if(tn){
1120
					tn->expanded = !tn->expanded;
1121
					eresized(0);
1122
				}
1123
				break;
1124
			}
1125
		}
1126
	}
1127
}