shithub: riscv

Download patch

ref: fd8d404d520a3f10c143f5cbe7c170606fffc75c
parent: dd468419f2fbaa2c3fec570a88d13e8eae8f3faf
author: iru <devnull@localhost>
date: Wed Apr 13 20:35:48 EDT 2011

Promote the old installer/livecd specific tools to normal tools under /sys/src/cmd. Where similar common tools already existed, I kept them.

--- a/sys/lib/dist.old/cmd/bflz.c
+++ /dev/null
@@ -1,374 +1,0 @@
-/*
- * Extraordinarily brute force Lempel & Ziv-like
- * compressor.  The input file must fit in memory
- * during compression, and the output file will
- * be reconstructed in memory during decompression.
- * We search for large common sequences and use a
- * greedy algorithm to choose which sequence gets
- * compressed first.
- *
- * Files begin with "BLZ\n" and a 32-bit uncompressed file length.
- *
- * Output format is a series of blocks followed by
- * a raw data section.  Each block begins with a 32-bit big-endian
- * number.  The top bit is type and the next 31 bits
- * are uncompressed size.  Type is one of
- *	0 - use raw data for this length
- *	1 - a 32-bit offset follows
- * After the blocks come the raw data.  (The end of the blocks can be
- * noted by summing block lengths until you reach the file length.)
- */
-
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-
-#define malloc sbrk
-
-int minrun = 16;
-int win = 16;
-ulong outn;
-int verbose;
-int mindist;
-
-enum { Prime = 16777213 };	/* smallest prime < 2^24 (so p*256+256 < 2^32) */
-enum { NOFF = 3 };
-
-Biobuf bout;
-ulong length;
-uchar *data;
-ulong sum32(ulong, void*, long);
-uchar *odat;
-int nodat;
-int nraw;
-int rawstart;
-int acct;
-int zlength;
-int maxchain;
-int maxrle[256];
-int nnew;
-
-typedef struct Node Node;
-struct Node {
-	Node *link;
-	ulong key;
-	ulong offset[NOFF];
-};
-
-Node *nodepool;
-int nnodepool;
-
-Node **hash;
-uint nhash;
-
-uint maxlen;
-uint maxsame;
-uint replacesame = 8*1024*1024;
-
-Node *freelist, **freeend;
-uint nalloc;
-
-Node*
-allocnode(void)
-{
-	int i;
-	Node *n;
-
-	if(nnodepool == 0){
-		nnodepool = 256*1024;
-		nodepool = malloc(sizeof(Node)*nnodepool);
-	}
-	if(freelist){
-		n = freelist;
-		freelist = n->link;
-		return n;
-	}
-	assert(nnodepool > 0);
-	nalloc++;
-	n = &nodepool[--nnodepool];
-	for(i=0; i<NOFF; i++)
-		n->offset[i] = -1;
-
-	return n;
-}
-
-void
-freenode(Node *n)
-{
-	if(freelist == nil)
-		freelist = n;
-	else
-		*freeend = n;
-	freeend = &n->link;
-	n->link = nil;
-}
-
-Node**
-llookup(ulong key)
-{
-	uint c;
-	Node **l, **top, *n;
-	
-	if(nhash == 0){
-		uint x;
-
-		x = length/8;
-		for(nhash=1; nhash<x; nhash<<=1)
-			;
-		hash = sbrk(sizeof(Node*)*nhash);
-	}
-
-	top = &hash[key&(nhash-1)];
-	c = 0;
-	for(l=top; *l; l=&(*l)->link){
-		c++;
-		if((*l)->key == key){
-			/* move to front */
-			n = *l;
-			*l = n->link;
-			n->link = *top;
-			*top = n;
-			return top;
-		}
-	}
-	if(c > maxlen)
-		maxlen = c;
-	return l;
-}
-
-Node*
-lookup(ulong key)
-{
-	return *llookup(key);
-}
-
-void
-insertnode(ulong key, ulong offset)
-{
-	int i;
-	Node *n, **l;
-
-	l = llookup(key);
-	if(*l == nil){
-		if(l==&hash[key&(nhash-1)])
-			nnew++;
-		*l = allocnode();
-		(*l)->key = key;
-	}
-	n = *l;
-
-	/* add or replace last */
-	for(i=0; i<NOFF-1 && n->offset[i]!=-1; i++)
-		;
-	n->offset[i] = offset;
-}
-
-void
-Bputint(Biobufhdr *b, int n)
-{
-	uchar p[4];
-
-	p[0] = n>>24;
-	p[1] = n>>16;
-	p[2] = n>>8;
-	p[3] = n;
-	Bwrite(b, p, 4);
-}
-
-void
-flushraw(void)
-{
-	if(nraw){
-		if(verbose)
-			fprint(2, "Raw %d+%d\n", rawstart, nraw);
-		zlength += 4+nraw;
-		Bputint(&bout, (1<<31)|nraw);
-		memmove(odat+nodat, data+rawstart, nraw);
-		nodat += nraw;
-		nraw = 0;
-	}
-}
-
-int
-rawbyte(int i)
-{
-	assert(acct == i);
-	if(nraw == 0)
-		rawstart = i;
-	acct++;
-	nraw++;
-	return 1;
-}
-
-int
-refblock(int i, int len, int off)
-{
-	assert(acct == i);
-	acct += len;
-	if(nraw)
-		flushraw();
-	if(verbose)
-		fprint(2, "Copy %d+%d from %d\n", i, len, off);
-	Bputint(&bout, len);
-	Bputint(&bout, off);
-	zlength += 4+4;
-	return len;
-}
-
-int
-cmprun(uchar *a, uchar *b, int len)
-{
-	int i;
-
-	if(a==b)
-		return 0;
-	for(i=0; i<len && a[i]==b[i]; i++)
-		;
-	return i;
-}
-
-int
-countrle(uchar *a)
-{
-	int i;
-
-	for(i=0; a[i]==a[0]; i++)
-		;
-	return i;
-}
-
-void
-compress(void)
-{
-	int best, i, j, o, rle, run, maxrun, maxoff;
-	ulong sum;
-	Node *n;
-
-	sum = 0;
-	for(i=0; i<win && i<length; i++)
-		sum = (sum*256+data[i])%Prime;
-	for(i=0; i<length-win; ){
-		maxrun = 0;
-		maxoff = 0;
-		if(verbose)
-			fprint(2, "look %.6lux\n", sum);
-		n = lookup(sum);
-		if(n){
-			best = -1;
-			for(o=0; o<NOFF; o++){
-				if(n->offset[o] == -1)
-					break;
-				run = cmprun(data+i, data+n->offset[o], length-i);
-				if(run > maxrun && n->offset[o]+mindist < i){
-					maxrun = run;
-					maxoff = n->offset[o];
-					best = o;
-				}
-			}
-			if(best > 0){
-				o = n->offset[best];
-				for(j=best; j>0; j--)
-					n->offset[j] = n->offset[j-1];
-				n->offset[0] = o;
-			}
-		}
-				
-		if(maxrun >= minrun)
-			j = i+refblock(i, maxrun, maxoff);
-		else
-			j = i+rawbyte(i);
-		for(; i<j; i++){
-			/* avoid huge chains from large runs of same byte */
-			rle = countrle(data+i);
-			if(rle<4)
-				insertnode(sum, i);
-			else if(rle>maxrle[data[i]]){
-				maxrle[data[i]] = rle;
-				insertnode(sum, i);
-			}
-			sum = (sum*256+data[i+win]) % Prime;
-			sum = (sum + data[i]*outn) % Prime;
-		}
-	}
-	/* could do better here */
-	for(; i<length; i++)
-		rawbyte(i);
-	flushraw();
-}
-
-void
-usage(void)
-{
-	fprint(2, "usage: bflz [-n winsize] [file]\n");
-	exits("usage");
-}
-
-void
-main(int argc, char **argv)
-{
-	int fd, i, n;
-	char buf[10485760];
-
-	ARGBEGIN{
-	case 'd':
-		verbose = 1;
-		break;
-	case 's':
-		replacesame = atoi(EARGF(usage()));
-		break;
-	case 'm':
-		mindist = atoi(EARGF(usage()));
-		break;
-	case 'n':
-		win = atoi(EARGF(usage()));
-		minrun = win;
-		break;
-	default:
-		usage();
-	}ARGEND
-
-	switch(argc){
-	default:
-		usage();
-	case 0:
-		fd = 0;
-		break;
-	case 1:
-		if((fd = open(argv[0], OREAD)) < 0)
-			sysfatal("open %s: %r", argv[0]);
-		break;
-	}
-
-	while((n = readn(fd, buf, sizeof buf)) > 0){
-		data = realloc(data, length+n);
-		if(data == nil)
-			sysfatal("realloc: %r");
-		memmove(data+length, buf, n);
-		length += n;
-		if(n < sizeof buf)
-			break;
-	}
-	odat = malloc(length);
-	if(odat == nil)
-		sysfatal("malloc: %r");
-
-	Binit(&bout, 1, OWRITE);
-	Bprint(&bout, "BLZ\n");
-	Bputint(&bout, length);
-	outn = 1;
-	for(i=0; i<win; i++)
-		outn = (outn * 256) % Prime;
-
-	if(verbose)
-		fprint(2, "256^%d = %.6lux\n", win, outn);
-	outn = Prime - outn;
-	if(verbose)
-		fprint(2, "outn = %.6lux\n", outn);
-
-	compress();
-	Bwrite(&bout, odat, nodat);
-	Bterm(&bout);
-	fprint(2, "brk %p\n", sbrk(1));
-	fprint(2, "%d nodes used; %d of %d hash slots used\n", nalloc, nnew, nhash);
-	exits(nil);	
-}
--- a/sys/lib/dist.old/cmd/bzfs/bzfs.h
+++ /dev/null
@@ -1,11 +1,0 @@
-int unbzip(int);
-void _unbzip(int, int);
-int unbflz(int);
-int xexpand(int);
-void *emalloc(ulong);
-void *erealloc(void*, ulong);
-char *estrdup(char*);
-
-void ramfsmain(int, char**);
-extern int chatty;
-void error(char*, ...);
--- a/sys/lib/dist.old/cmd/bzfs/mkext.c
+++ /dev/null
@@ -1,288 +1,0 @@
-/*
- * bzip2-based file system.
- * the file system itself is just a bzipped2 xzipped mkfs archive
- * prefixed with "bzfilesystem\n" and suffixed with
- * a kilobyte of zeros.
- *
- * changes to the file system are only kept in 
- * memory, not written back to the disk.
- *
- * this is intended for use on a floppy boot disk.
- * we assume the file is in the dos file system and
- * contiguous on the disk: finding it amounts to
- * looking at the beginning of each sector for 
- * "bzfilesystem\n".  then we pipe it through 
- * bunzip2 and store the files in a file tree in memory.
- * things are slightly complicated by the fact that
- * devfloppy requires reads to be on a 512-byte
- * boundary and be a multiple of 512 bytes; we
- * fork a process to relieve bunzip2 of this restriction.
- */
-
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-#include <auth.h>
-#include <fcall.h>
-#include "bzfs.h"
-
-enum{
-	LEN	= 8*1024,
-	NFLDS	= 6,		/* filename, modes, uid, gid, mtime, bytes */
-};
-
-void	mkdirs(char*, char*);
-void	mkdir(char*, ulong, ulong, char*, char*);
-void	extract(char*, ulong, ulong, char*, char*, ulong);
-void	seekpast(ulong);
-void	error(char*, ...);
-void	warn(char*, ...);
-void	usage(void);
-char *mtpt;
-Biobufhdr bin;
-uchar	binbuf[2*LEN];
-
-void
-usage(void)
-{
-	fprint(2, "usage: bzfs [-m mtpt] [-s] [-f file] [-h]\n");
-	exits("usage");
-}
-
-/*
- * floppy disks can only be read on 512-byte 
- * boundaries and in 512 byte multiples.
- * feed one over a pipe to allow arbitrary reading.
- */
-char zero[512];
-int
-blockread(int in, char *first, int nfirst)
-{
-	int p[2], out, n, rv;
-	char blk[512];
-
-	if(pipe(p) < 0)
-		sysfatal("pipe: %r");
-	rv = p[0];
-	out = p[1];
-	switch(rfork(RFPROC|RFNOTEG|RFFDG)){
-	case -1:
-		sysfatal("fork: %r");
-	case 0:
-		close(rv);
-		break;
-	default:
-		close(in);
-		close(out);
-		return rv;
-	}
-
-	write(out, first, nfirst);
-	
-	while((n=read(in, blk, sizeof blk)) > 0){
-		if(write(out, blk, n) != n)
-			break;
-		if(n == sizeof(blk) && memcmp(zero, blk, n) == n)
-			break;
-	}
-	_exits(0);
-	return -1;
-}
-
-enum { NAMELEN = 28 };
-
-void
-main(int argc, char **argv)
-{
-	char *rargv[10];
-	int rargc;
-	char *fields[NFLDS], name[2*LEN], *p, *namep;
-	char uid[NAMELEN], gid[NAMELEN];
-	ulong mode, bytes, mtime;
-	char *file;
-	int i, n, stdin, fd, chatty;
-	char blk[512];
-
-	if(argc>1 && strcmp(argv[1], "RAMFS") == 0){
-		argv[1] = argv[0];
-		ramfsmain(argc-1, argv+1);
-		exits(nil);
-	}
-	if(argc>1 && strcmp(argv[1], "BUNZIP") == 0){
-		_unbzip(0, 1);
-		exits(nil);
-	}
-
-	rfork(RFNOTEG);
-	stdin = 0;
-	file = nil;
-	namep = name;
-	mtpt = "/root";
-	chatty = 0;
-	ARGBEGIN{
-	case 'd':
-		chatty = !chatty;
-		break;
-	case 'f':
-		file = ARGF();
-		break;
-	case 's':
-		stdin++;
-		break;
-	case 'm':
-		mtpt = ARGF();
-		break;
-	default:
-		usage();
-	}ARGEND
-
-	if(argc != 0)
-		usage();
-
-	if(file == nil) {
-		fprint(2, "must specify -f file\n");
-		usage();
-	}
-
-	if((fd = open(file, OREAD)) < 0) {
-		fprint(2, "cannot open \"%s\": %r\n", file);
-		exits("open");
-	}
-
-	rargv[0] = "ramfs";
-	rargc = 1;
-	if(stdin)
-		rargv[rargc++] = "-i";
-	rargv[rargc++] = "-m";
-	rargv[rargc++] = mtpt;
-	rargv[rargc] = nil;
-	ramfsmain(rargc, rargv);
-
-	if(1 || strstr(file, "disk")) {	/* search for archive on block boundary */
-if(chatty) fprint(2, "searching for bz\n");
-		for(i=0;; i++){
-			if((n = readn(fd, blk, sizeof blk)) != sizeof blk)
-				sysfatal("read %d gets %d: %r\n", i, n);
-			if(strncmp(blk, "bzfilesystem\n", 13) == 0)
-				break;
-		}
-if(chatty) fprint(2, "found at %d\n", i);
-	}
-
-	if(chdir(mtpt) < 0)
-		error("chdir %s: %r", mtpt);
-
-	fd = unbflz(unbzip(blockread(fd, blk+13, sizeof(blk)-13)));
-
-	Binits(&bin, fd, OREAD, binbuf, sizeof binbuf);
-	while(p = Brdline(&bin, '\n')){
-		p[Blinelen(&bin)-1] = '\0';
-if(chatty) fprint(2, "%s\n", p);
-		if(strcmp(p, "end of archive") == 0){
-			_exits(0);
-		}
-		if(getfields(p, fields, NFLDS, 0, " \t") != NFLDS){
-			warn("too few fields in file header");
-			continue;
-		}
-		strcpy(namep, fields[0]);
-		mode = strtoul(fields[1], 0, 8);
-		mtime = strtoul(fields[4], 0, 10);
-		bytes = strtoul(fields[5], 0, 10);
-		strncpy(uid, fields[2], NAMELEN);
-		strncpy(gid, fields[3], NAMELEN);
-		if(mode & DMDIR)
-			mkdir(name, mode, mtime, uid, gid);
-		else
-			extract(name, mode, mtime, uid, gid, bytes);
-	}
-	fprint(2, "premature end of archive\n");
-	exits("premature end of archive");
-}
-
-char buf[8192];
-
-int
-ffcreate(char *name, ulong mode, char *uid, char *gid, ulong mtime, int length)
-{
-	int fd, om;
-	Dir nd;
-
-	sprint(buf, "%s/%s", mtpt, name);
-	om = ORDWR;
-	if(mode&DMDIR)
-		om = OREAD;
-	if((fd = create(buf, om, (mode&DMDIR)|0666)) < 0)
-		error("create %s: %r", buf);
-
-	nulldir(&nd);
-	nd.mode = mode;
-	nd.uid = uid;
-	nd.gid = gid;
-	nd.mtime = mtime;
-	if(length)
-		nd.length = length;
-	if(dirfwstat(fd, &nd) < 0)	
-		error("fwstat %s: %r", buf);
-
-	return fd;
-}
-
-void
-mkdir(char *name, ulong mode, ulong mtime, char *uid, char *gid)
-{
-	close(ffcreate(name, mode, uid, gid, mtime, 0));
-}
-
-void
-extract(char *name, ulong mode, ulong mtime, char *uid, char *gid, ulong bytes)
-{
-	int fd, tot, n;
-
-	fd = ffcreate(name, mode, uid, gid, mtime, bytes);
-
-	for(tot = 0; tot < bytes; tot += n){
-		n = sizeof buf;
-		if(tot + n > bytes)
-			n = bytes - tot;
-		n = Bread(&bin, buf, n);
-		if(n <= 0)
-			error("premature eof reading %s", name);
-		if(write(fd, buf, n) != n)
-			error("short write writing %s", name);
-	}
-	close(fd);
-}
-
-void
-error(char *fmt, ...)
-{
-	char buf[1024];
-	va_list arg;
-
-	sprint(buf, "%s: ", argv0);
-	va_start(arg, fmt);
-	vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
-	va_end(arg);
-	fprint(2, "%s\n", buf);
-	exits(0);
-}
-
-void
-warn(char *fmt, ...)
-{
-	char buf[1024];
-	va_list arg;
-
-	sprint(buf, "%s: ", argv0);
-	va_start(arg, fmt);
-	vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
-	va_end(arg);
-	fprint(2, "%s\n", buf);
-}
-
-int
-_efgfmt(Fmt*)
-{
-	return -1;
-}
--- a/sys/lib/dist.old/cmd/bzfs/mkfile
+++ /dev/null
@@ -1,20 +1,0 @@
-</$objtype/mkfile
-
-TARG=bzfs
-
-OFILES=\
-	mkext.$O\
-	oramfs.$O\
-	unbflz.$O\
-	unbzip.$O\
-
-HFILES=bzfs.h
-
-BIN=/sys/lib/dist/bin/$objtype
-LIB=/sys/src/cmd/bzip2/lib/libbzip2.a$O
-</sys/src/cmd/mkone
-
-CFLAGS=$CFLAGS -p -I/sys/src/cmd/bzip2/lib
-
-/sys/src/cmd/bzip2/lib/libbzip2.a$O:
-	@{cd /sys/src/cmd/bzip2/lib && mk}
--- a/sys/lib/dist.old/cmd/bzfs/oramfs.c
+++ /dev/null
@@ -1,927 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <auth.h>
-#include <fcall.h>
-#include "bzfs.h"
-
-/*
- * Rather than reading /adm/users, which is a lot of work for
- * a toy program, we assume all groups have the form
- *	NNN:user:user:
- * meaning that each user is the leader of his own group.
- */
-
-enum
-{
-	OPERM	= 0x3,		/* mask of all permission types in open mode */
-	Nram	= 512,
-	Maxsize	= 512*1024*1024,
-	Maxfdata	= 8192,
-};
-
-typedef struct Fid Fid;
-typedef struct Ram Ram;
-
-struct Fid
-{
-	short	busy;
-	short	open;
-	short	rclose;
-	int	fid;
-	Fid	*next;
-	char	*user;
-	Ram	*ram;
-};
-
-struct Ram
-{
-	short	busy;
-	short	open;
-	long	parent;		/* index in Ram array */
-	Qid	qid;
-	long	perm;
-	char	*name;
-	ulong	atime;
-	ulong	mtime;
-	char	*user;
-	char	*group;
-	char	*muid;
-	char	*data;
-	long	ndata;
-};
-
-enum
-{
-	Pexec =		1,
-	Pwrite = 	2,
-	Pread = 	4,
-	Pother = 	1,
-	Pgroup = 	8,
-	Powner =	64,
-};
-
-ulong	path;		/* incremented for each new file */
-Fid	*fids;
-Ram	ram[Nram];
-int	nram;
-int	mfd[2];
-char	*user;
-uchar	mdata[IOHDRSZ+Maxfdata];
-uchar	rdata[Maxfdata];	/* buffer for data in reply */
-uchar statbuf[STATMAX];
-Fcall thdr;
-Fcall	rhdr;
-int	messagesize = sizeof mdata;
-
-Fid *	newfid(int);
-uint	ramstat(Ram*, uchar*, uint);
-void	io(void);
-void	*erealloc(void*, ulong);
-void	*emalloc(ulong);
-char	*estrdup(char*);
-void	ramfsusage(void);
-int	perm(Fid*, Ram*, int);
-char *atom(char*);
-
-char	*rflush(Fid*), *rversion(Fid*), *rauth(Fid*),
-	*rattach(Fid*), *rwalk(Fid*),
-	*ropen(Fid*), *rcreate(Fid*),
-	*rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
-	*rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
-
-char 	*(*fcalls[])(Fid*) = {
-	[Tversion]	rversion,
-	[Tflush]	rflush,
-	[Tauth]	rauth,
-	[Tattach]	rattach,
-	[Twalk]		rwalk,
-	[Topen]		ropen,
-	[Tcreate]	rcreate,
-	[Tread]		rread,
-	[Twrite]	rwrite,
-	[Tclunk]	rclunk,
-	[Tremove]	rremove,
-	[Tstat]		rstat,
-	[Twstat]	rwstat,
-};
-
-char	Eperm[] =	"permission denied";
-char	Enotdir[] =	"not a directory";
-char	Enoauth[] =	"no authentication in ramfs";
-char	Enotexist[] =	"file does not exist";
-char	Einuse[] =	"file in use";
-char	Eexist[] =	"file exists";
-char	Eisdir[] =	"file is a directory";
-char	Enotowner[] =	"not owner";
-char	Eisopen[] = 	"file already open for I/O";
-char	Excl[] = 	"exclusive use file already open";
-char	Ename[] = 	"illegal name";
-char	Eversion[] =	"unknown 9P version";
-
-int debug;
-
-void
-notifyf(void *a, char *s)
-{
-	USED(a);
-	if(strncmp(s, "interrupt", 9) == 0)
-		noted(NCONT);
-	noted(NDFLT);
-}
-
-void
-ramfsmain(int argc, char *argv[])
-{
-	Ram *r;
-	char *defmnt;
-	int p[2];
-	char buf[32];
-	int fd, srvfd;
-	int stdio = 0;
-
-	srvfd = -1;
-	defmnt = "/tmp";
-	ARGBEGIN{
-	case 'D':
-		debug = 1;
-		break;
-	case 'i':		/* this is DIFFERENT from normal ramfs; use 1 for both for kernel */
-		defmnt = 0;
-		stdio = 1;
-		srvfd = 0;
-		mfd[0] = 1;
-		mfd[1] = 1;
-		break;
-	case 's':
-		defmnt = 0;
-		break;
-	case 'm':
-		defmnt = ARGF();
-		break;
-	default:
-		ramfsusage();
-	}ARGEND
-
-	if(!stdio){
-		if(pipe(p) < 0)
-			error("pipe failed");
-		srvfd = p[1];
-		mfd[0] = p[0];
-		mfd[1] = p[0];
-		if(defmnt == 0){
-			fd = create("#s/ramfs", OWRITE, 0666);
-			if(fd < 0)
-				error("create of /srv/ramfs failed");
-			sprint(buf, "%d", p[1]);
-			if(write(fd, buf, strlen(buf)) < 0)
-				error("writing /srv/ramfs");
-		}
-	}
-
-	user = atom(getuser());
-	notify(notifyf);
-	nram = 1;
-	r = &ram[0];
-	r->busy = 1;
-	r->data = 0;
-	r->ndata = 0;
-	r->perm = DMDIR | 0775;
-	r->qid.type = QTDIR;
-	r->qid.path = 0LL;
-	r->qid.vers = 0;
-	r->parent = 0;
-	r->user = user;
-	r->group = user;
-	r->muid = user;
-	r->atime = time(0);
-	r->mtime = r->atime;
-	r->name = estrdup(".");
-
-	if(debug)
-		fmtinstall('F', fcallfmt);
-	switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
-	case -1:
-		error("fork");
-	case 0:
-		close(srvfd);
-		io();
-		break;
-	default:
-		close(mfd[0]);	/* don't deadlock if child fails */
-		if(defmnt && mount(srvfd, -1, defmnt, MREPL|MCREATE, "") < 0)
-			error("mount failed: %r");
-	}
-}
-
-char*
-rversion(Fid*)
-{
-	Fid *f;
-
-	for(f = fids; f; f = f->next)
-		if(f->busy)
-			rclunk(f);
-	if(thdr.msize > sizeof mdata)
-		rhdr.msize = sizeof mdata;
-	else
-		rhdr.msize = thdr.msize;
-	messagesize = rhdr.msize;
-	if(strncmp(thdr.version, "9P2000", 6) != 0)
-		return Eversion;
-	rhdr.version = "9P2000";
-	return 0;
-}
-
-char*
-rauth(Fid*)
-{
-	return "ramfs: no authentication required";
-}
-
-char*
-rflush(Fid *f)
-{
-	USED(f);
-	return 0;
-}
-
-char*
-rattach(Fid *f)
-{
-	/* no authentication! */
-	f->busy = 1;
-	f->rclose = 0;
-	f->ram = &ram[0];
-	rhdr.qid = f->ram->qid;
-	if(thdr.uname[0])
-		f->user = atom(thdr.uname);
-	else
-		f->user = atom("none");
-	if(strcmp(user, "none") == 0)
-		user = f->user;
-	return 0;
-}
-
-char*
-clone(Fid *f, Fid **nf)
-{
-	if(f->open)
-		return Eisopen;
-	if(f->ram->busy == 0)
-		return Enotexist;
-	*nf = newfid(thdr.newfid);
-	(*nf)->busy = 1;
-	(*nf)->open = 0;
-	(*nf)->rclose = 0;
-	(*nf)->ram = f->ram;
-	(*nf)->user = f->user;	/* no ref count; the leakage is minor */
-	return 0;
-}
-
-char*
-rwalk(Fid *f)
-{
-	Ram *r, *fram;
-	char *name;
-	Ram *parent;
-	Fid *nf;
-	char *err;
-	ulong t;
-	int i;
-
-	err = nil;
-	nf = nil;
-	rhdr.nwqid = 0;
-	if(rhdr.newfid != rhdr.fid){
-		err = clone(f, &nf);
-		if(err)
-			return err;
-		f = nf;	/* walk the new fid */
-	}
-	fram = f->ram;
-	if(thdr.nwname > 0){
-		t = time(0);
-		for(i=0; i<thdr.nwname && i<MAXWELEM; i++){
-			if((fram->qid.type & QTDIR) == 0){
-				err = Enotdir;
- 				break;
-			}
-			if(fram->busy == 0){
-				err = Enotexist;
-				break;
-			}
-			fram->atime = t;
-			name = thdr.wname[i];
-			if(strcmp(name, ".") == 0){
-    Found:
-				rhdr.nwqid++;
-				rhdr.wqid[i] = fram->qid;
-				continue;
-			}
-			parent = &ram[fram->parent];
-#ifdef CHECKS
-			if(!perm(f, parent, Pexec)){
-				err = Eperm;
-				break;
-			}
-#endif
-			if(strcmp(name, "..") == 0){
-				fram = parent;
-				goto Found;
-			}
-			for(r=ram; r < &ram[nram]; r++)
-				if(r->busy && r->parent==fram-ram && strcmp(name, r->name)==0){
-					fram = r;
-					goto Found;
-				}
-			break;
-		}
-		if(i==0 && err == nil)
-			err = Enotexist;
-	}
-	if(nf != nil && (err!=nil || rhdr.nwqid<thdr.nwname)){
-		/* clunk the new fid, which is the one we walked */
-		f->busy = 0;
-		f->ram = nil;
-	}
-	if(rhdr.nwqid == thdr.nwname)	/* update the fid after a successful walk */
-		f->ram = fram;
-	return err;
-}
-
-char *
-ropen(Fid *f)
-{
-	Ram *r;
-	int mode, trunc;
-
-	if(f->open)
-		return Eisopen;
-	r = f->ram;
-	if(r->busy == 0)
-		return Enotexist;
-	if(r->perm & DMEXCL)
-		if(r->open)
-			return Excl;
-	mode = thdr.mode;
-	if(r->qid.type & QTDIR){
-		if(mode != OREAD)
-			return Eperm;
-		rhdr.qid = r->qid;
-		return 0;
-	}
-	if(mode & ORCLOSE){
-		/* can't remove root; must be able to write parent */
-		if(r->qid.path==0 || !perm(f, &ram[r->parent], Pwrite))
-			return Eperm;
-		f->rclose = 1;
-	}
-	trunc = mode & OTRUNC;
-	mode &= OPERM;
-	if(mode==OWRITE || mode==ORDWR || trunc)
-		if(!perm(f, r, Pwrite))
-			return Eperm;
-	if(mode==OREAD || mode==ORDWR)
-		if(!perm(f, r, Pread))
-			return Eperm;
-	if(mode==OEXEC)
-		if(!perm(f, r, Pexec))
-			return Eperm;
-	if(trunc && (r->perm&DMAPPEND)==0){
-		r->ndata = 0;
-		if(r->data)
-			free(r->data);
-		r->data = 0;
-		r->qid.vers++;
-	}
-	rhdr.qid = r->qid;
-	rhdr.iounit = messagesize-IOHDRSZ;
-	f->open = 1;
-	r->open++;
-	return 0;
-}
-
-char *
-rcreate(Fid *f)
-{
-	Ram *r;
-	char *name;
-	long parent, prm;
-
-	if(f->open)
-		return Eisopen;
-	if(f->ram->busy == 0)
-		return Enotexist;
-	parent = f->ram - ram;
-	if((f->ram->qid.type&QTDIR) == 0)
-		return Enotdir;
-	/* must be able to write parent */
-#ifdef CHECKS
-	if(!perm(f, f->ram, Pwrite))
-		return Eperm;
-#endif
-	prm = thdr.perm;
-	name = thdr.name;
-	if(strcmp(name, ".")==0 || strcmp(name, "..")==0)
-		return Ename;
-	for(r=ram; r<&ram[nram]; r++)
-		if(r->busy && parent==r->parent)
-		if(strcmp((char*)name, r->name)==0)
-			return Einuse;
-	for(r=ram; r->busy; r++)
-		if(r == &ram[Nram-1])
-			return "no free ram resources";
-	r->busy = 1;
-	r->qid.path = ++path;
-	r->qid.vers = 0;
-	if(prm & DMDIR)
-		r->qid.type |= QTDIR;
-	r->parent = parent;
-	free(r->name);
-	r->name = estrdup(name);
-	r->user = f->user;
-	r->group = f->ram->group;
-	r->muid = f->ram->muid;
-	if(prm & DMDIR)
-		prm = (prm&~0777) | (f->ram->perm&prm&0777);
-	else
-		prm = (prm&(~0777|0111)) | (f->ram->perm&prm&0666);
-	r->perm = prm;
-	r->ndata = 0;
-	if(r-ram >= nram)
-		nram = r - ram + 1;
-	r->atime = time(0);
-	r->mtime = r->atime;
-	f->ram->mtime = r->atime;
-	f->ram = r;
-	rhdr.qid = r->qid;
-	rhdr.iounit = messagesize-IOHDRSZ;
-	f->open = 1;
-	if(thdr.mode & ORCLOSE)
-		f->rclose = 1;
-	r->open++;
-	return 0;
-}
-
-char*
-rread(Fid *f)
-{
-	Ram *r;
-	uchar *buf;
-	long off;
-	int n, m, cnt;
-
-	if(f->ram->busy == 0)
-		return Enotexist;
-	n = 0;
-	rhdr.count = 0;
-	off = thdr.offset;
-	buf = rdata;
-	cnt = thdr.count;
-	if(cnt > messagesize)	/* shouldn't happen, anyway */
-		cnt = messagesize;
-	if(f->ram->qid.type & QTDIR){
-		for(r=ram+1; off > 0; r++){
-			if(r->busy && r->parent==f->ram-ram)
-				off -= ramstat(r, statbuf, sizeof statbuf);
-			if(r == &ram[nram-1])
-				return 0;
-		}
-		for(; r<&ram[nram] && n < cnt; r++){
-			if(!r->busy || r->parent!=f->ram-ram)
-				continue;
-			m = ramstat(r, buf+n, cnt-n);
-			if(m == 0)
-				break;
-			n += m;
-		}
-		rhdr.data = (char*)rdata;
-		rhdr.count = n;
-		return 0;
-	}
-	r = f->ram;
-	if(off >= r->ndata)
-		return 0;
-	r->atime = time(0);
-	n = cnt;
-	if(off+n > r->ndata)
-		n = r->ndata - off;
-	rhdr.data = r->data+off;
-	rhdr.count = n;
-	return 0;
-}
-
-char*
-rwrite(Fid *f)
-{
-	Ram *r;
-	ulong off;
-	int cnt;
-
-	r = f->ram;
-	if(r->busy == 0)
-		return Enotexist;
-	off = thdr.offset;
-	if(r->perm & DMAPPEND)
-		off = r->ndata;
-	cnt = thdr.count;
-	if(r->qid.type & QTDIR)
-		return Eisdir;
-	if(off+cnt >= Maxsize)		/* sanity check */
-		return "write too big";
-	if(off+cnt > r->ndata)
-		r->data = erealloc(r->data, off+cnt);
-	if(off > r->ndata)
-		memset(r->data+r->ndata, 0, off-r->ndata);
-	if(off+cnt > r->ndata)
-		r->ndata = off+cnt;
-	memmove(r->data+off, thdr.data, cnt);
-	r->qid.vers++;
-	r->mtime = time(0);
-	rhdr.count = cnt;
-	return 0;
-}
-
-void
-realremove(Ram *r)
-{
-	r->ndata = 0;
-	if(r->data)
-		free(r->data);
-	r->data = 0;
-	r->parent = 0;
-	memset(&r->qid, 0, sizeof r->qid);
-	free(r->name);
-	r->name = nil;
-	r->busy = 0;
-}
-
-char *
-rclunk(Fid *f)
-{
-	if(f->open)
-		f->ram->open--;
-	if(f->rclose)
-		realremove(f->ram);
-	f->busy = 0;
-	f->open = 0;
-	f->ram = 0;
-	return 0;
-}
-
-char *
-rremove(Fid *f)
-{
-	Ram *r;
-
-	if(f->open)
-		f->ram->open--;
-	f->busy = 0;
-	f->open = 0;
-	r = f->ram;
-	f->ram = 0;
-#ifdef CHECKS
-	if(r->qid.path == 0 || !perm(f, &ram[r->parent], Pwrite))
-		return Eperm;
-#endif
-	ram[r->parent].mtime = time(0);
-	realremove(r);
-	return 0;
-}
-
-char *
-rstat(Fid *f)
-{
-	if(f->ram->busy == 0)
-		return Enotexist;
-	rhdr.nstat = ramstat(f->ram, statbuf, sizeof statbuf);
-	rhdr.stat = statbuf;
-	return 0;
-}
-
-char *
-rwstat(Fid *f)
-{
-	Ram *r, *s;
-	Dir dir;
-
-	if(f->ram->busy == 0)
-		return Enotexist;
-	convM2D(thdr.stat, thdr.nstat, &dir, (char*)statbuf);
-	r = f->ram;
-
-	/*
-	 * To change length, must have write permission on file.
-	 */
-#ifdef CHECKS
-	if(dir.length!=~0 && dir.length!=r->ndata){
-	 	if(!perm(f, r, Pwrite))
-			return Eperm;
-	}
-#endif
-
-	/*
-	 * To change name, must have write permission in parent
-	 * and name must be unique.
-	 */
-	if(dir.name[0]!='\0' && strcmp(dir.name, r->name)!=0){
-#ifdef CHECKS
-	 	if(!perm(f, &ram[r->parent], Pwrite))
-			return Eperm;
-#endif
-		for(s=ram; s<&ram[nram]; s++)
-			if(s->busy && s->parent==r->parent)
-			if(strcmp(dir.name, s->name)==0)
-				return Eexist;
-	}
-
-#ifdef OWNERS
-	/*
-	 * To change mode, must be owner or group leader.
-	 * Because of lack of users file, leader=>group itself.
-	 */
-	if(dir.mode!=~0 && r->perm!=dir.mode){
-		if(strcmp(f->user, r->user) != 0)
-		if(strcmp(f->user, r->group) != 0)
-			return Enotowner;
-	}
-
-	/*
-	 * To change group, must be owner and member of new group,
-	 * or leader of current group and leader of new group.
-	 * Second case cannot happen, but we check anyway.
-	 */
-	if(dir.gid[0]!='\0' && strcmp(r->group, dir.gid)!=0){
-		if(strcmp(f->user, r->user) == 0)
-		if(strcmp(f->user, dir.gid) == 0)
-			goto ok;
-		if(strcmp(f->user, r->group) == 0)
-		if(strcmp(f->user, dir.gid) == 0)
-			goto ok;
-		return Enotowner;
-		ok:;
-	}
-#endif
-
-	/* all ok; do it */
-	if(dir.mode != ~0){
-		dir.mode &= ~DMDIR;	/* cannot change dir bit */
-		dir.mode |= r->perm&DMDIR;
-		r->perm = dir.mode;
-	}
-	if(dir.name[0] != '\0'){
-		free(r->name);
-		r->name = estrdup(dir.name);
-	}
-	if(dir.gid[0] != '\0')
-		r->group = atom(dir.gid);
-
-	if(dir.uid[0] != '\0')
-		r->user = atom(dir.uid);
-
-	if(dir.length!=~0 && dir.length!=r->ndata){
-		r->data = erealloc(r->data, dir.length);
-		if(r->ndata < dir.length)
-			memset(r->data+r->ndata, 0, dir.length-r->ndata);
-		r->ndata = dir.length;
-	}
-
-	if(dir.mtime != ~0)
-		r->mtime = dir.mtime;
-
-	ram[r->parent].mtime = time(0);
-	return 0;
-}
-
-uint
-ramstat(Ram *r, uchar *buf, uint nbuf)
-{
-	Dir dir;
-
-	dir.name = r->name;
-	dir.qid = r->qid;
-	dir.mode = r->perm;
-	dir.length = r->ndata;
-	dir.uid = r->user;
-	dir.gid = r->group;
-	dir.muid = r->muid;
-	dir.atime = r->atime;
-	dir.mtime = r->mtime;
-	return convD2M(&dir, buf, nbuf);
-}
-
-Fid *
-newfid(int fid)
-{
-	Fid *f, *ff;
-
-	ff = 0;
-	for(f = fids; f; f = f->next)
-		if(f->fid == fid)
-			return f;
-		else if(!ff && !f->busy)
-			ff = f;
-	if(ff){
-		ff->fid = fid;
-		return ff;
-	}
-	f = emalloc(sizeof *f);
-	f->ram = nil;
-	f->fid = fid;
-	f->next = fids;
-	fids = f;
-	return f;
-}
-
-void
-io(void)
-{
-	char *err;
-	int n, pid;
-
-	pid = getpid();
-
-	for(;;){
-		/*
-		 * reading from a pipe or a network device
-		 * will give an error after a few eof reads.
-		 * however, we cannot tell the difference
-		 * between a zero-length read and an interrupt
-		 * on the processes writing to us,
-		 * so we wait for the error.
-		 */
-		n = read9pmsg(mfd[0], mdata, messagesize);
-		if(n < 0)
-			error("mount read: %r");
-		if(n == 0)
-			continue;
-		if(convM2S(mdata, n, &thdr) == 0)
-			continue;
-
-		if(debug)
-			fprint(2, "ramfs %d:<-%F\n", pid, &thdr);
-
-		if(!fcalls[thdr.type])
-			err = "bad fcall type";
-		else
-			err = (*fcalls[thdr.type])(newfid(thdr.fid));
-		if(err){
-			rhdr.type = Rerror;
-			rhdr.ename = err;
-		}else{
-			rhdr.type = thdr.type + 1;
-			rhdr.fid = thdr.fid;
-		}
-		rhdr.tag = thdr.tag;
-		if(debug)
-			fprint(2, "ramfs %d:->%F\n", pid, &rhdr);/**/
-		n = convS2M(&rhdr, mdata, messagesize);
-		if(n == 0)
-			error("convS2M error on write");
-		if(write(mfd[1], mdata, n) != n)
-			error("mount write");
-	}
-}
-
-int
-perm(Fid *f, Ram *r, int p)
-{
-	if((p*Pother) & r->perm)
-		return 1;
-	if(strcmp(f->user, r->group)==0 && ((p*Pgroup) & r->perm))
-		return 1;
-	if(strcmp(f->user, r->user)==0 && ((p*Powner) & r->perm))
-		return 1;
-	return 0;
-}
-
-void *
-emalloc(ulong n)
-{
-	void *p;
-
-	p = malloc(n);
-	if(!p)
-		error("out of memory");
-	memset(p, 0, n);
-	return p;
-}
-
-void *
-erealloc(void *p, ulong n)
-{
-	p = realloc(p, n);
-	if(!p)
-		error("out of memory");
-	return p;
-}
-
-char *
-estrdup(char *q)
-{
-	char *p;
-	int n;
-
-	n = strlen(q)+1;
-	p = malloc(n);
-	if(!p)
-		error("out of memory");
-	memmove(p, q, n);
-	return p;
-}
-
-void
-ramfsusage(void)
-{
-	fprint(2, "usage: %s [-is] [-m mountpoint]\n", argv0);
-	exits("usage");
-}
-
-/*
- *	Custom allocators to avoid malloc overheads on small objects.
- * 	We never free these.  (See below.)
- */
-typedef struct Stringtab	Stringtab;
-struct Stringtab {
-	Stringtab *link;
-	char *str;
-};
-static Stringtab*
-taballoc(void)
-{
-	static Stringtab *t;
-	static uint nt;
-
-	if(nt == 0){
-		t = malloc(64*sizeof(Stringtab));
-		if(t == 0)
-			sysfatal("out of memory");
-		nt = 64;
-	}
-	nt--;
-	return t++;
-}
-
-static char*
-xstrdup(char *s)
-{
-	char *r;
-	int len;
-	static char *t;
-	static int nt;
-
-	len = strlen(s)+1;
-	if(len >= 8192)
-		sysfatal("strdup big string");
-
-	if(nt < len){
-		t = malloc(8192);
-		if(t == 0)
-			sysfatal("out of memory");
-		nt = 8192;
-	}
-	r = t;
-	t += len;
-	nt -= len;
-	strcpy(r, s);
-	return r;
-}
-
-/*
- *	Return a uniquely allocated copy of a string.
- *	Don't free these -- they stay in the table for the 
- *	next caller who wants that particular string.
- *	String comparison can be done with pointer comparison 
- *	if you know both strings are atoms.
- */
-static Stringtab *stab[1024];
-
-static uint
-hash(char *s)
-{
-	uint h;
-	uchar *p;
-
-	h = 0;
-	for(p=(uchar*)s; *p; p++)
-		h = h*37 + *p;
-	return h;
-}
-
-char*
-atom(char *str)
-{
-	uint h;
-	Stringtab *tab;
-	
-	h = hash(str) % nelem(stab);
-	for(tab=stab[h]; tab; tab=tab->link)
-		if(strcmp(str, tab->str) == 0)
-			return tab->str;
-
-	tab = taballoc();
-	tab->str = xstrdup(str);
-	tab->link = stab[h];
-	stab[h] = tab;
-	return tab->str;
-}
--- a/sys/lib/dist.old/cmd/bzfs/unbflz.c
+++ /dev/null
@@ -1,108 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-#include "bzfs.h"
-
-int
-Bgetint(Biobuf *b)
-{
-	uchar p[4];
-
-	if(Bread(b, p, 4) != 4)
-		sysfatal("short read");
-	return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
-}
-
-/*
- * memmove but make sure overlap works properly.
- */
-void
-copy(uchar *dst, uchar *src, int n)
-{
-	while(n-- > 0)
-		*dst++ = *src++;
-}
-
-int
-unbflz(int in)
-{
-	int rv, out, p[2];
-	Biobuf *b, bin;
-	char buf[5];
-	uchar *data;
-	int i, j, length, n, m, o, sum;
-	ulong *blk;
-	int nblk, mblk;
-
-	if(pipe(p) < 0)
-		sysfatal("pipe: %r");
-
-	rv = p[0];
-	out = p[1];
-	switch(rfork(RFPROC|RFFDG|RFNOTEG|RFMEM)){
-	case -1:
-		sysfatal("fork: %r");
-	case 0:
-		close(rv);
-		break;
-	default:
-		close(in);
-		close(out);
-		return rv;
-	}
-
-	Binit(&bin, in, OREAD);
-	b = &bin;
-
-	if(Bread(b, buf, 4) != 4)
-		sysfatal("short read");
-
-	if(memcmp(buf, "BLZ\n", 4) != 0)
-		sysfatal("bad header");
-
-	length = Bgetint(b);
-	data = malloc(length);
-	if(data == nil)
-		sysfatal("out of memory");
-	sum = 0;
-	nblk = 0;
-	mblk = 0;
-	blk = nil;
-	while(sum < length){
-		if(nblk>=mblk){
-			mblk += 16384;
-			blk = realloc(blk, (mblk+1)*sizeof(blk[0]));
-			if(blk == nil)
-				sysfatal("out of memory");
-		}
-		n = Bgetint(b);
-		blk[nblk++] = n;
-		if(n&(1<<31))
-			n &= ~(1<<31);
-		else
-			blk[nblk++] = Bgetint(b);
-		sum += n;
-	}
-	if(sum != length)
-		sysfatal("bad compressed data %d %d", sum, length);
-	i = 0;
-	j = 0;
-	while(i < length){
-		assert(j < nblk);
-		n = blk[j++];
-		if(n&(1<<31)){
-			n &= ~(1<<31);
-			if((m=Bread(b, data+i, n)) != n)
-				sysfatal("short read %d %d", n, m);
-		}else{
-			o = blk[j++];
-			copy(data+i, data+o, n);
-		}
-		i += n;
-	}
-	write(out, data, length);
-	close(in);
-	close(out);
-	_exits(0);
-	return -1;
-}
--- a/sys/lib/dist.old/cmd/bzfs/unbzip.c
+++ /dev/null
@@ -1,861 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-#include "bzfs.h"
-
-/*
- * THIS FILE IS NOT IDENTICAL TO THE ORIGINAL
- * FROM THE BZIP2 DISTRIBUTION.
- *
- * It has been modified, mainly to break the library
- * into smaller pieces.
- *
- * Russ Cox
- * [email protected]
- * July 2000
- */
-
-/*---------------------------------------------*/
-/*--
-  Place a 1 beside your platform, and 0 elsewhere.
-  Attempts to autosniff this even if you don't.
---*/
-
-
-/*--
-  Plan 9 from Bell Labs
---*/
-#define BZ_PLAN9     1
-#define BZ_UNIX 0
-
-#define exit(x) exits((x) ? "whoops" : nil)
-#define size_t ulong
-
-#ifdef __GNUC__
-#   define NORETURN __attribute__ ((noreturn))
-#else
-#   define NORETURN /**/
-#endif
-
-/*--
-  Some more stuff for all platforms :-)
-  This might have to get moved into the platform-specific
-  header files if we encounter a machine with different sizes.
---*/
-
-typedef char            Char;
-typedef unsigned char   Bool;
-typedef unsigned char   UChar;
-typedef int             Int32;
-typedef unsigned int    UInt32;
-typedef short           Int16;
-typedef unsigned short  UInt16;
-                                       
-#define True  ((Bool)1)
-#define False ((Bool)0)
-
-/*--
-  IntNative is your platform's `native' int size.
-  Only here to avoid probs with 64-bit platforms.
---*/
-typedef int IntNative;
-
-#include "bzfs.h"
-#include "bzlib.h"
-#include "bzlib_private.h"
-
-static int
-bunzip(int ofd, char *ofile, Biobuf *bin)
-{
-	int e, n, done, onemore;
-	char buf[8192];
-	char obuf[8192];
-	Biobuf bout;
-	bz_stream strm;
-
-	USED(ofile);
-
-	memset(&strm, 0, sizeof strm);
-	BZ2_bzDecompressInit(&strm, 0, 0);
-
-	strm.next_in = buf;
-	strm.avail_in = 0;
-	strm.next_out = obuf;
-	strm.avail_out = sizeof obuf;
-
-	done = 0;
-	Binit(&bout, ofd, OWRITE);
-
-	/*
-	 * onemore is a crummy hack to go 'round the loop
-	 * once after we finish, to flush the output buffer.
-	 */
-	onemore = 1;
-	SET(e);
-	do {
-		if(!done && strm.avail_in < sizeof buf) {
-			if(strm.avail_in)
-				memmove(buf, strm.next_in, strm.avail_in);
-			
-			n = Bread(bin, buf+strm.avail_in, sizeof(buf)-strm.avail_in);
-			if(n <= 0)
-				done = 1;
-			else
-				strm.avail_in += n;
-			strm.next_in = buf;
-		}
-		if(strm.avail_out < sizeof obuf) {
-			Bwrite(&bout, obuf, sizeof(obuf)-strm.avail_out);
-			strm.next_out = obuf;
-			strm.avail_out = sizeof obuf;
-		}
-
-		if(onemore == 0)
-			break;
-	} while((e=BZ2_bzDecompress(&strm)) == BZ_OK || onemore--);
-
-	if(e != BZ_STREAM_END) {
-		fprint(2, "bunzip2: decompress failed\n");
-		return 0;
-	}
-
-	if(BZ2_bzDecompressEnd(&strm) != BZ_OK) {
-		fprint(2, "bunzip2: decompress end failed (can't happen)\n");
-		return 0;
-	}
-
-	Bterm(&bout);
-
-	return 1;
-}
-
-void
-_unbzip(int in, int out)
-{
-	Biobuf bin;
-
-	Binit(&bin, in, OREAD);
-	if(bunzip(out, nil, &bin) != 1) {
-		fprint(2, "bunzip2 failed\n");
-		_exits("bunzip2");
-	}
-}
-
-int
-unbzip(int in)
-{
-	int rv, out, p[2];
-
-	if(pipe(p) < 0)
-		sysfatal("pipe: %r");
-
-	rv = p[0];
-	out = p[1];
-	switch(rfork(RFPROC|RFFDG|RFNOTEG|RFMEM)){
-	case -1:
-		sysfatal("fork: %r");
-	case 0:
-		close(rv);
-		break;
-	default:
-		close(in);
-		close(out);
-		return rv;
-	}
-
-	_unbzip(in, out);
-	_exits(0);
-	return -1;	/* not reached */
-}
-
-int bz_config_ok ( void )
-{
-   if (sizeof(int)   != 4) return 0;
-   if (sizeof(short) != 2) return 0;
-   if (sizeof(char)  != 1) return 0;
-   return 1;
-}
-
-void* default_bzalloc(void *o, int items, int size)
-{
-	USED(o);
-	return sbrk(items*size);
-}
-
-void default_bzfree(void*, void*)
-{
-}
-
-void
-bz_internal_error(int)
-{
-	abort();
-}
-
-/*-------------------------------------------------------------*/
-/*--- Decompression machinery                               ---*/
-/*---                                          decompress.c ---*/
-/*-------------------------------------------------------------*/
-
-/*--
-  This file is a part of bzip2 and/or libbzip2, a program and
-  library for lossless, block-sorting data compression.
-
-  Copyright (C) 1996-2000 Julian R Seward.  All rights reserved.
-
-  Redistribution and use in source and binary forms, with or without
-  modification, are permitted provided that the following conditions
-  are met:
-
-  1. Redistributions of source code must retain the above copyright
-     notice, this list of conditions and the following disclaimer.
-
-  2. The origin of this software must not be misrepresented; you must 
-     not claim that you wrote the original software.  If you use this 
-     software in a product, an acknowledgment in the product 
-     documentation would be appreciated but is not required.
-
-  3. Altered source versions must be plainly marked as such, and must
-     not be misrepresented as being the original software.
-
-  4. The name of the author may not be used to endorse or promote 
-     products derived from this software without specific prior written 
-     permission.
-
-  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
-  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
-  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
-  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-  Julian Seward, Cambridge, UK.
-  [email protected]
-  bzip2/libbzip2 version 1.0 of 21 March 2000
-
-  This program is based on (at least) the work of:
-     Mike Burrows
-     David Wheeler
-     Peter Fenwick
-     Alistair Moffat
-     Radford Neal
-     Ian H. Witten
-     Robert Sedgewick
-     Jon L. Bentley
-
-  For more information on these sources, see the manual.
---*/
-
-
-
-/*---------------------------------------------------*/
-static
-void makeMaps_d ( DState* s )
-{
-   Int32 i;
-   s->nInUse = 0;
-   for (i = 0; i < 256; i++)
-      if (s->inUse[i]) {
-         s->seqToUnseq[s->nInUse] = i;
-         s->nInUse++;
-      }
-}
-
-
-/*---------------------------------------------------*/
-#define RETURN(rrr)                               \
-   { retVal = rrr; goto save_state_and_return; };
-
-#define GET_BITS(lll,vvv,nnn)                     \
-	case lll: \
-		{ int x; if((retVal = getbits(s, lll, &x, nnn)) != 99) \
-			goto save_state_and_return; vvv=x; }\
-
-int
-getbits(DState *s, int lll, int *vvv, int nnn)
-{
-	s->state = lll;
-	
-	for(;;) {
-		if (s->bsLive >= nnn) {
-			UInt32 v;
-			v = (s->bsBuff >>
-				 (s->bsLive-nnn)) & ((1 << nnn)-1);
-			s->bsLive -= nnn;
-			*vvv = v;
-			return 99;
-		}
-		if (s->strm->avail_in == 0) return BZ_OK;
-		s->bsBuff
-			= (s->bsBuff << 8) |
-			  ((UInt32)
-				  (*((UChar*)(s->strm->next_in))));
-		s->bsLive += 8;
-		s->strm->next_in++;
-		s->strm->avail_in--;
-		s->strm->total_in_lo32++;
-		if (s->strm->total_in_lo32 == 0)
-			s->strm->total_in_hi32++;
-	}
-	return -1;	/* KEN */
-}
-
-#define GET_UCHAR(lll,uuu)                        \
-   GET_BITS(lll,uuu,8)
-
-#define GET_BIT(lll,uuu)                          \
-   GET_BITS(lll,uuu,1)
-
-/*---------------------------------------------------*/
-#define GET_MTF_VAL(label1,label2,lval)           \
-{                                                 \
-   if (groupPos == 0) {                           \
-      groupNo++;                                  \
-      if (groupNo >= nSelectors)                  \
-         RETURN(BZ_DATA_ERROR);                   \
-      groupPos = BZ_G_SIZE;                       \
-      gSel = s->selector[groupNo];                \
-      gMinlen = s->minLens[gSel];                 \
-      gLimit = &(s->limit[gSel][0]);              \
-      gPerm = &(s->perm[gSel][0]);                \
-      gBase = &(s->base[gSel][0]);                \
-   }                                              \
-   groupPos--;                                    \
-   zn = gMinlen;                                  \
-   GET_BITS(label1, zvec, zn);                    \
-   while (1) {                                    \
-      if (zn > 20 /* the longest code */)         \
-         RETURN(BZ_DATA_ERROR);                   \
-      if (zvec <= gLimit[zn]) break;              \
-      zn++;                                       \
-      GET_BIT(label2, zj);                        \
-      zvec = (zvec << 1) | zj;                    \
-   };                                             \
-   if (zvec - gBase[zn] < 0                       \
-       || zvec - gBase[zn] >= BZ_MAX_ALPHA_SIZE)  \
-      RETURN(BZ_DATA_ERROR);                      \
-   lval = gPerm[zvec - gBase[zn]];                \
-}
-
-
-/*---------------------------------------------------*/
-Int32 BZ2_decompress ( DState* s )
-{
-   UChar      uc;
-   Int32      retVal;
-   Int32      minLen, maxLen;
-   bz_stream* strm = s->strm;
-
-   /* stuff that needs to be saved/restored */
-   Int32  i;
-   Int32  j;
-   Int32  t;
-   Int32  alphaSize;
-   Int32  nGroups;
-   Int32  nSelectors;
-   Int32  EOB;
-   Int32  groupNo;
-   Int32  groupPos;
-   Int32  nextSym;
-   Int32  nblockMAX;
-   Int32  nblock;
-   Int32  es;
-   Int32  N;
-   Int32  curr;
-   Int32  zt;
-   Int32  zn; 
-   Int32  zvec;
-   Int32  zj;
-   Int32  gSel;
-   Int32  gMinlen;
-   Int32* gLimit;
-   Int32* gBase;
-   Int32* gPerm;
-
-   if (s->state == BZ_X_MAGIC_1) {
-      /*initialise the save area*/
-      s->save_i           = 0;
-      s->save_j           = 0;
-      s->save_t           = 0;
-      s->save_alphaSize   = 0;
-      s->save_nGroups     = 0;
-      s->save_nSelectors  = 0;
-      s->save_EOB         = 0;
-      s->save_groupNo     = 0;
-      s->save_groupPos    = 0;
-      s->save_nextSym     = 0;
-      s->save_nblockMAX   = 0;
-      s->save_nblock      = 0;
-      s->save_es          = 0;
-      s->save_N           = 0;
-      s->save_curr        = 0;
-      s->save_zt          = 0;
-      s->save_zn          = 0;
-      s->save_zvec        = 0;
-      s->save_zj          = 0;
-      s->save_gSel        = 0;
-      s->save_gMinlen     = 0;
-      s->save_gLimit      = NULL;
-      s->save_gBase       = NULL;
-      s->save_gPerm       = NULL;
-   }
-
-   /*restore from the save area*/
-   i           = s->save_i;
-   j           = s->save_j;
-   t           = s->save_t;
-   alphaSize   = s->save_alphaSize;
-   nGroups     = s->save_nGroups;
-   nSelectors  = s->save_nSelectors;
-   EOB         = s->save_EOB;
-   groupNo     = s->save_groupNo;
-   groupPos    = s->save_groupPos;
-   nextSym     = s->save_nextSym;
-   nblockMAX   = s->save_nblockMAX;
-   nblock      = s->save_nblock;
-   es          = s->save_es;
-   N           = s->save_N;
-   curr        = s->save_curr;
-   zt          = s->save_zt;
-   zn          = s->save_zn; 
-   zvec        = s->save_zvec;
-   zj          = s->save_zj;
-   gSel        = s->save_gSel;
-   gMinlen     = s->save_gMinlen;
-   gLimit      = s->save_gLimit;
-   gBase       = s->save_gBase;
-   gPerm       = s->save_gPerm;
-
-   retVal = BZ_OK;
-
-   switch (s->state) {
-
-      GET_UCHAR(BZ_X_MAGIC_1, uc);
-      if (uc != 'B') RETURN(BZ_DATA_ERROR_MAGIC);
-
-      GET_UCHAR(BZ_X_MAGIC_2, uc);
-      if (uc != 'Z') RETURN(BZ_DATA_ERROR_MAGIC);
-
-      GET_UCHAR(BZ_X_MAGIC_3, uc)
-      if (uc != 'h') RETURN(BZ_DATA_ERROR_MAGIC);
-
-      GET_BITS(BZ_X_MAGIC_4, s->blockSize100k, 8)
-      if (s->blockSize100k < '1' || 
-          s->blockSize100k > '9') RETURN(BZ_DATA_ERROR_MAGIC);
-      s->blockSize100k -= '0';
-
-      if (0 && s->smallDecompress) {
-         s->ll16 = BZALLOC( s->blockSize100k * 100000 * sizeof(UInt16) );
-         s->ll4  = BZALLOC( 
-                      ((1 + s->blockSize100k * 100000) >> 1) * sizeof(UChar) 
-                   );
-         if (s->ll16 == NULL || s->ll4 == NULL) RETURN(BZ_MEM_ERROR);
-      } else {
-         s->tt  = BZALLOC( s->blockSize100k * 100000 * sizeof(Int32) );
-         if (s->tt == NULL) RETURN(BZ_MEM_ERROR);
-      }
-
-      GET_UCHAR(BZ_X_BLKHDR_1, uc);
-
-      if (uc == 0x17) goto endhdr_2;
-      if (uc != 0x31) RETURN(BZ_DATA_ERROR);
-      GET_UCHAR(BZ_X_BLKHDR_2, uc);
-      if (uc != 0x41) RETURN(BZ_DATA_ERROR);
-      GET_UCHAR(BZ_X_BLKHDR_3, uc);
-      if (uc != 0x59) RETURN(BZ_DATA_ERROR);
-      GET_UCHAR(BZ_X_BLKHDR_4, uc);
-      if (uc != 0x26) RETURN(BZ_DATA_ERROR);
-      GET_UCHAR(BZ_X_BLKHDR_5, uc);
-      if (uc != 0x53) RETURN(BZ_DATA_ERROR);
-      GET_UCHAR(BZ_X_BLKHDR_6, uc);
-      if (uc != 0x59) RETURN(BZ_DATA_ERROR);
-
-      s->currBlockNo++;
-    //  if (s->verbosity >= 2)
-    //     VPrintf1 ( "\n    [%d: huff+mtf ", s->currBlockNo );
- 
-      s->storedBlockCRC = 0;
-      GET_UCHAR(BZ_X_BCRC_1, uc);
-      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
-      GET_UCHAR(BZ_X_BCRC_2, uc);
-      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
-      GET_UCHAR(BZ_X_BCRC_3, uc);
-      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
-      GET_UCHAR(BZ_X_BCRC_4, uc);
-      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
-
-      GET_BITS(BZ_X_RANDBIT, s->blockRandomised, 1);
-
-      s->origPtr = 0;
-      GET_UCHAR(BZ_X_ORIGPTR_1, uc);
-      s->origPtr = (s->origPtr << 8) | ((Int32)uc);
-      GET_UCHAR(BZ_X_ORIGPTR_2, uc);
-      s->origPtr = (s->origPtr << 8) | ((Int32)uc);
-      GET_UCHAR(BZ_X_ORIGPTR_3, uc);
-      s->origPtr = (s->origPtr << 8) | ((Int32)uc);
-
-      if (s->origPtr < 0)
-         RETURN(BZ_DATA_ERROR);
-      if (s->origPtr > 10 + 100000*s->blockSize100k) 
-         RETURN(BZ_DATA_ERROR);
-
-      /*--- Receive the mapping table ---*/
-      for (i = 0; i < 16; i++) {
-         GET_BIT(BZ_X_MAPPING_1, uc);
-         if (uc == 1) 
-            s->inUse16[i] = True; else 
-            s->inUse16[i] = False;
-      }
-
-      for (i = 0; i < 256; i++) s->inUse[i] = False;
-
-      for (i = 0; i < 16; i++)
-         if (s->inUse16[i])
-            for (j = 0; j < 16; j++) {
-               GET_BIT(BZ_X_MAPPING_2, uc);
-               if (uc == 1) s->inUse[i * 16 + j] = True;
-            }
-      makeMaps_d ( s );
-      if (s->nInUse == 0) RETURN(BZ_DATA_ERROR);
-      alphaSize = s->nInUse+2;
-
-      /*--- Now the selectors ---*/
-      GET_BITS(BZ_X_SELECTOR_1, nGroups, 3);
-      if (nGroups < 2 || nGroups > 6) RETURN(BZ_DATA_ERROR);
-      GET_BITS(BZ_X_SELECTOR_2, nSelectors, 15);
-      if (nSelectors < 1) RETURN(BZ_DATA_ERROR);
-      for (i = 0; i < nSelectors; i++) {
-         j = 0;
-         while (True) {
-            GET_BIT(BZ_X_SELECTOR_3, uc);
-            if (uc == 0) break;
-            j++;
-            if (j >= nGroups) RETURN(BZ_DATA_ERROR);
-         }
-         s->selectorMtf[i] = j;
-      }
-
-      /*--- Undo the MTF values for the selectors. ---*/
-      {
-         UChar pos[BZ_N_GROUPS], tmp, v;
-         for (v = 0; v < nGroups; v++) pos[v] = v;
-   
-         for (i = 0; i < nSelectors; i++) {
-            v = s->selectorMtf[i];
-            tmp = pos[v];
-            while (v > 0) { pos[v] = pos[v-1]; v--; }
-            pos[0] = tmp;
-            s->selector[i] = tmp;
-         }
-      }
-
-      /*--- Now the coding tables ---*/
-      for (t = 0; t < nGroups; t++) {
-         GET_BITS(BZ_X_CODING_1, curr, 5);
-         for (i = 0; i < alphaSize; i++) {
-            while (True) {
-               if (curr < 1 || curr > 20) RETURN(BZ_DATA_ERROR);
-               GET_BIT(BZ_X_CODING_2, uc);
-               if (uc == 0) break;
-               GET_BIT(BZ_X_CODING_3, uc);
-               if (uc == 0) curr++; else curr--;
-            }
-            s->len[t][i] = curr;
-         }
-      }
-
-      /*--- Create the Huffman decoding tables ---*/
-      for (t = 0; t < nGroups; t++) {
-         minLen = 32;
-         maxLen = 0;
-         for (i = 0; i < alphaSize; i++) {
-            if (s->len[t][i] > maxLen) maxLen = s->len[t][i];
-            if (s->len[t][i] < minLen) minLen = s->len[t][i];
-         }
-         BZ2_hbCreateDecodeTables ( 
-            &(s->limit[t][0]), 
-            &(s->base[t][0]), 
-            &(s->perm[t][0]), 
-            &(s->len[t][0]),
-            minLen, maxLen, alphaSize
-         );
-         s->minLens[t] = minLen;
-      }
-
-      /*--- Now the MTF values ---*/
-
-      EOB      = s->nInUse+1;
-      nblockMAX = 100000 * s->blockSize100k;
-      groupNo  = -1;
-      groupPos = 0;
-
-      for (i = 0; i <= 255; i++) s->unzftab[i] = 0;
-
-      /*-- MTF init --*/
-      {
-         Int32 ii, jj, kk;
-         kk = MTFA_SIZE-1;
-         for (ii = 256 / MTFL_SIZE - 1; ii >= 0; ii--) {
-            for (jj = MTFL_SIZE-1; jj >= 0; jj--) {
-               s->mtfa[kk] = (UChar)(ii * MTFL_SIZE + jj);
-               kk--;
-            }
-            s->mtfbase[ii] = kk + 1;
-         }
-      }
-      /*-- end MTF init --*/
-
-      nblock = 0;
-      GET_MTF_VAL(BZ_X_MTF_1, BZ_X_MTF_2, nextSym);
-
-      while (True) {
-
-         if (nextSym == EOB) break;
-
-         if (nextSym == BZ_RUNA || nextSym == BZ_RUNB) {
-
-            es = -1;
-            N = 1;
-            do {
-               if (nextSym == BZ_RUNA) es = es + (0+1) * N; else
-               if (nextSym == BZ_RUNB) es = es + (1+1) * N;
-               N = N * 2;
-               GET_MTF_VAL(BZ_X_MTF_3, BZ_X_MTF_4, nextSym);
-            }
-               while (nextSym == BZ_RUNA || nextSym == BZ_RUNB);
-
-            es++;
-            uc = s->seqToUnseq[ s->mtfa[s->mtfbase[0]] ];
-            s->unzftab[uc] += es;
-
-            if (0 && s->smallDecompress)
-               while (es > 0) {
-                  if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
-                  s->ll16[nblock] = (UInt16)uc;
-                  nblock++;
-                  es--;
-               }
-            else
-               while (es > 0) {
-                  if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
-                  s->tt[nblock] = (UInt32)uc;
-                  nblock++;
-                  es--;
-               };
-
-            continue;
-
-         } else {
-
-            if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
-
-            /*-- uc = MTF ( nextSym-1 ) --*/
-            {
-               Int32 ii, jj, kk, pp, lno, off;
-               UInt32 nn;
-               nn = (UInt32)(nextSym - 1);
-
-               if (nn < MTFL_SIZE) {
-                  /* avoid general-case expense */
-                  pp = s->mtfbase[0];
-                  uc = s->mtfa[pp+nn];
-                  while (nn > 3) {
-                     Int32 z = pp+nn;
-                     s->mtfa[(z)  ] = s->mtfa[(z)-1];
-                     s->mtfa[(z)-1] = s->mtfa[(z)-2];
-                     s->mtfa[(z)-2] = s->mtfa[(z)-3];
-                     s->mtfa[(z)-3] = s->mtfa[(z)-4];
-                     nn -= 4;
-                  }
-                  while (nn > 0) { 
-                     s->mtfa[(pp+nn)] = s->mtfa[(pp+nn)-1]; nn--; 
-                  };
-                  s->mtfa[pp] = uc;
-               } else { 
-                  /* general case */
-                  lno = nn / MTFL_SIZE;
-                  off = nn % MTFL_SIZE;
-                  pp = s->mtfbase[lno] + off;
-                  uc = s->mtfa[pp];
-                  while (pp > s->mtfbase[lno]) { 
-                     s->mtfa[pp] = s->mtfa[pp-1]; pp--; 
-                  };
-                  s->mtfbase[lno]++;
-                  while (lno > 0) {
-                     s->mtfbase[lno]--;
-                     s->mtfa[s->mtfbase[lno]] 
-                        = s->mtfa[s->mtfbase[lno-1] + MTFL_SIZE - 1];
-                     lno--;
-                  }
-                  s->mtfbase[0]--;
-                  s->mtfa[s->mtfbase[0]] = uc;
-                  if (s->mtfbase[0] == 0) {
-                     kk = MTFA_SIZE-1;
-                     for (ii = 256 / MTFL_SIZE-1; ii >= 0; ii--) {
-                        for (jj = MTFL_SIZE-1; jj >= 0; jj--) {
-                           s->mtfa[kk] = s->mtfa[s->mtfbase[ii] + jj];
-                           kk--;
-                        }
-                        s->mtfbase[ii] = kk + 1;
-                     }
-                  }
-               }
-            }
-            /*-- end uc = MTF ( nextSym-1 ) --*/
-
-            s->unzftab[s->seqToUnseq[uc]]++;
-            if (0 && s->smallDecompress)
-               s->ll16[nblock] = (UInt16)(s->seqToUnseq[uc]); else
-               s->tt[nblock]   = (UInt32)(s->seqToUnseq[uc]);
-            nblock++;
-
-            GET_MTF_VAL(BZ_X_MTF_5, BZ_X_MTF_6, nextSym);
-            continue;
-         }
-      }
-
-      /* Now we know what nblock is, we can do a better sanity
-         check on s->origPtr.
-      */
-      if (s->origPtr < 0 || s->origPtr >= nblock)
-         RETURN(BZ_DATA_ERROR);
-
-      s->state_out_len = 0;
-      s->state_out_ch  = 0;
-      BZ_INITIALISE_CRC ( s->calculatedBlockCRC );
-      s->state = BZ_X_OUTPUT;
-    //  if (s->verbosity >= 2) VPrintf0 ( "rt+rld" );
-
-      /*-- Set up cftab to facilitate generation of T^(-1) --*/
-      s->cftab[0] = 0;
-      for (i = 1; i <= 256; i++) s->cftab[i] = s->unzftab[i-1];
-      for (i = 1; i <= 256; i++) s->cftab[i] += s->cftab[i-1];
-
-      if (0 && s->smallDecompress) {
-
-         /*-- Make a copy of cftab, used in generation of T --*/
-         for (i = 0; i <= 256; i++) s->cftabCopy[i] = s->cftab[i];
-
-         /*-- compute the T vector --*/
-         for (i = 0; i < nblock; i++) {
-            uc = (UChar)(s->ll16[i]);
-            SET_LL(i, s->cftabCopy[uc]);
-            s->cftabCopy[uc]++;
-         }
-
-         /*-- Compute T^(-1) by pointer reversal on T --*/
-         i = s->origPtr;
-         j = GET_LL(i);
-         do {
-            Int32 tmp = GET_LL(j);
-            SET_LL(j, i);
-            i = j;
-            j = tmp;
-         }
-            while (i != s->origPtr);
-
-         s->tPos = s->origPtr;
-         s->nblock_used = 0;
-         if (s->blockRandomised) {
-            BZ_RAND_INIT_MASK;
-            BZ_GET_SMALL(s->k0); s->nblock_used++;
-            BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; 
-         } else {
-            BZ_GET_SMALL(s->k0); s->nblock_used++;
-         }
-
-      } else {
-
-         /*-- compute the T^(-1) vector --*/
-         for (i = 0; i < nblock; i++) {
-            uc = (UChar)(s->tt[i] & 0xff);
-            s->tt[s->cftab[uc]] |= (i << 8);
-            s->cftab[uc]++;
-         }
-
-         s->tPos = s->tt[s->origPtr] >> 8;
-         s->nblock_used = 0;
-         if (s->blockRandomised) {
-            BZ_RAND_INIT_MASK;
-            BZ_GET_FAST(s->k0); s->nblock_used++;
-            BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; 
-         } else {
-            BZ_GET_FAST(s->k0); s->nblock_used++;
-         }
-
-      }
-
-      RETURN(BZ_OK);
-
-
-
-    endhdr_2:
-
-      GET_UCHAR(BZ_X_ENDHDR_2, uc);
-      if (uc != 0x72) RETURN(BZ_DATA_ERROR);
-      GET_UCHAR(BZ_X_ENDHDR_3, uc);
-      if (uc != 0x45) RETURN(BZ_DATA_ERROR);
-      GET_UCHAR(BZ_X_ENDHDR_4, uc);
-      if (uc != 0x38) RETURN(BZ_DATA_ERROR);
-      GET_UCHAR(BZ_X_ENDHDR_5, uc);
-      if (uc != 0x50) RETURN(BZ_DATA_ERROR);
-      GET_UCHAR(BZ_X_ENDHDR_6, uc);
-      if (uc != 0x90) RETURN(BZ_DATA_ERROR);
-
-      s->storedCombinedCRC = 0;
-      GET_UCHAR(BZ_X_CCRC_1, uc);
-      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
-      GET_UCHAR(BZ_X_CCRC_2, uc);
-      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
-      GET_UCHAR(BZ_X_CCRC_3, uc);
-      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
-      GET_UCHAR(BZ_X_CCRC_4, uc);
-      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
-
-      s->state = BZ_X_IDLE;
-      RETURN(BZ_STREAM_END);
-
-      default: AssertH ( False, 4001 );
-   }
-
-   AssertH ( False, 4002 );
-
-   save_state_and_return:
-
-   s->save_i           = i;
-   s->save_j           = j;
-   s->save_t           = t;
-   s->save_alphaSize   = alphaSize;
-   s->save_nGroups     = nGroups;
-   s->save_nSelectors  = nSelectors;
-   s->save_EOB         = EOB;
-   s->save_groupNo     = groupNo;
-   s->save_groupPos    = groupPos;
-   s->save_nextSym     = nextSym;
-   s->save_nblockMAX   = nblockMAX;
-   s->save_nblock      = nblock;
-   s->save_es          = es;
-   s->save_N           = N;
-   s->save_curr        = curr;
-   s->save_zt          = zt;
-   s->save_zn          = zn;
-   s->save_zvec        = zvec;
-   s->save_zj          = zj;
-   s->save_gSel        = gSel;
-   s->save_gMinlen     = gMinlen;
-   s->save_gLimit      = gLimit;
-   s->save_gBase       = gBase;
-   s->save_gPerm       = gPerm;
-
-   return retVal;   
-}
-
-
-/*-------------------------------------------------------------*/
-/*--- end                                      decompress.c ---*/
-/*-------------------------------------------------------------*/
--- a/sys/lib/dist.old/cmd/cdsh.c
+++ /dev/null
@@ -1,133 +1,0 @@
-/*
- * The `cd' shell.  
- * Just has cd and lc.
- */
-
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-
-char *pwd;
-char *root = "/";
-
-void
-usage(void)
-{
-	fprint(2, "usage: cdsh [-r root]\n");
-	exits("usage");
-}
-
-int
-system(char *cmd)
-{
-	int pid;
-	if((pid = fork()) < 0)
-		return -1;
-
-	if(pid == 0) {
-		dup(2, 1);
-		execl("/bin/rc", "rc", "-c", cmd, nil);
-		exits("exec");
-	}
-	waitpid();
-	return 0;
-}
-
-int
-cd(char *s)
-{
-	char *newpwd;
-	int l;
-
-	if(s[0] == '/') {
-		cleanname(s);
-		newpwd = strdup(s);
-	} else {
-		l = strlen(pwd)+1+strlen(s)+1+50;	/* 50 = crud for unicode mistakes */
-		newpwd = malloc(l);
-		snprint(newpwd, l, "%s/%s", pwd, s);
-		cleanname(newpwd);
-		assert(newpwd[0] == '/');
-	}
-
-	if(chdir(root) < 0 || (newpwd[1] != '\0' && chdir(newpwd+1) < 0)) {
-		chdir(root);
-		chdir(pwd+1);
-		free(newpwd);
-		return -1;
-	} else {
-		free(pwd);
-		pwd = newpwd;
-		return 0;
-	}
-}
-
-void
-main(int argc, char **argv)
-{
-	char *p;
-	Biobuf bin;
-	char *f[2];
-	int nf;
-
-	ARGBEGIN{
-	case 'r':
-		root = ARGF();
-		if(root == nil)
-			usage();
-		if(root[0] != '/') {
-			fprint(2, "root must be rooted\n");
-			exits("root");
-		}
-		break;
-	default:
-		usage();
-	}ARGEND;
-
-	if(argc != 0)
-		usage();
-
-	cleanname(root);
-	if(cd("/") < 0) {
-		fprint(2, "cannot cd %s: %r\n", root);
-		exits("root");
-	}
-
-	Binit(&bin, 0, OREAD);
-	while(fprint(2, "%s%% ", pwd), (p = Brdline(&bin, '\n'))) {
-		p[Blinelen(&bin)-1] = '\0';
-		nf = tokenize(p, f, nelem(f));
-		if(nf < 1)
-			continue;
-		if(strcmp(f[0], "exit") == 0)
-			break;
-		if(strcmp(f[0], "lc") == 0) {
-			if(nf == 1) {
-				if(system("/bin/lc") < 0)
-					fprint(2, "lc: %r\n");
-			} else if(nf == 2) {
-				if(strpbrk(p, "'`{}^@$#&()|\\;><"))
-					fprint(2, "no shell characters allowed\n");
-				else {
-					p = f[1];
-					*--p = ' ';
-					*--p = 'c';
-					*--p = 'l';
-					if(system(p) < 0)
-						fprint(2, "lc: %r\n");
-				}
-			}
-			continue;
-		}
-		if(strcmp(f[0], "cd") == 0) {
-			if(nf < 2)
-				fprint(2, "usage: cd dir\n");
-			else if(cd(f[1]) < 0)
-				fprint(2, "cd: %r\n");
-			continue;
-		}
-		fprint(2, "commands are cd, lc, and exit\n");
-	}
-
-	print("%s\n", pwd);
-}
--- a/sys/lib/dist.old/cmd/clog.c
+++ /dev/null
@@ -1,59 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-
-char *argv0;
-
-int
-openlog(char *name)
-{
-	int fd;
-
-	fd = open(name, OWRITE);
-	if(fd < 0){
-		fprint(2, "%s: can't open %s: %r\n", argv0, name);
-		return -1;
-	}
-	seek(fd, 0, 2);
-	return fd;
-}
-
-void
-main(int argc, char **argv)
-{
-	Biobuf in;
-	int fd;
-	char *p, *t;
-	char buf[8192];
-
-	argv0 = argv[0];
-	if(argc != 4){
-		fprint(2, "usage: %s console logfile prefix\n", argv0);
-		exits("usage");
-	}
-
-	fd = open(argv[1], OREAD);
-	if(fd < 0){
-		fprint(2, "%s: can't open %s: %r\n", argv0, argv[1]);
-		exits("open");
-	}
-	Binit(&in, fd, OREAD);
-
-	fd = openlog(argv[2]);
-
-	for(;;){
-		if(p = Brdline(&in, '\n')){
-			p[Blinelen(&in)-1] = 0;
-			if(fprint(fd, "%s: %s\n", argv[3], p) < 0){
-				close(fd);
-				fd = openlog(argv[2]);
-				fprint(fd, "%s: %s\n", t, p);
-			}
-		} else if(Blinelen(&in) == 0)	// true eof
-			break;
-		else {
-			Bread(&in, buf, sizeof buf);
-		}
-	}
-	exits(0);
-}
--- a/sys/lib/dist.old/cmd/multi/mkfile
+++ /dev/null
@@ -1,76 +1,0 @@
-objtype=386
-</$objtype/mkfile
-
-TARG=multi
-
-PIECES=\
-	aux/mouse\
-	aux/pcmcia\
-	aux/vga\
-	aux/zerotrunc\
-	disk/fdisk\
-	disk/format\
-	disk/mbr\
-	disk/prep\
-#	fossil/fossil\
-#	fossil/flfmt\
-	ip/ipconfig\
-	ip/ppp\
-	ndb/cs\
-	ndb/dns\
-#	replica/applylog\
-	9660srv\
-#	awk\
-	basename\
-	cat\
-	chgrp\
-	chmod\
-	cleanname\
-	cmp\
-	cp\
-	date\
-	dd\
-	dossrv\
-	echo\
-	ed\
-	ext2srv\
-#	fcp\
-	grep\
-	hget\
-	hoc\
-	ls\
-	mc\
-	mount\
-	mv\
-	ps\
-	read\
-#	rio\
-	rm\
-	sed\
-	sort\
-	srv\
-#	stats\
-	syscall\
-	tail\
-	tee\
-	test\
-	wc\
-	xd\
-
-8.multi:V: mkmulti mkfile
-	mkmulti $PIECES 
-	ls -l 8.multi
-	ls -l /386/bin/$PIECES | awk '{s += $6} END{print s}'
-
-scripts:V:
-	rm -rf ../../pc/multi
-	mkdir ../../pc/multi
-	for(i in $PIECES){
-		b=`{basename $i}
-		echo '#!/bin/multi' >>../../pc/multi/$b
-		chmod +x ../../pc/multi/$b
-	}
-
-BIN=/sys/lib/dist/bin/$objtype
-</sys/src/cmd/mkmany
-
--- a/sys/lib/dist.old/cmd/multi/mkmulti
+++ /dev/null
@@ -1,70 +1,0 @@
-#!/bin/rc
-
-targ=multi
-
-n=0
-dir=`{pwd}
-
-fn grab {
-	echo using $*
-	for(i){
-		n=`{echo 1+$n|hoc}
-		mv $i $dir/a.$n.8
-	}
-}
-
-fn getfiles {
-	sed -n 's/^(pcc|8\^l|8l) +(-o [^ ]* +)?([^\-].*)/ \3/p' | sed 's/ -[^ ]*//g' |
-		sed 's/ [^ ]*\.a//g'
-}
-
-rm a.*.8
->multi.h
->multiproto.h
-
-for(i){
-echo $i...
-	b=`{basename $i}
-	p=$b
-	if(~ $b [0-9]*)
-		p=_$b
-	echo void $p^_main'(int, char**);' >>$dir/multiproto.h
-	echo "$b", $p^_main, >>$dir/multi.h
-	d=`{basename -d $i}
-	if(~ $i disk/prep disk/fdisk){
-		cd /sys/src/cmd/disk/prep
-		rm 8.$b
-		files=`{mk 8.$b | getfiles}
-	}
-	if not if(test -d /sys/src/cmd/$i && @{cd /sys/src/cmd/$i && mk 8.out}){
-		cd /sys/src/cmd/$i
-		rm 8.out
-		files=`{mk 8.out | getfiles}
-	}
-	if not if(test -d /sys/src/cmd/$i && @{cd /sys/src/cmd/$i && mk 8.$b}){
-		cd /sys/src/cmd/$i
-		rm 8.out
-		files=`{mk 8.$b | getfiles}
-	}
-	if not if(test -d /sys/src/cmd/$d && @{cd /sys/src/cmd/$d && mk 8.$b}){
-		cd /sys/src/cmd/$d
-		rm 8.$b
-		files=`{mk 8.$b | getfiles}
-	}
-	if not{
-		echo do not know how to make $i
-		exit oops
-	}
-	aux/8prefix $p^_ $files
-	grab $files
-	switch(`{pwd}){
-	case /sys/src/cmd /sys/src/cmd/aux /sys/src/cmd/ip
-		rm 8.$b
-	case *
-		mk clean
-	}
-}
-cd $dir
-8c -FVw multi.c
-8l -o 8.$targ multi.8 a.*.8
-# rm a.*.8
--- a/sys/lib/dist.old/cmd/multi/multi.c
+++ /dev/null
@@ -1,38 +1,0 @@
-#include <u.h>
-#include <libc.h>
-
-#include "multiproto.h"
-struct {
-	char *name; 
-	void (*fn)(int, char**);
-} mains[] =
-{
-#include "multi.h"
-};
-
-void
-main(int argc, char **argv)
-{
-	int i;
-	char *cmd, *p;
-	
-	if(argc == 1){
-		fprint(2, "usage: multi cmd args...\n");
-		exits("usage");
-	}
-	
-	cmd = argv[1];
-	if(p = strrchr(cmd, '/'))
-		cmd = p+1;
-	argv++;
-	argc--;
-
-	for(i=0; i<nelem(mains); i++){
-		if(strcmp(cmd, mains[i].name) == 0){
-			mains[i].fn(argc, argv);
-			return;
-		}
-	}
-	fprint(2, "multi: no such cmd %s\n", cmd);
-	exits("no cmd");
-}
--- a/sys/lib/dist.old/cmd/tailfsrv.c
+++ /dev/null
@@ -1,17 +1,0 @@
-#include <u.h>
-#include <libc.h>
-
-void
-main(void)
-{
-	int fd, p[2];
-	char buf[8192], n;
-
-	pipe(p);
-	fd = create("/srv/log", OWRITE, 0666);
-	fprint(fd, "%d", p[0]);
-	close(fd);
-	close(p[0]);
-	while((n = read(p[1], buf, sizeof buf)) >= 0)
-		write(1, buf, n);
-}
--- a/sys/lib/dist.old/cmd/touchfs.c
+++ /dev/null
@@ -1,66 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-
-void
-Bpass(Biobuf *bin, Biobuf *bout, int n)
-{
-	char buf[8192];
-	int m;
-
-	while(n > 0) {
-		m = sizeof buf;
-		if(m > n)
-			m = n;
-		m = Bread(bin, buf, m);
-		if(m <= 0) {
-			fprint(2, "corrupt archive\n");
-			exits("notdone");
-		}
-		Bwrite(bout, buf, m);
-		n -= m;
-	}
-	assert(n == 0);
-}
-
-void
-main(int argc, char **argv)
-{
-	char *p, *f[10];
-	Biobuf bin, bout;
-	int nf;
-	ulong d, size;
-
-	if(argc != 2) {
-		fprint(2, "usage: cat mkfs-archive | touchfs date (in seconds)\n");
-		exits("usage");
-	}
-
-	d = strtoul(argv[1], 0, 0);
-
-	quotefmtinstall();
-	Binit(&bin, 0, OREAD);
-	Binit(&bout, 1, OWRITE);
-
-	while(p = Brdline(&bin, '\n')) {
-		p[Blinelen(&bin)-1] = '\0';
-		if(strcmp(p, "end of archive") == 0) {
-			Bprint(&bout, "end of archive\n");
-			exits(0);
-		}
-
-		nf = tokenize(p, f, nelem(f));
-		if(nf != 6) {
-			fprint(2, "corrupt archive\n");
-			exits("notdone");
-		}
-
-		Bprint(&bout, "%q %q %q %q %lud %q\n",
-			f[0], f[1], f[2], f[3], d, f[5]);
-
-		size = strtoul(f[5], 0, 0);
-		Bpass(&bin, &bout, size);
-	}
-	fprint(2, "premature end of archive\n");
-	exits("notdone");
-}
--- a/sys/lib/dist.old/cmd/unbflz.c
+++ /dev/null
@@ -1,109 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-
-void
-usage(void)
-{
-	fprint(2, "usage: unbflz [file]\n");
-	exits("usage");
-}
-
-int
-Bgetint(Biobuf *b)
-{
-	uchar p[4];
-
-	if(Bread(b, p, 4) != 4)
-		sysfatal("short read");
-	return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
-}
-
-/*
- * memmove but make sure overlap works properly.
- */
-void
-copy(uchar *dst, uchar *src, int n)
-{
-	while(n-- > 0)
-		*dst++ = *src++;
-}
-
-void
-main(int argc, char **argv)
-{
-	Biobuf *b, bin;
-	char buf[5];
-	uchar *data;
-	ulong *blk, l;
-	int nblk, mblk;
-	int sum;
-	int i, j, length, m, n, o;
-
-	ARGBEGIN{
-	default:
-		usage();
-	}ARGEND
-
-	switch(argc){
-	default:
-		usage();
-	case 0:
-		Binit(&bin, 0, OREAD);
-		b = &bin;
-		break;
-	case 1:
-		if((b = Bopen(argv[0], OREAD)) == nil)
-			sysfatal("open %s: %r", argv[0]);
-		break;
-	}
-
-	if(Bread(b, buf, 4) != 4)
-		sysfatal("short read");
-
-	if(memcmp(buf, "BLZ\n", 4) != 0)
-		sysfatal("bad header");
-
-	length = Bgetint(b);
-	data = malloc(length);
-	if(data == nil)
-		sysfatal("out of memory");
-	sum = 0;
-	nblk = 0;
-	mblk = 0;
-	blk = nil;
-	while(sum < length){
-		if(nblk>=mblk){
-			mblk += 16384;
-			blk = realloc(blk, (mblk+1)*sizeof(blk[0]));
-			if(blk == nil)
-				sysfatal("out of memory");
-		}
-		l = Bgetint(b);
-		blk[nblk++] = l;
-		if(l&(1<<31))
-			l &= ~(1<<31);
-		else
-			blk[nblk++] = Bgetint(b);
-		sum += l;
-	}
-	if(sum != length)
-		sysfatal("bad compressed data %d %d", sum, length);
-	i = 0;
-	j = 0;
-	while(i < length){
-		assert(j < nblk);
-		n = blk[j++];
-		if(n&(1<<31)){
-			n &= ~(1<<31);
-			if((m=Bread(b, data+i, n)) != n)
-				sysfatal("short read %d %d", n, m);
-		}else{
-			o = blk[j++];
-			copy(data+i, data+o, n);
-		}
-		i += n;
-	}
-	write(1, data, length);
-	exits(nil);
-}
--- /dev/null
+++ b/sys/src/cmd/aux/bflz.c
@@ -1,0 +1,374 @@
+/*
+ * Extraordinarily brute force Lempel & Ziv-like
+ * compressor.  The input file must fit in memory
+ * during compression, and the output file will
+ * be reconstructed in memory during decompression.
+ * We search for large common sequences and use a
+ * greedy algorithm to choose which sequence gets
+ * compressed first.
+ *
+ * Files begin with "BLZ\n" and a 32-bit uncompressed file length.
+ *
+ * Output format is a series of blocks followed by
+ * a raw data section.  Each block begins with a 32-bit big-endian
+ * number.  The top bit is type and the next 31 bits
+ * are uncompressed size.  Type is one of
+ *	0 - use raw data for this length
+ *	1 - a 32-bit offset follows
+ * After the blocks come the raw data.  (The end of the blocks can be
+ * noted by summing block lengths until you reach the file length.)
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+#define malloc sbrk
+
+int minrun = 16;
+int win = 16;
+ulong outn;
+int verbose;
+int mindist;
+
+enum { Prime = 16777213 };	/* smallest prime < 2^24 (so p*256+256 < 2^32) */
+enum { NOFF = 3 };
+
+Biobuf bout;
+ulong length;
+uchar *data;
+ulong sum32(ulong, void*, long);
+uchar *odat;
+int nodat;
+int nraw;
+int rawstart;
+int acct;
+int zlength;
+int maxchain;
+int maxrle[256];
+int nnew;
+
+typedef struct Node Node;
+struct Node {
+	Node *link;
+	ulong key;
+	ulong offset[NOFF];
+};
+
+Node *nodepool;
+int nnodepool;
+
+Node **hash;
+uint nhash;
+
+uint maxlen;
+uint maxsame;
+uint replacesame = 8*1024*1024;
+
+Node *freelist, **freeend;
+uint nalloc;
+
+Node*
+allocnode(void)
+{
+	int i;
+	Node *n;
+
+	if(nnodepool == 0){
+		nnodepool = 256*1024;
+		nodepool = malloc(sizeof(Node)*nnodepool);
+	}
+	if(freelist){
+		n = freelist;
+		freelist = n->link;
+		return n;
+	}
+	assert(nnodepool > 0);
+	nalloc++;
+	n = &nodepool[--nnodepool];
+	for(i=0; i<NOFF; i++)
+		n->offset[i] = -1;
+
+	return n;
+}
+
+void
+freenode(Node *n)
+{
+	if(freelist == nil)
+		freelist = n;
+	else
+		*freeend = n;
+	freeend = &n->link;
+	n->link = nil;
+}
+
+Node**
+llookup(ulong key)
+{
+	uint c;
+	Node **l, **top, *n;
+	
+	if(nhash == 0){
+		uint x;
+
+		x = length/8;
+		for(nhash=1; nhash<x; nhash<<=1)
+			;
+		hash = sbrk(sizeof(Node*)*nhash);
+	}
+
+	top = &hash[key&(nhash-1)];
+	c = 0;
+	for(l=top; *l; l=&(*l)->link){
+		c++;
+		if((*l)->key == key){
+			/* move to front */
+			n = *l;
+			*l = n->link;
+			n->link = *top;
+			*top = n;
+			return top;
+		}
+	}
+	if(c > maxlen)
+		maxlen = c;
+	return l;
+}
+
+Node*
+lookup(ulong key)
+{
+	return *llookup(key);
+}
+
+void
+insertnode(ulong key, ulong offset)
+{
+	int i;
+	Node *n, **l;
+
+	l = llookup(key);
+	if(*l == nil){
+		if(l==&hash[key&(nhash-1)])
+			nnew++;
+		*l = allocnode();
+		(*l)->key = key;
+	}
+	n = *l;
+
+	/* add or replace last */
+	for(i=0; i<NOFF-1 && n->offset[i]!=-1; i++)
+		;
+	n->offset[i] = offset;
+}
+
+void
+Bputint(Biobufhdr *b, int n)
+{
+	uchar p[4];
+
+	p[0] = n>>24;
+	p[1] = n>>16;
+	p[2] = n>>8;
+	p[3] = n;
+	Bwrite(b, p, 4);
+}
+
+void
+flushraw(void)
+{
+	if(nraw){
+		if(verbose)
+			fprint(2, "Raw %d+%d\n", rawstart, nraw);
+		zlength += 4+nraw;
+		Bputint(&bout, (1<<31)|nraw);
+		memmove(odat+nodat, data+rawstart, nraw);
+		nodat += nraw;
+		nraw = 0;
+	}
+}
+
+int
+rawbyte(int i)
+{
+	assert(acct == i);
+	if(nraw == 0)
+		rawstart = i;
+	acct++;
+	nraw++;
+	return 1;
+}
+
+int
+refblock(int i, int len, int off)
+{
+	assert(acct == i);
+	acct += len;
+	if(nraw)
+		flushraw();
+	if(verbose)
+		fprint(2, "Copy %d+%d from %d\n", i, len, off);
+	Bputint(&bout, len);
+	Bputint(&bout, off);
+	zlength += 4+4;
+	return len;
+}
+
+int
+cmprun(uchar *a, uchar *b, int len)
+{
+	int i;
+
+	if(a==b)
+		return 0;
+	for(i=0; i<len && a[i]==b[i]; i++)
+		;
+	return i;
+}
+
+int
+countrle(uchar *a)
+{
+	int i;
+
+	for(i=0; a[i]==a[0]; i++)
+		;
+	return i;
+}
+
+void
+compress(void)
+{
+	int best, i, j, o, rle, run, maxrun, maxoff;
+	ulong sum;
+	Node *n;
+
+	sum = 0;
+	for(i=0; i<win && i<length; i++)
+		sum = (sum*256+data[i])%Prime;
+	for(i=0; i<length-win; ){
+		maxrun = 0;
+		maxoff = 0;
+		if(verbose)
+			fprint(2, "look %.6lux\n", sum);
+		n = lookup(sum);
+		if(n){
+			best = -1;
+			for(o=0; o<NOFF; o++){
+				if(n->offset[o] == -1)
+					break;
+				run = cmprun(data+i, data+n->offset[o], length-i);
+				if(run > maxrun && n->offset[o]+mindist < i){
+					maxrun = run;
+					maxoff = n->offset[o];
+					best = o;
+				}
+			}
+			if(best > 0){
+				o = n->offset[best];
+				for(j=best; j>0; j--)
+					n->offset[j] = n->offset[j-1];
+				n->offset[0] = o;
+			}
+		}
+				
+		if(maxrun >= minrun)
+			j = i+refblock(i, maxrun, maxoff);
+		else
+			j = i+rawbyte(i);
+		for(; i<j; i++){
+			/* avoid huge chains from large runs of same byte */
+			rle = countrle(data+i);
+			if(rle<4)
+				insertnode(sum, i);
+			else if(rle>maxrle[data[i]]){
+				maxrle[data[i]] = rle;
+				insertnode(sum, i);
+			}
+			sum = (sum*256+data[i+win]) % Prime;
+			sum = (sum + data[i]*outn) % Prime;
+		}
+	}
+	/* could do better here */
+	for(; i<length; i++)
+		rawbyte(i);
+	flushraw();
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: bflz [-n winsize] [file]\n");
+	exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+	int fd, i, n;
+	char buf[10485760];
+
+	ARGBEGIN{
+	case 'd':
+		verbose = 1;
+		break;
+	case 's':
+		replacesame = atoi(EARGF(usage()));
+		break;
+	case 'm':
+		mindist = atoi(EARGF(usage()));
+		break;
+	case 'n':
+		win = atoi(EARGF(usage()));
+		minrun = win;
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	switch(argc){
+	default:
+		usage();
+	case 0:
+		fd = 0;
+		break;
+	case 1:
+		if((fd = open(argv[0], OREAD)) < 0)
+			sysfatal("open %s: %r", argv[0]);
+		break;
+	}
+
+	while((n = readn(fd, buf, sizeof buf)) > 0){
+		data = realloc(data, length+n);
+		if(data == nil)
+			sysfatal("realloc: %r");
+		memmove(data+length, buf, n);
+		length += n;
+		if(n < sizeof buf)
+			break;
+	}
+	odat = malloc(length);
+	if(odat == nil)
+		sysfatal("malloc: %r");
+
+	Binit(&bout, 1, OWRITE);
+	Bprint(&bout, "BLZ\n");
+	Bputint(&bout, length);
+	outn = 1;
+	for(i=0; i<win; i++)
+		outn = (outn * 256) % Prime;
+
+	if(verbose)
+		fprint(2, "256^%d = %.6lux\n", win, outn);
+	outn = Prime - outn;
+	if(verbose)
+		fprint(2, "outn = %.6lux\n", outn);
+
+	compress();
+	Bwrite(&bout, odat, nodat);
+	Bterm(&bout);
+	fprint(2, "brk %p\n", sbrk(1));
+	fprint(2, "%d nodes used; %d of %d hash slots used\n", nalloc, nnew, nhash);
+	exits(nil);	
+}
--- /dev/null
+++ b/sys/src/cmd/aux/cdsh.c
@@ -1,0 +1,133 @@
+/*
+ * The `cd' shell.  
+ * Just has cd and lc.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+char *pwd;
+char *root = "/";
+
+void
+usage(void)
+{
+	fprint(2, "usage: cdsh [-r root]\n");
+	exits("usage");
+}
+
+int
+system(char *cmd)
+{
+	int pid;
+	if((pid = fork()) < 0)
+		return -1;
+
+	if(pid == 0) {
+		dup(2, 1);
+		execl("/bin/rc", "rc", "-c", cmd, nil);
+		exits("exec");
+	}
+	waitpid();
+	return 0;
+}
+
+int
+cd(char *s)
+{
+	char *newpwd;
+	int l;
+
+	if(s[0] == '/') {
+		cleanname(s);
+		newpwd = strdup(s);
+	} else {
+		l = strlen(pwd)+1+strlen(s)+1+50;	/* 50 = crud for unicode mistakes */
+		newpwd = malloc(l);
+		snprint(newpwd, l, "%s/%s", pwd, s);
+		cleanname(newpwd);
+		assert(newpwd[0] == '/');
+	}
+
+	if(chdir(root) < 0 || (newpwd[1] != '\0' && chdir(newpwd+1) < 0)) {
+		chdir(root);
+		chdir(pwd+1);
+		free(newpwd);
+		return -1;
+	} else {
+		free(pwd);
+		pwd = newpwd;
+		return 0;
+	}
+}
+
+void
+main(int argc, char **argv)
+{
+	char *p;
+	Biobuf bin;
+	char *f[2];
+	int nf;
+
+	ARGBEGIN{
+	case 'r':
+		root = ARGF();
+		if(root == nil)
+			usage();
+		if(root[0] != '/') {
+			fprint(2, "root must be rooted\n");
+			exits("root");
+		}
+		break;
+	default:
+		usage();
+	}ARGEND;
+
+	if(argc != 0)
+		usage();
+
+	cleanname(root);
+	if(cd("/") < 0) {
+		fprint(2, "cannot cd %s: %r\n", root);
+		exits("root");
+	}
+
+	Binit(&bin, 0, OREAD);
+	while(fprint(2, "%s%% ", pwd), (p = Brdline(&bin, '\n'))) {
+		p[Blinelen(&bin)-1] = '\0';
+		nf = tokenize(p, f, nelem(f));
+		if(nf < 1)
+			continue;
+		if(strcmp(f[0], "exit") == 0)
+			break;
+		if(strcmp(f[0], "lc") == 0) {
+			if(nf == 1) {
+				if(system("/bin/lc") < 0)
+					fprint(2, "lc: %r\n");
+			} else if(nf == 2) {
+				if(strpbrk(p, "'`{}^@$#&()|\\;><"))
+					fprint(2, "no shell characters allowed\n");
+				else {
+					p = f[1];
+					*--p = ' ';
+					*--p = 'c';
+					*--p = 'l';
+					if(system(p) < 0)
+						fprint(2, "lc: %r\n");
+				}
+			}
+			continue;
+		}
+		if(strcmp(f[0], "cd") == 0) {
+			if(nf < 2)
+				fprint(2, "usage: cd dir\n");
+			else if(cd(f[1]) < 0)
+				fprint(2, "cd: %r\n");
+			continue;
+		}
+		fprint(2, "commands are cd, lc, and exit\n");
+	}
+
+	print("%s\n", pwd);
+}
--- /dev/null
+++ b/sys/src/cmd/aux/multi/mkfile
@@ -1,0 +1,76 @@
+objtype=386
+</$objtype/mkfile
+
+TARG=multi
+
+PIECES=\
+	aux/mouse\
+	aux/pcmcia\
+	aux/vga\
+	aux/zerotrunc\
+	disk/fdisk\
+	disk/format\
+	disk/mbr\
+	disk/prep\
+#	fossil/fossil\
+#	fossil/flfmt\
+	ip/ipconfig\
+	ip/ppp\
+	ndb/cs\
+	ndb/dns\
+#	replica/applylog\
+	9660srv\
+#	awk\
+	basename\
+	cat\
+	chgrp\
+	chmod\
+	cleanname\
+	cmp\
+	cp\
+	date\
+	dd\
+	dossrv\
+	echo\
+	ed\
+	ext2srv\
+#	fcp\
+	grep\
+	hget\
+	hoc\
+	ls\
+	mc\
+	mount\
+	mv\
+	ps\
+	read\
+#	rio\
+	rm\
+	sed\
+	sort\
+	srv\
+#	stats\
+	syscall\
+	tail\
+	tee\
+	test\
+	wc\
+	xd\
+
+8.multi:V: mkmulti mkfile
+	mkmulti $PIECES 
+	ls -l 8.multi
+	ls -l /386/bin/$PIECES | awk '{s += $6} END{print s}'
+
+scripts:V:
+	rm -rf ../../pc/multi
+	mkdir ../../pc/multi
+	for(i in $PIECES){
+		b=`{basename $i}
+		echo '#!/bin/multi' >>../../pc/multi/$b
+		chmod +x ../../pc/multi/$b
+	}
+
+BIN=/sys/lib/dist/bin/$objtype
+</sys/src/cmd/mkmany
+
--- /dev/null
+++ b/sys/src/cmd/aux/multi/mkmulti
@@ -1,0 +1,70 @@
+#!/bin/rc
+
+targ=multi
+
+n=0
+dir=`{pwd}
+
+fn grab {
+	echo using $*
+	for(i){
+		n=`{echo 1+$n|hoc}
+		mv $i $dir/a.$n.8
+	}
+}
+
+fn getfiles {
+	sed -n 's/^(pcc|8\^l|8l) +(-o [^ ]* +)?([^\-].*)/ \3/p' | sed 's/ -[^ ]*//g' |
+		sed 's/ [^ ]*\.a//g'
+}
+
+rm a.*.8
+>multi.h
+>multiproto.h
+
+for(i){
+echo $i...
+	b=`{basename $i}
+	p=$b
+	if(~ $b [0-9]*)
+		p=_$b
+	echo void $p^_main'(int, char**);' >>$dir/multiproto.h
+	echo "$b", $p^_main, >>$dir/multi.h
+	d=`{basename -d $i}
+	if(~ $i disk/prep disk/fdisk){
+		cd /sys/src/cmd/disk/prep
+		rm 8.$b
+		files=`{mk 8.$b | getfiles}
+	}
+	if not if(test -d /sys/src/cmd/$i && @{cd /sys/src/cmd/$i && mk 8.out}){
+		cd /sys/src/cmd/$i
+		rm 8.out
+		files=`{mk 8.out | getfiles}
+	}
+	if not if(test -d /sys/src/cmd/$i && @{cd /sys/src/cmd/$i && mk 8.$b}){
+		cd /sys/src/cmd/$i
+		rm 8.out
+		files=`{mk 8.$b | getfiles}
+	}
+	if not if(test -d /sys/src/cmd/$d && @{cd /sys/src/cmd/$d && mk 8.$b}){
+		cd /sys/src/cmd/$d
+		rm 8.$b
+		files=`{mk 8.$b | getfiles}
+	}
+	if not{
+		echo do not know how to make $i
+		exit oops
+	}
+	aux/8prefix $p^_ $files
+	grab $files
+	switch(`{pwd}){
+	case /sys/src/cmd /sys/src/cmd/aux /sys/src/cmd/ip
+		rm 8.$b
+	case *
+		mk clean
+	}
+}
+cd $dir
+8c -FVw multi.c
+8l -o 8.$targ multi.8 a.*.8
+# rm a.*.8
--- /dev/null
+++ b/sys/src/cmd/aux/multi/multi.c
@@ -1,0 +1,38 @@
+#include <u.h>
+#include <libc.h>
+
+#include "multiproto.h"
+struct {
+	char *name; 
+	void (*fn)(int, char**);
+} mains[] =
+{
+#include "multi.h"
+};
+
+void
+main(int argc, char **argv)
+{
+	int i;
+	char *cmd, *p;
+	
+	if(argc == 1){
+		fprint(2, "usage: multi cmd args...\n");
+		exits("usage");
+	}
+	
+	cmd = argv[1];
+	if(p = strrchr(cmd, '/'))
+		cmd = p+1;
+	argv++;
+	argc--;
+
+	for(i=0; i<nelem(mains); i++){
+		if(strcmp(cmd, mains[i].name) == 0){
+			mains[i].fn(argc, argv);
+			return;
+		}
+	}
+	fprint(2, "multi: no such cmd %s\n", cmd);
+	exits("no cmd");
+}
--- /dev/null
+++ b/sys/src/cmd/aux/tailfsrv.c
@@ -1,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+
+void
+main(void)
+{
+	int fd, p[2];
+	char buf[8192], n;
+
+	pipe(p);
+	fd = create("/srv/log", OWRITE, 0666);
+	fprint(fd, "%d", p[0]);
+	close(fd);
+	close(p[0]);
+	while((n = read(p[1], buf, sizeof buf)) >= 0)
+		write(1, buf, n);
+}
--- /dev/null
+++ b/sys/src/cmd/aux/unbflz.c
@@ -1,0 +1,109 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+void
+usage(void)
+{
+	fprint(2, "usage: unbflz [file]\n");
+	exits("usage");
+}
+
+int
+Bgetint(Biobuf *b)
+{
+	uchar p[4];
+
+	if(Bread(b, p, 4) != 4)
+		sysfatal("short read");
+	return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
+}
+
+/*
+ * memmove but make sure overlap works properly.
+ */
+void
+copy(uchar *dst, uchar *src, int n)
+{
+	while(n-- > 0)
+		*dst++ = *src++;
+}
+
+void
+main(int argc, char **argv)
+{
+	Biobuf *b, bin;
+	char buf[5];
+	uchar *data;
+	ulong *blk, l;
+	int nblk, mblk;
+	int sum;
+	int i, j, length, m, n, o;
+
+	ARGBEGIN{
+	default:
+		usage();
+	}ARGEND
+
+	switch(argc){
+	default:
+		usage();
+	case 0:
+		Binit(&bin, 0, OREAD);
+		b = &bin;
+		break;
+	case 1:
+		if((b = Bopen(argv[0], OREAD)) == nil)
+			sysfatal("open %s: %r", argv[0]);
+		break;
+	}
+
+	if(Bread(b, buf, 4) != 4)
+		sysfatal("short read");
+
+	if(memcmp(buf, "BLZ\n", 4) != 0)
+		sysfatal("bad header");
+
+	length = Bgetint(b);
+	data = malloc(length);
+	if(data == nil)
+		sysfatal("out of memory");
+	sum = 0;
+	nblk = 0;
+	mblk = 0;
+	blk = nil;
+	while(sum < length){
+		if(nblk>=mblk){
+			mblk += 16384;
+			blk = realloc(blk, (mblk+1)*sizeof(blk[0]));
+			if(blk == nil)
+				sysfatal("out of memory");
+		}
+		l = Bgetint(b);
+		blk[nblk++] = l;
+		if(l&(1<<31))
+			l &= ~(1<<31);
+		else
+			blk[nblk++] = Bgetint(b);
+		sum += l;
+	}
+	if(sum != length)
+		sysfatal("bad compressed data %d %d", sum, length);
+	i = 0;
+	j = 0;
+	while(i < length){
+		assert(j < nblk);
+		n = blk[j++];
+		if(n&(1<<31)){
+			n &= ~(1<<31);
+			if((m=Bread(b, data+i, n)) != n)
+				sysfatal("short read %d %d", n, m);
+		}else{
+			o = blk[j++];
+			copy(data+i, data+o, n);
+		}
+		i += n;
+	}
+	write(1, data, length);
+	exits(nil);
+}
--- /dev/null
+++ b/sys/src/cmd/bzfs/bzfs.h
@@ -1,0 +1,11 @@
+int unbzip(int);
+void _unbzip(int, int);
+int unbflz(int);
+int xexpand(int);
+void *emalloc(ulong);
+void *erealloc(void*, ulong);
+char *estrdup(char*);
+
+void ramfsmain(int, char**);
+extern int chatty;
+void error(char*, ...);
--- /dev/null
+++ b/sys/src/cmd/bzfs/mkext.c
@@ -1,0 +1,288 @@
+/*
+ * bzip2-based file system.
+ * the file system itself is just a bzipped2 xzipped mkfs archive
+ * prefixed with "bzfilesystem\n" and suffixed with
+ * a kilobyte of zeros.
+ *
+ * changes to the file system are only kept in 
+ * memory, not written back to the disk.
+ *
+ * this is intended for use on a floppy boot disk.
+ * we assume the file is in the dos file system and
+ * contiguous on the disk: finding it amounts to
+ * looking at the beginning of each sector for 
+ * "bzfilesystem\n".  then we pipe it through 
+ * bunzip2 and store the files in a file tree in memory.
+ * things are slightly complicated by the fact that
+ * devfloppy requires reads to be on a 512-byte
+ * boundary and be a multiple of 512 bytes; we
+ * fork a process to relieve bunzip2 of this restriction.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <auth.h>
+#include <fcall.h>
+#include "bzfs.h"
+
+enum{
+	LEN	= 8*1024,
+	NFLDS	= 6,		/* filename, modes, uid, gid, mtime, bytes */
+};
+
+void	mkdirs(char*, char*);
+void	mkdir(char*, ulong, ulong, char*, char*);
+void	extract(char*, ulong, ulong, char*, char*, ulong);
+void	seekpast(ulong);
+void	error(char*, ...);
+void	warn(char*, ...);
+void	usage(void);
+char *mtpt;
+Biobufhdr bin;
+uchar	binbuf[2*LEN];
+
+void
+usage(void)
+{
+	fprint(2, "usage: bzfs [-m mtpt] [-s] [-f file] [-h]\n");
+	exits("usage");
+}
+
+/*
+ * floppy disks can only be read on 512-byte 
+ * boundaries and in 512 byte multiples.
+ * feed one over a pipe to allow arbitrary reading.
+ */
+char zero[512];
+int
+blockread(int in, char *first, int nfirst)
+{
+	int p[2], out, n, rv;
+	char blk[512];
+
+	if(pipe(p) < 0)
+		sysfatal("pipe: %r");
+	rv = p[0];
+	out = p[1];
+	switch(rfork(RFPROC|RFNOTEG|RFFDG)){
+	case -1:
+		sysfatal("fork: %r");
+	case 0:
+		close(rv);
+		break;
+	default:
+		close(in);
+		close(out);
+		return rv;
+	}
+
+	write(out, first, nfirst);
+	
+	while((n=read(in, blk, sizeof blk)) > 0){
+		if(write(out, blk, n) != n)
+			break;
+		if(n == sizeof(blk) && memcmp(zero, blk, n) == n)
+			break;
+	}
+	_exits(0);
+	return -1;
+}
+
+enum { NAMELEN = 28 };
+
+void
+main(int argc, char **argv)
+{
+	char *rargv[10];
+	int rargc;
+	char *fields[NFLDS], name[2*LEN], *p, *namep;
+	char uid[NAMELEN], gid[NAMELEN];
+	ulong mode, bytes, mtime;
+	char *file;
+	int i, n, stdin, fd, chatty;
+	char blk[512];
+
+	if(argc>1 && strcmp(argv[1], "RAMFS") == 0){
+		argv[1] = argv[0];
+		ramfsmain(argc-1, argv+1);
+		exits(nil);
+	}
+	if(argc>1 && strcmp(argv[1], "BUNZIP") == 0){
+		_unbzip(0, 1);
+		exits(nil);
+	}
+
+	rfork(RFNOTEG);
+	stdin = 0;
+	file = nil;
+	namep = name;
+	mtpt = "/root";
+	chatty = 0;
+	ARGBEGIN{
+	case 'd':
+		chatty = !chatty;
+		break;
+	case 'f':
+		file = ARGF();
+		break;
+	case 's':
+		stdin++;
+		break;
+	case 'm':
+		mtpt = ARGF();
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	if(argc != 0)
+		usage();
+
+	if(file == nil) {
+		fprint(2, "must specify -f file\n");
+		usage();
+	}
+
+	if((fd = open(file, OREAD)) < 0) {
+		fprint(2, "cannot open \"%s\": %r\n", file);
+		exits("open");
+	}
+
+	rargv[0] = "ramfs";
+	rargc = 1;
+	if(stdin)
+		rargv[rargc++] = "-i";
+	rargv[rargc++] = "-m";
+	rargv[rargc++] = mtpt;
+	rargv[rargc] = nil;
+	ramfsmain(rargc, rargv);
+
+	if(1 || strstr(file, "disk")) {	/* search for archive on block boundary */
+if(chatty) fprint(2, "searching for bz\n");
+		for(i=0;; i++){
+			if((n = readn(fd, blk, sizeof blk)) != sizeof blk)
+				sysfatal("read %d gets %d: %r\n", i, n);
+			if(strncmp(blk, "bzfilesystem\n", 13) == 0)
+				break;
+		}
+if(chatty) fprint(2, "found at %d\n", i);
+	}
+
+	if(chdir(mtpt) < 0)
+		error("chdir %s: %r", mtpt);
+
+	fd = unbflz(unbzip(blockread(fd, blk+13, sizeof(blk)-13)));
+
+	Binits(&bin, fd, OREAD, binbuf, sizeof binbuf);
+	while(p = Brdline(&bin, '\n')){
+		p[Blinelen(&bin)-1] = '\0';
+if(chatty) fprint(2, "%s\n", p);
+		if(strcmp(p, "end of archive") == 0){
+			_exits(0);
+		}
+		if(getfields(p, fields, NFLDS, 0, " \t") != NFLDS){
+			warn("too few fields in file header");
+			continue;
+		}
+		strcpy(namep, fields[0]);
+		mode = strtoul(fields[1], 0, 8);
+		mtime = strtoul(fields[4], 0, 10);
+		bytes = strtoul(fields[5], 0, 10);
+		strncpy(uid, fields[2], NAMELEN);
+		strncpy(gid, fields[3], NAMELEN);
+		if(mode & DMDIR)
+			mkdir(name, mode, mtime, uid, gid);
+		else
+			extract(name, mode, mtime, uid, gid, bytes);
+	}
+	fprint(2, "premature end of archive\n");
+	exits("premature end of archive");
+}
+
+char buf[8192];
+
+int
+ffcreate(char *name, ulong mode, char *uid, char *gid, ulong mtime, int length)
+{
+	int fd, om;
+	Dir nd;
+
+	sprint(buf, "%s/%s", mtpt, name);
+	om = ORDWR;
+	if(mode&DMDIR)
+		om = OREAD;
+	if((fd = create(buf, om, (mode&DMDIR)|0666)) < 0)
+		error("create %s: %r", buf);
+
+	nulldir(&nd);
+	nd.mode = mode;
+	nd.uid = uid;
+	nd.gid = gid;
+	nd.mtime = mtime;
+	if(length)
+		nd.length = length;
+	if(dirfwstat(fd, &nd) < 0)	
+		error("fwstat %s: %r", buf);
+
+	return fd;
+}
+
+void
+mkdir(char *name, ulong mode, ulong mtime, char *uid, char *gid)
+{
+	close(ffcreate(name, mode, uid, gid, mtime, 0));
+}
+
+void
+extract(char *name, ulong mode, ulong mtime, char *uid, char *gid, ulong bytes)
+{
+	int fd, tot, n;
+
+	fd = ffcreate(name, mode, uid, gid, mtime, bytes);
+
+	for(tot = 0; tot < bytes; tot += n){
+		n = sizeof buf;
+		if(tot + n > bytes)
+			n = bytes - tot;
+		n = Bread(&bin, buf, n);
+		if(n <= 0)
+			error("premature eof reading %s", name);
+		if(write(fd, buf, n) != n)
+			error("short write writing %s", name);
+	}
+	close(fd);
+}
+
+void
+error(char *fmt, ...)
+{
+	char buf[1024];
+	va_list arg;
+
+	sprint(buf, "%s: ", argv0);
+	va_start(arg, fmt);
+	vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	fprint(2, "%s\n", buf);
+	exits(0);
+}
+
+void
+warn(char *fmt, ...)
+{
+	char buf[1024];
+	va_list arg;
+
+	sprint(buf, "%s: ", argv0);
+	va_start(arg, fmt);
+	vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	fprint(2, "%s\n", buf);
+}
+
+int
+_efgfmt(Fmt*)
+{
+	return -1;
+}
--- /dev/null
+++ b/sys/src/cmd/bzfs/mkfile
@@ -1,0 +1,20 @@
+</$objtype/mkfile
+
+TARG=bzfs
+
+OFILES=\
+	mkext.$O\
+	oramfs.$O\
+	unbflz.$O\
+	unbzip.$O\
+
+HFILES=bzfs.h
+
+BIN=/sys/lib/dist/bin/$objtype
+LIB=/sys/src/cmd/bzip2/lib/libbzip2.a$O
+</sys/src/cmd/mkone
+
+CFLAGS=$CFLAGS -p -I/sys/src/cmd/bzip2/lib
+
+/sys/src/cmd/bzip2/lib/libbzip2.a$O:
+	@{cd /sys/src/cmd/bzip2/lib && mk}
--- /dev/null
+++ b/sys/src/cmd/bzfs/oramfs.c
@@ -1,0 +1,927 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include "bzfs.h"
+
+/*
+ * Rather than reading /adm/users, which is a lot of work for
+ * a toy program, we assume all groups have the form
+ *	NNN:user:user:
+ * meaning that each user is the leader of his own group.
+ */
+
+enum
+{
+	OPERM	= 0x3,		/* mask of all permission types in open mode */
+	Nram	= 512,
+	Maxsize	= 512*1024*1024,
+	Maxfdata	= 8192,
+};
+
+typedef struct Fid Fid;
+typedef struct Ram Ram;
+
+struct Fid
+{
+	short	busy;
+	short	open;
+	short	rclose;
+	int	fid;
+	Fid	*next;
+	char	*user;
+	Ram	*ram;
+};
+
+struct Ram
+{
+	short	busy;
+	short	open;
+	long	parent;		/* index in Ram array */
+	Qid	qid;
+	long	perm;
+	char	*name;
+	ulong	atime;
+	ulong	mtime;
+	char	*user;
+	char	*group;
+	char	*muid;
+	char	*data;
+	long	ndata;
+};
+
+enum
+{
+	Pexec =		1,
+	Pwrite = 	2,
+	Pread = 	4,
+	Pother = 	1,
+	Pgroup = 	8,
+	Powner =	64,
+};
+
+ulong	path;		/* incremented for each new file */
+Fid	*fids;
+Ram	ram[Nram];
+int	nram;
+int	mfd[2];
+char	*user;
+uchar	mdata[IOHDRSZ+Maxfdata];
+uchar	rdata[Maxfdata];	/* buffer for data in reply */
+uchar statbuf[STATMAX];
+Fcall thdr;
+Fcall	rhdr;
+int	messagesize = sizeof mdata;
+
+Fid *	newfid(int);
+uint	ramstat(Ram*, uchar*, uint);
+void	io(void);
+void	*erealloc(void*, ulong);
+void	*emalloc(ulong);
+char	*estrdup(char*);
+void	ramfsusage(void);
+int	perm(Fid*, Ram*, int);
+char *atom(char*);
+
+char	*rflush(Fid*), *rversion(Fid*), *rauth(Fid*),
+	*rattach(Fid*), *rwalk(Fid*),
+	*ropen(Fid*), *rcreate(Fid*),
+	*rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
+	*rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
+
+char 	*(*fcalls[])(Fid*) = {
+	[Tversion]	rversion,
+	[Tflush]	rflush,
+	[Tauth]	rauth,
+	[Tattach]	rattach,
+	[Twalk]		rwalk,
+	[Topen]		ropen,
+	[Tcreate]	rcreate,
+	[Tread]		rread,
+	[Twrite]	rwrite,
+	[Tclunk]	rclunk,
+	[Tremove]	rremove,
+	[Tstat]		rstat,
+	[Twstat]	rwstat,
+};
+
+char	Eperm[] =	"permission denied";
+char	Enotdir[] =	"not a directory";
+char	Enoauth[] =	"no authentication in ramfs";
+char	Enotexist[] =	"file does not exist";
+char	Einuse[] =	"file in use";
+char	Eexist[] =	"file exists";
+char	Eisdir[] =	"file is a directory";
+char	Enotowner[] =	"not owner";
+char	Eisopen[] = 	"file already open for I/O";
+char	Excl[] = 	"exclusive use file already open";
+char	Ename[] = 	"illegal name";
+char	Eversion[] =	"unknown 9P version";
+
+int debug;
+
+void
+notifyf(void *a, char *s)
+{
+	USED(a);
+	if(strncmp(s, "interrupt", 9) == 0)
+		noted(NCONT);
+	noted(NDFLT);
+}
+
+void
+ramfsmain(int argc, char *argv[])
+{
+	Ram *r;
+	char *defmnt;
+	int p[2];
+	char buf[32];
+	int fd, srvfd;
+	int stdio = 0;
+
+	srvfd = -1;
+	defmnt = "/tmp";
+	ARGBEGIN{
+	case 'D':
+		debug = 1;
+		break;
+	case 'i':		/* this is DIFFERENT from normal ramfs; use 1 for both for kernel */
+		defmnt = 0;
+		stdio = 1;
+		srvfd = 0;
+		mfd[0] = 1;
+		mfd[1] = 1;
+		break;
+	case 's':
+		defmnt = 0;
+		break;
+	case 'm':
+		defmnt = ARGF();
+		break;
+	default:
+		ramfsusage();
+	}ARGEND
+
+	if(!stdio){
+		if(pipe(p) < 0)
+			error("pipe failed");
+		srvfd = p[1];
+		mfd[0] = p[0];
+		mfd[1] = p[0];
+		if(defmnt == 0){
+			fd = create("#s/ramfs", OWRITE, 0666);
+			if(fd < 0)
+				error("create of /srv/ramfs failed");
+			sprint(buf, "%d", p[1]);
+			if(write(fd, buf, strlen(buf)) < 0)
+				error("writing /srv/ramfs");
+		}
+	}
+
+	user = atom(getuser());
+	notify(notifyf);
+	nram = 1;
+	r = &ram[0];
+	r->busy = 1;
+	r->data = 0;
+	r->ndata = 0;
+	r->perm = DMDIR | 0775;
+	r->qid.type = QTDIR;
+	r->qid.path = 0LL;
+	r->qid.vers = 0;
+	r->parent = 0;
+	r->user = user;
+	r->group = user;
+	r->muid = user;
+	r->atime = time(0);
+	r->mtime = r->atime;
+	r->name = estrdup(".");
+
+	if(debug)
+		fmtinstall('F', fcallfmt);
+	switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
+	case -1:
+		error("fork");
+	case 0:
+		close(srvfd);
+		io();
+		break;
+	default:
+		close(mfd[0]);	/* don't deadlock if child fails */
+		if(defmnt && mount(srvfd, -1, defmnt, MREPL|MCREATE, "") < 0)
+			error("mount failed: %r");
+	}
+}
+
+char*
+rversion(Fid*)
+{
+	Fid *f;
+
+	for(f = fids; f; f = f->next)
+		if(f->busy)
+			rclunk(f);
+	if(thdr.msize > sizeof mdata)
+		rhdr.msize = sizeof mdata;
+	else
+		rhdr.msize = thdr.msize;
+	messagesize = rhdr.msize;
+	if(strncmp(thdr.version, "9P2000", 6) != 0)
+		return Eversion;
+	rhdr.version = "9P2000";
+	return 0;
+}
+
+char*
+rauth(Fid*)
+{
+	return "ramfs: no authentication required";
+}
+
+char*
+rflush(Fid *f)
+{
+	USED(f);
+	return 0;
+}
+
+char*
+rattach(Fid *f)
+{
+	/* no authentication! */
+	f->busy = 1;
+	f->rclose = 0;
+	f->ram = &ram[0];
+	rhdr.qid = f->ram->qid;
+	if(thdr.uname[0])
+		f->user = atom(thdr.uname);
+	else
+		f->user = atom("none");
+	if(strcmp(user, "none") == 0)
+		user = f->user;
+	return 0;
+}
+
+char*
+clone(Fid *f, Fid **nf)
+{
+	if(f->open)
+		return Eisopen;
+	if(f->ram->busy == 0)
+		return Enotexist;
+	*nf = newfid(thdr.newfid);
+	(*nf)->busy = 1;
+	(*nf)->open = 0;
+	(*nf)->rclose = 0;
+	(*nf)->ram = f->ram;
+	(*nf)->user = f->user;	/* no ref count; the leakage is minor */
+	return 0;
+}
+
+char*
+rwalk(Fid *f)
+{
+	Ram *r, *fram;
+	char *name;
+	Ram *parent;
+	Fid *nf;
+	char *err;
+	ulong t;
+	int i;
+
+	err = nil;
+	nf = nil;
+	rhdr.nwqid = 0;
+	if(rhdr.newfid != rhdr.fid){
+		err = clone(f, &nf);
+		if(err)
+			return err;
+		f = nf;	/* walk the new fid */
+	}
+	fram = f->ram;
+	if(thdr.nwname > 0){
+		t = time(0);
+		for(i=0; i<thdr.nwname && i<MAXWELEM; i++){
+			if((fram->qid.type & QTDIR) == 0){
+				err = Enotdir;
+ 				break;
+			}
+			if(fram->busy == 0){
+				err = Enotexist;
+				break;
+			}
+			fram->atime = t;
+			name = thdr.wname[i];
+			if(strcmp(name, ".") == 0){
+    Found:
+				rhdr.nwqid++;
+				rhdr.wqid[i] = fram->qid;
+				continue;
+			}
+			parent = &ram[fram->parent];
+#ifdef CHECKS
+			if(!perm(f, parent, Pexec)){
+				err = Eperm;
+				break;
+			}
+#endif
+			if(strcmp(name, "..") == 0){
+				fram = parent;
+				goto Found;
+			}
+			for(r=ram; r < &ram[nram]; r++)
+				if(r->busy && r->parent==fram-ram && strcmp(name, r->name)==0){
+					fram = r;
+					goto Found;
+				}
+			break;
+		}
+		if(i==0 && err == nil)
+			err = Enotexist;
+	}
+	if(nf != nil && (err!=nil || rhdr.nwqid<thdr.nwname)){
+		/* clunk the new fid, which is the one we walked */
+		f->busy = 0;
+		f->ram = nil;
+	}
+	if(rhdr.nwqid == thdr.nwname)	/* update the fid after a successful walk */
+		f->ram = fram;
+	return err;
+}
+
+char *
+ropen(Fid *f)
+{
+	Ram *r;
+	int mode, trunc;
+
+	if(f->open)
+		return Eisopen;
+	r = f->ram;
+	if(r->busy == 0)
+		return Enotexist;
+	if(r->perm & DMEXCL)
+		if(r->open)
+			return Excl;
+	mode = thdr.mode;
+	if(r->qid.type & QTDIR){
+		if(mode != OREAD)
+			return Eperm;
+		rhdr.qid = r->qid;
+		return 0;
+	}
+	if(mode & ORCLOSE){
+		/* can't remove root; must be able to write parent */
+		if(r->qid.path==0 || !perm(f, &ram[r->parent], Pwrite))
+			return Eperm;
+		f->rclose = 1;
+	}
+	trunc = mode & OTRUNC;
+	mode &= OPERM;
+	if(mode==OWRITE || mode==ORDWR || trunc)
+		if(!perm(f, r, Pwrite))
+			return Eperm;
+	if(mode==OREAD || mode==ORDWR)
+		if(!perm(f, r, Pread))
+			return Eperm;
+	if(mode==OEXEC)
+		if(!perm(f, r, Pexec))
+			return Eperm;
+	if(trunc && (r->perm&DMAPPEND)==0){
+		r->ndata = 0;
+		if(r->data)
+			free(r->data);
+		r->data = 0;
+		r->qid.vers++;
+	}
+	rhdr.qid = r->qid;
+	rhdr.iounit = messagesize-IOHDRSZ;
+	f->open = 1;
+	r->open++;
+	return 0;
+}
+
+char *
+rcreate(Fid *f)
+{
+	Ram *r;
+	char *name;
+	long parent, prm;
+
+	if(f->open)
+		return Eisopen;
+	if(f->ram->busy == 0)
+		return Enotexist;
+	parent = f->ram - ram;
+	if((f->ram->qid.type&QTDIR) == 0)
+		return Enotdir;
+	/* must be able to write parent */
+#ifdef CHECKS
+	if(!perm(f, f->ram, Pwrite))
+		return Eperm;
+#endif
+	prm = thdr.perm;
+	name = thdr.name;
+	if(strcmp(name, ".")==0 || strcmp(name, "..")==0)
+		return Ename;
+	for(r=ram; r<&ram[nram]; r++)
+		if(r->busy && parent==r->parent)
+		if(strcmp((char*)name, r->name)==0)
+			return Einuse;
+	for(r=ram; r->busy; r++)
+		if(r == &ram[Nram-1])
+			return "no free ram resources";
+	r->busy = 1;
+	r->qid.path = ++path;
+	r->qid.vers = 0;
+	if(prm & DMDIR)
+		r->qid.type |= QTDIR;
+	r->parent = parent;
+	free(r->name);
+	r->name = estrdup(name);
+	r->user = f->user;
+	r->group = f->ram->group;
+	r->muid = f->ram->muid;
+	if(prm & DMDIR)
+		prm = (prm&~0777) | (f->ram->perm&prm&0777);
+	else
+		prm = (prm&(~0777|0111)) | (f->ram->perm&prm&0666);
+	r->perm = prm;
+	r->ndata = 0;
+	if(r-ram >= nram)
+		nram = r - ram + 1;
+	r->atime = time(0);
+	r->mtime = r->atime;
+	f->ram->mtime = r->atime;
+	f->ram = r;
+	rhdr.qid = r->qid;
+	rhdr.iounit = messagesize-IOHDRSZ;
+	f->open = 1;
+	if(thdr.mode & ORCLOSE)
+		f->rclose = 1;
+	r->open++;
+	return 0;
+}
+
+char*
+rread(Fid *f)
+{
+	Ram *r;
+	uchar *buf;
+	long off;
+	int n, m, cnt;
+
+	if(f->ram->busy == 0)
+		return Enotexist;
+	n = 0;
+	rhdr.count = 0;
+	off = thdr.offset;
+	buf = rdata;
+	cnt = thdr.count;
+	if(cnt > messagesize)	/* shouldn't happen, anyway */
+		cnt = messagesize;
+	if(f->ram->qid.type & QTDIR){
+		for(r=ram+1; off > 0; r++){
+			if(r->busy && r->parent==f->ram-ram)
+				off -= ramstat(r, statbuf, sizeof statbuf);
+			if(r == &ram[nram-1])
+				return 0;
+		}
+		for(; r<&ram[nram] && n < cnt; r++){
+			if(!r->busy || r->parent!=f->ram-ram)
+				continue;
+			m = ramstat(r, buf+n, cnt-n);
+			if(m == 0)
+				break;
+			n += m;
+		}
+		rhdr.data = (char*)rdata;
+		rhdr.count = n;
+		return 0;
+	}
+	r = f->ram;
+	if(off >= r->ndata)
+		return 0;
+	r->atime = time(0);
+	n = cnt;
+	if(off+n > r->ndata)
+		n = r->ndata - off;
+	rhdr.data = r->data+off;
+	rhdr.count = n;
+	return 0;
+}
+
+char*
+rwrite(Fid *f)
+{
+	Ram *r;
+	ulong off;
+	int cnt;
+
+	r = f->ram;
+	if(r->busy == 0)
+		return Enotexist;
+	off = thdr.offset;
+	if(r->perm & DMAPPEND)
+		off = r->ndata;
+	cnt = thdr.count;
+	if(r->qid.type & QTDIR)
+		return Eisdir;
+	if(off+cnt >= Maxsize)		/* sanity check */
+		return "write too big";
+	if(off+cnt > r->ndata)
+		r->data = erealloc(r->data, off+cnt);
+	if(off > r->ndata)
+		memset(r->data+r->ndata, 0, off-r->ndata);
+	if(off+cnt > r->ndata)
+		r->ndata = off+cnt;
+	memmove(r->data+off, thdr.data, cnt);
+	r->qid.vers++;
+	r->mtime = time(0);
+	rhdr.count = cnt;
+	return 0;
+}
+
+void
+realremove(Ram *r)
+{
+	r->ndata = 0;
+	if(r->data)
+		free(r->data);
+	r->data = 0;
+	r->parent = 0;
+	memset(&r->qid, 0, sizeof r->qid);
+	free(r->name);
+	r->name = nil;
+	r->busy = 0;
+}
+
+char *
+rclunk(Fid *f)
+{
+	if(f->open)
+		f->ram->open--;
+	if(f->rclose)
+		realremove(f->ram);
+	f->busy = 0;
+	f->open = 0;
+	f->ram = 0;
+	return 0;
+}
+
+char *
+rremove(Fid *f)
+{
+	Ram *r;
+
+	if(f->open)
+		f->ram->open--;
+	f->busy = 0;
+	f->open = 0;
+	r = f->ram;
+	f->ram = 0;
+#ifdef CHECKS
+	if(r->qid.path == 0 || !perm(f, &ram[r->parent], Pwrite))
+		return Eperm;
+#endif
+	ram[r->parent].mtime = time(0);
+	realremove(r);
+	return 0;
+}
+
+char *
+rstat(Fid *f)
+{
+	if(f->ram->busy == 0)
+		return Enotexist;
+	rhdr.nstat = ramstat(f->ram, statbuf, sizeof statbuf);
+	rhdr.stat = statbuf;
+	return 0;
+}
+
+char *
+rwstat(Fid *f)
+{
+	Ram *r, *s;
+	Dir dir;
+
+	if(f->ram->busy == 0)
+		return Enotexist;
+	convM2D(thdr.stat, thdr.nstat, &dir, (char*)statbuf);
+	r = f->ram;
+
+	/*
+	 * To change length, must have write permission on file.
+	 */
+#ifdef CHECKS
+	if(dir.length!=~0 && dir.length!=r->ndata){
+	 	if(!perm(f, r, Pwrite))
+			return Eperm;
+	}
+#endif
+
+	/*
+	 * To change name, must have write permission in parent
+	 * and name must be unique.
+	 */
+	if(dir.name[0]!='\0' && strcmp(dir.name, r->name)!=0){
+#ifdef CHECKS
+	 	if(!perm(f, &ram[r->parent], Pwrite))
+			return Eperm;
+#endif
+		for(s=ram; s<&ram[nram]; s++)
+			if(s->busy && s->parent==r->parent)
+			if(strcmp(dir.name, s->name)==0)
+				return Eexist;
+	}
+
+#ifdef OWNERS
+	/*
+	 * To change mode, must be owner or group leader.
+	 * Because of lack of users file, leader=>group itself.
+	 */
+	if(dir.mode!=~0 && r->perm!=dir.mode){
+		if(strcmp(f->user, r->user) != 0)
+		if(strcmp(f->user, r->group) != 0)
+			return Enotowner;
+	}
+
+	/*
+	 * To change group, must be owner and member of new group,
+	 * or leader of current group and leader of new group.
+	 * Second case cannot happen, but we check anyway.
+	 */
+	if(dir.gid[0]!='\0' && strcmp(r->group, dir.gid)!=0){
+		if(strcmp(f->user, r->user) == 0)
+		if(strcmp(f->user, dir.gid) == 0)
+			goto ok;
+		if(strcmp(f->user, r->group) == 0)
+		if(strcmp(f->user, dir.gid) == 0)
+			goto ok;
+		return Enotowner;
+		ok:;
+	}
+#endif
+
+	/* all ok; do it */
+	if(dir.mode != ~0){
+		dir.mode &= ~DMDIR;	/* cannot change dir bit */
+		dir.mode |= r->perm&DMDIR;
+		r->perm = dir.mode;
+	}
+	if(dir.name[0] != '\0'){
+		free(r->name);
+		r->name = estrdup(dir.name);
+	}
+	if(dir.gid[0] != '\0')
+		r->group = atom(dir.gid);
+
+	if(dir.uid[0] != '\0')
+		r->user = atom(dir.uid);
+
+	if(dir.length!=~0 && dir.length!=r->ndata){
+		r->data = erealloc(r->data, dir.length);
+		if(r->ndata < dir.length)
+			memset(r->data+r->ndata, 0, dir.length-r->ndata);
+		r->ndata = dir.length;
+	}
+
+	if(dir.mtime != ~0)
+		r->mtime = dir.mtime;
+
+	ram[r->parent].mtime = time(0);
+	return 0;
+}
+
+uint
+ramstat(Ram *r, uchar *buf, uint nbuf)
+{
+	Dir dir;
+
+	dir.name = r->name;
+	dir.qid = r->qid;
+	dir.mode = r->perm;
+	dir.length = r->ndata;
+	dir.uid = r->user;
+	dir.gid = r->group;
+	dir.muid = r->muid;
+	dir.atime = r->atime;
+	dir.mtime = r->mtime;
+	return convD2M(&dir, buf, nbuf);
+}
+
+Fid *
+newfid(int fid)
+{
+	Fid *f, *ff;
+
+	ff = 0;
+	for(f = fids; f; f = f->next)
+		if(f->fid == fid)
+			return f;
+		else if(!ff && !f->busy)
+			ff = f;
+	if(ff){
+		ff->fid = fid;
+		return ff;
+	}
+	f = emalloc(sizeof *f);
+	f->ram = nil;
+	f->fid = fid;
+	f->next = fids;
+	fids = f;
+	return f;
+}
+
+void
+io(void)
+{
+	char *err;
+	int n, pid;
+
+	pid = getpid();
+
+	for(;;){
+		/*
+		 * reading from a pipe or a network device
+		 * will give an error after a few eof reads.
+		 * however, we cannot tell the difference
+		 * between a zero-length read and an interrupt
+		 * on the processes writing to us,
+		 * so we wait for the error.
+		 */
+		n = read9pmsg(mfd[0], mdata, messagesize);
+		if(n < 0)
+			error("mount read: %r");
+		if(n == 0)
+			continue;
+		if(convM2S(mdata, n, &thdr) == 0)
+			continue;
+
+		if(debug)
+			fprint(2, "ramfs %d:<-%F\n", pid, &thdr);
+
+		if(!fcalls[thdr.type])
+			err = "bad fcall type";
+		else
+			err = (*fcalls[thdr.type])(newfid(thdr.fid));
+		if(err){
+			rhdr.type = Rerror;
+			rhdr.ename = err;
+		}else{
+			rhdr.type = thdr.type + 1;
+			rhdr.fid = thdr.fid;
+		}
+		rhdr.tag = thdr.tag;
+		if(debug)
+			fprint(2, "ramfs %d:->%F\n", pid, &rhdr);/**/
+		n = convS2M(&rhdr, mdata, messagesize);
+		if(n == 0)
+			error("convS2M error on write");
+		if(write(mfd[1], mdata, n) != n)
+			error("mount write");
+	}
+}
+
+int
+perm(Fid *f, Ram *r, int p)
+{
+	if((p*Pother) & r->perm)
+		return 1;
+	if(strcmp(f->user, r->group)==0 && ((p*Pgroup) & r->perm))
+		return 1;
+	if(strcmp(f->user, r->user)==0 && ((p*Powner) & r->perm))
+		return 1;
+	return 0;
+}
+
+void *
+emalloc(ulong n)
+{
+	void *p;
+
+	p = malloc(n);
+	if(!p)
+		error("out of memory");
+	memset(p, 0, n);
+	return p;
+}
+
+void *
+erealloc(void *p, ulong n)
+{
+	p = realloc(p, n);
+	if(!p)
+		error("out of memory");
+	return p;
+}
+
+char *
+estrdup(char *q)
+{
+	char *p;
+	int n;
+
+	n = strlen(q)+1;
+	p = malloc(n);
+	if(!p)
+		error("out of memory");
+	memmove(p, q, n);
+	return p;
+}
+
+void
+ramfsusage(void)
+{
+	fprint(2, "usage: %s [-is] [-m mountpoint]\n", argv0);
+	exits("usage");
+}
+
+/*
+ *	Custom allocators to avoid malloc overheads on small objects.
+ * 	We never free these.  (See below.)
+ */
+typedef struct Stringtab	Stringtab;
+struct Stringtab {
+	Stringtab *link;
+	char *str;
+};
+static Stringtab*
+taballoc(void)
+{
+	static Stringtab *t;
+	static uint nt;
+
+	if(nt == 0){
+		t = malloc(64*sizeof(Stringtab));
+		if(t == 0)
+			sysfatal("out of memory");
+		nt = 64;
+	}
+	nt--;
+	return t++;
+}
+
+static char*
+xstrdup(char *s)
+{
+	char *r;
+	int len;
+	static char *t;
+	static int nt;
+
+	len = strlen(s)+1;
+	if(len >= 8192)
+		sysfatal("strdup big string");
+
+	if(nt < len){
+		t = malloc(8192);
+		if(t == 0)
+			sysfatal("out of memory");
+		nt = 8192;
+	}
+	r = t;
+	t += len;
+	nt -= len;
+	strcpy(r, s);
+	return r;
+}
+
+/*
+ *	Return a uniquely allocated copy of a string.
+ *	Don't free these -- they stay in the table for the 
+ *	next caller who wants that particular string.
+ *	String comparison can be done with pointer comparison 
+ *	if you know both strings are atoms.
+ */
+static Stringtab *stab[1024];
+
+static uint
+hash(char *s)
+{
+	uint h;
+	uchar *p;
+
+	h = 0;
+	for(p=(uchar*)s; *p; p++)
+		h = h*37 + *p;
+	return h;
+}
+
+char*
+atom(char *str)
+{
+	uint h;
+	Stringtab *tab;
+	
+	h = hash(str) % nelem(stab);
+	for(tab=stab[h]; tab; tab=tab->link)
+		if(strcmp(str, tab->str) == 0)
+			return tab->str;
+
+	tab = taballoc();
+	tab->str = xstrdup(str);
+	tab->link = stab[h];
+	stab[h] = tab;
+	return tab->str;
+}
--- /dev/null
+++ b/sys/src/cmd/bzfs/unbflz.c
@@ -1,0 +1,108 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "bzfs.h"
+
+int
+Bgetint(Biobuf *b)
+{
+	uchar p[4];
+
+	if(Bread(b, p, 4) != 4)
+		sysfatal("short read");
+	return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
+}
+
+/*
+ * memmove but make sure overlap works properly.
+ */
+void
+copy(uchar *dst, uchar *src, int n)
+{
+	while(n-- > 0)
+		*dst++ = *src++;
+}
+
+int
+unbflz(int in)
+{
+	int rv, out, p[2];
+	Biobuf *b, bin;
+	char buf[5];
+	uchar *data;
+	int i, j, length, n, m, o, sum;
+	ulong *blk;
+	int nblk, mblk;
+
+	if(pipe(p) < 0)
+		sysfatal("pipe: %r");
+
+	rv = p[0];
+	out = p[1];
+	switch(rfork(RFPROC|RFFDG|RFNOTEG|RFMEM)){
+	case -1:
+		sysfatal("fork: %r");
+	case 0:
+		close(rv);
+		break;
+	default:
+		close(in);
+		close(out);
+		return rv;
+	}
+
+	Binit(&bin, in, OREAD);
+	b = &bin;
+
+	if(Bread(b, buf, 4) != 4)
+		sysfatal("short read");
+
+	if(memcmp(buf, "BLZ\n", 4) != 0)
+		sysfatal("bad header");
+
+	length = Bgetint(b);
+	data = malloc(length);
+	if(data == nil)
+		sysfatal("out of memory");
+	sum = 0;
+	nblk = 0;
+	mblk = 0;
+	blk = nil;
+	while(sum < length){
+		if(nblk>=mblk){
+			mblk += 16384;
+			blk = realloc(blk, (mblk+1)*sizeof(blk[0]));
+			if(blk == nil)
+				sysfatal("out of memory");
+		}
+		n = Bgetint(b);
+		blk[nblk++] = n;
+		if(n&(1<<31))
+			n &= ~(1<<31);
+		else
+			blk[nblk++] = Bgetint(b);
+		sum += n;
+	}
+	if(sum != length)
+		sysfatal("bad compressed data %d %d", sum, length);
+	i = 0;
+	j = 0;
+	while(i < length){
+		assert(j < nblk);
+		n = blk[j++];
+		if(n&(1<<31)){
+			n &= ~(1<<31);
+			if((m=Bread(b, data+i, n)) != n)
+				sysfatal("short read %d %d", n, m);
+		}else{
+			o = blk[j++];
+			copy(data+i, data+o, n);
+		}
+		i += n;
+	}
+	write(out, data, length);
+	close(in);
+	close(out);
+	_exits(0);
+	return -1;
+}
--- /dev/null
+++ b/sys/src/cmd/bzfs/unbzip.c
@@ -1,0 +1,861 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "bzfs.h"
+
+/*
+ * THIS FILE IS NOT IDENTICAL TO THE ORIGINAL
+ * FROM THE BZIP2 DISTRIBUTION.
+ *
+ * It has been modified, mainly to break the library
+ * into smaller pieces.
+ *
+ * Russ Cox
+ * [email protected]
+ * July 2000
+ */
+
+/*---------------------------------------------*/
+/*--
+  Place a 1 beside your platform, and 0 elsewhere.
+  Attempts to autosniff this even if you don't.
+--*/
+
+
+/*--
+  Plan 9 from Bell Labs
+--*/
+#define BZ_PLAN9     1
+#define BZ_UNIX 0
+
+#define exit(x) exits((x) ? "whoops" : nil)
+#define size_t ulong
+
+#ifdef __GNUC__
+#   define NORETURN __attribute__ ((noreturn))
+#else
+#   define NORETURN /**/
+#endif
+
+/*--
+  Some more stuff for all platforms :-)
+  This might have to get moved into the platform-specific
+  header files if we encounter a machine with different sizes.
+--*/
+
+typedef char            Char;
+typedef unsigned char   Bool;
+typedef unsigned char   UChar;
+typedef int             Int32;
+typedef unsigned int    UInt32;
+typedef short           Int16;
+typedef unsigned short  UInt16;
+                                       
+#define True  ((Bool)1)
+#define False ((Bool)0)
+
+/*--
+  IntNative is your platform's `native' int size.
+  Only here to avoid probs with 64-bit platforms.
+--*/
+typedef int IntNative;
+
+#include "bzfs.h"
+#include "bzlib.h"
+#include "bzlib_private.h"
+
+static int
+bunzip(int ofd, char *ofile, Biobuf *bin)
+{
+	int e, n, done, onemore;
+	char buf[8192];
+	char obuf[8192];
+	Biobuf bout;
+	bz_stream strm;
+
+	USED(ofile);
+
+	memset(&strm, 0, sizeof strm);
+	BZ2_bzDecompressInit(&strm, 0, 0);
+
+	strm.next_in = buf;
+	strm.avail_in = 0;
+	strm.next_out = obuf;
+	strm.avail_out = sizeof obuf;
+
+	done = 0;
+	Binit(&bout, ofd, OWRITE);
+
+	/*
+	 * onemore is a crummy hack to go 'round the loop
+	 * once after we finish, to flush the output buffer.
+	 */
+	onemore = 1;
+	SET(e);
+	do {
+		if(!done && strm.avail_in < sizeof buf) {
+			if(strm.avail_in)
+				memmove(buf, strm.next_in, strm.avail_in);
+			
+			n = Bread(bin, buf+strm.avail_in, sizeof(buf)-strm.avail_in);
+			if(n <= 0)
+				done = 1;
+			else
+				strm.avail_in += n;
+			strm.next_in = buf;
+		}
+		if(strm.avail_out < sizeof obuf) {
+			Bwrite(&bout, obuf, sizeof(obuf)-strm.avail_out);
+			strm.next_out = obuf;
+			strm.avail_out = sizeof obuf;
+		}
+
+		if(onemore == 0)
+			break;
+	} while((e=BZ2_bzDecompress(&strm)) == BZ_OK || onemore--);
+
+	if(e != BZ_STREAM_END) {
+		fprint(2, "bunzip2: decompress failed\n");
+		return 0;
+	}
+
+	if(BZ2_bzDecompressEnd(&strm) != BZ_OK) {
+		fprint(2, "bunzip2: decompress end failed (can't happen)\n");
+		return 0;
+	}
+
+	Bterm(&bout);
+
+	return 1;
+}
+
+void
+_unbzip(int in, int out)
+{
+	Biobuf bin;
+
+	Binit(&bin, in, OREAD);
+	if(bunzip(out, nil, &bin) != 1) {
+		fprint(2, "bunzip2 failed\n");
+		_exits("bunzip2");
+	}
+}
+
+int
+unbzip(int in)
+{
+	int rv, out, p[2];
+
+	if(pipe(p) < 0)
+		sysfatal("pipe: %r");
+
+	rv = p[0];
+	out = p[1];
+	switch(rfork(RFPROC|RFFDG|RFNOTEG|RFMEM)){
+	case -1:
+		sysfatal("fork: %r");
+	case 0:
+		close(rv);
+		break;
+	default:
+		close(in);
+		close(out);
+		return rv;
+	}
+
+	_unbzip(in, out);
+	_exits(0);
+	return -1;	/* not reached */
+}
+
+int bz_config_ok ( void )
+{
+   if (sizeof(int)   != 4) return 0;
+   if (sizeof(short) != 2) return 0;
+   if (sizeof(char)  != 1) return 0;
+   return 1;
+}
+
+void* default_bzalloc(void *o, int items, int size)
+{
+	USED(o);
+	return sbrk(items*size);
+}
+
+void default_bzfree(void*, void*)
+{
+}
+
+void
+bz_internal_error(int)
+{
+	abort();
+}
+
+/*-------------------------------------------------------------*/
+/*--- Decompression machinery                               ---*/
+/*---                                          decompress.c ---*/
+/*-------------------------------------------------------------*/
+
+/*--
+  This file is a part of bzip2 and/or libbzip2, a program and
+  library for lossless, block-sorting data compression.
+
+  Copyright (C) 1996-2000 Julian R Seward.  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+
+  2. The origin of this software must not be misrepresented; you must 
+     not claim that you wrote the original software.  If you use this 
+     software in a product, an acknowledgment in the product 
+     documentation would be appreciated but is not required.
+
+  3. Altered source versions must be plainly marked as such, and must
+     not be misrepresented as being the original software.
+
+  4. The name of the author may not be used to endorse or promote 
+     products derived from this software without specific prior written 
+     permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  Julian Seward, Cambridge, UK.
+  [email protected]
+  bzip2/libbzip2 version 1.0 of 21 March 2000
+
+  This program is based on (at least) the work of:
+     Mike Burrows
+     David Wheeler
+     Peter Fenwick
+     Alistair Moffat
+     Radford Neal
+     Ian H. Witten
+     Robert Sedgewick
+     Jon L. Bentley
+
+  For more information on these sources, see the manual.
+--*/
+
+
+
+/*---------------------------------------------------*/
+static
+void makeMaps_d ( DState* s )
+{
+   Int32 i;
+   s->nInUse = 0;
+   for (i = 0; i < 256; i++)
+      if (s->inUse[i]) {
+         s->seqToUnseq[s->nInUse] = i;
+         s->nInUse++;
+      }
+}
+
+
+/*---------------------------------------------------*/
+#define RETURN(rrr)                               \
+   { retVal = rrr; goto save_state_and_return; };
+
+#define GET_BITS(lll,vvv,nnn)                     \
+	case lll: \
+		{ int x; if((retVal = getbits(s, lll, &x, nnn)) != 99) \
+			goto save_state_and_return; vvv=x; }\
+
+int
+getbits(DState *s, int lll, int *vvv, int nnn)
+{
+	s->state = lll;
+	
+	for(;;) {
+		if (s->bsLive >= nnn) {
+			UInt32 v;
+			v = (s->bsBuff >>
+				 (s->bsLive-nnn)) & ((1 << nnn)-1);
+			s->bsLive -= nnn;
+			*vvv = v;
+			return 99;
+		}
+		if (s->strm->avail_in == 0) return BZ_OK;
+		s->bsBuff
+			= (s->bsBuff << 8) |
+			  ((UInt32)
+				  (*((UChar*)(s->strm->next_in))));
+		s->bsLive += 8;
+		s->strm->next_in++;
+		s->strm->avail_in--;
+		s->strm->total_in_lo32++;
+		if (s->strm->total_in_lo32 == 0)
+			s->strm->total_in_hi32++;
+	}
+	return -1;	/* KEN */
+}
+
+#define GET_UCHAR(lll,uuu)                        \
+   GET_BITS(lll,uuu,8)
+
+#define GET_BIT(lll,uuu)                          \
+   GET_BITS(lll,uuu,1)
+
+/*---------------------------------------------------*/
+#define GET_MTF_VAL(label1,label2,lval)           \
+{                                                 \
+   if (groupPos == 0) {                           \
+      groupNo++;                                  \
+      if (groupNo >= nSelectors)                  \
+         RETURN(BZ_DATA_ERROR);                   \
+      groupPos = BZ_G_SIZE;                       \
+      gSel = s->selector[groupNo];                \
+      gMinlen = s->minLens[gSel];                 \
+      gLimit = &(s->limit[gSel][0]);              \
+      gPerm = &(s->perm[gSel][0]);                \
+      gBase = &(s->base[gSel][0]);                \
+   }                                              \
+   groupPos--;                                    \
+   zn = gMinlen;                                  \
+   GET_BITS(label1, zvec, zn);                    \
+   while (1) {                                    \
+      if (zn > 20 /* the longest code */)         \
+         RETURN(BZ_DATA_ERROR);                   \
+      if (zvec <= gLimit[zn]) break;              \
+      zn++;                                       \
+      GET_BIT(label2, zj);                        \
+      zvec = (zvec << 1) | zj;                    \
+   };                                             \
+   if (zvec - gBase[zn] < 0                       \
+       || zvec - gBase[zn] >= BZ_MAX_ALPHA_SIZE)  \
+      RETURN(BZ_DATA_ERROR);                      \
+   lval = gPerm[zvec - gBase[zn]];                \
+}
+
+
+/*---------------------------------------------------*/
+Int32 BZ2_decompress ( DState* s )
+{
+   UChar      uc;
+   Int32      retVal;
+   Int32      minLen, maxLen;
+   bz_stream* strm = s->strm;
+
+   /* stuff that needs to be saved/restored */
+   Int32  i;
+   Int32  j;
+   Int32  t;
+   Int32  alphaSize;
+   Int32  nGroups;
+   Int32  nSelectors;
+   Int32  EOB;
+   Int32  groupNo;
+   Int32  groupPos;
+   Int32  nextSym;
+   Int32  nblockMAX;
+   Int32  nblock;
+   Int32  es;
+   Int32  N;
+   Int32  curr;
+   Int32  zt;
+   Int32  zn; 
+   Int32  zvec;
+   Int32  zj;
+   Int32  gSel;
+   Int32  gMinlen;
+   Int32* gLimit;
+   Int32* gBase;
+   Int32* gPerm;
+
+   if (s->state == BZ_X_MAGIC_1) {
+      /*initialise the save area*/
+      s->save_i           = 0;
+      s->save_j           = 0;
+      s->save_t           = 0;
+      s->save_alphaSize   = 0;
+      s->save_nGroups     = 0;
+      s->save_nSelectors  = 0;
+      s->save_EOB         = 0;
+      s->save_groupNo     = 0;
+      s->save_groupPos    = 0;
+      s->save_nextSym     = 0;
+      s->save_nblockMAX   = 0;
+      s->save_nblock      = 0;
+      s->save_es          = 0;
+      s->save_N           = 0;
+      s->save_curr        = 0;
+      s->save_zt          = 0;
+      s->save_zn          = 0;
+      s->save_zvec        = 0;
+      s->save_zj          = 0;
+      s->save_gSel        = 0;
+      s->save_gMinlen     = 0;
+      s->save_gLimit      = NULL;
+      s->save_gBase       = NULL;
+      s->save_gPerm       = NULL;
+   }
+
+   /*restore from the save area*/
+   i           = s->save_i;
+   j           = s->save_j;
+   t           = s->save_t;
+   alphaSize   = s->save_alphaSize;
+   nGroups     = s->save_nGroups;
+   nSelectors  = s->save_nSelectors;
+   EOB         = s->save_EOB;
+   groupNo     = s->save_groupNo;
+   groupPos    = s->save_groupPos;
+   nextSym     = s->save_nextSym;
+   nblockMAX   = s->save_nblockMAX;
+   nblock      = s->save_nblock;
+   es          = s->save_es;
+   N           = s->save_N;
+   curr        = s->save_curr;
+   zt          = s->save_zt;
+   zn          = s->save_zn; 
+   zvec        = s->save_zvec;
+   zj          = s->save_zj;
+   gSel        = s->save_gSel;
+   gMinlen     = s->save_gMinlen;
+   gLimit      = s->save_gLimit;
+   gBase       = s->save_gBase;
+   gPerm       = s->save_gPerm;
+
+   retVal = BZ_OK;
+
+   switch (s->state) {
+
+      GET_UCHAR(BZ_X_MAGIC_1, uc);
+      if (uc != 'B') RETURN(BZ_DATA_ERROR_MAGIC);
+
+      GET_UCHAR(BZ_X_MAGIC_2, uc);
+      if (uc != 'Z') RETURN(BZ_DATA_ERROR_MAGIC);
+
+      GET_UCHAR(BZ_X_MAGIC_3, uc)
+      if (uc != 'h') RETURN(BZ_DATA_ERROR_MAGIC);
+
+      GET_BITS(BZ_X_MAGIC_4, s->blockSize100k, 8)
+      if (s->blockSize100k < '1' || 
+          s->blockSize100k > '9') RETURN(BZ_DATA_ERROR_MAGIC);
+      s->blockSize100k -= '0';
+
+      if (0 && s->smallDecompress) {
+         s->ll16 = BZALLOC( s->blockSize100k * 100000 * sizeof(UInt16) );
+         s->ll4  = BZALLOC( 
+                      ((1 + s->blockSize100k * 100000) >> 1) * sizeof(UChar) 
+                   );
+         if (s->ll16 == NULL || s->ll4 == NULL) RETURN(BZ_MEM_ERROR);
+      } else {
+         s->tt  = BZALLOC( s->blockSize100k * 100000 * sizeof(Int32) );
+         if (s->tt == NULL) RETURN(BZ_MEM_ERROR);
+      }
+
+      GET_UCHAR(BZ_X_BLKHDR_1, uc);
+
+      if (uc == 0x17) goto endhdr_2;
+      if (uc != 0x31) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_BLKHDR_2, uc);
+      if (uc != 0x41) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_BLKHDR_3, uc);
+      if (uc != 0x59) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_BLKHDR_4, uc);
+      if (uc != 0x26) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_BLKHDR_5, uc);
+      if (uc != 0x53) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_BLKHDR_6, uc);
+      if (uc != 0x59) RETURN(BZ_DATA_ERROR);
+
+      s->currBlockNo++;
+    //  if (s->verbosity >= 2)
+    //     VPrintf1 ( "\n    [%d: huff+mtf ", s->currBlockNo );
+ 
+      s->storedBlockCRC = 0;
+      GET_UCHAR(BZ_X_BCRC_1, uc);
+      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
+      GET_UCHAR(BZ_X_BCRC_2, uc);
+      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
+      GET_UCHAR(BZ_X_BCRC_3, uc);
+      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
+      GET_UCHAR(BZ_X_BCRC_4, uc);
+      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
+
+      GET_BITS(BZ_X_RANDBIT, s->blockRandomised, 1);
+
+      s->origPtr = 0;
+      GET_UCHAR(BZ_X_ORIGPTR_1, uc);
+      s->origPtr = (s->origPtr << 8) | ((Int32)uc);
+      GET_UCHAR(BZ_X_ORIGPTR_2, uc);
+      s->origPtr = (s->origPtr << 8) | ((Int32)uc);
+      GET_UCHAR(BZ_X_ORIGPTR_3, uc);
+      s->origPtr = (s->origPtr << 8) | ((Int32)uc);
+
+      if (s->origPtr < 0)
+         RETURN(BZ_DATA_ERROR);
+      if (s->origPtr > 10 + 100000*s->blockSize100k) 
+         RETURN(BZ_DATA_ERROR);
+
+      /*--- Receive the mapping table ---*/
+      for (i = 0; i < 16; i++) {
+         GET_BIT(BZ_X_MAPPING_1, uc);
+         if (uc == 1) 
+            s->inUse16[i] = True; else 
+            s->inUse16[i] = False;
+      }
+
+      for (i = 0; i < 256; i++) s->inUse[i] = False;
+
+      for (i = 0; i < 16; i++)
+         if (s->inUse16[i])
+            for (j = 0; j < 16; j++) {
+               GET_BIT(BZ_X_MAPPING_2, uc);
+               if (uc == 1) s->inUse[i * 16 + j] = True;
+            }
+      makeMaps_d ( s );
+      if (s->nInUse == 0) RETURN(BZ_DATA_ERROR);
+      alphaSize = s->nInUse+2;
+
+      /*--- Now the selectors ---*/
+      GET_BITS(BZ_X_SELECTOR_1, nGroups, 3);
+      if (nGroups < 2 || nGroups > 6) RETURN(BZ_DATA_ERROR);
+      GET_BITS(BZ_X_SELECTOR_2, nSelectors, 15);
+      if (nSelectors < 1) RETURN(BZ_DATA_ERROR);
+      for (i = 0; i < nSelectors; i++) {
+         j = 0;
+         while (True) {
+            GET_BIT(BZ_X_SELECTOR_3, uc);
+            if (uc == 0) break;
+            j++;
+            if (j >= nGroups) RETURN(BZ_DATA_ERROR);
+         }
+         s->selectorMtf[i] = j;
+      }
+
+      /*--- Undo the MTF values for the selectors. ---*/
+      {
+         UChar pos[BZ_N_GROUPS], tmp, v;
+         for (v = 0; v < nGroups; v++) pos[v] = v;
+   
+         for (i = 0; i < nSelectors; i++) {
+            v = s->selectorMtf[i];
+            tmp = pos[v];
+            while (v > 0) { pos[v] = pos[v-1]; v--; }
+            pos[0] = tmp;
+            s->selector[i] = tmp;
+         }
+      }
+
+      /*--- Now the coding tables ---*/
+      for (t = 0; t < nGroups; t++) {
+         GET_BITS(BZ_X_CODING_1, curr, 5);
+         for (i = 0; i < alphaSize; i++) {
+            while (True) {
+               if (curr < 1 || curr > 20) RETURN(BZ_DATA_ERROR);
+               GET_BIT(BZ_X_CODING_2, uc);
+               if (uc == 0) break;
+               GET_BIT(BZ_X_CODING_3, uc);
+               if (uc == 0) curr++; else curr--;
+            }
+            s->len[t][i] = curr;
+         }
+      }
+
+      /*--- Create the Huffman decoding tables ---*/
+      for (t = 0; t < nGroups; t++) {
+         minLen = 32;
+         maxLen = 0;
+         for (i = 0; i < alphaSize; i++) {
+            if (s->len[t][i] > maxLen) maxLen = s->len[t][i];
+            if (s->len[t][i] < minLen) minLen = s->len[t][i];
+         }
+         BZ2_hbCreateDecodeTables ( 
+            &(s->limit[t][0]), 
+            &(s->base[t][0]), 
+            &(s->perm[t][0]), 
+            &(s->len[t][0]),
+            minLen, maxLen, alphaSize
+         );
+         s->minLens[t] = minLen;
+      }
+
+      /*--- Now the MTF values ---*/
+
+      EOB      = s->nInUse+1;
+      nblockMAX = 100000 * s->blockSize100k;
+      groupNo  = -1;
+      groupPos = 0;
+
+      for (i = 0; i <= 255; i++) s->unzftab[i] = 0;
+
+      /*-- MTF init --*/
+      {
+         Int32 ii, jj, kk;
+         kk = MTFA_SIZE-1;
+         for (ii = 256 / MTFL_SIZE - 1; ii >= 0; ii--) {
+            for (jj = MTFL_SIZE-1; jj >= 0; jj--) {
+               s->mtfa[kk] = (UChar)(ii * MTFL_SIZE + jj);
+               kk--;
+            }
+            s->mtfbase[ii] = kk + 1;
+         }
+      }
+      /*-- end MTF init --*/
+
+      nblock = 0;
+      GET_MTF_VAL(BZ_X_MTF_1, BZ_X_MTF_2, nextSym);
+
+      while (True) {
+
+         if (nextSym == EOB) break;
+
+         if (nextSym == BZ_RUNA || nextSym == BZ_RUNB) {
+
+            es = -1;
+            N = 1;
+            do {
+               if (nextSym == BZ_RUNA) es = es + (0+1) * N; else
+               if (nextSym == BZ_RUNB) es = es + (1+1) * N;
+               N = N * 2;
+               GET_MTF_VAL(BZ_X_MTF_3, BZ_X_MTF_4, nextSym);
+            }
+               while (nextSym == BZ_RUNA || nextSym == BZ_RUNB);
+
+            es++;
+            uc = s->seqToUnseq[ s->mtfa[s->mtfbase[0]] ];
+            s->unzftab[uc] += es;
+
+            if (0 && s->smallDecompress)
+               while (es > 0) {
+                  if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
+                  s->ll16[nblock] = (UInt16)uc;
+                  nblock++;
+                  es--;
+               }
+            else
+               while (es > 0) {
+                  if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
+                  s->tt[nblock] = (UInt32)uc;
+                  nblock++;
+                  es--;
+               };
+
+            continue;
+
+         } else {
+
+            if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
+
+            /*-- uc = MTF ( nextSym-1 ) --*/
+            {
+               Int32 ii, jj, kk, pp, lno, off;
+               UInt32 nn;
+               nn = (UInt32)(nextSym - 1);
+
+               if (nn < MTFL_SIZE) {
+                  /* avoid general-case expense */
+                  pp = s->mtfbase[0];
+                  uc = s->mtfa[pp+nn];
+                  while (nn > 3) {
+                     Int32 z = pp+nn;
+                     s->mtfa[(z)  ] = s->mtfa[(z)-1];
+                     s->mtfa[(z)-1] = s->mtfa[(z)-2];
+                     s->mtfa[(z)-2] = s->mtfa[(z)-3];
+                     s->mtfa[(z)-3] = s->mtfa[(z)-4];
+                     nn -= 4;
+                  }
+                  while (nn > 0) { 
+                     s->mtfa[(pp+nn)] = s->mtfa[(pp+nn)-1]; nn--; 
+                  };
+                  s->mtfa[pp] = uc;
+               } else { 
+                  /* general case */
+                  lno = nn / MTFL_SIZE;
+                  off = nn % MTFL_SIZE;
+                  pp = s->mtfbase[lno] + off;
+                  uc = s->mtfa[pp];
+                  while (pp > s->mtfbase[lno]) { 
+                     s->mtfa[pp] = s->mtfa[pp-1]; pp--; 
+                  };
+                  s->mtfbase[lno]++;
+                  while (lno > 0) {
+                     s->mtfbase[lno]--;
+                     s->mtfa[s->mtfbase[lno]] 
+                        = s->mtfa[s->mtfbase[lno-1] + MTFL_SIZE - 1];
+                     lno--;
+                  }
+                  s->mtfbase[0]--;
+                  s->mtfa[s->mtfbase[0]] = uc;
+                  if (s->mtfbase[0] == 0) {
+                     kk = MTFA_SIZE-1;
+                     for (ii = 256 / MTFL_SIZE-1; ii >= 0; ii--) {
+                        for (jj = MTFL_SIZE-1; jj >= 0; jj--) {
+                           s->mtfa[kk] = s->mtfa[s->mtfbase[ii] + jj];
+                           kk--;
+                        }
+                        s->mtfbase[ii] = kk + 1;
+                     }
+                  }
+               }
+            }
+            /*-- end uc = MTF ( nextSym-1 ) --*/
+
+            s->unzftab[s->seqToUnseq[uc]]++;
+            if (0 && s->smallDecompress)
+               s->ll16[nblock] = (UInt16)(s->seqToUnseq[uc]); else
+               s->tt[nblock]   = (UInt32)(s->seqToUnseq[uc]);
+            nblock++;
+
+            GET_MTF_VAL(BZ_X_MTF_5, BZ_X_MTF_6, nextSym);
+            continue;
+         }
+      }
+
+      /* Now we know what nblock is, we can do a better sanity
+         check on s->origPtr.
+      */
+      if (s->origPtr < 0 || s->origPtr >= nblock)
+         RETURN(BZ_DATA_ERROR);
+
+      s->state_out_len = 0;
+      s->state_out_ch  = 0;
+      BZ_INITIALISE_CRC ( s->calculatedBlockCRC );
+      s->state = BZ_X_OUTPUT;
+    //  if (s->verbosity >= 2) VPrintf0 ( "rt+rld" );
+
+      /*-- Set up cftab to facilitate generation of T^(-1) --*/
+      s->cftab[0] = 0;
+      for (i = 1; i <= 256; i++) s->cftab[i] = s->unzftab[i-1];
+      for (i = 1; i <= 256; i++) s->cftab[i] += s->cftab[i-1];
+
+      if (0 && s->smallDecompress) {
+
+         /*-- Make a copy of cftab, used in generation of T --*/
+         for (i = 0; i <= 256; i++) s->cftabCopy[i] = s->cftab[i];
+
+         /*-- compute the T vector --*/
+         for (i = 0; i < nblock; i++) {
+            uc = (UChar)(s->ll16[i]);
+            SET_LL(i, s->cftabCopy[uc]);
+            s->cftabCopy[uc]++;
+         }
+
+         /*-- Compute T^(-1) by pointer reversal on T --*/
+         i = s->origPtr;
+         j = GET_LL(i);
+         do {
+            Int32 tmp = GET_LL(j);
+            SET_LL(j, i);
+            i = j;
+            j = tmp;
+         }
+            while (i != s->origPtr);
+
+         s->tPos = s->origPtr;
+         s->nblock_used = 0;
+         if (s->blockRandomised) {
+            BZ_RAND_INIT_MASK;
+            BZ_GET_SMALL(s->k0); s->nblock_used++;
+            BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; 
+         } else {
+            BZ_GET_SMALL(s->k0); s->nblock_used++;
+         }
+
+      } else {
+
+         /*-- compute the T^(-1) vector --*/
+         for (i = 0; i < nblock; i++) {
+            uc = (UChar)(s->tt[i] & 0xff);
+            s->tt[s->cftab[uc]] |= (i << 8);
+            s->cftab[uc]++;
+         }
+
+         s->tPos = s->tt[s->origPtr] >> 8;
+         s->nblock_used = 0;
+         if (s->blockRandomised) {
+            BZ_RAND_INIT_MASK;
+            BZ_GET_FAST(s->k0); s->nblock_used++;
+            BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; 
+         } else {
+            BZ_GET_FAST(s->k0); s->nblock_used++;
+         }
+
+      }
+
+      RETURN(BZ_OK);
+
+
+
+    endhdr_2:
+
+      GET_UCHAR(BZ_X_ENDHDR_2, uc);
+      if (uc != 0x72) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_ENDHDR_3, uc);
+      if (uc != 0x45) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_ENDHDR_4, uc);
+      if (uc != 0x38) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_ENDHDR_5, uc);
+      if (uc != 0x50) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_ENDHDR_6, uc);
+      if (uc != 0x90) RETURN(BZ_DATA_ERROR);
+
+      s->storedCombinedCRC = 0;
+      GET_UCHAR(BZ_X_CCRC_1, uc);
+      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
+      GET_UCHAR(BZ_X_CCRC_2, uc);
+      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
+      GET_UCHAR(BZ_X_CCRC_3, uc);
+      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
+      GET_UCHAR(BZ_X_CCRC_4, uc);
+      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
+
+      s->state = BZ_X_IDLE;
+      RETURN(BZ_STREAM_END);
+
+      default: AssertH ( False, 4001 );
+   }
+
+   AssertH ( False, 4002 );
+
+   save_state_and_return:
+
+   s->save_i           = i;
+   s->save_j           = j;
+   s->save_t           = t;
+   s->save_alphaSize   = alphaSize;
+   s->save_nGroups     = nGroups;
+   s->save_nSelectors  = nSelectors;
+   s->save_EOB         = EOB;
+   s->save_groupNo     = groupNo;
+   s->save_groupPos    = groupPos;
+   s->save_nextSym     = nextSym;
+   s->save_nblockMAX   = nblockMAX;
+   s->save_nblock      = nblock;
+   s->save_es          = es;
+   s->save_N           = N;
+   s->save_curr        = curr;
+   s->save_zt          = zt;
+   s->save_zn          = zn;
+   s->save_zvec        = zvec;
+   s->save_zj          = zj;
+   s->save_gSel        = gSel;
+   s->save_gMinlen     = gMinlen;
+   s->save_gLimit      = gLimit;
+   s->save_gBase       = gBase;
+   s->save_gPerm       = gPerm;
+
+   return retVal;   
+}
+
+
+/*-------------------------------------------------------------*/
+/*--- end                                      decompress.c ---*/
+/*-------------------------------------------------------------*/
--- /dev/null
+++ b/sys/src/cmd/touchfs.c
@@ -1,0 +1,66 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+void
+Bpass(Biobuf *bin, Biobuf *bout, int n)
+{
+	char buf[8192];
+	int m;
+
+	while(n > 0) {
+		m = sizeof buf;
+		if(m > n)
+			m = n;
+		m = Bread(bin, buf, m);
+		if(m <= 0) {
+			fprint(2, "corrupt archive\n");
+			exits("notdone");
+		}
+		Bwrite(bout, buf, m);
+		n -= m;
+	}
+	assert(n == 0);
+}
+
+void
+main(int argc, char **argv)
+{
+	char *p, *f[10];
+	Biobuf bin, bout;
+	int nf;
+	ulong d, size;
+
+	if(argc != 2) {
+		fprint(2, "usage: cat mkfs-archive | touchfs date (in seconds)\n");
+		exits("usage");
+	}
+
+	d = strtoul(argv[1], 0, 0);
+
+	quotefmtinstall();
+	Binit(&bin, 0, OREAD);
+	Binit(&bout, 1, OWRITE);
+
+	while(p = Brdline(&bin, '\n')) {
+		p[Blinelen(&bin)-1] = '\0';
+		if(strcmp(p, "end of archive") == 0) {
+			Bprint(&bout, "end of archive\n");
+			exits(0);
+		}
+
+		nf = tokenize(p, f, nelem(f));
+		if(nf != 6) {
+			fprint(2, "corrupt archive\n");
+			exits("notdone");
+		}
+
+		Bprint(&bout, "%q %q %q %q %lud %q\n",
+			f[0], f[1], f[2], f[3], d, f[5]);
+
+		size = strtoul(f[5], 0, 0);
+		Bpass(&bin, &bout, size);
+	}
+	fprint(2, "premature end of archive\n");
+	exits("notdone");
+}