Add support for user-level 9P servers/clients and various bug fixes to go with them.
diff --git a/src/cmd/9p.c b/src/cmd/9p.c
index 7c018c3..e948174 100644
--- a/src/cmd/9p.c
+++ b/src/cmd/9p.c
@@ -11,7 +11,9 @@
 	fprint(2, "usage: 9p [-a address] cmd args...\n");
 	fprint(2, "possible cmds:\n");
 	fprint(2, "	read name\n");
+	fprint(2, "	readfd name\n");
 	fprint(2, "	write name\n");
+	fprint(2, "	writefd name\n");
 	fprint(2, "	stat name\n");
 //	fprint(2, "	ls name\n");
 	fprint(2, "without -a, name elem/path means /path on server unix!$ns/elem\n");
@@ -20,6 +22,8 @@
 
 void xread(int, char**);
 void xwrite(int, char**);
+void xreadfd(int, char**);
+void xwritefd(int, char**);
 void xstat(int, char**);
 void xls(int, char**);
 
@@ -29,6 +33,8 @@
 } cmds[] = {
 	"read", xread,
 	"write", xwrite,
+	"readfd", xreadfd,
+	"writefd", xwritefd,
 	"stat", xstat,
 //	"ls", xls,
 };
@@ -64,7 +70,6 @@
 xparse(char *name, char **path)
 {
 	int fd;
-	char *ns;
 	char *p;
 	Fsys *fs;
 
@@ -75,22 +80,17 @@
 		else
 			*p++ = 0;
 		*path = p;
-		if(*name == 0)
-			usage();
-		ns = getenv("ns");
-		if(ns == nil)
-			sysfatal("ns not set");
-		addr = smprint("unix!%s/%s", ns, name);
-		if(addr == nil)
-			sysfatal("out of memory");
-	}else
+		fs = nsmount(name, "");
+		if(fs == nil)
+			sysfatal("mount: %r");
+	}else{
 		*path = name;
-
-	fprint(2, "dial %s...", addr);
-	if((fd = dial(addr, nil, nil, nil)) < 0)
-		sysfatal("dial: %r");
-	if((fs = fsmount(fd)) == nil)
-		sysfatal("fsmount: %r");
+		fprint(2, "dial %s...", addr);
+		if((fd = dial(addr, nil, nil, nil)) < 0)
+			sysfatal("dial: %r");
+		if((fs = fsmount(fd, "")) == nil)
+			sysfatal("fsmount: %r");
+	}
 	return fs;
 }
 
@@ -120,6 +120,15 @@
 	return fid;
 }
 
+int
+xopenfd(char *name, int mode)
+{
+	Fsys *fs;
+
+	fs = xparse(name, &name);
+	return fsopenfd(fs, name, mode);
+}
+
 void
 xread(int argc, char **argv)
 {
@@ -144,6 +153,29 @@
 }
 
 void
+xreadfd(int argc, char **argv)
+{
+	char buf[1024];
+	int n;
+	int fd;
+
+	ARGBEGIN{
+	default:
+		usage();
+	}ARGEND
+
+	if(argc != 1)
+		usage();
+
+	fd = xopenfd(argv[0], OREAD);
+	while((n = read(fd, buf, sizeof buf)) > 0)
+		write(1, buf, n);
+	if(n < 0)
+		sysfatal("read error: %r");
+	exits(0);	
+}
+
+void
 xwrite(int argc, char **argv)
 {
 	char buf[1024];
@@ -168,6 +200,30 @@
 }
 
 void
+xwritefd(int argc, char **argv)
+{
+	char buf[1024];
+	int n;
+	int fd;
+
+	ARGBEGIN{
+	default:
+		usage();
+	}ARGEND
+
+	if(argc != 1)
+		usage();
+
+	fd = xopenfd(argv[0], OWRITE|OTRUNC);
+	while((n = read(0, buf, sizeof buf)) > 0)
+		if(write(fd, buf, n) != n)
+			sysfatal("write error: %r");
+	if(n < 0)
+		sysfatal("read error: %r");
+	exits(0);	
+}
+
+void
 xstat(int argc, char **argv)
 {
 	Dir *d;
diff --git a/src/cmd/9pserve.c b/src/cmd/9pserve.c
index ad77236..c33beb1 100644
--- a/src/cmd/9pserve.c
+++ b/src/cmd/9pserve.c
@@ -2,6 +2,8 @@
 #include <libc.h>
 #include <fcall.h>
 #include <thread.h>
+#include <poll.h>
+#include <errno.h>
 
 enum
 {
@@ -38,6 +40,7 @@
 	int ref;
 	int ctag;
 	int tag;
+	int isopenfd;
 	Fcall tx;
 	Fcall rx;
 	Fid *fid;
@@ -52,6 +55,8 @@
 struct Conn
 {
 	int fd;
+	int fdmode;
+	Fid *fdfid;
 	int nmsg;
 	int nfid;
 	Channel *inc;
@@ -89,7 +94,7 @@
 Queue *qalloc(void);
 int sendq(Queue*, void*);
 void *recvq(Queue*);
-void selectthread(void*);
+void pollthread(void*);
 void connthread(void*);
 void connoutthread(void*);
 void listenthread(void*);
@@ -100,6 +105,10 @@
 int taccept(int, char*);
 int iolisten(Ioproc*, char*, char*);
 int ioaccept(Ioproc*, int, char*);
+int iorecvfd(Ioproc*, int);
+int iosendfd(Ioproc*, int, int);
+void mainproc(void*);
+int ignorepipe(void*, char*);
 
 void
 usage(void)
@@ -110,14 +119,13 @@
 }
 
 uchar vbuf[128];
-
+extern int _threaddebuglevel;
 void
 threadmain(int argc, char **argv)
 {
 	char *file;
-	int n;
-	Fcall f;
 
+	if(verbose) fprint(2, "9pserve running\n");
 	ARGBEGIN{
 	default:
 		usage();
@@ -142,6 +150,20 @@
 	if((afd = announce(addr, adir)) < 0)
 		sysfatal("announce %s: %r", addr);
 
+	proccreate(mainproc, nil, STACK);
+	threadexits(0);
+}
+
+void
+mainproc(void *v)
+{
+	int n;
+	Fcall f;
+	USED(v);
+
+	yield();	/* let threadmain exit */
+
+	atnotify(ignorepipe, 1);
 	fmtinstall('D', dirfmt);
 	fmtinstall('M', dirmodefmt);
 	fmtinstall('F', fcallfmt);
@@ -150,10 +172,6 @@
 	outq = qalloc();
 	inq = qalloc();
 
-//	threadcreateidle(selectthread, nil, STACK);
-	threadcreate(inputthread, nil, STACK);
-	threadcreate(outputthread, nil, STACK);
-
 	f.type = Tversion;
 	f.version = "9P2000";
 	f.msize = 8192;
@@ -165,7 +183,22 @@
 	if(convM2S(vbuf, n, &f) != n)
 		sysfatal("convM2S failure");
 	if(verbose > 1) fprint(2, "* -> %F\n", &f);
+
+	threadcreate(inputthread, nil, STACK);
+	threadcreate(outputthread, nil, STACK);
 	threadcreate(listenthread, nil, STACK);
+	threadcreateidle(pollthread, nil, STACK);
+	threadexits(0);
+}
+
+int
+ignorepipe(void *v, char *s)
+{
+	USED(v);
+	if(strcmp(s, "sys: write on closed pipe") == 0)
+		return 1;
+	fprint(2, "msg: %s\n", s);
+	return 0;
 }
 
 void
@@ -178,10 +211,6 @@
 	USED(arg);
 	for(;;){
 		c = emalloc(sizeof(Conn));
-		c->inc = chancreate(sizeof(void*), 0);
-		c->internal = chancreate(sizeof(void*), 0);
-		c->inq = qalloc();
-		c->outq = qalloc();
 		c->fd = iolisten(io, adir, c->dir);
 		if(c->fd < 0){
 			if(verbose) fprint(2, "listen: %r\n");
@@ -189,13 +218,17 @@
 			free(c);
 			return;
 		}
+		c->inc = chancreate(sizeof(void*), 0);
+		c->internal = chancreate(sizeof(void*), 0);
+		c->inq = qalloc();
+		c->outq = qalloc();
 		if(verbose) fprint(2, "incoming call on %s\n", c->dir);
 		threadcreate(connthread, c, STACK);
 	}	
 }
 
 void
-sendmsg(Msg *m)
+send9pmsg(Msg *m)
 {
 	int n, nn;
 
@@ -226,7 +259,7 @@
 	m->rx.type = Rerror;
 	m->rx.ename = ename;
 	m->rx.tag = m->tx.tag;
-	sendmsg(m);
+	send9pmsg(m);
 }
 
 void
@@ -250,7 +283,7 @@
 	c->fd = fd;
 	threadcreate(connoutthread, c, STACK);
 	while((m = mread9p(io, c->fd)) != nil){
-		if(verbose > 1) fprint(2, "%s -> %F\n", c->dir, &m->tx);
+		if(verbose > 1) fprint(2, "fd#%d -> %F\n", c->fd, &m->tx);
 		m->c = c;
 		m->ctag = m->tx.tag;
 		c->nmsg++;
@@ -267,13 +300,13 @@
 				m->rx.msize = 8192;
 			m->rx.version = "9P2000";
 			m->rx.type = Rversion;
-			sendmsg(m);
+			send9pmsg(m);
 			continue;
 		case Tflush:
 			if((m->oldm = gethash(c->tag, m->tx.oldtag)) == nil){
 				m->rx.tag = m->tx.tag;
 				m->rx.type = Rflush;
-				sendmsg(m);
+				send9pmsg(m);
 				continue;
 			}
 			m->oldm->ref++;
@@ -318,6 +351,15 @@
 			}
 			m->afid->ref++;
 			break;
+		case Topenfd:
+			if(m->tx.mode != OREAD && (m->tx.mode&~OTRUNC) != OWRITE){
+				err(m, "openfd mode must be OREAD or OWRITE");
+				continue;
+			}
+			m->isopenfd = 1;
+			m->tx.type = Topen;
+			m->tpkt[4] = Topen;
+			/* fall through */
 		case Tcreate:
 		case Topen:
 		case Tclunk:
@@ -363,6 +405,7 @@
 			m = msgnew();
 			m->internal = 1;
 			m->c = c;
+			c->nmsg++;
 			m->tx.type = Tflush;
 			m->tx.tag = m->tag;
 			m->tx.oldtag = om->tag;
@@ -371,7 +414,9 @@
 			m->ref++;	/* for outq */
 			sendomsg(m);
 			recvp(c->internal);
-			msgput(m);
+			msgput(m);	/* got from recvp */
+			msgput(m);	/* got from msgnew */
+			msgput(om);	/* got from hash table */
 		}
 	}
 
