Today's changes.
More changes.
diff --git a/src/cmd/9pserve.c b/src/cmd/9pserve.c
index 8acd552..644385c 100644
--- a/src/cmd/9pserve.c
+++ b/src/cmd/9pserve.c
@@ -1150,7 +1150,7 @@
 	}
 }
 
-#ifdef _LIB9_H_
+#ifdef _LIBC_H_
 /* unix select-based polling */
 struct Ioproc
 {
diff --git a/src/cmd/9term/9term.c b/src/cmd/9term/9term.c
index c5a2c42..50c0cfc 100644
--- a/src/cmd/9term/9term.c
+++ b/src/cmd/9term/9term.c
@@ -1,15 +1,24 @@
-#include "9term.h"
-
-#define fatal	sysfatal
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <draw.h>
+#include <thread.h>
+#include <mouse.h>
+#include <cursor.h>
+#include <keyboard.h>
+#include <frame.h>
+#include <plumb.h>
+#include <complete.h>
+#include "term.h"
 
 typedef struct Text	Text;
 typedef struct Readbuf	Readbuf;
 
 enum
 {
-	/* these are chosen to use malloc()'s properties well */
 	HiWater	= 640000,	/* max size of history */
-	LoWater	= 330000,	/* min size of history after max'ed */
+	LoWater	= 400000,	/* min size of history after max'ed */
+	MinWater	= 20000,
 };
 
 /* various geometric paramters */
@@ -30,21 +39,22 @@
 	Scroll,
 };
 
-
-#define	SCROLLKEY	Kdown
 #define	ESC		0x1B
-#define CUT		0x18	/* ctrl-x */		
-#define COPY		0x03	/* crtl-c */
-#define PASTE		0x16	/* crtl-v */
-#define BACKSCROLLKEY	Kup
+#define	CUT		0x18	/* ctrl-x */		
+#define	COPY		0x03	/* crtl-c */
+#define	PASTE		0x16	/* crtl-v */
 
 #define	READBUFSIZE 8192
