diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/fossil/view.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/fossil/view.c')
-rwxr-xr-x | sys/src/cmd/fossil/view.c | 1127 |
1 files changed, 1127 insertions, 0 deletions
diff --git a/sys/src/cmd/fossil/view.c b/sys/src/cmd/fossil/view.c new file mode 100755 index 000000000..c2ac99762 --- /dev/null +++ b/sys/src/cmd/fossil/view.c @@ -0,0 +1,1127 @@ +#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; +Header h; +Super super; +VtSession *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"; +} +#pragma varargck argpos stringnode 1 + +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 */ + VtPointerType0, /* BtData | 1 */ + VtPointerType1, /* BtData | 2 */ + VtPointerType2, /* BtData | 3 */ + VtPointerType3, /* BtData | 4 */ + VtPointerType4, /* BtData | 5 */ + VtPointerType5, /* BtData | 6 */ + VtPointerType6, /* BtData | 7 */ + VtDirType, /* BtDir | 0 */ + VtPointerType0, /* BtDir | 1 */ + VtPointerType1, /* BtDir | 2 */ + VtPointerType2, /* BtDir | 3 */ + VtPointerType3, /* BtDir | 4 */ + VtPointerType4, /* BtDir | 5 */ + VtPointerType5, /* BtDir | 6 */ + VtPointerType6, /* 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)) + return stringnode("unpack %d-byte root: %R", n); + + h.blockSize = vac.blockSize; + t = stringnode("vac version=%#ux name=%s type=%s blockSize=%ud score=%V prev=%V", + vac.version, 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; + 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->nkid >= 0) + return; + + j = 0; + if(t->printlabel){ + t->kid = mallocz(Q*sizeof(t->kid[0]), 1); + 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->nkid = j; + return; + case 0: + break; + case 1: + if(j%Q == 0) + t->kid = realloc(t->kid, (j+Q)*sizeof(t->kid[0])); + t->kid[j++] = nn; + break; + } + } +} + +int +nilgen(void*, Block*, int, Tnode**) +{ + return -1; +} + +Tnode* +initxblock(Block *b, char *s, int (*gen)(void*, Block*, int, Tnode**), 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->str = smprint("Block %V: %s", b->score, s); + else + t->str = smprint("Block %#ux: %s", b->addr, s); + t->printlabel = 1; + t->nkid = -1; + t->expand = xblockexpand; + return 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; + Entry e; +}; + +void +xentryexpand(Tnode *tt) +{ + Xentry *t = (Xentry*)tt; + + if(t->nkid >= 0) + return; + + t->nkid = 1; + t->kid = mallocz(sizeof(t->kid[0])*t->nkid, 1); + t->kid[0] = initxsource(t->e, 1); +} + +Tnode* +initxentry(Entry e) +{ + Xentry *t; + + t = mallocz(sizeof *t, 1); + t->nkid = -1; + 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->str = smprint("%s archive=%d snap=%d tag=%#ux", t->str, e.archive, e.snap, e.tag); + t->expand = xentryexpand; + t->e = e; + return 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*, 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*, 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*, 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->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 = 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]; + + vtAttach(); + + fmtinstall('V', scoreFmt); + fmtinstall('R', vtErrFmt); + + z = vtDial(nil, 1); + if(z == nil) + fprint(2, "warning: cannot dial venti: %R\n"); + 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: vtree /dev/sdC0/fossil\n"); + exits("usage"); +} + +Tree t; + +void +eresized(int new) +{ + Rectangle r; + r = screen->r; + 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 +main(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); + initdraw(0, "/lib/font/bit/lucidasans/unicode.8.font", "tree"); + 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: + exits(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; + } + } + } +} |