@@ -382,6 +427,7 @@
 			m = msgnew();
 			m->internal = 1;
 			m->c = c;
+			c->nmsg++;
 			m->tx.type = Tclunk;
 			m->tx.tag = m->tag;
 			m->tx.fid = f->fid;
@@ -390,7 +436,9 @@
 			m->ref++;
 			sendomsg(m);
 			recvp(c->internal);
-			msgput(m);
+			msgput(m);	/* got from recvp */
+			msgput(m);	/* got from msgnew */
+			fidput(f);	/* got from hash table */
 		}
 	}
 
@@ -398,9 +446,157 @@
 	assert(c->nmsg == 0);
 	assert(c->nfid == 0);
 	close(c->fd);
+	chanfree(c->internal);
+	c->internal = 0;
+	chanfree(c->inc);
+	c->inc = 0;
+	free(c->inq);
+	c->inq = 0;
+	free(c->outq);
+	c->outq = 0;
 	free(c);
 }
 
+static void
+openfdthread(void *v)
+{
+	Conn *c;
+	Fid *fid;
+	Msg *m;
+	int n;
+	vlong tot;
+	Ioproc *io;
+	char buf[1024];
+
+	c = v;
+	fid = c->fdfid;
+	io = ioproc();
+
+	tot = 0;
+	if(c->fdmode == OREAD){
+		for(;;){
+			if(verbose) fprint(2, "tread...");
+			m = msgnew();
+			m->internal = 1;
+			m->c = c;
+			m->tx.type = Tread;
+			m->tx.count = 8192;
+			m->tx.fid = fid->fid;
+			m->tx.tag = m->tag;
+			m->tx.offset = tot;
+			m->fid = fid;
+			fid->ref++;
+			m->ref++;
+			sendomsg(m);
+			recvp(c->internal);
+			if(m->rx.type == Rerror)
+				break;
+			if(m->rx.count == 0)
+				break;
+			tot += m->rx.count;
+			if(iowrite(io, c->fd, m->rx.data, m->rx.count) != m->rx.count)
+				break;
+			msgput(m);
+			msgput(m);
+		}
+	}else{
+		for(;;){
+			if(verbose) fprint(2, "twrite...");
+			if((n=ioread(io, c->fd, buf, sizeof buf)) <= 0){
+				m = nil;
+				break;
+			}
+			m = msgnew();
+			m->internal = 1;
+			m->c = c;
+			m->tx.type = Twrite;
+			m->tx.fid = fid->fid;
+			m->tx.data = buf;
+			m->tx.count = n;
+			m->tx.tag = m->tag;
+			m->tx.offset = tot;
+			m->fid = fid;
+			fid->ref++;
+			m->ref++;
+			sendomsg(m);
+			recvp(c->internal);
+			if(m->rx.type == Rerror)
+				break;
+			tot = n;
+			msgput(m);
+			msgput(m);
+		}
+	}
+	if(verbose) fprint(2, "eof on %d fid %d\n", c->fd, fid->fid);
+	close(c->fd);
+	closeioproc(io);
+	if(m){
+		msgput(m);
+		msgput(m);
+	}
+	m = msgnew();
+	m->internal = 1;
+	m->c = c;
+	m->tx.type = Tclunk;
+	m->tx.fid = fid->fid;
+	m->fid = fid;
+	fid->ref++;
+	m->ref++;
+	sendomsg(m);
+	recvp(c->internal);
+	msgput(m);
+	msgput(m);
+	fidput(fid);
+	c->fdfid = nil;
+	chanfree(c->internal);
+	c->internal = 0;
+	free(c);
+}			
+
+int
+xopenfd(Msg *m)
+{
+	char errs[ERRMAX];
+	int n, p[2];
+	Conn *nc;
+
+	if(pipe(p) < 0){
+		rerrstr(errs, sizeof errs);
+		err(m, errs);
+	}
+	if(verbose) fprint(2, "xopen pipe %d %d...", p[0], p[1]);
+
+	/* now we're committed. */
+
+	/* a new connection for this fid */
+	nc = emalloc(sizeof(Conn));
+	nc->internal = chancreate(sizeof(void*), 0);
+
+	/* a ref for us */
+	nc->fdfid = m->fid;
+	m->fid->ref++;
+	nc->fdmode = m->tx.mode;
+	nc->fd = p[0];
+
+	/* clunk fid from other connection */
+	if(delhash(m->c->fid, m->fid->cfid, m->fid) == 0)
+		fidput(m->fid);
+
+	/* a thread to tend the pipe */
+	threadcreate(openfdthread, nc, STACK);
+
+	/* rewrite as Ropenfd */
+	m->rx.type = Ropenfd;
+	n = GBIT32(m->rpkt);
+	m->rpkt = erealloc(m->rpkt, n+4);
+	PBIT32(m->rpkt+n, p[1]);
+	n += 4;
+	PBIT32(m->rpkt, n);
+	m->rpkt[4] = Ropenfd;
+	m->rx.unixfd = p[1];
+	return 0;
+}
+
 void
 connoutthread(void *arg)
 {
@@ -413,6 +609,9 @@
 	io = ioproc();
 	while((m = recvq(c->outq)) != nil){
 		err = m->tx.type+1 != m->rx.type;
+		if(!err && m->isopenfd)
+			if(xopenfd(m) < 0)
+				continue;
 		switch(m->tx.type){
 		case Tflush:
 			om = m->oldm;
@@ -446,7 +645,7 @@
 		}
 		if(delhash(m->c->tag, m->ctag, m) == 0)
 			msgput(m);
-		if(verbose > 1) fprint(2, "%s <- %F\n", c->dir, &m->rx);
+		if(verbose > 1) fprint(2, "fd#%d <- %F\n", c->fd, &m->rx);
 		rewritehdr(&m->rx, m->rpkt);
 		if(mwrite9p(io, c->fd, m->rpkt) < 0)
 			if(verbose) fprint(2, "write error: %r\n");
@@ -473,6 +672,8 @@
 		msgput(m);
 	}
 	closeioproc(io);
+	fprint(2, "output eof\n");
+	threadexitsall(0);
 }	
 
 void
