|  | #include "stdinc.h" | 
|  | #include "dat.h" | 
|  | #include "fns.h" | 
|  | #include <draw.h> | 
|  | #include <event.h> | 
|  |  | 
|  | /* --- tree.h */ | 
|  | typedef struct Tree Tree; | 
|  | typedef struct Tnode Tnode; | 
|  |  | 
|  | struct Tree | 
|  | { | 
|  | Tnode *root; | 
|  | Point offset; | 
|  | Image *clipr; | 
|  | }; | 
|  |  | 
|  | struct Tnode | 
|  | { | 
|  | Point offset; | 
|  |  | 
|  | char *str; | 
|  | //	char *(*strfn)(Tnode*); | 
|  | //	uint (*draw)(Tnode*, Image*, Image*, Point); | 
|  | void (*expand)(Tnode*); | 
|  | void (*collapse)(Tnode*); | 
|  |  | 
|  | uint expanded; | 
|  | Tnode **kid; | 
|  | int nkid; | 
|  | void *aux; | 
|  | }; | 
|  |  | 
|  | typedef struct Atree Atree; | 
|  | struct Atree | 
|  | { | 
|  | int resizefd; | 
|  | Tnode *root; | 
|  | }; | 
|  |  | 
|  | Atree *atreeinit(char*); | 
|  |  | 
|  | /* --- visfossil.c */ | 
|  | Tnode *initxheader(void); | 
|  | Tnode *initxcache(char *name); | 
|  | Tnode *initxsuper(void); | 
|  | Tnode *initxlocalroot(char *name, u32int addr); | 
|  | Tnode *initxentry(Entry); | 
|  | Tnode *initxsource(Entry, int); | 
|  | Tnode *initxentryblock(Block*, Entry*); | 
|  | Tnode *initxdatablock(Block*, uint); | 
|  | Tnode *initxroot(char *name, uchar[VtScoreSize]); | 
|  |  | 
|  | int fd; | 
|  | int mainstacksize = STACK; | 
|  | Header h; | 
|  | Super super; | 
|  | VtConn *z; | 
|  | VtRoot vac; | 
|  | int showinactive; | 
|  |  | 
|  | /* | 
|  | * dumbed down versions of fossil routines | 
|  | */ | 
|  | char* | 
|  | bsStr(int state) | 
|  | { | 
|  | static char s[100]; | 
|  |  | 
|  | if(state == BsFree) | 
|  | return "Free"; | 
|  | if(state == BsBad) | 
|  | return "Bad"; | 
|  |  | 
|  | sprint(s, "%x", state); | 
|  | if(!(state&BsAlloc)) | 
|  | strcat(s, ",Free");	/* should not happen */ | 
|  | if(state&BsVenti) | 
|  | strcat(s, ",Venti"); | 
|  | if(state&BsClosed) | 
|  | strcat(s, ",Closed"); | 
|  | return s; | 
|  | } | 
|  |  | 
|  | char *bttab[] = { | 
|  | "BtData", | 
|  | "BtData+1", | 
|  | "BtData+2", | 
|  | "BtData+3", | 
|  | "BtData+4", | 
|  | "BtData+5", | 
|  | "BtData+6", | 
|  | "BtData+7", | 
|  | "BtDir", | 
|  | "BtDir+1", | 
|  | "BtDir+2", | 
|  | "BtDir+3", | 
|  | "BtDir+4", | 
|  | "BtDir+5", | 
|  | "BtDir+6", | 
|  | "BtDir+7", | 
|  | }; | 
|  |  | 
|  | char* | 
|  | btStr(int type) | 
|  | { | 
|  | if(type < nelem(bttab)) | 
|  | return bttab[type]; | 
|  | return "unknown"; | 
|  | } | 
|  |  | 
|  | Block* | 
|  | allocBlock(void) | 
|  | { | 
|  | Block *b; | 
|  |  | 
|  | b = mallocz(sizeof(Block)+h.blockSize, 1); | 
|  | b->data = (void*)&b[1]; | 
|  | return b; | 
|  | } | 
|  |  | 
|  | void | 
|  | blockPut(Block *b) | 
|  | { | 
|  | free(b); | 
|  | } | 
|  |  | 
|  | static u32int | 
|  | partStart(int part) | 
|  | { | 
|  | switch(part){ | 
|  | default: | 
|  | assert(0); | 
|  | case PartSuper: | 
|  | return h.super; | 
|  | case PartLabel: | 
|  | return h.label; | 
|  | case PartData: | 
|  | return h.data; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | static u32int | 
|  | partEnd(int part) | 
|  | { | 
|  | switch(part){ | 
|  | default: | 
|  | assert(0); | 
|  | case PartSuper: | 
|  | return h.super+1; | 
|  | case PartLabel: | 
|  | return h.data; | 
|  | case PartData: | 
|  | return h.end; | 
|  | } | 
|  | } | 
|  |  | 
|  | Block* | 
|  | readBlock(int part, u32int addr) | 
|  | { | 
|  | u32int start, end; | 
|  | u64int offset; | 
|  | int n, nn; | 
|  | Block *b; | 
|  | uchar *buf; | 
|  |  | 
|  | start = partStart(part); | 
|  | end = partEnd(part); | 
|  | if(addr >= end-start){ | 
|  | werrstr("bad addr 0x%.8ux; wanted 0x%.8ux - 0x%.8ux", addr, start, end); | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | b = allocBlock(); | 
|  | b->addr = addr; | 
|  | buf = b->data; | 
|  | offset = ((u64int)(addr+start))*h.blockSize; | 
|  | n = h.blockSize; | 
|  | while(n > 0){ | 
|  | nn = pread(fd, buf, n, offset); | 
|  | if(nn < 0){ | 
|  | blockPut(b); | 
|  | return nil; | 
|  | } | 
|  | if(nn == 0){ | 
|  | werrstr("short read"); | 
|  | blockPut(b); | 
|  | return nil; | 
|  | } | 
|  | n -= nn; | 
|  | offset += nn; | 
|  | buf += nn; | 
|  | } | 
|  | return b; | 
|  | } | 
|  |  | 
|  | int vtType[BtMax] = { | 
|  | VtDataType,		/* BtData | 0  */ | 
|  | VtDataType+1,		/* BtData | 1  */ | 
|  | VtDataType+2,		/* BtData | 2  */ | 
|  | VtDataType+3,		/* BtData | 3  */ | 
|  | VtDataType+4,		/* BtData | 4  */ | 
|  | VtDataType+5,		/* BtData | 5  */ | 
|  | VtDataType+6,		/* BtData | 6  */ | 
|  | VtDataType+7,		/* BtData | 7  */ | 
|  | VtDirType,		/* BtDir | 0  */ | 
|  | VtDirType+1,		/* BtDir | 1  */ | 
|  | VtDirType+2,		/* BtDir | 2  */ | 
|  | VtDirType+3,		/* BtDir | 3  */ | 
|  | VtDirType+4,		/* BtDir | 4  */ | 
|  | VtDirType+5,		/* BtDir | 5  */ | 
|  | VtDirType+6,		/* BtDir | 6  */ | 
|  | VtDirType+7,		/* BtDir | 7  */ | 
|  | }; | 
|  |  | 
|  | Block* | 
|  | ventiBlock(uchar score[VtScoreSize], uint type) | 
|  | { | 
|  | int n; | 
|  | Block *b; | 
|  |  | 
|  | b = allocBlock(); | 
|  | memmove(b->score, score, VtScoreSize); | 
|  | b->addr = NilBlock; | 
|  |  | 
|  | n = vtread(z, b->score, vtType[type], b->data, h.blockSize); | 
|  | if(n < 0){ | 
|  | fprint(2, "vtread returns %d: %r\n", n); | 
|  | blockPut(b); | 
|  | return nil; | 
|  | } | 
|  | vtzeroextend(vtType[type], b->data, n, h.blockSize); | 
|  | b->l.type = type; | 
|  | b->l.state = 0; | 
|  | b->l.tag = 0; | 
|  | b->l.epoch = 0; | 
|  | return b; | 
|  | } | 
|  |  | 
|  | Block* | 
|  | dataBlock(uchar score[VtScoreSize], uint type, uint tag) | 
|  | { | 
|  | Block *b, *bl; | 
|  | int lpb; | 
|  | Label l; | 
|  | u32int addr; | 
|  |  | 
|  | addr = globalToLocal(score); | 
|  | if(addr == NilBlock) | 
|  | return ventiBlock(score, type); | 
|  |  | 
|  | lpb = h.blockSize/LabelSize; | 
|  | bl = readBlock(PartLabel, addr/lpb); | 
|  | if(bl == nil) | 
|  | return nil; | 
|  | if(!labelUnpack(&l, bl->data, addr%lpb)){ | 
|  | werrstr("%r"); | 
|  | blockPut(bl); | 
|  | return nil; | 
|  | } | 
|  | blockPut(bl); | 
|  | if(l.type != type){ | 
|  | werrstr("type mismatch; got %d (%s) wanted %d (%s)", | 
|  | l.type, btStr(l.type), type, btStr(type)); | 
|  | return nil; | 
|  | } | 
|  | if(tag && l.tag != tag){ | 
|  | werrstr("tag mismatch; got 0x%.8ux wanted 0x%.8ux", | 
|  | l.tag, tag); | 
|  | return nil; | 
|  | } | 
|  | b = readBlock(PartData, addr); | 
|  | if(b == nil) | 
|  | return nil; | 
|  | b->l = l; | 
|  | return b; | 
|  | } | 
|  |  | 
|  | Entry* | 
|  | copyEntry(Entry e) | 
|  | { | 
|  | Entry *p; | 
|  |  | 
|  | p = mallocz(sizeof *p, 1); | 
|  | *p = e; | 
|  | return p; | 
|  | } | 
|  |  | 
|  | MetaBlock* | 
|  | copyMetaBlock(MetaBlock mb) | 
|  | { | 
|  | MetaBlock *p; | 
|  |  | 
|  | p = mallocz(sizeof mb, 1); | 
|  | *p = mb; | 
|  | return p; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * visualizer | 
|  | */ | 
|  |  | 
|  | #pragma	varargck	argpos	stringnode	1 | 
|  |  | 
|  | Tnode* | 
|  | stringnode(char *fmt, ...) | 
|  | { | 
|  | va_list arg; | 
|  | Tnode *t; | 
|  |  | 
|  | t = mallocz(sizeof(Tnode), 1); | 
|  | va_start(arg, fmt); | 
|  | t->str = vsmprint(fmt, arg); | 
|  | va_end(arg); | 
|  | t->nkid = -1; | 
|  | return t; | 
|  | } | 
|  |  | 
|  | void | 
|  | xcacheexpand(Tnode *t) | 
|  | { | 
|  | if(t->nkid >= 0) | 
|  | return; | 
|  |  | 
|  | t->nkid = 1; | 
|  | t->kid = mallocz(sizeof(t->kid[0])*t->nkid, 1); | 
|  | t->kid[0] = initxheader(); | 
|  | } | 
|  |  | 
|  | Tnode* | 
|  | initxcache(char *name) | 
|  | { | 
|  | Tnode *t; | 
|  |  | 
|  | if((fd = open(name, OREAD)) < 0) | 
|  | sysfatal("cannot open %s: %r", name); | 
|  |  | 
|  | t = stringnode("%s", name); | 
|  | t->expand = xcacheexpand; | 
|  | return t; | 
|  | } | 
|  |  | 
|  | void | 
|  | xheaderexpand(Tnode *t) | 
|  | { | 
|  | if(t->nkid >= 0) | 
|  | return; | 
|  |  | 
|  | t->nkid = 1; | 
|  | t->kid = mallocz(sizeof(t->kid[0])*t->nkid, 1); | 
|  | t->kid[0] = initxsuper(); | 
|  | //t->kid[1] = initxlabel(h.label); | 
|  | //t->kid[2] = initxdata(h.data); | 
|  | } | 
|  |  | 
|  | Tnode* | 
|  | initxheader(void) | 
|  | { | 
|  | u8int buf[HeaderSize]; | 
|  | Tnode *t; | 
|  |  | 
|  | if(pread(fd, buf, HeaderSize, HeaderOffset) < HeaderSize) | 
|  | return stringnode("error reading header: %r"); | 
|  | if(!headerUnpack(&h, buf)) | 
|  | return stringnode("error unpacking header: %r"); | 
|  |  | 
|  | t = stringnode("header " | 
|  | "version=%#ux (%d) " | 
|  | "blockSize=%#ux (%d) " | 
|  | "super=%#lux (%ld) " | 
|  | "label=%#lux (%ld) " | 
|  | "data=%#lux (%ld) " | 
|  | "end=%#lux (%ld)", | 
|  | h.version, h.version, h.blockSize, h.blockSize, | 
|  | h.super, h.super, | 
|  | h.label, h.label, h.data, h.data, h.end, h.end); | 
|  | t->expand = xheaderexpand; | 
|  | return t; | 
|  | } | 
|  |  | 
|  | void | 
|  | xsuperexpand(Tnode *t) | 
|  | { | 
|  | if(t->nkid >= 0) | 
|  | return; | 
|  |  | 
|  | t->nkid = 1; | 
|  | t->kid = mallocz(sizeof(t->kid[0])*t->nkid, 1); | 
|  | t->kid[0] = initxlocalroot("active", super.active); | 
|  | //	t->kid[1] = initxlocalroot("next", super.next); | 
|  | //	t->kid[2] = initxlocalroot("current", super.current); | 
|  | } | 
|  |  | 
|  | Tnode* | 
|  | initxsuper(void) | 
|  | { | 
|  | Block *b; | 
|  | Tnode *t; | 
|  |  | 
|  | b = readBlock(PartSuper, 0); | 
|  | if(b == nil) | 
|  | return stringnode("reading super: %r"); | 
|  | if(!superUnpack(&super, b->data)){ | 
|  | blockPut(b); | 
|  | return stringnode("unpacking super: %r"); | 
|  | } | 
|  | blockPut(b); | 
|  | t = stringnode("super " | 
|  | "version=%#ux " | 
|  | "epoch=[%#ux,%#ux) " | 
|  | "qid=%#llux " | 
|  | "active=%#x " | 
|  | "next=%#x " | 
|  | "current=%#x " | 
|  | "last=%V " | 
|  | "name=%s", | 
|  | super.version, super.epochLow, super.epochHigh, | 
|  | super.qid, super.active, super.next, super.current, | 
|  | super.last, super.name); | 
|  | t->expand = xsuperexpand; | 
|  | return t; | 
|  | } | 
|  |  | 
|  | void | 
|  | xvacrootexpand(Tnode *t) | 
|  | { | 
|  | if(t->nkid >= 0) | 
|  | return; | 
|  |  | 
|  | t->nkid = 1; | 
|  | t->kid = mallocz(sizeof(t->kid[0])*t->nkid, 1); | 
|  | t->kid[0] = initxroot("root", vac.score); | 
|  | } | 
|  |  | 
|  | Tnode* | 
|  | initxvacroot(uchar score[VtScoreSize]) | 
|  | { | 
|  | Tnode *t; | 
|  | uchar buf[VtRootSize]; | 
|  | int n; | 
|  |  | 
|  | if((n = vtread(z, score, VtRootType, buf, VtRootSize)) < 0) | 
|  | return stringnode("reading root %V: %r", score); | 
|  |  | 
|  | if(vtrootunpack(&vac, buf) < 0) | 
|  | return stringnode("unpack %d-byte root: %r", n); | 
|  |  | 
|  | h.blockSize = vac.blocksize; | 
|  | t = stringnode("vac version=%#ux name=%s type=%s blocksize=%lud score=%V prev=%V", | 
|  | VtRootVersion, vac.name, vac.type, vac.blocksize, vac.score, vac.prev); | 
|  | t->expand = xvacrootexpand; | 
|  | return t; | 
|  | } | 
|  |  | 
|  | Tnode* | 
|  | initxlabel(Label l) | 
|  | { | 
|  | return stringnode("label type=%s state=%s epoch=%#ux tag=%#ux", | 
|  | btStr(l.type), bsStr(l.state), l.epoch, l.tag); | 
|  | } | 
|  |  | 
|  | typedef struct Xblock Xblock; | 
|  | struct Xblock | 
|  | { | 
|  | Tnode t; | 
|  | Block *b; | 
|  | int (*gen)(void*, Block*, int, Tnode**); | 
|  | void *arg; | 
|  | int printlabel; | 
|  | }; | 
|  |  | 
|  | void | 
|  | xblockexpand(Tnode *tt) | 
|  | { | 
|  | int i, j; | 
|  | enum { Q = 32 }; | 
|  | Xblock *t = (Xblock*)tt; | 
|  | Tnode *nn; | 
|  |  | 
|  | if(t->t.nkid >= 0) | 
|  | return; | 
|  |  | 
|  | j = 0; | 
|  | if(t->printlabel){ | 
|  | t->t.kid = mallocz(Q*sizeof(t->t.kid[0]), 1); | 
|  | t->t.kid[0] = initxlabel(t->b->l); | 
|  | j = 1; | 
|  | } | 
|  |  | 
|  | for(i=0;; i++){ | 
|  | switch((*t->gen)(t->arg, t->b, i, &nn)){ | 
|  | case -1: | 
|  | t->t.nkid = j; | 
|  | return; | 
|  | case 0: | 
|  | break; | 
|  | case 1: | 
|  | if(j%Q == 0) | 
|  | t->t.kid = realloc(t->t.kid, (j+Q)*sizeof(t->t.kid[0])); | 
|  | t->t.kid[j++] = nn; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | int | 
|  | nilgen(void *v, Block *b, int o, Tnode **tp) | 
|  | { | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | Tnode* | 
|  | initxblock(Block *b, char *s, int (*gen)(void *v, Block *b, int o, Tnode **tp), void *arg) | 
|  | { | 
|  | Xblock *t; | 
|  |  | 
|  | if(gen == nil) | 
|  | gen = nilgen; | 
|  | t = mallocz(sizeof(Xblock), 1); | 
|  | t->b = b; | 
|  | t->gen = gen; | 
|  | t->arg = arg; | 
|  | if(b->addr == NilBlock) | 
|  | t->t.str = smprint("Block %V: %s", b->score, s); | 
|  | else | 
|  | t->t.str = smprint("Block %#ux: %s", b->addr, s); | 
|  | t->printlabel = 1; | 
|  | t->t.nkid = -1; | 
|  | t->t.expand = xblockexpand; | 
|  | return (Tnode*)t; | 
|  | } | 
|  |  | 
|  | int | 
|  | xentrygen(void *v, Block *b, int o, Tnode **tp) | 
|  | { | 
|  | Entry e; | 
|  | Entry *ed; | 
|  |  | 
|  | ed = v; | 
|  | if(o >= ed->dsize/VtEntrySize) | 
|  | return -1; | 
|  |  | 
|  | entryUnpack(&e, b->data, o); | 
|  | if(!showinactive && !(e.flags & VtEntryActive)) | 
|  | return 0; | 
|  | *tp = initxentry(e); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | Tnode* | 
|  | initxentryblock(Block *b, Entry *ed) | 
|  | { | 
|  | return initxblock(b, "entry", xentrygen, ed); | 
|  | } | 
|  |  | 
|  | typedef struct Xentry Xentry; | 
|  | struct Xentry | 
|  | { | 
|  | Tnode t; | 
|  | Entry e; | 
|  | }; | 
|  |  | 
|  | void | 
|  | xentryexpand(Tnode *tt) | 
|  | { | 
|  | Xentry *t = (Xentry*)tt; | 
|  |  | 
|  | if(t->t.nkid >= 0) | 
|  | return; | 
|  |  | 
|  | t->t.nkid = 1; | 
|  | t->t.kid = mallocz(sizeof(t->t.kid[0])*t->t.nkid, 1); | 
|  | t->t.kid[0] = initxsource(t->e, 1); | 
|  | } | 
|  |  | 
|  | Tnode* | 
|  | initxentry(Entry e) | 
|  | { | 
|  | Xentry *t; | 
|  |  | 
|  | t = mallocz(sizeof *t, 1); | 
|  | t->t.nkid = -1; | 
|  | t->t.str = smprint("Entry gen=%#ux psize=%d dsize=%d depth=%d flags=%#ux size=%lld score=%V", | 
|  | e.gen, e.psize, e.dsize, e.depth, e.flags, e.size, e.score); | 
|  | if(e.flags & VtEntryLocal) | 
|  | t->t.str = smprint("%s archive=%d snap=%d tag=%#ux", t->t.str, e.archive, e.snap, e.tag); | 
|  | t->t.expand = xentryexpand; | 
|  | t->e = e; | 
|  | return (Tnode*)t; | 
|  | } | 
|  |  | 
|  | int | 
|  | ptrgen(void *v, Block *b, int o, Tnode **tp) | 
|  | { | 
|  | Entry *ed; | 
|  | Entry e; | 
|  |  | 
|  | ed = v; | 
|  | if(o >= ed->psize/VtScoreSize) | 
|  | return -1; | 
|  |  | 
|  | e = *ed; | 
|  | e.depth--; | 
|  | memmove(e.score, b->data+o*VtScoreSize, VtScoreSize); | 
|  | if(memcmp(e.score, vtzeroscore, VtScoreSize) == 0) | 
|  | return 0; | 
|  | *tp = initxsource(e, 0); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | etype(int flags, int depth) | 
|  | { | 
|  | uint t; | 
|  |  | 
|  | if(flags&_VtEntryDir) | 
|  | t = BtDir; | 
|  | else | 
|  | t = BtData; | 
|  | return t+depth; | 
|  | } | 
|  |  | 
|  | Tnode* | 
|  | initxsource(Entry e, int dowrap) | 
|  | { | 
|  | Block *b; | 
|  | Tnode *t, *tt; | 
|  |  | 
|  | b = dataBlock(e.score, etype(e.flags, e.depth), e.tag); | 
|  | if(b == nil) | 
|  | return stringnode("dataBlock: %r"); | 
|  |  | 
|  | if((e.flags & VtEntryActive) == 0) | 
|  | return stringnode("inactive Entry"); | 
|  |  | 
|  | if(e.depth == 0){ | 
|  | if(e.flags & _VtEntryDir) | 
|  | tt = initxentryblock(b, copyEntry(e)); | 
|  | else | 
|  | tt = initxdatablock(b, e.dsize); | 
|  | }else{ | 
|  | tt = initxblock(b, smprint("%s+%d pointer", (e.flags & _VtEntryDir) ? "BtDir" : "BtData", e.depth), | 
|  | ptrgen, copyEntry(e)); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * wrap the contents of the Source in a Source node, | 
|  | * just so it's closer to what you see in the code. | 
|  | */ | 
|  | if(dowrap){ | 
|  | t = stringnode("Source"); | 
|  | t->nkid = 1; | 
|  | t->kid = mallocz(sizeof(Tnode*)*1, 1); | 
|  | t->kid[0] = tt; | 
|  | tt = t; | 
|  | } | 
|  | return tt; | 
|  | } | 
|  |  | 
|  | int | 
|  | xlocalrootgen(void *v, Block *b, int o, Tnode **tp) | 
|  | { | 
|  | Entry e; | 
|  |  | 
|  | if(o >= 1) | 
|  | return -1; | 
|  | entryUnpack(&e, b->data, o); | 
|  | *tp = initxentry(e); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | Tnode* | 
|  | initxlocalroot(char *name, u32int addr) | 
|  | { | 
|  | uchar score[VtScoreSize]; | 
|  | Block *b; | 
|  |  | 
|  | localToGlobal(addr, score); | 
|  | b = dataBlock(score, BtDir, RootTag); | 
|  | if(b == nil) | 
|  | return stringnode("read data block %#ux: %r", addr); | 
|  | return initxblock(b, smprint("'%s' fs root", name), xlocalrootgen, nil); | 
|  | } | 
|  |  | 
|  | int | 
|  | xvacrootgen(void *v, Block *b, int o, Tnode **tp) | 
|  | { | 
|  | Entry e; | 
|  |  | 
|  | if(o >= 3) | 
|  | return -1; | 
|  | entryUnpack(&e, b->data, o); | 
|  | *tp = initxentry(e); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | Tnode* | 
|  | initxroot(char *name, uchar score[VtScoreSize]) | 
|  | { | 
|  | Block *b; | 
|  |  | 
|  | b = dataBlock(score, BtDir, RootTag); | 
|  | if(b == nil) | 
|  | return stringnode("read data block %V: %r", score); | 
|  | return initxblock(b, smprint("'%s' fs root", name), xvacrootgen, nil); | 
|  | } | 
|  | Tnode* | 
|  | initxdirentry(MetaEntry *me) | 
|  | { | 
|  | DirEntry dir; | 
|  | Tnode *t; | 
|  |  | 
|  | if(!deUnpack(&dir, me)) | 
|  | return stringnode("deUnpack: %r"); | 
|  |  | 
|  | 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); | 
|  | t->nkid = 1; | 
|  | t->kid = mallocz(sizeof(t->kid[0])*1, 1); | 
|  | t->kid[0] = stringnode( | 
|  | "qid=%#llux\n" | 
|  | "uid=%s gid=%s mid=%s\n" | 
|  | "mtime=%lud mcount=%lud ctime=%lud atime=%lud\n" | 
|  | "mode=%luo\n" | 
|  | "plan9 %d p9path %#llux p9version %lud\n" | 
|  | "qidSpace %d offset %#llux max %#llux", | 
|  | dir.qid, | 
|  | dir.uid, dir.gid, dir.mid, | 
|  | dir.mtime, dir.mcount, dir.ctime, dir.atime, | 
|  | dir.mode, | 
|  | dir.plan9, dir.p9path, dir.p9version, | 
|  | dir.qidSpace, dir.qidOffset, dir.qidMax); | 
|  | return t; | 
|  | } | 
|  |  | 
|  | int | 
|  | metaentrygen(void *v, Block *b, int o, Tnode **tp) | 
|  | { | 
|  | Tnode *t; | 
|  | MetaBlock *mb; | 
|  | MetaEntry me; | 
|  |  | 
|  | mb = v; | 
|  | if(o >= mb->nindex) | 
|  | return -1; | 
|  | meUnpack(&me, mb, o); | 
|  |  | 
|  | t = stringnode("MetaEntry %d bytes", mb->size); | 
|  | t->kid = mallocz(sizeof(t->kid[0])*1, 1); | 
|  | t->kid[0] = initxdirentry(&me); | 
|  | t->nkid = 1; | 
|  | *tp = t; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | int | 
|  | metablockgen(void *v, Block *b, int o, Tnode **tp) | 
|  | { | 
|  | Xblock *t; | 
|  | MetaBlock *mb; | 
|  |  | 
|  | if(o >= 1) | 
|  | return -1; | 
|  |  | 
|  | /* hack: reuse initxblock as a generic iterator */ | 
|  | mb = v; | 
|  | t = (Xblock*)initxblock(b, "", metaentrygen, mb); | 
|  | t->t.str = smprint("MetaBlock %d/%d space used, %d add'l free %d/%d table used%s", | 
|  | mb->size, mb->maxsize, mb->free, mb->nindex, mb->maxindex, | 
|  | mb->botch ? " [BOTCH]" : ""); | 
|  | t->printlabel = 0; | 
|  | *tp = (Tnode*)t; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * attempt to guess at the type of data in the block. | 
|  | * it could just be data from a file, but we're hoping it's MetaBlocks. | 
|  | */ | 
|  | Tnode* | 
|  | initxdatablock(Block *b, uint n) | 
|  | { | 
|  | MetaBlock mb; | 
|  |  | 
|  | if(n > h.blockSize) | 
|  | n = h.blockSize; | 
|  |  | 
|  | if(mbUnpack(&mb, b->data, n)) | 
|  | return initxblock(b, "metadata", metablockgen, copyMetaBlock(mb)); | 
|  |  | 
|  | return initxblock(b, "data", nil, nil); | 
|  | } | 
|  |  | 
|  | int | 
|  | parseScore(uchar *score, char *buf, int n) | 
|  | { | 
|  | int i, c; | 
|  |  | 
|  | memset(score, 0, VtScoreSize); | 
|  |  | 
|  | if(n < VtScoreSize*2) | 
|  | return 0; | 
|  | for(i=0; i<VtScoreSize*2; i++){ | 
|  | if(buf[i] >= '0' && buf[i] <= '9') | 
|  | c = buf[i] - '0'; | 
|  | else if(buf[i] >= 'a' && buf[i] <= 'f') | 
|  | c = buf[i] - 'a' + 10; | 
|  | else if(buf[i] >= 'A' && buf[i] <= 'F') | 
|  | c = buf[i] - 'A' + 10; | 
|  | else{ | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if((i & 1) == 0) | 
|  | c <<= 4; | 
|  |  | 
|  | score[i>>1] |= c; | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | int | 
|  | scoreFmt(Fmt *f) | 
|  | { | 
|  | uchar *v; | 
|  | int i; | 
|  | u32int addr; | 
|  |  | 
|  | v = va_arg(f->args, uchar*); | 
|  | if(v == nil){ | 
|  | fmtprint(f, "*"); | 
|  | }else if((addr = globalToLocal(v)) != NilBlock) | 
|  | fmtprint(f, "0x%.8ux", addr); | 
|  | else{ | 
|  | for(i = 0; i < VtScoreSize; i++) | 
|  | fmtprint(f, "%2.2ux", v[i]); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | Atree* | 
|  | atreeinit(char *arg) | 
|  | { | 
|  | Atree *a; | 
|  | uchar score[VtScoreSize]; | 
|  |  | 
|  | fmtinstall('V', scoreFmt); | 
|  |  | 
|  | z = vtdial(nil); | 
|  | if(z == nil) | 
|  | fprint(2, "warning: cannot dial venti: %r\n"); | 
|  | else if(vtconnect(z) < 0){ | 
|  | fprint(2, "warning: cannot connect to venti: %r\n"); | 
|  | z = nil; | 
|  | } | 
|  | a = mallocz(sizeof(Atree), 1); | 
|  | if(strncmp(arg, "vac:", 4) == 0){ | 
|  | if(!parseScore(score, arg+4, strlen(arg+4))){ | 
|  | fprint(2, "cannot parse score\n"); | 
|  | return nil; | 
|  | } | 
|  | a->root = initxvacroot(score); | 
|  | }else | 
|  | a->root = initxcache(arg); | 
|  | a->resizefd = -1; | 
|  | return a; | 
|  | } | 
|  |  | 
|  | /* --- tree.c */ | 
|  | enum | 
|  | { | 
|  | Nubwidth = 11, | 
|  | Nubheight = 11, | 
|  | Linewidth = Nubwidth*2+4, | 
|  | }; | 
|  |  | 
|  | uint | 
|  | drawtext(char *s, Image *m, Image *clipr, Point o) | 
|  | { | 
|  | char *t, *nt, *e; | 
|  | uint dy; | 
|  |  | 
|  | if(s == nil) | 
|  | s = "???"; | 
|  |  | 
|  | dy = 0; | 
|  | for(t=s; t&&*t; t=nt){ | 
|  | if(nt = strchr(t, '\n')){ | 
|  | e = nt; | 
|  | nt++; | 
|  | }else | 
|  | e = t+strlen(t); | 
|  |  | 
|  | _string(m, Pt(o.x, o.y+dy), display->black, ZP, display->defaultfont, | 
|  | t, nil, e-t, clipr->clipr, nil, ZP, SoverD); | 
|  | dy += display->defaultfont->height; | 
|  | } | 
|  | return dy; | 
|  | } | 
|  |  | 
|  | void | 
|  | drawnub(Image *m, Image *clipr, Point o, Tnode *t) | 
|  | { | 
|  | clipr = nil; | 
|  |  | 
|  | if(t->nkid == 0) | 
|  | return; | 
|  | if(t->nkid == -1 && t->expand == nil) | 
|  | return; | 
|  |  | 
|  | o.y += (display->defaultfont->height-Nubheight)/2; | 
|  | draw(m, rectaddpt(Rect(0,0,1,Nubheight), o), display->black, clipr, ZP); | 
|  | draw(m, rectaddpt(Rect(0,0,Nubwidth,1), o), display->black, clipr, o); | 
|  | draw(m, rectaddpt(Rect(Nubwidth-1,0,Nubwidth,Nubheight), o), | 
|  | display->black, clipr, addpt(o, Pt(Nubwidth-1, 0))); | 
|  | draw(m, rectaddpt(Rect(0, Nubheight-1, Nubwidth, Nubheight), o), | 
|  | display->black, clipr, addpt(o, Pt(0, Nubheight-1))); | 
|  |  | 
|  | draw(m, rectaddpt(Rect(0, Nubheight/2, Nubwidth, Nubheight/2+1), o), | 
|  | display->black, clipr, addpt(o, Pt(0, Nubheight/2))); | 
|  | if(!t->expanded) | 
|  | draw(m, rectaddpt(Rect(Nubwidth/2, 0, Nubwidth/2+1, Nubheight), o), | 
|  | display->black, clipr, addpt(o, Pt(Nubwidth/2, 0))); | 
|  |  | 
|  | } | 
|  |  | 
|  | uint | 
|  | drawnode(Tnode *t, Image *m, Image *clipr, Point o) | 
|  | { | 
|  | int i; | 
|  | char *fs, *s; | 
|  | uint dy; | 
|  | Point oo; | 
|  |  | 
|  | if(t == nil) | 
|  | return 0; | 
|  |  | 
|  | t->offset = o; | 
|  |  | 
|  | oo = Pt(o.x+Nubwidth+2, o.y); | 
|  | //	if(t->draw) | 
|  | //		dy = (*t->draw)(t, m, clipr, oo); | 
|  | //	else{ | 
|  | fs = nil; | 
|  | if(t->str) | 
|  | s = t->str; | 
|  | //	else if(t->strfn) | 
|  | //		fs = s = (*t->strfn)(t); | 
|  | else | 
|  | s = "???"; | 
|  | dy = drawtext(s, m, clipr, oo); | 
|  | free(fs); | 
|  | //	} | 
|  |  | 
|  | if(t->expanded){ | 
|  | if(t->nkid == -1 && t->expand) | 
|  | (*t->expand)(t); | 
|  | oo = Pt(o.x+Nubwidth+(Linewidth-Nubwidth)/2, o.y+dy); | 
|  | for(i=0; i<t->nkid; i++) | 
|  | oo.y += drawnode(t->kid[i], m, clipr, oo); | 
|  | dy = oo.y - o.y; | 
|  | } | 
|  | drawnub(m, clipr, o, t); | 
|  | return dy; | 
|  | } | 
|  |  | 
|  | void | 
|  | drawtree(Tree *t, Image *m, Rectangle r) | 
|  | { | 
|  | Point p; | 
|  |  | 
|  | draw(m, r, display->white, nil, ZP); | 
|  |  | 
|  | replclipr(t->clipr, 1, r); | 
|  | p = addpt(t->offset, r.min); | 
|  | drawnode(t->root, m, t->clipr, p); | 
|  | } | 
|  |  | 
|  | Tnode* | 
|  | findnode(Tnode *t, Point p) | 
|  | { | 
|  | int i; | 
|  | Tnode *tt; | 
|  |  | 
|  | if(ptinrect(p, rectaddpt(Rect(0,0,Nubwidth, Nubheight), t->offset))) | 
|  | return t; | 
|  | if(!t->expanded) | 
|  | return nil; | 
|  | for(i=0; i<t->nkid; i++) | 
|  | if(tt = findnode(t->kid[i], p)) | 
|  | return tt; | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | void | 
|  | usage(void) | 
|  | { | 
|  | fprint(2, "usage: fossil/view /dev/sdC0/fossil\n"); | 
|  | threadexitsall("usage"); | 
|  | } | 
|  |  | 
|  | Tree t; | 
|  |  | 
|  | void | 
|  | eresized(int new) | 
|  | { | 
|  | if(new && getwindow(display, Refnone) < 0) | 
|  | fprint(2,"can't reattach to window"); | 
|  | drawtree(&t, screen, screen->r); | 
|  | } | 
|  |  | 
|  | enum | 
|  | { | 
|  | Left = 1<<0, | 
|  | Middle = 1<<1, | 
|  | Right = 1<<2, | 
|  |  | 
|  | MMenu = 2, | 
|  | }; | 
|  |  | 
|  | char *items[] = { "exit", 0 }; | 
|  | enum { IExit, }; | 
|  |  | 
|  | Menu menu; | 
|  |  | 
|  | void | 
|  | threadmain(int argc, char **argv) | 
|  | { | 
|  | int n; | 
|  | char *dir; | 
|  | Event e; | 
|  | Point op, p; | 
|  | Tnode *tn; | 
|  | Mouse m; | 
|  | int Eready; | 
|  | Atree *fs; | 
|  |  | 
|  | ARGBEGIN{ | 
|  | case 'a': | 
|  | showinactive = 1; | 
|  | break; | 
|  | default: | 
|  | usage(); | 
|  | }ARGEND | 
|  |  | 
|  | switch(argc){ | 
|  | default: | 
|  | usage(); | 
|  | case 1: | 
|  | dir = argv[0]; | 
|  | break; | 
|  | } | 
|  |  | 
|  | fs = atreeinit(dir); | 
|  | #ifdef PLAN9PORT | 
|  | initdraw(0, "/lib/font/bit/lucsans/unicode.8.font", "tree"); | 
|  | #else | 
|  | initdraw(0, "/lib/font/bit/lucidasans/unicode.8.font", "tree"); | 
|  | #endif | 
|  | t.root = fs->root; | 
|  | t.offset = ZP; | 
|  | t.clipr = allocimage(display, Rect(0,0,1,1), GREY1, 1, DOpaque); | 
|  |  | 
|  | eresized(0); | 
|  | flushimage(display, 1); | 
|  |  | 
|  | einit(Emouse); | 
|  |  | 
|  | menu.item = items; | 
|  | menu.gen = 0; | 
|  | menu.lasthit = 0; | 
|  | if(fs->resizefd > 0){ | 
|  | Eready = 1<<3; | 
|  | estart(Eready, fs->resizefd, 1); | 
|  | }else | 
|  | Eready = 0; | 
|  |  | 
|  | for(;;){ | 
|  | switch(n=eread(Emouse|Eready, &e)){ | 
|  | default: | 
|  | if(Eready && n==Eready) | 
|  | eresized(0); | 
|  | break; | 
|  | case Emouse: | 
|  | m = e.mouse; | 
|  | switch(m.buttons){ | 
|  | case Left: | 
|  | op = t.offset; | 
|  | p = m.xy; | 
|  | do { | 
|  | t.offset = addpt(t.offset, subpt(m.xy, p)); | 
|  | p = m.xy; | 
|  | eresized(0); | 
|  | m = emouse(); | 
|  | }while(m.buttons == Left); | 
|  | if(m.buttons){ | 
|  | t.offset = op; | 
|  | eresized(0); | 
|  | } | 
|  | break; | 
|  | case Middle: | 
|  | n = emenuhit(MMenu, &m, &menu); | 
|  | if(n == -1) | 
|  | break; | 
|  | switch(n){ | 
|  | case IExit: | 
|  | threadexitsall(nil); | 
|  | } | 
|  | break; | 
|  | case Right: | 
|  | do | 
|  | m = emouse(); | 
|  | while(m.buttons == Right); | 
|  | if(m.buttons) | 
|  | break; | 
|  | tn = findnode(t.root, m.xy); | 
|  | if(tn){ | 
|  | tn->expanded = !tn->expanded; | 
|  | eresized(0); | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } |