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*
bsStr(int state)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*
btStr(int type)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*
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 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*
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))
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*
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;
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->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
nilgen(void *,Block *,int,Tnode **)508 nilgen(void*, Block*, int, Tnode**)
509 {
510 return -1;
511 }
512
513 Tnode*
initxblock(Block * b,char * s,int (* gen)(void *,Block *,int,Tnode **),void * arg)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
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;
561 Entry e;
562 };
563
564 void
xentryexpand(Tnode * tt)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*
initxentry(Entry e)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
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 *,Block * b,int o,Tnode ** tp)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*
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 *,Block * b,int o,Tnode ** tp)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*
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 *,int o,Tnode ** tp)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
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->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*
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 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
drawtext(char * s,Image * m,Image * clipr,Point o)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
drawnub(Image * m,Image * clipr,Point o,Tnode * t)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
drawnode(Tnode * t,Image * m,Image * clipr,Point o)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
drawtree(Tree * t,Image * m,Rectangle r)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*
findnode(Tnode * t,Point p)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
usage(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
eresized(int new)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
main(int argc,char ** argv)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 }
1128