@@ -483,6 +684,7 @@
 	Msg *m;
 	Ioproc *io;
 
+	if(verbose) fprint(2, "input thread\n");
 	io = ioproc();
 	USED(arg);
 	while((pkt = read9ppkt(io, 0)) != nil){
@@ -514,6 +716,8 @@
 			sendq(m->c->outq, m);
 	}
 	closeioproc(io);
+	fprint(2, "input eof\n");
+	threadexitsall(0);
 }
 
 void*
@@ -626,15 +830,20 @@
 	m->c->nmsg--;
 	m->c = nil;
 	fidput(m->fid);
-	fidput(m->afid);
-	fidput(m->newfid);
-	free(m->tpkt);
-	free(m->rpkt);
 	m->fid = nil;
+	fidput(m->afid);
 	m->afid = nil;
+	fidput(m->newfid);
 	m->newfid = nil;
+	free(m->tpkt);
 	m->tpkt = nil;
+	free(m->rpkt);
 	m->rpkt = nil;
+	if(m->rx.type == Ropenfd)
+		close(m->rx.unixfd);
+	m->rx.unixfd = -1;
+	m->isopenfd = 0;
+	m->internal = 0;
 	m->next = freemsg;
 	freemsg = m;
 }
@@ -649,6 +858,7 @@
 	m = msgtab[n];
 	if(m->ref == 0)
 		return nil;
+	if(verbose) fprint(2, "msgget %d = %p\n", n, m);
 	m->ref++;
 	return m;
 }
@@ -768,6 +978,12 @@
 		free(pkt);
 		return nil;
 	}
+/* would do this if we ever got one of these, but we only generate them
+	if(pkt[4] == Ropenfd){
+		newfd = iorecvfd(io, fd);
+		PBIT32(pkt+n-4, newfd);
+	}
+*/
 	return pkt;
 }
 
@@ -795,7 +1011,7 @@
 int
 mwrite9p(Ioproc *io, int fd, uchar *pkt)
 {
-	int n;
+	int n, nfd;
 
 	n = GBIT32(pkt);
 	if(verbose > 2) fprint(2, "write %d %d %.*H\n", fd, n, n, pkt);
@@ -803,6 +1019,13 @@
 		fprint(2, "write error: %r\n");
 		return -1;
 	}
+	if(pkt[4] == Ropenfd){
+		nfd = GBIT32(pkt+n-4);
+		if(iosendfd(io, fd, nfd) < 0){
+			fprint(2, "send fd error: %r\n");
+			return -1;
+		}
+	}
 	return 0;
 }
 
@@ -871,42 +1094,212 @@
 
 #ifdef _LIB9_H_
 /* unix select-based polling */
+struct Ioproc
+{
+	Channel *c;
+	Ioproc *next;
+	int index;
+};
+
+static struct Ioproc **pio;
+static struct pollfd *pfd;
+static int npfd;
+static struct Ioproc *iofree;
+
 Ioproc*
 ioproc(void)
 {
-	return nil;
+	Ioproc *io;
+
+	if(iofree == nil){
+		pfd = erealloc(pfd, (npfd+1)*sizeof(pfd[0]));
+		pfd[npfd].events = 0;
+		pfd[npfd].fd = -1;
+		iofree = emalloc(sizeof(Ioproc));
+		iofree->index = npfd;
+		iofree->c = chancreate(sizeof(ulong), 1);
+		pio = erealloc(pio, (npfd+1)*sizeof(pio[0]));
+		pio[npfd] = iofree;
+		npfd++;
+	}
+	io = iofree;
+	iofree = io->next;
+	return io;
+}
+
+void
+closeioproc(Ioproc *io)
+{
+	io->next = iofree;
+	iofree = io;
+}
+
+void
+pollthread(void *v)
+{
+	int i, n;
+
+	for(;;){
+		yield();
+		for(i=0; i<npfd; i++)
+			pfd[i].revents = 0;
+		if(verbose){
+			fprint(2, "poll:");
+			for(i=0; i<npfd; i++)
+				if(pfd[i].events)
+					fprint(2, " %d%c", pfd[i].fd, pfd[i].events==POLLIN ? 'r' : pfd[i].events==POLLOUT ? 'w' : '?');
+			fprint(2, "\n");
+		}
+		n = poll(pfd, npfd, -1);
+		if(n <= 0)
+			continue;
+		for(i=0; i<npfd; i++)
+			if(pfd[i].fd != -1 && pfd[i].revents){
+				pfd[i].fd = -1;
+				pfd[i].events = 0;
+				pfd[i].revents = 0;
+				nbsendul(pio[i]->c, 1);
+			}
+	}	
+}
+
+static void
+noblock(int fd)
+{
+	fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0)|O_NONBLOCK);
+}
+
+static void
+xwait(Ioproc *io, int fd, int e)
+{
+	if(verbose) fprint(2, "wait for %d%c\n", fd, e==POLLIN ? 'r' : 'w');
+	pfd[io->index].fd = fd;
+	pfd[io->index].events = e;
+	recvul(io->c);
+	if(verbose) fprint(2, "got %d\n", fd);
+}
+
+static void
+rwait(Ioproc *io, int fd)
+{
+	xwait(io, fd, POLLIN);
+}
+
+static void
+wwait(Ioproc *io, int fd)
+{
+	xwait(io, fd, POLLOUT);
 }
 
 long
 ioread(Ioproc *io, int fd, void *v, long n)
 {
+	long r;
 	USED(io);
 
-	xxx;
+	noblock(fd);
+	while((r=read(fd, v, n)) < 0 && errno == EWOULDBLOCK)
+		rwait(io, fd);
+	return r;
+}
+
+long
+ioreadn(Ioproc *io, int fd, void *v, long n)
+{
+	long tot, m;
+	uchar *u;
+
+	u = v;
+	for(tot=0; tot<n; tot+=m){
+		m = ioread(io, fd, u+tot, n-tot);
+		if(m <= 0){
+			if(tot)
+				break;
+			return m;
+		}
+	}
+	return tot;
+}
+
+int
+iorecvfd(Ioproc *io, int fd)
+{
+	int r;
+
+	noblock(fd);
+	while((r=recvfd(fd)) < 0 && errno == EWOULDBLOCK)
+		rwait(io, fd);
+	return r;
+}
+
+int
+iosendfd(Ioproc *io, int s, int fd)
+{
+	int r;
+
+	noblock(s);
+	while((r=sendfd(s, fd)) < 0 && errno == EWOULDBLOCK)
+		wwait(io, s);
+if(r < 0) fprint(2, "sent %d, %d\n", s, fd);
+	return r;
+}
+
+static long
+_iowrite(Ioproc *io, int fd, void *v, long n)
+{
+	long r;
+	USED(io);
+
+	noblock(fd);
+	while((r=write(fd, v, n)) < 0 && errno == EWOULDBLOCK)
+		wwait(io, fd);
+	return r;
 }
 
 long
 iowrite(Ioproc *io, int fd, void *v, long n)
 {
-	USED(io);
+	long tot, m;
+	uchar *u;
 
-	xxx;
+	u = v;
+	for(tot=0; tot<n; tot+=m){
+		m = _iowrite(io, fd, u+tot, n-tot);
+		if(m <= 0){
+			if(tot)
+				break;
+			return m;
+		}
+	}
+	return tot;
 }
 
 int
