venti: add venti/dump program
diff --git a/src/cmd/venti/dump.c b/src/cmd/venti/dump.c
new file mode 100644
index 0000000..8481303
--- /dev/null
+++ b/src/cmd/venti/dump.c
@@ -0,0 +1,134 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <venti.h>
+#include <libsec.h>
+#include <thread.h>
+
+VtConn *z;
+char *host;
+
+void
+usage(void)
+{
+	fprint(2, "usage: venti/dump [-h host] score\n");
+	threadexitsall("usage");
+}
+
+Biobuf bout;
+char spaces[256];
+
+void
+dump(int indent, uchar *score, int type)
+{
+	int i, n;
+	uchar *buf;
+	VtEntry e;
+	VtRoot root;
+	
+	if(spaces[0] == 0)
+		memset(spaces, ' ', sizeof spaces-1);
+
+	buf = vtmallocz(VtMaxLumpSize);
+	if(memcmp(score, vtzeroscore, VtScoreSize) == 0)
+		n = 0;
+	else
+		n = vtread(z, score, type, buf, VtMaxLumpSize);
+	if(n < 0){
+		Bprint(&bout, "%.*serror reading %V: %r\n", indent*4, spaces, score);
+		goto out;
+	}
+	switch(type){
+	case VtRootType:
+		if(vtrootunpack(&root, buf) < 0){
+			Bprint(&bout, "%.*serror unpacking root %V: %r\n", indent*4, spaces, score);
+			goto out;
+		}
+		Bprint(&bout, "%.*s%V root name=%s type=%s prev=%V bsize=%d\n",
+			indent*4, spaces, score, root.name, root.type, root.prev, root.blocksize);
+		dump(indent+1, root.score, VtDirType);
+		break;
+	
+	case VtDirType:
+		Bprint(&bout, "%.*s%V dir n=%d\n", indent*4, spaces, score, n);
+		for(i=0; i*VtEntrySize<n; i++){
+			if(vtentryunpack(&e, buf, i) < 0){
+				Bprint(&bout, "%.*s%d: cannot unpack\n", indent+1, spaces, i);
+				continue;
+			}
+			Bprint(&bout, "%.*s%d: gen=%#lux psize=%d dsize=%d type=%d flags=%#x size=%llud score=%V\n",
+				(indent+1)*4, spaces, i, e.gen, e.psize, e.dsize, e.type, e.flags, e.size, e.score);
+			dump(indent+2, e.score, e.type);
+		}
+		break;
+	
+	case VtDataType:
+		Bprint(&bout, "%.*s%V data n=%d", indent*4, spaces, score, n);
+		for(i=0; i<n; i++){
+			if(i%16 == 0)
+				Bprint(&bout, "\n%.*s", (indent+1)*4, spaces);
+			Bprint(&bout, " %02x", buf[i]);
+		}
+		Bprint(&bout, "\n");
+		break;
+
+	default:
+		if(type >= VtDirType)
+			Bprint(&bout, "%.*s%V dir+%d\n", indent*4, spaces, score, type-VtDirType);
+		else
+			Bprint(&bout, "%.*s%V data+%d\n", indent*4, spaces, score, type-VtDirType);
+		for(i=0; i<n; i+=VtScoreSize)
+			dump(indent+1, buf+i, type-1);
+		break;
+	}
+out:
+	free(buf);		
+}
+
+
+void
+threadmain(int argc, char *argv[])
+{
+	int type, n;
+	uchar score[VtScoreSize];
+	uchar *buf;
+	char *prefix;
+
+	fmtinstall('F', vtfcallfmt);
+	fmtinstall('V', vtscorefmt);
+
+	type = -1;
+	ARGBEGIN{
+	case 'h':
+		host = EARGF(usage());
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	if(argc != 1)
+		usage();
+
+	if(vtparsescore(argv[0], &prefix, score) < 0)
+		sysfatal("could not parse score: %r");
+
+	buf = vtmallocz(VtMaxLumpSize);
+	z = vtdial(host);
+	if(z == nil)
+		sysfatal("dialing venti: %r");
+	if(vtconnect(z) < 0)
+		sysfatal("vtconnect src: %r");
+
+	for(type=0; type<VtMaxType; type++){
+		n = vtread(z, score, type, buf, VtMaxLumpSize);
+		if(n >= 0)
+			goto havetype;
+	}
+	sysfatal("cannot find block %V", score);
+
+havetype:
+	Binit(&bout, 1, OWRITE);
+	dump(0, score, type);
+	Bflush(&bout);
+	threadexitsall(nil);
+}
diff --git a/src/cmd/venti/mkfile b/src/cmd/venti/mkfile
index eb68bab..b5cfff7 100644
--- a/src/cmd/venti/mkfile
+++ b/src/cmd/venti/mkfile
@@ -7,6 +7,7 @@
 	read\
 	sync\
 	write\
+	dump\
 
 BIN=$BIN/venti