+#define TRUE 1
+#define FALSE 0
+
 
 struct Text
 {
 	Frame		*f;		/* frame ofr terminal */
 	Mouse		m;
 	uint		nr;		/* num of runes in term */
+	uint		maxr;	/* max num of runes in r */
 	Rune		*r;		/* runes for term */
 	uint		nraw;		/* num of runes in raw buffer */
 	Rune		*raw;		/* raw buffer */
@@ -72,7 +82,6 @@
 void	tcheck(void);
 void	updatesel(void);
 void	doreshape(void);
-void	rcstart(int fd[2], int, char**);
 void	runewrite(Rune*, int);
 void	consread(void);
 void	conswrite(char*, int);
@@ -99,11 +108,10 @@
 void	scroll(int);
 void	hostproc(void *arg);
 void	hoststart(void);
-int	getchildwd(int, char*, int);
 void	plumbstart(void);
 void	plumb(uint, uint);
 void	plumbclick(uint*, uint*);
-int	getpts(int fd[], char *slave);
+uint	insert(Rune*, int, uint, int);
 
 #define	runemalloc(n)		malloc((n)*sizeof(Rune))
 #define	runerealloc(a, n)	realloc(a, (n)*sizeof(Rune))
@@ -115,7 +123,7 @@
 int		scrolling;	/* window scrolls */
 int		clickmsec;	/* time of last click */
 uint		clickq0;	/* point of last click */
-int		rcfd[2];
+int		rcfd;
 int		rcpid;
 int		maxtab;
 int		use9wm;
@@ -211,7 +219,7 @@
 
 	mc = initmouse(nil, screen);
 	kc = initkeyboard(nil);
-	rcstart(rcfd, argc, argv);
+	rcpid = rcstart(argc, argv, &rcfd);
 	hoststart();
 	plumbstart();
 
@@ -265,8 +273,9 @@
 
 	i = 0;
 	for(;;){
+		/* Let typing have a go -- maybe there's a rubout waiting. */
 		i = 1-i;	/* toggle */
-		n = threadread(rcfd[0], rcbuf[i].data, sizeof rcbuf[i].data);
+		n = threadread(rcfd, rcbuf[i].data, sizeof rcbuf[i].data);
 		if(n <= 0){
 			if(n < 0)
 				fprint(2, "9term: host read error: %r\n");
@@ -308,7 +317,7 @@
 			a[2].op = CHANNOP;;
 		switch(alt(a)) {
 		default:
-			fatal("impossible");
+			sysfatal("impossible");
 		case 0:
 			t.m = mc->m;
 			mouse();
@@ -330,23 +339,23 @@
 doreshape(void)
 {
 	if(getwindow(display, Refnone) < 0)
-		fatal("can't reattach to window");
+		sysfatal("can't reattach to window");
 	draw(screen, screen->r, cols[BACK], nil, ZP);
 	geom();
 	scrdraw();
 }
 
-struct winsize ows;
-
 void
 geom(void)
 {
-	struct winsize ws;
 	Point p;
 	Rectangle r;
 
 	r = screen->r;
-	scrollr = screen->r;
+	r.min.y++;
+	r.max.y--;
+
+	scrollr = r;
 	scrollr.max.x = r.min.x+Scrollwid;
 	lastsr = Rect(0,0,0,0);
 
@@ -362,13 +371,7 @@
 	if(p.x == 0 || p.y == 0)
 		return;
 
-	ws.ws_row = Dy(r)/p.y;
-	ws.ws_col = Dx(r)/p.x;
-	ws.ws_xpixel = Dx(r);
-	ws.ws_ypixel = Dy(r);
-	if(ws.ws_row != ows.ws_row || ws.ws_col != ows.ws_col)
-	if(ioctl(rcfd[0], TIOCSWINSZ, &ws) < 0)
-		fprint(2, "ioctl: %r\n");
+	updatewinsize(Dy(r)/p.y, Dx(r)/p.x, Dx(r), Dy(r));
 }
 
 void
@@ -585,7 +588,10 @@
 			show(t.q0);
 		break;
 	case Send:
-		snarf();
+		if(t.q0 != t.q1)
+			snarf();
+		else
+			snarfupdate();
 		t.q0 = t.q1 = t.nr;
 		updatesel();
 		paste(t.snarf, t.nsnarf, 1);
@@ -605,37 +611,182 @@
 		plumb(t.q0, t.q1);
 		break;
 	default:
-		fatal("bad menu item");
+		sysfatal("bad menu item");
 	}
 }
 
+int
+windfilewidth(uint q0, int oneelement)
+{
+	uint q;
+	Rune r;
+
+	q = q0;
+	while(q > 0){
+		r = t.r[q-1];
+		if(r<=' ')
+			break;
+		if(oneelement && r=='/')
+			break;
+		--q;
+	}
+	return q0-q;
+}
+
+void
+showcandidates(Completion *c)
+{
+	int i;
+	Fmt f;
+	Rune *rp;
+	uint nr, qline, q0;
+	char *s;
+
+	runefmtstrinit(&f);
+	if (c->nmatch == 0)
+		s = "[no matches in ";
+	else
+		s = "[";
+	if(c->nfile > 32)
+		fmtprint(&f, "%s%d files]\n", s, c->nfile);
+	else{
+		fmtprint(&f, "%s", s);
+		for(i=0; i<c->nfile; i++){
+			if(i > 0)
+				fmtprint(&f, " ");
+			fmtprint(&f, "%s", c->filename[i]);
+		}
+		fmtprint(&f, "]\n");
+	}
+	/* place text at beginning of line before host point */
+	qline = t.qh;
+	while(qline>0 && t.r[qline-1] != '\n')
+		qline--;
+
+	rp = runefmtstrflush(&f);
+	nr = runestrlen(rp);
+
+	q0 = t.q0;
+	q0 += insert(rp, nr, qline, 0) - qline;
+	free(rp);
+	t.q0 = q0+nr;
+	t.q1 = q0+nr;
+	updatesel();
+}
+
+Rune*
+namecomplete(void)
+{
+	int nstr, npath;
+	Rune *rp, *path, *str;
+	Completion *c;
+	char *s, *dir, *root;
+
+	/* control-f: filename completion; works back to white space or / */
+	if(t.q0<t.nr && t.r[t.q0]>' ')	/* must be at end of word */
+		return nil;
+	nstr = windfilewidth(t.q0, TRUE);
+	str = runemalloc(nstr);
+	runemove(str, t.r+(t.q0-nstr), nstr);
+	npath = windfilewidth(t.q0-nstr, FALSE);
+	path = runemalloc(npath);
+	runemove(path, t.r+(t.q0-nstr-npath), npath);
+	rp = nil;
+
+	/* is path rooted? if not, we need to make it relative to window path */
+	if(npath>0 && path[0]=='/'){
+		dir = malloc(UTFmax*npath+1);
+		sprint(dir, "%.*S", npath, path);
+	}else{
+		if(strcmp(wdir, "") == 0)
+			root = ".";
+		else
+			root = wdir;
+		dir = malloc(strlen(root)+1+UTFmax*npath+1);
+		sprint(dir, "%s/%.*S", root, npath, path);
+	}
+	dir = cleanname(dir);
+
+	s = smprint("%.*S", nstr, str);
+	c = complete(dir, s);
+	free(s);
+	if(c == nil)
+		goto Return;
+
+	if(!c->advance)
+		showcandidates(c);
+
+	if(c->advance)
+		rp = runesmprint("%s", c->string);
+
+  Return:
+	freecompletion(c);
+	free(dir);
+	free(path);
+	free(str);
+	return rp;
+}
+
 void
 key(Rune r)
 {
-	uint sig;
+	Rune *rp;
+	int nr;
 
 	if(r == 0)
 		return;
-	if(r==SCROLLKEY){	/* scroll key */
+	switch(r){
+	case Kpgup:
+		setorigin(backnl(t.org, t.f->maxlines*2/3), 1);
+		return;
+	case Kpgdown:
 		setorigin(line2q(t.f->maxlines*2/3), 1);
 		if(t.qh<=t.org+t.f->nchars)
 			consread();
 		return;
-	}else if(r == BACKSCROLLKEY){
-		setorigin(backnl(t.org, t.f->maxlines*2/3), 1);
+	case Kup:
+		setorigin(backnl(t.org, t.f->maxlines/3), 1);
 		return;
-	}else if(r == CUT){
+	case Kdown:
+		setorigin(line2q(t.f->maxlines/3), 1);
+		if(t.qh<=t.org+t.f->nchars)
+			consread();
+		return;
+	case Kleft:
+		if(t.q0 > 0){
+			t.q0--;
+			t.q1 = t.q0;
+			updatesel();
+			show(t.q0);
+		}
+		return;
+	case Kright:
+		if(t.q1 < t.nr){
+			t.q1++;
+			t.q0 = t.q1;
+			updatesel();
+			show(t.q1);
+		}
+		return;
+	case Khome:
+		show(0);
+		return;
+	case Kend:
+	case 0x05:
+		show(t.nr);
+		return;
+	case CUT:
 		snarf();
 		cut();
 		if(scrolling)
 			show(t.q0);
 		return;
-	}else if(r == COPY){
+	case COPY:
 		snarf();
 		if(scrolling)
 			show(t.q0);
 		return;
-	}else if(r == PASTE){
+	case PASTE:
 		snarfupdate();
 		paste(t.snarf, t.nsnarf, 0);
 		if(scrolling)
@@ -661,19 +812,21 @@
 	snarf();
 
 	switch(r) {
+	case 0x03:	/* ^C: send interrupt */
 	case 0x7F:	/* DEL: send interrupt */
 		t.qh = t.q0 = t.q1 = t.nr;
 		show(t.q0);
-		goto Default;
-fprint(2, "send interrupt to %d group\n", rcpid);
-#ifdef TIOCSIG
-		sig = 2; /* SIGINT */
-		if(ioctl(rcfd[0], TIOCSIG, &sig) < 0)
-			fprint(2, "sending interrupt: %r\n");
-#else
-		postnote(PNGROUP, rcpid, "interrupt");
-#endif
+		write(rcfd, "\x7F", 1);
 		break;
+	case 0x06:	/* ^F: file name completion */
+	case Kins:		/* Insert: file name completion */
+		rp = namecomplete();
+		if(rp == nil)
+			return;
+		nr = runestrlen(rp);
+		paste(rp, nr, 1);
+		free(rp);
+		return;
 	case 0x08:	/* ^H: erase character */
 	case 0x15:	/* ^U: erase line */
 	case 0x17:	/* ^W: erase word */
@@ -682,7 +835,6 @@
 		cut();
 		break;
 	default:
-	Default:
 		paste(&r, 1, 1);
 		break;
 	}
@@ -773,7 +925,7 @@
 		}
 		/* take out control-d when not doing a zero length write */
 		n = p-buf;
-		if(write(rcfd[1], buf, n) < 0)
+		if(write(rcfd, buf, n) < 0)
 			exits(0);
 /*		mallocstats(); */
 	}
@@ -833,7 +985,6 @@
 void
 runewrite(Rune *r, int n)
 {
-	uint m;
 	int i;
 	uint initial;
 	uint q0, q1;
@@ -896,37 +1047,7 @@
 		updatesel();
 	}
 
-	if(t.nr>HiWater && t.qh>=t.org){
-		m = HiWater-LoWater;
-		if(m > t.org);
-			m = t.org;
-		t.org -= m;
-		t.qh -= m;
-		if(t.q0 > m)
-			t.q0 -= m;
-		else
-			t.q0 = 0;
-		if(t.q1 > m)
-			t.q1 -= m;
-		else
-			t.q1 = 0;
-		t.nr -= m;
-		runemove(t.r, t.r+m, t.nr);
-	}
-	t.r = runerealloc(t.r, t.nr+n);
-	runemove(t.r+t.qh+n, t.r+t.qh, t.nr-t.qh);
-	runemove(t.r+t.qh, r, n);
-	t.nr += n;
-	if(t.qh < t.org)
-		t.org += n;
-	else if(t.qh <= t.f->nchars+t.org)
-		frinsert(t.f, r, r+n, t.qh-t.org);
-	if (t.qh <= t.q0)
-		t.q0 += n;
-	if (t.qh <= t.q1)
-		t.q1 += n;
-	t.qh += n;
-	updatesel();
+	insert(r, n, t.qh, 1);
 }
 
 
@@ -1009,12 +1130,83 @@
 	putsnarf(sbuf);
 }
 
+uint
+min(uint x, uint y)
+{
+	if(x < y)
+		return x;
+	return y;
+}
+
+uint
+max(uint x, uint y)
+{
+	if(x > y)
+		return x;
+	return y;
+}
+
+uint
+insert(Rune *r, int n, uint q0, int hostwrite)
+{
+	uint m;
+
+	if(n == 0)
+		return q0;
+	if(t.nr+n>HiWater && q0>=t.org && q0>=t.qh){
+		m = min(HiWater-LoWater, min(t.org, t.qh));
+		t.org -= m;
+		t.qh -= m;
+		if(t.q0 > m)
+			t.q0 -= m;
+		else
+			t.q0 = 0;
+		if(t.q1 > m)
+			t.q1 -= m;
+		else
+			t.q1 = 0;
+		t.nr -= m;
+		runemove(t.r, t.r+m, t.nr);
+		q0 -= m;
+	}
+	if(t.nr+n > t.maxr){
+		/*
+		 * Minimize realloc breakage:
+		 *	Allocate at least MinWater
+		 * 	Double allocation size each time
+		 *	But don't go much above HiWater
+		 */
+		m = max(min(2*(t.nr+n), HiWater), t.nr+n)+MinWater;
+		if(m > HiWater)
+			m = max(HiWater+MinWater, t.nr+n);
+		if(m > t.maxr){
+			t.r = runerealloc(t.r, m);
+			t.maxr = m;
+		}
+	}
+	runemove(t.r+q0+n, t.r+q0, t.nr-q0);
+	runemove(t.r+q0, r, n);
+	t.nr += n;
+	/* if output touches, advance selection, not qh; works best for keyboard and output */
+	if(q0 <= t.q1)
+		t.q1 += n;
+	if(q0 <= t.q0)
+		t.q0 += n;
+	if(q0 < t.qh || (q0==t.qh && hostwrite))
+		t.qh += n;
+	else
+		consread();
+	if(q0 < t.org)
+		t.org += n;
+	else if(q0 <= t.org+t.f->nchars)
+		frinsert(t.f, r, r+n, q0-t.org);
+	return q0;
+}
+
 void
 paste(Rune *r, int n, int advance)
 {
 	Rune *rbuf;
-	uint m;
-	uint q0;
 
 	if(rawon && t.q0==t.nr){
 		addraw(r, n);
@@ -1024,6 +1216,7 @@
 	cut();
 	if(n == 0)
 		return;
+
 	/*
 	 * if this is a button2 execute then we might have been passed
 	 * runes inside the buffer.  must save them before realloc.
@@ -1035,36 +1228,7 @@
 		r = rbuf;
 	}
 
-	if(t.nr>HiWater && t.q0>=t.org && t.q0>=t.qh){
-		m = HiWater-LoWater;
-		if(m > t.org)
-			m = t.org;
-		if(m > t.qh);
-			m = t.qh;
-		t.org -= m;
-		t.qh -= m;
-		t.q0 -= m;
-		t.q1 -= m;
-		t.nr -= m;
-		runemove(t.r, t.r+m, t.nr);
-	}
-
-	t.r = runerealloc(t.r, t.nr+n);
-	q0 = t.q0;
-	runemove(t.r+q0+n, t.r+q0, t.nr-q0);
-	runemove(t.r+q0, r, n);
-	t.nr += n;
-	if(q0 < t.qh)
-		t.qh += n;
-	else
-		consread();
-	if(q0 < t.org)
-		t.org += n;
-	else if(q0 <= t.f->nchars+t.org)
-		frinsert(t.f, r, r+n, q0-t.org);
-	if(advance)
-		t.q0 += n;
-	t.q1 += n;
+	insert(r, n, t.q0, 0);
 	updatesel();
 	free(rbuf);
 }
@@ -1322,61 +1486,6 @@
 }
 
 void
-rcstart(int fd[2], int argc, char **argv)
-{
-	int pid;
-	char *xargv[3];
-	char slave[256];
-	int sfd;
-
-	if(argc == 0){
-		argc = 2;
-		argv = xargv;
-		argv[0] = getenv("SHELL");
-		if(argv[0] == 0)
-			argv[0] = "rc";
-		argv[1] = "-i";
-		argv[2] = 0;
-	}
-	/*
-	 * fd0 is slave (tty), fd1 is master (pty)
-	 */
-	fd[0] = fd[1] = -1;
-	if(getpts(fd, slave) < 0)
-		fprint(2, "getpts: %r\n");
-
-	switch(pid = fork()) {
-	case 0:
-		putenv("TERM", "9term");
-		close(fd[1]);
-		setsid();
-//		tcsetpgrp(0, pid);
-		sfd = open(slave, ORDWR);
-		if(sfd < 0)
-			fprint(2, "open %s: %r\n", slave);
-		if(ioctl(sfd, TIOCSCTTY, 0) < 0)
-			fprint(2, "ioctl TIOCSCTTY: %r\n");
-//		ioctl(sfd, I_PUSH, "ptem");
-//		ioctl(sfd, I_PUSH, "ldterm");
-		dup(sfd, 0);
-		dup(sfd, 1);
-		dup(sfd, 2);
-		system("stty tabs -onlcr -echo erase ^h intr ^?");
-		execvp(argv[0], argv);
-		fprint(2, "exec %s failed: %r\n", argv[0]);
-		_exits("oops");
-		break;
-	case -1:
-		fatal("proc failed: %r");
-		break;
-	}
-	close(fd[0]);
-	fd[0] = fd[1];
-
-	rcpid = pid;
-}
-
-void
 tcheck(void)
 {
 	Frame *f;
@@ -1421,7 +1530,7 @@
 			freeimage(scrx);
 		scrx = allocimage(display, Rect(0, 0, 32, r.max.y), screen->chan, 1, DPaleyellow);
 		if(scrx == 0)
-			fatal("scroll balloc");
+			sysfatal("scroll balloc");
 	}
 	r1.min.x = 0;
 	r1.max.x = Dx(r);
@@ -1525,16 +1634,11 @@
 	char *p;
 	int i, p0, n;
 	char cbuf[100];
-	char *w;
 
-	if(getchildwd(rcpid, childwdir, sizeof childwdir) == 0)
-		w = childwdir;
-	else
-		w = wdir;
 	pm = malloc(sizeof(Plumbmsg));
 	pm->src = strdup("9term");
 	pm->dst = 0;
-	pm->wdir = strdup(w);
+	pm->wdir = strdup(wdir);
 	pm->type = strdup("text");
 	pm->data = nil;
 	if(q1 > q0)
diff --git a/src/cmd/9term/9term.h b/src/cmd/9term/9term.h
index 4e8d61f..57a8359 100644
--- a/src/cmd/9term/9term.h
+++ b/src/cmd/9term/9term.h
@@ -1,19 +1,4 @@
-#include <u.h>
-#include <libc.h>
-#include <ctype.h>
-#include <draw.h>
-#include <thread.h>
-#include <mouse.h>
-#include <cursor.h>
-#include <keyboard.h>
-#include <frame.h>
-#include <plumb.h>
-#include <termios.h>
-#include <sys/termios.h>
-#ifdef __linux__
-#include <pty.h>
-#endif
-
-extern int getchildwd(int, char*, int);
 extern int getpts(int[], char*);
-
+extern int childpty(int[], char*);
+extern void updatewinsize(int, int, int, int);
+extern int rcfd[];
diff --git a/src/cmd/9term/FreeBSD.c b/src/cmd/9term/FreeBSD.c
index e8b894d..7022d4d 100644
--- a/src/cmd/9term/FreeBSD.c
+++ b/src/cmd/9term/FreeBSD.c
@@ -1,17 +1,43 @@
 #include "9term.h"
+#include <termios.h>
+#include <sys/termios.h>
 #include <libutil.h>
 
 int
-getchildwd(int pid, char *wdir, int bufn)
-{
-	USED(pid);
-	USED(wdir);
-	USED(bufn);
-	return -1;
-}
-
-int
 getpts(int fd[], char *slave)
 {
 	return openpty(&fd[1], &fd[0], slave, 0, 0);
 }
+
+int
+childpty(int fd[], char *slave)
+{
+	int sfd;
+
+	close(fd[1]);
+	setsid();
+	sfd = open(slave, ORDWR);
+	if(sfd < 0)
+		sysfatal("open %s: %r\n", slave);
+	if(ioctl(sfd, TIOCSCTTY, 0) < 0)
+		fprint(2, "ioctl TIOCSCTTY: %r\n");
+	return sfd;
+}
+
+struct winsize ows;
+
+void
+updatewinsize(int row, int col, int dx, int dy)
+{
+	struct winsize ws;
+
+	ws.ws_row = row;
+	ws.ws_col = col;
+	ws.ws_xpixel = dx;
+	ws.ws_ypixel = dy;
+	if(ws.ws_row != ows.ws_row || ws.ws_col != ows.ws_col)
+	if(ioctl(rcfd[0], TIOCSWINSZ, &ws) < 0)
+		fprint(2, "ioctl: %r\n");
+	ows = ws;
+}
+
diff --git a/src/cmd/9term/Linux.c b/src/cmd/9term/Linux.c
index 7dd2237..872417e 100644
--- a/src/cmd/9term/Linux.c
+++ b/src/cmd/9term/Linux.c
@@ -1,22 +1,46 @@
+#include <u.h>
+#include <termios.h>
+#include <sys/termios.h>
+#include <pty.h>
+#include <libc.h>
 #include "9term.h"
 
 int
-getchildwd(int pid, char *wdir, int bufn)
-{
-	char path[256];
-	int n;
-
-	snprint(path, sizeof path, "/proc/%d/cwd", pid);
-	n = readlink(path, wdir, bufn);
-	if(n < 0)
-		return -1;
-	wdir[n] = '\0';
-	return 0;
-}
-
-int
 getpts(int fd[], char *slave)
 {
 	openpty(&fd[1], &fd[0], slave, 0, 0);
 	return 0;
 }
+
+int
+childpty(int fd[], char *slave)
+{
+	int sfd;
+
+	close(fd[1]);
+	setsid();
+	sfd = open(slave, ORDWR);
+	if(sfd < 0)
+		sysfatal("open %s: %r\n", slave);
+	if(ioctl(sfd, TIOCSCTTY, 0) < 0)
+		fprint(2, "ioctl TIOCSCTTY: %r\n");
+	return sfd;
+}
+
+struct winsize ows;
+
+void
+updatewinsize(int row, int col, int dx, int dy)
+{
+	struct winsize ws;
+
+	ws.ws_row = row;
+	ws.ws_col = col;
+	ws.ws_xpixel = dx;
+	ws.ws_ypixel = dy;
+	if(ws.ws_row != ows.ws_row || ws.ws_col != ows.ws_col)
+	if(ioctl(rcfd[0], TIOCSWINSZ, &ws) < 0)
+		fprint(2, "ioctl: %r\n");
+	ows = ws;
+}
+
diff --git a/src/cmd/9term/SunOS.c b/src/cmd/9term/SunOS.c
index 6a37ab3..d7db9fc 100644
--- a/src/cmd/9term/SunOS.c
+++ b/src/cmd/9term/SunOS.c
@@ -1,21 +1,6 @@
 #include "9term.h"
-
-int
-getchildwd(int pid, char *wdir, int bufn)
-{
-	char path[256];
-	char cwd[256];
-
-	if(getcwd(cwd, sizeof cwd) < 0)
-		return -1;
-	snprint(path, sizeof path, "/proc/%d/cwd", pid);
-	if(chdir(path) < 0)
-		return -1;
-	if(getcwd(wdir, bufn) < 0)
-		return -1;
-	chdir(cwd);
-	return 0;
-}
+#include <termios.h>
+#include <sys/termios.h>
 
 int
 getpts(int fd[], char *slave)
@@ -28,3 +13,21 @@
 	fd[0] = open(slave, OREAD);
 	return 0;
 }
+
+struct winsize ows;
+
+void
+updatewinsize(int row, int col, int dx, int dy)
+{
+	struct winsize ws;
+
+	ws.ws_row = row;
+	ws.ws_col = col;
+	ws.ws_xpixel = dx;
+	ws.ws_ypixel = dy;
+	if(ws.ws_row != ows.ws_row || ws.ws_col != ows.ws_col)
+	if(ioctl(rcfd[0], TIOCSWINSZ, &ws) < 0)
+		fprint(2, "ioctl: %r\n");
+	ows = ws;
+}
+
diff --git a/src/cmd/9term/mkfile b/src/cmd/9term/mkfile
index d0d5ca7..8701ad9 100644
--- a/src/cmd/9term/mkfile
+++ b/src/cmd/9term/mkfile
@@ -1,15 +1,15 @@
 PLAN9=../../..
 <$PLAN9/src/mkhdr
 
-TARG=9term
+TARG=9term win
 
 OFILES=\
-	9term.$O\
+	rcstart.$O\
 	$SYSNAME.$O\
 
-SHORTLIB=frame draw plumb fs mux thread 9
+SHORTLIB=complete frame draw plumb fs mux thread 9
 
-<$PLAN9/src/mkone
+<$PLAN9/src/mkmany
 
 LDFLAGS=-L$X11/lib -lX11 -lutil
 
diff --git a/src/cmd/9term/rcstart.c b/src/cmd/9term/rcstart.c
new file mode 100644
index 0000000..7596bc4
--- /dev/null
+++ b/src/cmd/9term/rcstart.c
@@ -0,0 +1,51 @@
+#include <u.h>
+#include <libc.h>
+#include "term.h"
+
+int
+rcstart(int argc, char **argv, int *pfd)
+{
+	int pid;
+	int fd[2];
+	char *xargv[3];
+	char slave[256];
+	int sfd;
+
+	if(argc == 0){
+		argc = 2;
+		argv = xargv;
+		argv[0] = getenv("SHELL");
+		if(argv[0] == 0)
+			argv[0] = "rc";
+		argv[1] = "-i";
+		argv[2] = 0;
+	}
+	/*
+	 * fd0 is slave (tty), fd1 is master (pty)
+	 */
+	fd[0] = fd[1] = -1;
+	if(getpts(fd, slave) < 0)
+		fprint(2, "getpts: %r\n");
+
+
+	switch(pid = fork()) {
+	case 0:
+		putenv("TERM", "9term");
+		sfd = childpty(fd, slave);
+		dup(sfd, 0);
+		dup(sfd, 1);
+		dup(sfd, 2);
+		system("stty tabs -onlcr -echo erase ^h intr ^?");
+		execvp(argv[0], argv);
+		fprint(2, "exec %s failed: %r\n", argv[0]);
+		_exits("oops");
+		break;
+	case -1:
+		sysfatal("proc failed: %r");
+		break;
+	}
+	close(fd[0]);
+	*pfd = fd[1];
+	return pid;
+}
+
diff --git a/src/cmd/9term/term.h b/src/cmd/9term/term.h
new file mode 100644
index 0000000..a608b7e
--- /dev/null
+++ b/src/cmd/9term/term.h
@@ -0,0 +1,5 @@
+extern int getpts(int[], char*);
+extern int childpty(int[], char*);
+extern void updatewinsize(int, int, int, int);
+extern int rcfd;
+extern int rcstart(int, char*[], int*);
diff --git a/src/cmd/win.c b/src/cmd/9term/win.c
similarity index 94%
rename from src/cmd/win.c
rename to src/cmd/9term/win.c
index 7f15f05..95d84a3 100644
--- a/src/cmd/win.c
+++ b/src/cmd/9term/win.c
@@ -3,6 +3,7 @@
 #include <thread.h>
 #include <fcall.h>
 #include <fs.h>
+#include "term.h"
 
 #define	EVENTSIZE	256
 #define	STACK	32768
@@ -51,11 +52,11 @@
 int	ntyper;
 int	ntypebreak;
 int	debug;
+int	rcfd;
+
 char *name;
 
 char **prog;
-int p[2];
-Channel *cpid;
 Channel *cwait;
 int pid = -1;
 
@@ -124,9 +125,11 @@
 
 	prog = argv;
 
-	if(argc > 0)
+	if(argc > 0){
 		name = argv[0];
-	else
+		argc--;
+		argv++;
+	}else
 		name = "gnot";
 
 	threadnotify(nopipes, 1);
@@ -156,14 +159,9 @@
 */
 	fsunmount(fs);
 
-	if(pipe(p) < 0)
-		sysfatal("pipe: %r");
-
-	cpid = chancreate(sizeof(ulong), 1);
 	cwait = threadwaitchan();
 	threadcreate(waitthread, nil, STACK);
-	threadcreate(runproc, nil, STACK);
-	pid = recvul(cpid);
+	pid = rcstart(argc, argv, &rcfd);
 	if(pid == -1)
 		sysfatal("exec failed");
 
@@ -179,30 +177,6 @@
 	stdinproc(nil);
 }
 