-iolisten(Ioproc *io, char *a, char *b)
+iolisten(Ioproc *io, char *dir, char *ndir)
 {
+	int fd;
+	int r;
+	extern int _p9netfd(char*);
 	USED(io);
 
-	xxx;
+	if((fd = _p9netfd(dir)) < 0)
+		return -1;
+	noblock(fd);
+	while((r=listen(dir, ndir)) < 0 && errno == EWOULDBLOCK)
+		rwait(io, fd);
+	return r;
 }
 
 int
 ioaccept(Ioproc *io, int fd, char *dir)
 {
+	int r;
 	USED(io);
 
-	xxx;
+	noblock(fd);
+	while((r=accept(fd, dir)) < 0 && errno == EWOULDBLOCK)
+		rwait(io, fd);
+	return r;
 }
 
 #else
diff --git a/src/cmd/9term/9term.c b/src/cmd/9term/9term.c
index ef51d86..cfee001 100644
--- a/src/cmd/9term/9term.c
+++ b/src/cmd/9term/9term.c
@@ -1212,7 +1212,6 @@
 		argv[1] = "-i";
 		argv[2] = 0;
 	}
-
 	/*
 	 * fd0 is slave (tty), fd1 is master (pty)
 	 */
@@ -1222,7 +1221,7 @@
 
         switch(pid = fork()) {
 	case 0:
-		putenv("TERM=9term");
+		putenv("TERM", "9term");
 		close(fd[1]);
 		setsid();
 	//	tcsetpgrp(0, pid);
@@ -1238,6 +1237,7 @@
 		dup(sfd, 2);
 		system("stty tabs -onlcr -echo");
 		execvp(argv[0], argv);
+		fprint(2, "exec %s failed: %r\n", argv[0]);
 		_exits("oops");
 		break;
 	case -1:
@@ -1388,9 +1388,7 @@
 void
 plumbstart(void)
 {
-	char buf[256];
-	snprint(buf, sizeof buf,  "%s/mnt/plumb", getenv("HOME"));
-	if((plumbfd = plumbopen(buf, OWRITE)) < 0)
+	if((plumbfd = plumbopen("send", OWRITE)) < 0)
 		fatal("plumbopen");
 }
 
diff --git a/src/cmd/9term/mkfile b/src/cmd/9term/mkfile
index d7d4a6d..2706dda 100644
--- a/src/cmd/9term/mkfile
+++ b/src/cmd/9term/mkfile
@@ -9,5 +9,5 @@
 
 <$PLAN9/src/mkone
 
-LDFLAGS=-lframe -ldraw -lplumb -lthread -l9 -lfmt -lutf -L$X11/lib -lX11 -lutil
+LDFLAGS=-lframe -ldraw -lplumb -lfs -lmux -lthread -l9 -lfmt -lutf -L$X11/lib -lX11 -lutil
 
diff --git a/src/cmd/dc.c b/src/cmd/dc.c
index 21967de..daff401 100644
--- a/src/cmd/dc.c
+++ b/src/cmd/dc.c
@@ -21,6 +21,7 @@
 #define NE 3
 #define length(p)	((p)->wt-(p)->beg)
 #define rewind(p)	(p)->rd=(p)->beg
+#undef create
 #define create(p)	(p)->rd = (p)->wt = (p)->beg
 #define fsfile(p)	(p)->rd = (p)->wt
 #define truncate(p)	(p)->wt = (p)->rd
diff --git a/src/cmd/dict/dict.c b/src/cmd/dict/dict.c
index dccd303..8cbf6aa 100644
--- a/src/cmd/dict/dict.c
+++ b/src/cmd/dict/dict.c
@@ -59,18 +59,13 @@
 main(int argc, char **argv)
 {
 	int i, cmd, kflag;
-	char *line, *p, *root;
+	char *line, *p;
 
 	Binit(&binbuf, 0, OREAD);
 	Binit(&boutbuf, 1, OWRITE);
 	kflag = 0;
 	line = 0;
 	dict = 0;
-	root = getenv("PLAN9");
-	if(root == nil)
-		root = "/usr/local/plan9";
-	if(chdir(root) < 0)
-		sysfatal("chdir %s: %r", root);
 
 	for(i=0; dicts[i].name; i++){
 		if(access(dicts[i].path, 0)>=0 && access(dicts[i].indexpath, 0)>=0){
@@ -126,12 +121,12 @@
 	}
 	bdict = Bopen(dict->path, OREAD);
 	if(!bdict) {
-		err("can't open dictionary %s/%s", root, dict->path);
+		err("can't open dictionary %s", dict->path);
 		exits("nodict");
 	}
 	bindex = Bopen(dict->indexpath, OREAD);
 	if(!bindex) {
-		err("can't open index %s/%s", root, dict->indexpath);
+		err("can't open index %s", dict->indexpath);
 		exits("noindex");
 	}
 	indextop = Bseek(bindex, 0L, 2);
diff --git a/src/cmd/dict/utils.c b/src/cmd/dict/utils.c
index 8e4db9e..6916d54 100644
--- a/src/cmd/dict/utils.c
+++ b/src/cmd/dict/utils.c
@@ -5,160 +5,160 @@
 
 Dict dicts[] = {
 	{"oed",		"Oxford English Dictionary, 2nd Ed.",
-	 "dict/oed2",	"dict/oed2index",
+	 "#9/dict/oed2",	"#9/dict/oed2index",
 	 oednextoff,	oedprintentry,		oedprintkey},
 	{"ahd",		"American Heritage Dictionary, 2nd College Ed.",
 	 "ahd/DICT.DB",	"ahd/index",
 	 ahdnextoff,	ahdprintentry,		ahdprintkey},
 	{"pgw",		"Project Gutenberg Webster Dictionary",
-	 "dict/pgw",	"dict/pgwindex",
+	 "#9/dict/pgw",	"#9/dict/pgwindex",
 	 pgwnextoff,	pgwprintentry,		pgwprintkey},
 	{"thesaurus",	"Collins Thesaurus",
-	 "dict/thesaurus",	"dict/thesindex",
+	 "#9/dict/thesaurus",	"#9/dict/thesindex",
 	 thesnextoff,	thesprintentry,	thesprintkey},
 
 	{"ce",		"Gendai Chinese->English",
-	 "dict/world/sansdata/sandic24.dat",
-	 "dict/world/sansdata/ceindex",
+	 "#9/dict/world/sansdata/sandic24.dat",
+	 "#9/dict/world/sansdata/ceindex",
 	 worldnextoff,	worldprintentry,	worldprintkey},
 	{"ceh",		"Gendai Chinese->English (Hanzi index)",
-	 "dict/world/sansdata/sandic24.dat",
-	 "dict/world/sansdata/cehindex",
+	 "#9/dict/world/sansdata/sandic24.dat",
+	 "#9/dict/world/sansdata/cehindex",
 	 worldnextoff,	worldprintentry,	worldprintkey},
 	{"ec",		"Gendai English->Chinese",
-	 "dict/world/sansdata/sandic24.dat",
-	 "dict/world/sansdata/ecindex",
+	 "#9/dict/world/sansdata/sandic24.dat",
+	 "#9/dict/world/sansdata/ecindex",
 	 worldnextoff,	worldprintentry,	worldprintkey},
 
 	{"dae",		"Gyldendal Danish->English",
-	 "dict/world/gylddata/sandic30.dat",
-	 "dict/world/gylddata/daeindex",
+	 "#9/dict/world/gylddata/sandic30.dat",
+	 "#9/dict/world/gylddata/daeindex",
 	 worldnextoff,	worldprintentry,	worldprintkey},
 	{"eda",		"Gyldendal English->Danish",
-	 "dict/world/gylddata/sandic29.dat",
-	 "dict/world/gylddata/edaindex",
+	 "#9/dict/world/gylddata/sandic29.dat",
+	 "#9/dict/world/gylddata/edaindex",
 	 worldnextoff,	worldprintentry,	worldprintkey},
 
 	{"due",		"Wolters-Noordhoff Dutch->English",
-	 "dict/world/woltdata/sandic07.dat",
-	 "dict/world/woltdata/deindex",
+	 "#9/dict/world/woltdata/sandic07.dat",
+	 "#9/dict/world/woltdata/deindex",
 	 worldnextoff,	worldprintentry,	worldprintkey},
 	{"edu",		"Wolters-Noordhoff English->Dutch",
-	 "dict/world/woltdata/sandic06.dat",
-	 "dict/world/woltdata/edindex",
+	 "#9/dict/world/woltdata/sandic06.dat",
+	 "#9/dict/world/woltdata/edindex",
 	 worldnextoff,	worldprintentry,	worldprintkey},
 
 	{"fie",		"WSOY Finnish->English",
-	 "dict/world/werndata/sandic32.dat",
-	 "dict/world/werndata/fieindex",
+	 "#9/dict/world/werndata/sandic32.dat",
+	 "#9/dict/world/werndata/fieindex",
 	 worldnextoff,	worldprintentry,	worldprintkey},
 	{"efi",		"WSOY English->Finnish",
-	 "dict/world/werndata/sandic31.dat",
-	 "dict/world/werndata/efiindex",
+	 "#9/dict/world/werndata/sandic31.dat",
+	 "#9/dict/world/werndata/efiindex",
 	 worldnextoff,	worldprintentry,	worldprintkey},
 
 	{"fe",		"Collins French->English",
-	 "dict/fe",	"dict/feindex",
+	 "#9/dict/fe",	"#9/dict/feindex",
 	 pcollnextoff,	pcollprintentry,	pcollprintkey},
 	{"ef",		"Collins English->French",
-	 "dict/ef",	"dict/efindex",
+	 "#9/dict/ef",	"#9/dict/efindex",
 	 pcollnextoff,	pcollprintentry,	pcollprintkey},
 
 	{"ge",		"Collins German->English",
-	 "dict/ge",	"dict/geindex",
+	 "#9/dict/ge",	"#9/dict/geindex",
 	 pcollgnextoff,	pcollgprintentry,	pcollgprintkey},
 	{"eg",		"Collins English->German",
-	 "dict/eg",	"dict/egindex",
+	 "#9/dict/eg",	"#9/dict/egindex",
 	 pcollgnextoff,	pcollgprintentry,	pcollgprintkey},
 
 	{"ie",		"Collins Italian->English",
-	 "dict/ie",	"dict/ieindex",
+	 "#9/dict/ie",	"#9/dict/ieindex",
 	 pcollnextoff,	pcollprintentry,	pcollprintkey},
 	{"ei",		"Collins English->Italian",
-	 "dict/ei",	"dict/eiindex",
+	 "#9/dict/ei",	"#9/dict/eiindex",
 	 pcollnextoff,	pcollprintentry,	pcollprintkey},
 
 	{"je",		"Sanshusha Japanese->English",
-	 "dict/world/sansdata/sandic18.dat",
-	 "dict/world/sansdata/jeindex",
+	 "#9/dict/world/sansdata/sandic18.dat",
+	 "#9/dict/world/sansdata/jeindex",
 	 worldnextoff,	worldprintentry,	worldprintkey},
 	{"jek",		"Sanshusha Japanese->English (Kanji index)",
-	 "dict/world/sansdata/sandic18.dat",
-	 "dict/world/sansdata/jekindex",
+	 "#9/dict/world/sansdata/sandic18.dat",
+	 "#9/dict/world/sansdata/jekindex",
 	 worldnextoff,	worldprintentry,	worldprintkey},
 	{"ej",		"Sanshusha English->Japanese",
-	 "dict/world/sansdata/sandic18.dat",
-	 "dict/world/sansdata/ejindex",
+	 "#9/dict/world/sansdata/sandic18.dat",
+	 "#9/dict/world/sansdata/ejindex",
 	 worldnextoff,	worldprintentry,	worldprintkey},
 
 	{"tjeg",	"Sanshusha technical Japanese->English,German",
-	 "dict/world/sansdata/sandic16.dat",
-	 "dict/world/sansdata/tjegindex",
+	 "#9/dict/world/sansdata/sandic16.dat",
+	 "#9/dict/world/sansdata/tjegindex",
 	 worldnextoff,	worldprintentry,	worldprintkey},
 	{"tjegk",	"Sanshusha technical Japanese->English,German (Kanji index)",
-	 "dict/world/sansdata/sandic16.dat",
-	 "dict/world/sansdata/tjegkindex",
+	 "#9/dict/world/sansdata/sandic16.dat",
+	 "#9/dict/world/sansdata/tjegkindex",
 	 worldnextoff,	worldprintentry,	worldprintkey},
 	{"tegj",	"Sanshusha technical English->German,Japanese",
-	 "dict/world/sansdata/sandic16.dat",
-	 "dict/world/sansdata/tegjindex",
+	 "#9/dict/world/sansdata/sandic16.dat",
+	 "#9/dict/world/sansdata/tegjindex",
 	 worldnextoff,	worldprintentry,	worldprintkey},
 	{"tgje",	"Sanshusha technical German->Japanese,English",
-	 "dict/world/sansdata/sandic16.dat",
-	 "dict/world/sansdata/tgjeindex",
+	 "#9/dict/world/sansdata/sandic16.dat",
+	 "#9/dict/world/sansdata/tgjeindex",
 	 worldnextoff,	worldprintentry,	worldprintkey},
 
 	{"ne",		"Kunnskapforlaget Norwegian->English",
-	 "dict/world/kunndata/sandic28.dat",
-	 "dict/world/kunndata/neindex",
+	 "#9/dict/world/kunndata/sandic28.dat",
+	 "#9/dict/world/kunndata/neindex",
 	 worldnextoff,	worldprintentry,	worldprintkey},
 	{"en",		"Kunnskapforlaget English->Norwegian",
-	 "dict/world/kunndata/sandic27.dat",
-	 "dict/world/kunndata/enindex",
+	 "#9/dict/world/kunndata/sandic27.dat",
+	 "#9/dict/world/kunndata/enindex",
 	 worldnextoff,	worldprintentry,	worldprintkey},
 
 	{"re",		"Leon Ungier Russian->English",
-	 "dict/re",	"dict/reindex",
+	 "#9/dict/re",	"#9/dict/reindex",
 	 simplenextoff,	simpleprintentry,	simpleprintkey},
 	{"er",		"Leon Ungier English->Russian",
-	 "dict/re",	"dict/erindex",
+	 "#9/dict/re",	"#9/dict/erindex",
 	 simplenextoff,	simpleprintentry,	simpleprintkey},
 
 	{"se",		"Collins Spanish->English",
-	 "dict/se",	"dict/seindex",
+	 "#9/dict/se",	"#9/dict/seindex",
 	 pcollnextoff,	pcollprintentry,	pcollprintkey},
 	{"es",		"Collins English->Spanish",
-	 "dict/es",	"dict/esindex",
+	 "#9/dict/es",	"#9/dict/esindex",
 	 pcollnextoff,	pcollprintentry,	pcollprintkey},
 
 	{"swe",		"Esselte Studium Swedish->English",
-	 "dict/world/essedata/sandic34.dat",
-	 "dict/world/essedata/sweindex",
+	 "#9/dict/world/essedata/sandic34.dat",
+	 "#9/dict/world/essedata/sweindex",
 	 worldnextoff,	worldprintentry,	worldprintkey},
 	{"esw",		"Esselte Studium English->Swedish",
-	 "dict/world/essedata/sandic33.dat",
-	 "dict/world/essedata/eswindex",
+	 "#9/dict/world/essedata/sandic33.dat",
+	 "#9/dict/world/essedata/eswindex",
 	 worldnextoff,	worldprintentry,	worldprintkey},
 
 	{"movie",	"Movies -- by title",
-	 "movie/data",	"dict/movtindex",
+	 "movie/data",	"#9/dict/movtindex",
 	 movienextoff,	movieprintentry,	movieprintkey},
 	{"moviea",	"Movies -- by actor",
-	 "movie/data",	"dict/movaindex",
+	 "movie/data",	"#9/dict/movaindex",
 	 movienextoff,	movieprintentry,	movieprintkey},
 	{"movied",	"Movies -- by director",
-	 "movie/data",	"dict/movdindex",
+	 "movie/data",	"#9/dict/movdindex",
 	 movienextoff,	movieprintentry,	movieprintkey},
 
 	{"slang",	"English Slang",
-	 "dict/slang",	"dict/slangindex",
+	 "#9/dict/slang",	"#9/dict/slangindex",
 	 slangnextoff,	slangprintentry,	slangprintkey},
 
 	{"robert",	"Robert Électronique",
-	 "dict/robert/_pointers",	"dict/robert/_index",
+	 "#9/dict/robert/_pointers",	"#9/dict/robert/_index",
 	 robertnextoff,	robertindexentry,	robertprintkey},
 	{"robertv",	"Robert Électronique - formes des verbes",
-	 "dict/robert/flex.rob",	"dict/robert/_flexindex",
+	 "#9/dict/robert/flex.rob",	"#9/dict/robert/_flexindex",
 	 robertnextflex,	robertflexentry,	robertprintkey},
 
 	{0, 0, 0, 0, 0}
diff --git a/src/cmd/mkfile b/src/cmd/mkfile
index 8450d94..e2131e7 100644
--- a/src/cmd/mkfile
+++ b/src/cmd/mkfile
@@ -2,11 +2,11 @@
 <$PLAN9/src/mkhdr
 
 TARG=`ls *.c | sed 's/\.c//'`
-LDFLAGS=$LDFLAGS -lthread -lsec -lfs -lmux -lregexp9 -l9 -lbio -lfmt -lutf
+LDFLAGS=$LDFLAGS -lthread -lsec -lfs -lmux -lregexp9 -lbio -l9 -lfmt -lutf
 
 <$PLAN9/src/mkmany
 
-BUGGERED='CVS|oplumb|plumb|plumb2|mk|vac|9term|venti|htmlfmt'
+BUGGERED='CVS|oplumb|plumb2|mk|vac|9term|venti|htmlfmt'
 DIRS=`ls -l |sed -n 's/^d.* //p' |egrep -v "$BUGGERED"`
 
 <$PLAN9/src/mkdirs
diff --git a/src/cmd/plumb/fsys.c b/src/cmd/plumb/fsys.c
index 6f95a23..912e5ca 100644
--- a/src/cmd/plumb/fsys.c
+++ b/src/cmd/plumb/fsys.c
@@ -3,14 +3,13 @@
 #include <bio.h>
 #include <regexp.h>
 #include <thread.h>
-#include <auth.h>
 #include <fcall.h>
 #include <plumb.h>
 #include "plumber.h"
 
 enum
 {
-	Stack = 8*1024
+	Stack = 32*1024
 };
 
 typedef struct Dirtab Dirtab;
@@ -73,13 +72,12 @@
 
 struct	/* needed because incref() doesn't return value */
 {
-	Lock;
-	int			ref;
+	Lock	lk;
+	int	ref;
 } rulesref;
 
 enum
 {
-	DEBUG	= 0,
 	NDIR	= 50,
 	Nhash	= 16,
 
@@ -99,13 +97,10 @@
 static int	ndir = NQID;
 
 static int		srvfd;
-static int		srvclosefd;			/* rock for end of pipe to close */
-static int		clockfd;
 static int		clock;
 static Fid		*fids[Nhash];
 static QLock	readlock;
 static QLock	queue;
-static char	srvfile[128];
 static int		messagesize = 8192+IOHDRSZ;	/* good start */
 
 static void	fsysproc(void*);
@@ -183,54 +178,35 @@
 static ulong
 getclock(void)
 {
-	char buf[32];
-
-	seek(clockfd, 0, 0);
-	read(clockfd, buf, sizeof buf);
-	return atoi(buf);
+	return time(0);
 }
 
 void
 startfsys(void)
 {
-	int p[2], fd;
+	int p[2];
 
 	fmtinstall('F', fcallfmt);
-	clockfd = open("/dev/time", OREAD|OCEXEC);
 	clock = getclock();
 	if(pipe(p) < 0)
 		error("can't create pipe: %r");
 	/* 0 will be server end, 1 will be client end */
 	srvfd = p[0];
-	srvclosefd = p[1];
-	sprint(srvfile, "/srv/plumb.%s.%d", user, getpid());
-	if(putenv("plumbsrv", srvfile) < 0)
-		error("can't write $plumbsrv: %r");
-	fd = create(srvfile, OWRITE|OCEXEC|ORCLOSE, 0600);
-	if(fd < 0)
-		error("can't create /srv file: %r");
-	if(fprint(fd, "%d", p[1]) <= 0)
-		error("can't write /srv/file: %r");
-	/* leave fd open; ORCLOSE will take care of it */
-
-	procrfork(fsysproc, nil, Stack, RFFDG);
-
-	close(p[0]);
-	if(mount(p[1], -1, "/mnt/plumb", MREPL, "") < 0)
-		error("can't mount /mnt/plumb: %r");
+	if(post9pservice(p[1], "plumb") < 0)
+		sysfatal("post9pservice plumb: %r");
 	close(p[1]);
+	proccreate(fsysproc, nil, Stack);
 }
 
 static void
-fsysproc(void*)
+fsysproc(void *v)
 {
 	int n;
 	Fcall *t;
 	Fid *f;
 	uchar *buf;
 
-	close(srvclosefd);
-	srvclosefd = -1;
+	USED(v);
 	t = nil;
 	for(;;){
 		buf = malloc(messagesize);	/* avoid memset of emalloc */
@@ -250,7 +226,7 @@
 			t = emalloc(sizeof(Fcall));
 		if(convM2S(buf, n, t) != n)
 			error("convert error in convM2S");
-		if(DEBUG)
+		if(debug)
 			fprint(2, "<= %F\n", t);
 		if(fcall[t->type] == nil)
 			fsysrespond(t, buf, Ebadfcall);
@@ -281,7 +257,7 @@
 		error("convert error in convS2M");
 	if(write(srvfd, buf, n) != n)
 		error("write error in respond");
-	if(DEBUG)
+	if(debug)
 		fprint(2, "=> %F\n", t);
 	free(buf);
 }
@@ -555,8 +531,10 @@
 }
 
 static Fcall*
-fsysversion(Fcall *t, uchar *buf, Fid*)
+fsysversion(Fcall *t, uchar *buf, Fid *fid)
 {
+	USED(fid);
+
 	if(t->msize < 256){
 		fsysrespond(t, buf, "version: message size too small");
 		return t;
@@ -574,8 +552,9 @@
 }
 
 static Fcall*
-fsysauth(Fcall *t, uchar *buf, Fid*)
+fsysauth(Fcall *t, uchar *buf, Fid *fid)
 {
+	USED(fid);
 	fsysrespond(t, buf, "plumber: authentication not required");
 	return t;
 }
@@ -605,10 +584,11 @@
 }
 
 static Fcall*
-fsysflush(Fcall *t, uchar *buf, Fid*)
+fsysflush(Fcall *t, uchar *buf, Fid *fid)
 {
 	int i;
 
+	USED(fid);
 	qlock(&queue);
 	for(i=NQID; i<ndir; i++)
 		flushqueue(&dir[i], t->oldtag);
@@ -729,14 +709,14 @@
 	if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
 		goto Deny;
 	if(f->qid.path==Qrules && (mode==OWRITE || mode==ORDWR)){
-		lock(&rulesref);
+		lock(&rulesref.lk);
 		if(rulesref.ref++ != 0){
 			rulesref.ref--;
-			unlock(&rulesref);
+			unlock(&rulesref.lk);
 			fsysrespond(t, buf, Einuse);
 			return t;
 		}
-		unlock(&rulesref);
+		unlock(&rulesref.lk);
 	}
 	if(clearrules){
 		writerules(nil, 0);
@@ -761,8 +741,9 @@
 }
 
 static Fcall*
-fsyscreate(Fcall *t, uchar *buf, Fid*)
+fsyscreate(Fcall *t, uchar *buf, Fid *fid)
 {
+	USED(fid);
 	fsysrespond(t, buf, Eperm);
 	return t;
 }
@@ -916,15 +897,17 @@
 }
 
 static Fcall*
-fsyswstat(Fcall *t, uchar *buf, Fid*)
+fsyswstat(Fcall *t, uchar *buf, Fid *fid)
 {
+	USED(fid);
 	fsysrespond(t, buf, Eperm);
 	return t;
 }
 
 static Fcall*
-fsysremove(Fcall *t, uchar *buf, Fid*)
+fsysremove(Fcall *t, uchar *buf, Fid *fid)
 {
+	USED(fid);
 	fsysrespond(t, buf, Eperm);
 	return t;
 }
@@ -945,9 +928,9 @@
 			 * unless last write ended with a blank line
 			 */
 			writerules(nil, 0);
-			lock(&rulesref);
+			lock(&rulesref.lk);
 			rulesref.ref--;
-			unlock(&rulesref);
+			unlock(&rulesref.lk);
 		}
 		prev = nil;
 		for(p=d->fopen; p; p=p->nextopen){
diff --git a/src/cmd/plumb/match.c b/src/cmd/plumb/match.c
index 42a9232..dc1abbb 100644
--- a/src/cmd/plumb/match.c
+++ b/src/cmd/plumb/match.c
@@ -6,6 +6,7 @@
 #include <plumb.h>
 #include "plumber.h"
 
+/*
 static char*
 nonnil(char *s)
 {
@@ -13,6 +14,7 @@
 		return "";
 	return s;
 }
+*/
 
 int
 verbis(int obj, Plumbmsg *m, Rule *r)
@@ -44,10 +46,10 @@
 		free(match[i]);
 		match[i] = nil;
 	}
-	for(i=0; i<10 && rs[i].sp!=nil; i++){
-		n = rs[i].ep-rs[i].sp;
+	for(i=0; i<10 && rs[i].s.sp!=nil; i++){
+		n = rs[i].e.ep-rs[i].s.sp;
 		match[i] = emalloc(n+1);
-		memmove(match[i], rs[i].sp, n);
+		memmove(match[i], rs[i].s.sp, n);
 		match[i][n] = '\0';
 	}
 }
@@ -66,7 +68,7 @@
 	for(i=0; i<=click; i++){
 		memset(rs, 0, 10*sizeof(Resub));
 		if(regexec(re, text+i, rs, 10))
-			if(rs[0].sp<=clickp && clickp<=rs[0].ep)
+			if(rs[0].s.sp<=clickp && clickp<=rs[0].e.ep)
 				return 1;
 	}
 	return 0;
@@ -94,8 +96,8 @@
 		}
 		if(!clickmatch(r->regex, m->data, rs, atoi(clickval)))
 			break;
-		p0 = rs[0].sp - m->data;
-		p1 = rs[0].ep - m->data;
+		p0 = rs[0].s.sp - m->data;
+		p1 = rs[0].e.ep - m->data;
 		if(e->p0 >=0 && !(p0==e->p0 && p1==e->p1))
 			break;
 		e->clearclick = 1;
@@ -120,7 +122,7 @@
 		/* must match full text */
 		if(ntext < 0)
 			ntext = strlen(alltext);
-		if(!regexec(r->regex, alltext, rs, 10) || rs[0].sp!=alltext || rs[0].ep!=alltext+ntext)
+		if(!regexec(r->regex, alltext, rs, 10) || rs[0].s.sp!=alltext || rs[0].e.ep!=alltext+ntext)
 			break;
 		setvar(rs, e->match);
 		return 1;
@@ -389,7 +391,7 @@
 {
 	NARGS		= 100,
 	NARGCHAR	= 8*1024,
-	EXECSTACK 	= 4096+(NARGS+1)*sizeof(char*)+NARGCHAR
+	EXECSTACK 	= 32*1024+(NARGS+1)*sizeof(char*)+NARGCHAR
 };
 
 /* copy argv to stack and free the incoming strings, so we don't leak argument vectors */
@@ -419,19 +421,17 @@
 void
 execproc(void *v)
 {
+	int fd[3];
 	char **av;
-	char buf[1024], *args[NARGS+1], argc[NARGCHAR];
+	char *args[NARGS+1], argc[NARGCHAR];
 
-	rfork(RFFDG);
-	close(0);
-	open("/dev/null", OREAD);
+	fd[0] = open("/dev/null", OREAD);
+	fd[1] = dup(1, -1);
+	fd[2] = dup(2, -1);
 	av = v;
 	stackargv(av, args, argc);
 	free(av);
-	procexec(nil, args[0], args);
-	if(args[0][0]!='/' && strncmp(args[0], "./", 2)!=0 && strncmp(args[0], "../", 3)!=0)
-		snprint(buf, sizeof buf, "/bin/%s", args[0]);
-	procexec(nil, buf, args);
+	procexec(nil, fd, args[0], args);
 	threadexits("can't exec");
 }
 
diff --git a/src/cmd/plumb/mkfile b/src/cmd/plumb/mkfile
index d6a1465..6550387 100644
--- a/src/cmd/plumb/mkfile
+++ b/src/cmd/plumb/mkfile
@@ -1,10 +1,9 @@
-</$objtype/mkfile
+PLAN9=../../..
+<$PLAN9/src/mkhdr
 
 TARG=plumber plumb
 
-
-BIN=/$objtype/bin
-</sys/src/cmd/mkmany
+<$PLAN9/src/mkmany
 
 PLUMBER=plumber.$O fsys.$O match.$O rules.$O
 PLUMB=plumb.$O
@@ -15,6 +14,4 @@
 $O.plumb:	$PLUMB
 $O.plumber:	$PLUMBER
 
-syms:V:
-	8c -a plumber.c	>syms
-	8c -aa fsys.c match.c rules.c >>syms
+LDFLAGS=$LDFLAGS -lplumb -lfs -lmux -lthread -lregexp9 -l9 -lbio -lfmt -lutf
diff --git a/src/cmd/plumb/plumber.c b/src/cmd/plumb/plumber.c
index d0bd9c1..424469f 100644
--- a/src/cmd/plumb/plumber.c
+++ b/src/cmd/plumb/plumber.c
@@ -3,10 +3,10 @@
 #include <regexp.h>
 #include <thread.h>
 #include <plumb.h>
-#include <auth.h>
 #include <fcall.h>
 #include "plumber.h"
 
+int debug;
 char	*plumbfile;
 char *user;
 char *home;
@@ -47,13 +47,18 @@
 	progname = "plumber";
 
 	ARGBEGIN{
+	case 'd':
+		debug = 1;
+		break;
 	case 'p':
 		plumbfile = ARGF();
 		break;
 	}ARGEND
 
-	user = getenv("user");
+	user = getuser();
 	home = getenv("home");
+	if(home == nil)
+		home = getenv("HOME");
 	if(user==nil || home==nil)
 		error("can't initialize $user or $home: %r");
 	if(plumbfile == nil){
diff --git a/src/cmd/plumb/plumber.h b/src/cmd/plumb/plumber.h
index 0d1205f..4b9267a 100644
--- a/src/cmd/plumb/plumber.h
+++ b/src/cmd/plumb/plumber.h
@@ -91,3 +91,4 @@
 char		*lasterror;
 char		**ports;
 int		nports;
+int		debug;
diff --git a/src/cmd/plumb/rules.c b/src/cmd/plumb/rules.c
index 262f6d6..b51bb61 100644
--- a/src/cmd/plumb/rules.c
+++ b/src/cmd/plumb/rules.c
@@ -143,7 +143,7 @@
 getline(void)
 {
 	static int n = 0;
-	static char *s, *incl;
+	static char *s /*, *incl*/;
 	int c, i;
 
 	i = 0;
@@ -414,7 +414,7 @@
 	t = args[1];
 	fd = open(t, OREAD);
 	if(fd<0 && t[0]!='/' && strncmp(t, "./", 2)!=0 && strncmp(t, "../", 3)!=0){
-		snprint(buf, sizeof buf, "/sys/lib/plumb/%s", t);
+		snprint(buf, sizeof buf, "#9/plumb/%s", t);
 		t = buf;
 		fd = open(t, OREAD);
 	}
diff --git a/src/cmd/rc/plan9ish.c b/src/cmd/rc/plan9ish.c
index 1b250ba..abb66cc 100644
--- a/src/cmd/rc/plan9ish.c
+++ b/src/cmd/rc/plan9ish.c
@@ -27,6 +27,8 @@
 char*
 Rcmain(void)
 {
+	return "#9/rcmain";
+/*
 	static char buf[256];
 	char *root;
 
@@ -35,9 +37,10 @@
 		root = "/usr/local/plan9";
 	snprint(buf, sizeof buf, "%s/rcmain", root);
 	return buf;
+*/
 }
 
-char Fdprefix[]="/dev/fd/";
+char Fdprefix[]="#d/";
 void execfinit(void);
 void execbind(void);
 void execmount(void);
diff --git a/src/cmd/sam/unix.c b/src/cmd/sam/unix.c
index 45cfc3b..b8a67b9 100644
--- a/src/cmd/sam/unix.c
+++ b/src/cmd/sam/unix.c
@@ -214,92 +214,4 @@
 	return p;
 }
 
-#if 0
-char *
-strdup(const char *s)
-{
-	return strcpy(emalloc(strlen(s)), s);
-}
-#endif
-
-/*
-void exits(const char *s)
-{
-    if (s) fprint(2, "exit: %s\n", s);
-    exit(s != 0);
-}
-
-void
-_exits(const char *s)
-{
-    if (s) fprint(2, "exit: %s\n", s);
-    _exit(s != 0);
-}
-
-int errstr(char *buf, int size)
-{
-    extern int errno;
-                
-    snprint(buf, size, "%s", strerror(errno));
-    return 1;       
-}                       
-*/
-                    
-int
-create(char *name, int omode, ulong perm)
-{
-    int mode;
-    int fd; 
-        
-    if (omode & OWRITE) mode = O_WRONLY;
-    else if (omode & OREAD) mode = O_RDONLY;
-    else mode = O_RDWR;
-
-    if ((fd = open(name, mode|O_CREAT|O_TRUNC, perm)) < 0)
-	return fd;
-
-    if (omode & OCEXEC)
-	fcntl(fd, F_SETFD, fcntl(fd,F_GETFD,0) | FD_CLOEXEC);
-
-    /* SES - not exactly right, but hopefully good enough. */
-    if (omode & ORCLOSE)
-	remove(name);
-
-    return fd;                          
-}
-
-/* SHOULD BE ELSEWHERE */
-#if 0	/* needed on old __APPLE__ */
-#include <lib9.h>
-
-Lock plk;
-
-ulong
-pread(int fd, void *buf, ulong n, ulong off)
-{
-	ulong rv;
-
-	lock(&plk);
-	if (lseek(fd, off, 0) != off)
-		return -1;
-	rv = read(fd, buf, n);
-	unlock(&plk);
-
-	return rv;
-}
-
-ulong
-pwrite(int fd, void *buf, ulong n, ulong off)
-{
-	ulong rv;
-
-	lock(&plk);
-	if (lseek(fd, off, 0) != off)
-		return -1;
-	rv = write(fd, buf, n);
-	unlock(&plk);
-
-	return rv;
-}
-#endif
 
diff --git a/src/cmd/yacc.c b/src/cmd/yacc.c
index 05e9d58..d9e3076 100644
--- a/src/cmd/yacc.c
+++ b/src/cmd/yacc.c
@@ -13,8 +13,8 @@
 #define SETBIT(a,i)	((a)[(i)>>5] |= (1<<((i)&037)))
 #define NWORDS(n)	(((n)+32)/32)
 
-#define PARSER		"lib/yaccpar"
-#define PARSERS		"lib/yaccpars"
+#define PARSER		"#9/lib/yaccpar"
+#define PARSERS		"#9/lib/yaccpars"
 #define TEMPNAME	"y.tmp.XXXXXX"
 #define ACTNAME		"y.acts.XXXXXX"
 #define OFILE		"tab.c"
@@ -398,19 +398,10 @@
 others(void)
 {
 	int c, i, j;
-	char *s, *root;
 
-	root = getenv("PLAN9");
-	if(root == nil)
-		root = "/usr/local/plan9";
-	s = malloc(strlen(root)+1+strlen(parser)+1);
-	strcpy(s, root);
-	strcat(s, "/");
-	strcat(s, parser);
-	finput = Bopen(s, OREAD);
+	finput = Bopen(parser, OREAD);
 	if(finput == 0)
-		error("cannot find parser %s", s);
-	free(s);
+		error("cannot find parser %s", parser);
 	warray("yyr1", levprd, nprod);
 	aryfil(temp1, nprod, 0);
 	PLOOP(1, i)