1125 lines
19 KiB
C
1125 lines
19 KiB
C
#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;
|
|
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];
|
|
|
|
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)
|
|
{
|
|
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
|
|
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);
|
|
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:
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|