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 }