-char *shell[] = { "rc", "-i", 0 };
-void
-runproc(void *v)
-{
-	int fd[3];
-	char *sh;
-
-	USED(v);
-
-	fd[0] = p[1];
-//	fd[1] = bodyfd;
-//	fd[2] = bodyfd;
-	fd[1] = p[1];
-	fd[2] = p[1];
-
-	if(prog[0] == nil){
-		prog = shell;
-		if((sh = getenv("SHELL")) != nil)
-			shell[0] = sh;
-	}
-	threadexec(cpid, fd, prog[0], prog);
-	threadexits(nil);
-}
-
 void
 error(char *s)
 {
@@ -329,7 +303,7 @@
 	Fid *efd = eventfd;
 	Fid *dfd = datafd;
 	Fid *afd = addrfd;
-	int fd0 = p[0];
+	int fd0 = rcfd;
 	Event e, e2, e3, e4;
 
 	USED(v);
@@ -426,7 +400,7 @@
 void
 stdoutproc(void *v)
 {
-	int fd1 = p[0];
+	int fd1 = rcfd;
 	Fid *afd = addrfd;
 	Fid *dfd = datafd;
 	int n, m, w, npart;
@@ -439,6 +413,8 @@
 	buf = malloc(8192+UTFmax+1);
 	npart = 0;
 	for(;;){
+		/* Let typing have a go -- maybe there's a rubout waiting. */
+		yield();
 		n = threadread(fd1, buf+npart, 8192);
 		if(n < 0)
 			error(nil);
@@ -556,7 +532,7 @@
 	for(i=0; i<nb; i+=w){
 		w = chartorune(&r, b+i);
 		if((r==0x7F||r==3) && c=='K'){
-			postnote(PNGROUP, pid, "interrupt");
+			write(rcfd, "\x7F", 1);
 			/* toss all typing */
 			q.p += ntyper+nr;
 			ntypebreak = 0;
diff --git a/src/cmd/acme/acme.c b/src/cmd/acme/acme.c
index d4b4cad..9636bea 100644
--- a/src/cmd/acme/acme.c
+++ b/src/cmd/acme/acme.c
@@ -161,7 +161,8 @@
 	cerr = chancreate(sizeof(char*), 0);
 	cedit = chancreate(sizeof(int), 0);
 	cexit = chancreate(sizeof(int), 0);
-	if(cwait==nil || ccommand==nil || ckill==nil || cxfidalloc==nil || cxfidfree==nil || cerr==nil || cexit==nil){
+	cwarn = chancreate(sizeof(void*), 1);
+	if(cwait==nil || ccommand==nil || ckill==nil || cxfidalloc==nil || cxfidfree==nil || cerr==nil || cexit==nil || cwarn==nil){
 		fprint(2, "acme: can't create initial channels: %r\n");
 		exits("channels");
 	}
@@ -251,7 +252,7 @@
 
 	w = coladd(c, nil, nil, -1);
 	cvttorunes(s, strlen(s), rb, &nb, &nr, nil);
-	rs = cleanrname((Runestr){rb, nr});
+	rs = cleanrname(runestr(rb, nr));
 	winsetname(w, rs.r, rs.nr);
 	textload(&w->body, 0, s, 1);
 	w->body.file->mod = FALSE;
@@ -403,7 +404,6 @@
 				winlock(t->w, 'K');
 				wincommit(t->w, t);
 				winunlock(t->w);
-				flushwarnings(1);
 				flushimage(display, 1);
 			}
 			alts[KTimer].c = nil;
@@ -430,7 +430,6 @@
 			}
 			if(nbrecv(keyboardctl->c, &r) > 0)
 				goto casekeyboard;
-			flushwarnings(1);
 			flushimage(display, 1);
 			break;
 		}
@@ -447,7 +446,7 @@
 	Plumbmsg *pm;
 	Mouse m;
 	char *act;
-	enum { MResize, MMouse, MPlumb, NMALT };
+	enum { MResize, MMouse, MPlumb, MWarnings, NMALT };
 	static Alt alts[NMALT+1];
 
 	USED(v);
@@ -461,11 +460,18 @@
 	alts[MPlumb].c = cplumb;
 	alts[MPlumb].v = &pm;
 	alts[MPlumb].op = CHANRCV;
+	alts[MWarnings].c = cwarn;
+	alts[MWarnings].v = nil;
+	alts[MWarnings].op = CHANRCV;
 	if(cplumb == nil)
 		alts[MPlumb].op = CHANNOP;
 	alts[NMALT].op = CHANEND;
 	
 	for(;;){
+		qlock(&row.lk);
+		flushwarnings();
+		qunlock(&row.lk);
+		flushimage(display, 1);
 		switch(alt(alts)){
 		case MResize:
 			if(getwindow(display, Refnone) < 0)
@@ -473,8 +479,6 @@
 			draw(screen, screen->r, display->white, nil, ZP);
 			scrlresize();
 			rowresize(&row, screen->clipr);
-			flushwarnings(1);
-			flushimage(display, 1);
 			break;
 		case MPlumb:
 			if(strcmp(pm->type, "text") == 0){
@@ -484,10 +488,10 @@
 				else if(strcmp(act, "showdata")==0)
 					plumbshow(pm);
 			}
-			flushwarnings(1);
-			flushimage(display, 1);
 			plumbfree(pm);
 			break;
+		case MWarnings:
+			break;
 		case MMouse:
 			/*
 			 * Make a copy so decisions are consistent; mousectl changes
@@ -570,8 +574,6 @@
 				goto Continue;
 			}
     Continue:
-			flushwarnings(0);
-			flushimage(display, 1);
 			qunlock(&row.lk);
 			break;
 		}
diff --git a/src/cmd/acme/dat.h b/src/cmd/acme/dat.h
index 94cfa38..338a1ff 100644
--- a/src/cmd/acme/dat.h
+++ b/src/cmd/acme/dat.h
@@ -543,5 +543,6 @@
 Channel	*cexit;		/* chan(int) */
 Channel	*cerr;		/* chan(char*) */
 Channel	*cedit;		/* chan(int) */
+Channel	*cwarn;		/* chan(void*)[1] (really chan(unit)[1]) */
 
 #define	STACK	32768
diff --git a/src/cmd/acme/ecmd.c b/src/cmd/acme/ecmd.c
index 0dfae90..7ccb942 100644
--- a/src/cmd/acme/ecmd.c
+++ b/src/cmd/acme/ecmd.c
@@ -268,7 +268,7 @@
 			runemove(n, dir.r, dir.nr);
 			n[dir.nr] = '/';
 			runemove(n+dir.nr+1, r, nn);
-			rs = cleanrname((Runestr){n, dir.nr+1+nn});
+			rs = cleanrname(runestr(n, dir.nr+1+nn));
 		}
 		w = lookfile(rs.r, rs.nr);
 		if(w == nil){
diff --git a/src/cmd/acme/exec.c b/src/cmd/acme/exec.c
index 74f9f47..0e3389d 100644
--- a/src/cmd/acme/exec.c
+++ b/src/cmd/acme/exec.c
@@ -25,6 +25,7 @@
 void	get(Text*, Text*, Text*, int, int, Rune*, int);
 void	id(Text*, Text*, Text*, int, int, Rune*, int);
 void	incl(Text*, Text*, Text*, int, int, Rune*, int);
+void	indent(Text*, Text*, Text*, int, int, Rune*, int);
 void	xkill(Text*, Text*, Text*, int, int, Rune*, int);
 void	local(Text*, Text*, Text*, int, int, Rune*, int);
 void	look(Text*, Text*, Text*, int, int, Rune*, int);
@@ -58,6 +59,7 @@
 static Rune LGet[] = { 'G', 'e', 't', 0 };
 static Rune LID[] = { 'I', 'D', 0 };
 static Rune LIncl[] = { 'I', 'n', 'c', 'l', 0 };
+static Rune LIndent[] = { 'I', 'n', 'd', 'e', 'n', 't', 0 };
 static Rune LKill[] = { 'K', 'i', 'l', 'l', 0 };
 static Rune LLoad[] = { 'L', 'o', 'a', 'd', 0 };
 static Rune LLocal[] = { 'L', 'o', 'c', 'a', 'l', 0 };
@@ -87,6 +89,7 @@
 	{ LGet,		get,		FALSE,	TRUE,	XXX		},
 	{ LID,		id,		FALSE,	XXX,		XXX		},
 	{ LIncl,		incl,		FALSE,	XXX,		XXX		},
+	{ LIndent,		indent,	FALSE,	XXX,		XXX		},
 	{ LKill,		xkill,		FALSE,	XXX,		XXX		},
 	{ LLoad,		dump,	FALSE,	FALSE,	XXX		},
 	{ LLocal,		local,	FALSE,	XXX,		XXX		},
@@ -1443,7 +1446,6 @@
 	goto Fail;
 
 Hard:
-
 	/*
 	 * ugly: set path = (. $cputype /bin)
 	 * should honor $path if unusual.
diff --git a/src/cmd/acme/fns.h b/src/cmd/acme/fns.h
index 9fba7d7..c164bb3 100644
--- a/src/cmd/acme/fns.h
+++ b/src/cmd/acme/fns.h
@@ -69,6 +69,7 @@
 void	fsysinit(void);
 Mntdir*	fsysmount(Rune*, int, Rune**, int);
 void		fsysdelid(Mntdir*);
+void		fsysincid(Mntdir*);
 Xfid*		respond(Xfid*, Fcall*, char*);
 int		rxcompile(Rune*);
 int		rgetc(void*, uint);
@@ -86,9 +87,11 @@
 Rune*	skipbl(Rune*, int, int*);
 Rune*	findbl(Rune*, int, int*);
 char*	edittext(Window*, int, Rune*, int);
-void		flushwarnings(int);
+void		flushwarnings(void);
 void		startplumbing(void);
 
+Runestr	runestr(Rune*, uint);
+
 #define	runemalloc(a)		(Rune*)emalloc((a)*sizeof(Rune))
 #define	runerealloc(a, b)	(Rune*)erealloc((a), (b)*sizeof(Rune))
 #define	runemove(a, b, c)	memmove((a), (b), (c)*sizeof(Rune))
diff --git a/src/cmd/acme/fsys.c b/src/cmd/acme/fsys.c
index f178f86..af4255c 100644
--- a/src/cmd/acme/fsys.c
+++ b/src/cmd/acme/fsys.c
@@ -37,22 +37,25 @@
 static	Xfid*	fsysstat(Xfid*, Fid*);
 static	Xfid*	fsyswstat(Xfid*, Fid*);
 
-Xfid* 	(*fcall[Tmax])(Xfid*, Fid*) =
+Xfid* 	(*fcall[Tmax])(Xfid*, Fid*);
+
+static void
+initfcall(void)
 {
-	[Tflush]	= fsysflush,
-	[Tversion]	= fsysversion,
-	[Tauth]	= fsysauth,
-	[Tattach]	= fsysattach,
-	[Twalk]	= fsyswalk,
-	[Topen]	= fsysopen,
-	[Tcreate]	= fsyscreate,
-	[Tread]	= fsysread,
-	[Twrite]	= fsyswrite,
-	[Tclunk]	= fsysclunk,
-	[Tremove]= fsysremove,
-	[Tstat]	= fsysstat,
-	[Twstat]	= fsyswstat,
-};
+	fcall[Tflush]	= fsysflush;
+	fcall[Tversion]	= fsysversion;
+	fcall[Tauth]	= fsysauth;
+	fcall[Tattach]	= fsysattach;
+	fcall[Twalk]	= fsyswalk;
+	fcall[Topen]	= fsysopen;
+	fcall[Tcreate]	= fsyscreate;
+	fcall[Tread]	= fsysread;
+	fcall[Twrite]	= fsyswrite;
+	fcall[Tclunk]	= fsysclunk;
+	fcall[Tremove]= fsysremove;
+	fcall[Tstat]	= fsysstat;
+	fcall[Twstat]	= fsyswstat;
+}
 
 char Eperm[] = "permission denied";
 char Eexist[] = "file does not exist";
@@ -113,6 +116,7 @@
 	int p[2];
 	char *u;
 
+	initfcall();
 	if(pipe(p) < 0)
 		error("can't create pipe");
 	if(post9pservice(p[0], "acme") < 0)
@@ -187,6 +191,14 @@
 }
 
 void
+fsysincid(Mntdir *m)
+{
+	qlock(&mnt.lk);
+	m->ref++;
+	qunlock(&mnt.lk);
+}
+
+void
 fsysdelid(Mntdir *idm)
 {
 	Mntdir *m, *prev;
@@ -331,7 +343,7 @@
 			m->ref++;
 			break;
 		}
-	if(m == nil){
+	if(m == nil && x->fcall.aname[0]){
 		snprint(buf, sizeof buf, "unknown id '%s' in attach", x->fcall.aname);
 		sendp(cerr, estrdup(buf));
 	}
diff --git a/src/cmd/acme/look.c b/src/cmd/acme/look.c
index f6c4d4e..7023382 100644
--- a/src/cmd/acme/look.c
+++ b/src/cmd/acme/look.c
@@ -259,7 +259,7 @@
 	}
 	cvttorunes(name, strlen(name), rb, &nb, &nr, nil);
 	free(p);
-	rs = cleanrname((Runestr){rb, nr});
+	rs = cleanrname(runestr(rb, nr));
 	winsetname(w, rs.r, rs.nr);
 	r = runemalloc(m->ndata);
 	cvttorunes(m->data, m->ndata, r, &nb, &nr, nil);
@@ -385,13 +385,13 @@
 	n = access(a, 0);
 	free(a);
 	if(n < 0)
-		return (Runestr){nil, 0};
+		return runestr(nil, 0);
 	r = runemalloc(m+1+nfile);
 	runemove(r, dir, m);
 	runemove(r+m, Lslash, 1);
 	runemove(r+m+1, file, nfile);
 	free(file);
-	return cleanrname((Runestr){r, m+1+nfile});
+	return cleanrname(runestr(r, m+1+nfile));
 }
 
 static	Rune	*objdir;
@@ -442,7 +442,7 @@
 	return file;
 
     Rescue:
-	return (Runestr){r, n};
+	return runestr(r, n);
 }
 
 Runestr
@@ -475,11 +475,11 @@
 		goto Rescue;
 	runemove(b+slash+1, r, n);
 	free(r);
-	return cleanrname((Runestr){b, slash+1+n});
+	return cleanrname(runestr(b, slash+1+n));
 
     Rescue:
 	free(b);
-	tmp = (Runestr){r, n};
+	tmp = runestr(r, n);
 	if(r)
 		return cleanrname(tmp);
 	return tmp;
diff --git a/src/cmd/acme/text.c b/src/cmd/acme/text.c
index 8bdf0b7..c0cd7ec 100644
--- a/src/cmd/acme/text.c
+++ b/src/cmd/acme/text.c
@@ -578,7 +578,7 @@
 		path[i] = textreadc(t, q++);
 	/* is path rooted? if not, we need to make it relative to window path */
 	if(npath>0 && path[0]=='/')
-		dir = (Runestr){path, npath};
+		dir = runestr(path, npath);
 	else{
 		dir = dirname(t, nil, 0);
 		if(dir.nr + 1 + npath > nelem(tmp)){
diff --git a/src/cmd/acme/util.c b/src/cmd/acme/util.c
index de71107..a7307e0 100644
--- a/src/cmd/acme/util.c
+++ b/src/cmd/acme/util.c
@@ -14,6 +14,16 @@
 static	Point		prevmouse;
 static	Window	*mousew;
 
+Runestr
+runestr(Rune *r, uint n)
+{
+	Runestr rs;
+
+	rs.r = r;
+	rs.nr = n;
+	return rs;
+}
+
 void
 cvttorunes(char *p, int n, Rune *r, int *nb, int *nr, int *nulls)
 {
@@ -133,12 +143,17 @@
 	}
 	warn = emalloc(sizeof(Warning));
 	warn->next = warnings;
+	warn->md = md;
+	if(md)
+		fsysincid(md);
 	warnings = warn;
 	bufinsert(&warn->buf, 0, r, nr);
+	nbsendp(cwarn, 0);
 }
 
+/* called while row is locked */
 void
-flushwarnings(int dolock)
+flushwarnings(void)
 {
 	Warning *warn, *next;
 	Window *w;
@@ -146,8 +161,6 @@
 	int owner, nr, q0, n;
 	Rune *r;
 
-	if(dolock)
-		qlock(&row.lk);
 	if(row.ncol == 0){	/* really early error */
 		rowinit(&row, screen->clipr);
 		rowadd(&row, nil, -1);
@@ -189,11 +202,11 @@
 		winunlock(w);
 		bufclose(&warn->buf);
 		next = warn->next;
+		if(warn->md)
+			fsysdelid(warn->md);
 		free(warn);
 	}
 	warnings = nil;
-	if(dolock)
-		qunlock(&row.lk);
 }
 
 void
diff --git a/src/cmd/acme/xfid.c b/src/cmd/acme/xfid.c
index b3bef2c..839308e 100644
--- a/src/cmd/acme/xfid.c
+++ b/src/cmd/acme/xfid.c
@@ -543,7 +543,6 @@
 	}
 	if(w)
 		winunlock(w);
-	flushwarnings(1);
 }
 
 void
@@ -814,7 +813,6 @@
 			qunlock(&row.lk);
 			goto Rescue;
 		}
-		flushwarnings(0);
 		qunlock(&row.lk);
 
 	}
@@ -1032,7 +1030,6 @@
 			b[n++] = '\n';
 		}
 	}
