URI:
       tview.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tview.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 }