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 Tnode* 305 stringnode(char *fmt, ...) 306 { 307 va_list arg; 308 Tnode *t; 309 310 t = mallocz(sizeof(Tnode), 1); 311 va_start(arg, fmt); 312 t->str = vsmprint(fmt, arg); 313 va_end(arg); 314 t->nkid = -1; 315 return t; 316 } 317 318 void 319 xcacheexpand(Tnode *t) 320 { 321 if(t->nkid >= 0) 322 return; 323 324 t->nkid = 1; 325 t->kid = mallocz(sizeof(t->kid[0])*t->nkid, 1); 326 t->kid[0] = initxheader(); 327 } 328 329 Tnode* 330 initxcache(char *name) 331 { 332 Tnode *t; 333 334 if((fd = open(name, OREAD)) < 0) 335 sysfatal("cannot open %s: %r", name); 336 337 t = stringnode("%s", name); 338 t->expand = xcacheexpand; 339 return t; 340 } 341 342 void 343 xheaderexpand(Tnode *t) 344 { 345 if(t->nkid >= 0) 346 return; 347 348 t->nkid = 1; 349 t->kid = mallocz(sizeof(t->kid[0])*t->nkid, 1); 350 t->kid[0] = initxsuper(); 351 //t->kid[1] = initxlabel(h.label); 352 //t->kid[2] = initxdata(h.data); 353 } 354 355 Tnode* 356 initxheader(void) 357 { 358 u8int buf[HeaderSize]; 359 Tnode *t; 360 361 if(pread(fd, buf, HeaderSize, HeaderOffset) < HeaderSize) 362 return stringnode("error reading header: %r"); 363 if(!headerUnpack(&h, buf)) 364 return stringnode("error unpacking header: %R"); 365 366 t = stringnode("header " 367 "version=%#ux (%d) " 368 "blockSize=%#ux (%d) " 369 "super=%#lux (%ld) " 370 "label=%#lux (%ld) " 371 "data=%#lux (%ld) " 372 "end=%#lux (%ld)", 373 h.version, h.version, h.blockSize, h.blockSize, 374 h.super, h.super, 375 h.label, h.label, h.data, h.data, h.end, h.end); 376 t->expand = xheaderexpand; 377 return t; 378 } 379 380 void 381 xsuperexpand(Tnode *t) 382 { 383 if(t->nkid >= 0) 384 return; 385 386 t->nkid = 1; 387 t->kid = mallocz(sizeof(t->kid[0])*t->nkid, 1); 388 t->kid[0] = initxlocalroot("active", super.active); 389 // t->kid[1] = initxlocalroot("next", super.next); 390 // t->kid[2] = initxlocalroot("current", super.current); 391 } 392 393 Tnode* 394 initxsuper(void) 395 { 396 Block *b; 397 Tnode *t; 398 399 b = readBlock(PartSuper, 0); 400 if(b == nil) 401 return stringnode("reading super: %r"); 402 if(!superUnpack(&super, b->data)){ 403 blockPut(b); 404 return stringnode("unpacking super: %R"); 405 } 406 blockPut(b); 407 t = stringnode("super " 408 "version=%#ux " 409 "epoch=[%#ux,%#ux) " 410 "qid=%#llux " 411 "active=%#x " 412 "next=%#x " 413 "current=%#x " 414 "last=%V " 415 "name=%s", 416 super.version, super.epochLow, super.epochHigh, 417 super.qid, super.active, super.next, super.current, 418 super.last, super.name); 419 t->expand = xsuperexpand; 420 return t; 421 } 422 423 void 424 xvacrootexpand(Tnode *t) 425 { 426 if(t->nkid >= 0) 427 return; 428 429 t->nkid = 1; 430 t->kid = mallocz(sizeof(t->kid[0])*t->nkid, 1); 431 t->kid[0] = initxroot("root", vac.score); 432 } 433 434 Tnode* 435 initxvacroot(uchar score[VtScoreSize]) 436 { 437 Tnode *t; 438 uchar buf[VtRootSize]; 439 int n; 440 441 if((n = vtRead(z, score, VtRootType, buf, VtRootSize)) < 0) 442 return stringnode("reading root %V: %R", score); 443 444 if(!vtRootUnpack(&vac, buf)) 445 return stringnode("unpack %d-byte root: %R", n); 446 447 h.blockSize = vac.blockSize; 448 t = stringnode("vac version=%#ux name=%s type=%s blockSize=%ud score=%V prev=%V", 449 vac.version, vac.name, vac.type, vac.blockSize, vac.score, vac.prev); 450 t->expand = xvacrootexpand; 451 return t; 452 } 453 454 Tnode* 455 initxlabel(Label l) 456 { 457 return stringnode("label type=%s state=%s epoch=%#ux tag=%#ux", 458 btStr(l.type), bsStr(l.state), l.epoch, l.tag); 459 } 460 461 typedef struct Xblock Xblock; 462 struct Xblock 463 { 464 Tnode; 465 Block *b; 466 int (*gen)(void*, Block*, int, Tnode**); 467 void *arg; 468 int printlabel; 469 }; 470 471 void 472 xblockexpand(Tnode *tt) 473 { 474 int i, j; 475 enum { Q = 32 }; 476 Xblock *t = (Xblock*)tt; 477 Tnode *nn; 478 479 if(t->nkid >= 0) 480 return; 481 482 j = 0; 483 if(t->printlabel){ 484 t->kid = mallocz(Q*sizeof(t->kid[0]), 1); 485 t->kid[0] = initxlabel(t->b->l); 486 j = 1; 487 } 488 489 for(i=0;; i++){ 490 switch((*t->gen)(t->arg, t->b, i, &nn)){ 491 case -1: 492 t->nkid = j; 493 return; 494 case 0: 495 break; 496 case 1: 497 if(j%Q == 0) 498 t->kid = realloc(t->kid, (j+Q)*sizeof(t->kid[0])); 499 t->kid[j++] = nn; 500 break; 501 } 502 } 503 } 504 505 int 506 nilgen(void*, Block*, int, Tnode**) 507 { 508 return -1; 509 } 510 511 Tnode* 512 initxblock(Block *b, char *s, int (*gen)(void*, Block*, int, Tnode**), void *arg) 513 { 514 Xblock *t; 515 516 if(gen == nil) 517 gen = nilgen; 518 t = mallocz(sizeof(Xblock), 1); 519 t->b = b; 520 t->gen = gen; 521 t->arg = arg; 522 if(b->addr == NilBlock) 523 t->str = smprint("Block %V: %s", b->score, s); 524 else 525 t->str = smprint("Block %#ux: %s", b->addr, s); 526 t->printlabel = 1; 527 t->nkid = -1; 528 t->expand = xblockexpand; 529 return t; 530 } 531 532 int 533 xentrygen(void *v, Block *b, int o, Tnode **tp) 534 { 535 Entry e; 536 Entry *ed; 537 538 ed = v; 539 if(o >= ed->dsize/VtEntrySize) 540 return -1; 541 542 entryUnpack(&e, b->data, o); 543 if(!showinactive && !(e.flags & VtEntryActive)) 544 return 0; 545 *tp = initxentry(e); 546 return 1; 547 } 548 549 Tnode* 550 initxentryblock(Block *b, Entry *ed) 551 { 552 return initxblock(b, "entry", xentrygen, ed); 553 } 554 555 typedef struct Xentry Xentry; 556 struct Xentry 557 { 558 Tnode; 559 Entry e; 560 }; 561 562 void 563 xentryexpand(Tnode *tt) 564 { 565 Xentry *t = (Xentry*)tt; 566 567 if(t->nkid >= 0) 568 return; 569 570 t->nkid = 1; 571 t->kid = mallocz(sizeof(t->kid[0])*t->nkid, 1); 572 t->kid[0] = initxsource(t->e, 1); 573 } 574 575 Tnode* 576 initxentry(Entry e) 577 { 578 Xentry *t; 579 580 t = mallocz(sizeof *t, 1); 581 t->nkid = -1; 582 t->str = smprint("Entry gen=%#ux psize=%d dsize=%d depth=%d flags=%#ux size=%lld score=%V", 583 e.gen, e.psize, e.dsize, e.depth, e.flags, e.size, e.score); 584 if(e.flags & VtEntryLocal) 585 t->str = smprint("%s archive=%d snap=%d tag=%#ux", t->str, e.archive, e.snap, e.tag); 586 t->expand = xentryexpand; 587 t->e = e; 588 return t; 589 } 590 591 int 592 ptrgen(void *v, Block *b, int o, Tnode **tp) 593 { 594 Entry *ed; 595 Entry e; 596 597 ed = v; 598 if(o >= ed->psize/VtScoreSize) 599 return -1; 600 601 e = *ed; 602 e.depth--; 603 memmove(e.score, b->data+o*VtScoreSize, VtScoreSize); 604 if(memcmp(e.score, vtZeroScore, VtScoreSize) == 0) 605 return 0; 606 *tp = initxsource(e, 0); 607 return 1; 608 } 609 610 static int 611 etype(int flags, int depth) 612 { 613 uint t; 614 615 if(flags&VtEntryDir) 616 t = BtDir; 617 else 618 t = BtData; 619 return t+depth; 620 } 621 622 Tnode* 623 initxsource(Entry e, int dowrap) 624 { 625 Block *b; 626 Tnode *t, *tt; 627 628 b = dataBlock(e.score, etype(e.flags, e.depth), e.tag); 629 if(b == nil) 630 return stringnode("dataBlock: %r"); 631 632 if((e.flags & VtEntryActive) == 0) 633 return stringnode("inactive Entry"); 634 635 if(e.depth == 0){ 636 if(e.flags & VtEntryDir) 637 tt = initxentryblock(b, copyEntry(e)); 638 else 639 tt = initxdatablock(b, e.dsize); 640 }else{ 641 tt = initxblock(b, smprint("%s+%d pointer", (e.flags & VtEntryDir) ? "BtDir" : "BtData", e.depth), 642 ptrgen, copyEntry(e)); 643 } 644 645 /* 646 * wrap the contents of the Source in a Source node, 647 * just so it's closer to what you see in the code. 648 */ 649 if(dowrap){ 650 t = stringnode("Source"); 651 t->nkid = 1; 652 t->kid = mallocz(sizeof(Tnode*)*1, 1); 653 t->kid[0] = tt; 654 tt = t; 655 } 656 return tt; 657 } 658 659 int 660 xlocalrootgen(void*, Block *b, int o, Tnode **tp) 661 { 662 Entry e; 663 664 if(o >= 1) 665 return -1; 666 entryUnpack(&e, b->data, o); 667 *tp = initxentry(e); 668 return 1; 669 } 670 671 Tnode* 672 initxlocalroot(char *name, u32int addr) 673 { 674 uchar score[VtScoreSize]; 675 Block *b; 676 677 localToGlobal(addr, score); 678 b = dataBlock(score, BtDir, RootTag); 679 if(b == nil) 680 return stringnode("read data block %#ux: %R", addr); 681 return initxblock(b, smprint("'%s' fs root", name), xlocalrootgen, nil); 682 } 683 684 int 685 xvacrootgen(void*, Block *b, int o, Tnode **tp) 686 { 687 Entry e; 688 689 if(o >= 3) 690 return -1; 691 entryUnpack(&e, b->data, o); 692 *tp = initxentry(e); 693 return 1; 694 } 695 696 Tnode* 697 initxroot(char *name, uchar score[VtScoreSize]) 698 { 699 Block *b; 700 701 b = dataBlock(score, BtDir, RootTag); 702 if(b == nil) 703 return stringnode("read data block %V: %R", score); 704 return initxblock(b, smprint("'%s' fs root", name), xvacrootgen, nil); 705 } 706 Tnode* 707 initxdirentry(MetaEntry *me) 708 { 709 DirEntry dir; 710 Tnode *t; 711 712 if(!deUnpack(&dir, me)) 713 return stringnode("deUnpack: %R"); 714 715 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); 716 t->nkid = 1; 717 t->kid = mallocz(sizeof(t->kid[0])*1, 1); 718 t->kid[0] = stringnode( 719 "qid=%#llux\n" 720 "uid=%s gid=%s mid=%s\n" 721 "mtime=%lud mcount=%lud ctime=%lud atime=%lud\n" 722 "mode=%luo\n" 723 "plan9 %d p9path %#llux p9version %lud\n" 724 "qidSpace %d offset %#llux max %#llux", 725 dir.qid, 726 dir.uid, dir.gid, dir.mid, 727 dir.mtime, dir.mcount, dir.ctime, dir.atime, 728 dir.mode, 729 dir.plan9, dir.p9path, dir.p9version, 730 dir.qidSpace, dir.qidOffset, dir.qidMax); 731 return t; 732 } 733 734 int 735 metaentrygen(void *v, Block*, int o, Tnode **tp) 736 { 737 Tnode *t; 738 MetaBlock *mb; 739 MetaEntry me; 740 741 mb = v; 742 if(o >= mb->nindex) 743 return -1; 744 meUnpack(&me, mb, o); 745 746 t = stringnode("MetaEntry %d bytes", mb->size); 747 t->kid = mallocz(sizeof(t->kid[0])*1, 1); 748 t->kid[0] = initxdirentry(&me); 749 t->nkid = 1; 750 *tp = t; 751 return 1; 752 } 753 754 int 755 metablockgen(void *v, Block *b, int o, Tnode **tp) 756 { 757 Xblock *t; 758 MetaBlock *mb; 759 760 if(o >= 1) 761 return -1; 762 763 /* hack: reuse initxblock as a generic iterator */ 764 mb = v; 765 t = (Xblock*)initxblock(b, "", metaentrygen, mb); 766 t->str = smprint("MetaBlock %d/%d space used, %d add'l free %d/%d table used%s", 767 mb->size, mb->maxsize, mb->free, mb->nindex, mb->maxindex, 768 mb->botch ? " [BOTCH]" : ""); 769 t->printlabel = 0; 770 *tp = t; 771 return 1; 772 } 773 774 /* 775 * attempt to guess at the type of data in the block. 776 * it could just be data from a file, but we're hoping it's MetaBlocks. 777 */ 778 Tnode* 779 initxdatablock(Block *b, uint n) 780 { 781 MetaBlock mb; 782 783 if(n > h.blockSize) 784 n = h.blockSize; 785 786 if(mbUnpack(&mb, b->data, n)) 787 return initxblock(b, "metadata", metablockgen, copyMetaBlock(mb)); 788 789 return initxblock(b, "data", nil, nil); 790 } 791 792 int 793 parseScore(uchar *score, char *buf, int n) 794 { 795 int i, c; 796 797 memset(score, 0, VtScoreSize); 798 799 if(n < VtScoreSize*2) 800 return 0; 801 for(i=0; i<VtScoreSize*2; i++){ 802 if(buf[i] >= '0' && buf[i] <= '9') 803 c = buf[i] - '0'; 804 else if(buf[i] >= 'a' && buf[i] <= 'f') 805 c = buf[i] - 'a' + 10; 806 else if(buf[i] >= 'A' && buf[i] <= 'F') 807 c = buf[i] - 'A' + 10; 808 else{ 809 return 0; 810 } 811 812 if((i & 1) == 0) 813 c <<= 4; 814 815 score[i>>1] |= c; 816 } 817 return 1; 818 } 819 820 int 821 scoreFmt(Fmt *f) 822 { 823 uchar *v; 824 int i; 825 u32int addr; 826 827 v = va_arg(f->args, uchar*); 828 if(v == nil){ 829 fmtprint(f, "*"); 830 }else if((addr = globalToLocal(v)) != NilBlock) 831 fmtprint(f, "0x%.8ux", addr); 832 else{ 833 for(i = 0; i < VtScoreSize; i++) 834 fmtprint(f, "%2.2ux", v[i]); 835 } 836 837 return 0; 838 } 839 840 Atree* 841 atreeinit(char *arg) 842 { 843 Atree *a; 844 uchar score[VtScoreSize]; 845 846 vtAttach(); 847 848 fmtinstall('V', scoreFmt); 849 fmtinstall('R', vtErrFmt); 850 851 z = vtDial(nil, 1); 852 if(z == nil) 853 fprint(2, "warning: cannot dial venti: %R\n"); 854 if(!vtConnect(z, 0)){ 855 fprint(2, "warning: cannot connect to venti: %R\n"); 856 z = nil; 857 } 858 a = mallocz(sizeof(Atree), 1); 859 if(strncmp(arg, "vac:", 4) == 0){ 860 if(!parseScore(score, arg+4, strlen(arg+4))){ 861 fprint(2, "cannot parse score\n"); 862 return nil; 863 } 864 a->root = initxvacroot(score); 865 }else 866 a->root = initxcache(arg); 867 a->resizefd = -1; 868 return a; 869 } 870 871 /* --- tree.c */ 872 enum 873 { 874 Nubwidth = 11, 875 Nubheight = 11, 876 Linewidth = Nubwidth*2+4, 877 }; 878 879 uint 880 drawtext(char *s, Image *m, Image *clipr, Point o) 881 { 882 char *t, *nt, *e; 883 uint dy; 884 885 if(s == nil) 886 s = "???"; 887 888 dy = 0; 889 for(t=s; t&&*t; t=nt){ 890 if(nt = strchr(t, '\n')){ 891 e = nt; 892 nt++; 893 }else 894 e = t+strlen(t); 895 896 _string(m, Pt(o.x, o.y+dy), display->black, ZP, display->defaultfont, 897 t, nil, e-t, clipr->clipr, nil, ZP, SoverD); 898 dy += display->defaultfont->height; 899 } 900 return dy; 901 } 902 903 void 904 drawnub(Image *m, Image *clipr, Point o, Tnode *t) 905 { 906 clipr = nil; 907 908 if(t->nkid == 0) 909 return; 910 if(t->nkid == -1 && t->expand == nil) 911 return; 912 913 o.y += (display->defaultfont->height-Nubheight)/2; 914 draw(m, rectaddpt(Rect(0,0,1,Nubheight), o), display->black, clipr, ZP); 915 draw(m, rectaddpt(Rect(0,0,Nubwidth,1), o), display->black, clipr, o); 916 draw(m, rectaddpt(Rect(Nubwidth-1,0,Nubwidth,Nubheight), o), 917 display->black, clipr, addpt(o, Pt(Nubwidth-1, 0))); 918 draw(m, rectaddpt(Rect(0, Nubheight-1, Nubwidth, Nubheight), o), 919 display->black, clipr, addpt(o, Pt(0, Nubheight-1))); 920 921 draw(m, rectaddpt(Rect(0, Nubheight/2, Nubwidth, Nubheight/2+1), o), 922 display->black, clipr, addpt(o, Pt(0, Nubheight/2))); 923 if(!t->expanded) 924 draw(m, rectaddpt(Rect(Nubwidth/2, 0, Nubwidth/2+1, Nubheight), o), 925 display->black, clipr, addpt(o, Pt(Nubwidth/2, 0))); 926 927 } 928 929 uint 930 drawnode(Tnode *t, Image *m, Image *clipr, Point o) 931 { 932 int i; 933 char *fs, *s; 934 uint dy; 935 Point oo; 936 937 if(t == nil) 938 return 0; 939 940 t->offset = o; 941 942 oo = Pt(o.x+Nubwidth+2, o.y); 943 // if(t->draw) 944 // dy = (*t->draw)(t, m, clipr, oo); 945 // else{ 946 fs = nil; 947 if(t->str) 948 s = t->str; 949 // else if(t->strfn) 950 // fs = s = (*t->strfn)(t); 951 else 952 s = "???"; 953 dy = drawtext(s, m, clipr, oo); 954 free(fs); 955 // } 956 957 if(t->expanded){ 958 if(t->nkid == -1 && t->expand) 959 (*t->expand)(t); 960 oo = Pt(o.x+Nubwidth+(Linewidth-Nubwidth)/2, o.y+dy); 961 for(i=0; i<t->nkid; i++) 962 oo.y += drawnode(t->kid[i], m, clipr, oo); 963 dy = oo.y - o.y; 964 } 965 drawnub(m, clipr, o, t); 966 return dy; 967 } 968 969 void 970 drawtree(Tree *t, Image *m, Rectangle r) 971 { 972 Point p; 973 974 draw(m, r, display->white, nil, ZP); 975 976 replclipr(t->clipr, 1, r); 977 p = addpt(t->offset, r.min); 978 drawnode(t->root, m, t->clipr, p); 979 } 980 981 Tnode* 982 findnode(Tnode *t, Point p) 983 { 984 int i; 985 Tnode *tt; 986 987 if(ptinrect(p, rectaddpt(Rect(0,0,Nubwidth, Nubheight), t->offset))) 988 return t; 989 if(!t->expanded) 990 return nil; 991 for(i=0; i<t->nkid; i++) 992 if(tt = findnode(t->kid[i], p)) 993 return tt; 994 return nil; 995 } 996 997 void 998 usage(void) 999 { 1000 fprint(2, "usage: vtree /dev/sdC0/fossil\n"); 1001 exits("usage"); 1002 } 1003 1004 Tree t; 1005 1006 void 1007 eresized(int new) 1008 { 1009 Rectangle r; 1010 r = screen->r; 1011 if(new && getwindow(display, Refnone) < 0) 1012 fprint(2,"can't reattach to window"); 1013 drawtree(&t, screen, screen->r); 1014 } 1015 1016 enum 1017 { 1018 Left = 1<<0, 1019 Middle = 1<<1, 1020 Right = 1<<2, 1021 1022 MMenu = 2, 1023 }; 1024 1025 char *items[] = { "exit", 0 }; 1026 enum { IExit, }; 1027 1028 Menu menu; 1029 1030 void 1031 main(int argc, char **argv) 1032 { 1033 int n; 1034 char *dir; 1035 Event e; 1036 Point op, p; 1037 Tnode *tn; 1038 Mouse m; 1039 int Eready; 1040 Atree *fs; 1041 1042 ARGBEGIN{ 1043 case 'a': 1044 showinactive = 1; 1045 break; 1046 default: 1047 usage(); 1048 }ARGEND 1049 1050 switch(argc){ 1051 default: 1052 usage(); 1053 case 1: 1054 dir = argv[0]; 1055 break; 1056 } 1057 1058 fs = atreeinit(dir); 1059 initdraw(0, "/lib/font/bit/lucidasans/unicode.8.font", "tree"); 1060 t.root = fs->root; 1061 t.offset = ZP; 1062 t.clipr = allocimage(display, Rect(0,0,1,1), GREY1, 1, DOpaque); 1063 1064 eresized(0); 1065 flushimage(display, 1); 1066 1067 einit(Emouse); 1068 1069 menu.item = items; 1070 menu.gen = 0; 1071 menu.lasthit = 0; 1072 if(fs->resizefd > 0){ 1073 Eready = 1<<3; 1074 estart(Eready, fs->resizefd, 1); 1075 }else 1076 Eready = 0; 1077 1078 for(;;){ 1079 switch(n=eread(Emouse|Eready, &e)){ 1080 default: 1081 if(Eready && n==Eready) 1082 eresized(0); 1083 break; 1084 case Emouse: 1085 m = e.mouse; 1086 switch(m.buttons){ 1087 case Left: 1088 op = t.offset; 1089 p = m.xy; 1090 do { 1091 t.offset = addpt(t.offset, subpt(m.xy, p)); 1092 p = m.xy; 1093 eresized(0); 1094 m = emouse(); 1095 }while(m.buttons == Left); 1096 if(m.buttons){ 1097 t.offset = op; 1098 eresized(0); 1099 } 1100 break; 1101 case Middle: 1102 n = emenuhit(MMenu, &m, &menu); 1103 if(n == -1) 1104 break; 1105 switch(n){ 1106 case IExit: 1107 exits(nil); 1108 } 1109 break; 1110 case Right: 1111 do 1112 m = emouse(); 1113 while(m.buttons == Right); 1114 if(m.buttons) 1115 break; 1116 tn = findnode(t.root, m.xy); 1117 if(tn){ 1118 tn->expanded = !tn->expanded; 1119 eresized(0); 1120 } 1121 break; 1122 } 1123 } 1124 } 1125 } 1126