-	flushwarnings(0);
 	qunlock(&row.lk);
 	off = x->fcall.offset;
 	cnt = x->fcall.count;
diff --git a/src/cmd/dict/dict.c b/src/cmd/dict/dict.c
index 8cbf6aa..a3ddd89 100644
--- a/src/cmd/dict/dict.c
+++ b/src/cmd/dict/dict.c
@@ -119,6 +119,8 @@
 		line = malloc(strlen(p)+5);
 		sprint(line, "/%s/P\n", p);
 	}
+	dict->path = unsharp(dict->path);
+	dict->indexpath = unsharp(dict->indexpath);
 	bdict = Bopen(dict->path, OREAD);
 	if(!bdict) {
 		err("can't open dictionary %s", dict->path);
diff --git a/src/cmd/diff/diff.h b/src/cmd/diff/diff.h
index 8e56765..c809ae8 100644
--- a/src/cmd/diff/diff.h
+++ b/src/cmd/diff/diff.h
@@ -1,3 +1,5 @@
+#define stdout bstdout
+
 char mode;			/* '\0', 'e', 'f', 'h' */
 char bflag;			/* ignore multiple and trailing blanks */
 char rflag;			/* recurse down directory trees */
diff --git a/src/cmd/diff/main.c b/src/cmd/diff/main.c
index 43eb6dd..00f69a3 100644
--- a/src/cmd/diff/main.c
+++ b/src/cmd/diff/main.c
@@ -26,6 +26,8 @@
 done(int status)
 {
 	rmtmpfiles();
+Bflush(&stdout);
+Bterm(&stdout);
 	switch(status)
 	{
 	case 0:
diff --git a/src/cmd/fortune.c b/src/cmd/fortune.c
index f3acfca..4aa895f 100644
--- a/src/cmd/fortune.c
+++ b/src/cmd/fortune.c
@@ -4,8 +4,8 @@
 
 #define index findex
 char choice[2048];
-char index[] = "/sys/games/lib/fortunes.index";
-char fortunes[] = "/sys/games/lib/fortunes";
+char *index = "#9/lib/fortunes.index";
+char *fortunes = "#9/lib/fortunes";
 
 #define lrand rand
 
@@ -21,6 +21,9 @@
 	Dir *fbuf, *ixbuf;
 	Biobuf *f, g;
 
+	index = unsharp(index);
+	fortunes = unsharp(index);
+
 	newindex = 0;
 	oldindex = 0;
 	ix = offs = 0;
@@ -55,6 +58,7 @@
 		}
 	}
 	if(oldindex){
+		srand(getpid());
 		seek(ix, lrand()%(ixbuf->length/sizeof(offs))*sizeof(offs), 0);
 		read(ix, off, sizeof(off));
 		Bseek(f, off[0]|(off[1]<<8)|(off[2]<<16)|(off[3]<<24), 0);
diff --git a/src/cmd/grep/comp.c b/src/cmd/grep/comp.c
new file mode 100644
index 0000000..4a3f3e8
--- /dev/null
+++ b/src/cmd/grep/comp.c
@@ -0,0 +1,277 @@
+#include	"grep.h"
+
+/*
+ * incremental compiler.
+ * add the branch c to the
+ * state s.
+ */
+void
+increment(State *s, int c)
+{
+	int i;
+	State *t, **tt;
+	Re *re1, *re2;
+
+	nfollow = 0;
+	gen++;
+	matched = 0;
+	for(i=0; i<s->count; i++)
+		fol1(s->re[i], c);
+	qsort(follow, nfollow, sizeof(*follow), fcmp);
+	for(tt=&state0; t = *tt;) {
+		if(t->count > nfollow) {
+			tt = &t->linkleft;
+			goto cont;
+		}
+		if(t->count < nfollow) {
+			tt = &t->linkright;
+			goto cont;
+		}
+		for(i=0; i<nfollow; i++) {
+			re1 = t->re[i];
+			re2 = follow[i];
+			if(re1 > re2) {
+				tt = &t->linkleft;
+				goto cont;
+			}
+			if(re1 < re2) {
+				tt = &t->linkright;
+				goto cont;
+			}
+		}
+		if(!!matched && !t->match) {
+			tt = &t->linkleft;
+			goto cont;
+		}
+		if(!matched && !!t->match) {
+			tt = &t->linkright;
+			goto cont;
+		}
+		s->next[c] = t;
+		return;
+	cont:;
+	}
+
+	t = sal(nfollow);
+	*tt = t;
+	for(i=0; i<nfollow; i++) {
+		re1 = follow[i];
+		t->re[i] = re1;
+	}
+	s->next[c] = t;
+	t->match = matched;
+}
+
+int
+fcmp(const void *va, const void *vb)
+{
+	Re **aa, **bb;
+	Re *a, *b;
+
+	aa = (Re**)va;
+	bb = (Re**)vb;
+	a = *aa;
+	b = *bb;
+	if(a > b)
+		return 1;
+	if(a < b)
+		return -1;
+	return 0;
+}
+
+void
+fol1(Re *r, int c)
+{
+	Re *r1;
+
+loop:
+	if(r->gen == gen)
+		return;
+	if(nfollow >= maxfollow)
+		error("nfollow");
+	r->gen = gen;
+	switch(r->type) {
+	default:
+		error("fol1");
+
+	case Tcase:
+		if(c >= 0 && c < 256)
+		if(r1 = r->cases[c])
+			follow[nfollow++] = r1;
+		if(r = r->next)
+			goto loop;
+		break;
+
+	case Talt:
+	case Tor:
+		fol1(r->alt, c);
+		r = r->next;
+		goto loop;
+
+	case Tbegin:
+		if(c == '\n' || c == Cbegin)
+			follow[nfollow++] = r->next;
+		break;
+
+	case Tend:
+		if(c == '\n')
+			matched = 1;
+		break;
+
+	case Tclass:
+		if(c >= r->lo && c <= r->hi)
+			follow[nfollow++] = r->next;
+		break;
+	}
+}
+
+Rune	tab1[] =
+{
+	0x007f,
+	0x07ff,
+};
+Rune	tab2[] =
+{
+	0x003f,
+	0x0fff,
+};
+
+Re2
+rclass(Rune p0, Rune p1)
+{
+	char xc0[6], xc1[6];
+	int i, n, m;
+	Re2 x;
+
+	if(p0 > p1)
+		return re2char(0xff, 0xff);	// no match
+
+	/*
+	 * bust range into same length
+	 * character sequences
+	 */
+	for(i=0; i<nelem(tab1); i++) {
+		m = tab1[i];
+		if(p0 <= m && p1 > m)
+			return re2or(rclass(p0, m), rclass(m+1, p1));
+	}
+
+	/*
+	 * bust range into part of a single page
+	 * or into full pages
+	 */
+	for(i=0; i<nelem(tab2); i++) {
+		m = tab2[i];
+		if((p0 & ~m) != (p1 & ~m)) {
+			if((p0 & m) != 0)
+				return re2or(rclass(p0, p0|m), rclass((p0|m)+1, p1));
+			if((p1 & m) != m)
+				return re2or(rclass(p0, (p1&~m)-1), rclass(p1&~m, p1));
+		}
+	}
+
+	n = runetochar(xc0, &p0);
+	i = runetochar(xc1, &p1);
+	if(i != n)
+		error("length");
+
+	x = re2char(xc0[0], xc1[0]);
+	for(i=1; i<n; i++)
+		x = re2cat(x, re2char(xc0[i], xc1[i]));
+	return x;
+}
+
+int
+pcmp(const void *va, const void *vb)
+{
+	int n;
+	Rune *a, *b;
+
+	a = (Rune*)va;
+	b = (Rune*)vb;
+
+	n = a[0] - b[0];
+	if(n)
+		return n;
+	return a[1] - b[1];
+}
+
+/*
+ * convert character chass into
+ * run-pair ranges of matches.
+ * this is 10646/utf specific and
+ * needs to be changed for some
+ * other input character set.
+ * this is the key to a fast
+ * regular search of characters
+ * by looking at sequential bytes.
+ */
+Re2
+re2class(char *s)
+{
+	Rune pairs[200], *p, *q, ov;
+	int nc;
+	Re2 x;
+
+	nc = 0;
+	if(*s == '^') {
+		nc = 1;
+		s++;
+	}
+
+	p = pairs;
+	s += chartorune(p, s);
+	for(;;) {
+		if(*p == '\\')
+			s += chartorune(p, s);
+		if(*p == 0)
+			break;
+		p[1] = *p;
+		p += 2;
+		s += chartorune(p, s);
+		if(*p != '-')
+			continue;
+		s += chartorune(p, s);
+		if(*p == '\\')
+			s += chartorune(p, s);
+		if(*p == 0)
+			break;
+		p[-1] = *p;
+		s += chartorune(p, s);
+	}
+	*p = 0;
+	qsort(pairs, (p-pairs)/2, 2*sizeof(*pairs), pcmp);
+
+	q = pairs;
+	for(p=pairs+2; *p; p+=2) {
+		if(p[0] > p[1])
+			continue;
+		if(p[0] > q[1] || p[1] < q[0]) {
+			q[2] = p[0];
+			q[3] = p[1];
+			q += 2;
+			continue;
+		}
+		if(p[0] < q[0])
+			q[0] = p[0];
+		if(p[1] > q[1])
+			q[1] = p[1];
+	}
+	q[2] = 0;
+
+	p = pairs;
+	if(nc) {
+		x = rclass(0, p[0]-1);
+		ov = p[1]+1;
+		for(p+=2; *p; p+=2) {
+			x = re2or(x, rclass(ov, p[0]-1));
+			ov = p[1]+1;
+		}
+		x = re2or(x, rclass(ov, 0xffff));
+	} else {
+		x = rclass(p[0], p[1]);
+		for(p+=2; *p; p+=2)
+			x = re2or(x, rclass(p[0], p[1]));
+	}
+	return x;
+}
diff --git a/src/cmd/grep/grep.h b/src/cmd/grep/grep.h
new file mode 100644
index 0000000..8445df5
--- /dev/null
+++ b/src/cmd/grep/grep.h
@@ -0,0 +1,125 @@
+#include	<u.h>
+#include	<libc.h>
+#include	<bio.h>
+
+#ifndef	EXTERN
+#define	EXTERN	extern
+#endif
+
+typedef	struct	Re	Re;
+typedef	struct	Re2	Re2;
+typedef	struct	State	State;
+
+struct	State
+{
+	int	count;
+	int	match;
+	Re**	re;
+	State*	linkleft;
+	State*	linkright;
+	State*	next[256];
+};
+struct	Re2
+{
+	Re*	beg;
+	Re*	end;
+};
+struct	Re
+{
+	uchar	type;
+	ushort	gen;
+	union
+	{
+		Re*	alt;	/* Talt */
+		Re**	cases;	/* case */
+		struct		/* class */
+		{
+			Rune	lo;
+			Rune	hi;
+		};	
+		Rune	val;	/* char */
+	};
+	Re*	next;
+};
+
+enum
+{
+	Talt		= 1,
+	Tbegin,
+	Tcase,
+	Tclass,
+	Tend,
+	Tor,
+
+	Caselim		= 7,
+	Nhunk		= 1<<16,
+	Cbegin		= 0x10000,
+	Flshcnt		= (1<<9)-1,
+
+	Cflag		= 1<<0,
+	Hflag		= 1<<1,
+	Iflag		= 1<<2,
+	Llflag		= 1<<3,
+	LLflag		= 1<<4,
+	Nflag		= 1<<5,
+	Sflag		= 1<<6,
+	Vflag		= 1<<7,
+	Bflag		= 1<<8
+};
+
+EXTERN	union
+{
+	char	string[16*1024];
+	struct
+	{
+		/*
+		 * if a line requires multiple reads, we keep shifting
+		 * buf down into pre and then do another read into
+		 * buf.  so you'll get the last 16-32k of the matching line.
+		 * if pre were smaller than buf you'd get a suffix of the
+		 * line with a hole cut out.
+		 */
+		uchar	pre[16*1024];	/* to save to previous '\n' */
+		uchar	buf[16*1024];	/* input buffer */
+	};
+} u;
+
+EXTERN	char	*filename;
+EXTERN	Biobuf	bout;
+EXTERN	char	flags[256];
+EXTERN	Re**	follow;
+EXTERN	ushort	gen;
+EXTERN	char*	input;
+EXTERN	long	lineno;
+EXTERN	int	literal;
+EXTERN	int	matched;
+EXTERN	long	maxfollow;
+EXTERN	long	nfollow;
+EXTERN	int	peekc;
+EXTERN	Biobuf*	rein;
+EXTERN	State*	state0;
+EXTERN	Re2	topre;
+
+extern	Re*	addcase(Re*);
+extern	void	appendnext(Re*, Re*);
+extern	void	error(char*);
+extern	int	fcmp(const void*, const void*); 	/* (Re**, Re**) */
+extern	void	fol1(Re*, int);
+extern	int	getrec(void);
+extern	void	increment(State*, int);
+#define initstate grepinitstate
+extern	State*	initstate(Re*);
+extern	void*	mal(int);
+extern	void	patchnext(Re*, Re*);
+extern	Re*	ral(int);
+extern	Re2	re2cat(Re2, Re2);
+extern	Re2	re2class(char*);
+extern	Re2	re2or(Re2, Re2);
+extern	Re2	re2char(int, int);
+extern	Re2	re2star(Re2);
+extern	State*	sal(int);
+extern	int	search(char*, int);
+extern	void	str2top(char*);
+extern	int	yyparse(void);
+extern	void	reprint(char*, Re*);
+extern	void	yyerror(char*, ...);
diff --git a/src/cmd/grep/grep.y b/src/cmd/grep/grep.y
new file mode 100644
index 0000000..94a744c
--- /dev/null
+++ b/src/cmd/grep/grep.y
@@ -0,0 +1,226 @@
+%{
+#include	"grep.h"
+%}
+
+%union
+{
+	int	val;
+	char*	str;
+	Re2	re;
+}
+
+%type	<re>	expr prog
+%type	<re>	expr0 expr1 expr2 expr3 expr4
+%token	<str>	LCLASS
+%token	<val>	LCHAR
+%token		LLPAREN LRPAREN LALT LSTAR LPLUS LQUES
+%token		LBEGIN LEND LDOT LBAD LNEWLINE
+%%
+
+prog:
+	expr newlines
+	{
+		$$.beg = ral(Tend);
+		$$.end = $$.beg;
+		$$ = re2cat(re2star(re2or(re2char(0x00, '\n'-1), re2char('\n'+1, 0xff))), $$);
+		$$ = re2cat($1, $$);
+		$$ = re2cat(re2star(re2char(0x00, 0xff)), $$);
+		topre = $$;
+	}
+
+expr:
+	expr0
+|	expr newlines expr0
+	{
+		$$ = re2or($1, $3);
+	}
+
+expr0:
+	expr1
+|	LSTAR { literal = 1; } expr1
+	{
+		$$ = $3;
+	}
+
+expr1:
+	expr2
+|	expr1 LALT expr2
+	{
+		$$ = re2or($1, $3);
+	}
+
+expr2:
+	expr3
+|	expr2 expr3
+	{
+		$$ = re2cat($1, $2);
+	}
+
+expr3:
+	expr4
+|	expr3 LSTAR
+	{
+		$$ = re2star($1);
+	}
+|	expr3 LPLUS
+	{
+		$$.beg = ral(Talt);
+		patchnext($1.end, $$.beg);
+		$$.beg->alt = $1.beg;
+		$$.end = $$.beg;
+		$$.beg = $1.beg;
+	}
+|	expr3 LQUES
+	{
+		$$.beg = ral(Talt);
+		$$.beg->alt = $1.beg;
+		$$.end = $1.end;
+		appendnext($$.end,  $$.beg);
+	}
+
+expr4:
+	LCHAR
+	{
+		$$.beg = ral(Tclass);
+		$$.beg->lo = $1;
+		$$.beg->hi = $1;
+		$$.end = $$.beg;
+	}
+|	LBEGIN
+	{
+		$$.beg = ral(Tbegin);
+		$$.end = $$.beg;
+	}
+|	LEND
+	{
+		$$.beg = ral(Tend);
+		$$.end = $$.beg;
+	}
+|	LDOT
+	{
+		$$ = re2class("^\n");
+	}
+|	LCLASS
+	{
+		$$ = re2class($1);
+	}
+|	LLPAREN expr1 LRPAREN
+	{
+		$$ = $2;
+	}
+
+newlines:
+	LNEWLINE
+|	newlines LNEWLINE
+%%
+
+void
+yyerror(char *e, ...)
+{
+	if(filename)
+		fprint(2, "grep: %s:%ld: %s\n", filename, lineno, e);
+	else
+		fprint(2, "grep: %s\n", e);
+	exits("syntax");
+}
+
+long
+yylex(void)
+{
+	char *q, *eq;
+	int c, s;
+
+	if(peekc) {
+		s = peekc;
+		peekc = 0;
+		return s;
+	}
+	c = getrec();
+	if(literal) {
+		if(c != 0 && c != '\n') {
+			yylval.val = c;
+			return LCHAR;
+		}
+		literal = 0;
+	}
+	switch(c) {
+	default:
+		yylval.val = c;
+		s = LCHAR;
+		break;
+	case '\\':
+		c = getrec();
+		yylval.val = c;
+		s = LCHAR;
+		if(c == '\n')
+			s = LNEWLINE;
+		break;
+	case '[':
+		goto getclass;
+	case '(':
+		s = LLPAREN;
+		break;
+	case ')':
+		s = LRPAREN;
+		break;
+	case '|':
+		s = LALT;
+		break;
+	case '*':
+		s = LSTAR;
+		break;
+	case '+':
+		s = LPLUS;
+		break;
+	case '?':
+		s = LQUES;
+		break;
+	case '^':
+		s = LBEGIN;
+		break;
+	case '$':
+		s = LEND;
+		break;
+	case '.':
+		s = LDOT;
+		break;
+	case 0:
+		peekc = -1;
+	case '\n':
+		s = LNEWLINE;
+		break;
+	}
+	return s;
+
+getclass:
+	q = u.string;
+	eq = q + nelem(u.string) - 5;
+	c = getrec();
+	if(c == '^') {
+		q[0] = '^';
+		q[1] = '\n';
+		q[2] = '-';
+		q[3] = '\n';
+		q += 4;
+		c = getrec();
+	}
+	for(;;) {
+		if(q >= eq)
+			error("class too long");
+		if(c == ']' || c == 0)
+			break;
+		if(c == '\\') {
+			*q++ = c;
+			c = getrec();
+			if(c == 0)
+				break;
+		}
+		*q++ = c;
+		c = getrec();
+	}
+	*q = 0;
+	if(c == 0)
+		return LBAD;
+	yylval.str = u.string;
+	return LCLASS;
+}
diff --git a/src/cmd/grep/main.c b/src/cmd/grep/main.c
new file mode 100644
index 0000000..f2a6e04
--- /dev/null
+++ b/src/cmd/grep/main.c
@@ -0,0 +1,260 @@
+#define	EXTERN
+#include	"grep.h"
+
+char *validflags = "bchiLlnsv";
+void
+usage(void)
+{
+	fprint(2, "usage: grep [-%s] [-f file] [-e expr] [file ...]\n", validflags);
+	exits("usage");
+}
+
+void
+main(int argc, char *argv[])
+{
+	int i, status;
+
+	ARGBEGIN {
+	default:
+		if(utfrune(validflags, ARGC()) == nil)
+			usage();
+		flags[ARGC()]++;
+		break;
+
+	case 'e':
+		flags['e']++;
+		lineno = 0;
+		str2top(ARGF());
+		break;
+
+	case 'f':
+		flags['f']++;
+		filename = ARGF();
+		rein = Bopen(filename, OREAD);
+		if(rein == 0) {
+			fprint(2, "grep: can't open %s: %r\n", filename);
+			exits("open");
+		}
+		lineno = 1;
+		str2top(filename);
+		break;
+	} ARGEND
+
+	if(flags['f'] == 0 && flags['e'] == 0) {
+		if(argc <= 0)
+			usage();
+		str2top(argv[0]);
+		argc--;
+		argv++;
+	}
+
+	follow = mal(maxfollow*sizeof(*follow));
+	state0 = initstate(topre.beg);
+
+	Binit(&bout, 1, OWRITE);
+	switch(argc) {
+	case 0:
+		status = search(0, 0);
+		break;
+	case 1:
+		status = search(argv[0], 0);
+		break;
+	default:
+		status = 0;
+		for(i=0; i<argc; i++)
+			status |= search(argv[i], Hflag);
+		break;
+	}
+	if(status)
+		exits(0);
+	exits("no matches");
+}
+
+int
+search(char *file, int flag)
+{
+	State *s, *ns;
+	int c, fid, eof, nl, empty;
+	long count, lineno, n;
+	uchar *elp, *lp, *bol;
+
+	if(file == 0) {
+		file = "stdin";
+		fid = 0;
+		flag |= Bflag;
+	} else
+		fid = open(file, OREAD);
+
+	if(fid < 0) {
+		fprint(2, "grep: can't open %s: %r\n", file);
+		return 0;
+	}
+
+	if(flags['b'])
+		flag ^= Bflag;		/* dont buffer output */
+	if(flags['c'])
+		flag |= Cflag;		/* count */
+	if(flags['h'])
+		flag &= ~Hflag;		/* do not print file name in output */
+	if(flags['i'])
+		flag |= Iflag;		/* fold upper-lower */
+	if(flags['l'])
+		flag |= Llflag;		/* print only name of file if any match */
+	if(flags['L'])
+		flag |= LLflag;		/* print only name of file if any non match */
+	if(flags['n'])
+		flag |= Nflag;		/* count only */
+	if(flags['s'])
+		flag |= Sflag;		/* status only */
+	if(flags['v'])
+		flag |= Vflag;		/* inverse match */
+
+	s = state0;
+	lineno = 0;
+	count = 0;
+	eof = 0;
+	empty = 1;
+	nl = 0;
+	lp = u.buf;
+	bol = lp;
+
+loop0:
+	n = lp-bol;
+	if(n > sizeof(u.pre))
+		n = sizeof(u.pre);
+	memmove(u.buf-n, bol, n);
+	bol = u.buf-n;
+	n = read(fid, u.buf, sizeof(u.buf));
+	/* if file has no final newline, simulate one to emit matches to last line */
+	if(n > 0) {
+		empty = 0;
+		nl = u.buf[n-1]=='\n';
+	} else {
+		if(n < 0){
+			fprint(2, "grep: read error on %s: %r\n", file);
+			return count != 0;
+		}
+		if(!eof && !nl && !empty) {
+			u.buf[0] = '\n';
+			n = 1;
+			eof = 1;
+		}
+	}
+	if(n <= 0) {
+		close(fid);
+		if(flag & Cflag) {
+			if(flag & Hflag)
+				Bprint(&bout, "%s:", file);
+			Bprint(&bout, "%ld\n", count);
+		}
+		if(((flag&Llflag) && count != 0) || ((flag&LLflag) && count == 0))
+			Bprint(&bout, "%s\n", file);
+		Bflush(&bout);
+		return count != 0;
+	}
+	lp = u.buf;
+	elp = lp+n;
+	if(flag & Iflag)
+		goto loopi;
+
+/*
+ * normal character loop
+ */
+loop:
+	c = *lp;
+	ns = s->next[c];
+	if(ns == 0) {
+		increment(s, c);
+		goto loop;
+	}
+//	if(flags['2'])
+//		if(s->match)
+//			print("%d: %.2x**\n", s, c);
+//		else
+//			print("%d: %.2x\n", s, c);
+	lp++;
+	s = ns;
+	if(c == '\n') {
+		lineno++;
+		if(!!s->match == !(flag&Vflag)) {
+			count++;
+			if(flag & (Cflag|Sflag|Llflag|LLflag))
+				goto cont;
+			if(flag & Hflag)
+				Bprint(&bout, "%s:", file);
+			if(flag & Nflag)
+				Bprint(&bout, "%ld: ", lineno);
+			/* suppress extra newline at EOF unless we are labeling matches with file name */
+			Bwrite(&bout, bol, lp-bol-(eof && !(flag&Hflag)));
+			if(flag & Bflag)
+				Bflush(&bout);
+		}
+		if((lineno & Flshcnt) == 0)
+			Bflush(&bout);
+	cont:
+		bol = lp;
+	}
+	if(lp != elp)
+		goto loop;
+	goto loop0;
+
+/*
+ * character loop for -i flag
+ * for speed
+ */
+loopi:
+	c = *lp;
+	if(c >= 'A' && c <= 'Z')
+		c += 'a'-'A';
+	ns = s->next[c];
+	if(ns == 0) {
+		increment(s, c);
+		goto loopi;
+	}
+	lp++;
+	s = ns;
+	if(c == '\n') {
+		lineno++;
+		if(!!s->match == !(flag&Vflag)) {
+			count++;
+			if(flag & (Cflag|Sflag|Llflag|LLflag))
+				goto conti;
+			if(flag & Hflag)
+				Bprint(&bout, "%s:", file);
+			if(flag & Nflag)
+				Bprint(&bout, "%ld: ", lineno);
+			/* suppress extra newline at EOF unless we are labeling matches with file name */
+			Bwrite(&bout, bol, lp-bol-(eof && !(flag&Hflag)));
+			if(flag & Bflag)
+				Bflush(&bout);
+		}
+		if((lineno & Flshcnt) == 0)
+			Bflush(&bout);
+	conti:
+		bol = lp;
+	}
+	if(lp != elp)
+		goto loopi;
+	goto loop0;
+}
+
+State*
+initstate(Re *r)
+{
+	State *s;
+	int i;
+
+	addcase(r);
+	if(flags['1'])
+		reprint("r", r);
+	nfollow = 0;
+	gen++;
+	fol1(r, Cbegin);
+	follow[nfollow++] = r;
+	qsort(follow, nfollow, sizeof(*follow), fcmp);
+
+	s = sal(nfollow);
+	for(i=0; i<nfollow; i++)
+		s->re[i] = follow[i];
+	return s;
+}
diff --git a/src/cmd/grep/mkfile b/src/cmd/grep/mkfile
new file mode 100644
index 0000000..fd17abd
--- /dev/null
+++ b/src/cmd/grep/mkfile
@@ -0,0 +1,20 @@
+PLAN9=../../..
+<$PLAN9/src/mkhdr
+
+# Calling this grep breaks a LOT.  Like egrep on Linux.
+# And probably configure.
+
+TARG=9grep
+HFILES=\
+	grep.h\
+
+OFILES=\
+	y.tab.$O\
+	main.$O\
+	comp.$O\
+	sub.$O\
+
+YFILES=grep.y\
+
+SHORTLIB=bio 9
+<$PLAN9/src/mkone
diff --git a/src/cmd/grep/sub.c b/src/cmd/grep/sub.c
new file mode 100644
index 0000000..be2acee
--- /dev/null
+++ b/src/cmd/grep/sub.c
@@ -0,0 +1,317 @@
+#include	"grep.h"
+
+void*
+mal(int n)
+{
+	static char *s;
+	static int m = 0;
+	void *v;
+
+	n = (n+3) & ~3;
+	if(m < n) {
+		if(n > Nhunk) {
+			v = sbrk(n);
+			memset(v, 0, n);
+			return v;
+		}
+		s = sbrk(Nhunk);
+		m = Nhunk;
+	}
+	v = s;
+	s += n;
+	m -= n;
+	memset(v, 0, n);
+	return v;
+}
+
+State*
+sal(int n)
+{
+	State *s;
+
+	s = mal(sizeof(*s));
+//	s->next = mal(256*sizeof(*s->next));
+	s->count = n;
+	s->re = mal(n*sizeof(*state0->re));
+	return s;
+}
+
+Re*
+ral(int type)
+{
+	Re *r;
+
+	r = mal(sizeof(*r));
+	r->type = type;
+	maxfollow++;
+	return r;
+}
+
+void
+error(char *s)
+{
+	fprint(2, "grep: internal error: %s\n", s);
+	exits(s);
+}
+
+int
+countor(Re *r)
+{
+	int n;
+
+	n = 0;
+loop:
+	switch(r->type) {
+	case Tor:
+		n += countor(r->alt);
+		r = r->next;
+		goto loop;
+	case Tclass:
+		return n + r->hi - r->lo + 1;
+	}
+	return n;
+}
+
+Re*
+oralloc(int t, Re *r, Re *b)
+{
+	Re *a;
+
+	if(b == 0)
+		return r;
+	a = ral(t);
+	a->alt = r;
+	a->next = b;
+	return a;
+}
+
+void
+case1(Re *c, Re *r)
+{
+	int n;
+
+loop:
+	switch(r->type) {
+	case Tor:
+		case1(c, r->alt);
+		r = r->next;
+		goto loop;
+
+	case Tclass:	/* add to character */
+		for(n=r->lo; n<=r->hi; n++)
+			c->cases[n] = oralloc(Tor, r->next, c->cases[n]);
+		break;
+
+	default:	/* add everything unknown to next */
+		c->next = oralloc(Talt, r, c->next);
+		break;
+	}
+}
+
+Re*
+addcase(Re *r)
+{
+	int i, n;
+	Re *a;
+
+	if(r->gen == gen)
+		return r;
+	r->gen = gen;
+	switch(r->type) {
+	default:
+		error("addcase");
+
+	case Tor:
+		n = countor(r);
+		if(n >= Caselim) {
+			a = ral(Tcase);
+			a->cases = mal(256*sizeof(*a->cases));
+			case1(a, r);
+			for(i=0; i<256; i++)
+				if(a->cases[i]) {
+					r = a->cases[i];
+					if(countor(r) < n)
+						a->cases[i] = addcase(r);
+				}
+			return a;
+		}
+		return r;
+
+	case Talt:
+		r->next = addcase(r->next);
+		r->alt = addcase(r->alt);
+		return r;
+
+	case Tbegin:
+	case Tend:
+	case Tclass:
+		return r;
+	}
+}
+
+void
+str2top(char *p)
+{
+	Re2 oldtop;
+
+	oldtop = topre;
+	input = p;
+	topre.beg = 0;
+	topre.end = 0;
+	yyparse();
+	gen++;
+	if(topre.beg == 0)
+		yyerror("syntax");
+	if(oldtop.beg)
+		topre = re2or(oldtop, topre);
+}
+
+void
+appendnext(Re *a, Re *b)
+{
+	Re *n;
+
+	while(n = a->next)
+		a = n;
+	a->next = b;
+}
+
+void
+patchnext(Re *a, Re *b)
+{
+	Re *n;
+
+	while(a) {
+		n = a->next;
+		a->next = b;
+		a = n;
+	}
+}
+
+int
+getrec(void)
+{
+	int c;
+
+	if(flags['f']) {
+		c = Bgetc(rein);
+		if(c <= 0)
+			return 0;
+	} else
+		c = *input++ & 0xff;
+	if(flags['i'] && c >= 'A' && c <= 'Z')
+		c += 'a'-'A';
+	if(c == '\n')
+		lineno++;
+	return c;
+}
+
+Re2
+re2cat(Re2 a, Re2 b)
+{
+	Re2 c;
+
+	c.beg = a.beg;
+	c.end = b.end;
+	patchnext(a.end, b.beg);
+	return c;
+}
+
+Re2
+re2star(Re2 a)
+{
+	Re2 c;
+
+	c.beg = ral(Talt);
+	c.beg->alt = a.beg;
+	patchnext(a.end, c.beg);
+	c.end = c.beg;
+	return c;
+}
+
+Re2
+re2or(Re2 a, Re2 b)
+{
+	Re2 c;
+
+	c.beg = ral(Tor);
+	c.beg->alt = b.beg;
+	c.beg->next = a.beg;
+	c.end = b.end;
+	appendnext(c.end,  a.end);
+	return c;
+}
+
+Re2
+re2char(int c0, int c1)
+{
+	Re2 c;
+
+	c.beg = ral(Tclass);
+	c.beg->lo = c0 & 0xff;
+	c.beg->hi = c1 & 0xff;
+	c.end = c.beg;
+	return c;
+}
+
+void
+reprint1(Re *a)
+{
+	int i, j;
+
+loop:
+	if(a == 0)
+		return;
+	if(a->gen == gen)
+		return;
+	a->gen = gen;
+	print("%p: ", a);
+	switch(a->type) {
+	default:
+		print("type %d\n", a->type);
+		error("print1 type");
+
+	case Tcase:
+		print("case ->%p\n", a->next);
+		for(i=0; i<256; i++)
+			if(a->cases[i]) {
+				for(j=i+1; j<256; j++)
+					if(a->cases[i] != a->cases[j])
+						break;
+				print("	[%.2x-%.2x] ->%p\n", i, j-1, a->cases[i]);
+				i = j-1;
+			}
+		for(i=0; i<256; i++)
+			reprint1(a->cases[i]);
+		break;
+
+	case Tbegin:
+		print("^ ->%p\n", a->next);
+		break;
+
+	case Tend:
+		print("$ ->%p\n", a->next);
+		break;
+
+	case Tclass:
+		print("[%.2x-%.2x] ->%p\n", a->lo, a->hi, a->next);
+		break;
+
+	case Tor:
+	case Talt:
+		print("| %p ->%p\n", a->alt, a->next);
+		reprint1(a->alt);
+		break;
+	}
+	a = a->next;
+	goto loop;
+}
+
+void
+reprint(char *s, Re *r)
+{
+	print("%s:\n", s);
+	gen++;
+	reprint1(r);
+	print("\n\n");
+}
diff --git a/src/cmd/mkfile b/src/cmd/mkfile
index 3ad7ea7..292c051 100644
--- a/src/cmd/mkfile
+++ b/src/cmd/mkfile
@@ -3,11 +3,11 @@
 
 TARG=`ls *.c | sed 's/\.c//'`
 LDFLAGS=$LDFLAGS
-SHORTLIB=sec fs mux regexp9 thread bio 9
+SHORTLIB=mach sec fs mux regexp9 thread bio 9
 
 <$PLAN9/src/mkmany
 
-BUGGERED='CVS|9term|faces|factotum|htmlfmt|mk|rio|upas|vac|venti'
+BUGGERED='CVS|faces|factotum|htmlfmt|mk|upas|vac|venti'
 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 4776d12..33a4458 100644
--- a/src/cmd/plumb/fsys.c
+++ b/src/cmd/plumb/fsys.c
@@ -97,6 +97,7 @@
 static int	ndir = NQID;
 
 static int		srvfd;
+#define clock plumbclock	/* SunOS name clash */
 static int		clock;
 static Fid		*fids[Nhash];
 static QLock	readlock;
diff --git a/src/cmd/plumb/plumber.c b/src/cmd/plumb/plumber.c
index 80a57af..bf7afa3 100644
--- a/src/cmd/plumb/plumber.c
+++ b/src/cmd/plumb/plumber.c
@@ -54,9 +54,10 @@
 		error("can't initialize $user or $home: %r");
 	if(plumbfile == nil){
 		sprint(buf, "%s/lib/plumbing", home);
-		if(access(buf, 0) < 0)
-			sprint(buf, "#9/plumb/initial.plumbing");
-		plumbfile = estrdup(buf);
+		if(access(buf, 0) >= 0)
+			plumbfile = estrdup(buf);
+		else
+			plumbfile = unsharp("#9/plumb/initial.plumbing");
 	}
 
 	fd = open(plumbfile, OREAD);
diff --git a/src/cmd/plumb/rules.c b/src/cmd/plumb/rules.c
index 689edf5..ab27787 100644
--- a/src/cmd/plumb/rules.c
+++ b/src/cmd/plumb/rules.c
@@ -415,7 +415,7 @@
 	fd = open(t, OREAD);
 	if(fd<0 && t[0]!='/' && strncmp(t, "./", 2)!=0 && strncmp(t, "../", 3)!=0){
 		snprint(buf, sizeof buf, "#9/plumb/%s", t);
-		t = buf;
+		t = unsharp(buf);
 		fd = open(t, OREAD);
 	}
 	if(fd < 0)
diff --git a/src/cmd/rc/plan9ish.c b/src/cmd/rc/plan9ish.c
index eb93a4f..6b32a22 100644
--- a/src/cmd/rc/plan9ish.c
+++ b/src/cmd/rc/plan9ish.c
@@ -27,20 +27,10 @@
 char*
 Rcmain(void)
 {
-	return "#9/rcmain";
-/*
-	static char buf[256];
-	char *root;
-
-	root = getenv("PLAN9");
-	if(root == nil)
-		root = "/usr/local/plan9";
-	snprint(buf, sizeof buf, "%s/rcmain", root);
-	return buf;
-*/
+	return unsharp("#9/rcmain");
 }
 
-char Fdprefix[]="#d/";
+char Fdprefix[]="/dev/fd/";
 void execfinit(void);
 void execbind(void);
 void execmount(void);
diff --git a/src/cmd/rio/Imakefile b/src/cmd/rio/Imakefile
new file mode 100644
index 0000000..43a7893
--- /dev/null
+++ b/src/cmd/rio/Imakefile
@@ -0,0 +1,27 @@
+INCLUDES = -I$(TOP)
+DEPLIBS = $(DEPXLIB)
+LOCAL_LIBRARIES = $(XLIB)
+DEFINES = -DSHAPE # -g3 -DDEBUG -DDEBUG_EV
+SRCS = main.c event.c manage.c menu.c client.c grab.c cursor.c error.c color.c
+OBJS = main.o event.o manage.o menu.o client.o grab.o cursor.o error.o color.o
+HFILES = dat.h fns.h patchlevel.h
+MFILES = README 9wm.man Imakefile Makefile.no-imake
+
+ComplexProgramTarget(rio)
+
+bun:
+	bundle $(MFILES) $(SRCS) $(HFILES) >bun
+
+dist:
+	bundle $(MFILES) main.c event.c manage.c >bun1
+	bundle menu.c client.c grab.c cursor.c error.c $(HFILES) >bun2
+
+trout: 9wm.man
+	troff -man 9wm.man >trout
+
+vu: trout
+	xditview trout
+
+clean::
+	$(RM) bun bun[12] trout core
+
diff --git a/src/cmd/rio/color.c b/src/cmd/rio/color.c
index 8c53466..0e3c8d8 100644
--- a/src/cmd/rio/color.c
+++ b/src/cmd/rio/color.c
@@ -8,7 +8,7 @@
 #include "fns.h"
 
 unsigned long
-colorpixel(Display *dpy, int depth, ulong rgb)
+colorpixel(Display *dpy, int depth, unsigned long rgb)
 {
 	int r, g, b;
 
diff --git a/src/cmd/rio/main.c b/src/cmd/rio/main.c
index 0953f1b..0292977 100644
--- a/src/cmd/rio/main.c
+++ b/src/cmd/rio/main.c
@@ -9,6 +9,9 @@
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 #include <X11/Xatom.h>
+#ifdef SHAPE
+#include <X11/extensions/shape.h>
+#endif
 #include "dat.h"
 #include "fns.h"
 #include "patchlevel.h"
@@ -70,12 +73,15 @@
 	int i, background, do_exit, do_restart;
 	char *fname;
 	int shape_event;
+#ifdef SHAPE
+	int dummy;
+#endif
 
 	shape_event = 0;
 	myargv = argv;			/* for restart */
 
 	do_exit = do_restart = 0;
-	background = 1;
+	background = 0;
 	font = 0;
 	fname = 0;
 	for (i = 1; i < argc; i++)
@@ -289,12 +295,11 @@
 	XSync(dpy, False);
 
 	if (background) {
-/*
 		XSetWindowBackgroundPixmap(dpy, s->root, s->root_pixmap);
 		XClearWindow(dpy, s->root);
-*/
+	} else
 		system("xsetroot -solid grey30");
-	}
+
 	s->menuwin = XCreateSimpleWindow(dpy, s->root, 0, 0, 1, 1, 2, colorpixel(dpy, s->depth, 0x88CC88), colorpixel(dpy, s->depth, 0xE9FFE9));
 	s->sweepwin = XCreateSimpleWindow(dpy, s->root, 0, 0, 1, 1, 4, s->red, colorpixel(dpy, s->depth, 0xEEEEEE));
 }
diff --git a/src/cmd/rio/mkfile b/src/cmd/rio/mkfile
index 8881190..b5e8b11 100644
--- a/src/cmd/rio/mkfile
+++ b/src/cmd/rio/mkfile
@@ -1,3 +1,4 @@
+PLAN9=../../..
 <$PLAN9/src/mkhdr
 
 OFILES=\
@@ -18,3 +19,7 @@
 LDFLAGS=-L$X11/lib -lXext -lX11
 
 <$PLAN9/src/mkone
+
+CFLAGS=$CFLAGS -DSHAPE -I$X11/include
+
+
diff --git a/src/cmd/spell/sprog.c b/src/cmd/spell/sprog.c
index e63fbb8..8d5519e 100644
--- a/src/cmd/spell/sprog.c
+++ b/src/cmd/spell/sprog.c
@@ -460,6 +460,9 @@
 	int low;
 	Bits h;
 
+	codefile = unsharp(codefile);
+	brfile = unsharp(brfile);
+
 	Binit(&bin, 0, OREAD);
 	Binit(&bout, 1, OWRITE);
 	for(i=0; c = "aeiouyAEIOUY"[i]; i++)
diff --git a/src/cmd/yacc.c b/src/cmd/yacc.c
index 0ff114b..6614414 100644
--- a/src/cmd/yacc.c
+++ b/src/cmd/yacc.c
@@ -399,7 +399,7 @@
 {
 	int c, i, j;
 
-	finput = Bopen(parser, OREAD);
+	finput = Bopen(unsharp(parser), OREAD);
 	if(finput == 0)
 		error("cannot open parser %s: %r", parser);
 	warray("yyr1", levprd, nprod);
diff --git a/src/lib9/_p9translate.c b/src/lib9/_p9translate.c
index 4eb6eac..84cd65c 100644
--- a/src/lib9/_p9translate.c
+++ b/src/lib9/_p9translate.c
@@ -14,7 +14,7 @@
 };
 
 char*
-_p9translate(char *old)
+plan9translate(char *old)
 {
 	char *new;
 	int i, olen, nlen, len;
@@ -36,7 +36,7 @@
 		len = strlen(old)+nlen-olen;
 		new = malloc(len+1);
 		if(new == nil)
-			return nil;
+			return "<out of memory>";
 		strcpy(new, replace[i].new);
 		strcpy(new+nlen, old+olen);
 		assert(strlen(new) == len);
diff --git a/src/lib9/access.c b/src/lib9/access.c
deleted file mode 100644
index 20b00c3..0000000
--- a/src/lib9/access.c
+++ /dev/null
@@ -1,19 +0,0 @@
-#include <u.h>
-#define NOPLAN9DEFINES
-#include <libc.h>
-
-char *_p9translate(char*);
-
-int
-p9access(char *xname, int what)
-{
-	int ret;
-	char *name;
-
-	if((name = _p9translate(xname)) == nil)
-		return -1;
-	ret = access(name, what);
-	if(name != xname)
-		free(name);
-	return ret;
-}
diff --git a/src/lib9/announce.c b/src/lib9/announce.c
index 9f07bd2..8fdf1d4 100644
--- a/src/lib9/announce.c
+++ b/src/lib9/announce.c
@@ -40,7 +40,8 @@
 	char *net;
 	u32int host;
 	int port, s;
-	int n, sn;
+	int n;
+	socklen_t sn;
 	struct sockaddr_in sa;
 	struct sockaddr_un sun;
 
@@ -72,7 +73,7 @@
 	if((s = socket(AF_INET, proto, 0)) < 0)
 		return -1;
 	sn = sizeof n;
-	if(port && getsockopt(s, SOL_SOCKET, SO_TYPE, (char*)&n, &sn) >= 0
+	if(port && getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0
 	&& n == SOCK_STREAM){
 		n = 1;
 		setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n);
diff --git a/src/lib9/create.c b/src/lib9/create.c
index bdad5f6..97f6e7e 100644
--- a/src/lib9/create.c
+++ b/src/lib9/create.c
@@ -3,16 +3,10 @@
 #include <libc.h>
 #include <sys/stat.h>
 
-extern char *_p9translate(char*);
-
 int
-p9create(char *xpath, int mode, ulong perm)
+p9create(char *path, int mode, ulong perm)
 {
 	int fd, cexec, umode, rclose;
-	char *path;
-
-	if((path = _p9translate(xpath)) == nil)
-		return -1;
 
 	cexec = mode&OCEXEC;
 	rclose = mode&ORCLOSE;
@@ -48,7 +42,5 @@
 		if(rclose)
 			remove(path);
 	}
-	if(path != xpath)
-		free(path);
 	return fd;
 }
diff --git a/src/lib9/ctime.c b/src/lib9/ctime.c
index e9d971b..0782d09 100644
--- a/src/lib9/ctime.c
+++ b/src/lib9/ctime.c
@@ -15,6 +15,7 @@
 char*
 asctime(Tm *t)
 {
+	int i;
 	char *ncp;
 	static char cbuf[30];
 
@@ -32,6 +33,12 @@
 	ct_numb(cbuf+14, t->min+100);
 	ct_numb(cbuf+17, t->sec+100);
 	ncp = t->zone;
+	for(i=0; i<3; i++)
+		if(ncp[i] == 0)
+			break;
+	for(; i<3; i++)
+		ncp[i] = '?';
+	ncp = t->zone;
 	cbuf[20] = *ncp++;
 	cbuf[21] = *ncp++;
 	cbuf[22] = *ncp;
diff --git a/src/lib9/date.c b/src/lib9/date.c
index 8ece0c6..8f852de 100644
--- a/src/lib9/date.c
+++ b/src/lib9/date.c
@@ -1,6 +1,5 @@
-#include <stdlib.h> /* setenv etc. */
-
 #include <u.h>
+#include <stdlib.h> /* setenv etc. */
 #define NOPLAN9DEFINES
 #include <libc.h>
 #include <time.h>
@@ -25,6 +24,8 @@
 static void
 tm2Tm(struct tm *tm, Tm *bigtm)
 {
+	char *s;
+
 	memset(bigtm, 0, sizeof *bigtm);
 	bigtm->sec = tm->tm_sec;
 	bigtm->min = tm->tm_min;
@@ -39,6 +40,13 @@
 #ifdef _HAVETZOFF
 	bigtm->tzoff = tm->tm_gmtoff;
 #endif
+	if(bigtm->zone[0] == 0){
+		s = getenv("TIMEZONE");
+		if(s){
+			strecpy(bigtm->zone, bigtm->zone+4, tm->tm_zone);
+			free(s);
+		}
+	}
 }
 
 static void
diff --git a/src/lib9/dirread.c b/src/lib9/dirread.c
index aef0102..0a72d62 100644
--- a/src/lib9/dirread.c
+++ b/src/lib9/dirread.c
@@ -18,7 +18,7 @@
 	nn = getdirentries(fd, (void*)buf, n, &off);
 	return nn;
 }
-#elif defined(__APPLE__) || defined(__FreeBSD__)
+#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
 static int
 mygetdents(int fd, struct dirent *buf, int n)
 {
@@ -171,7 +171,7 @@
 		ts += n;
 	}
 	if(ts >= 0)
-		ts = dirpackage(fd, buf, ts, d);
+		ts = dirpackage(fd, (char*)buf, ts, d);
 	free(buf);
 	if(ts == 0 && n < 0)
 		return -1;
diff --git a/src/lib9/errstr.c b/src/lib9/errstr.c
index 0c6ab31..578f489 100644
--- a/src/lib9/errstr.c
+++ b/src/lib9/errstr.c
@@ -5,9 +5,10 @@
  * okay.
  */
 
+#include <u.h>
 #include <errno.h>
 #include <string.h>
-#include <lib9.h>
+#include <libc.h>
 
 enum
 {
diff --git a/src/lib9/ffork-OpenBSD.c b/src/lib9/ffork-OpenBSD.c
new file mode 100644
index 0000000..5e677f7
--- /dev/null
+++ b/src/lib9/ffork-OpenBSD.c
@@ -0,0 +1 @@
+#include "ffork-pthread.c"
diff --git a/src/lib9/getuser.c b/src/lib9/getuser.c
index b53a389..09c0704 100644
--- a/src/lib9/getuser.c
+++ b/src/lib9/getuser.c
@@ -1,6 +1,5 @@
-#include <pwd.h>
-
 #include <u.h>
+#include <pwd.h>
 #include <libc.h>
 
 char*
diff --git a/src/lib9/lock.c b/src/lib9/lock.c
index 5d6f2f3..e97b967 100644
--- a/src/lib9/lock.c
+++ b/src/lib9/lock.c
@@ -1,6 +1,7 @@
+#include <u.h>
 #include <unistd.h>
 #include <sched.h>
-#include <lib9.h>
+#include <libc.h>
 
 int _ntas;
 static int
diff --git a/src/lib9/mallocz.c b/src/lib9/mallocz.c
index c631300..a3e9151 100644
--- a/src/lib9/mallocz.c
+++ b/src/lib9/mallocz.c
@@ -1,6 +1,7 @@
+#include <u.h>
 #include <unistd.h>
 #include <string.h>
-#include <lib9.h>
+#include <libc.h>
 
 void*
 mallocz(unsigned long n, int clr)
diff --git a/src/lib9/mkfile b/src/lib9/mkfile
index d388d22..4dda2e1 100644
--- a/src/lib9/mkfile
+++ b/src/lib9/mkfile
@@ -69,8 +69,6 @@
 	_p9dialparse.$O\
 	_p9dir.$O\
 	_p9proc.$O\
-	_p9translate.$O\
-	access.$O\
 	announce.$O\
 	argv0.$O\
 	atexit.$O\
@@ -100,6 +98,7 @@
 	exec.$O\
 	fcallfmt.$O\
 	ffork-$SYSNAME.$O\
+	get9root.$O\
 	getcallerpc-$OBJTYPE.$O\
 	getenv.$O\
 	getfields.$O\
@@ -142,6 +141,7 @@
 	u16.$O\
 	u32.$O\
 	u64.$O\
+	unsharp.$O\
 	wait.$O\
 	waitpid.$O\
 
diff --git a/src/lib9/notify.c b/src/lib9/notify.c
index 2e589de..4d2a79e 100644
--- a/src/lib9/notify.c
+++ b/src/lib9/notify.c
@@ -1,6 +1,5 @@
-#include <signal.h>
-
 #include <u.h>
+#include <signal.h>
 #define NOPLAN9DEFINES
 #include <libc.h>
 #include "9proc.h"
diff --git a/src/lib9/open.c b/src/lib9/open.c
index bb597e8..0356a7d 100644
--- a/src/lib9/open.c
+++ b/src/lib9/open.c
@@ -2,12 +2,9 @@
 #define NOPLAN9DEFINES
 #include <libc.h>
 
-extern char* _p9translate(char*);
-
 int
-p9open(char *xname, int mode)
+p9open(char *name, int mode)
 {
-	char *name;
 	int cexec, rclose;
 	int fd, umode;
 
@@ -23,8 +20,6 @@
 		werrstr("mode not supported");
 		return -1;
 	}
-	if((name = _p9translate(xname)) == nil)
-		return -1;
 	fd = open(name, umode);
 	if(fd >= 0){
 		if(cexec)
@@ -32,7 +27,5 @@
 		if(rclose)
 			remove(name);
 	}
-	if(name != xname)
-		free(name);
 	return fd;
 }
diff --git a/src/lib9/rendez-Linux.c b/src/lib9/rendez-Linux.c
index 05c52ae..673818e 100644
--- a/src/lib9/rendez-Linux.c
+++ b/src/lib9/rendez-Linux.c
@@ -1 +1,3 @@
+/* Could use futex(2) here instead of signals? */
+
 #include "rendez-signal.c"
diff --git a/src/lib9/rendez-OpenBSD.c b/src/lib9/rendez-OpenBSD.c
new file mode 100644
index 0000000..05c52ae
--- /dev/null
+++ b/src/lib9/rendez-OpenBSD.c
@@ -0,0 +1 @@
+#include "rendez-signal.c"
diff --git a/src/lib9/rendez-pthread.c b/src/lib9/rendez-pthread.c
index b4b95f2..fe930ce 100644
--- a/src/lib9/rendez-pthread.c
+++ b/src/lib9/rendez-pthread.c
@@ -32,9 +32,10 @@
  * shared memory and mutexes.
  */
 
+#include <u.h>
 #include <pthread.h>
 #include <signal.h>
-#include <lib9.h>
+#include <libc.h>
 
 enum
 {
diff --git a/src/lib9/time.c b/src/lib9/time.c
index 169a82f..3670636 100644
--- a/src/lib9/time.c
+++ b/src/lib9/time.c
@@ -1,7 +1,7 @@
-#include <sys/time.h>
-#include <sys/resource.h>
-
 #include <u.h>
+#include <sys/time.h>
+#include <time.h>
+#include <sys/resource.h>
 #define NOPLAN9DEFINES
 #include <libc.h>
 
diff --git a/src/libdraw/openfont.c b/src/libdraw/openfont.c
index a359327..05370ea 100644
--- a/src/libdraw/openfont.c
+++ b/src/libdraw/openfont.c
@@ -18,8 +18,10 @@
 		nambuf = smprint("#9/font/%s", name+14);
 		if(nambuf == nil)
 			return 0;
+		nambuf = unsharp(nambuf);
+		if(nambuf == nil)
+			return 0;
 		if((fd = open(nambuf, OREAD)) < 0){
-fprint(2, "failed at %s\n", nambuf);
 			free(nambuf);
 			return 0;
 		}
diff --git a/src/libdraw/x11-init.c b/src/libdraw/x11-init.c
index 1283f43..c959eee 100644
--- a/src/libdraw/x11-init.c
+++ b/src/libdraw/x11-init.c
@@ -185,26 +185,22 @@
 	/* 
 	 * Figure out underlying screen format.
 	 */
-	_x.depth = DefaultDepth(_x.display, xrootid);
 	if(XMatchVisualInfo(_x.display, xrootid, 16, TrueColor, &xvi)
 	|| XMatchVisualInfo(_x.display, xrootid, 16, DirectColor, &xvi)){
 		_x.vis = xvi.visual;
 		_x.depth = 16;
-		_x.usetable = 1;
 	}
 	else
 	if(XMatchVisualInfo(_x.display, xrootid, 15, TrueColor, &xvi)
 	|| XMatchVisualInfo(_x.display, xrootid, 15, DirectColor, &xvi)){
 		_x.vis = xvi.visual;
 		_x.depth = 15;
-		_x.usetable = 1;
 	}
 	else
 	if(XMatchVisualInfo(_x.display, xrootid, 24, TrueColor, &xvi)
 	|| XMatchVisualInfo(_x.display, xrootid, 24, DirectColor, &xvi)){
 		_x.vis = xvi.visual;
 		_x.depth = 24;
-		_x.usetable = 1;
 	}
 	else
 	if(XMatchVisualInfo(_x.display, xrootid, 8, PseudoColor, &xvi)
@@ -218,6 +214,7 @@
 		_x.depth = 8;
 	}
 	else{
+		_x.depth = DefaultDepth(_x.display, xrootid);
 		if(_x.depth != 8){
 			werrstr("can't understand depth %d screen", _x.depth);
 			goto err0;
@@ -225,6 +222,9 @@
 		_x.vis = DefaultVisual(_x.display, xrootid);
 	}
 
+	if(DefaultDepth(_x.display, xrootid) == _x.depth)
+		_x.usetable = 1;
+
 	/*
 	 * _x.depth is only the number of significant pixel bits,
 	 * not the total number of pixel bits.  We need to walk the
@@ -298,7 +298,7 @@
 		Dx(r),		/* width */
 	 	Dy(r),		/* height */
 		0,		/* border width */
-		DefaultDepthOfScreen(xscreen),	/* depth */
+		_x.depth,	/* depth */
 		InputOutput,	/* class */
 		_x.vis,		/* visual */
 				/* valuemask */
@@ -563,6 +563,18 @@
 
 	if(_x.depth >= 24) {
 		/*
+		 * This is needed for SunOS.  Ask Axel Belinfante.
+		 */
+		if(_x.usetable == 0){
+			_x.cmap = XCreateColormap(_x.display, w, _x.vis, AllocAll); 
+			XStoreColors(_x.display, _x.cmap, _x.map, 256);
+			for(i = 0; i < 256; i++){
+				_x.tox11[i] = i;
+				_x.toplan9[i] = i;
+			}
+		}
+
+		/*
 		 * The pixel value returned from XGetPixel needs to
 		 * be converted to RGB so we can call rgb2cmap()
 		 * to translate between 24 bit X and our color. Unfortunately,
@@ -573,7 +585,6 @@
 		 * some displays say MSB even though they run on LSB.
 		 * Besides, this is more anal.
 		 */
-
 		c = _x.map[19];	/* known to have different R, G, B values */
 		if(!XAllocColor(_x.display, _x.cmap, &c)){
 			werrstr("XAllocColor: %r");
diff --git a/src/libdraw/x11-itrans.c b/src/libdraw/x11-itrans.c
index 8e72b01..509b55c 100644
--- a/src/libdraw/x11-itrans.c
+++ b/src/libdraw/x11-itrans.c
@@ -11,6 +11,9 @@
 #include <keyboard.h>
 #include "x11-memdraw.h"
 
+#undef time
+
+
 static int
 __xtoplan9kbd(XEvent *e)
 {
diff --git a/src/libplumb/mesg.c b/src/libplumb/mesg.c
index c4094fa..4b27869 100755
--- a/src/libplumb/mesg.c
+++ b/src/libplumb/mesg.c
@@ -68,7 +68,6 @@
 	if(buf == nil)
 		return -1;
 	n = fswrite(fid, buf, n);
-fprint(2, "fswrite %d\n", n);
 	free(buf);
 	return n;
 }
diff --git a/src/libthread/asm-OpenBSD-386.s b/src/libthread/asm-OpenBSD-386.s
new file mode 100644
index 0000000..35e2ab6
--- /dev/null
+++ b/src/libthread/asm-OpenBSD-386.s
@@ -0,0 +1,49 @@
+.globl	_setlabel
+.type	_setlabel,@function
+
+_setlabel:
+	movl	4(%esp), %eax
+	movl	0(%esp), %edx
+	movl	%edx, 0(%eax)
+	movl	%ebx, 4(%eax)
+	movl	%esp, 8(%eax)
+	movl	%ebp, 12(%eax)
+	movl	%esi, 16(%eax)
+	movl	%edi, 20(%eax)
+	xorl	%eax, %eax
+	ret
+
+.globl	_gotolabel
+.type	_gotolabel,@function
+
+_gotolabel:
+	movl	4(%esp), %edx
+	movl	0(%edx), %ecx
+	movl	4(%edx), %ebx
+	movl	8(%edx), %esp
+	movl	12(%edx), %ebp
+	movl	16(%edx), %esi
+	movl	20(%edx), %edi
+	xorl	%eax, %eax
+	incl	%eax
+	movl	%ecx, 0(%esp)
+	ret
+
+
+# .globl	_xinc
+# _xinc:
+# 	movl 4(%esp), %eax
+# 	lock incl 0(%eax)
+# 	ret
+# 
+# .globl	_xdec
+# _xdec:
+# 	movl 4(%esp), %eax
+# 	lock decl 0(%eax)
+# 	jz iszero
+# 	movl $1, %eax
+# 	ret
+# iszero:
+# 	movl $0, %eax
+# 	ret
+# 
diff --git a/src/libthread/exec-unix.c b/src/libthread/exec-unix.c
index bfffa14..eb31e99 100644
--- a/src/libthread/exec-unix.c
+++ b/src/libthread/exec-unix.c
@@ -3,8 +3,8 @@
 #include "threadimpl.h"
 
 static void efork(int[3], int[2], char*, char**);
-void
-threadexec(Channel *pidc, int fd[3], char *prog, char *args[])
+static void
+_threadexec(Channel *pidc, int fd[3], char *prog, char *args[], int freeargs)
 {
 	int pfd[2];
 	int n, pid;
@@ -40,6 +40,8 @@
 		efork(fd, pfd, prog, args);
 		_exit(0);
 	default:
+		if(freeargs)
+			free(args);
 		break;
 	}
 
@@ -69,9 +71,42 @@
 }
 
 void
+threadexec(Channel *pidc, int fd[3], char *prog, char *args[])
+{
+	_threadexec(pidc, fd, prog, args, 0);
+}
+
+/*
+ * The &f+1 trick doesn't work on SunOS, so we might 
+ * as well bite the bullet and do this correctly.
+ */
+void
 threadexecl(Channel *pidc, int fd[3], char *f, ...)
 {
-	threadexec(pidc, fd, f, &f+1);
+	char **args, *s;
+	int n;
+	va_list arg;
+
+	va_start(arg, f);
+	for(n=0; va_arg(arg, char*) != 0; n++)
+		;
+	n++;
+	va_end(arg);
+
+	args = malloc(n*sizeof(args[0]));
+	if(args == nil){
+		if(pidc)
+			sendul(pidc, ~0);
+		return;
+	}
+
+	va_start(arg, f);
+	for(n=0; (s=va_arg(arg, char*)) != 0; n++)
+		args[n] = s;
+	args[n] = 0;
+	va_end(arg);
+
+	_threadexec(pidc, fd, f, args, 1);
 }
 
 static void
diff --git a/src/libthread/label.h b/src/libthread/label.h
index 874fb07..5161e37 100644
--- a/src/libthread/label.h
+++ b/src/libthread/label.h
@@ -7,7 +7,7 @@
 typedef struct Label Label;
 #define LABELDPC 0
 
-#if defined (__i386__) && (defined(__FreeBSD__) || defined(__linux__))
+#if defined (__i386__) && (defined(__FreeBSD__) || defined(__linux__) || defined(__OpenBSD__))
 struct Label
 {
 	ulong pc;
diff --git a/src/mkfile b/src/mkfile
index 4568360..a81246c 100644
--- a/src/mkfile
+++ b/src/mkfile
@@ -1,6 +1,6 @@
 <mkhdr
 
-BUGGERED='9p|html|httpd|ip|venti'
+BUGGERED='9p|fmt|html|httpd|ip|utf|venti'
 LIBDIRS=`ls -ld lib* | sed -n 's/^d.* //p' |egrep -v "^lib($BUGGERED)$"`
 
 DIRS=\