shithub: acme-themes

Download patch

ref: ef88bf19c7f048b4bcf188a3d4767375d759edbc
parent: ff84152ce1d5225a887239d3c3788c6eb6d42c53
author: glenda <[email protected]>
date: Wed Oct 20 21:50:42 EDT 2021

Cleaning out acme files

--- a/acme.c
+++ /dev/null
@@ -1,1071 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <thread.h>
-#include <cursor.h>
-#include <mouse.h>
-#include <keyboard.h>
-#include <frame.h>
-#include <fcall.h>
-#include <plumb.h>
-#include "dat.h"
-#include "fns.h"
-	/* for generating syms in mkfile only: */
-	#include <bio.h>
-	#include "edit.h"
-
-void	mousethread(void*);
-void	keyboardthread(void*);
-void	waitthread(void*);
-void	xfidallocthread(void*);
-void	newwindowthread(void*);
-void 	plumbproc(void*);
-void 	themeload(char *s, int n);
-
-Reffont	**fontcache;
-int		nfontcache;
-char		wdir[512] = ".";
-Reffont	*reffonts[2];
-int		snarffd = -1;
-int		mainpid;
-int		plumbsendfd;
-int		plumbeditfd;
-
-enum{
-	NSnarf = 1000	/* less than 1024, I/O buffer size */
-};
-Rune	snarfrune[NSnarf+1];
-
-char		*fontnames[2];
-
-Command *command;
-
-void	acmeerrorinit(void);
-void	readfile(Column*, char*);
-int	shutdown(void*, char*);
-
-void
-derror(Display*, char *errorstr)
-{
-	error(errorstr);
-}
-
-void
-threadmain(int argc, char *argv[])
-{
-	int i;
-	char *p, *loadfile;
-	char buf[256];
-	Column *c;
-	int ncol;
-	Display *d;
-
-	rfork(RFENVG|RFNAMEG);
-
-	ncol = -1;
-
-	loadfile = nil;
-	ARGBEGIN{
-	case 'a':
-		globalindent[AUTOINDENT] = TRUE;
-		break;
-	case 'b':
-		bartflag = TRUE;
-		break;
-	case 'c':
-		p = ARGF();
-		if(p == nil)
-			goto Usage;
-		ncol = atoi(p);
-		if(ncol <= 0)
-			goto Usage;
-		break;
-	case 'f':
-		fontnames[0] = ARGF();
-		if(fontnames[0] == nil)
-			goto Usage;
-		break;
-	case 'F':
-		fontnames[1] = ARGF();
-		if(fontnames[1] == nil)
-			goto Usage;
-		break;
-	case 'i':
-		globalindent[SPACESINDENT] = TRUE;
-		break;
-	case 'l':
-		loadfile = ARGF();
-		if(loadfile == nil)
-			goto Usage;
-		break;
-	default:
-	Usage:
-		fprint(2, "usage: acme [-aib] [-c ncol] [-f font] [-F fixedfont] [-l loadfile | file...]\n");
-		exits("usage");
-	}ARGEND
-
-	if(fontnames[0] == nil)
-		fontnames[0] = getenv("font");
-	if(fontnames[0] == nil)
-		fontnames[0] = "/lib/font/bit/vga/unicode.font";
-	if(access(fontnames[0], 0) < 0){
-		fprint(2, "acme: can't access %s: %r\n", fontnames[0]);
-		exits("font open");
-	}
-	if(fontnames[1] == nil)
-		fontnames[1] = fontnames[0];
-	fontnames[0] = estrdup(fontnames[0]);
-	fontnames[1] = estrdup(fontnames[1]);
-
-	quotefmtinstall();
-	cputype = getenv("cputype");
-	objtype = getenv("objtype");
-	home = getenv("home");
-	p = getenv("tabstop");
-	if(p != nil){
-		maxtab = strtoul(p, nil, 0);
-		free(p);
-	}
-	if(maxtab == 0)
-		maxtab = 4; 
-	if(loadfile)
-		rowloadfonts(loadfile);
-	putenv("font", fontnames[0]);
-	snarffd = open("/dev/snarf", OREAD|OCEXEC);
-	if(cputype){
-		sprint(buf, "/acme/bin/%s", cputype);
-		bind(buf, "/bin", MBEFORE);
-	}
-	bind("/acme/bin", "/bin", MBEFORE);
-	getwd(wdir, sizeof wdir);
-
-	if(geninitdraw(nil, derror, fontnames[0], "acme", nil, Refnone) < 0){
-		fprint(2, "acme: can't open display: %r\n");
-		exits("geninitdraw");
-	}
-	d = display;
-	font = d->defaultfont;
-
-	reffont.f = font;
-	reffonts[0] = &reffont;
-	incref(&reffont);	/* one to hold up 'font' variable */
-	incref(&reffont);	/* one to hold up reffonts[0] */
-	fontcache = emalloc(sizeof(Reffont*));
-	nfontcache = 1;
-	fontcache[0] = &reffont;
-
-	iconinit();
-	timerinit();
-	rxinit();
-
-	cwait = threadwaitchan();
-	ccommand = chancreate(sizeof(Command**), 0);
-	ckill = chancreate(sizeof(Rune*), 0);
-	cxfidalloc = chancreate(sizeof(Xfid*), 0);
-	cxfidfree = chancreate(sizeof(Xfid*), 0);
-	cnewwindow = chancreate(sizeof(Channel*), 0);
-	cerr = chancreate(sizeof(char*), 0);
-	cedit = chancreate(sizeof(int), 0);
-	cexit = chancreate(sizeof(int), 0);
-	cwarn = chancreate(sizeof(void*), 1);
-	if(cwait==nil || ccommand==nil || ckill==nil || cxfidalloc==nil || cxfidfree==nil || cnewwindow==nil || cerr==nil || cedit==nil || cexit==nil || cwarn==nil){
-		fprint(2, "acme: can't create initial channels: %r\n");
-		threadexitsall("channels");
-	}
-
-	mousectl = initmouse(nil, screen);
-	if(mousectl == nil){
-		fprint(2, "acme: can't initialize mouse: %r\n");
-		threadexitsall("mouse");
-	}
-	mouse = mousectl;
-	keyboardctl = initkeyboard(nil);
-	if(keyboardctl == nil){
-		fprint(2, "acme: can't initialize keyboard: %r\n");
-		threadexitsall("keyboard");
-	}
-	mainpid = getpid();
-	plumbeditfd = plumbopen("edit", OREAD|OCEXEC);
-	if(plumbeditfd >= 0){
-		cplumb = chancreate(sizeof(Plumbmsg*), 0);
-		proccreate(plumbproc, nil, STACK);
-	}
-	plumbsendfd = plumbopen("send", OWRITE|OCEXEC);
-
-	fsysinit();
-
-	#define	WPERCOL	8
-	disk = diskinit();
-	if(!loadfile || !rowload(&row, loadfile, TRUE)){
-		rowinit(&row, screen->clipr);
-		if(ncol < 0){
-			if(argc == 0)
-				ncol = 2;
-			else{
-				ncol = (argc+(WPERCOL-1))/WPERCOL;
-				if(ncol < 2)
-					ncol = 2;
-			}
-		}
-		if(ncol == 0)
-			ncol = 2;
-		for(i=0; i<ncol; i++){
-			c = rowadd(&row, nil, -1);
-			if(c==nil && i==0)
-				error("initializing columns");
-		}
-		c = row.col[row.ncol-1];
-		if(argc == 0)
-			readfile(c, wdir);
-		else
-			for(i=0; i<argc; i++){
-				p = utfrrune(argv[i], '/');
-				if((p!=nil && strcmp(p, "/guide")==0) || i/WPERCOL>=row.ncol)
-					readfile(c, argv[i]);
-				else
-					readfile(row.col[i/WPERCOL], argv[i]);
-			}
-	}
-	flushimage(display, 1);
-
-	acmeerrorinit();
-	threadcreate(keyboardthread, nil, STACK);
-	threadcreate(mousethread, nil, STACK);
-	threadcreate(waitthread, nil, STACK);
-	threadcreate(xfidallocthread, nil, STACK);
-	threadcreate(newwindowthread, nil, STACK);
-
-	threadnotify(shutdown, 1);
-	recvul(cexit);
-	killprocs();
-	threadexitsall(nil);
-}
-
-void
-readfile(Column *c, char *s)
-{
-	Window *w;
-	Rune rb[256];
-	int nb, nr;
-	Runestr rs;
-
-	w = coladd(c, nil, nil, -1);
-	cvttorunes(s, strlen(s), rb, &nb, &nr, nil);
-	rs = cleanrname((Runestr){rb, nr});
-	winsetname(w, rs.r, rs.nr);
-	textload(&w->body, 0, s, 1);
-	w->body.file->mod = FALSE;
-	w->dirty = FALSE;
-	winsettag(w);
-	textscrdraw(&w->body);
-	textsetselect(&w->tag, w->tag.file->nc, w->tag.file->nc);
-	xfidlog(w, "new");
-}
-
-char *oknotes[] ={
-	"delete",
-	"hangup",
-	"kill",
-	"exit",
-	nil
-};
-
-int	dumping;
-
-int
-shutdown(void*, char *msg)
-{
-	int i;
-
-	killprocs();
-	if(!dumping && strcmp(msg, "kill")!=0 && strcmp(msg, "exit")!=0 && getpid()==mainpid){
-		dumping = TRUE;
-		rowdump(&row, nil);
-	}
-	for(i=0; oknotes[i]; i++)
-		if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0)
-			threadexitsall(msg);
-	print("acme: %s\n", msg);
-	abort();
-	return 0;
-}
-
-void
-killprocs(void)
-{
-	Command *c;
-
-	fsysclose();
-//	if(display)
-//		flushimage(display, 1);
-
-	for(c=command; c; c=c->next)
-		postnote(PNGROUP, c->pid, "hangup");
-	remove(acmeerrorfile);
-}
-
-static int errorfd;
-
-void
-acmeerrorproc(void *)
-{
-	char *buf, *s;
-	int n;
-
-	threadsetname("acmeerrorproc");
-	buf = emalloc(8192+1);
-	while((n=read(errorfd, buf, 8192)) >= 0){
-		buf[n] = '\0';
-		s = estrdup(buf);
-		sendp(cerr, s);
-	}
-	free(buf);
-}
-
-void
-acmeerrorinit(void)
-{
-	int fd, pfd[2];
-	char buf[64];
-
-	if(pipe(pfd) < 0)
-		error("can't create pipe");
-	sprint(acmeerrorfile, "/srv/acme.%s.%d", user, mainpid);
-	fd = create(acmeerrorfile, OWRITE, 0666);
-	if(fd < 0){
-		remove(acmeerrorfile);
-  		fd = create(acmeerrorfile, OWRITE, 0666);
-		if(fd < 0)
-			error("can't create acmeerror file");
-	}
-	sprint(buf, "%d", pfd[0]);
-	write(fd, buf, strlen(buf));
-	close(fd);
-	/* reopen pfd[1] close on exec */
-	sprint(buf, "/fd/%d", pfd[1]);
-	errorfd = open(buf, OREAD|OCEXEC);
-	if(errorfd < 0)
-		error("can't re-open acmeerror file");
-	close(pfd[1]);
-	close(pfd[0]);
-	proccreate(acmeerrorproc, nil, STACK);
-}
-
-void
-plumbproc(void *)
-{
-	Plumbmsg *m;
-
-	threadsetname("plumbproc");
-	for(;;){
-		m = plumbrecv(plumbeditfd);
-		if(m == nil)
-			threadexits(nil);
-		sendp(cplumb, m);
-	}
-}
-
-void
-keyboardthread(void *)
-{
-	Rune r;
-	Timer *timer;
-	Text *t;
-	enum { KTimer, KKey, NKALT };
-	static Alt alts[NKALT+1];
-
-	alts[KTimer].c = nil;
-	alts[KTimer].v = nil;
-	alts[KTimer].op = CHANNOP;
-	alts[KKey].c = keyboardctl->c;
-	alts[KKey].v = &r;
-	alts[KKey].op = CHANRCV;
-	alts[NKALT].op = CHANEND;
-
-	timer = nil;
-	typetext = nil;
-	threadsetname("keyboardthread");
-	for(;;){
-		switch(alt(alts)){
-		case KTimer:
-			timerstop(timer);
-			t = typetext;
-			if(t!=nil && t->what==Tag){
-				winlock(t->w, 'K');
-				wincommit(t->w, t);
-				winunlock(t->w);
-				flushimage(display, 1);
-			}
-			alts[KTimer].c = nil;
-			alts[KTimer].op = CHANNOP;
-			break;
-		case KKey:
-		casekeyboard:
-			typetext = rowtype(&row, r, mouse->xy);
-			t = typetext;
-			if(t!=nil && t->col!=nil && !(r==Kdown || r==Kleft || r==Kright))	/* scrolling doesn't change activecol */
-				activecol = t->col;
-			if(t!=nil && t->w!=nil)
-				t->w->body.file->curtext = &t->w->body;
-			if(timer != nil)
-				timercancel(timer);
-			if(t!=nil && t->what==Tag) {
-				timer = timerstart(500);
-				alts[KTimer].c = timer->c;
-				alts[KTimer].op = CHANRCV;
-			}else{
-				timer = nil;
-				alts[KTimer].c = nil;
-				alts[KTimer].op = CHANNOP;
-			}
-			if(nbrecv(keyboardctl->c, &r) > 0)
-				goto casekeyboard;
-			flushimage(display, 1);
-			break;
-		}
-	}
-}
-
-void
-mousethread(void *)
-{
-	Text *t, *argt;
-	int but;
-	uint q0, q1;
-	Window *w;
-	Plumbmsg *pm;
-	Mouse m;
-	char *act;
-	enum { MResize, MMouse, MPlumb, MWarnings, NMALT };
-	static Alt alts[NMALT+1];
-
-	threadsetname("mousethread");
-	alts[MResize].c = mousectl->resizec;
-	alts[MResize].v = nil;
-	alts[MResize].op = CHANRCV;
-	alts[MMouse].c = mousectl->c;
-	alts[MMouse].v = &mousectl->Mouse;
-	alts[MMouse].op = CHANRCV;
-	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);
-		flushwarnings();
-		qunlock(&row);
-		flushimage(display, 1);
-		switch(alt(alts)){
-		case MResize:
-			if(getwindow(display, Refnone) < 0)
-				error("attach to window");
-			scrlresize();
-			rowresize(&row, screen->clipr);
-			break;
-		case MPlumb:
-			if(strcmp(pm->type, "text") == 0){
-				act = plumblookup(pm->attr, "action");
-				if(act==nil || strcmp(act, "showfile")==0)
-					plumblook(pm);
-				else if(strcmp(act, "showdata")==0)
-					plumbshow(pm);
-			}
-			plumbfree(pm);
-			break;
-		case MWarnings:
-			break;
-		case MMouse:
-			/*
-			 * Make a copy so decisions are consistent; mousectl changes
-			 * underfoot.  Can't just receive into m because this introduces
-			 * another race; see /sys/src/libdraw/mouse.c.
-			 */
-			m = mousectl->Mouse;
-			qlock(&row);
-			t = rowwhich(&row, m.xy);
-
-			if((t!=mousetext && t!=nil && t->w!=nil) &&
-				(mousetext==nil || mousetext->w==nil || t->w->id!=mousetext->w->id)) {
-				xfidlog(t->w, "focus");
-			}
-
-			if(t!=mousetext && mousetext!=nil && mousetext->w!=nil){
-				winlock(mousetext->w, 'M');
-				mousetext->eq0 = ~0;
-				wincommit(mousetext->w, mousetext);
-				winunlock(mousetext->w);
-			}
-			mousetext = t;
-			if(t == nil)
-				goto Continue;
-			w = t->w;
-			if(t==nil || m.buttons==0)
-				goto Continue;
-			but = 0;
-			if(m.buttons == 1)
-				but = 1;
-			else if(m.buttons == 2)
-				but = 2;
-			else if(m.buttons == 4)
-				but = 3;
-			barttext = t;
-			if(t->what==Body && ptinrect(m.xy, t->scrollr)){
-				if(but){
-					winlock(w, 'M');
-					t->eq0 = ~0;
-					textscroll(t, but);
-					winunlock(w);
-				}
-				goto Continue;
-			}
-			/* scroll buttons, wheels, etc. */
-			if(t->what==Body && w != nil && (m.buttons & (8|16))){
-				if(m.buttons & 8)
-					but = Kscrolloneup;
-				else
-					but = Kscrollonedown;
-				winlock(w, 'M');
-				t->eq0 = ~0;
-				texttype(t, but);
-				winunlock(w);
-				goto Continue;
-			}
-			if(ptinrect(m.xy, t->scrollr)){
-				if(but){
-					if(t->what == Columntag)
-						rowdragcol(&row, t->col, but);
-					else if(t->what == Tag){
-						coldragwin(t->col, t->w, but);
-						if(t->w)
-							barttext = &t->w->body;
-					}
-					if(t->col)
-						activecol = t->col;
-				}
-				goto Continue;
-			}
-			if(m.buttons){
-				if(w)
-					winlock(w, 'M');
-				t->eq0 = ~0;
-				if(w)
-					wincommit(w, t);
-				else
-					textcommit(t, TRUE);
-				if(m.buttons & 1){
-					textselect(t);
-					if(w)
-						winsettag(w);
-					argtext = t;
-					seltext = t;
-					if(t->col)
-						activecol = t->col;	/* button 1 only */
-					if(t->w!=nil && t==&t->w->body)
-						activewin = t->w;
-				}else if(m.buttons & 2){
-					if(textselect2(t, &q0, &q1, &argt))
-						execute(t, q0, q1, FALSE, argt);
-				}else if(m.buttons & 4){
-					if(textselect3(t, &q0, &q1))
-						look3(t, q0, q1, FALSE);
-				}
-				if(w)
-					winunlock(w);
-				goto Continue;
-			}
-    Continue:
-			qunlock(&row);
-			break;
-		}
-	}
-}
-
-/*
- * There is a race between process exiting and our finding out it was ever created.
- * This structure keeps a list of processes that have exited we haven't heard of.
- */
-typedef struct Pid Pid;
-struct Pid
-{
-	int	pid;
-	char	msg[ERRMAX];
-	Pid	*next;
-};
-
-void
-waitthread(void *)
-{
-	Waitmsg *w;
-	Command *c, *lc;
-	uint pid;
-	int found, ncmd;
-	Rune *cmd;
-	char *err;
-	Text *t;
-	Pid *pids, *p, *lastp;
-	enum { WErr, WKill, WWait, WCmd, NWALT };
-	Alt alts[NWALT+1];
-
-	threadsetname("waitthread");
-	pids = nil;
-	alts[WErr].c = cerr;
-	alts[WErr].v = &err;
-	alts[WErr].op = CHANRCV;
-	alts[WKill].c = ckill;
-	alts[WKill].v = &cmd;
-	alts[WKill].op = CHANRCV;
-	alts[WWait].c = cwait;
-	alts[WWait].v = &w;
-	alts[WWait].op = CHANRCV;
-	alts[WCmd].c = ccommand;
-	alts[WCmd].v = &c;
-	alts[WCmd].op = CHANRCV;
-	alts[NWALT].op = CHANEND;
-
-	command = nil;
-	for(;;){
-		switch(alt(alts)){
-		case WErr:
-			qlock(&row);
-			warning(nil, "%s", err);
-			free(err);
-			flushimage(display, 1);
-			qunlock(&row);
-			break;
-		case WKill:
-			found = FALSE;
-			ncmd = runestrlen(cmd);
-			for(c=command; c; c=c->next){
-				/* -1 for blank */
-				if(runeeq(c->name, c->nname-1, cmd, ncmd) == TRUE){
-					if(postnote(PNGROUP, c->pid, "kill") < 0)
-						warning(nil, "kill %S: %r\n", cmd);
-					found = TRUE;
-				}
-			}
-			if(!found)
-				warning(nil, "Kill: no process %S\n", cmd);
-			free(cmd);
-			break;
-		case WWait:
-			pid = w->pid;
-			lc = nil;
-			for(c=command; c; c=c->next){
-				if(c->pid == pid){
-					if(lc)
-						lc->next = c->next;
-					else
-						command = c->next;
-					break;
-				}
-				lc = c;
-			}
-			qlock(&row);
-			t = &row.tag;
-			textcommit(t, TRUE);
-			if(c == nil){
-				/* helper processes use this exit status */
-				if(strncmp(w->msg, "libthread", 9) != 0){
-					p = emalloc(sizeof(Pid));
-					p->pid = pid;
-					strncpy(p->msg, w->msg, sizeof(p->msg));
-					p->next = pids;
-					pids = p;
-				}
-			}else{
-				if(search(t, c->name, c->nname)){
-					textdelete(t, t->q0, t->q1, TRUE);
-					textsetselect(t, 0, 0);
-				}
-				if(w->msg[0])
-					warning(c->md, "%s\n", w->msg);
-				flushimage(display, 1);
-			}
-			qunlock(&row);
-			free(w);
-    Freecmd:
-			if(c){
-				if(c->iseditcmd)
-					sendul(cedit, 0);
-				free(c->text);
-				free(c->name);
-				fsysdelid(c->md);
-				free(c);
-			}
-			break;
-		case WCmd:
-			/* has this command already exited? */
-			lastp = nil;
-			for(p=pids; p!=nil; p=p->next){
-				if(p->pid == c->pid){
-					if(p->msg[0])
-						warning(c->md, "%s\n", p->msg);
-					if(lastp == nil)
-						pids = p->next;
-					else
-						lastp->next = p->next;
-					free(p);
-					goto Freecmd;
-				}
-				lastp = p;
-			}
-			c->next = command;
-			command = c;
-			qlock(&row);
-			t = &row.tag;
-			textcommit(t, TRUE);
-			textinsert(t, 0, c->name, c->nname, TRUE);
-			textsetselect(t, 0, 0);
-			flushimage(display, 1);
-			qunlock(&row);
-			break;
-		}
-	}
-}
-
-void
-xfidallocthread(void*)
-{
-	Xfid *xfree, *x;
-	enum { Alloc, Free, N };
-	static Alt alts[N+1];
-
-	threadsetname("xfidallocthread");
-	alts[Alloc].c = cxfidalloc;
-	alts[Alloc].v = nil;
-	alts[Alloc].op = CHANRCV;
-	alts[Free].c = cxfidfree;
-	alts[Free].v = &x;
-	alts[Free].op = CHANRCV;
-	alts[N].op = CHANEND;
-
-	xfree = nil;
-	for(;;){
-		switch(alt(alts)){
-		case Alloc:
-			x = xfree;
-			if(x)
-				xfree = x->next;
-			else{
-				x = emalloc(sizeof(Xfid));
-				x->c = chancreate(sizeof(void(*)(Xfid*)), 0);
-				x->arg = x;
-				threadcreate(xfidctl, x->arg, STACK);
-			}
-			sendp(cxfidalloc, x);
-			break;
-		case Free:
-			x->next = xfree;
-			xfree = x;
-			break;
-		}
-	}
-}
-
-/* this thread, in the main proc, allows fsysproc to get a window made without doing graphics */
-void
-newwindowthread(void*)
-{
-	Window *w;
-
-	threadsetname("newwindowthread");
-
-	for(;;){
-		/* only fsysproc is talking to us, so synchronization is trivial */
-		recvp(cnewwindow);
-		w = makenewwindow(nil);
-		winsettag(w);
-		xfidlog(w, "new");
-		sendp(cnewwindow, w);
-	}
-}
-
-Reffont*
-rfget(int fix, int save, int setfont, char *name)
-{
-	Reffont *r;
-	Font *f;
-	int i;
-
-	r = nil;
-	if(name == nil){
-		name = fontnames[fix];
-		r = reffonts[fix];
-	}
-	if(r == nil){
-		for(i=0; i<nfontcache; i++)
-			if(strcmp(name, fontcache[i]->f->name) == 0){
-				r = fontcache[i];
-				goto Found;
-			}
-		f = openfont(display, name);
-		if(f == nil){
-			warning(nil, "can't open font file %s: %r\n", name);
-			return nil;
-		}
-		r = emalloc(sizeof(Reffont));
-		r->f = f;
-		fontcache = erealloc(fontcache, (nfontcache+1)*sizeof(Reffont*));
-		fontcache[nfontcache++] = r;
-	}
-    Found:
-	if(save){
-		incref(r);
-		if(reffonts[fix])
-			rfclose(reffonts[fix]);
-		reffonts[fix] = r;
-		if(name != fontnames[fix]){
-			free(fontnames[fix]);
-			fontnames[fix] = estrdup(name);
-		}
-	}
-	if(setfont){
-		reffont.f = r->f;
-		incref(r);
-		rfclose(reffonts[0]);
-		font = r->f;
-		reffonts[0] = r;
-		incref(r);
-		iconinit();
-	}
-	incref(r);
-	return r;
-}
-
-void
-rfclose(Reffont *r)
-{
-	int i;
-
-	if(decref(r) == 0){
-		for(i=0; i<nfontcache; i++)
-			if(r == fontcache[i])
-				break;
-		if(i >= nfontcache)
-			warning(nil, "internal error: can't find font in cache\n");
-		else{
-			nfontcache--;
-			memmove(fontcache+i, fontcache+i+1, (nfontcache-i)*sizeof(Reffont*));
-		}
-		freefont(r->f);
-		free(r);
-	}
-}
-
-Cursor boxcursor = {
-	{-7, -7},
-	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-	 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
-	 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
-	 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
-	{0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
-	 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
-	 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
-	 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00}
-};
-
-static char *
-readall(int f, int *osz)
-{
-	int bufsz, sz, n;
-	char *s;
-
-	bufsz = 1023;
-	s = nil;
-	for(sz = 0;; sz += n){
-		if(bufsz-sz < 1024){
-			bufsz *= 2;
-			s = realloc(s, bufsz);
-		}
-		if((n = readn(f, s+sz, bufsz-sz-1)) < 1)
-			break;
-	}
-	if(n < 0 || sz < 1){
-		free(s);
-		return nil;
-	}
-	s[sz] = 0;
-	*osz = sz;
-
-	return s;
-}
-
-void
-iconinit(void)
-{
-	Rectangle r;
-	Image *tmp;
-
-	/* jgs3 - Apply the themes */
-	int f, sz;
-	char *s;
-	if((f = open("/dev/theme", OREAD|OCEXEC)) >= 0){
-		if((s = readall(f, &sz)) != nil)
-			themeload(s, sz);
-		free(s);
-		close(f);
-
-		/* Menu */
-		tagcols[BACK] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, theme[Colmenuback].rgb<<8|0xff);
-		tagcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, theme[Colmenuhigh].rgb<<8|0xff);
-		tagcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, theme[Colmenubord].rgb<<8|0xff);
-		tagcols[TEXT] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, theme[Colmenutext].rgb<<8|0xff);
-		tagcols[HTEXT] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, theme[Colmenuhtext].rgb<<8|0xff);
-
-		/* Body */
-		textcols[BACK] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, theme[Colback].rgb<<8|0xff);
-		textcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, theme[Colhigh].rgb<<8|0xff);
-		textcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, theme[Colbord].rgb<<8|0xff);
-		textcols[TEXT] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, theme[Coltext].rgb<<8|0xff);
-		textcols[HTEXT] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, theme[Colhtext].rgb<<8|0xff);
-
-	} else {
-		/* Blue */
-		tagcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
-		tagcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
-		tagcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
-		tagcols[TEXT] = display->black;
-		tagcols[HTEXT] = display->black;
-
-		/* Yellow */
-		textcols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
-		textcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
-		textcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellowgreen);
-		textcols[TEXT] = display->black;
-		textcols[HTEXT] = display->black;
-	}
-
-	if(button){
-		freeimage(button);
-		freeimage(modbutton);
-		freeimage(colbutton);
-	}
-
-	r = Rect(0, 0, Scrollwid+2, font->height+1);
-	button = allocimage(display, r, screen->chan, 0, DNofill);
-	draw(button, r, tagcols[BACK], nil, r.min);
-	r.max.x -= 2;
-	border(button, r, 2, tagcols[BORD], ZP);
-
-	r = button->r;
-	modbutton = allocimage(display, r, screen->chan, 0, DNofill);
-	draw(modbutton, r, tagcols[BACK], nil, r.min);
-	r.max.x -= 2;
-	border(modbutton, r, 2, tagcols[BORD], ZP);
-	r = insetrect(r, 2);
-	tmp = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedblue);
-	draw(modbutton, r, tmp, nil, ZP);
-	freeimage(tmp);
-
-	r = button->r;
-	colbutton = allocimage(display, r, screen->chan, 0, DPurpleblue);
-
-	but2col = allocimage(display, r, screen->chan, 1, 0xAA0000FF);
-	but3col = allocimage(display, r, screen->chan, 1, 0x006600FF);
-}
-
-/*
- * /dev/snarf updates when the file is closed, so we must open our own
- * fd here rather than use snarffd
- */
-
-/* rio truncates larges snarf buffers, so this avoids using the
- * service if the string is huge */
-
-#define MAXSNARF 100*1024
-
-void
-putsnarf(void)
-{
-	int fd, i, n;
-
-	if(snarffd<0 || snarfbuf.nc==0)
-		return;
-	if(snarfbuf.nc > MAXSNARF)
-		return;
-	fd = open("/dev/snarf", OWRITE);
-	if(fd < 0)
-		return;
-	for(i=0; i<snarfbuf.nc; i+=n){
-		n = snarfbuf.nc-i;
-		if(n >= NSnarf)
-			n = NSnarf;
-		bufread(&snarfbuf, i, snarfrune, n);
-		if(fprint(fd, "%.*S", n, snarfrune) < 0)
-			break;
-	}
-	close(fd);
-}
-
-void
-getsnarf()
-{
-	int nulls;
-
-	if(snarfbuf.nc > MAXSNARF)
-		return;
-	if(snarffd < 0)
-		return;
-	seek(snarffd, 0, 0);
-	bufreset(&snarfbuf);
-	bufload(&snarfbuf, 0, snarffd, &nulls);
-}
-
-/* jgs1 - functions to load colors on startup */
-void
-themeload(char *s, int n)
-{
-	int i;
-	char *t, *a[2], *e;
-	Image *newc;
-	u32int rgb;
-
-	if((t = malloc(n+1)) == nil)
-		return;
-	memmove(t, s, n);
-	t[n] = 0;
-
-	for(s = t; s != nil && *s; s = e){
-		if((e = strchr(s, '\n')) != nil)
-			*e++ = 0;
-		if(tokenize(s, a, 2) == 2){
-			for(i = 0; i < nelem(theme); i++) {
-				if(strcmp(theme[i].id, a[0]) == 0) {
-					rgb = strtoul(a[1], nil, 16);
-					if((newc = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, rgb<<8 | 0xff)) != nil) {
-						theme[i].rgb = rgb;
-					}
-					if(new != nil){
-						freeimage(col[i]);
-						col[i] = newc;
-					}
-					break;
-				}
-			}
-		}
-	}
-	free(t);
-}
-
-char *
-themestring(int *n)
-{
-	char *s, *t, *e;
-	int i;
-
-	if((t = malloc(512)) != nil){
-		s = t;
-		e = s+512;
-		for(i = 0; i < nelem(theme); i++)
-			s = seprint(s, e, "%s\t%06ux\n", theme[i].id, theme[i].rgb);
-		*n = s - t;
-	}
-
-	return t;
-}
--- a/addr.c
+++ /dev/null
@@ -1,291 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <thread.h>
-#include <cursor.h>
-#include <mouse.h>
-#include <keyboard.h>
-#include <frame.h>
-#include <fcall.h>
-#include <plumb.h>
-#include "dat.h"
-#include "fns.h"
-
-enum
-{
-	None = 0,
-	Fore = '+',
-	Back = '-',
-};
-
-enum
-{
-	Char,
-	Line,
-};
-
-int
-isaddrc(int r)
-{
-	if(r && utfrune("0123456789+-/$.#,;", r)!=nil)
-		return TRUE;
-	return FALSE;
-}
-
-/*
- * quite hard: could be almost anything but white space, but we are a little conservative,
- * aiming for regular expressions of alphanumerics and no white space
- */
-int
-isregexc(int r)
-{
-	if(r == 0)
-		return FALSE;
-	if(isalnum(r))
-		return TRUE;
-	if(utfrune("^+-.*?#,;[]()$", r)!=nil)
-		return TRUE;
-	return FALSE;
-}
-
-// nlcounttopos starts at q0 and advances nl lines,
-// being careful not to walk past the end of the text,
-// and then nr chars, being careful not to walk past
-// the end of the current line.
-// It returns the final position.
-long
-nlcounttopos(Text *t, long q0, long nl, long nr)
-{
-	while(nl > 0 && q0 < t->file->nc) {
-		if(textreadc(t, q0++) == '\n')
-			nl--;
-	}
-	if(nl > 0)
-		return q0;
-	while(nr > 0 && q0 < t->file->nc && textreadc(t, q0) != '\n') {
-		q0++;
-		nr--;
-	}
-	return q0;
-}
-
-Range
-number(Mntdir *md, Text *t, Range r, int line, int dir, int size, int *evalp)
-{
-	uint q0, q1;
-
-	if(size == Char){
-		if(dir == Fore)
-			line = r.q1+line;
-		else if(dir == Back){
-			if(r.q0==0 && line>0)
-				r.q0 = t->file->nc;
-			line = r.q0 - line;
-		}
-		if(line<0 || line>t->file->nc)
-			goto Rescue;
-		*evalp = TRUE;
-		return (Range){line, line};
-	}
-	q0 = r.q0;
-	q1 = r.q1;
-	switch(dir){
-	case None:
-		q0 = 0;
-		q1 = 0;
-	Forward:
-		while(line>0 && q1<t->file->nc)
-			if(textreadc(t, q1++) == '\n' || q1==t->file->nc)
-				if(--line > 0)
-					q0 = q1;
-		if(line > 0)
-			goto Rescue;
-		break;
-	case Fore:
-		if(q1 > 0)
-			while(q1<t->file->nc && textreadc(t, q1-1) != '\n')
-				q1++;
-		q0 = q1;
-		goto Forward;
-	case Back:
-		if(q0 < t->file->nc)
-			while(q0>0 && textreadc(t, q0-1)!='\n')
-				q0--;
-		q1 = q0;
-		while(line>0 && q0>0){
-			if(textreadc(t, q0-1) == '\n'){
-				if(--line >= 0)
-					q1 = q0;
-			}
-			--q0;
-		}
-		/* :1-1 is :0 = #0, but :1-2 is an error */
-		if(line > 1)
-			goto Rescue;
-		while(q0>0 && textreadc(t, q0-1)!='\n')
-			--q0;
-	}
-	*evalp = TRUE;
-	return (Range){q0, q1};
-
-    Rescue:
-	if(md != nil)
-		warning(nil, "address out of range\n");
-	*evalp = FALSE;
-	return r;
-}
-
-
-Range
-regexp(Mntdir *md, Text *t, Range lim, Range r, Rune *pat, int dir, int *foundp)
-{
-	int found;
-	Rangeset sel;
-	int q;
-
-	if(pat[0] == '\0' && rxnull()){
-		warning(md, "no previous regular expression\n");
-		*foundp = FALSE;
-		return r;
-	}
-	if(pat[0] && rxcompile(pat) == FALSE){
-		*foundp = FALSE;
-		return r;
-	}
-	if(dir == Back)
-		found = rxbexecute(t, r.q0, &sel);
-	else{
-		if(lim.q0 < 0)
-			q = Infinity;
-		else
-			q = lim.q1;
-		found = rxexecute(t, nil, r.q1, q, &sel);
-	}
-	if(!found && md==nil)
-		warning(nil, "no match for regexp\n");
-	*foundp = found;
-	return sel.r[0];
-}
-
-Range
-address(Mntdir *md, Text *t, Range lim, Range ar, void *a, uint q0, uint q1, int (*getc)(void*, uint),  int *evalp, uint *qp)
-{
-	int dir, size, npat;
-	int prevc, c, nc, n;
-	uint q;
-	Rune *pat;
-	Range r, nr;
-
-	r = ar;
-	q = q0;
-	dir = None;
-	size = Line;
-	c = 0;
-	while(q < q1){
-		prevc = c;
-		c = (*getc)(a, q++);
-		switch(c){
-		default:
-			*qp = q-1;
-			return r;
-		case ';':
-			ar = r;
-			/* fall through */
-		case ',':
-			if(prevc == 0)	/* lhs defaults to 0 */
-				r.q0 = 0;
-			if(q>=q1 && t!=nil && t->file!=nil)	/* rhs defaults to $ */
-				r.q1 = t->file->nc;
-			else{
-				nr = address(md, t, lim, ar, a, q, q1, getc, evalp, &q);
-				r.q1 = nr.q1;
-			}
-			*qp = q;
-			return r;
-		case '+':
-		case '-':
-			if(*evalp && (prevc=='+' || prevc=='-'))
-				if((nc=(*getc)(a, q))!='#' && nc!='/' && nc!='?')
-					r = number(md, t, r, 1, prevc, Line, evalp);	/* do previous one */
-			dir = c;
-			break;
-		case '.':
-		case '$':
-			if(q != q0+1){
-				*qp = q-1;
-				return r;
-			}
-			if(*evalp)
-				if(c == '.')
-					r = ar;
-				else
-					r = (Range){t->file->nc, t->file->nc};
-			if(q < q1)
-				dir = Fore;
-			else
-				dir = None;
-			break;
-		case '#':
-			if(q==q1 || (c=(*getc)(a, q++))<'0' || '9'<c){
-				*qp = q-1;
-				return r;
-			}
-			size = Char;
-			/* fall through */
-		case '0': case '1': case '2': case '3': case '4':
-		case '5': case '6': case '7': case '8': case '9':
-			n = c -'0';
-			while(q<q1){
-				nc = (*getc)(a, q++);
-				if(nc<'0' || '9'<nc){
-					q--;
-					break;
-				}
-				n = n*10+(nc-'0');
-			}
-			if(*evalp)
-				r = number(md, t, r, n, dir, size, evalp);
-			dir = None;
-			size = Line;
-			break;
-		case '?':
-			dir = Back;
-			/* fall through */
-		case '/':
-			npat = 0;
-			pat = nil;
-			while(q<q1){
-				c = (*getc)(a, q++);
-				switch(c){
-				case '\n':
-					--q;
-					goto out;
-				case '\\':
-					pat = runerealloc(pat, npat+1);
-					pat[npat++] = c;
-					if(q == q1)
-						goto out;
-					c = (*getc)(a, q++);
-					break;
-				case '/':
-					goto out;
-				}
-				pat = runerealloc(pat, npat+1);
-				pat[npat++] = c;
-			}
-		    out:
-			pat = runerealloc(pat, npat+1);
-			pat[npat] = 0;
-			if(*evalp)
-				r = regexp(md, t, lim, r, pat, dir, evalp);
-			free(pat);
-			dir = None;
-			size = Line;
-			break;
-		}
-	}
-	if(*evalp && dir != None)
-		r = number(md, t, r, 1, dir, Line, evalp);	/* do previous one */
-	*qp = q;
-	return r;
-}
--- a/buff.c
+++ /dev/null
@@ -1,322 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <thread.h>
-#include <cursor.h>
-#include <mouse.h>
-#include <keyboard.h>
-#include <frame.h>
-#include <fcall.h>
-#include <plumb.h>
-#include "dat.h"
-#include "fns.h"
-
-enum
-{
-	Slop = 100,	/* room to grow with reallocation */
-};
-
-static
-void
-sizecache(Buffer *b, uint n)
-{
-	if(n <= b->cmax)
-		return;
-	b->cmax = n+Slop;
-	b->c = runerealloc(b->c, b->cmax);
-}
-
-static
-void
-addblock(Buffer *b, uint i, uint n)
-{
-	if(i > b->nbl)
-		error("internal error: addblock");
-
-	b->bl = realloc(b->bl, (b->nbl+1)*sizeof b->bl[0]);
-	if(i < b->nbl)
-		memmove(b->bl+i+1, b->bl+i, (b->nbl-i)*sizeof(Block*));
-	b->bl[i] = disknewblock(disk, n);
-	b->nbl++;
-}
-
-static
-void
-delblock(Buffer *b, uint i)
-{
-	if(i >= b->nbl)
-		error("internal error: delblock");
-
-	diskrelease(disk, b->bl[i]);
-	b->nbl--;
-	if(i < b->nbl)
-		memmove(b->bl+i, b->bl+i+1, (b->nbl-i)*sizeof(Block*));
-	b->bl = realloc(b->bl, b->nbl*sizeof b->bl[0]);
-}
-
-/*
- * Move cache so b->cq <= q0 < b->cq+b->cnc.
- * If at very end, q0 will fall on end of cache block.
- */
-
-static
-void
-flush(Buffer *b)
-{
-	if(b->cdirty || b->cnc==0){
-		if(b->cnc == 0)
-			delblock(b, b->cbi);
-		else
-			diskwrite(disk, &b->bl[b->cbi], b->c, b->cnc);
-		b->cdirty = FALSE;
-	}
-}
-
-static
-void
-setcache(Buffer *b, uint q0)
-{
-	Block **blp, *bl;
-	uint i, q;
-
-	if(q0 > b->nc)
-		error("internal error: setcache");
-	/*
-	 * flush and reload if q0 is not in cache.
-	 */
-	if(b->nc == 0 || (b->cq<=q0 && q0<b->cq+b->cnc))
-		return;
-	/*
-	 * if q0 is at end of file and end of cache, continue to grow this block
-	 */
-	if(q0==b->nc && q0==b->cq+b->cnc && b->cnc<Maxblock)
-		return;
-	flush(b);
-	/* find block */
-	if(q0 < b->cq){
-		q = 0;
-		i = 0;
-	}else{
-		q = b->cq;
-		i = b->cbi;
-	}
-	blp = &b->bl[i];
-	while(q+(*blp)->n <= q0 && q+(*blp)->n < b->nc){
-		q += (*blp)->n;
-		i++;
-		blp++;
-		if(i >= b->nbl)
-			error("block not found");
-	}
-	bl = *blp;
-	/* remember position */
-	b->cbi = i;
-	b->cq = q;
-	sizecache(b, bl->n);
-	b->cnc = bl->n;
-	/*read block*/
-	diskread(disk, bl, b->c, b->cnc);
-}
-
-void
-bufinsert(Buffer *b, uint q0, Rune *s, uint n)
-{
-	uint i, m, t, off;
-
-	if(q0 > b->nc)
-		error("internal error: bufinsert");
-
-	while(n > 0){
-		setcache(b, q0);
-		off = q0-b->cq;
-		if(b->cnc+n <= Maxblock){
-			/* Everything fits in one block. */
-			t = b->cnc+n;
-			m = n;
-			if(b->bl == nil){	/* allocate */
-				if(b->cnc != 0)
-					error("internal error: bufinsert1 cnc!=0");
-				addblock(b, 0, t);
-				b->cbi = 0;
-			}
-			sizecache(b, t);
-			runemove(b->c+off+m, b->c+off, b->cnc-off);
-			runemove(b->c+off, s, m);
-			b->cnc = t;
-			goto Tail;
-		}
-		/*
-		 * We must make a new block.  If q0 is at
-		 * the very beginning or end of this block,
-		 * just make a new block and fill it.
-		 */
-		if(q0==b->cq || q0==b->cq+b->cnc){
-			if(b->cdirty)
-				flush(b);
-			m = min(n, Maxblock);
-			if(b->bl == nil){	/* allocate */
-				if(b->cnc != 0)
-					error("internal error: bufinsert2 cnc!=0");
-				i = 0;
-			}else{
-				i = b->cbi;
-				if(q0 > b->cq)
-					i++;
-			}
-			addblock(b, i, m);
-			sizecache(b, m);
-			runemove(b->c, s, m);
-			b->cq = q0;
-			b->cbi = i;
-			b->cnc = m;
-			goto Tail;
-		}
-		/*
-		 * Split the block; cut off the right side and
-		 * let go of it.
-		 */
-		m = b->cnc-off;
-		if(m > 0){
-			i = b->cbi+1;
-			addblock(b, i, m);
-			diskwrite(disk, &b->bl[i], b->c+off, m);
-			b->cnc -= m;
-		}
-		/*
-		 * Now at end of block.  Take as much input
-		 * as possible and tack it on end of block.
-		 */
-		m = min(n, Maxblock-b->cnc);
-		sizecache(b, b->cnc+m);
-		runemove(b->c+b->cnc, s, m);
-		b->cnc += m;
-  Tail:
-		b->nc += m;
-		q0 += m;
-		s += m;
-		n -= m;
-		b->cdirty = TRUE;
-	}
-}
-
-void
-bufdelete(Buffer *b, uint q0, uint q1)
-{
-	uint m, n, off;
-
-	if(!(q0<=q1 && q0<=b->nc && q1<=b->nc))
-		error("internal error: bufdelete");
-	while(q1 > q0){
-		setcache(b, q0);
-		off = q0-b->cq;
-		if(q1 > b->cq+b->cnc)
-			n = b->cnc - off;
-		else
-			n = q1-q0;
-		m = b->cnc - (off+n);
-		if(m > 0)
-			runemove(b->c+off, b->c+off+n, m);
-		b->cnc -= n;
-		b->cdirty = TRUE;
-		q1 -= n;
-		b->nc -= n;
-	}
-}
-
-static int
-bufloader(void *v, uint q0, Rune *r, int nr)
-{
-	bufinsert(v, q0, r, nr);
-	return nr;
-}
-
-uint
-loadfile(int fd, uint q0, int *nulls, int(*f)(void*, uint, Rune*, int), void *arg)
-{
-	char *p;
-	Rune *r;
-	int l, m, n, nb, nr;
-	uint q1;
-
-	p = emalloc((Maxblock+UTFmax+1)*sizeof p[0]);
-	r = runemalloc(Maxblock);
-	m = 0;
-	n = 1;
-	q1 = q0;
-	/*
-	 * At top of loop, may have m bytes left over from
-	 * last pass, possibly representing a partial rune.
-	 */
-	while(n > 0){
-		n = read(fd, p+m, Maxblock);
-		if(n < 0){
-			warning(nil, "read error in Buffer.load");
-			break;
-		}
-		m += n;
-		p[m] = 0;
-		l = m;
-		if(n > 0)
-			l -= UTFmax;
-		cvttorunes(p, l, r, &nb, &nr, nulls);
-		memmove(p, p+nb, m-nb);
-		m -= nb;
-		q1 += (*f)(arg, q1, r, nr);
-	}
-	free(p);
-	free(r);
-	return q1-q0;
-}
-
-uint
-bufload(Buffer *b, uint q0, int fd, int *nulls)
-{
-	if(q0 > b->nc)
-		error("internal error: bufload");
-	return loadfile(fd, q0, nulls, bufloader, b);
-}
-
-void
-bufread(Buffer *b, uint q0, Rune *s, uint n)
-{
-	uint m;
-
-	if(!(q0<=b->nc && q0+n<=b->nc))
-		error("bufread: internal error");
-
-	while(n > 0){
-		setcache(b, q0);
-		m = min(n, b->cnc-(q0-b->cq));
-		runemove(s, b->c+(q0-b->cq), m);
-		q0 += m;
-		s += m;
-		n -= m;
-	}
-}
-
-void
-bufreset(Buffer *b)
-{
-	int i;
-
-	b->nc = 0;
-	b->cnc = 0;
-	b->cq = 0;
-	b->cdirty = 0;
-	b->cbi = 0;
-	/* delete backwards to avoid n² behavior */
-	for(i=b->nbl-1; --i>=0; )
-		delblock(b, i);
-}
-
-void
-bufclose(Buffer *b)
-{
-	bufreset(b);
-	free(b->c);
-	b->c = nil;
-	b->cnc = 0;
-	free(b->bl);
-	b->bl = nil;
-	b->nbl = 0;
-}
--- a/cols.c
+++ /dev/null
@@ -1,561 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <thread.h>
-#include <cursor.h>
-#include <mouse.h>
-#include <keyboard.h>
-#include <frame.h>
-#include <fcall.h>
-#include <plumb.h>
-#include "dat.h"
-#include "fns.h"
-
-void
-colinit(Column *c, Rectangle r)
-{
-	Rectangle r1;
-	Text *t;
-
-	draw(screen, r, display->white, nil, ZP);
-	c->r = r;
-	c->w = nil;
-	c->nw = 0;
-	t = &c->tag;
-	t->w = nil;
-	t->col = c;
-	r1 = r;
-	r1.max.y = r1.min.y + font->height;
-	textinit(t, fileaddtext(nil, t), r1, &reffont, tagcols);
-	t->what = Columntag;
-	r1.min.y = r1.max.y;
-	r1.max.y += Border;
-	draw(screen, r1, display->black, nil, ZP);
-	textinsert(t, 0, L"New Cut Paste Snarf Sort Zerox Delcol ", 38, TRUE);
-	textsetselect(t, t->file->nc, t->file->nc);
-	draw(screen, t->scrollr, colbutton, nil, colbutton->r.min);
-	c->safe = TRUE;
-}
-
-Window*
-coladd(Column *c, Window *w, Window *clone, int y)
-{
-	Rectangle r, r1;
-	Window *v;
-	int i, t;
-
-	v = nil;
-	r = c->r;
-	r.min.y = c->tag.r.max.y+Border;
-	if(y<r.min.y && c->nw>0){	/* steal half of last window by default */
-		v = c->w[c->nw-1];
-		y = v->body.r.min.y+Dy(v->body.r)/2;
-	}
-	/* look for window we'll land on */
-	for(i=0; i<c->nw; i++){
-		v = c->w[i];
-		if(y < v->r.max.y)
-			break;
-	}
-	if(c->nw > 0){
-		if(i < c->nw)
-			i++;	/* new window will go after v */
-		/*
-		 * if v's too small, grow it first.
-		 */
-		if(!c->safe || v->body.maxlines<=3){
-			colgrow(c, v, 1);
-			y = v->body.r.min.y+Dy(v->body.r)/2;
-		}
-		r = v->r;
-		if(i == c->nw)
-			t = c->r.max.y;
-		else
-			t = c->w[i]->r.min.y-Border;
-		r.max.y = t;
-		draw(screen, r, textcols[BACK], nil, ZP);
-		r1 = r;
-		y = min(y, t-(v->tag.font->height+v->body.font->height+Border+1));
-		r1.max.y = min(y, v->body.r.min.y+v->body.nlines*v->body.font->height);
-		r1.min.y = winresize(v, r1, FALSE);
-		r1.max.y = r1.min.y+Border;
-		draw(screen, r1, display->black, nil, ZP);
-		r.min.y = r1.max.y;
-	}
-	if(w == nil){
-		w = emalloc(sizeof(Window));
-		w->rdselfd = -1;
-		w->col = c;
-		draw(screen, r, textcols[BACK], nil, ZP);
-		wininit(w, clone, r);
-	}else{
-		w->col = c;
-		winresize(w, r, FALSE);
-	}
-	w->tag.col = c;
-	w->tag.row = c->row;
-	w->body.col = c;
-	w->body.row = c->row;
-	c->w = realloc(c->w, (c->nw+1)*sizeof(Window*));
-	memmove(c->w+i+1, c->w+i, (c->nw-i)*sizeof(Window*));
-	c->nw++;
-	c->w[i] = w;
-	savemouse(w);
-	/* near but not on the button */
-	moveto(mousectl, addpt(w->tag.scrollr.max, Pt(3, 3)));
-	barttext = &w->body;
-	c->safe = TRUE;
-	return w;
-}
-
-void
-colclose(Column *c, Window *w, int dofree)
-{
-	Rectangle r;
-	int i, didmouse, up;
-
-	/* w is locked */
-	if(!c->safe)
-		colgrow(c, w, 1);
-	for(i=0; i<c->nw; i++)
-		if(c->w[i] == w)
-			goto Found;
-	error("can't find window");
-  Found:
-	r = w->r;
-	w->tag.col = nil;
-	w->body.col = nil;
-	w->col = nil;
-	didmouse = restoremouse(w);
-	if(dofree){
-		windelete(w);
-		winclose(w);
-	}
-	c->nw--;
-	memmove(c->w+i, c->w+i+1, (c->nw-i)*sizeof(Window*));
-	c->w = realloc(c->w, c->nw*sizeof(Window*));
-	if(c->nw == 0){
-		draw(screen, r, display->white, nil, ZP);
-		return;
-	}
-	up = 0;
-	if(i == c->nw){		/* extend last window down */
-		w = c->w[i-1];
-		r.min.y = w->r.min.y;
-		r.max.y = c->r.max.y;
-	}else{			/* extend next window up */
-		up = 1;
-		w = c->w[i];
-		r.max.y = w->r.max.y;
-	}
-	draw(screen, r, textcols[BACK], nil, ZP);
-	if(c->safe) {
-		if(!didmouse && up)
-			w->showdel = TRUE;
-		winresize(w, r, FALSE);
-		if(!didmouse && up)
-			movetodel(w);
-	}
-}
-
-void
-colcloseall(Column *c)
-{
-	int i;
-	Window *w;
-
-	if(c == activecol)
-		activecol = nil;
-	textclose(&c->tag);
-	for(i=0; i<c->nw; i++){
-		w = c->w[i];
-		winclose(w);
-	}
-	c->nw = 0;
-	free(c->w);
-	free(c);
-	clearmouse();
-}
-
-void
-colmousebut(Column *c)
-{
-	moveto(mousectl, divpt(addpt(c->tag.scrollr.min, c->tag.scrollr.max), 2));
-}
-
-void
-colresize(Column *c, Rectangle r)
-{
-	int i, old, new;
-	Rectangle r1, r2;
-	Window *w;
-
-	clearmouse();
-	r1 = r;
-	r1.max.y = r1.min.y + c->tag.font->height;
-	textresize(&c->tag, r1);
-	draw(screen, c->tag.scrollr, colbutton, nil, colbutton->r.min);
-	r1.min.y = r1.max.y;
-	r1.max.y += Border;
-	draw(screen, r1, display->black, nil, ZP);
-	r1.max.y = r.max.y;
-	new = Dy(r) - c->nw*(Border + font->height);
-	old = Dy(c->r) - c->nw*(Border + font->height);
-	for(i=0; i<c->nw; i++){
-		w = c->w[i];
-		w->maxlines = 0;
-		if(i == c->nw-1)
-			r1.max.y = r.max.y;
-		else {
-			r1.max.y = r1.min.y;
-			if(new > 0 && old > 0 && Dy(w->r) > font->height)
-				r1.max.y += (Dy(w->r)-font->height)*new/old + Border + font->height;
-		}
-		r2 = r1;
-		r2.max.y = r2.min.y+Border;
-		draw(screen, r2, display->black, nil, ZP);
-		r1.min.y = r2.max.y;
-		r1.min.y = winresize(w, r1, FALSE);
-	}
-	c->r = r;
-}
-
-static
-int
-colcmp(void *a, void *b)
-{
-	Rune *r1, *r2;
-	int i, nr1, nr2;
-
-	r1 = (*(Window**)a)->body.file->name;
-	nr1 = (*(Window**)a)->body.file->nname;
-	r2 = (*(Window**)b)->body.file->name;
-	nr2 = (*(Window**)b)->body.file->nname;
-	for(i=0; i<nr1 && i<nr2; i++){
-		if(*r1 != *r2)
-			return *r1-*r2;
-		r1++;
-		r2++;
-	}
-	return nr1-nr2;
-}
-
-void
-colsort(Column *c)
-{
-	int i, y;
-	Rectangle r, r1, *rp;
-	Window **wp, *w;
-
-	if(c->nw == 0)
-		return;
-	clearmouse();
-	rp = emalloc(c->nw*sizeof(Rectangle));
-	wp = emalloc(c->nw*sizeof(Window*));
-	memmove(wp, c->w, c->nw*sizeof(Window*));
-	qsort(wp, c->nw, sizeof(Window*), colcmp);
-	for(i=0; i<c->nw; i++)
-		rp[i] = wp[i]->r;
-	r = c->r;
-	r.min.y = c->tag.r.max.y;
-	draw(screen, r, textcols[BACK], nil, ZP);
-	y = r.min.y;
-	for(i=0; i<c->nw; i++){
-		w = wp[i];
-		r.min.y = y;
-		if(i == c->nw-1)
-			r.max.y = c->r.max.y;
-		else
-			r.max.y = r.min.y+Dy(w->r)+Border;
-		r1 = r;
-		r1.max.y = r1.min.y+Border;
-		draw(screen, r1, display->black, nil, ZP);
-		r.min.y = r1.max.y;
-		y = winresize(w, r, FALSE);
-	}
-	free(rp);
-	free(c->w);
-	c->w = wp;
-}
-
-void
-colgrow(Column *c, Window *w, int but)
-{
-	Rectangle r, cr;
-	int i, j, k, l, y1, y2, *nl, *ny, tot, nnl, onl, dnl, h;
-	Window *v;
-
-	for(i=0; i<c->nw; i++)
-		if(c->w[i] == w)
-			goto Found;
-	error("can't find window");
-
-  Found:
-	cr = c->r;
-	if(but < 0){	/* make sure window fills its own space properly */
-		r = w->r;
-		if(i==c->nw-1 || c->safe==FALSE)
-			r.max.y = cr.max.y;
-		else
-			r.max.y = c->w[i+1]->r.min.y;
-		winresize(w, r, FALSE);
-		return;
-	}
-	cr.min.y = c->w[0]->r.min.y;
-	if(but == 3){	/* full size */
-		if(i != 0){
-			v = c->w[0];
-			c->w[0] = w;
-			c->w[i] = v;
-		}
-		draw(screen, cr, textcols[BACK], nil, ZP);
-		winresize(w, cr, FALSE);
-		for(i=1; i<c->nw; i++)
-			c->w[i]->body.maxlines = 0;
-		c->safe = FALSE;
-		return;
-	}
-	/* store old #lines for each window */
-	onl = w->body.maxlines;
-	nl = emalloc(c->nw * sizeof(int));
-	ny = emalloc(c->nw * sizeof(int));
-	tot = 0;
-	for(j=0; j<c->nw; j++){
-		l = c->w[j]->body.maxlines;
-		nl[j] = l;
-		tot += l;
-	}
-	/* approximate new #lines for this window */
-	if(but == 2){	/* as big as can be */
-		memset(nl, 0, c->nw * sizeof(int));
-		goto Pack;
-	}
-	nnl = min(onl + max(min(5, w->maxlines), onl/2), tot);
-	if(nnl < w->maxlines)
-		nnl = (w->maxlines+nnl)/2;
-	if(nnl == 0)
-		nnl = 2;
-	dnl = nnl - onl;
-	/* compute new #lines for each window */
-	for(k=1; k<c->nw; k++){
-		/* prune from later window */
-		j = i+k;
-		if(j<c->nw && nl[j]){
-			l = min(dnl, max(1, nl[j]/2));
-			nl[j] -= l;
-			nl[i] += l;
-			dnl -= l;
-		}
-		/* prune from earlier window */
-		j = i-k;
-		if(j>=0 && nl[j]){
-			l = min(dnl, max(1, nl[j]/2));
-			nl[j] -= l;
-			nl[i] += l;
-			dnl -= l;
-		}
-	}
-    Pack:
-	/* pack everyone above */
-	y1 = cr.min.y;
-	for(j=0; j<i; j++){
-		v = c->w[j];
-		r = v->r;
-		r.min.y = y1;
-		r.max.y = y1+Dy(v->tag.all);
-		if(nl[j])
-			r.max.y += 1 + nl[j]*v->body.font->height;
-		if(!c->safe || !eqrect(v->r, r)){
-			draw(screen, r, textcols[BACK], nil, ZP);
-			winresize(v, r, c->safe);
-		}
-		r.min.y = v->r.max.y;
-		r.max.y += Border;
-		draw(screen, r, display->black, nil, ZP);
-		y1 = r.max.y;
-	}
-	/* scan to see new size of everyone below */
-	y2 = c->r.max.y;
-	for(j=c->nw-1; j>i; j--){
-		v = c->w[j];
-		r = v->r;
-		r.min.y = y2-Dy(v->tag.all);
-		if(nl[j])
-			r.min.y -= 1 + nl[j]*v->body.font->height;
-		r.min.y -= Border;
-		ny[j] = r.min.y;
-		y2 = r.min.y;
-	}
-	/* compute new size of window */
-	r = w->r;
-	r.min.y = y1;
-	r.max.y = r.min.y+Dy(w->tag.all);
-	h = w->body.font->height;
-	if(y2-r.max.y >= 1+h+Border){
-		r.max.y += 1;
-		r.max.y += h*((y2-r.max.y)/h);
-	}
-	/* draw window */
-	if(!c->safe || !eqrect(w->r, r)){
-		draw(screen, r, textcols[BACK], nil, ZP);
-		winresize(w, r, c->safe);
-	}
-	if(i < c->nw-1){
-		r.min.y = r.max.y;
-		r.max.y += Border;
-		draw(screen, r, display->black, nil, ZP);
-		for(j=i+1; j<c->nw; j++)
-			ny[j] -= (y2-r.max.y);
-	}
-	/* pack everyone below */
-	y1 = r.max.y;
-	for(j=i+1; j<c->nw; j++){
-		v = c->w[j];
-		r = v->r;
-		r.min.y = y1;
-		r.max.y = y1+Dy(v->tag.all);
-		if(nl[j])
-			r.max.y += 1 + nl[j]*v->body.font->height;
-		if(!c->safe || !eqrect(v->r, r)){
-			draw(screen, r, textcols[BACK], nil, ZP);
-			winresize(v, r, c->safe);
-		}
-		if(j < c->nw-1){	/* no border on last window */
-			r.min.y = v->r.max.y;
-			r.max.y = r.min.y + Border;
-			draw(screen, r, display->black, nil, ZP);
-		}
-		y1 = r.max.y;
-	}
-	r = w->r;
-	r.min.y = y1;
-	r.max.y = c->r.max.y;
-	draw(screen, r, textcols[BACK], nil, ZP);
-	free(nl);
-	free(ny);
-	c->safe = TRUE;
-	winmousebut(w);
-}
-
-void
-coldragwin(Column *c, Window *w, int but)
-{
-	Rectangle r;
-	int i, b;
-	Point p, op;
-	Window *v;
-	Column *nc;
-
-	clearmouse();
-	setcursor(mousectl, &boxcursor);
-	b = mouse->buttons;
-	op = mouse->xy;
-	while(mouse->buttons == b)
-		readmouse(mousectl);
-	setcursor(mousectl, nil);
-	if(mouse->buttons){
-		while(mouse->buttons)
-			readmouse(mousectl);
-		return;
-	}
-
-	for(i=0; i<c->nw; i++)
-		if(c->w[i] == w)
-			goto Found;
-	error("can't find window");
-
-  Found:
-	p = mouse->xy;
-	if(abs(p.x-op.x)<5 && abs(p.y-op.y)<5){
-		colgrow(c, w, but);
-		winmousebut(w);
-		return;
-	}
-	/* is it a flick to the right? */
-	if(abs(p.y-op.y)<10 && p.x>op.x+30 && rowwhichcol(c->row, p)==c)
-		p.x = op.x+Dx(w->r);	/* yes: toss to next column */
-	nc = rowwhichcol(c->row, p);
-	if(nc!=nil && nc!=c){
-		colclose(c, w, FALSE);
-		coladd(nc, w, nil, p.y);
-		winmousebut(w);
-		return;
-	}
-	if(i==0 && c->nw==1)
-		return;			/* can't do it */
-	if((i>0 && p.y<c->w[i-1]->r.min.y) || (i<c->nw-1 && p.y>w->r.max.y)
-	|| (i==0 && p.y>w->r.max.y)){
-		/* shuffle */
-		colclose(c, w, FALSE);
-		coladd(c, w, nil, p.y);
-		winmousebut(w);
-		return;
-	}
-	if(i == 0)
-		return;
-	v = c->w[i-1];
-	if(p.y < v->tag.all.max.y)
-		p.y = v->tag.all.max.y;
-	if(p.y > w->r.max.y-Dy(w->tag.all)-Border)
-		p.y = w->r.max.y-Dy(w->tag.all)-Border;
-	r = v->r;
-	r.max.y = p.y;
-	if(r.max.y > v->body.r.min.y){
-		r.max.y -= (r.max.y-v->body.r.min.y)%v->body.font->height;
-		if(v->body.r.min.y == v->body.r.max.y)
-			r.max.y++;
-	}
-	if(!eqrect(v->r, r)){
-		draw(screen, r, textcols[BACK], nil, ZP);
-		winresize(v, r, c->safe);
-	}
-	r.min.y = v->r.max.y;
-	r.max.y = r.min.y+Border;
-	draw(screen, r, display->black, nil, ZP);
-	r.min.y = r.max.y;
-	if(i == c->nw-1)
-		r.max.y = c->r.max.y;
-	else
-		r.max.y = c->w[i+1]->r.min.y-Border;
-	if(!eqrect(w->r, r)){
-		draw(screen, r, textcols[BACK], nil, ZP);
-		winresize(w, r, c->safe);
-	}
-	c->safe = TRUE;
-    	winmousebut(w);
-}
-
-Text*
-colwhich(Column *c, Point p)
-{
-	int i;
-	Window *w;
-
-	if(!ptinrect(p, c->r))
-		return nil;
-	if(ptinrect(p, c->tag.all))
-		return &c->tag;
-	for(i=0; i<c->nw; i++){
-		w = c->w[i];
-		if(ptinrect(p, w->r)){
-			if(ptinrect(p, w->tag.all))
-				return &w->tag;
-			return &w->body;
-		}
-		/* scrollr drops below w->r on low windows */
-		if(ptinrect(p, w->body.scrollr))
-			return &w->body;
-	}
-	return nil;
-}
-
-int
-colclean(Column *c)
-{
-	int i, clean;
-
-	clean = TRUE;
-	for(i=0; i<c->nw; i++)
-		clean &= winclean(c->w[i], TRUE);
-	return clean;
-}
--- a/dat.h
+++ /dev/null
@@ -1,636 +1,0 @@
-enum
-{
-	Qdir,
-	Qacme,
-	Qcons,
-	Qconsctl,
-	Qdraw,
-	Qeditout,
-	Qindex,
-	Qlabel,
-	Qlog,
-	Qnew,
-
-	QWaddr,
-	QWbody,
-	QWctl,
-	QWdata,
-	QWeditout,
-	QWerrors,
-	QWevent,
-	QWrdsel,
-	QWwrsel,
-	QWtag,
-	QWxdata,
-	QMAX,
-};
-
-enum
-{
-	Blockincr =	256,
-	Maxblock = 	8*1024,
-	NRange =		10,
-	Infinity = 		0x7FFFFFFF,	/* huge value for regexp address */
-};
-
-typedef	struct	Block Block;
-typedef	struct	Buffer Buffer;
-typedef	struct	Command Command;
-typedef	struct	Column Column;
-typedef	struct	Dirlist Dirlist;
-typedef	struct	Dirtab Dirtab;
-typedef	struct	Disk Disk;
-typedef	struct	Expand Expand;
-typedef	struct	Fid Fid;
-typedef	struct	File File;
-typedef	struct	Elog Elog;
-typedef	struct	Mntdir Mntdir;
-typedef	struct	Range Range;
-typedef	struct	Rangeset Rangeset;
-typedef	struct	Reffont Reffont;
-typedef	struct	Row Row;
-typedef	struct	Runestr Runestr;
-typedef	struct	Text Text;
-typedef	struct	Timer Timer;
-typedef	struct	Window Window;
-typedef	struct	Xfid Xfid;
-
-struct Runestr
-{
-	Rune	*r;
-	int	nr;
-};
-
-struct Range
-{
-	int	q0;
-	int	q1;
-};
-
-struct Block
-{
-	vlong		addr;	/* disk address in bytes */
-	union
-	{
-		uint	n;		/* number of used runes in block */
-		Block	*next;	/* pointer to next in free list */
-	};
-};
-
-struct Disk
-{
-	int		fd;
-	vlong		addr;	/* length of temp file */
-	Block	*free[Maxblock/Blockincr+1];
-};
-
-Disk*	diskinit(void);
-Block*	disknewblock(Disk*, uint);
-void		diskrelease(Disk*, Block*);
-void		diskread(Disk*, Block*, Rune*, uint);
-void		diskwrite(Disk*, Block**, Rune*, uint);
-
-struct Buffer
-{
-	uint	nc;
-	Rune	*c;			/* cache */
-	uint	cnc;			/* bytes in cache */
-	uint	cmax;		/* size of allocated cache */
-	uint	cq;			/* position of cache */
-	int		cdirty;	/* cache needs to be written */
-	uint	cbi;			/* index of cache Block */
-	Block	**bl;		/* array of blocks */
-	uint	nbl;			/* number of blocks */
-};
-void		bufinsert(Buffer*, uint, Rune*, uint);
-void		bufdelete(Buffer*, uint, uint);
-uint		bufload(Buffer*, uint, int, int*);
-void		bufread(Buffer*, uint, Rune*, uint);
-void		bufclose(Buffer*);
-void		bufreset(Buffer*);
-
-struct Elog
-{
-	short	type;		/* Delete, Insert, Filename */
-	uint		q0;		/* location of change (unused in f) */
-	uint		nd;		/* number of deleted characters */
-	uint		nr;		/* # runes in string or file name */
-	Rune		*r;
-};
-void	elogterm(File*);
-void	elogclose(File*);
-void	eloginsert(File*, int, Rune*, int);
-void	elogdelete(File*, int, int);
-void	elogreplace(File*, int, int, Rune*, int);
-void	elogapply(File*);
-
-struct File
-{
-	Buffer;			/* the data */
-	Buffer	delta;	/* transcript of changes */
-	Buffer	epsilon;	/* inversion of delta for redo */
-	Buffer	*elogbuf;	/* log of pending editor changes */
-	Elog		elog;		/* current pending change */
-	Rune		*name;	/* name of associated file */
-	int		nname;	/* size of name */
-	uvlong	qidpath;	/* of file when read */
-	uint		mtime;	/* of file when read */
-	int		dev;		/* of file when read */
-	int		unread;	/* file has not been read from disk */
-	int		editclean;	/* mark clean after edit command */
-
-	int		seq;		/* if seq==0, File acts like Buffer */
-	int		mod;
-	Text		*curtext;	/* most recently used associated text */
-	Text		**text;	/* list of associated texts */
-	int		ntext;
-	int		dumpid;	/* used in dumping zeroxed windows */
-};
-File*		fileaddtext(File*, Text*);
-void		fileclose(File*);
-void		filedelete(File*, uint, uint);
-void		filedeltext(File*, Text*);
-void		fileinsert(File*, uint, Rune*, uint);
-uint		fileload(File*, uint, int, int*);
-void		filemark(File*);
-void		filereset(File*);
-void		filesetname(File*, Rune*, int);
-void		fileundelete(File*, Buffer*, uint, uint);
-void		fileuninsert(File*, Buffer*, uint, uint);
-void		fileunsetname(File*, Buffer*);
-void		fileundo(File*, int, uint*, uint*);
-uint		fileredoseq(File*);
-
-enum	/* Text.what */
-{
-	Columntag,
-	Rowtag,
-	Tag,
-	Body,
-};
-
-struct Text
-{
-	File		*file;
-	Frame;
-	Reffont	*reffont;
-	uint	org;
-	uint	q0;
-	uint	q1;
-	int	what;
-	int	tabstop;
-	Window	*w;
-	Rectangle scrollr;
-	Rectangle lastsr;
-	Rectangle all;
-	Row		*row;
-	Column	*col;
-
-	uint	eq0;	/* start of typing for ESC */
-	uint	cq0;	/* cache position */
-	int		ncache;	/* storage for insert */
-	int		ncachealloc;
-	Rune	*cache;
-	int	nofill;
-	int	needundo;
-};
-
-uint		textbacknl(Text*, uint, uint);
-uint		textbsinsert(Text*, uint, Rune*, uint, int, int*);
-int		textbswidth(Text*, Rune);
-int		textclickmatch(Text*, int, int, int, uint*);
-void		textclose(Text*);
-void		textcolumnate(Text*, Dirlist**, int);
-void		textcommit(Text*, int);
-void		textconstrain(Text*, uint, uint, uint*, uint*);
-void		textdelete(Text*, uint, uint, int);
-void		textstretchsel(Text*, uint, uint*, uint*, int);
-void		textfill(Text*);
-void		textframescroll(Text*, int);
-void		textinit(Text*, File*, Rectangle, Reffont*, Image**);
-void		textinsert(Text*, uint, Rune*, uint, int);
-uint		textload(Text*, uint, char*, int);
-Rune		textreadc(Text*, uint);
-void		textredraw(Text*, Rectangle, Font*, Image*, int);
-void		textreset(Text*);
-int		textresize(Text*, Rectangle);
-void		textscrdraw(Text*);
-void		textscroll(Text*, int);
-void		textselect(Text*);
-int		textselect2(Text*, uint*, uint*, Text**);
-int		textselect23(Text*, uint*, uint*, Image*, int);
-int		textselect3(Text*, uint*, uint*);
-void		textsetorigin(Text*, uint, int);
-void		textsetselect(Text*, uint, uint);
-void		textshow(Text*, uint, uint, int);
-void		texttype(Text*, Rune);
-
-enum
-{
-	SPACESINDENT	= 0,
-	AUTOINDENT,
-	NINDENT,
-};
-
-struct Window
-{
-		QLock;
-		Ref;
-	Text		tag;
-	Text		body;
-	Rectangle	r;
-	uchar	isdir;
-	uchar	isscratch;
-	uchar	filemenu;
-	uchar	dirty;
-	uchar	indent[NINDENT];
-	uchar	showdel;
-	uint		noredraw;
-	int		id;
-	Range	addr;
-	Range	limit;
-	uchar	nopen[QMAX];
-	uchar	nomark;
-	uchar	noscroll;
-	Range	wrselrange;
-	int		rdselfd;
-	Column	*col;
-	Xfid		*eventx;
-	char		*events;
-	int		nevents;
-	int		owner;
-	int		maxlines;
-	Dirlist	**dlp;
-	int		ndl;
-	int		putseq;
-	int		nincl;
-	Rune		**incl;
-	Reffont	*reffont;
-	QLock	ctllock;
-	uint		ctlfid;
-	char		*dumpstr;
-	char		*dumpdir;
-	int		dumpid;
-	int		utflastqid;
-	int		utflastboff;
-	int		utflastq;
-	int		tagsafe;		/* taglines is correct */
-	int		tagexpand;
-	int		taglines;
-	Rectangle	tagtop;
-};
-
-void	wininit(Window*, Window*, Rectangle);
-void	winlock(Window*, int);
-void	winlock1(Window*, int);
-void	winunlock(Window*);
-void	wintype(Window*, Text*, Rune);
-void	winundo(Window*, int);
-void	winsetname(Window*, Rune*, int);
-void	winsettag(Window*);
-void	winsettag1(Window*);
-void	wincommit(Window*, Text*);
-int	winresize(Window*, Rectangle, int);
-void	winclose(Window*);
-void	windelete(Window*);
-int	winclean(Window*, int);
-void	windirfree(Window*);
-void	winevent(Window*, char*, ...);
-void	winmousebut(Window*);
-void	winaddincl(Window*, Rune*, int);
-void	wincleartag(Window*);
-char	*winctlprint(Window*, char*, int);
-
-struct Column
-{
-	Rectangle r;
-	Text	tag;
-	Row		*row;
-	Window	**w;
-	int		nw;
-	int		safe;
-};
-
-void		colinit(Column*, Rectangle);
-Window*	coladd(Column*, Window*, Window*, int);
-void		colclose(Column*, Window*, int);
-void		colcloseall(Column*);
-void		colresize(Column*, Rectangle);
-Text*	colwhich(Column*, Point);
-void		coldragwin(Column*, Window*, int);
-void		colgrow(Column*, Window*, int);
-int		colclean(Column*);
-void		colsort(Column*);
-void		colmousebut(Column*);
-
-struct Row
-{
-	QLock;
-	Rectangle r;
-	Text	tag;
-	Column	**col;
-	int		ncol;
-
-};
-
-void		rowinit(Row*, Rectangle);
-Column*	rowadd(Row*, Column *c, int);
-void		rowclose(Row*, Column*, int);
-Text*	rowwhich(Row*, Point);
-Column*	rowwhichcol(Row*, Point);
-void		rowresize(Row*, Rectangle);
-Text*	rowtype(Row*, Rune, Point);
-void		rowdragcol(Row*, Column*, int but);
-int		rowclean(Row*);
-void		rowdump(Row*, char*);
-int		rowload(Row*, char*, int);
-void		rowloadfonts(char*);
-
-struct Timer
-{
-	int		dt;
-	int		cancel;
-	Channel	*c;	/* chan(int) */
-	Timer	*next;
-};
-
-struct Command
-{
-	int		pid;
-	Rune		*name;
-	int		nname;
-	char		*text;
-	char		**av;
-	int		iseditcmd;
-	Mntdir	*md;
-	Command	*next;
-};
-
-struct Dirtab
-{
-	char	*name;
-	uchar	type;
-	uint	qid;
-	uint	perm;
-};
-
-struct Mntdir
-{
-	int		id;
-	int		ref;
-	Rune		*dir;
-	int		ndir;
-	Mntdir	*next;
-	int		nincl;
-	Rune		**incl;
-};
-
-struct Fid
-{
-	int		fid;
-	int		busy;
-	int		open;
-	Qid		qid;
-	Window	*w;
-	Dirtab	*dir;
-	Fid		*next;
-	Mntdir	*mntdir;
-	int		nrpart;
-	uchar	rpart[UTFmax];
-	vlong	logoff;	// for putlog
-};
-
-
-struct Xfid
-{
-	void		*arg;	/* args to xfidinit */
-	Fcall;
-	Xfid	*next;
-	Channel	*c;		/* chan(void(*)(Xfid*)) */
-	Fid	*f;
-	uchar	*buf;
-	int	flushed;
-};
-
-void		xfidctl(void *);
-void		xfidflush(Xfid*);
-void		xfidopen(Xfid*);
-void		xfidclose(Xfid*);
-void		xfidread(Xfid*);
-void		xfidwrite(Xfid*);
-void		xfidctlwrite(Xfid*, Window*);
-void		xfideventread(Xfid*, Window*);
-void		xfideventwrite(Xfid*, Window*);
-void		xfidindexread(Xfid*);
-void		xfidutfread(Xfid*, Text*, uint, int);
-int		xfidruneread(Xfid*, Text*, uint, uint);
-void		xfidlogopen(Xfid*);
-void		xfidlogread(Xfid*);
-void		xfidlogflush(Xfid*);
-void		xfidlog(Window*, char*);
-
-struct Reffont
-{
-	Ref;
-	Font		*f;
-
-};
-Reffont	*rfget(int, int, int, char*);
-void		rfclose(Reffont*);
-
-struct Rangeset
-{
-	Range	r[NRange];
-};
-
-struct Dirlist
-{
-	Rune	*r;
-	int		nr;
-	int		wid;
-};
-
-struct Expand
-{
-	uint	q0;
-	uint	q1;
-	Rune	*name;
-	int	nname;
-	char	*bname;
-	int	jump;
-	union{
-		Text	*at;
-		Rune	*ar;
-	};
-	int	(*agetc)(void*, uint);
-	int	a0;
-	int	a1;
-};
-
-enum
-{
-	/* fbufalloc() guarantees room off end of BUFSIZE */
-	BUFSIZE = Maxblock+IOHDRSZ,	/* size from fbufalloc() */
-	RBUFSIZE = BUFSIZE/sizeof(Rune),
-	EVENTSIZE = 256,
-	Scrollwid = 12,	/* width of scroll bar */
-	Scrollgap = 4,	/* gap right of scroll bar */
-	Margin = 4,	/* margin around text */
-	Border = 2,	/* line between rows, cols, windows */
-};
-
-#define	QID(w,q)	((w<<8)|(q))
-#define	WIN(q)	((((ulong)(q).path)>>8) & 0xFFFFFF)
-#define	FILE(q)	((q).path & 0xFF)
-
-enum
-{
-	FALSE,
-	TRUE,
-	XXX,
-};
-
-enum
-{
-	Empty	= 0,
-	Null		= '-',
-	Delete	= 'd',
-	Insert	= 'i',
-	Replace	= 'r',
-	Filename	= 'f',
-};
-
-enum	/* editing */
-{
-	Inactive	= 0,
-	Inserting,
-	Collecting,
-};
-
-uint		globalincref;
-uint		seq;
-uint		maxtab;	/* size of a tab, in units of the '0' character */
-
-Display		*display;
-Image		*screen;
-Font			*font;
-Mouse		*mouse;
-Mousectl		*mousectl;
-Keyboardctl	*keyboardctl;
-Reffont		reffont;
-Image		*modbutton;
-Image		*colbutton;
-Image		*button;
-Image		*but2col;
-Image		*but3col;
-Cursor		boxcursor;
-Row			row;
-int			timerpid;
-Disk			*disk;
-Text			*seltext;
-Text			*argtext;
-Text			*mousetext;	/* global because Text.close needs to clear it */
-Text			*typetext;		/* global because Text.close needs to clear it */
-Text			*barttext;		/* shared between mousetask and keyboardthread */
-int			bartflag;
-Window		*activewin;
-Column		*activecol;
-Buffer		snarfbuf;
-Rectangle		nullrect;
-int			fsyspid;
-char			*user;
-char			*cputype;
-char			*objtype;
-char			*home;
-char			*fontnames[2];
-char			acmeerrorfile[128];
-Image		*tagcols[NCOL];
-Image		*textcols[NCOL];
-int			plumbsendfd;
-int			plumbeditfd;
-char			wdir[];
-int			editing;
-int			messagesize;		/* negotiated in 9P version setup */
-int			globalindent[NINDENT];
-Rune		*delcmd;			/* what command deleted the window. eg, Del, Delete, Delmesg */
-
-Channel	*cplumb;		/* chan(Plumbmsg*) */
-Channel	*cwait;		/* chan(Waitmsg) */
-Channel	*ccommand;	/* chan(Command*) */
-Channel	*ckill;		/* chan(Rune*) */
-Channel	*cxfidalloc;	/* chan(Xfid*) */
-Channel	*cxfidfree;	/* chan(Xfid*) */
-Channel	*cnewwindow;	/* chan(Channel*) */
-Channel	*mouseexit0;	/* chan(int) */
-Channel	*mouseexit1;	/* chan(int) */
-Channel	*cexit;		/* chan(int) */
-Channel	*cerr;		/* chan(char*) */
-Channel	*cedit;		/* chan(int) */
-Channel	*cwarn;		/* chan(void*)[1] (really chan(unit)[1]) */
-
-#define	STACK	8192
-
-/* jgs1 - theme scructs */
-enum {
-	Colrioback,
-
-	/* the following group has to be in order, they are used by libframe */
-	Colback,
-	Colhigh,
-	Colbord,
-	Coltext,
-	Colhtext,
-
-	Coltitle,
-	Colltitle,
-	Colhold,
-	Collhold,
-	Colpalehold,
-	Colpaletext,
-	Colsize,
-
-	/* menuhit */
-	Colmenubar,
-	Colmenuback,
-	Colmenuhigh,
-	Colmenubord,
-	Colmenutext,
-	Colmenuhtext,
-
-	Numcolors
-};
-
-typedef struct Color Color;
-
-struct Color {
-	char *id;
-	union {
-		u32int rgb;
-		char *path;
-	};
-	int flags;
-};
-
-static Color theme[Numcolors] = {
-	[Colrioback]   = {"rioback",   {0x777777}, 0},
-	[Colback]      = {"back",      {0xffffff}, 0},
-	[Colhigh]      = {"high",      {0xcccccc}, 0},
-	[Colbord]      = {"border",    {0x999999}, 0},
-	[Coltext]      = {"text",      {DBlack>>8}, 0},
-	[Colhtext]     = {"htext",     {DBlack>>8}, 0},
-	[Coltitle]     = {"title",     {DGreygreen>>8}, 0},
-	[Colltitle]    = {"ltitle",    {DPalegreygreen>>8}, 0},
-	[Colhold]      = {"hold",      {DMedblue>>8}, 0},
-	[Collhold]     = {"lhold",     {DGreyblue>>8}, 0},
-	[Colpalehold]  = {"palehold",  {DPalegreyblue>>8}, 0},
-	[Colpaletext]  = {"paletext",  {0x666666}, 0},
-	[Colsize]      = {"size",      {DRed>>8}, 0},
-	[Colmenubar]   = {"menubar",   {DDarkgreen>>8}, 1},
-	[Colmenuback]  = {"menuback",  {0xeaffea}, 1},
-	[Colmenuhigh]  = {"menuhigh",  {DDarkgreen>>8}, 1},
-	[Colmenubord]  = {"menubord",  {DMedgreen>>8}, 1},
-	[Colmenutext]  = {"menutext",  {DBlack>>8}, 1},
-	[Colmenuhtext] = {"menuhtext", {0xeaffea}, 1},
-};
-
-Image *col[Numcolors];
\ No newline at end of file
--- a/disk.c
+++ /dev/null
@@ -1,142 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <thread.h>
-#include <cursor.h>
-#include <mouse.h>
-#include <keyboard.h>
-#include <frame.h>
-#include <fcall.h>
-#include <plumb.h>
-#include "dat.h"
-#include "fns.h"
-
-static	Block	*blist;
-
-int
-tempfile(void)
-{
-	char buf[128];
-	int i, fd;
-
-	snprint(buf, sizeof buf, "/tmp/X%d.%.4sacme", getpid(), user);
-	for(i='A'; i<='Z'; i++){
-		buf[5] = i;
-		if(access(buf, AEXIST) == 0)
-			continue;
-		fd = create(buf, ORDWR|ORCLOSE|OCEXEC, 0600);
-		if(fd >= 0)
-			return fd;
-	}
-	return -1;
-}
-
-Disk*
-diskinit()
-{
-	Disk *d;
-
-	d = emalloc(sizeof(Disk));
-	d->fd = tempfile();
-	if(d->fd < 0){
-		fprint(2, "acme: can't create temp file: %r\n");
-		threadexitsall("diskinit");
-	}
-	return d;
-}
-
-static
-uint
-ntosize(uint n, uint *ip)
-{
-	uint size;
-
-	if(n > Maxblock)
-		error("internal error: ntosize");
-	size = n;
-	if(size & (Blockincr-1))
-		size += Blockincr - (size & (Blockincr-1));
-	/* last bucket holds blocks of exactly Maxblock */
-	if(ip)
-		*ip = size/Blockincr;
-	return size * sizeof(Rune);
-}
-
-Block*
-disknewblock(Disk *d, uint n)
-{
-	uint i, j, size;
-	Block *b;
-
-	size = ntosize(n, &i);
-	b = d->free[i];
-	if(b)
-		d->free[i] = b->next;
-	else{
-		/* allocate in chunks to reduce malloc overhead */
-		if(blist == nil){
-			blist = emalloc(100*sizeof(Block));
-			for(j=0; j<100-1; j++)
-				blist[j].next = &blist[j+1];
-		}
-		b = blist;
-		blist = b->next;
-		b->addr = d->addr;
-		if(d->addr+size < d->addr){
-			error("temp file overflow");
-		}
-		d->addr += size;
-	}
-	b->n = n;
-	return b;
-}
-
-void
-diskrelease(Disk *d, Block *b)
-{
-	uint i;
-
-	ntosize(b->n, &i);
-	b->next = d->free[i];
-	d->free[i] = b;
-}
-
-void
-diskwrite(Disk *d, Block **bp, Rune *r, uint n)
-{
-	int size, nsize;
-	Block *b;
-
-	b = *bp;
-	size = ntosize(b->n, nil);
-	nsize = ntosize(n, nil);
-	if(size != nsize){
-		diskrelease(d, b);
-		b = disknewblock(d, n);
-		*bp = b;
-	}
-	if(pwrite(d->fd, r, n*sizeof(Rune), b->addr) != n*sizeof(Rune))
-		error("write error to temp file");
-	b->n = n;
-}
-
-void
-diskread(Disk *d, Block *b, Rune *r, uint n)
-{
-	int tot, nr;
-	char *p;
-
-	if(n > b->n)
-		error("internal error: diskread");
-
-	ntosize(b->n, nil);
-	n *= sizeof(Rune);
-	p = (char*)r;
-	for(tot = 0; tot < n; tot += nr){
-		nr = pread(d->fd, p+tot, n-tot, b->addr+tot);
-		if(nr <= 0)
-			error("read error from temp file");
-	}
-	if(tot != n)
-		error("read error from temp file");
-}
--- a/ecmd.c
+++ /dev/null
@@ -1,1366 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <thread.h>
-#include <cursor.h>
-#include <mouse.h>
-#include <keyboard.h>
-#include <frame.h>
-#include <fcall.h>
-#include <plumb.h>
-#include "dat.h"
-#include "edit.h"
-#include "fns.h"
-
-int	Glooping;
-int	nest;
-char	Enoname[] = "no file name given";
-
-Address	addr;
-File	*menu;
-Rangeset	sel;
-extern	Text*	curtext;
-Rune	*collection;
-int	ncollection;
-
-int	append(File*, Cmd*, long);
-int	pdisplay(File*);
-void	pfilename(File*);
-void	looper(File*, Cmd*, int);
-void	filelooper(Text*, Cmd*, int);
-void	linelooper(File*, Cmd*);
-Address	lineaddr(long, Address, int);
-int	filematch(File*, String*);
-File	*tofile(String*);
-Rune*	cmdname(File *f, String *s, int);
-void	runpipe(Text*, int, Rune*, int, int);
-
-void
-clearcollection(void)
-{
-	free(collection);
-	collection = nil;
-	ncollection = 0;
-}
-
-void
-resetxec(void)
-{
-	Glooping = nest = 0;
-	clearcollection();
-}
-
-void
-mkaddr(Address *a, File *f)
-{
-	a->r.q0 = f->curtext->q0;
-	a->r.q1 = f->curtext->q1;
-	a->f = f;
-}
-
-int
-cmdexec(Text *t, Cmd *cp)
-{
-	int i;
-	Addr *ap;
-	File *f;
-	Window *w;
-	Address dot;
-
-	if(t == nil)
-		w = nil;
-	else
-		w = t->w;
-	if(w==nil && (cp->addr==0 || cp->addr->type!='"') &&
-	    !utfrune("bBnqUXY!", cp->cmdc) &&
-	    !(cp->cmdc=='D' && cp->text))
-		editerror("no current window");
-	i = cmdlookup(cp->cmdc);	/* will be -1 for '{' */
-	f = nil;
-	if(t && t->w){
-		t = &t->w->body;
-		f = t->file;
-		f->curtext = t;
-	}
-	if(i>=0 && cmdtab[i].defaddr != aNo){
-		if((ap=cp->addr)==0 && cp->cmdc!='\n'){
-			cp->addr = ap = newaddr();
-			ap->type = '.';
-			if(cmdtab[i].defaddr == aAll)
-				ap->type = '*';
-		}else if(ap && ap->type=='"' && ap->next==0 && cp->cmdc!='\n'){
-			ap->next = newaddr();
-			ap->next->type = '.';
-			if(cmdtab[i].defaddr == aAll)
-				ap->next->type = '*';
-		}
-		if(cp->addr){	/* may be false for '\n' (only) */
-			static Address none = {0,0,nil};
-			if(f){
-				mkaddr(&dot, f);
-				addr = cmdaddress(ap, dot, 0);
-			}else	/* a " */
-				addr = cmdaddress(ap, none, 0);
-			f = addr.f;
-			t = f->curtext;
-		}
-	}
-	switch(cp->cmdc){
-	case '{':
-		mkaddr(&dot, f);
-		if(cp->addr != nil)
-			dot = cmdaddress(cp->addr, dot, 0);
-		for(cp = cp->cmd; cp; cp = cp->next){
-			if(dot.r.q1 > t->file->nc)
-				editerror("dot extends past end of buffer during { command");
-			t->q0 = dot.r.q0;
-			t->q1 = dot.r.q1;
-			cmdexec(t, cp);
-		}
-		break;
-	default:
-		if(i < 0)
-			editerror("unknown command %c in cmdexec", cp->cmdc);
-		i = (*cmdtab[i].fn)(t, cp);
-		return i;
-	}
-	return 1;
-}
-
-char*
-edittext(Window *w, int q, Rune *r, int nr)
-{
-	File *f;
-
-	switch(editing){
-	case Inactive:
-		return "permission denied";
-	case Inserting:
-		f = w->body.file;
-		eloginsert(f, q, r, nr);
-		return nil;
-	case Collecting:
-		collection = runerealloc(collection, ncollection+nr+1);
-		runemove(collection+ncollection, r, nr);
-		ncollection += nr;
-		collection[ncollection] = '\0';
-		return nil;
-	default:
-		return "unknown state in edittext";
-	}
-}
-
-/* string is known to be NUL-terminated */
-Rune*
-filelist(Text *t, Rune *r, int nr)
-{
-	if(nr == 0)
-		return nil;
-	r = skipbl(r, nr, &nr);
-	clearcollection();
-	if(r[0] != '<'){
-		if((collection = runestrdup(r)) != nil)
-			ncollection += runestrlen(r);
-	}else
-		/* use < command to collect text */
-		runpipe(t, '<', r+1, nr-1, Collecting);
-	return collection;
-}
-
-int
-a_cmd(Text *t, Cmd *cp)
-{
-	return append(t->file, cp, addr.r.q1);
-}
-
-int
-b_cmd(Text*, Cmd *cp)
-{
-	File *f;
-
-	f = tofile(cp->text);
-	if(nest == 0)
-		pfilename(f);
-	curtext = f->curtext;
-	return TRUE;
-}
-
-int
-B_cmd(Text *t, Cmd *cp)
-{
-	Rune *list, *r, *s;
-	int nr;
-
-	list = filelist(t, cp->text->r, cp->text->n);
-	if(list == nil)
-		editerror(Enoname);
-	r = list;
-	nr = runestrlen(r);
-	r = skipbl(r, nr, &nr);
-	if(nr == 0)
-		new(t, t, nil, 0, 0, r, 0);
-	else while(nr > 0){
-		s = findbl(r, nr, &nr);
-		*s = '\0';
-		new(t, t, nil, 0, 0, r, runestrlen(r));
-		if(nr > 0)
-			r = skipbl(s+1, nr-1, &nr);
-	}
-	clearcollection();
-	return TRUE;
-}
-
-int
-c_cmd(Text *t, Cmd *cp)
-{
-	elogreplace(t->file, addr.r.q0, addr.r.q1, cp->text->r, cp->text->n);
-	t->q0 = addr.r.q0;
-	t->q1 = addr.r.q0;
-	return TRUE;
-}
-
-int
-d_cmd(Text *t, Cmd*)
-{
-	if(addr.r.q1 > addr.r.q0)
-		elogdelete(t->file, addr.r.q0, addr.r.q1);
-	t->q0 = addr.r.q0;
-	t->q1 = addr.r.q0;
-	return TRUE;
-}
-
-void
-D1(Text *t)
-{
-	if(t->w->body.file->ntext>1 || winclean(t->w, FALSE))
-		colclose(t->col, t->w, TRUE);
-}
-
-int
-D_cmd(Text *t, Cmd *cp)
-{
-	Rune *list, *r, *s, *n;
-	int nr, nn;
-	Window *w;
-	Runestr dir, rs;
-	char buf[128];
-
-	list = filelist(t, cp->text->r, cp->text->n);
-	if(list == nil){
-		D1(t);
-		return TRUE;
-	}
-	dir = dirname(t, nil, 0);
-	r = list;
-	nr = runestrlen(r);
-	r = skipbl(r, nr, &nr);
-	do{
-		s = findbl(r, nr, &nr);
-		*s = '\0';
-		/* first time through, could be empty string, meaning delete file empty name */
-		nn = runestrlen(r);
-		if(r[0]=='/' || nn==0 || dir.nr==0){
-			rs.r = runestrdup(r);
-			rs.nr = nn;
-		}else{
-			n = runemalloc(dir.nr+1+nn);
-			runemove(n, dir.r, dir.nr);
-			n[dir.nr] = '/';
-			runemove(n+dir.nr+1, r, nn);
-			rs = cleanrname((Runestr){n, dir.nr+1+nn});
-		}
-		w = lookfile(rs.r, rs.nr);
-		if(w == nil){
-			snprint(buf, sizeof buf, "no such file %.*S", rs.nr, rs.r);
-			free(rs.r);
-			editerror(buf);
-		}
-		free(rs.r);
-		D1(&w->body);
-		if(nr > 0)
-			r = skipbl(s+1, nr-1, &nr);
-	}while(nr > 0);
-	clearcollection();
-	free(dir.r);
-	return TRUE;
-}
-
-static int
-readloader(void *v, uint q0, Rune *r, int nr)
-{
-	if(nr > 0)
-		eloginsert(v, q0, r, nr);
-	return 0;
-}
-
-int
-e_cmd(Text *t, Cmd *cp)
-{
-	Rune *name;
-	File *f;
-	int i, isdir, q0, q1, fd, nulls, samename, allreplaced;
-	char *s, tmp[128];
-	Dir *d;
-
-	f = t->file;
-	q0 = addr.r.q0;
-	q1 = addr.r.q1;
-	if(cp->cmdc == 'e'){
-		if(winclean(t->w, TRUE)==FALSE)
-			editerror("");	/* winclean generated message already */
-		q0 = 0;
-		q1 = f->nc;
-	}
-	allreplaced = (q0==0 && q1==f->nc);
-	name = cmdname(f, cp->text, cp->cmdc=='e');
-	if(name == nil)
-		editerror(Enoname);
-	i = runestrlen(name);
-	samename = runeeq(name, i, t->file->name, t->file->nname);
-	s = runetobyte(name, i);
-	free(name);
-	fd = open(s, OREAD);
-	if(fd < 0){
-		snprint(tmp, sizeof tmp, "can't open %s: %r", s);
-		free(s);
-		editerror(tmp);
-	}
-	d = dirfstat(fd);
-	isdir = (d!=nil && (d->qid.type&QTDIR));
-	free(d);
-	if(isdir){
-		close(fd);
-		snprint(tmp, sizeof tmp, "%s is a directory", s);
-		free(s);
-		editerror(tmp);
-	}
-	elogdelete(f, q0, q1);
-	nulls = 0;
-	loadfile(fd, q1, &nulls, readloader, f);
-	free(s);
-	close(fd);
-	if(nulls)
-		warning(nil, "%s: NUL bytes elided\n", s);
-	else if(allreplaced && samename)
-		f->editclean = TRUE;
-	return TRUE;
-}
-
-int
-f_cmd(Text *t, Cmd *cp)
-{
-	Rune *name;
-	String *str;
-	String empty;
-
-	if(cp->text == nil){
-		empty.n = 0;
-		empty.r = L"";
-		str = &empty;
-	}else
-		str = cp->text;
-	name = cmdname(t->file, str, TRUE);
-	free(name);
-	pfilename(t->file);
-	return TRUE;
-}
-
-int
-g_cmd(Text *t, Cmd *cp)
-{
-	if(t->file != addr.f){
-		warning(nil, "internal error: g_cmd f!=addr.f\n");
-		return FALSE;
-	}
-	if(rxcompile(cp->re->r) == FALSE)
-		editerror("bad regexp in g command");
-	if(rxexecute(t, nil, addr.r.q0, addr.r.q1, &sel) ^ cp->cmdc=='v'){
-		t->q0 = addr.r.q0;
-		t->q1 = addr.r.q1;
-		return cmdexec(t, cp->cmd);
-	}
-	return TRUE;
-}
-
-int
-i_cmd(Text *t, Cmd *cp)
-{
-	return append(t->file, cp, addr.r.q0);
-}
-
-void
-copy(File *f, Address addr2)
-{
-	long p;
-	int ni;
-	Rune *buf;
-
-	buf = fbufalloc();
-	for(p=addr.r.q0; p<addr.r.q1; p+=ni){
-		ni = addr.r.q1-p;
-		if(ni > RBUFSIZE)
-			ni = RBUFSIZE;
-		bufread(f, p, buf, ni);
-		eloginsert(addr2.f, addr2.r.q1, buf, ni);
-	}
-	fbuffree(buf);
-}
-
-void
-move(File *f, Address addr2)
-{
-	if(addr.f!=addr2.f || addr.r.q1<=addr2.r.q0){
-		elogdelete(f, addr.r.q0, addr.r.q1);
-		copy(f, addr2);
-	}else if(addr.r.q0 >= addr2.r.q1){
-		copy(f, addr2);
-		elogdelete(f, addr.r.q0, addr.r.q1);
-	}else if(addr.r.q0==addr2.r.q0 && addr.r.q1==addr2.r.q1){
-		;	/* move to self; no-op */
-	}else
-		editerror("move overlaps itself");
-}
-
-int
-m_cmd(Text *t, Cmd *cp)
-{
-	Address dot, addr2;
-
-	mkaddr(&dot, t->file);
-	addr2 = cmdaddress(cp->mtaddr, dot, 0);
-	if(cp->cmdc == 'm')
-		move(t->file, addr2);
-	else
-		copy(t->file, addr2);
-	return TRUE;
-}
-
-int
-p_cmd(Text *t, Cmd*)
-{
-	return pdisplay(t->file);
-}
-
-int
-s_cmd(Text *t, Cmd *cp)
-{
-	int i, j, k, c, m, n, nrp, didsub;
-	long p1, op, delta;
-	String *buf;
-	Rangeset *rp;
-	char *err;
-	Rune *rbuf;
-
-	n = cp->num;
-	op= -1;
-	if(rxcompile(cp->re->r) == FALSE)
-		editerror("bad regexp in s command");
-	nrp = 0;
-	rp = nil;
-	delta = 0;
-	didsub = FALSE;
-	for(p1 = addr.r.q0; p1<=addr.r.q1 && rxexecute(t, nil, p1, addr.r.q1, &sel); ){
-		if(sel.r[0].q0 == sel.r[0].q1){	/* empty match? */
-			if(sel.r[0].q0 == op){
-				p1++;
-				continue;
-			}
-			p1 = sel.r[0].q1+1;
-		}else
-			p1 = sel.r[0].q1;
-		op = sel.r[0].q1;
-		if(--n>0)
-			continue;
-		nrp++;
-		rp = erealloc(rp, nrp*sizeof(Rangeset));
-		rp[nrp-1] = sel;
-	}
-	rbuf = fbufalloc();
-	buf = allocstring(0);
-	for(m=0; m<nrp; m++){
-		buf->n = 0;
-		buf->r[0] = L'\0';
-		sel = rp[m];
-		for(i = 0; i<cp->text->n; i++)
-			if((c = cp->text->r[i])=='\\' && i<cp->text->n-1){
-				c = cp->text->r[++i];
-				if('1'<=c && c<='9') {
-					j = c-'0';
-					if(sel.r[j].q1-sel.r[j].q0>RBUFSIZE){
-						err = "replacement string too long";
-						goto Err;
-					}
-					bufread(t->file, sel.r[j].q0, rbuf, sel.r[j].q1-sel.r[j].q0);
-					for(k=0; k<sel.r[j].q1-sel.r[j].q0; k++)
-						Straddc(buf, rbuf[k]);
-				}else
-				 	Straddc(buf, c);
-			}else if(c!='&')
-				Straddc(buf, c);
-			else{
-				if(sel.r[0].q1-sel.r[0].q0>RBUFSIZE){
-					err = "right hand side too long in substitution";
-					goto Err;
-				}
-				bufread(t->file, sel.r[0].q0, rbuf, sel.r[0].q1-sel.r[0].q0);
-				for(k=0; k<sel.r[0].q1-sel.r[0].q0; k++)
-					Straddc(buf, rbuf[k]);
-			}
-		elogreplace(t->file, sel.r[0].q0, sel.r[0].q1,  buf->r, buf->n);
-		delta -= sel.r[0].q1-sel.r[0].q0;
-		delta += buf->n;
-		didsub = 1;
-		if(!cp->flag)
-			break;
-	}
-	free(rp);
-	freestring(buf);
-	fbuffree(rbuf);
-	if(!didsub && nest==0)
-		editerror("no substitution");
-	t->q0 = addr.r.q0;
-	t->q1 = addr.r.q1;
-	return TRUE;
-
-Err:
-	free(rp);
-	freestring(buf);
-	fbuffree(rbuf);
-	editerror(err);
-	return FALSE;
-}
-
-int
-u_cmd(Text *t, Cmd *cp)
-{
-	int n, oseq, flag;
-
-	n = cp->num;
-	flag = TRUE;
-	if(n < 0){
-		n = -n;
-		flag = FALSE;
-	}
-	oseq = -1;
-	while(n-->0 && t->file->seq!=0 && t->file->seq!=oseq){
-		oseq = t->file->seq;
-		undo(t, nil, nil, flag, 0, nil, 0);
-	}
-	return TRUE;
-}
-
-int
-w_cmd(Text *t, Cmd *cp)
-{
-	Rune *r;
-	File *f;
-
-	f = t->file;
-	if(f->seq == seq)
-		editerror("can't write file with pending modifications");
-	r = cmdname(f, cp->text, FALSE);
-	if(r == nil)
-		editerror("no name specified for 'w' command");
-	putfile(f, addr.r.q0, addr.r.q1, r, runestrlen(r));
-	/* r is freed by putfile */
-	return TRUE;
-}
-
-int
-x_cmd(Text *t, Cmd *cp)
-{
-	if(cp->re)
-		looper(t->file, cp, cp->cmdc=='x');
-	else
-		linelooper(t->file, cp);
-	return TRUE;
-}
-
-int
-X_cmd(Text *t, Cmd *cp)
-{
-	filelooper(t, cp, cp->cmdc=='X');
-	return TRUE;
-}
-
-void
-runpipe(Text *t, int cmd, Rune *cr, int ncr, int state)
-{
-	Rune *r, *s;
-	int n;
-	Runestr dir;
-	Window *w;
-
-	r = skipbl(cr, ncr, &n);
-	if(n == 0)
-		editerror("no command specified for %c", cmd);
-	w = nil;
-	if(state == Inserting){
-		w = t->w;
-		t->q0 = addr.r.q0;
-		t->q1 = addr.r.q1;
-		if(cmd == '<' || cmd=='|')
-			elogdelete(t->file, t->q0, t->q1);
-	}
-	s = runemalloc(n+2);
-	s[0] = cmd;
-	runemove(s+1, r, n);
-	n++;
-	dir.r = nil;
-	dir.nr = 0;
-	if(t != nil)
-		dir = dirname(t, nil, 0);
-	if(dir.nr==1 && dir.r[0]=='.'){	/* sigh */
-		free(dir.r);
-		dir.r = nil;
-		dir.nr = 0;
-	}
-	editing = state;
-	if(t!=nil && t->w!=nil)
-		incref(t->w);	/* run will decref */
-	run(w, runetobyte(s, n), dir.r, dir.nr, TRUE, nil, nil, TRUE);
-	free(s);
-	if(t!=nil && t->w!=nil)
-		winunlock(t->w);
-	qunlock(&row);
-	recvul(cedit);
-	qlock(&row);
-	editing = Inactive;
-	if(t!=nil && t->w!=nil)
-		winlock(t->w, 'M');
-}
-
-int
-pipe_cmd(Text *t, Cmd *cp)
-{
-	runpipe(t, cp->cmdc, cp->text->r, cp->text->n, Inserting);
-	return TRUE;
-}
-
-long
-nlcount(Text *t, long q0, long q1, long *pnr)
-{
-	long nl, start;
-	Rune *buf;
-	int i, nbuf;
-
-	buf = fbufalloc();
-	nbuf = 0;
-	i = nl = 0;
-	start = q0;
-	while(q0 < q1){
-		if(i == nbuf){
-			nbuf = q1-q0;
-			if(nbuf > RBUFSIZE)
-				nbuf = RBUFSIZE;
-			bufread(t->file, q0, buf, nbuf);
-			i = 0;
-		}
-		if(buf[i++] == '\n'){
-			start = q0+1;
-			nl++;
-		}
-		q0++;
-	}
-	fbuffree(buf);
-	if(pnr != nil)
-		*pnr = q0 - start;
-	return nl;
-}
-
-enum {
-	PosnLine = 0,
-	PosnChars = 1,
-	PosnLineChars = 2,
-};
-
-void
-printposn(Text *t, int mode)
-{
-	long l1, l2, r1, r2;
-
-	if (t != nil && t->file != nil && t->file->name != nil)
-		warning(nil, "%.*S:", t->file->nname, t->file->name);
-	switch(mode) {
-	case PosnChars:
-		warning(nil, "#%d", addr.r.q0);
-		if(addr.r.q1 != addr.r.q0)
-			warning(nil, ",#%d", addr.r.q1);
-		warning(nil, "\n");
-		return;
-	default:
-	case PosnLine:
-		l1 = 1+nlcount(t, 0, addr.r.q0, nil);
-		l2 = l1+nlcount(t, addr.r.q0, addr.r.q1, nil);
-		/* check if addr ends with '\n' */
-		if(addr.r.q1>0 && addr.r.q1>addr.r.q0 && textreadc(t, addr.r.q1-1)=='\n')
-			--l2;
-		warning(nil, "%lud", l1);
-		if(l2 != l1)
-			warning(nil, ",%lud", l2);
-		warning(nil, "\n");
-		return;
-	case PosnLineChars:
-		l1 = 1+nlcount(t, 0, addr.r.q0, &r1);
-		l2 = l1+nlcount(t, addr.r.q0, addr.r.q1, &r2);
-		if(l2 == l1)
-			r2 += r1;
-		warning(nil, "%lud+#%lud", l1, r1);
-		if(l2 != l1)
-			warning(nil, ",%lud+#%lud", l2, r2);
-		warning(nil, "\n");
-		return;
-	}
-}
-
-int
-eq_cmd(Text *t, Cmd *cp)
-{
-	int mode;
-
-	switch(cp->text->n){
-	case 0:
-		mode = PosnLine;
-		break;
-	case 1:
-		if(cp->text->r[0] == '#'){
-			mode = PosnChars;
-			break;
-		}
-		if(cp->text->r[0] == '+'){
-			mode = PosnLineChars;
-			break;
-		}
-	default:
-		SET(mode);
-		editerror("newline expected");
-	}
-	printposn(t, mode);
-	return TRUE;
-}
-
-int
-nl_cmd(Text *t, Cmd *cp)
-{
-	Address a;
-	File *f;
-
-	f = t->file;
-	if(cp->addr == 0){
-		/* First put it on newline boundaries */
-		mkaddr(&a, f);
-		addr = lineaddr(0, a, -1);
-		a = lineaddr(0, a, 1);
-		addr.r.q1 = a.r.q1;
-		if(addr.r.q0==t->q0 && addr.r.q1==t->q1){
-			mkaddr(&a, f);
-			addr = lineaddr(1, a, 1);
-		}
-	}
-	textshow(t, addr.r.q0, addr.r.q1, 1);
-	return TRUE;
-}
-
-int
-append(File *f, Cmd *cp, long p)
-{
-	if(cp->text->n > 0)
-		eloginsert(f, p, cp->text->r, cp->text->n);
-	f->curtext->q0 = p;
-	f->curtext->q1 = p;
-	return TRUE;
-}
-
-int
-pdisplay(File *f)
-{
-	long p1, p2;
-	int np;
-	Rune *buf;
-
-	p1 = addr.r.q0;
-	p2 = addr.r.q1;
-	if(p2 > f->nc)
-		p2 = f->nc;
-	buf = fbufalloc();
-	while(p1 < p2){
-		np = p2-p1;
-		if(np>RBUFSIZE-1)
-			np = RBUFSIZE-1;
-		bufread(f, p1, buf, np);
-		buf[np] = L'\0';
-		warning(nil, "%S", buf);
-		p1 += np;
-	}
-	fbuffree(buf);
-	f->curtext->q0 = addr.r.q0;
-	f->curtext->q1 = addr.r.q1;
-	return TRUE;
-}
-
-void
-pfilename(File *f)
-{
-	int dirty;
-	Window *w;
-
-	w = f->curtext->w;
-	/* same check for dirty as in settag, but we know ncache==0 */
-	dirty = !w->isdir && !w->isscratch && f->mod;
-	warning(nil, "%c%c%c %.*S\n", " '"[dirty],
-		'+', " ."[curtext!=nil && curtext->file==f], f->nname, f->name);
-}
-
-void
-loopcmd(File *f, Cmd *cp, Range *rp, long nrp)
-{
-	long i;
-
-	for(i=0; i<nrp; i++){
-		f->curtext->q0 = rp[i].q0;
-		f->curtext->q1 = rp[i].q1;
-		cmdexec(f->curtext, cp);
-	}
-}
-
-void
-looper(File *f, Cmd *cp, int xy)
-{
-	long p, op, nrp;
-	Range r, tr;
-	Range *rp;
-
-	r = addr.r;
-	op= xy? -1 : r.q0;
-	nest++;
-	if(rxcompile(cp->re->r) == FALSE)
-		editerror("bad regexp in %c command", cp->cmdc);
-	nrp = 0;
-	rp = nil;
-	for(p = r.q0; p<=r.q1; ){
-		if(!rxexecute(f->curtext, nil, p, r.q1, &sel)){ /* no match, but y should still run */
-			if(xy || op>r.q1)
-				break;
-			tr.q0 = op, tr.q1 = r.q1;
-			p = r.q1+1;	/* exit next loop */
-		}else{
-			if(sel.r[0].q0==sel.r[0].q1){	/* empty match? */
-				if(sel.r[0].q0==op){
-					p++;
-					continue;
-				}
-				p = sel.r[0].q1+1;
-			}else
-				p = sel.r[0].q1;
-			if(xy)
-				tr = sel.r[0];
-			else
-				tr.q0 = op, tr.q1 = sel.r[0].q0;
-		}
-		op = sel.r[0].q1;
-		nrp++;
-		rp = erealloc(rp, nrp*sizeof(Range));
-		rp[nrp-1] = tr;
-	}
-	loopcmd(f, cp->cmd, rp, nrp);
-	free(rp);
-	--nest;
-}
-
-void
-linelooper(File *f, Cmd *cp)
-{
-	long nrp, p;
-	Range r, linesel;
-	Address a, a3;
-	Range *rp;
-
-	nest++;
-	nrp = 0;
-	rp = nil;
-	r = addr.r;
-	a3.f = f;
-	a3.r.q0 = a3.r.q1 = r.q0;
-	a = lineaddr(0, a3, 1);
-	linesel = a.r;
-	for(p = r.q0; p<r.q1; p = a3.r.q1){
-		a3.r.q0 = a3.r.q1;
-		if(p!=r.q0 || linesel.q1==p){
-			a = lineaddr(1, a3, 1);
-			linesel = a.r;
-		}
-		if(linesel.q0 >= r.q1)
-			break;
-		if(linesel.q1 >= r.q1)
-			linesel.q1 = r.q1;
-		if(linesel.q1 > linesel.q0)
-			if(linesel.q0>=a3.r.q1 && linesel.q1>a3.r.q1){
-				a3.r = linesel;
-				nrp++;
-				rp = erealloc(rp, nrp*sizeof(Range));
-				rp[nrp-1] = linesel;
-				continue;
-			}
-		break;
-	}
-	loopcmd(f, cp->cmd, rp, nrp);
-	free(rp);
-	--nest;
-}
-
-struct Looper
-{
-	Cmd *cp;
-	int	XY;
-	Window	**w;
-	int	nw;
-} loopstruct;	/* only one; X and Y can't nest */
-
-void
-alllooper(Window *w, void *v)
-{
-	Text *t;
-	struct Looper *lp;
-	Cmd *cp;
-
-	lp = v;
-	cp = lp->cp;
-//	if(w->isscratch || w->isdir)
-//		return;
-	t = &w->body;
-	/* only use this window if it's the current window for the file */
-	if(t->file->curtext != t)
-		return;
-//	if(w->nopen[QWevent] > 0)
-//		return;
-	/* no auto-execute on files without names */
-	if(cp->re==nil && t->file->nname==0)
-		return;
-	if(cp->re==nil || filematch(t->file, cp->re)==lp->XY){
-		lp->w = erealloc(lp->w, (lp->nw+1)*sizeof(Window*));
-		lp->w[lp->nw++] = w;
-	}
-}
-
-void
-alllocker(Window *w, void *v)
-{
-	if(v)
-		incref(w);
-	else
-		winclose(w);
-}
-
-void
-filelooper(Text *t, Cmd *cp, int XY)
-{
-	int i;
-	Text *targ;
-
-	if(Glooping++)
-		editerror("can't nest %c command", "YX"[XY]);
-	nest++;
-
-	loopstruct.cp = cp;
-	loopstruct.XY = XY;
-	if(loopstruct.w)	/* error'ed out last time */
-		free(loopstruct.w);
-	loopstruct.w = nil;
-	loopstruct.nw = 0;
-	allwindows(alllooper, &loopstruct);
-	/*
-	 * add a ref to all windows to keep safe windows accessed by X
-	 * that would not otherwise have a ref to hold them up during
-	 * the shenanigans.  note this with globalincref so that any
-	 * newly created windows start with an extra reference.
-	 */
-	allwindows(alllocker, (void*)1);
-	globalincref = 1;
-	/*
-	 * Unlock the window running the X command.
-	 * We'll need to lock and unlock each target window in turn.
-	 */
-	if(t && t->w)
-		winunlock(t->w);
-	for(i=0; i<loopstruct.nw; i++){
-		targ = &loopstruct.w[i]->body;
-		if(targ && targ->w)
-			winlock(targ->w, cp->cmdc);
-		cmdexec(targ, cp->cmd);
-		if(targ && targ->w)
-			winunlock(targ->w);
-	}
-	if(t && t->w)
-		winlock(t->w, cp->cmdc);
-	allwindows(alllocker, (void*)0);
-	globalincref = 0;
-	free(loopstruct.w);
-	loopstruct.w = nil;
-
-	--Glooping;
-	--nest;
-}
-
-void
-nextmatch(File *f, String *r, long p, int sign)
-{
-	if(rxcompile(r->r) == FALSE)
-		editerror("bad regexp in command address");
-	if(sign >= 0){
-		if(!rxexecute(f->curtext, nil, p, 0x7FFFFFFFL, &sel))
-			editerror("no match for regexp");
-		if(sel.r[0].q0==sel.r[0].q1 && sel.r[0].q0==p){
-			if(++p>f->nc)
-				p = 0;
-			if(!rxexecute(f->curtext, nil, p, 0x7FFFFFFFL, &sel))
-				editerror("address");
-		}
-	}else{
-		if(!rxbexecute(f->curtext, p, &sel))
-			editerror("no match for regexp");
-		if(sel.r[0].q0==sel.r[0].q1 && sel.r[0].q1==p){
-			if(--p<0)
-				p = f->nc;
-			if(!rxbexecute(f->curtext, p, &sel))
-				editerror("address");
-		}
-	}
-}
-
-File	*matchfile(String*);
-Address	charaddr(long, Address, int);
-Address	lineaddr(long, Address, int);
-
-Address
-cmdaddress(Addr *ap, Address a, int sign)
-{
-	File *f = a.f;
-	Address a1, a2;
-
-	do{
-		switch(ap->type){
-		case 'l':
-		case '#':
-			a = (*(ap->type=='#'?charaddr:lineaddr))(ap->num, a, sign);
-			break;
-
-		case '.':
-			mkaddr(&a, f);
-			break;
-
-		case '$':
-			a.r.q0 = a.r.q1 = f->nc;
-			break;
-
-		case '\'':
-editerror("can't handle '");
-//			a.r = f->mark;
-			break;
-
-		case '?':
-			sign = -sign;
-			if(sign == 0)
-				sign = -1;
-			/* fall through */
-		case '/':
-			nextmatch(f, ap->re, sign>=0? a.r.q1 : a.r.q0, sign);
-			a.r = sel.r[0];
-			break;
-
-		case '"':
-			f = matchfile(ap->re);
-			mkaddr(&a, f);
-			break;
-
-		case '*':
-			a.r.q0 = 0, a.r.q1 = f->nc;
-			return a;
-
-		case ',':
-		case ';':
-			if(ap->left)
-				a1 = cmdaddress(ap->left, a, 0);
-			else
-				a1.f = a.f, a1.r.q0 = a1.r.q1 = 0;
-			if(ap->type == ';'){
-				f = a1.f;
-				a = a1;
-				f->curtext->q0 = a1.r.q0;
-				f->curtext->q1 = a1.r.q1;
-			}
-			if(ap->next)
-				a2 = cmdaddress(ap->next, a, 0);
-			else
-				a2.f = a.f, a2.r.q0 = a2.r.q1 = f->nc;
-			if(a1.f != a2.f)
-				editerror("addresses in different files");
-			a.f = a1.f, a.r.q0 = a1.r.q0, a.r.q1 = a2.r.q1;
-			if(a.r.q1 < a.r.q0)
-				editerror("addresses out of order");
-			return a;
-
-		case '+':
-		case '-':
-			sign = 1;
-			if(ap->type == '-')
-				sign = -1;
-			if(ap->next==0 || ap->next->type=='+' || ap->next->type=='-')
-				a = lineaddr(1L, a, sign);
-			break;
-		default:
-			error("cmdaddress");
-			return a;
-		}
-	}while(ap = ap->next);	/* assign = */
-	return a;
-}
-
-struct Tofile{
-	File		*f;
-	String	*r;
-};
-
-void
-alltofile(Window *w, void *v)
-{
-	Text *t;
-	struct Tofile *tp;
-
-	tp = v;
-	if(tp->f != nil)
-		return;
-	if(w->isscratch || w->isdir)
-		return;
-	t = &w->body;
-	/* only use this window if it's the current window for the file */
-	if(t->file->curtext != t)
-		return;
-//	if(w->nopen[QWevent] > 0)
-//		return;
-	if(runeeq(tp->r->r, tp->r->n, t->file->name, t->file->nname))
-		tp->f = t->file;
-}
-
-File*
-tofile(String *r)
-{
-	struct Tofile t;
-	String rr;
-
-	rr.r = skipbl(r->r, r->n, &rr.n);
-	t.f = nil;
-	t.r = &rr;
-	allwindows(alltofile, &t);
-	if(t.f == nil)
-		editerror("no such file\"%S\"", rr.r);
-	return t.f;
-}
-
-void
-allmatchfile(Window *w, void *v)
-{
-	struct Tofile *tp;
-	Text *t;
-
-	tp = v;
-	if(w->isscratch || w->isdir)
-		return;
-	t = &w->body;
-	/* only use this window if it's the current window for the file */
-	if(t->file->curtext != t)
-		return;
-//	if(w->nopen[QWevent] > 0)
-//		return;
-	if(filematch(w->body.file, tp->r)){
-		if(tp->f != nil)
-			editerror("too many files match \"%S\"", tp->r->r);
-		tp->f = w->body.file;
-	}
-}
-
-File*
-matchfile(String *r)
-{
-	struct Tofile tf;
-
-	tf.f = nil;
-	tf.r = r;
-	allwindows(allmatchfile, &tf);
-
-	if(tf.f == nil)
-		editerror("no file matches \"%S\"", r->r);
-	return tf.f;
-}
-
-int
-filematch(File *f, String *r)
-{
-	char *buf;
-	Rune *rbuf;
-	Window *w;
-	int match, i, dirty;
-	Rangeset s;
-
-	/* compile expr first so if we get an error, we haven't allocated anything */
-	if(rxcompile(r->r) == FALSE)
-		editerror("bad regexp in file match");
-	buf = fbufalloc();
-	w = f->curtext->w;
-	/* same check for dirty as in settag, but we know ncache==0 */
-	dirty = !w->isdir && !w->isscratch && f->mod;
-	snprint(buf, BUFSIZE, "%c%c%c %.*S\n", " '"[dirty],
-		'+', " ."[curtext!=nil && curtext->file==f], f->nname, f->name);
-	rbuf = bytetorune(buf, &i);
-	fbuffree(buf);
-	match = rxexecute(nil, rbuf, 0, i, &s);
-	free(rbuf);
-	return match;
-}
-
-Address
-charaddr(long l, Address addr, int sign)
-{
-	if(sign == 0)
-		addr.r.q0 = addr.r.q1 = l;
-	else if(sign < 0)
-		addr.r.q1 = addr.r.q0 -= l;
-	else if(sign > 0)
-		addr.r.q0 = addr.r.q1 += l;
-	if(addr.r.q0<0 || addr.r.q1>addr.f->nc)
-		editerror("address out of range");
-	return addr;
-}
-
-Address
-lineaddr(long l, Address addr, int sign)
-{
-	int n;
-	int c;
-	File *f = addr.f;
-	Address a;
-	long p;
-
-	a.f = f;
-	if(sign >= 0){
-		if(l == 0){
-			if(sign==0 || addr.r.q1==0){
-				a.r.q0 = a.r.q1 = 0;
-				return a;
-			}
-			a.r.q0 = addr.r.q1;
-			p = addr.r.q1-1;
-		}else{
-			if(sign==0 || addr.r.q1==0){
-				p = 0;
-				n = 1;
-			}else{
-				p = addr.r.q1-1;
-				n = textreadc(f->curtext, p++)=='\n';
-			}
-			while(n < l){
-				if(p >= f->nc)
-					editerror("address out of range");
-				if(textreadc(f->curtext, p++) == '\n')
-					n++;
-			}
-			a.r.q0 = p;
-		}
-		while(p < f->nc && textreadc(f->curtext, p++)!='\n')
-			;
-		a.r.q1 = p;
-	}else{
-		p = addr.r.q0;
-		if(l == 0)
-			a.r.q1 = addr.r.q0;
-		else{
-			for(n = 0; n<l; ){	/* always runs once */
-				if(p == 0){
-					if(++n != l)
-						editerror("address out of range");
-				}else{
-					c = textreadc(f->curtext, p-1);
-					if(c != '\n' || ++n != l)
-						p--;
-				}
-			}
-			a.r.q1 = p;
-			if(p > 0)
-				p--;
-		}
-		while(p > 0 && textreadc(f->curtext, p-1)!='\n')	/* lines start after a newline */
-			p--;
-		a.r.q0 = p;
-	}
-	return a;
-}
-
-struct Filecheck
-{
-	File	*f;
-	Rune	*r;
-	int nr;
-};
-
-void
-allfilecheck(Window *w, void *v)
-{
-	struct Filecheck *fp;
-	File *f;
-
-	fp = v;
-	f = w->body.file;
-	if(w->body.file == fp->f)
-		return;
-	if(runeeq(fp->r, fp->nr, f->name, f->nname))
-		warning(nil, "warning: duplicate file name \"%.*S\"\n", fp->nr, fp->r);
-}
-
-Rune*
-cmdname(File *f, String *str, int set)
-{
-	Rune *r, *s;
-	int n;
-	struct Filecheck fc;
-	Runestr newname;
-
-	r = nil;
-	n = str->n;
-	s = str->r;
-	if(n == 0){
-		/* no name; use existing */
-		if(f->nname == 0)
-			return nil;
-		r = runemalloc(f->nname+1);
-		runemove(r, f->name, f->nname);
-		return r;
-	}
-	s = skipbl(s, n, &n);
-	if(n == 0)
-		goto Return;
-
-	if(s[0] == '/'){
-		r = runemalloc(n+1);
-		runemove(r, s, n);
-	}else{
-		newname = dirname(f->curtext, runestrdup(s), n);
-		n = newname.nr;
-		r = runemalloc(n+1);	/* NUL terminate */
-		runemove(r, newname.r, n);
-		free(newname.r);
-	}
-	fc.f = f;
-	fc.r = r;
-	fc.nr = n;
-	allwindows(allfilecheck, &fc);
-	if(f->nname == 0)
-		set = TRUE;
-
-    Return:
-	if(set && !runeeq(r, n, f->name, f->nname)){
-		filemark(f);
-		f->mod = TRUE;
-		f->curtext->w->dirty = TRUE;
-		winsetname(f->curtext->w, r, n);
-	}
-	return r;
-}
--- a/edit.c
+++ /dev/null
@@ -1,679 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <thread.h>
-#include <cursor.h>
-#include <mouse.h>
-#include <keyboard.h>
-#include <frame.h>
-#include <fcall.h>
-#include <plumb.h>
-#include "dat.h"
-#include "edit.h"
-#include "fns.h"
-
-static char	linex[]="\n";
-static char	wordx[]=" \t\n";
-struct cmdtab cmdtab[]={
-/*	cmdc	text	regexp	addr	defcmd	defaddr	count	token	 fn	*/
-	'\n',	0,	0,	0,	0,	aDot,	0,	0,	nl_cmd,
-	'a',	1,	0,	0,	0,	aDot,	0,	0,	a_cmd,
-	'b',	0,	0,	0,	0,	aNo,	0,	linex,	b_cmd,
-	'c',	1,	0,	0,	0,	aDot,	0,	0,	c_cmd,
-	'd',	0,	0,	0,	0,	aDot,	0,	0,	d_cmd,
-	'e',	0,	0,	0,	0,	aNo,	0,	wordx,	e_cmd,
-	'f',	0,	0,	0,	0,	aNo,	0,	wordx,	f_cmd,
-	'g',	0,	1,	0,	'p',	aDot,	0,	0,	g_cmd,
-	'i',	1,	0,	0,	0,	aDot,	0,	0,	i_cmd,
-	'm',	0,	0,	1,	0,	aDot,	0,	0,	m_cmd,
-	'p',	0,	0,	0,	0,	aDot,	0,	0,	p_cmd,
-	'r',	0,	0,	0,	0,	aDot,	0,	wordx,	e_cmd,
-	's',	0,	1,	0,	0,	aDot,	1,	0,	s_cmd,
-	't',	0,	0,	1,	0,	aDot,	0,	0,	m_cmd,
-	'u',	0,	0,	0,	0,	aNo,	2,	0,	u_cmd,
-	'v',	0,	1,	0,	'p',	aDot,	0,	0,	g_cmd,
-	'w',	0,	0,	0,	0,	aAll,	0,	wordx,	w_cmd,
-	'x',	0,	1,	0,	'p',	aDot,	0,	0,	x_cmd,
-	'y',	0,	1,	0,	'p',	aDot,	0,	0,	x_cmd,
-	'=',	0,	0,	0,	0,	aDot,	0,	linex,	eq_cmd,
-	'B',	0,	0,	0,	0,	aNo,	0,	linex,	B_cmd,
-	'D',	0,	0,	0,	0,	aNo,	0,	linex,	D_cmd,
-	'X',	0,	1,	0,	'f',	aNo,	0,	0,	X_cmd,
-	'Y',	0,	1,	0,	'f',	aNo,	0,	0,	X_cmd,
-	'<',	0,	0,	0,	0,	aDot,	0,	linex,	pipe_cmd,
-	'|',	0,	0,	0,	0,	aDot,	0,	linex,	pipe_cmd,
-	'>',	0,	0,	0,	0,	aDot,	0,	linex,	pipe_cmd,
-/* deliberately unimplemented:
-	'k',	0,	0,	0,	0,	aDot,	0,	0,	k_cmd,
-	'n',	0,	0,	0,	0,	aNo,	0,	0,	n_cmd,
-	'q',	0,	0,	0,	0,	aNo,	0,	0,	q_cmd,
-	'!',	0,	0,	0,	0,	aNo,	0,	linex,	plan9_cmd,
- */
-	0,	0,	0,	0,	0,	0,	0,	0,
-};
-
-Cmd	*parsecmd(int);
-Addr	*compoundaddr(void);
-Addr	*simpleaddr(void);
-void	freecmd(void);
-void	okdelim(int);
-
-Rune	*cmdstartp;
-Rune	*cmdendp;
-Rune	*cmdp;
-Channel	*editerrc;
-
-String	*lastpat;
-int	patset;
-
-List	cmdlist;
-List	addrlist;
-List	stringlist;
-Text	*curtext;
-int	editing = Inactive;
-
-String*	newstring(int);
-
-void
-editthread(void*)
-{
-	Cmd *cmdp;
-
-	threadsetname("editthread");
-	while((cmdp=parsecmd(0)) != 0){
-//		ocurfile = curfile;
-//		loaded = curfile && !curfile->unread;
-		if(cmdexec(curtext, cmdp) == 0)
-			break;
-		freecmd();
-	}
-	sendp(editerrc, nil);
-}
-
-void
-allelogterm(Window *w, void*)
-{
-	elogterm(w->body.file);
-}
-
-void
-alleditinit(Window *w, void*)
-{
-	textcommit(&w->tag, TRUE);
-	textcommit(&w->body, TRUE);
-	w->body.file->editclean = FALSE;
-}
-
-void
-allupdate(Window *w, void*)
-{
-	Text *t;
-	int i;
-	File *f;
-
-	t = &w->body;
-	f = t->file;
-	if(f->curtext != t)	/* do curtext only */
-		return;
-	if(f->elog.type == Null)
-		elogterm(f);
-	else if(f->elog.type != Empty){
-		elogapply(f);
-		if(f->editclean){
-			f->mod = FALSE;
-			for(i=0; i<f->ntext; i++)
-				f->text[i]->w->dirty = FALSE;
-		}
-	}
-	textsetselect(t, t->q0, t->q1);
-	textscrdraw(t);
-	winsettag(w);
-}
-
-void
-editerror(char *fmt, ...)
-{
-	va_list arg;
-	char *s;
-
-	va_start(arg, fmt);
-	s = vsmprint(fmt, arg);
-	va_end(arg);
-	freecmd();
-	allwindows(allelogterm, nil);	/* truncate the edit logs */
-	sendp(editerrc, s);
-	threadexits(nil);
-}
-
-void
-editcmd(Text *ct, Rune *r, uint n)
-{
-	char *err;
-
-	if(n == 0)
-		return;
-	if(2*n > RBUFSIZE){
-		warning(nil, "string too long\n");
-		return;
-	}
-
-	allwindows(alleditinit, nil);
-	if(cmdstartp)
-		free(cmdstartp);
-	cmdstartp = runemalloc(n+2);
-	runemove(cmdstartp, r, n);
-	if(r[n] != '\n')
-		cmdstartp[n++] = '\n';
-	cmdstartp[n] = '\0';
-	cmdendp = cmdstartp+n;
-	cmdp = cmdstartp;
-	if(ct->w == nil)
-		curtext = nil;
-	else
-		curtext = &ct->w->body;
-	resetxec();
-	if(editerrc == nil){
-		editerrc = chancreate(sizeof(char*), 0);
-		lastpat = allocstring(0);
-	}
-	threadcreate(editthread, nil, STACK);
-	err = recvp(editerrc);
-	editing = Inactive;
-	if(err != nil){
-		if(err[0] != '\0')
-			warning(nil, "Edit: %s\n", err);
-		free(err);
-	}
-
-	/* update everyone whose edit log has data */
-	allwindows(allupdate, nil);
-}
-
-int
-getch(void)
-{
-	if(*cmdp == *cmdendp)
-		return -1;
-	return *cmdp++;
-}
-
-int
-nextc(void)
-{
-	if(*cmdp == *cmdendp)
-		return -1;
-	return *cmdp;
-}
-
-void
-ungetch(void)
-{
-	if(--cmdp < cmdstartp)
-		error("ungetch");
-}
-
-long
-getnum(int signok)
-{
-	long n;
-	int c, sign;
-
-	n = 0;
-	sign = 1;
-	if(signok>1 && nextc()=='-'){
-		sign = -1;
-		getch();
-	}
-	if((c=nextc())<'0' || '9'<c)	/* no number defaults to 1 */
-		return sign;
-	while('0'<=(c=getch()) && c<='9')
-		n = n*10 + (c-'0');
-	ungetch();
-	return sign*n;
-}
-
-int
-cmdskipbl(void)
-{
-	int c;
-	do
-		c = getch();
-	while(c==' ' || c=='\t');
-	if(c >= 0)
-		ungetch();
-	return c;
-}
-
-/*
- * Check that list has room for one more element.
- */
-void
-growlist(List *l)
-{
-	if(l->listptr==0 || l->nalloc==0){
-		l->nalloc = INCR;
-		l->listptr = emalloc(INCR*sizeof(void*));
-		l->nused = 0;
-	}else if(l->nused == l->nalloc){
-		l->listptr = erealloc(l->listptr, (l->nalloc+INCR)*sizeof(void*));
-		memset(l->ptr+l->nalloc, 0, INCR*sizeof(void*));
-		l->nalloc += INCR;
-	}
-}
-
-/*
- * Remove the ith element from the list
- */
-void
-dellist(List *l, int i)
-{
-	l->nused--;
-	memmove(&l->ptr[i], &l->ptr[i+1], (l->nused-i)*sizeof(void*));
-}
-
-/*
- * Add a new element, whose position is i, to the list
- */
-void
-inslist(List *l, int i, void *v)
-{
-	growlist(l);
-	memmove(&l->ptr[i+1], &l->ptr[i], (l->nused-i)*sizeof(void*));
-	l->ptr[i] = v;
-	l->nused++;
-}
-
-void
-listfree(List *l)
-{
-	free(l->listptr);
-	free(l);
-}
-
-String*
-allocstring(int n)
-{
-	String *s;
-
-	s = emalloc(sizeof(String));
-	s->n = n;
-	s->nalloc = n+10;
-	s->r = emalloc(s->nalloc*sizeof(Rune));
-	s->r[n] = '\0';
-	return s;
-}
-
-void
-freestring(String *s)
-{
-	free(s->r);
-	free(s);
-}
-
-Cmd*
-newcmd(void){
-	Cmd *p;
-
-	p = emalloc(sizeof(Cmd));
-	inslist(&cmdlist, cmdlist.nused, p);
-	return p;
-}
-
-String*
-newstring(int n)
-{
-	String *p;
-
-	p = allocstring(n);
-	inslist(&stringlist, stringlist.nused, p);
-	return p;
-}
-
-Addr*
-newaddr(void)
-{
-	Addr *p;
-
-	p = emalloc(sizeof(Addr));
-	inslist(&addrlist, addrlist.nused, p);
-	return p;
-}
-
-void
-freecmd(void)
-{
-	int i;
-
-	while(cmdlist.nused > 0)
-		free(cmdlist.ucharptr[--cmdlist.nused]);
-	while(addrlist.nused > 0)
-		free(addrlist.ucharptr[--addrlist.nused]);
-	while(stringlist.nused>0){
-		i = --stringlist.nused;
-		freestring(stringlist.stringptr[i]);
-	}
-}
-
-void
-okdelim(int c)
-{
-	if(c=='\\' || ('a'<=c && c<='z')
-	|| ('A'<=c && c<='Z') || ('0'<=c && c<='9'))
-		editerror("bad delimiter %c\n", c);
-}
-
-void
-atnl(void)
-{
-	int c;
-
-	cmdskipbl();
-	c = getch();
-	if(c != '\n')
-		editerror("newline expected (saw %C)", c);
-}
-
-void
-Straddc(String *s, int c)
-{
-	if(s->n+1 >= s->nalloc){
-		s->nalloc += 10;
-		s->r = erealloc(s->r, s->nalloc*sizeof(Rune));
-	}
-	s->r[s->n++] = c;
-	s->r[s->n] = '\0';
-}
-
-void
-getrhs(String *s, int delim, int cmd)
-{
-	int c;
-
-	while((c = getch())>0 && c!=delim && c!='\n'){
-		if(c == '\\'){
-			if((c=getch()) <= 0)
-				error("bad right hand side");
-			if(c == '\n'){
-				ungetch();
-				c='\\';
-			}else if(c == 'n')
-				c='\n';
-			else if(c!=delim && (cmd=='s' || c!='\\'))	/* s does its own */
-				Straddc(s, '\\');
-		}
-		Straddc(s, c);
-	}
-	ungetch();	/* let client read whether delimiter, '\n' or whatever */
-}
-
-String *
-collecttoken(char *end)
-{
-	String *s = newstring(0);
-	int c;
-
-	while((c=nextc())==' ' || c=='\t')
-		Straddc(s, getch()); /* blanks significant for getname() */
-	while((c=getch())>0 && utfrune(end, c)==0)
-		Straddc(s, c);
-	if(c != '\n')
-		atnl();
-	return s;
-}
-
-String *
-collecttext(void)
-{
-	String *s;
-	int begline, i, c, delim;
-
-	s = newstring(0);
-	if(cmdskipbl()=='\n'){
-		getch();
-		i = 0;
-		do{
-			begline = i;
-			while((c = getch())>0 && c!='\n')
-				i++, Straddc(s, c);
-			i++, Straddc(s, '\n');
-			if(c < 0)
-				goto Return;
-		}while(s->r[begline]!='.' || s->r[begline+1]!='\n');
-		s->r[s->n-2] = '\0';
-		s->n -= 2;
-	}else{
-		okdelim(delim = getch());
-		getrhs(s, delim, 'a');
-		if(nextc()==delim)
-			getch();
-		atnl();
-	}
-    Return:
-	return s;
-}
-
-int
-cmdlookup(int c)
-{
-	int i;
-
-	for(i=0; cmdtab[i].cmdc; i++)
-		if(cmdtab[i].cmdc == c)
-			return i;
-	return -1;
-}
-
-Cmd*
-parsecmd(int nest)
-{
-	int i, c;
-	struct cmdtab *ct;
-	Cmd *cp, *ncp;
-	Cmd cmd;
-
-	cmd.next = cmd.cmd = 0;
-	cmd.re = 0;
-	cmd.flag = cmd.num = 0;
-	cmd.addr = compoundaddr();
-	if(cmdskipbl() == -1)
-		return 0;
-	if((c=getch())==-1)
-		return 0;
-	cmd.cmdc = c;
-	if(cmd.cmdc=='c' && nextc()=='d'){	/* sleazy two-character case */
-		getch();		/* the 'd' */
-		cmd.cmdc='c'|0x100;
-	}
-	i = cmdlookup(cmd.cmdc);
-	if(i >= 0){
-		if(cmd.cmdc == '\n')
-			goto Return;	/* let nl_cmd work it all out */
-		ct = &cmdtab[i];
-		if(ct->defaddr==aNo && cmd.addr)
-			editerror("command takes no address");
-		if(ct->count)
-			cmd.num = getnum(ct->count);
-		if(ct->regexp){
-			/* x without pattern -> .*\n, indicated by cmd.re==0 */
-			/* X without pattern is all files */
-			if((ct->cmdc!='x' && ct->cmdc!='X') ||
-			   ((c = nextc())!=' ' && c!='\t' && c!='\n')){
-				cmdskipbl();
-				if((c = getch())=='\n' || c<0)
-					editerror("no address");
-				okdelim(c);
-				cmd.re = getregexp(c);
-				if(ct->cmdc == 's'){
-					cmd.text = newstring(0);
-					getrhs(cmd.text, c, 's');
-					if(nextc() == c){
-						getch();
-						if(nextc() == 'g')
-							cmd.flag = getch();
-					}
-			
-				}
-			}
-		}
-		if(ct->addr && (cmd.mtaddr=simpleaddr())==0)
-			editerror("bad address");
-		if(ct->defcmd){
-			if(cmdskipbl() == '\n'){
-				getch();
-				cmd.cmd = newcmd();
-				cmd.cmd->cmdc = ct->defcmd;
-			}else if((cmd.cmd = parsecmd(nest))==0)
-				error("defcmd");
-		}else if(ct->text)
-			cmd.text = collecttext();
-		else if(ct->token)
-			cmd.text = collecttoken(ct->token);
-		else
-			atnl();
-	}else
-		switch(cmd.cmdc){
-		case '{':
-			cp = 0;
-			do{
-				if(cmdskipbl()=='\n')
-					getch();
-				ncp = parsecmd(nest+1);
-				if(cp)
-					cp->next = ncp;
-				else
-					cmd.cmd = ncp;
-			}while(cp = ncp);
-			break;
-		case '}':
-			atnl();
-			if(nest==0)
-				editerror("right brace with no left brace");
-			return 0;
-		default:
-			editerror("unknown command %c", cmd.cmdc);
-		}
-    Return:
-	cp = newcmd();
-	*cp = cmd;
-	return cp;
-}
-
-String*
-getregexp(int delim)
-{
-	String *buf, *r;
-	int i, c;
-
-	buf = allocstring(0);
-	for(i=0; ; i++){
-		if((c = getch())=='\\'){
-			if(nextc()==delim)
-				c = getch();
-			else if(nextc()=='\\'){
-				Straddc(buf, c);
-				c = getch();
-			}
-		}else if(c==delim || c=='\n')
-			break;
-		if(i >= RBUFSIZE)
-			editerror("regular expression too long");
-		Straddc(buf, c);
-	}
-	if(c!=delim && c)
-		ungetch();
-	if(buf->n > 0){
-		patset = TRUE;
-		freestring(lastpat);
-		lastpat = buf;
-	}else
-		freestring(buf);
-	if(lastpat->n == 0)
-		editerror("no regular expression defined");
-	r = newstring(lastpat->n);
-	runemove(r->r, lastpat->r, lastpat->n);	/* newstring put \0 at end */
-	return r;
-}
-
-Addr *
-simpleaddr(void)
-{
-	Addr addr;
-	Addr *ap, *nap;
-
-	addr.next = 0;
-	addr.left = 0;
-	switch(cmdskipbl()){
-	case '#':
-		addr.type = getch();
-		addr.num = getnum(1);
-		break;
-	case '0': case '1': case '2': case '3': case '4':
-	case '5': case '6': case '7': case '8': case '9': 
-		addr.num = getnum(1);
-		addr.type='l';
-		break;
-	case '/': case '?': case '"':
-		addr.re = getregexp(addr.type = getch());
-		break;
-	case '.':
-	case '$':
-	case '+':
-	case '-':
-	case '\'':
-		addr.type = getch();
-		break;
-	default:
-		return 0;
-	}
-	if(addr.next = simpleaddr())
-		switch(addr.next->type){
-		case '.':
-		case '$':
-		case '\'':
-			if(addr.type!='"')
-		case '"':
-				editerror("bad address syntax");
-			break;
-		case 'l':
-		case '#':
-			if(addr.type=='"')
-				break;
-			/* fall through */
-		case '/':
-		case '?':
-			if(addr.type!='+' && addr.type!='-'){
-				/* insert the missing '+' */
-				nap = newaddr();
-				nap->type='+';
-				nap->next = addr.next;
-				addr.next = nap;
-			}
-			break;
-		case '+':
-		case '-':
-			break;
-		default:
-			error("simpleaddr");
-		}
-	ap = newaddr();
-	*ap = addr;
-	return ap;
-}
-
-Addr *
-compoundaddr(void)
-{
-	Addr addr;
-	Addr *ap, *next;
-
-	addr.left = simpleaddr();
-	if((addr.type = cmdskipbl())!=',' && addr.type!=';')
-		return addr.left;
-	getch();
-	next = addr.next = compoundaddr();
-	if(next && (next->type==',' || next->type==';') && next->left==0)
-		editerror("bad address syntax");
-	ap = newaddr();
-	*ap = addr;
-	return ap;
-}
--- a/edit.h
+++ /dev/null
@@ -1,99 +1,0 @@
-#pragma	varargck	argpos	editerror	1
-
-typedef struct Addr	Addr;
-typedef struct Address	Address;
-typedef struct Cmd	Cmd;
-typedef struct List	List;
-typedef struct String	String;
-
-struct String
-{
-	int	n;		/* excludes NUL */
-	Rune	*r;		/* includes NUL */
-	int	nalloc;
-};
-
-struct Addr
-{
-	char	type;	/* # (char addr), l (line addr), / ? . $ + - , ; */
-	union{
-		String	*re;
-		Addr	*left;		/* left side of , and ; */
-	};
-	ulong	num;
-	Addr	*next;			/* or right side of , and ; */
-};
-
-struct Address
-{
-	Range	r;
-	File	*f;
-};
-
-struct Cmd
-{
-	Addr	*addr;			/* address (range of text) */
-	String	*re;			/* regular expression for e.g. 'x' */
-	union{
-		Cmd	*cmd;		/* target of x, g, {, etc. */
-		String	*text;		/* text of a, c, i; rhs of s */
-		Addr	*mtaddr;		/* address for m, t */
-	};
-	Cmd	*next;			/* pointer to next element in {} */
-	short	num;
-	ushort	flag;			/* whatever */
-	ushort	cmdc;			/* command character; 'x' etc. */
-};
-
-extern struct cmdtab{
-	ushort	cmdc;		/* command character */
-	uchar	text;		/* takes a textual argument? */
-	uchar	regexp;		/* takes a regular expression? */
-	uchar	addr;		/* takes an address (m or t)? */
-	uchar	defcmd;		/* default command; 0==>none */
-	uchar	defaddr;	/* default address */
-	uchar	count;		/* takes a count e.g. s2/// */
-	char	*token;		/* takes text terminated by one of these */
-	int	(*fn)(Text*, Cmd*);	/* function to call with parse tree */
-}cmdtab[];
-
-#define	INCR	25	/* delta when growing list */
-
-struct List
-{
-	int	nalloc;
-	int	nused;
-	union{
-		void	*listptr;
-		void*	*ptr;
-		uchar*	*ucharptr;
-		String*	*stringptr;
-	};
-};
-
-enum Defaddr{	/* default addresses */
-	aNo,
-	aDot,
-	aAll,
-};
-
-int	nl_cmd(Text*, Cmd*), a_cmd(Text*, Cmd*), b_cmd(Text*, Cmd*);
-int	c_cmd(Text*, Cmd*), d_cmd(Text*, Cmd*);
-int	B_cmd(Text*, Cmd*), D_cmd(Text*, Cmd*), e_cmd(Text*, Cmd*);
-int	f_cmd(Text*, Cmd*), g_cmd(Text*, Cmd*), i_cmd(Text*, Cmd*);
-int	k_cmd(Text*, Cmd*), m_cmd(Text*, Cmd*), n_cmd(Text*, Cmd*);
-int	p_cmd(Text*, Cmd*);
-int	s_cmd(Text*, Cmd*), u_cmd(Text*, Cmd*), w_cmd(Text*, Cmd*);
-int	x_cmd(Text*, Cmd*), X_cmd(Text*, Cmd*), pipe_cmd(Text*, Cmd*);
-int	eq_cmd(Text*, Cmd*);
-
-String	*allocstring(int);
-void	freestring(String*);
-String	*getregexp(int);
-Addr	*newaddr(void);
-Address	cmdaddress(Addr*, Address, int);
-int	cmdexec(Text*, Cmd*);
-void	editerror(char*, ...);
-int	cmdlookup(int);
-void	resetxec(void);
-void	Straddc(String*, int);
--- a/elog.c
+++ /dev/null
@@ -1,353 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <thread.h>
-#include <cursor.h>
-#include <mouse.h>
-#include <keyboard.h>
-#include <frame.h>
-#include <fcall.h>
-#include <plumb.h>
-#include "dat.h"
-#include "fns.h"
-#include "edit.h"
-
-static char Wsequence[] = "warning: changes out of sequence\n";
-static int	warned = FALSE;
-
-/*
- * Log of changes made by editing commands.  Three reasons for this:
- * 1) We want addresses in commands to apply to old file, not file-in-change.
- * 2) It's difficult to track changes correctly as things move, e.g. ,x m$
- * 3) This gives an opportunity to optimize by merging adjacent changes.
- * It's a little bit like the Undo/Redo log in Files, but Point 3) argues for a
- * separate implementation.  To do this well, we use Replace as well as
- * Insert and Delete
- */
-
-typedef struct Buflog Buflog;
-struct Buflog
-{
-	short	type;		/* Replace, Filename */
-	uint		q0;		/* location of change (unused in f) */
-	uint		nd;		/* # runes to delete */
-	uint		nr;		/* # runes in string or file name */
-};
-
-enum
-{
-	Buflogsize = sizeof(Buflog)/sizeof(Rune),
-};
-
-/*
- * Minstring shouldn't be very big or we will do lots of I/O for small changes.
- * Maxstring is RBUFSIZE so we can fbufalloc() once and not realloc elog.r.
- */
-enum
-{
-	Minstring = 16,		/* distance beneath which we merge changes */
-	Maxstring = RBUFSIZE,	/* maximum length of change we will merge into one */
-};
-
-void
-eloginit(File *f)
-{
-	if(f->elog.type != Empty)
-		return;
-	f->elog.type = Null;
-	if(f->elogbuf == nil)
-		f->elogbuf = emalloc(sizeof(Buffer));
-	if(f->elog.r == nil)
-		f->elog.r = fbufalloc();
-	bufreset(f->elogbuf);
-}
-
-void
-elogclose(File *f)
-{
-	if(f->elogbuf){
-		bufclose(f->elogbuf);
-		free(f->elogbuf);
-		f->elogbuf = nil;
-	}
-}
-
-void
-elogreset(File *f)
-{
-	f->elog.type = Null;
-	f->elog.nd = 0;
-	f->elog.nr = 0;
-}
-
-void
-elogterm(File *f)
-{
-	elogreset(f);
-	if(f->elogbuf)
-		bufreset(f->elogbuf);
-	f->elog.type = Empty;
-	fbuffree(f->elog.r);
-	f->elog.r = nil;
-	warned = FALSE;
-}
-
-void
-elogflush(File *f)
-{
-	Buflog b;
-
-	b.type = f->elog.type;
-	b.q0 = f->elog.q0;
-	b.nd = f->elog.nd;
-	b.nr = f->elog.nr;
-	switch(f->elog.type){
-	default:
-		warning(nil, "unknown elog type 0x%ux\n", f->elog.type);
-		break;
-	case Null:
-		break;
-	case Insert:
-	case Replace:
-		if(f->elog.nr > 0)
-			bufinsert(f->elogbuf, f->elogbuf->nc, f->elog.r, f->elog.nr);
-		/* fall through */
-	case Delete:
-		bufinsert(f->elogbuf, f->elogbuf->nc, (Rune*)&b, Buflogsize);
-		break;
-	}
-	elogreset(f);
-}
-
-void
-elogreplace(File *f, int q0, int q1, Rune *r, int nr)
-{
-	uint gap;
-
-	if(q0==q1 && nr==0)
-		return;
-	eloginit(f);
-	if(f->elog.type!=Null && q0<f->elog.q0){
-		if(warned++ == 0)
-			warning(nil, Wsequence);
-		elogflush(f);
-	}
-	/* try to merge with previous */
-	gap = q0 - (f->elog.q0+f->elog.nd);	/* gap between previous and this */
-	if(f->elog.type==Replace && f->elog.nr+gap+nr<Maxstring){
-		if(gap < Minstring){
-			if(gap > 0){
-				bufread(f, f->elog.q0+f->elog.nd, f->elog.r+f->elog.nr, gap);
-				f->elog.nr += gap;
-			}
-			f->elog.nd += gap + q1-q0;
-			runemove(f->elog.r+f->elog.nr, r, nr);
-			f->elog.nr += nr;
-			return;
-		}
-	}
-	elogflush(f);
-	f->elog.type = Replace;
-	f->elog.q0 = q0;
-	f->elog.nd = q1-q0;
-	f->elog.nr = nr;
-	if(nr > RBUFSIZE)
-		editerror("internal error: replacement string too large(%d)", nr);
-	runemove(f->elog.r, r, nr);
-}
-
-void
-eloginsert(File *f, int q0, Rune *r, int nr)
-{
-	int n;
-
-	if(nr == 0)
-		return;
-	eloginit(f);
-	if(f->elog.type!=Null && q0<f->elog.q0){
-		if(warned++ == 0)
-			warning(nil, Wsequence);
-		elogflush(f);
-	}
-	/* try to merge with previous */
-	if(f->elog.type==Insert && q0==f->elog.q0 && f->elog.nr+nr<Maxstring){
-		runemove(f->elog.r+f->elog.nr, r, nr);
-		f->elog.nr += nr;
-		return;
-	}
-	while(nr > 0){
-		elogflush(f);
-		f->elog.type = Insert;
-		f->elog.q0 = q0;
-		n = nr;
-		if(n > RBUFSIZE)
-			n = RBUFSIZE;
-		f->elog.nr = n;
-		runemove(f->elog.r, r, n);
-		r += n;
-		nr -= n;
-	}
-}
-
-void
-elogdelete(File *f, int q0, int q1)
-{
-	if(q0 == q1)
-		return;
-	eloginit(f);
-	if(f->elog.type!=Null && q0<f->elog.q0+f->elog.nd){
-		if(warned++ == 0)
-			warning(nil, Wsequence);
-		elogflush(f);
-	}
-	/* try to merge with previous */
-	if(f->elog.type==Delete && f->elog.q0+f->elog.nd==q0){
-		f->elog.nd += q1-q0;
-		return;
-	}
-	elogflush(f);
-	f->elog.type = Delete;
-	f->elog.q0 = q0;
-	f->elog.nd = q1-q0;
-}
-
-#define tracelog 0
-void
-elogapply(File *f)
-{
-	Buflog b;
-	Rune *buf;
-	uint i, n, up, mod;
-	uint tq0, tq1;
-	Buffer *log;
-	Text *t;
-	int owner;
-
-	elogflush(f);
-	log = f->elogbuf;
-	t = f->curtext;
-
-	buf = fbufalloc();
-	mod = FALSE;
-
-	owner = 0;
-	if(t->w){
-		owner = t->w->owner;
-		if(owner == 0)
-			t->w->owner = 'E';
-	}
-
-	/*
-	 * The edit commands have already updated the selection in t->q0, t->q1,
-	 * but using coordinates relative to the unmodified buffer.  As we apply the log,
-	 * we have to update the coordinates to be relative to the modified buffer.
-	 * Textinsert and textdelete will do this for us; our only work is to apply the
-	 * convention that an insertion at t->q0==t->q1 is intended to select the 
-	 * inserted text.
-	 */
-
-	/*
-	 * We constrain the addresses in here (with textconstrain()) because
-	 * overlapping changes will generate bogus addresses.   We will warn
-	 * about changes out of sequence but proceed anyway; here we must
-	 * keep things in range.
-	 */
-
-	while(log->nc > 0){
-		up = log->nc-Buflogsize;
-		bufread(log, up, (Rune*)&b, Buflogsize);
-		switch(b.type){
-		default:
-			fprint(2, "elogapply: 0x%ux\n", b.type);
-			abort();
-			break;
-
-		case Replace:
-			if(tracelog)
-				warning(nil, "elog replace %d %d (%d %d)\n",
-					b.q0, b.q0+b.nd, t->q0, t->q1);
-			if(!mod){
-				mod = TRUE;
-				filemark(f);
-			}
-			textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1);
-			textdelete(t, tq0, tq1, TRUE);
-			up -= b.nr;
-			for(i=0; i<b.nr; i+=n){
-				n = b.nr - i;
-				if(n > RBUFSIZE)
-					n = RBUFSIZE;
-				bufread(log, up+i, buf, n);
-				textinsert(t, tq0+i, buf, n, TRUE);
-			}
-			if(t->q0 == b.q0 && t->q1 == b.q0)
-				t->q1 += b.nr;
-			break;
-
-		case Delete:
-			if(tracelog)
-				warning(nil, "elog delete %d %d (%d %d)\n",
-					b.q0, b.q0+b.nd, t->q0, t->q1);
-			if(!mod){
-				mod = TRUE;
-				filemark(f);
-			}
-			textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1);
-			textdelete(t, tq0, tq1, TRUE);
-			break;
-
-		case Insert:
-			if(tracelog)
-				warning(nil, "elog insert %d %d (%d %d)\n",
-					b.q0, b.q0+b.nr, t->q0, t->q1);
-			if(!mod){
-				mod = TRUE;
-				filemark(f);
-			}
-			textconstrain(t, b.q0, b.q0, &tq0, &tq1);
-			up -= b.nr;
-			for(i=0; i<b.nr; i+=n){
-				n = b.nr - i;
-				if(n > RBUFSIZE)
-					n = RBUFSIZE;
-				bufread(log, up+i, buf, n);
-				textinsert(t, tq0+i, buf, n, TRUE);
-			}
-			if(t->q0 == b.q0 && t->q1 == b.q0)
-				t->q1 += b.nr;
-			break;
-
-/*		case Filename:
-			f->seq = u.seq;
-			fileunsetname(f, epsilon);
-			f->mod = u.mod;
-			up -= u.n;
-			free(f->name);
-			if(u.n == 0)
-				f->name = nil;
-			else
-				f->name = runemalloc(u.n);
-			bufread(delta, up, f->name, u.n);
-			f->nname = u.n;
-			break;
-*/
-		}
-		bufdelete(log, up, log->nc);
-	}
-	fbuffree(buf);
-	elogterm(f);
-
-	/*
-	 * Bad addresses will cause bufload to crash, so double check.
-	 * If changes were out of order, we expect problems so don't complain further.
-	 */
-	if(t->q0 > f->nc || t->q1 > f->nc || t->q0 > t->q1){
-		if(!warned)
-			warning(nil, "elogapply: can't happen %d %d %d\n", t->q0, t->q1, f->nc);
-		t->q1 = min(t->q1, f->nc);
-		t->q0 = min(t->q0, t->q1);
-	}
-
-	if(t->w)
-		t->w->owner = owner;
-}
--- a/exec.c
+++ /dev/null
@@ -1,1501 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <thread.h>
-#include <cursor.h>
-#include <mouse.h>
-#include <keyboard.h>
-#include <frame.h>
-#include <fcall.h>
-#include <plumb.h>
-#include "dat.h"
-#include "fns.h"
-
-Buffer	snarfbuf;
-
-/*
- * These functions get called as:
- *
- *	fn(et, t, argt, flag1, flag1, flag2, s, n);
- *
- * Where the arguments are:
- *
- *	et: the Text* in which the executing event (click) occurred
- *	t: the Text* containing the current selection (Edit, Cut, Snarf, Paste)
- *	argt: the Text* containing the argument for a 2-1 click.
- *	e->flag1: from Exectab entry
- * 	e->flag2: from Exectab entry
- *	s: the command line remainder (e.g., "x" if executing "Dump x")
- *	n: length of s  (s is *not* NUL-terminated)
- */
-
-void	del(Text*, Text*, Text*, int, int, Rune*, int);
-void	delcol(Text*, Text*, Text*, int, int, Rune*, int);
-void	dump(Text*, Text*, Text*, int, int, Rune*, int);
-void	edit(Text*, Text*, Text*, int, int, Rune*, int);
-void	exit(Text*, Text*, Text*, int, int, Rune*, int);
-void	fontx(Text*, Text*, Text*, int, int, Rune*, int);
-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	kill(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);
-void	newcol(Text*, Text*, Text*, int, int, Rune*, int);
-void	paste(Text*, Text*, Text*, int, int, Rune*, int);
-void	put(Text*, Text*, Text*, int, int, Rune*, int);
-void	putall(Text*, Text*, Text*, int, int, Rune*, int);
-void	sendx(Text*, Text*, Text*, int, int, Rune*, int);
-void	sort(Text*, Text*, Text*, int, int, Rune*, int);
-void	tab(Text*, Text*, Text*, int, int, Rune*, int);
-void	zeroxx(Text*, Text*, Text*, int, int, Rune*, int);
-
-typedef struct Exectab Exectab;
-struct Exectab
-{
-	Rune	*name;
-	void	(*fn)(Text*, Text*, Text*, int, int, Rune*, int);
-	int		mark;
-	int		flag1;
-	int		flag2;
-};
-
-Exectab exectab[] = {
-	{ L"Cut",		cut,		TRUE,	TRUE,	TRUE	},
-	{ L"Del",		del,		FALSE,	FALSE,	XXX		},
-	{ L"Delcol",	delcol,	FALSE,	XXX,		XXX		},
-	{ L"Delete",	del,		FALSE,	TRUE,	XXX		},
-	{ L"Dump",	dump,	FALSE,	TRUE,	XXX		},
-	{ L"Edit",		edit,		FALSE,	XXX,		XXX		},
-	{ L"Exit",		exit,		FALSE,	XXX,		XXX		},
-	{ L"Font",		fontx,	FALSE,	XXX,		XXX		},
-	{ L"Get",		get,		FALSE,	TRUE,	XXX		},
-	{ L"ID",		id,		FALSE,	XXX,		XXX		},
-	{ L"Incl",		incl,		FALSE,	XXX,		XXX		},
-	{ L"Indent",	indent,		FALSE,	AUTOINDENT,		XXX		},
-	{ L"Kill",		kill,		FALSE,	XXX,		XXX		},
-	{ L"Load",		dump,	FALSE,	FALSE,	XXX		},
-	{ L"Local",		local,	FALSE,	XXX,		XXX		},
-	{ L"Look",		look,		FALSE,	XXX,		XXX		},
-	{ L"New",		new,		FALSE,	XXX,		XXX		},
-	{ L"Newcol",	newcol,	FALSE,	XXX,		XXX		},
-	{ L"Paste",		paste,	TRUE,	TRUE,	XXX		},
-	{ L"Put",		put,		FALSE,	XXX,		XXX		},
-	{ L"Putall",		putall,	FALSE,	XXX,		XXX		},
-	{ L"Redo",		undo,	FALSE,	FALSE,	XXX		},
-	{ L"Send",		sendx,	TRUE,	XXX,		XXX		},
-	{ L"Snarf",		cut,		FALSE,	TRUE,	FALSE	},
-	{ L"Sort",		sort,		FALSE,	XXX,		XXX		},
-	{ L"Spaces",	indent,		FALSE,	SPACESINDENT,	XXX		},
-	{ L"Tab",		tab,		FALSE,	XXX,		XXX		},
-	{ L"Undo",		undo,	FALSE,	TRUE,	XXX		},
-	{ L"Zerox",	zeroxx,	FALSE,	XXX,		XXX		},
-	{ nil, 			nil,		0,		0,		0		},
-};
-
-Exectab*
-lookup(Rune *r, int n)
-{
-	Exectab *e;
-	int nr;
-
-	r = skipbl(r, n, &n);
-	if(n == 0)
-		return nil;
-	findbl(r, n, &nr);
-	nr = n-nr;
-	for(e=exectab; e->name; e++)
-		if(runeeq(r, nr, e->name, runestrlen(e->name)) == TRUE)
-			return e;
-	return nil;
-}
-
-int
-isexecc(int c)
-{
-	if(isfilec(c))
-		return 1;
-	return c=='<' || c=='|' || c=='>';
-}
-
-void
-execute(Text *t, uint aq0, uint aq1, int external, Text *argt)
-{
-	uint q0, q1;
-	Rune *r, *s;
-	char *b, *a, *aa;
-	Exectab *e;
-	int c, n, f;
-	Runestr dir;
-
-	q0 = aq0;
-	q1 = aq1;
-	if(q1 == q0){	/* expand to find word (actually file name) */
-		/* if in selection, choose selection */
-		if(t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){
-			q0 = t->q0;
-			q1 = t->q1;
-		}else{
-			while(q1<t->file->nc && isexecc(c=textreadc(t, q1)) && c!=':')
-				q1++;
-			while(q0>0 && isexecc(c=textreadc(t, q0-1)) && c!=':')
-				q0--;
-			if(q1 == q0)
-				return;
-		}
-	}
-	r = runemalloc(q1-q0);
-	bufread(t->file, q0, r, q1-q0);
-	free(delcmd);
-	delcmd = runesmprint("%.*S", q1-q0, r);
-	e = lookup(r, q1-q0);
-	if(!external && t->w!=nil && t->w->nopen[QWevent]>0){
-		f = 0;
-		if(e)
-			f |= 1;
-		if(q0!=aq0 || q1!=aq1){
-			bufread(t->file, aq0, r, aq1-aq0);
-			f |= 2;
-		}
-		aa = getbytearg(argt, TRUE, TRUE, &a);
-		if(a){	
-			if(strlen(a) > EVENTSIZE){	/* too big; too bad */
-				free(r);
-				free(aa);
-				free(a);
-				warning(nil, "`argument string too long\n");
-				return;
-			}
-			f |= 8;
-		}
-		c = 'x';
-		if(t->what == Body)
-			c = 'X';
-		n = aq1-aq0;
-		if(n <= EVENTSIZE)
-			winevent(t->w, "%c%d %d %d %d %.*S\n", c, aq0, aq1, f, n, n, r);
-		else
-			winevent(t->w, "%c%d %d %d 0 \n", c, aq0, aq1, f, n);
-		if(q0!=aq0 || q1!=aq1){
-			n = q1-q0;
-			bufread(t->file, q0, r, n);
-			if(n <= EVENTSIZE)
-				winevent(t->w, "%c%d %d 0 %d %.*S\n", c, q0, q1, n, n, r);
-			else
-				winevent(t->w, "%c%d %d 0 0 \n", c, q0, q1, n);
-		}
-		if(a){
-			winevent(t->w, "%c0 0 0 %d %s\n", c, utflen(a), a);
-			if(aa)
-				winevent(t->w, "%c0 0 0 %d %s\n", c, utflen(aa), aa);
-			else
-				winevent(t->w, "%c0 0 0 0 \n", c);
-		}
-		free(r);
-		free(aa);
-		free(a);
-		return;
-	}
-	if(e){
-		if(e->mark && seltext!=nil)
-		if(seltext->what == Body){
-			seq++;
-			filemark(seltext->w->body.file);
-		}
-		s = skipbl(r, q1-q0, &n);
-		s = findbl(s, n, &n);
-		s = skipbl(s, n, &n);
-		(*e->fn)(t, seltext, argt, e->flag1, e->flag2, s, n);
-		free(r);
-		return;
-	}
-
-	b = runetobyte(r, q1-q0);
-	free(r);
-	dir = dirname(t, nil, 0);
-	if(dir.nr==1 && dir.r[0]=='.'){	/* sigh */
-		free(dir.r);
-		dir.r = nil;
-		dir.nr = 0;
-	}
-	aa = getbytearg(argt, TRUE, TRUE, &a);
-	if(t->w)
-		incref(t->w);
-	run(t->w, b, dir.r, dir.nr, TRUE, aa, a, FALSE);
-}
-
-char*
-printarg(Text *argt, uint q0, uint q1)
-{
-	char *buf;
-
-	if(argt->what!=Body || argt->file->name==nil)
-		return nil;
-	buf = emalloc(argt->file->nname+32);
-	if(q0 == q1)
-		sprint(buf, "%.*S:#%d", argt->file->nname, argt->file->name, q0);
-	else
-		sprint(buf, "%.*S:#%d,#%d", argt->file->nname, argt->file->name, q0, q1);
-	return buf;
-}
-
-char*
-getarg(Text *argt, int doaddr, int dofile, Rune **rp, int *nrp)
-{
-	int n;
-	Expand e;
-	char *a;
-
-	*rp = nil;
-	*nrp = 0;
-	if(argt == nil)
-		return nil;
-	a = nil;
-	textcommit(argt, TRUE);
-	if(expand(argt, argt->q0, argt->q1, &e)){
-		free(e.bname);
-		if(e.nname && dofile){
-			e.name = runerealloc(e.name, e.nname+1);
-			if(doaddr)
-				a = printarg(argt, e.q0, e.q1);
-			*rp = e.name;
-			*nrp = e.nname;
-			return a;
-		}
-		free(e.name);
-	}else{
-		e.q0 = argt->q0;
-		e.q1 = argt->q1;
-	}
-	n = e.q1 - e.q0;
-	*rp = runemalloc(n+1);
-	bufread(argt->file, e.q0, *rp, n);
-	if(doaddr)
-		a = printarg(argt, e.q0, e.q1);
-	*nrp = n;
-	return a;
-}
-
-char*
-getbytearg(Text *argt, int doaddr, int dofile, char **bp)
-{
-	Rune *r;
-	int n;
-	char *aa;
-
-	*bp = nil;
-	aa = getarg(argt, doaddr, dofile, &r, &n);
-	if(r == nil)
-		return nil;
-	*bp = runetobyte(r, n);
-	free(r);
-	return aa;
-}
-
-void
-newcol(Text *et, Text*, Text*, int, int, Rune*, int)
-{
-	Column *c;
-	Window *w;
-
-	c = rowadd(et->row, nil, -1);
-	if(c) {
-		w = coladd(c, nil, nil, -1);
-		winsettag(w);
-		xfidlog(w, "new");
-	}
-}
-
-void
-delcol(Text *et, Text*, Text*, int, int, Rune*, int)
-{
-	int i;
-	Column *c;
-	Window *w;
-
-	c = et->col;
-	if(c==nil || colclean(c)==0)
-		return;
-	for(i=0; i<c->nw; i++){
-		w = c->w[i];
-		if(w->nopen[QWevent]+w->nopen[QWaddr]+w->nopen[QWdata]+w->nopen[QWxdata] > 0){
-			warning(nil, "can't delete column; %.*S is running an external command\n", w->body.file->nname, w->body.file->name);
-			return;
-		}
-	}
-	rowclose(et->col->row, et->col, TRUE);
-}
-
-void
-del(Text *et, Text*, Text *argt, int flag1, int, Rune *arg, int narg)
-{
-	Window *w;
-	char *name, *p;
-	Plumbmsg *pm;
-
-	if(et->col==nil || et->w == nil)
-		return;
-	if(flag1 || et->w->body.file->ntext>1 || winclean(et->w, FALSE)){
-		w = et->w;
-		name = getname(&w->body, argt, arg, narg, TRUE);
-		if(name && plumbsendfd >= 0){
-			pm = emalloc(sizeof(Plumbmsg));
-			pm->src = estrdup("acme");
-			pm->dst = estrdup("close");
-			pm->wdir = estrdup(name);
-			if(p = strrchr(pm->wdir, '/'))
-				*p = '\0';
-			pm->type = estrdup("text");
-			pm->attr = nil;
-			pm->data = estrdup(name);
-			pm->ndata = strlen(pm->data);
-			if(pm->ndata < messagesize-1024)
-				plumbsend(plumbsendfd, pm);
-			else
-				plumbfree(pm);
-		}
-		colclose(et->col, et->w, TRUE);
-	}
-}
-
-void
-sort(Text *et, Text*, Text*, int, int, Rune*, int)
-{
-	if(et->col)
-		colsort(et->col);
-}
-
-uint
-seqof(Window *w, int isundo)
-{
-	/* if it's undo, see who changed with us */
-	if(isundo)
-		return w->body.file->seq;
-	/* if it's redo, see who we'll be sync'ed up with */
-	return fileredoseq(w->body.file);
-}
-
-void
-undo(Text *et, Text*, Text*, int flag1, int, Rune*, int)
-{
-	int i, j;
-	Column *c;
-	Window *w;
-	uint seq;
-
-	if(et==nil || et->w== nil)
-		return;
-	seq = seqof(et->w, flag1);
-	if(seq == 0){
-		/* nothing to undo */
-		return;
-	}
-	/*
-	 * Undo the executing window first. Its display will update. other windows
-	 * in the same file will not call show() and jump to a different location in the file.
-	 * Simultaneous changes to other files will be chaotic, however.
-	 */
-	winundo(et->w, flag1);
-	for(i=0; i<row.ncol; i++){
-		c = row.col[i];
-		for(j=0; j<c->nw; j++){
-			w = c->w[j];
-			if(w == et->w)
-				continue;
-			if(seqof(w, flag1) == seq)
-				winundo(w, flag1);
-		}
-	}
-}
-
-char*
-getname(Text *t, Text *argt, Rune *arg, int narg, int isput)
-{
-	char *s;
-	Rune *r;
-	int i, n, promote;
-	Runestr dir;
-
-	getarg(argt, FALSE, TRUE, &r, &n);
-	promote = FALSE;
-	if(r == nil)
-		promote = TRUE;
-	else if(isput){
-		/* if are doing a Put, want to synthesize name even for non-existent file */
-		/* best guess is that file name doesn't contain a slash */
-		promote = TRUE;
-		for(i=0; i<n; i++)
-			if(r[i] == '/'){
-				promote = FALSE;
-				break;
-			}
-		if(promote){
-			t = argt;
-			arg = r;
-			narg = n;
-		}
-	}
-	if(promote){
-		n = narg;
-		if(n <= 0){
-			s = runetobyte(t->file->name, t->file->nname);
-			return s;
-		}
-		/* prefix with directory name if necessary */
-		dir.r = nil;
-		dir.nr = 0;
-		if(n>0 && arg[0]!='/'){
-			dir = dirname(t, nil, 0);
-			if(dir.nr==1 && dir.r[0]=='.'){	/* sigh */
-				free(dir.r);
-				dir.r = nil;
-				dir.nr = 0;
-			}
-		}
-		if(dir.r){
-			r = runemalloc(dir.nr+n+1);
-			runemove(r, dir.r, dir.nr);
-			free(dir.r);
-			if(dir.nr>0 && r[dir.nr]!='/' && n>0 && arg[0]!='/')
-				r[dir.nr++] = '/';
-			runemove(r+dir.nr, arg, n);
-			n += dir.nr;
-		}else{
-			r = runemalloc(n+1);
-			runemove(r, arg, n);
-		}
-	}
-	s = runetobyte(r, n);
-	free(r);
-	if(strlen(s) == 0){
-		free(s);
-		s = nil;
-	}
-	return s;
-}
-
-void
-zeroxx(Text *et, Text *t, Text*, int, int, Rune*, int)
-{
-	Window *nw;
-	int c, locked;
-
-	locked = FALSE;
-	if(t!=nil && t->w!=nil && t->w!=et->w){
-		locked = TRUE;
-		c = 'M';
-		if(et->w)
-			c = et->w->owner;
-		winlock(t->w, c);
-	}
-	if(t == nil)
-		t = et;
-	if(t==nil || t->w==nil)
-		return;
-	t = &t->w->body;
-	if(t->w->isdir)
-		warning(nil, "%.*S is a directory; Zerox illegal\n", t->file->nname, t->file->name);
-	else{
-		nw = coladd(t->w->col, nil, t->w, -1);
-		/* ugly: fix locks so w->unlock works */
-		winlock1(nw, t->w->owner);
-		xfidlog(nw, "zerox");
-	}
-	if(locked)
-		winunlock(t->w);
-}
-
-typedef struct TextAddr TextAddr;
-struct TextAddr {
-	long lorigin; // line+rune for origin
-	long rorigin;
-	long lq0; // line+rune for q0
-	long rq0;
-	long lq1; // line+rune for q1
-	long rq1;
-};
-
-void
-get(Text *et, Text *t, Text *argt, int flag1, int, Rune *arg, int narg)
-{
-	char *name;
-	Rune *r;
-	int i, n, dirty, samename, isdir;
-	TextAddr *addr, *a;
-	Window *w;
-	Text *u;
-	Dir *d;
-	long q0, q1;
-
-	if(flag1)
-		if(et==nil || et->w==nil)
-			return;
-	if(!et->w->isdir && (et->w->body.file->nc>0 && !winclean(et->w, TRUE)))
-		return;
-	w = et->w;
-	t = &w->body;
-	name = getname(t, argt, arg, narg, FALSE);
-	if(name == nil){
-		warning(nil, "no file name\n");
-		return;
-	}
-	if(t->file->ntext>1){
-		d = dirstat(name);
-		isdir = (d!=nil && (d->qid.type & QTDIR));
-		free(d);
-		if(isdir){
-			warning(nil, "%s is a directory; can't read with multiple windows on it\n", name);
-			return;
-		}
-	}
-	addr = emalloc((t->file->ntext)*sizeof(TextAddr));
-	for(i=0; i<t->file->ntext; i++) {
-		a = &addr[i];
-		u = t->file->text[i];
-		a->lorigin = nlcount(u, 0, u->org, &a->rorigin);
-		a->lq0 = nlcount(u, 0, u->q0, &a->rq0);
-		a->lq1 = nlcount(u, u->q0, u->q1, &a->rq1);
-	}
-	r = bytetorune(name, &n);
-	for(i=0; i<t->file->ntext; i++){
-		u = t->file->text[i];
-		/* second and subsequent calls with zero an already empty buffer, but OK */
-		textreset(u);
-		windirfree(u->w);
-	}
-	samename = runeeq(r, n, t->file->name, t->file->nname);
-	textload(t, 0, name, samename);
-	if(samename){
-		t->file->mod = FALSE;
-		dirty = FALSE;
-	}else{
-		t->file->mod = TRUE;
-		dirty = TRUE;
-	}
-	for(i=0; i<t->file->ntext; i++)
-		t->file->text[i]->w->dirty = dirty;
-	free(name);
-	free(r);
-	winsettag(w);
-	t->file->unread = FALSE;
-	for(i=0; i<t->file->ntext; i++){
-		u = t->file->text[i];
-		textsetselect(&u->w->tag, u->w->tag.file->nc, u->w->tag.file->nc);
-		if(samename) {
-			a = &addr[i];
-			// warning(nil, "%d %d %d %d %d %d\n", a->lorigin, a->rorigin, a->lq0, a->rq0, a->lq1, a->rq1);
-			q0 = nlcounttopos(u, 0, a->lq0, a->rq0);
-			q1 = nlcounttopos(u, q0, a->lq1, a->rq1);
-			textsetselect(u, q0, q1);
-			q0 = nlcounttopos(u, 0, a->lorigin, a->rorigin);
-			textsetorigin(u, q0, FALSE);
-		}
-		textscrdraw(u);
-	}
-	free(addr);
-	xfidlog(w, "get");
-}
-
-void
-putfile(File *f, int q0, int q1, Rune *namer, int nname)
-{
-	uint n, m;
-	Rune *r;
-	char *s, *name, *p;
-	int i, fd, q;
-	Dir *d, *d1;
-	Window *w;
-	Plumbmsg *pm;
-	int isapp;
-
-	w = f->curtext->w;
-	name = runetobyte(namer, nname);
-	d = dirstat(name);
-	if(d!=nil && runeeq(namer, nname, f->name, f->nname)){
-		/* f->mtime+1 because when talking over NFS it's often off by a second */
-		if(f->dev!=d->dev || f->qidpath!=d->qid.path || f->mtime+1<d->mtime){
-			f->dev = d->dev;
-			f->qidpath = d->qid.path;
-			f->mtime = d->mtime;
-			if(f->unread)
-				warning(nil, "%s not written; file already exists\n", name);
-			else
-				warning(nil, "%s modified%s%s since last read\n", name, d->muid[0]?" by ":"", d->muid);
-			goto Rescue1;
-		}
-	}
-	fd = create(name, OWRITE, 0666);
-	if(fd < 0){
-		warning(nil, "can't create file %s: %r\n", name);
-		goto Rescue1;
-	}
-	r = fbufalloc();
-	s = fbufalloc();
-	free(d);
-	d = dirfstat(fd);
-	isapp = (d!=nil && d->length>0 && (d->qid.type&QTAPPEND));
-	if(isapp){
-		warning(nil, "%s not written; file is append only\n", name);
-		goto Rescue2;
-	}
-
-	for(q=q0; q<q1; q+=n){
-		n = q1 - q;
-		if(n > (BUFSIZE-1)/UTFmax)
-			n = (BUFSIZE-1)/UTFmax;
-		bufread(f, q, r, n);
-		m = snprint(s, BUFSIZE, "%.*S", n, r);
-		if(write(fd, s, m) != m){
-			warning(nil, "can't write file %s: %r\n", name);
-			goto Rescue2;
-		}
-	}
-	if(runeeq(namer, nname, f->name, f->nname)){
-		if(q0!=0 || q1!=f->nc){
-			f->mod = TRUE;
-			w->dirty = TRUE;
-			f->unread = TRUE;
-		}else{
-			d1 = dirfstat(fd);
-			if(d1 != nil){
-				free(d);
-				d = d1;
-			}
-			f->qidpath = d->qid.path;
-			f->dev = d->dev;
-			f->mtime = d->mtime;
-			f->mod = FALSE;
-			w->dirty = FALSE;
-			f->unread = FALSE;
-		}
-		for(i=0; i<f->ntext; i++){
-			f->text[i]->w->putseq = f->seq;
-			f->text[i]->w->dirty = w->dirty;
-		}
-	}
-	if(plumbsendfd >= 0){
-		pm = emalloc(sizeof(Plumbmsg));
-		pm->src = estrdup("acme");
-		pm->dst = estrdup("put");
-		pm->wdir = estrdup(name);
-		if(p = strrchr(pm->wdir, '/'))
-			*p = '\0';
-		pm->type = estrdup("text");
-		pm->attr = nil;
-		pm->data = estrdup(name);
-		pm->ndata = strlen(pm->data);
-		if(pm->ndata < messagesize-1024)
-			plumbsend(plumbsendfd, pm);
-		else
-			plumbfree(pm);
-	}
-	fbuffree(s);
-	fbuffree(r);
-	free(d);
-	free(namer);
-	free(name);
-	close(fd);
-	winsettag(w);
-	return;
-
-    Rescue2:
-	fbuffree(s);
-	fbuffree(r);
-	close(fd);
-	/* fall through */
-
-    Rescue1:
-	free(d);
-	free(namer);
-	free(name);
-}
-
-void
-put(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg)
-{
-	int nname;
-	Rune  *namer;
-	Window *w;
-	File *f;
-	char *name;
-
-	if(et==nil || et->w==nil || et->w->isdir)
-		return;
-	w = et->w;
-	f = w->body.file;
-	name = getname(&w->body, argt, arg, narg, TRUE);
-	if(name == nil){
-		warning(nil, "no file name\n");
-		return;
-	}
-	namer = bytetorune(name, &nname);
-	putfile(f, 0, f->nc, namer, nname);
-	xfidlog(w, "put");
-	free(name);
-}
-
-void
-dump(Text *, Text *, Text *argt, int isdump, int, Rune *arg, int narg)
-{
-	char *name;
-
-	if(narg)
-		name = runetobyte(arg, narg);
-	else
-		getbytearg(argt, FALSE, TRUE, &name);
-	if(isdump)
-		rowdump(&row, name);
-	else
-		rowload(&row, name, FALSE);
-	free(name);
-}
-
-void
-cut(Text *et, Text *t, Text*, int dosnarf, int docut, Rune*, int)
-{
-	uint q0, q1, n, locked, c;
-	Rune *r;
-
-	/*
-	 * if not executing a mouse chord (et != t) and snarfing (dosnarf)
-	 * and executed Cut or Snarf in window tag (et->w != nil),
-	 * then use the window body selection or the tag selection
-	 * or do nothing at all.
-	 */
-	if(et!=t && dosnarf && et->w!=nil){
-		if(et->w->body.q1>et->w->body.q0){
-			t = &et->w->body;
-			if(docut)
-				filemark(t->file);	/* seq has been incremented by execute */
-		}else if(et->w->tag.q1>et->w->tag.q0)
-			t = &et->w->tag;
-		else
-			t = nil;
-	}
-	if(t == nil)	/* no selection */
-		return;
-
-	locked = FALSE;
-	if(t->w!=nil && et->w!=t->w){
-		locked = TRUE;
-		c = 'M';
-		if(et->w)
-			c = et->w->owner;
-		winlock(t->w, c);
-	}
-	if(t->q0 == t->q1){
-		if(locked)
-			winunlock(t->w);
-		return;
-	}
-	if(dosnarf){
-		q0 = t->q0;
-		q1 = t->q1;
-		bufdelete(&snarfbuf, 0, snarfbuf.nc);
-		r = fbufalloc();
-		while(q0 < q1){
-			n = q1 - q0;
-			if(n > RBUFSIZE)
-				n = RBUFSIZE;
-			bufread(t->file, q0, r, n);
-			bufinsert(&snarfbuf, snarfbuf.nc, r, n);
-			q0 += n;
-		}
-		fbuffree(r);
-		putsnarf();
-	}
-	if(docut){
-		textdelete(t, t->q0, t->q1, TRUE);
-		textsetselect(t, t->q0, t->q0);
-		if(t->w){
-			textscrdraw(t);
-			winsettag(t->w);
-		}
-	}else if(dosnarf)	/* Snarf command */
-		argtext = t;
-	if(locked)
-		winunlock(t->w);
-}
-
-void
-paste(Text *et, Text *t, Text*, int selectall, int tobody, Rune*, int)
-{
-	int c;
-	uint q, q0, q1, n;
-	Rune *r;
-
-	/* if(tobody), use body of executing window  (Paste or Send command) */
-	if(tobody && et!=nil && et->w!=nil){
-		t = &et->w->body;
-		filemark(t->file);	/* seq has been incremented by execute */
-	}
-	if(t == nil)
-		return;
-
-	getsnarf();
-	if(t==nil || snarfbuf.nc==0)
-		return;
-	if(t->w!=nil && et->w!=t->w){
-		c = 'M';
-		if(et->w)
-			c = et->w->owner;
-		winlock(t->w, c);
-	}
-	cut(t, t, nil, FALSE, TRUE, nil, 0);
-	q = 0;
-	q0 = t->q0;
-	q1 = t->q0+snarfbuf.nc;
-	r = fbufalloc();
-	while(q0 < q1){
-		n = q1 - q0;
-		if(n > RBUFSIZE)
-			n = RBUFSIZE;
-		if(r == nil)
-			r = runemalloc(n);
-		bufread(&snarfbuf, q, r, n);
-		textinsert(t, q0, r, n, TRUE);
-		q += n;
-		q0 += n;
-	}
-	fbuffree(r);
-	if(selectall)
-		textsetselect(t, t->q0, q1);
-	else
-		textsetselect(t, q1, q1);
-	if(t->w){
-		textscrdraw(t);
-		winsettag(t->w);
-	}
-	if(t->w!=nil && et->w!=t->w)
-		winunlock(t->w);
-}
-
-void
-look(Text *et, Text *t, Text *argt, int, int, Rune *arg, int narg)
-{
-	Rune *r;
-	int n;
-
-	if(et && et->w){
-		t = &et->w->body;
-		if(narg > 0){
-			search(t, arg, narg);
-			return;
-		}
-		getarg(argt, FALSE, FALSE, &r, &n);
-		if(r == nil){
-			n = t->q1-t->q0;
-			r = runemalloc(n);
-			bufread(t->file, t->q0, r, n);
-		}
-		search(t, r, n);
-		free(r);
-	}
-}
-
-void
-sendx(Text *et, Text *t, Text*, int, int, Rune*, int)
-{
-	if(et->w==nil)
-		return;
-	t = &et->w->body;
-	if(t->q0 != t->q1)
-		cut(t, t, nil, TRUE, FALSE, nil, 0);
-	textsetselect(t, t->file->nc, t->file->nc);
-	paste(t, t, nil, TRUE, TRUE, nil, 0);
-	if(textreadc(t, t->file->nc-1) != '\n'){
-		textinsert(t, t->file->nc, L"\n", 1, TRUE);
-		textsetselect(t, t->file->nc, t->file->nc);
-	}
-}
-
-void
-edit(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg)
-{
-	Rune *r;
-	int len;
-
-	if(et == nil)
-		return;
-	getarg(argt, FALSE, TRUE, &r, &len);
-	seq++;
-	if(r != nil){
-		editcmd(et, r, len);
-		free(r);
-	}else
-		editcmd(et, arg, narg);
-}
-
-void
-exit(Text*, Text*, Text*, int, int, Rune*, int)
-{
-	if(rowclean(&row)){
-		sendul(cexit, 0);
-		threadexits(nil);
-	}
-}
-
-void
-putall(Text*, Text*, Text*, int, int, Rune*, int)
-{
-	int i, j, e;
-	Window *w;
-	Column *c;
-	char *a;
-
-	for(i=0; i<row.ncol; i++){
-		c = row.col[i];
-		for(j=0; j<c->nw; j++){
-			w = c->w[j];
-			if(w->isscratch || w->isdir || w->body.file->nname==0)
-				continue;
-			if(w->nopen[QWevent] > 0)
-				continue;
-			a = runetobyte(w->body.file->name, w->body.file->nname);
-			e = access(a, 0);
-			if(w->body.file->mod || w->body.ncache)
-				if(e < 0)
-					warning(nil, "no auto-Put of %s: %r\n", a);
-				else{
-					wincommit(w, &w->body);
-					put(&w->body, nil, nil, XXX, XXX, nil, 0);
-				}
-			free(a);
-		}
-	}
-}
-
-
-void
-id(Text *et, Text*, Text*, int, int, Rune*, int)
-{
-	if(et && et->w)
-		warning(nil, "/mnt/acme/%d/\n", et->w->id);
-}
-
-void
-local(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg)
-{
-	char *a, *aa;
-	Runestr dir;
-
-	aa = getbytearg(argt, TRUE, TRUE, &a);
-
-	dir = dirname(et, nil, 0);
-	if(dir.nr==1 && dir.r[0]=='.'){	/* sigh */
-		free(dir.r);
-		dir.r = nil;
-		dir.nr = 0;
-	}
-	run(nil, runetobyte(arg, narg), dir.r, dir.nr, FALSE, aa, a, FALSE);
-}
-
-void
-kill(Text*, Text*, Text *argt, int, int, Rune *arg, int narg)
-{
-	Rune *a, *cmd, *r;
-	int na;
-
-	getarg(argt, FALSE, FALSE, &r, &na);
-	if(r)
-		kill(nil, nil, nil, 0, 0, r, na);
-	/* loop condition: *arg is not a blank */
-	for(;;){
-		a = findbl(arg, narg, &na);
-		if(a == arg)
-			break;
-		cmd = runemalloc(narg-na+1);
-		runemove(cmd, arg, narg-na);
-		sendp(ckill, cmd);
-		arg = skipbl(a, na, &narg);
-	}
-}
-
-void
-fontx(Text *et, Text *t, Text *argt, int, int, Rune *arg, int narg)
-{
-	Rune *a, *r, *flag, *file;
-	int na, nf;
-	char *aa;
-	Reffont *newfont;
-	Dirlist *dp;
-	int i, fix;
-
-	if(et==nil || et->w==nil)
-		return;
-	t = &et->w->body;
-	flag = nil;
-	file = nil;
-	/* loop condition: *arg is not a blank */
-	nf = 0;
-	for(;;){
-		a = findbl(arg, narg, &na);
-		if(a == arg)
-			break;
-		r = runemalloc(narg-na+1);
-		runemove(r, arg, narg-na);
-		if(runeeq(r, narg-na, L"fix", 3) || runeeq(r, narg-na, L"var", 3)){
-			free(flag);
-			flag = r;
-		}else{
-			free(file);
-			file = r;
-			nf = narg-na;
-		}
-		arg = skipbl(a, na, &narg);
-	}
-	getarg(argt, FALSE, TRUE, &r, &na);
-	if(r)
-		if(runeeq(r, na, L"fix", 3) || runeeq(r, na, L"var", 3)){
-			free(flag);
-			flag = r;
-		}else{
-			free(file);
-			file = r;
-			nf = na;
-		}
-	fix = 1;
-	if(flag)
-		fix = runeeq(flag, runestrlen(flag), L"fix", 3);
-	else if(file == nil){
-		newfont = rfget(FALSE, FALSE, FALSE, nil);
-		if(newfont)
-			fix = strcmp(newfont->f->name, t->font->name)==0;
-	}
-	if(file){
-		aa = runetobyte(file, nf);
-		newfont = rfget(fix, flag!=nil, FALSE, aa);
-		free(aa);
-	}else
-		newfont = rfget(fix, FALSE, FALSE, nil);
-	if(newfont){
-		draw(screen, t->w->r, textcols[BACK], nil, ZP);
-		rfclose(t->reffont);
-		t->reffont = newfont;
-		t->font = newfont->f;
-		frinittick(t);
-		if(t->w->isdir){
-			t->all.min.x++;	/* force recolumnation; disgusting! */
-			for(i=0; i<t->w->ndl; i++){
-				dp = t->w->dlp[i];
-				aa = runetobyte(dp->r, dp->nr);
-				dp->wid = stringwidth(newfont->f, aa);
-				free(aa);
-			}
-		}
-		/* avoid shrinking of window due to quantization */
-		colgrow(t->w->col, t->w, -1);
-	}
-	free(file);
-	free(flag);
-}
-
-void
-incl(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg)
-{
-	Rune *a, *r;
-	Window *w;
-	int na, n, len;
-
-	if(et==nil || et->w==nil)
-		return;
-	w = et->w;
-	n = 0;
-	getarg(argt, FALSE, TRUE, &r, &len);
-	if(r){
-		n++;
-		winaddincl(w, r, len);
-	}
-	/* loop condition: *arg is not a blank */
-	for(;;){
-		a = findbl(arg, narg, &na);
-		if(a == arg)
-			break;
-		r = runemalloc(narg-na+1);
-		runemove(r, arg, narg-na);
-		n++;
-		winaddincl(w, r, narg-na);
-		arg = skipbl(a, na, &narg);
-	}
-	if(n==0 && w->nincl){
-		for(n=w->nincl; --n>=0; )
-			warning(nil, "%S ", w->incl[n]);
-		warning(nil, "\n");
-	}
-}
-
-enum {
-	IGlobal = -2,
-	IError = -1,
-};
-
-static int
-indentval(Rune *s, int n, int type)
-{
-	static char *strs[] = {
-		[SPACESINDENT] "Spaces",
-		[AUTOINDENT] "Indent",
-	};
-
-	if(n < 2)
-		return IError;
-	if(runestrncmp(s, L"ON", n) == 0){
-		globalindent[type] = TRUE;
-		warning(nil, "%s ON\n", strs[type]);
-		return IGlobal;
-	}
-	if(runestrncmp(s, L"OFF", n) == 0){
-		globalindent[type] = FALSE;
-		warning(nil, "%s OFF\n", strs[type]);
-		return IGlobal;
-	}
-	if(runestrncmp(s, L"on", n) == 0)
-		return TRUE;
-	if(runestrncmp(s, L"off", n) == 0)
-		return FALSE;
-	return IError;
-}
-
-static void
-fixindent(Window *w, void *v)
-{
-	int t = *(int*)v;
-	w->indent[t] = globalindent[t];
-}
-
-void
-indent(Text *et, Text*, Text *argt, int type, int, Rune *arg, int narg)
-{
-	Rune *a, *r;
-	Window *w;
-	int na, len, ival;
-
-	w = nil;
-	if(et!=nil && et->w!=nil)
-		w = et->w;
-	ival = IError;
-	getarg(argt, FALSE, TRUE, &r, &len);
-	if(r!=nil && len>0)
-		ival = indentval(r, len, type);
-	else{
-		a = findbl(arg, narg, &na);
-		if(a != arg)
-			ival = indentval(arg, narg-na, type);
-	}
-	if(ival == IGlobal)
-		allwindows(fixindent, &type);
-	else if(w != nil && ival >= 0)
-		w->indent[type] = ival;
-}
-
-void
-tab(Text *et, Text*, Text *argt, int, int, Rune *arg, int narg)
-{
-	Rune *a, *r;
-	Window *w;
-	int na, len, tab;
-	char *p;
-
-	if(et==nil || et->w==nil)
-		return;
-	w = et->w;
-	getarg(argt, FALSE, TRUE, &r, &len);
-	tab = 0;
-	if(r!=nil && len>0){
-		p = runetobyte(r, len);
-		if('0'<=p[0] && p[0]<='9')
-			tab = atoi(p);
-		free(p);
-	}else{
-		a = findbl(arg, narg, &na);
-		if(a != arg){
-			p = runetobyte(arg, narg-na);
-			if('0'<=p[0] && p[0]<='9')
-				tab = atoi(p);
-			free(p);
-		}
-	}
-	if(tab > 0){
-		if(w->body.tabstop != tab){
-			w->body.tabstop = tab;
-			winresize(w, w->r, 1);
-		}
-	}else
-		warning(nil, "%.*S: Tab %d\n", w->body.file->nname, w->body.file->name, w->body.tabstop);
-}
-
-void
-runproc(void *argvp)
-{
-	/* args: */
-		Window *win;
-		char *s;
-		Rune *rdir;
-		int ndir;
-		int newns;
-		char *argaddr;
-		char *arg;
-		Command *c;
-		Channel *cpid;
-		int iseditcmd;
-	/* end of args */
-	char *e, *t, *name, *filename, *dir, **av, *news;
-	Rune r, **incl;
-	int ac, w, inarg, i, n, fd, nincl, winid;
-	int pipechar;
-	char buf[512];
-	void **argv;
-
-	argv = argvp;
-	win = argv[0];
-	s = argv[1];
-	rdir = argv[2];
-	ndir = (uintptr)argv[3];
-	newns = (uintptr)argv[4];
-	argaddr = argv[5];
-	arg = argv[6];
-	c = argv[7];
-	cpid = argv[8];
-	iseditcmd = (uintptr)argv[9];
-	free(argv);
-
-	t = s;
-	while(*t==' ' || *t=='\n' || *t=='\t')
-		t++;
-	for(e=t; *e; e++)
-		if(*e==' ' || *e=='\n' || *e=='\t' )
-			break;
-	name = emalloc((e-t)+2);
-	memmove(name, t, e-t);
-	name[e-t] = 0;
-	e = utfrrune(name, '/');
-	if(e)
-		memmove(name, e+1, strlen(e+1)+1);	/* strcpy but overlaps */
-	strcat(name, " ");	/* add blank here for ease in waittask */
-	c->name = bytetorune(name, &c->nname);
-	free(name);
-	pipechar = 0;
-	if(*t=='<' || *t=='|' || *t=='>')
-		pipechar = *t++;
-	c->iseditcmd = iseditcmd;
-	c->text = s;
-	if(rdir != nil){
-		dir = runetobyte(rdir, ndir);
-		chdir(dir);	/* ignore error: probably app. window */
-		free(dir);
-	}
-	if(newns){
-		nincl = 0;
-		incl = nil;
-		if(win){
-			filename = smprint("%.*S", win->body.file->nname, win->body.file->name);
-			nincl = win->nincl;
-			if(nincl > 0){
-				incl = emalloc(nincl*sizeof(Rune*));
-				for(i=0; i<nincl; i++){
-					n = runestrlen(win->incl[i]);
-					incl[i] = runemalloc(n+1);
-					runemove(incl[i], win->incl[i], n);
-				}
-			}
-			winid = win->id;
-		}else{
-			filename = nil;
-			winid = 0;
-			if(activewin)
-				winid = activewin->id;
-		}
-		rfork(RFNAMEG|RFENVG|RFFDG|RFNOTEG);
-		sprint(buf, "%d", winid);
-		putenv("winid", buf);
-
-		if(filename){
-			putenv("%", filename);
-			free(filename);
-		}
-		c->md = fsysmount(rdir, ndir, incl, nincl);
-		if(c->md == nil){
-			fprint(2, "child: can't mount /dev/cons: %r\n");
-			threadexits("mount");
-		}
-		close(0);
-		if(winid>0 && (pipechar=='|' || pipechar=='>')){
-			sprint(buf, "/mnt/acme/%d/rdsel", winid);
-			open(buf, OREAD);
-		}else
-			open("/dev/null", OREAD);
-		close(1);
-		if((winid>0 || iseditcmd) && (pipechar=='|' || pipechar=='<')){
-			if(iseditcmd){
-				if(winid > 0)
-					sprint(buf, "/mnt/acme/%d/editout", winid);
-				else
-					sprint(buf, "/mnt/acme/editout");
-			}else
-				sprint(buf, "/mnt/acme/%d/wrsel", winid);
-			open(buf, OWRITE);
-			close(2);
-			open("/dev/cons", OWRITE);
-		}else{
-			open("/dev/cons", OWRITE);
-			dup(1, 2);
-		}
-	}else{
-		rfork(RFFDG|RFNOTEG);
-		fsysclose();
-		close(0);
-		open("/dev/null", OREAD);
-		close(1);
-		open(acmeerrorfile, OWRITE);
-		dup(1, 2);
-	}
-
-	if(win)
-		winclose(win);
-
-	if(argaddr)
-		putenv("acmeaddr", argaddr);
-	if(strlen(t) > sizeof buf-10)	/* may need to print into stack */
-		goto Hard;
-	inarg = FALSE;
-	for(e=t; *e; e+=w){
-		w = chartorune(&r, e);
-		if(r==' ' || r=='\t')
-			continue;
-		if(r < ' ')
-			goto Hard;
-		if(utfrune("#;&|^$=`'{}()<>[]*?^~`", r))
-			goto Hard;
-		inarg = TRUE;
-	}
-	if(!inarg)
-		goto Fail;
-
-	ac = 0;
-	av = nil;
-	inarg = FALSE;
-	for(e=t; *e; e+=w){
-		w = chartorune(&r, e);
-		if(r==' ' || r=='\t'){
-			inarg = FALSE;
-			*e = 0;
-			continue;
-		}
-		if(!inarg){
-			inarg = TRUE;
-			av = realloc(av, (ac+1)*sizeof(char**));
-			av[ac++] = e;
-		}
-	}
-	av = realloc(av, (ac+2)*sizeof(char**));
-	av[ac++] = arg;
-	av[ac] = nil;
-	c->av = av;
-	procexec(cpid, av[0], av);
-	e = av[0];
-	if(e[0]=='/' || (e[0]=='.' && e[1]=='/'))
-		goto Fail;
-	if(cputype){
-		sprint(buf, "%s/%s", cputype, av[0]);
-		procexec(cpid, buf, av);
-	}
-	sprint(buf, "/bin/%s", av[0]);
-	procexec(cpid, buf, av);
-	goto Fail;
-
-Hard:
-
-	/*
-	 * ugly: set path = (. $cputype /bin)
-	 * should honor $path if unusual.
-	 */
-	if(cputype){
-		n = 0;
-		memmove(buf+n, ".", 2);
-		n += 2;
-		i = strlen(cputype)+1;
-		memmove(buf+n, cputype, i);
-		n += i;
-		memmove(buf+n, "/bin", 5);
-		n += 5;
-		fd = create("/env/path", OWRITE, 0666);
-		write(fd, buf, n);
-		close(fd);
-	}
-
-	if(arg){
-		news = emalloc(strlen(t) + 1 + 1 + strlen(arg) + 1 + 1);
-		if(news){
-			sprint(news, "%s '%s'", t, arg);	/* BUG: what if quote in arg? */
-			free(s);
-			t = news;
-			c->text = news;
-		}
-	}
-	procexecl(cpid, "/bin/rc", "rc", "-c", t, nil);
-
-   Fail:
-	/* procexec hasn't happened, so send a zero */
-	sendul(cpid, 0);
-	threadexits(nil);
-}
-
-void
-runwaittask(void *v)
-{
-	Command *c;
-	Channel *cpid;
-	void **a;
-
-	threadsetname("runwaittask");
-	a = v;
-	c = a[0];
-	cpid = a[1];
-	free(a);
-	do
-		c->pid = recvul(cpid);
-	while(c->pid == ~0);
-	free(c->av);
-	if(c->pid != 0)	/* successful exec */
-		sendp(ccommand, c);
-	else{
-		if(c->iseditcmd)
-			sendul(cedit, 0);
-		free(c->name);
-		free(c->text);
-		free(c);
-	}
-	chanfree(cpid);
-}
-
-void
-run(Window *win, char *s, Rune *rdir, int ndir, int newns, char *argaddr, char *xarg, int iseditcmd)
-{
-	void **arg;
-	Command *c;
-	Channel *cpid;
-
-	if(s == nil)
-		return;
-
-	arg = emalloc(10*sizeof(void*));
-	c = emalloc(sizeof *c);
-	cpid = chancreate(sizeof(ulong), 0);
-	arg[0] = win;
-	arg[1] = s;
-	arg[2] = rdir;
-	arg[3] = (void*)ndir;
-	arg[4] = (void*)newns;
-	arg[5] = argaddr;
-	arg[6] = xarg;
-	arg[7] = c;
-	arg[8] = cpid;
-	arg[9] = (void*)iseditcmd;
-	proccreate(runproc, arg, STACK);
-	/* mustn't block here because must be ready to answer mount() call in run() */
-	arg = emalloc(2*sizeof(void*));
-	arg[0] = c;
-	arg[1] = cpid;
-	threadcreate(runwaittask, arg, STACK);
-}
--- a/file.c
+++ /dev/null
@@ -1,310 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <thread.h>
-#include <cursor.h>
-#include <mouse.h>
-#include <keyboard.h>
-#include <frame.h>
-#include <fcall.h>
-#include <plumb.h>
-#include "dat.h"
-#include "fns.h"
-
-/*
- * Structure of Undo list:
- * 	The Undo structure follows any associated data, so the list
- *	can be read backwards: read the structure, then read whatever
- *	data is associated (insert string, file name) and precedes it.
- *	The structure includes the previous value of the modify bit
- *	and a sequence number; successive Undo structures with the
- *	same sequence number represent simultaneous changes.
- */
-
-typedef struct Undo Undo;
-struct Undo
-{
-	short	type;		/* Delete, Insert, Filename */
-	short	mod;	/* modify bit */
-	uint		seq;		/* sequence number */
-	uint		p0;		/* location of change (unused in f) */
-	uint		n;		/* # runes in string or file name */
-};
-
-enum
-{
-	Undosize = sizeof(Undo)/sizeof(Rune),
-};
-
-File*
-fileaddtext(File *f, Text *t)
-{
-	if(f == nil){
-		f = emalloc(sizeof(File));
-		f->unread = TRUE;
-	}
-	f->text = realloc(f->text, (f->ntext+1)*sizeof(Text*));
-	f->text[f->ntext++] = t;
-	f->curtext = t;
-	return f;
-}
-
-void
-filedeltext(File *f, Text *t)
-{
-	int i;
-
-	for(i=0; i<f->ntext; i++)
-		if(f->text[i] == t)
-			goto Found;
-	error("can't find text in filedeltext");
-
-    Found:
-	f->ntext--;
-	if(f->ntext == 0){
-		fileclose(f);
-		return;
-	}
-	memmove(f->text+i, f->text+i+1, (f->ntext-i)*sizeof(Text*));
-	if(f->curtext == t)
-		f->curtext = f->text[0];
-}
-
-void
-fileinsert(File *f, uint p0, Rune *s, uint ns)
-{
-	if(p0 > f->nc)
-		error("internal error: fileinsert");
-	if(f->seq > 0)
-		fileuninsert(f, &f->delta, p0, ns);
-	bufinsert(f, p0, s, ns);
-	if(ns)
-		f->mod = TRUE;
-}
-
-void
-fileuninsert(File *f, Buffer *delta, uint p0, uint ns)
-{
-	Undo u;
-
-	/* undo an insertion by deleting */
-	u.type = Delete;
-	u.mod = f->mod;
-	u.seq = f->seq;
-	u.p0 = p0;
-	u.n = ns;
-	bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
-}
-
-void
-filedelete(File *f, uint p0, uint p1)
-{
-	if(!(p0<=p1 && p0<=f->nc && p1<=f->nc))
-		error("internal error: filedelete");
-	if(f->seq > 0)
-		fileundelete(f, &f->delta, p0, p1);
-	bufdelete(f, p0, p1);
-	if(p1 > p0)
-		f->mod = TRUE;
-}
-
-void
-fileundelete(File *f, Buffer *delta, uint p0, uint p1)
-{
-	Undo u;
-	Rune *buf;
-	uint i, n;
-
-	/* undo a deletion by inserting */
-	u.type = Insert;
-	u.mod = f->mod;
-	u.seq = f->seq;
-	u.p0 = p0;
-	u.n = p1-p0;
-	buf = fbufalloc();
-	for(i=p0; i<p1; i+=n){
-		n = p1 - i;
-		if(n > RBUFSIZE)
-			n = RBUFSIZE;
-		bufread(f, i, buf, n);
-		bufinsert(delta, delta->nc, buf, n);
-	}
-	fbuffree(buf);
-	bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
-
-}
-
-void
-filesetname(File *f, Rune *name, int n)
-{
-	if(f->seq > 0)
-		fileunsetname(f, &f->delta);
-	free(f->name);
-	f->name = runemalloc(n);
-	runemove(f->name, name, n);
-	f->nname = n;
-	f->unread = TRUE;
-}
-
-void
-fileunsetname(File *f, Buffer *delta)
-{
-	Undo u;
-
-	/* undo a file name change by restoring old name */
-	u.type = Filename;
-	u.mod = f->mod;
-	u.seq = f->seq;
-	u.p0 = 0;	/* unused */
-	u.n = f->nname;
-	if(f->nname)
-		bufinsert(delta, delta->nc, f->name, f->nname);
-	bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
-}
-
-uint
-fileload(File *f, uint p0, int fd, int *nulls)
-{
-	if(f->seq > 0)
-		error("undo in file.load unimplemented");
-	return bufload(f, p0, fd, nulls);
-}
-
-/* return sequence number of pending redo */
-uint
-fileredoseq(File *f)
-{
-	Undo u;
-	Buffer *delta;
-
-	delta = &f->epsilon;
-	if(delta->nc == 0)
-		return 0;
-	bufread(delta, delta->nc-Undosize, (Rune*)&u, Undosize);
-	return u.seq;
-}
-
-void
-fileundo(File *f, int isundo, uint *q0p, uint *q1p)
-{
-	Undo u;
-	Rune *buf;
-	uint i, j, n, up;
-	uint stop;
-	Buffer *delta, *epsilon;
-
-	if(isundo){
-		/* undo; reverse delta onto epsilon, seq decreases */
-		delta = &f->delta;
-		epsilon = &f->epsilon;
-		stop = f->seq;
-	}else{
-		/* redo; reverse epsilon onto delta, seq increases */
-		delta = &f->epsilon;
-		epsilon = &f->delta;
-		stop = 0;	/* don't know yet */
-	}
-
-	buf = fbufalloc();
-	while(delta->nc > 0){
-		up = delta->nc-Undosize;
-		bufread(delta, up, (Rune*)&u, Undosize);
-		if(isundo){
-			if(u.seq < stop){
-				f->seq = u.seq;
-				goto Return;
-			}
-		}else{
-			if(stop == 0)
-				stop = u.seq;
-			if(u.seq > stop)
-				goto Return;
-		}
-		switch(u.type){
-		default:
-			fprint(2, "undo: 0x%ux\n", u.type);
-			abort();
-			break;
-
-		case Delete:
-			f->seq = u.seq;
-			fileundelete(f, epsilon, u.p0, u.p0+u.n);
-			f->mod = u.mod;
-			bufdelete(f, u.p0, u.p0+u.n);
-			for(j=0; j<f->ntext; j++)
-				textdelete(f->text[j], u.p0, u.p0+u.n, FALSE);
-			*q0p = u.p0;
-			*q1p = u.p0;
-			break;
-
-		case Insert:
-			f->seq = u.seq;
-			fileuninsert(f, epsilon, u.p0, u.n);
-			f->mod = u.mod;
-			up -= u.n;
-			for(i=0; i<u.n; i+=n){
-				n = u.n - i;
-				if(n > RBUFSIZE)
-					n = RBUFSIZE;
-				bufread(delta, up+i, buf, n);
-				bufinsert(f, u.p0+i, buf, n);
-				for(j=0; j<f->ntext; j++)
-					textinsert(f->text[j], u.p0+i, buf, n, FALSE);
-			}
-			*q0p = u.p0;
-			*q1p = u.p0+u.n;
-			break;
-
-		case Filename:
-			f->seq = u.seq;
-			fileunsetname(f, epsilon);
-			f->mod = u.mod;
-			up -= u.n;
-			free(f->name);
-			if(u.n == 0)
-				f->name = nil;
-			else
-				f->name = runemalloc(u.n);
-			bufread(delta, up, f->name, u.n);
-			f->nname = u.n;
-			break;
-		}
-		bufdelete(delta, up, delta->nc);
-	}
-	if(isundo)
-		f->seq = 0;
-    Return:
-	fbuffree(buf);
-}
-
-void
-filereset(File *f)
-{
-	bufreset(&f->delta);
-	bufreset(&f->epsilon);
-	f->seq = 0;
-}
-
-void
-fileclose(File *f)
-{
-	free(f->name);
-	f->nname = 0;
-	f->name = nil;
-	free(f->text);
-	f->ntext = 0;
-	f->text = nil;
-	bufclose(f);
-	bufclose(&f->delta);
-	bufclose(&f->epsilon);
-	elogclose(f);
-	free(f);
-}
-
-void
-filemark(File *f)
-{
-	if(f->epsilon.nc)
-		bufdelete(&f->epsilon, 0, f->epsilon.nc);
-	f->seq = seq;
-}
--- a/fns.h
+++ /dev/null
@@ -1,96 +1,0 @@
-#pragma	varargck	argpos	warning	2
-
-void	warning(Mntdir*, char*, ...);
-
-#define	fbufalloc()	emalloc(BUFSIZE)
-#define	fbuffree(x)	free(x)
-
-void	plumblook(Plumbmsg*m);
-void	plumbshow(Plumbmsg*m);
-void	putsnarf(void);
-void	getsnarf(void);
-int	tempfile(void);
-void	scrlresize(void);
-Font*	getfont(int, int, char*);
-char*	getarg(Text*, int, int, Rune**, int*);
-char*	getbytearg(Text*, int, int, char**);
-void	new(Text*, Text*, Text*, int, int, Rune*, int);
-void	undo(Text*, Text*, Text*, int, int, Rune*, int);
-char*	getname(Text*, Text*, Rune*, int, int);
-void	scrsleep(uint);
-void	savemouse(Window*);
-int	restoremouse(Window*);
-void	clearmouse(void);
-void	allwindows(void(*)(Window*, void*), void*);
-uint loadfile(int, uint, int*, int(*)(void*, uint, Rune*, int), void*);
-void	movetodel(Window*);
-
-Window*	errorwin(Mntdir*, int);
-Window*	errorwinforwin(Window*);
-Runestr cleanrname(Runestr);
-void	run(Window*, char*, Rune*, int, int, char*, char*, int);
-void fsysclose(void);
-void	setcurtext(Text*, int);
-int	isfilec(Rune);
-void	rxinit(void);
-int rxnull(void);
-Runestr	dirname(Text*, Rune*, int);
-void	error(char*);
-void	cvttorunes(char*, int, Rune*, int*, int*, int*);
-void*	tmalloc(uint);
-void	tfree(void);
-void	killprocs(void);
-void	killtasks(void);
-int	runeeq(Rune*, uint, Rune*, uint);
-int	ALEF_tid(void);
-void	iconinit(void);
-Timer*	timerstart(int);
-void	timerstop(Timer*);
-void	timercancel(Timer*);
-void	timerinit(void);
-void	cut(Text*, Text*, Text*, int, int, Rune*, int);
-void	paste(Text*, Text*, Text*, int, int, Rune*, int);
-void	get(Text*, Text*, Text*, int, int, Rune*, int);
-void	put(Text*, Text*, Text*, int, int, Rune*, int);
-void	putfile(File*, int, int, Rune*, int);
-void	fontx(Text*, Text*, Text*, int, int, Rune*, int);
-int	isspace(Rune);
-int	isalnum(Rune);
-void	execute(Text*, uint, uint, int, Text*);
-int	search(Text*, Rune*, uint);
-void	look3(Text*, uint, uint, int);
-void	editcmd(Text*, Rune*, uint);
-uint	min(uint, uint);
-uint	max(uint, uint);
-Window*	lookfile(Rune*, int);
-Window*	lookid(int, int);
-char*	runetobyte(Rune*, int);
-Rune*	bytetorune(char*, int*);
-void	fsysinit(void);
-Mntdir*	fsysmount(Rune*, int, Rune**, int);
-void		fsysincid(Mntdir*);
-void		fsysdelid(Mntdir*);
-Xfid*		respond(Xfid*, Fcall*, char*);
-int		rxcompile(Rune*);
-int		rgetc(void*, uint);
-int		tgetc(void*, uint);
-int		isaddrc(int);
-int		isregexc(int);
-void *emalloc(uint);
-void *erealloc(void*, uint);
-char	*estrdup(char*);
-Range		address(Mntdir*, Text*, Range, Range, void*, uint, uint, int (*)(void*, uint),  int*, uint*);
-int		rxexecute(Text*, Rune*, uint, uint, Rangeset*);
-int		rxbexecute(Text*, uint, Rangeset*);
-Window*	makenewwindow(Text *t);
-int	expand(Text*, uint, uint, Expand*);
-Rune*	skipbl(Rune*, int, int*);
-Rune*	findbl(Rune*, int, int*);
-char*	edittext(Window*, int, Rune*, int);
-void		flushwarnings(void);
-long	nlcount(Text*, long, long, long*);
-long	nlcounttopos(Text*, long, long, long);
-
-#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))
--- a/fsys.c
+++ /dev/null
@@ -1,749 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <thread.h>
-#include <cursor.h>
-#include <mouse.h>
-#include <keyboard.h>
-#include <frame.h>
-#include <fcall.h>
-#include <plumb.h>
-#include "dat.h"
-#include "fns.h"
-
-static	int	cfd;
-static	int	sfd;
-
-enum
-{
-	Nhash	= 16,
-	DEBUG	= 0
-};
-
-static	Fid	*fids[Nhash];
-
-Fid	*newfid(int);
-
-static	Xfid*	fsysflush(Xfid*, Fid*);
-static	Xfid*	fsysauth(Xfid*, Fid*);
-static	Xfid*	fsysversion(Xfid*, Fid*);
-static	Xfid*	fsysattach(Xfid*, Fid*);
-static	Xfid*	fsyswalk(Xfid*, Fid*);
-static	Xfid*	fsysopen(Xfid*, Fid*);
-static	Xfid*	fsyscreate(Xfid*, Fid*);
-static	Xfid*	fsysread(Xfid*, Fid*);
-static	Xfid*	fsyswrite(Xfid*, Fid*);
-static	Xfid*	fsysclunk(Xfid*, Fid*);
-static	Xfid*	fsysremove(Xfid*, Fid*);
-static	Xfid*	fsysstat(Xfid*, Fid*);
-static	Xfid*	fsyswstat(Xfid*, Fid*);
-
-Xfid* 	(*fcall[Tmax])(Xfid*, Fid*) =
-{
-	[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,
-};
-
-char Eperm[] = "permission denied";
-char Eexist[] = "file does not exist";
-char Enotdir[] = "not a directory";
-
-Dirtab dirtab[]=
-{
-	{ ".",			QTDIR,	Qdir,		0500|DMDIR },
-	{ "acme",		QTDIR,	Qacme,	0500|DMDIR },
-	{ "cons",		QTFILE,	Qcons,	0600 },
-	{ "consctl",	QTFILE,	Qconsctl,	0000 },
-	{ "draw",		QTDIR,	Qdraw,	0000|DMDIR },	/* to suppress graphics progs started in acme */
-	{ "editout",	QTFILE,	Qeditout,	0200 },
-	{ "index",		QTFILE,	Qindex,	0400 },
-	{ "label",		QTFILE,	Qlabel,	0600 },
-	{ "log",		QTFILE,	Qlog,	0400 },
-	{ "new",		QTDIR,	Qnew,	0500|DMDIR },
-	{ nil, }
-};
-
-Dirtab dirtabw[]=
-{
-	{ ".",			QTDIR,		Qdir,			0500|DMDIR },
-	{ "addr",		QTFILE,		QWaddr,		0600 },
-	{ "body",		QTAPPEND,	QWbody,		0600|DMAPPEND },
-	{ "ctl",		QTFILE,		QWctl,		0600 },
-	{ "data",		QTFILE,		QWdata,		0600 },
-	{ "editout",	QTFILE,		QWeditout,	0200 },
-	{ "errors",		QTFILE,		QWerrors,		0200 },
-	{ "event",		QTFILE,		QWevent,		0600 },
-	{ "rdsel",		QTFILE,		QWrdsel,		0400 },
-	{ "wrsel",		QTFILE,		QWwrsel,		0200 },
-	{ "tag",		QTAPPEND,	QWtag,		0600|DMAPPEND },
-	{ "xdata",		QTFILE,		QWxdata,		0600 },
-	{ nil, }
-};
-
-typedef struct Mnt Mnt;
-struct Mnt
-{
-	QLock;
-	int		id;
-	Mntdir	*md;
-};
-
-Mnt	mnt;
-
-Xfid*	respond(Xfid*, Fcall*, char*);
-int		dostat(int, Dirtab*, uchar*, int, uint);
-uint	getclock(void);
-
-char	*user = "Wile E. Coyote";
-int	clockfd;
-static int closing = 0;
-int	messagesize = Maxblock+IOHDRSZ;	/* good start */
-
-void	fsysproc(void *);
-
-void
-fsysinit(void)
-{
-	int p[2];
-
-	if(pipe(p) < 0)
-		error("can't create pipe");
-	cfd = p[0];
-	sfd = p[1];
-	fmtinstall('F', fcallfmt);
-	clockfd = open("/dev/time", OREAD|OCEXEC);
-	user = getuser();
-	proccreate(fsysproc, nil, STACK);
-}
-
-void
-fsysproc(void *)
-{
-	int n;
-	Xfid *x;
-	Fid *f;
-	Fcall t;
-	uchar *buf;
-
-	x = nil;
-	for(;;){
-		buf = emalloc(messagesize+UTFmax);	/* overflow for appending partial rune in xfidwrite */
-		n = read9pmsg(sfd, buf, messagesize);
-		if(n <= 0){
-			if(closing)
-				break;
-			error("i/o error on server channel");
-		}
-		if(x == nil){
-			sendp(cxfidalloc, nil);
-			x = recvp(cxfidalloc);
-		}
-		x->buf = buf;
-		if(convM2S(buf, n, x) != n)
-			error("convert error in convM2S");
-		if(DEBUG)
-			fprint(2, "%F\n", &x->Fcall);
-		if(fcall[x->type] == nil)
-			x = respond(x, &t, "bad fcall type");
-		else{
-			switch(x->type){
-			case Tversion:
-			case Tauth:
-			case Tflush:
-				f = nil;
-				break;
-			case Tattach:
-				f = newfid(x->fid);
-				break;
-			default:
-				f = newfid(x->fid);
-				if(!f->busy){
-					x->f = f;
-					x = respond(x, &t, "fid not in use");
-					continue;
-				}
-				break;
-			}
-			x->f = f;
-			x  = (*fcall[x->type])(x, f);
-		}
-	}
-}
-
-Mntdir*
-fsysaddid(Rune *dir, int ndir, Rune **incl, int nincl)
-{
-	Mntdir *m;
-	int id;
-
-	qlock(&mnt);
-	id = ++mnt.id;
-	m = emalloc(sizeof *m);
-	m->id = id;
-	m->dir =  dir;
-	m->ref = 1;	/* one for Command, one will be incremented in attach */
-	m->ndir = ndir;
-	m->next = mnt.md;
-	m->incl = incl;
-	m->nincl = nincl;
-	mnt.md = m;
-	qunlock(&mnt);
-	return m;
-}
-
-void
-fsysincid(Mntdir *m)
-{
-	qlock(&mnt);
-	m->ref++;
-	qunlock(&mnt);
-}
-
-void
-fsysdelid(Mntdir *idm)
-{
-	Mntdir *m, *prev;
-	int i;
-	char buf[64];
-
-	if(idm == nil)
-		return;
-	qlock(&mnt);
-	if(--idm->ref > 0){
-		qunlock(&mnt);
-		return;
-	}
-	prev = nil;
-	for(m=mnt.md; m; m=m->next){
-		if(m == idm){
-			if(prev)
-				prev->next = m->next;
-			else
-				mnt.md = m->next;
-			for(i=0; i<m->nincl; i++)
-				free(m->incl[i]);
-			free(m->incl);
-			free(m->dir);
-			free(m);
-			qunlock(&mnt);
-			return;
-		}
-		prev = m;
-	}
-	qunlock(&mnt);
-	sprint(buf, "fsysdelid: can't find id %d\n", idm->id);
-	sendp(cerr, estrdup(buf));
-}
-
-/*
- * Called only in exec.c:/^run(), from a different FD group
- */
-Mntdir*
-fsysmount(Rune *dir, int ndir, Rune **incl, int nincl)
-{
-	char buf[256];
-	Mntdir *m;
-
-	/* close server side so don't hang if acme is half-exited */
-	close(sfd);
-	m = fsysaddid(dir, ndir, incl, nincl);
-	sprint(buf, "%d", m->id);
-	if(mount(cfd, -1, "/mnt/acme", MREPL, buf) == -1){
-		fsysdelid(m);
-		return nil;
-	}
-	bind("/mnt/acme", "/mnt/wsys", MREPL);
-	if(bind("/mnt/acme", "/dev", MBEFORE) == -1){
-		fsysdelid(m);
-		return nil;
-	}
-	return m;
-}
-
-void
-fsysclose(void)
-{
-	closing = 1;
-	close(cfd);
-	close(sfd);
-}
-
-Xfid*
-respond(Xfid *x, Fcall *t, char *err)
-{
-	int n;
-
-	if(err){
-		t->type = Rerror;
-		t->ename = err;
-	}else
-		t->type = x->type+1;
-	t->fid = x->fid;
-	t->tag = x->tag;
-	if(x->buf == nil)
-		x->buf = emalloc(messagesize);
-	n = convS2M(t, x->buf, messagesize);
-	if(n <= 0)
-		error("convert error in convS2M");
-	if(write(sfd, x->buf, n) != n)
-		error("write error in respond");
-	free(x->buf);
-	x->buf = nil;
-	if(DEBUG)
-		fprint(2, "r: %F\n", t);
-	return x;
-}
-
-static
-Xfid*
-fsysversion(Xfid *x, Fid*)
-{
-	Fcall t;
-
-	if(x->msize < 256)
-		return respond(x, &t, "version: message size too small");
-	messagesize = x->msize;
-	t.msize = messagesize;
-	t.version = "9P2000";
-	if(strncmp(x->version, "9P", 2) != 0)
-		t.version = "unknown";
-	return respond(x, &t, nil);
-}
-
-static
-Xfid*
-fsysauth(Xfid *x, Fid*)
-{
-	Fcall t;
-
-	return respond(x, &t, "acme: authentication not required");
-}
-
-static
-Xfid*
-fsysflush(Xfid *x, Fid*)
-{
-	sendp(x->c, xfidflush);
-	return nil;
-}
-
-static
-Xfid*
-fsysattach(Xfid *x, Fid *f)
-{
-	Fcall t;
-	int id;
-	Mntdir *m;
-
-	if(strcmp(x->uname, user) != 0)
-		return respond(x, &t, Eperm);
-	f->busy = TRUE;
-	f->open = FALSE;
-	f->qid.path = Qdir;
-	f->qid.type = QTDIR;
-	f->qid.vers = 0;
-	f->dir = dirtab;
-	f->nrpart = 0;
-	f->w = nil;
-	t.qid = f->qid;
-	f->mntdir = nil;
-	id = atoi(x->aname);
-	qlock(&mnt);
-	for(m=mnt.md; m; m=m->next)
-		if(m->id == id){
-			f->mntdir = m;
-			m->ref++;
-			break;
-		}
-	if(m == nil)
-		sendp(cerr, estrdup("unknown id in attach"));
-	qunlock(&mnt);
-	return respond(x, &t, nil);
-}
-
-static
-Xfid*
-fsyswalk(Xfid *x, Fid *f)
-{
-	Fcall t;
-	int c, i, j, id;
-	Qid q;
-	uchar type;
-	ulong path;
-	Fid *nf;
-	Dirtab *d, *dir;
-	Window *w;
-	char *err;
-
-	nf = nil;
-	w = nil;
-	if(f->open)
-		return respond(x, &t, "walk of open file");
-	if(x->fid != x->newfid){
-		nf = newfid(x->newfid);
-		if(nf->busy)
-			return respond(x, &t, "newfid already in use");
-		nf->busy = TRUE;
-		nf->open = FALSE;
-		nf->mntdir = f->mntdir;
-		if(f->mntdir)
-			f->mntdir->ref++;
-		nf->dir = f->dir;
-		nf->qid = f->qid;
-		nf->w = f->w;
-		nf->nrpart = 0;	/* not open, so must be zero */
-		if(nf->w)
-			incref(nf->w);
-		f = nf;	/* walk f */
-	}
-
-	t.nwqid = 0;
-	err = nil;
-	dir = nil;
-	id = WIN(f->qid);
-	q = f->qid;
-
-	if(x->nwname > 0){
-		for(i=0; i<x->nwname; i++){
-			if((q.type & QTDIR) == 0){
-				err = Enotdir;
-				break;
-			}
-
-			if(strcmp(x->wname[i], "..") == 0){
-				type = QTDIR;
-				path = Qdir;
-				id = 0;
-				if(w){
-					winclose(w);
-					w = nil;
-				}
-    Accept:
-				if(i == MAXWELEM){
-					err = "name too long";
-					break;
-				}
-				q.type = type;
-				q.vers = 0;
-				q.path = QID(id, path);
-				t.wqid[t.nwqid++] = q;
-				continue;
-			}
-
-			/* is it a numeric name? */
-			for(j=0; (c=x->wname[i][j]); j++)
-				if(c<'0' || '9'<c)
-					goto Regular;
-			/* yes: it's a directory */
-			if(w)	/* name has form 27/23; get out before losing w */
-				break;
-			id = atoi(x->wname[i]);
-			qlock(&row);
-			w = lookid(id, FALSE);
-			if(w == nil){
-				qunlock(&row);
-				break;
-			}
-			incref(w);	/* we'll drop reference at end if there's an error */
-			path = Qdir;
-			type = QTDIR;
-			qunlock(&row);
-			dir = dirtabw;
-			goto Accept;
-	
-    Regular:
-//			if(FILE(f->qid) == Qacme)	/* empty directory */
-//				break;
-			if(strcmp(x->wname[i], "new") == 0){
-				if(w)
-					error("w set in walk to new");
-				sendp(cnewwindow, nil);	/* signal newwindowthread */
-				w = recvp(cnewwindow);	/* receive new window */
-				incref(w);
-				type = QTDIR;
-				path = QID(w->id, Qdir);
-				id = w->id;
-				dir = dirtabw;
-				goto Accept;
-			}
-
-			if(id == 0)
-				d = dirtab;
-			else
-				d = dirtabw;
-			d++;	/* skip '.' */
-			for(; d->name; d++)
-				if(strcmp(x->wname[i], d->name) == 0){
-					path = d->qid;
-					type = d->type;
-					dir = d;
-					goto Accept;
-				}
-
-			break;	/* file not found */
-		}
-
-		if(i==0 && err == nil)
-			err = Eexist;
-	}
-
-	if(err!=nil || t.nwqid<x->nwname){
-		if(nf){
-			nf->busy = FALSE;
-			fsysdelid(nf->mntdir);
-		}
-	}else if(t.nwqid  == x->nwname){
-		if(w){
-			f->w = w;
-			w = nil;	/* don't drop the reference */
-		}
-		if(dir)
-			f->dir = dir;
-		f->qid = q;
-	}
-
-	if(w != nil)
-		winclose(w);
-
-	return respond(x, &t, err);
-}
-
-static
-Xfid*
-fsysopen(Xfid *x, Fid *f)
-{
-	Fcall t;
-	int m;
-
-	/* can't truncate anything, so just disregard */
-	x->mode &= ~(OTRUNC|OCEXEC);
-	/* can't execute or remove anything */
-	if(x->mode==OEXEC || (x->mode&ORCLOSE))
-		goto Deny;
-	switch(x->mode){
-	default:
-		goto Deny;
-	case OREAD:
-		m = 0400;
-		break;
-	case OWRITE:
-		m = 0200;
-		break;
-	case ORDWR:
-		m = 0600;
-		break;
-	}
-	if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
-		goto Deny;
-
-	sendp(x->c, xfidopen);
-	return nil;
-
-    Deny:
-	return respond(x, &t, Eperm);
-}
-
-static
-Xfid*
-fsyscreate(Xfid *x, Fid*)
-{
-	Fcall t;
-
-	return respond(x, &t, Eperm);
-}
-
-static
-int
-idcmp(void *a, void *b)
-{
-	return *(int*)a - *(int*)b;
-}
-
-static
-Xfid*
-fsysread(Xfid *x, Fid *f)
-{
-	Fcall t;
-	uchar *b;
-	int i, id, n, o, e, j, k, *ids, nids;
-	Dirtab *d, dt;
-	Column *c;
-	uint clock, len;
-	char buf[16];
-
-	if(f->qid.type & QTDIR){
-		if(FILE(f->qid) == Qacme){	/* empty dir */
-			t.data = nil;
-			t.count = 0;
-			respond(x, &t, nil);
-			return x;
-		}
-		o = x->offset;
-		e = x->offset+x->count;
-		clock = getclock();
-		b = emalloc(messagesize);
-		id = WIN(f->qid);
-		n = 0;
-		if(id > 0)
-			d = dirtabw;
-		else
-			d = dirtab;
-		d++;	/* first entry is '.' */
-		for(i=0; d->name!=nil && i<e; i+=len){
-			len = dostat(WIN(x->f->qid), d, b+n, x->count-n, clock);
-			if(len <= BIT16SZ)
-				break;
-			if(i >= o)
-				n += len;
-			d++;
-		}
-		if(id == 0){
-			qlock(&row);
-			nids = 0;
-			ids = nil;
-			for(j=0; j<row.ncol; j++){
-				c = row.col[j];
-				for(k=0; k<c->nw; k++){
-					ids = realloc(ids, (nids+1)*sizeof(int));
-					ids[nids++] = c->w[k]->id;
-				}
-			}
-			qunlock(&row);
-			qsort(ids, nids, sizeof ids[0], idcmp);
-			j = 0;
-			dt.name = buf;
-			for(; j<nids && i<e; i+=len){
-				k = ids[j];
-				sprint(dt.name, "%d", k);
-				dt.qid = QID(k, Qdir);
-				dt.type = QTDIR;
-				dt.perm = DMDIR|0700;
-				len = dostat(k, &dt, b+n, x->count-n, clock);
-				if(len == 0)
-					break;
-				if(i >= o)
-					n += len;
-				j++;
-			}
-			free(ids);
-		}
-		t.data = (char*)b;
-		t.count = n;
-		respond(x, &t, nil);
-		free(b);
-		return x;
-	}
-	sendp(x->c, xfidread);
-	return nil;
-}
-
-static
-Xfid*
-fsyswrite(Xfid *x, Fid*)
-{
-	sendp(x->c, xfidwrite);
-	return nil;
-}
-
-static
-Xfid*
-fsysclunk(Xfid *x, Fid *f)
-{
-	fsysdelid(f->mntdir);
-	sendp(x->c, xfidclose);
-	return nil;
-}
-
-static
-Xfid*
-fsysremove(Xfid *x, Fid*)
-{
-	Fcall t;
-
-	return respond(x, &t, Eperm);
-}
-
-static
-Xfid*
-fsysstat(Xfid *x, Fid *f)
-{
-	Fcall t;
-
-	t.stat = emalloc(messagesize-IOHDRSZ);
-	t.nstat = dostat(WIN(x->f->qid), f->dir, t.stat, messagesize-IOHDRSZ, getclock());
-	x = respond(x, &t, nil);
-	free(t.stat);
-	return x;
-}
-
-static
-Xfid*
-fsyswstat(Xfid *x, Fid*)
-{
-	Fcall t;
-
-	return respond(x, &t, Eperm);
-}
-
-Fid*
-newfid(int fid)
-{
-	Fid *f, *ff, **fh;
-
-	ff = nil;
-	fh = &fids[fid&(Nhash-1)];
-	for(f=*fh; f; f=f->next)
-		if(f->fid == fid)
-			return f;
-		else if(ff==nil && f->busy==FALSE)
-			ff = f;
-	if(ff){
-		ff->fid = fid;
-		return ff;
-	}
-	f = emalloc(sizeof *f);
-	f->fid = fid;
-	f->next = *fh;
-	*fh = f;
-	return f;
-}
-
-uint
-getclock()
-{
-	char buf[32];
-
-	buf[0] = '\0';
-	pread(clockfd, buf, sizeof buf, 0);
-	return atoi(buf);
-}
-
-int
-dostat(int id, Dirtab *dir, uchar *buf, int nbuf, uint clock)
-{
-	Dir d;
-
-	d.qid.path = QID(id, dir->qid);
-	d.qid.vers = 0;
-	d.qid.type = dir->type;
-	d.mode = dir->perm;
-	d.length = 0;	/* would be nice to do better */
-	d.name = dir->name;
-	d.uid = user;
-	d.gid = user;
-	d.muid = user;
-	d.atime = clock;
-	d.mtime = clock;
-	return convD2M(&d, buf, nbuf);
-}
--- a/logf.c
+++ /dev/null
@@ -1,202 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <thread.h>
-#include <cursor.h>
-#include <mouse.h>
-#include <keyboard.h>
-#include <frame.h>
-#include <fcall.h>
-#include <plumb.h>
-#include <libsec.h>
-#include "dat.h"
-#include "fns.h"
-
-// State for global log file.
-typedef struct Log Log;
-struct Log
-{
-	QLock lk;
-	Rendez r;
-
-	vlong start; // msg[0] corresponds to 'start' in the global sequence of events
-
-	// queued events (nev=entries in ev, mev=capacity of p)
-	char **ev;
-	int nev;
-	int mev;
-
-	// open acme/put files that need to read events
-	Fid **f;
-	int nf;
-	int mf;
-
-	// active (blocked) reads waiting for events
-	Xfid **read;
-	int nread;
-	int mread;
-};
-
-static Log eventlog;
-
-void
-xfidlogopen(Xfid *x)
-{
-	qlock(&eventlog.lk);
-	if(eventlog.nf >= eventlog.mf) {
-		eventlog.mf = eventlog.mf*2;
-		if(eventlog.mf == 0)
-			eventlog.mf = 8;
-		eventlog.f = erealloc(eventlog.f, eventlog.mf*sizeof eventlog.f[0]);
-	}
-	eventlog.f[eventlog.nf++] = x->f;
-	x->f->logoff = eventlog.start + eventlog.nev;
-
-	qunlock(&eventlog.lk);
-}
-
-void
-xfidlogclose(Xfid *x)
-{
-	int i;
-
-	qlock(&eventlog.lk);
-	for(i=0; i<eventlog.nf; i++) {
-		if(eventlog.f[i] == x->f) {
-			eventlog.f[i] = eventlog.f[--eventlog.nf];
-			break;
-		}
-	}
-	qunlock(&eventlog.lk);
-}
-
-void
-xfidlogread(Xfid *x)
-{
-	char *p;
-	int i;
-	Fcall fc;
-
-	qlock(&eventlog.lk);
-	if(eventlog.nread >= eventlog.mread) {
-		eventlog.mread = eventlog.mread*2;
-		if(eventlog.mread == 0)
-			eventlog.mread = 8;
-		eventlog.read = erealloc(eventlog.read, eventlog.mread*sizeof eventlog.read[0]);
-	}
-	eventlog.read[eventlog.nread++] = x;
-
-	if(eventlog.r.l == nil)
-		eventlog.r.l = &eventlog.lk;
-	x->flushed = FALSE;
-	while(x->f->logoff >= eventlog.start+eventlog.nev && !x->flushed)
-		rsleep(&eventlog.r);
-
-	for(i=0; i<eventlog.nread; i++) {
-		if(eventlog.read[i] == x) {
-			eventlog.read[i] = eventlog.read[--eventlog.nread];
-			break;
-		}
-	}
-
-	if(x->flushed) {
-		qunlock(&eventlog.lk);
-		return;
-	}
-
-	i = x->f->logoff - eventlog.start;
-	p = estrdup(eventlog.ev[i]);
-	x->f->logoff++;
-	qunlock(&eventlog.lk);
-
-	fc.data = p;
-	fc.count = strlen(p);
-	respond(x, &fc, nil);
-	free(p);
-}
-
-void
-xfidlogflush(Xfid *x)
-{
-	int i;
-	Xfid *rx;
-
-	qlock(&eventlog.lk);
-	for(i=0; i<eventlog.nread; i++) {
-		rx = eventlog.read[i];
-		if(rx->tag == x->oldtag) {
-			rx->flushed = TRUE;
-			rwakeupall(&eventlog.r);
-		}
-	}
-	qunlock(&eventlog.lk);
-}
-
-/*
- * add a log entry for op on w.
- * expected calls:
- *
- * op == "new" for each new window
- *	- caller of coladd or makenewwindow responsible for calling
- *		xfidlog after setting window name
- *	- exception: zerox
- *
- * op == "zerox" for new window created via zerox
- *	- called from zeroxx
- *
- * op == "get" for Get executed on window
- *	- called from get
- *
- * op == "put" for Put executed on window
- *	- called from put
- *
- * op == "del" for deleted window
- *	- called from winclose
- *
- * op == "focus" for window focus change
- *	- called from mousethread
- */
-void
-xfidlog(Window *w, char *op)
-{
-	int i, n;
-	vlong min;
-	File *f;
-	char *name;
-
-	qlock(&eventlog.lk);
-	if(eventlog.nev >= eventlog.mev) {
-		// Remove and free any entries that all readers have read.
-		min = eventlog.start + eventlog.nev;
-		for(i=0; i<eventlog.nf; i++) {
-			if(min > eventlog.f[i]->logoff)
-				min = eventlog.f[i]->logoff;
-		}
-		if(min > eventlog.start) {
-			n = min - eventlog.start;
-			for(i=0; i<n; i++)
-				free(eventlog.ev[i]);
-			eventlog.nev -= n;
-			eventlog.start += n;
-			memmove(eventlog.ev, eventlog.ev+n, eventlog.nev*sizeof eventlog.ev[0]);
-		}
-
-		// Otherwise grow.
-		if(eventlog.nev >= eventlog.mev) {
-			eventlog.mev = eventlog.mev*2;
-			if(eventlog.mev == 0)
-				eventlog.mev = 8;
-			eventlog.ev = erealloc(eventlog.ev, eventlog.mev*sizeof eventlog.ev[0]);
-		}
-	}
-	f = w->body.file;
-	name = runetobyte(f->name, f->nname);
-	if(name == nil)
-		name = estrdup("");
-	eventlog.ev[eventlog.nev++] = smprint("%d %s %s\n", w->id, op, name);
-	free(name);
-	if(eventlog.r.l == nil)
-		eventlog.r.l = &eventlog.lk;
-	rwakeupall(&eventlog.r);
-	qunlock(&eventlog.lk);
-}
--- a/look.c
+++ /dev/null
@@ -1,737 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <thread.h>
-#include <cursor.h>
-#include <mouse.h>
-#include <keyboard.h>
-#include <frame.h>
-#include <fcall.h>
-#include <plumb.h>
-#include "dat.h"
-#include "fns.h"
-
-Window*	openfile(Text*, Expand*);
-
-int	nuntitled;
-
-void
-look3(Text *t, uint q0, uint q1, int external)
-{
-	int n, c, f, expanded;
-	Text *ct;
-	Expand e;
-	Rune *r;
-	uint p;
-	Plumbmsg *m;
-	Runestr dir;
-	char buf[32];
-
-	ct = seltext;
-	if(ct == nil)
-		seltext = t;
-	expanded = expand(t, q0, q1, &e);
-	if(!external && t->w!=nil && t->w->nopen[QWevent]>0){
-		/* send alphanumeric expansion to external client */
-		if(expanded == FALSE)
-			return;
-		f = 0;
-		if((e.at!=nil && t->w!=nil) || (e.nname>0 && lookfile(e.name, e.nname)!=nil))
-			f = 1;		/* acme can do it without loading a file */
-		if(q0!=e.q0 || q1!=e.q1)
-			f |= 2;	/* second (post-expand) message follows */
-		if(e.nname)
-			f |= 4;	/* it's a file name */
-		c = 'l';
-		if(t->what == Body)
-			c = 'L';
-		n = q1-q0;
-		if(n <= EVENTSIZE){
-			r = runemalloc(n);
-			bufread(t->file, q0, r, n);
-			winevent(t->w, "%c%d %d %d %d %.*S\n", c, q0, q1, f, n, n, r);
-			free(r);
-		}else
-			winevent(t->w, "%c%d %d %d 0 \n", c, q0, q1, f, n);
-		if(q0==e.q0 && q1==e.q1)
-			return;
-		if(e.nname){
-			n = e.nname;
-			if(e.a1 > e.a0)
-				n += 1+(e.a1-e.a0);
-			r = runemalloc(n);
-			runemove(r, e.name, e.nname);
-			if(e.a1 > e.a0){
-				r[e.nname] = ':';
-				bufread(e.at->file, e.a0, r+e.nname+1, e.a1-e.a0);
-			}
-		}else{
-			n = e.q1 - e.q0;
-			r = runemalloc(n);
-			bufread(t->file, e.q0, r, n);
-		}
-		f &= ~2;
-		if(n <= EVENTSIZE)
-			winevent(t->w, "%c%d %d %d %d %.*S\n", c, e.q0, e.q1, f, n, n, r);
-		else
-			winevent(t->w, "%c%d %d %d 0 \n", c, e.q0, e.q1, f, n);
-		free(r);
-		goto Return;
-	}
-	if(plumbsendfd >= 0){
-		/* send whitespace-delimited word to plumber */
-		m = emalloc(sizeof(Plumbmsg));
-		m->src = estrdup("acme");
-		m->dst = nil;
-		dir = dirname(t, nil, 0);
-		if(dir.nr==1 && dir.r[0]=='.'){	/* sigh */
-			free(dir.r);
-			dir.r = nil;
-			dir.nr = 0;
-		}
-		if(dir.nr == 0)
-			m->wdir = estrdup(wdir);
-		else
-			m->wdir = runetobyte(dir.r, dir.nr);
-		free(dir.r);
-		m->type = estrdup("text");
-		m->attr = nil;
-		buf[0] = '\0';
-		if(q1 == q0){
-			if(t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){
-				q0 = t->q0;
-				q1 = t->q1;
-			}else{
-				p = q0;
-				while(q0>0 && (c=tgetc(t, q0-1))!=' ' && c!='\t' && c!='\n')
-					q0--;
-				while(q1<t->file->nc && (c=tgetc(t, q1))!=' ' && c!='\t' && c!='\n')
-					q1++;
-				if(q1 == q0){
-					plumbfree(m);
-					goto Return;
-				}
-				sprint(buf, "click=%d", p-q0);
-				m->attr = plumbunpackattr(buf);
-			}
-		}
-		r = runemalloc(q1-q0);
-		bufread(t->file, q0, r, q1-q0);
-		m->data = runetobyte(r, q1-q0);
-		m->ndata = strlen(m->data);
-		free(r);
-		if(m->ndata<messagesize-1024 && plumbsend(plumbsendfd, m) >= 0){
-			plumbfree(m);
-			goto Return;
-		}
-		plumbfree(m);
-		/* plumber failed to match; fall through */
-	}
-
-	/* interpret alphanumeric string ourselves */
-	if(expanded == FALSE)
-		return;
-	if(e.name || e.at)
-		openfile(t, &e);
-	else{
-		if(t->w == nil)
-			return;
-		ct = &t->w->body;
-		if(t->w != ct->w)
-			winlock(ct->w, 'M');
-		if(t == ct)
-			textsetselect(ct, e.q1, e.q1);
-		n = e.q1 - e.q0;
-		r = runemalloc(n);
-		bufread(t->file, e.q0, r, n);
-		if(search(ct, r, n) && e.jump)
-			moveto(mousectl, addpt(frptofchar(ct, ct->p0), Pt(4, ct->font->height-4)));
-		if(t->w != ct->w)
-			winunlock(ct->w);
-		free(r);
-	}
-
-   Return:
-	free(e.name);
-	free(e.bname);
-}
-
-int
-plumbgetc(void *a, uint n)
-{
-	Rune *r;
-
-	r = a;
-	if(n>runestrlen(r))
-		return 0;
-	return r[n];
-}
-
-void
-plumblook(Plumbmsg *m)
-{
-	Expand e;
-	char *addr;
-
-	if(m->ndata >= BUFSIZE){
-		warning(nil, "insanely long file name (%d bytes) in plumb message (%.32s...)\n", m->ndata, m->data);
-		return;
-	}
-	e.q0 = 0;
-	e.q1 = 0;
-	if(m->data[0] == '\0')
-		return;
-	e.ar = nil;
-	e.bname = m->data;
-	e.name = bytetorune(e.bname, &e.nname);
-	e.jump = TRUE;
-	e.a0 = 0;
-	e.a1 = 0;
-	addr = plumblookup(m->attr, "addr");
-	if(addr != nil){
-		e.ar = bytetorune(addr, &e.a1);
-		e.agetc = plumbgetc;
-	}
-	openfile(nil, &e);
-	free(e.name);
-	free(e.at);
-}
-
-void
-plumbshow(Plumbmsg *m)
-{
-	Window *w;
-	Rune rb[256], *r;
-	int nb, nr;
-	Runestr rs;
-	char *name, *p, namebuf[16];
-
-	w = makenewwindow(nil);
-	name = plumblookup(m->attr, "filename");
-	if(name == nil){
-		name = namebuf;
-		nuntitled++;
-		snprint(namebuf, sizeof namebuf, "Untitled-%d", nuntitled);
-	}
-	p = nil;
-	if(name[0]!='/' && m->wdir!=nil && m->wdir[0]!='\0'){
-		nb = strlen(m->wdir) + 1 + strlen(name) + 1;
-		p = emalloc(nb);
-		snprint(p, nb, "%s/%s", m->wdir, name);
-		name = p;
-	}
-	cvttorunes(name, strlen(name), rb, &nb, &nr, nil);
-	free(p);
-	rs = cleanrname((Runestr){rb, nr});
-	winsetname(w, rs.r, rs.nr);
-	r = runemalloc(m->ndata);
-	cvttorunes(m->data, m->ndata, r, &nb, &nr, nil);
-	textinsert(&w->body, 0, r, nr, TRUE);
-	free(r);
-	w->body.file->mod = FALSE;
-	w->dirty = FALSE;
-	winsettag(w);
-	textscrdraw(&w->body);
-	textsetselect(&w->tag, w->tag.file->nc, w->tag.file->nc);
-	xfidlog(w, "new");
-}
-
-int
-search(Text *ct, Rune *r, uint n)
-{
-	uint q, nb, maxn;
-	int around;
-	Rune *s, *b, *c;
-
-	if(n==0 || n>ct->file->nc)
-		return FALSE;
-	if(2*n > RBUFSIZE){
-		warning(nil, "string too long\n");
-		return FALSE;
-	}
-	maxn = max(2*n, RBUFSIZE);
-	s = fbufalloc();
-	b = s;
-	nb = 0;
-	b[nb] = 0;
-	around = 0;
-	q = ct->q1;
-	for(;;){
-		if(q >= ct->file->nc){
-			q = 0;
-			around = 1;
-			nb = 0;
-			b[nb] = 0;
-		}
-		if(nb > 0){
-			c = runestrchr(b, r[0]);
-			if(c == nil){
-				q += nb;
-				nb = 0;
-				b[nb] = 0;
-				if(around && q>=ct->q1)
-					break;
-				continue;
-			}
-			q += (c-b);
-			nb -= (c-b);
-			b = c;
-		}
-		/* reload if buffer covers neither string nor rest of file */
-		if(nb<n && nb!=ct->file->nc-q){
-			nb = ct->file->nc-q;
-			if(nb >= maxn)
-				nb = maxn-1;
-			bufread(ct->file, q, s, nb);
-			b = s;
-			b[nb] = '\0';
-		}
-		/* this runeeq is fishy but the null at b[nb] makes it safe */
-		if(runeeq(b, n, r, n)==TRUE){
-			if(ct->w){
-				textshow(ct, q, q+n, 1);
-				winsettag(ct->w);
-			}else{
-				ct->q0 = q;
-				ct->q1 = q+n;
-			}
-			seltext = ct;
-			fbuffree(s);
-			return TRUE;
-		}
-		--nb;
-		b++;
-		q++;
-		if(around && q>=ct->q1)
-			break;
-	}
-	fbuffree(s);
-	return FALSE;
-}
-
-int
-isfilec(Rune r)
-{
-	if(isalnum(r))
-		return TRUE;
-	if(runestrchr(L".-+/:@", r))
-		return TRUE;
-	return FALSE;
-}
-
-/* Runestr wrapper for cleanname */
-Runestr
-cleanrname(Runestr rs)
-{
-	char *s;
-	int nb, nulls;
-
-	s = runetobyte(rs.r, rs.nr);
-	cleanname(s);
-	cvttorunes(s, strlen(s), rs.r, &nb, &rs.nr, &nulls);
-	free(s);
-	return rs;
-}
-
-Runestr
-includefile(Rune *dir, Rune *file, int nfile)
-{
-	int m, n;
-	char *a;
-	Rune *r;
-
-	m = runestrlen(dir);
-	a = emalloc((m+1+nfile)*UTFmax+1);
-	sprint(a, "%S/%.*S", dir, nfile, file);
-	n = access(a, 0);
-	free(a);
-	if(n < 0)
-		return (Runestr){nil, 0};
-	r = runemalloc(m+1+nfile);
-	runemove(r, dir, m);
-	runemove(r+m, L"/", 1);
-	runemove(r+m+1, file, nfile);
-	free(file);
-	return cleanrname((Runestr){r, m+1+nfile});
-}
-
-static	Rune	*objdir;
-
-Runestr
-includename(Text *t, Rune *r, int n)
-{
-	Window *w;
-	char buf[128];
-	Runestr file;
-	int i;
-
-	if(objdir==nil && objtype!=nil){
-		sprint(buf, "/%s/include", objtype);
-		objdir = bytetorune(buf, &i);
-		objdir = runerealloc(objdir, i+1);
-		objdir[i] = '\0';	
-	}
-
-	w = t->w;
-	if(n==0 || r[0]=='/' || w==nil)
-		goto Rescue;
-	if(n>2 && r[0]=='.' && r[1]=='/')
-		goto Rescue;
-	file.r = nil;
-	file.nr = 0;
-	for(i=0; i<w->nincl && file.r==nil; i++)
-		file = includefile(w->incl[i], r, n);
-
-	if(file.r == nil)
-		file = includefile(L"/sys/include", r, n);
-	if(file.r==nil && objdir!=nil)
-		file = includefile(objdir, r, n);
-	if(file.r == nil)
-		goto Rescue;
-	return file;
-
-    Rescue:
-	return (Runestr){r, n};
-}
-
-Runestr
-dirname(Text *t, Rune *r, int n)
-{
-	Rune *b, c;
-	uint m, nt;
-	int slash;
-	Runestr tmp;
-
-	b = nil;
-	if(t==nil || t->w==nil)
-		goto Rescue;
-	nt = t->w->tag.file->nc;
-	if(nt == 0)
-		goto Rescue;
-	if(n>=1 && r[0]=='/')
-		goto Rescue;
-	b = runemalloc(nt+n+1);
-	bufread(t->w->tag.file, 0, b, nt);
-	slash = -1;
-	for(m=0; m<nt; m++){
-		c = b[m];
-		if(c == '/')
-			slash = m;
-		if(c==' ' || c=='\t')
-			break;
-	}
-	if(slash < 0)
-		goto Rescue;
-	runemove(b+slash+1, r, n);
-	free(r);
-	return cleanrname((Runestr){b, slash+1+n});
-
-    Rescue:
-	free(b);
-	tmp = (Runestr){r, n};
-	if(r)
-		return cleanrname(tmp);
-	return tmp;
-}
-
-int
-expandfile(Text *t, uint q0, uint q1, Expand *e)
-{
-	int i, n, nname, colon, eval;
-	uint amin, amax;
-	Rune *r, c;
-	Window *w;
-	Runestr rs;
-
-	amax = q1;
-	if(q1 == q0){
-		colon = -1;
-		while(q1<t->file->nc && isfilec(c=textreadc(t, q1))){
-			if(c == ':'){
-				colon = q1;
-				break;
-			}
-			q1++;
-		}
-		while(q0>0 && (isfilec(c=textreadc(t, q0-1)) || isaddrc(c) || isregexc(c))){
-			q0--;
-			if(colon<0 && c==':')
-				colon = q0;
-		}
-		/*
-		 * if it looks like it might begin file: , consume address chars after :
-		 * otherwise terminate expansion at :
-		 */
-		if(colon >= 0){
-			q1 = colon;
-			if(colon<t->file->nc-1 && isaddrc(textreadc(t, colon+1))){
-				q1 = colon+1;
-				while(q1<t->file->nc && isaddrc(textreadc(t, q1)))
-					q1++;
-			}
-		}
-		if(q1 > q0)
-			if(colon >= 0){	/* stop at white space */
-				for(amax=colon+1; amax<t->file->nc; amax++)
-					if((c=textreadc(t, amax))==' ' || c=='\t' || c=='\n')
-						break;
-			}else
-				amax = t->file->nc;
-	}
-	amin = amax;
-	e->q0 = q0;
-	e->q1 = q1;
-	n = q1-q0;
-	if(n == 0)
-		return FALSE;
-	/* see if it's a file name */
-	r = runemalloc(n);
-	bufread(t->file, q0, r, n);
-	/* first, does it have bad chars? */
-	nname = -1;
-	for(i=0; i<n; i++){
-		c = r[i];
-		if(c==':' && nname<0){
-			if(q0+i+1<t->file->nc && (i==n-1 || isaddrc(textreadc(t, q0+i+1))))
-				amin = q0+i;
-			else
-				goto Isntfile;
-			nname = i;
-		}
-	}
-	if(nname == -1)
-		nname = n;
-	for(i=0; i<nname; i++)
-		if(!isfilec(r[i]))
-			goto Isntfile;
-	/*
-	 * See if it's a file name in <>, and turn that into an include
-	 * file name if so.  Should probably do it for "" too, but that's not
-	 * restrictive enough syntax and checking for a #include earlier on the
-	 * line would be silly.
-	 */
-	if(q0>0 && textreadc(t, q0-1)=='<' && q1<t->file->nc && textreadc(t, q1)=='>'){
-		rs = includename(t, r, nname);
-		r = rs.r;
-		nname = rs.nr;
-	}
-	else if(amin == q0)
-		goto Isfile;
-	else{
-		rs = dirname(t, r, nname);
-		r = rs.r;
-		nname = rs.nr;
-	}
-	e->bname = runetobyte(r, nname);
-	/* if it's already a window name, it's a file */
-	w = lookfile(r, nname);
-	if(w != nil)
-		goto Isfile;
-	/* if it's the name of a file, it's a file */
-	if(access(e->bname, 0) < 0){
-		free(e->bname);
-		e->bname = nil;
-		goto Isntfile;
-	}
-
-  Isfile:
-	e->name = r;
-	e->nname = nname;
-	e->at = t;
-	e->a0 = amin+1;
-	eval = FALSE;
-	address(nil, nil, (Range){-1,-1}, (Range){0, 0}, t, e->a0, amax, tgetc, &eval, (uint*)&e->a1);
-	return TRUE;
-
-   Isntfile:
-	free(r);
-	return FALSE;
-}
-
-int
-expand(Text *t, uint q0, uint q1, Expand *e)
-{
-	memset(e, 0, sizeof *e);
-	e->agetc = tgetc;
-	/* if in selection, choose selection */
-	e->jump = TRUE;
-	if(q1==q0 && t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){
-		q0 = t->q0;
-		q1 = t->q1;
-		if(t->what == Tag)
-			e->jump = FALSE;
-	}
-
-	if(expandfile(t, q0, q1, e))
-		return TRUE;
-
-	if(q0 == q1){
-		while(q1<t->file->nc && isalnum(textreadc(t, q1)))
-			q1++;
-		while(q0>0 && isalnum(textreadc(t, q0-1)))
-			q0--;
-	}
-	e->q0 = q0;
-	e->q1 = q1;
-	return q1 > q0;
-}
-
-Window*
-lookfile(Rune *s, int n)
-{
-	int i, j, k;
-	Window *w;
-	Column *c;
-	Text *t;
-
-	/* avoid terminal slash on directories */
-	if(n>1 && s[n-1] == '/')
-		--n;
-	for(j=0; j<row.ncol; j++){
-		c = row.col[j];
-		for(i=0; i<c->nw; i++){
-			w = c->w[i];
-			t = &w->body;
-			k = t->file->nname;
-			if(k>1 && t->file->name[k-1] == '/')
-				k--;
-			if(runeeq(t->file->name, k, s, n)){
-				w = w->body.file->curtext->w;
-				if(w->col != nil)	/* protect against race deleting w */
-					return w;
-			}
-		}
-	}
-	return nil;
-}
-
-Window*
-lookid(int id, int dump)
-{
-	int i, j;
-	Window *w;
-	Column *c;
-
-	for(j=0; j<row.ncol; j++){
-		c = row.col[j];
-		for(i=0; i<c->nw; i++){
-			w = c->w[i];
-			if(dump && w->dumpid == id)
-				return w;
-			if(!dump && w->id == id)
-				return w;
-		}
-	}
-	return nil;
-}
-
-
-Window*
-openfile(Text *t, Expand *e)
-{
-	Range r;
-	Window *w, *ow;
-	int eval, i, n;
-	Rune *rp;
-	uint dummy;
-
-	if(e->nname == 0){
-		w = t->w;
-		if(w == nil)
-			return nil;
-	}else
-		w = lookfile(e->name, e->nname);
-	if(w){
-		t = &w->body;
-		if(!t->col->safe && t->maxlines==0) /* window is obscured by full-column window */
-			colgrow(t->col, t->col->w[0], 1);
-	}else{
-		ow = nil;
-		if(t)
-			ow = t->w;
-		w = makenewwindow(t);
-		t = &w->body;
-		winsetname(w, e->name, e->nname);
-		textload(t, 0, e->bname, 1);
-		t->file->mod = FALSE;
-		t->w->dirty = FALSE;
-		winsettag(t->w);
-		textsetselect(&t->w->tag, t->w->tag.file->nc, t->w->tag.file->nc);
-		if(ow != nil){
-			for(i=ow->nincl; --i>=0; ){
-				n = runestrlen(ow->incl[i]);
-				rp = runemalloc(n);
-				runemove(rp, ow->incl[i], n);
-				winaddincl(w, rp, n);
-			}
-			for(i=0; i < NINDENT; i++)
-				w->indent[i] = ow->indent[i];
-		}else
-			for(i=0; i < NINDENT; i++)
-				w->indent[i] = globalindent[i];
-		xfidlog(w, "new");
-	}
-	if(e->a1 == e->a0)
-		eval = FALSE;
-	else{
-		eval = TRUE;
-		r = address(nil, t, (Range){-1, -1}, (Range){t->q0, t->q1}, e->at, e->a0, e->a1, e->agetc, &eval, &dummy);
-		if(eval == FALSE)
-			e->jump = FALSE;	/* don't jump if invalid address */
-	}
-	if(eval == FALSE){
-		r.q0 = t->q0;
-		r.q1 = t->q1;
-	}
-	textshow(t, r.q0, r.q1, 1);
-	winsettag(t->w);
-	seltext = t;
-	if(e->jump)
-		moveto(mousectl, addpt(frptofchar(t, t->p0), Pt(4, font->height-4)));
-	return w;
-}
-
-void
-new(Text *et, Text *t, Text *argt, int flag1, int flag2, Rune *arg, int narg)
-{
-	int ndone;
-	Rune *a, *f;
-	int na, nf;
-	Expand e;
-	Runestr rs;
-	Window *w;
-
-	getarg(argt, FALSE, TRUE, &a, &na);
-	if(a){
-		new(et, t, nil, flag1, flag2, a, na);
-		if(narg == 0)
-			return;
-	}
-	/* loop condition: *arg is not a blank */
-	for(ndone=0; ; ndone++){
-		a = findbl(arg, narg, &na);
-		if(a == arg){
-			if(ndone==0 && et->col!=nil) {
-				w = coladd(et->col, nil, nil, -1);
-				winsettag(w);
-				xfidlog(w, "new");
-			}
-			break;
-		}
-		nf = narg-na;
-		f = runemalloc(nf);
-		runemove(f, arg, nf);
-		rs = dirname(et, f, nf);
-		f = rs.r;
-		nf = rs.nr;
-		memset(&e, 0, sizeof e);
-		e.name = f;
-		e.nname = nf;
-		e.bname = runetobyte(f, nf);
-		e.jump = TRUE;
-		openfile(et, &e);
-		free(f);
-		free(e.bname);
-		arg = skipbl(a, na, &narg);
-	}
-}
--- a/mkfile
+++ /dev/null
@@ -1,46 +1,0 @@
-</$objtype/mkfile
-BIN=/$objtype/bin
-
-TARG=acme-themes
-
-OFILES=\
-	acme.$O\
-	addr.$O\
-	buff.$O\
-	cols.$O\
-	disk.$O\
-	ecmd.$O\
-	edit.$O\
-	elog.$O\
-	exec.$O\
-	file.$O\
-	fsys.$O\
-	logf.$O\
-	look.$O\
-	regx.$O\
-	rows.$O\
-	scrl.$O\
-	text.$O\
-	time.$O\
-	util.$O\
-	wind.$O\
-	xfid.$O\
-
-HFILES=dat.h\
-	edit.h\
-	fns.h\
-
-UPDATE=\
-	mkfile\
-	$HFILES\
-	${OFILES:%.$O=%.c}\
-
-</sys/src/cmd/mkone
-
-$O.out:	/$objtype/lib/libframe.a /$objtype/lib/libdraw.a /$objtype/lib/libthread.a
-
-edit.$O ecmd.$O elog.$O:	edit.h
-
-syms:V:
-	$CC -a acme.c > syms
-	for(i in ????.c) $CC -aa $i >> syms
--- a/regx.c
+++ /dev/null
@@ -1,839 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <thread.h>
-#include <cursor.h>
-#include <mouse.h>
-#include <keyboard.h>
-#include <frame.h>
-#include <fcall.h>
-#include <plumb.h>
-#include "dat.h"
-#include "fns.h"
-
-Rangeset	sel;
-Rune		*lastregexp;
-
-/*
- * Machine Information
- */
-typedef struct Inst Inst;
-struct Inst
-{
-	uint	type;	/* <= Runemax+1 ==> literal, otherwise action */
-	union {
-		int sid;
-		int subid;
-		int class;
-		Inst *other;
-		Inst *right;
-	};
-	union{
-		Inst *left;
-		Inst *next;
-	};
-};
-
-#define	NPROG	1024
-Inst	program[NPROG];
-Inst	*progp;
-Inst	*startinst;	/* First inst. of program; might not be program[0] */
-Inst	*bstartinst;	/* same for backwards machine */
-Channel	*rechan;	/* chan(Inst*) */
-
-typedef struct Ilist Ilist;
-struct Ilist
-{
-	Inst	*inst;		/* Instruction of the thread */
-	Rangeset se;
-	uint	startp;		/* first char of match */
-};
-
-#define	NLIST	127
-
-Ilist	*tl, *nl;	/* This list, next list */
-Ilist	list[2][NLIST+1];	/* +1 for trailing null */
-static	Rangeset sempty;
-
-/*
- * Actions and Tokens
- *
- *	0x100xx are operators, value == precedence
- *	0x200xx are tokens, i.e. operands for operators
- */
-enum {
-	OPERATOR = Runemask+1,	/* Bitmask of all operators */
-	START	= OPERATOR,	/* Start, used for marker on stack */
-	RBRA,			/* Right bracket, ) */
-	LBRA,			/* Left bracket, ( */
-	OR,			/* Alternation, | */
-	CAT,			/* Concatentation, implicit operator */
-	STAR,			/* Closure, * */
-	PLUS,			/* a+ == aa* */
-	QUEST,			/* a? == a|nothing, i.e. 0 or 1 a's */
-
-	ANY	= OPERATOR<<1,	/* Any character but newline, . */
-	NOP,			/* No operation, internal use only */
-	BOL,			/* Beginning of line, ^ */
-	EOL,			/* End of line, $ */
-	CCLASS,			/* Character class, [] */
-	NCCLASS,		/* Negated character class, [^] */
-	END,			/* Terminate: match found */
-
-	ISATOR	= OPERATOR,
-	ISAND	= OPERATOR<<1,
-};
-
-/*
- * Parser Information
- */
-typedef struct Node Node;
-struct Node
-{
-	Inst	*first;
-	Inst	*last;
-};
-
-#define	NSTACK	20
-Node	andstack[NSTACK];
-Node	*andp;
-int	atorstack[NSTACK];
-int	*atorp;
-int	lastwasand;	/* Last token was operand */
-int	cursubid;
-int	subidstack[NSTACK];
-int	*subidp;
-int	backwards;
-int	nbra;
-Rune	*exprp;		/* pointer to next character in source expression */
-#define	DCLASS	10	/* allocation increment */
-int	nclass;		/* number active */
-int	Nclass;		/* high water mark */
-Rune	**class;
-int	negateclass;
-
-int	addinst(Ilist *l, Inst *inst, Rangeset *sep);
-void	newmatch(Rangeset*);
-void	bnewmatch(Rangeset*);
-void	pushand(Inst*, Inst*);
-void	pushator(int);
-Node	*popand(int);
-int	popator(void);
-void	startlex(Rune*);
-int	lex(void);
-void	operator(int);
-void	operand(int);
-void	evaluntil(int);
-void	optimize(Inst*);
-void	bldcclass(void);
-
-void
-rxinit(void)
-{
-	rechan = chancreate(sizeof(Inst*), 0);
-	lastregexp = runemalloc(1);
-}
-
-void
-regerror(char *e)
-{
-	lastregexp[0] = 0;
-	warning(nil, "regexp: %s\n", e);
-	sendp(rechan, nil);
-	threadexits(nil);
-}
-
-Inst *
-newinst(int t)
-{
-	if(progp >= &program[NPROG])
-		regerror("expression too long");
-	progp->type = t;
-	progp->left = nil;
-	progp->right = nil;
-	return progp++;
-}
-
-void
-realcompile(void *arg)
-{
-	int token;
-	Rune *s;
-
-	threadsetname("regcomp");
-	s = arg;
-	startlex(s);
-	atorp = atorstack;
-	andp = andstack;
-	subidp = subidstack;
-	cursubid = 0;
-	lastwasand = FALSE;
-	/* Start with a low priority operator to prime parser */
-	pushator(START-1);
-	while((token=lex()) != END){
-		if((token&ISATOR) == OPERATOR)
-			operator(token);
-		else
-			operand(token);
-	}
-	/* Close with a low priority operator */
-	evaluntil(START);
-	/* Force END */
-	operand(END);
-	evaluntil(START);
-	if(nbra)
-		regerror("unmatched `('");
-	--andp;	/* points to first and only operand */
-	sendp(rechan, andp->first);
-	threadexits(nil);
-}
-
-/* r is null terminated */
-int
-rxcompile(Rune *r)
-{
-	int i, nr;
-	Inst *oprogp;
-
-	nr = runestrlen(r)+1;
-	if(runeeq(lastregexp, runestrlen(lastregexp)+1, r, nr)==TRUE)
-		return TRUE;
-	lastregexp[0] = 0;
-	for(i=0; i<nclass; i++)
-		free(class[i]);
-	nclass = 0;
-	progp = program;
-	backwards = FALSE;
-	bstartinst = nil;
-	threadcreate(realcompile, r, STACK);
-	startinst = recvp(rechan);
-	if(startinst == nil)
-		return FALSE;
-	optimize(program);
-	oprogp = progp;
-	backwards = TRUE;
-	threadcreate(realcompile, r, STACK);
-	bstartinst = recvp(rechan);
-	if(bstartinst == nil)
-		return FALSE;
-	optimize(oprogp);
-	lastregexp = runerealloc(lastregexp, nr);
-	runemove(lastregexp, r, nr);
-	return TRUE;
-}
-
-void
-operand(int t)
-{
-	Inst *i;
-	if(lastwasand)
-		operator(CAT);	/* catenate is implicit */
-	i = newinst(t);
-	if(t == CCLASS){
-		if(negateclass)
-			i->type = NCCLASS;	/* UGH */
-		i->class = nclass-1;		/* UGH */
-	}
-	pushand(i, i);
-	lastwasand = TRUE;
-}
-
-void
-operator(int t)
-{
-	if(t==RBRA && --nbra<0)
-		regerror("unmatched `)'");
-	if(t==LBRA){
-		cursubid++;	/* silently ignored */
-		nbra++;
-		if(lastwasand)
-			operator(CAT);
-	}else
-		evaluntil(t);
-	if(t!=RBRA)
-		pushator(t);
-	lastwasand = FALSE;
-	if(t==STAR || t==QUEST || t==PLUS || t==RBRA)
-		lastwasand = TRUE;	/* these look like operands */
-}
-
-void
-pushand(Inst *f, Inst *l)
-{
-	if(andp >= &andstack[NSTACK])
-		error("operand stack overflow");
-	andp->first = f;
-	andp->last = l;
-	andp++;
-}
-
-void
-pushator(int t)
-{
-	if(atorp >= &atorstack[NSTACK])
-		error("operator stack overflow");
-	*atorp++=t;
-	if(cursubid >= NRange)
-		*subidp++= -1;
-	else
-		*subidp++=cursubid;
-}
-
-Node *
-popand(int op)
-{
-	char buf[64];
-
-	if(andp <= &andstack[0])
-		if(op){
-			sprint(buf, "missing operand for %c", op);
-			regerror(buf);
-		}else
-			regerror("malformed regexp");
-	return --andp;
-}
-
-int
-popator()
-{
-	if(atorp <= &atorstack[0])
-		error("operator stack underflow");
-	--subidp;
-	return *--atorp;
-}
-
-void
-evaluntil(int pri)
-{
-	Node *op1, *op2, *t;
-	Inst *inst1, *inst2;
-
-	while(pri==RBRA || atorp[-1]>=pri){
-		switch(popator()){
-		case LBRA:
-			op1 = popand('(');
-			inst2 = newinst(RBRA);
-			inst2->subid = *subidp;
-			op1->last->next = inst2;
-			inst1 = newinst(LBRA);
-			inst1->subid = *subidp;
-			inst1->next = op1->first;
-			pushand(inst1, inst2);
-			return;		/* must have been RBRA */
-		default:
-			error("unknown regexp operator");
-			break;
-		case OR:
-			op2 = popand('|');
-			op1 = popand('|');
-			inst2 = newinst(NOP);
-			op2->last->next = inst2;
-			op1->last->next = inst2;
-			inst1 = newinst(OR);
-			inst1->right = op1->first;
-			inst1->left = op2->first;
-			pushand(inst1, inst2);
-			break;
-		case CAT:
-			op2 = popand(0);
-			op1 = popand(0);
-			if(backwards && op2->first->type!=END){
-				t = op1;
-				op1 = op2;
-				op2 = t;
-			}
-			op1->last->next = op2->first;
-			pushand(op1->first, op2->last);
-			break;
-		case STAR:
-			op2 = popand('*');
-			inst1 = newinst(OR);
-			op2->last->next = inst1;
-			inst1->right = op2->first;
-			pushand(inst1, inst1);
-			break;
-		case PLUS:
-			op2 = popand('+');
-			inst1 = newinst(OR);
-			op2->last->next = inst1;
-			inst1->right = op2->first;
-			pushand(op2->first, inst1);
-			break;
-		case QUEST:
-			op2 = popand('?');
-			inst1 = newinst(OR);
-			inst2 = newinst(NOP);
-			inst1->left = inst2;
-			inst1->right = op2->first;
-			op2->last->next = inst2;
-			pushand(inst1, inst2);
-			break;
-		}
-	}
-}
-
-
-void
-optimize(Inst *start)
-{
-	Inst *inst, *target;
-
-	for(inst=start; inst->type!=END; inst++){
-		target = inst->next;
-		while(target->type == NOP)
-			target = target->next;
-		inst->next = target;
-	}
-}
-
-void
-startlex(Rune *s)
-{
-	exprp = s;
-	nbra = 0;
-}
-
-
-int
-lex(void){
-	int c;
-
-	c = *exprp++;
-	switch(c){
-	case '\\':
-		if(*exprp)
-			if((c= *exprp++)=='n')
-				c='\n';
-		break;
-	case 0:
-		c = END;
-		--exprp;	/* In case we come here again */
-		break;
-	case '*':
-		c = STAR;
-		break;
-	case '?':
-		c = QUEST;
-		break;
-	case '+':
-		c = PLUS;
-		break;
-	case '|':
-		c = OR;
-		break;
-	case '.':
-		c = ANY;
-		break;
-	case '(':
-		c = LBRA;
-		break;
-	case ')':
-		c = RBRA;
-		break;
-	case '^':
-		c = BOL;
-		break;
-	case '$':
-		c = EOL;
-		break;
-	case '[':
-		c = CCLASS;
-		bldcclass();
-		break;
-	}
-	return c;
-}
-
-int
-nextrec(void)
-{
-	if(exprp[0]==0 || (exprp[0]=='\\' && exprp[1]==0))
-		regerror("malformed `[]'");
-	if(exprp[0] == '\\'){
-		exprp++;
-		if(*exprp=='n'){
-			exprp++;
-			return '\n';
-		}
-		return *exprp++|(Runemask+1);
-	}
-	return *exprp++;
-}
-
-void
-bldcclass(void)
-{
-	int c1, c2, n, na;
-	Rune *classp;
-
-	classp = runemalloc(DCLASS);
-	n = 0;
-	na = DCLASS;
-	/* we have already seen the '[' */
-	if(*exprp == '^'){
-		classp[n++] = '\n';	/* don't match newline in negate case */
-		negateclass = TRUE;
-		exprp++;
-	}else
-		negateclass = FALSE;
-	while((c1 = nextrec()) != ']'){
-		if(c1 == '-'){
-    Error:
-			free(classp);
-			regerror("malformed `[]'");
-		}
-		if(n+4 >= na){		/* 3 runes plus NUL */
-			na += DCLASS;
-			classp = runerealloc(classp, na);
-		}
-		if(*exprp == '-'){
-			exprp++;	/* eat '-' */
-			if((c2 = nextrec()) == ']')
-				goto Error;
-			classp[n+0] = Runemax;
-			classp[n+1] = c1 & Runemask;
-			classp[n+2] = c2 & Runemask;
-			n += 3;
-		}else
-			classp[n++] = c1 & Runemask;
-	}
-	classp[n] = 0;
-	if(nclass == Nclass){
-		Nclass += DCLASS;
-		class = realloc(class, Nclass*sizeof(Rune*));
-	}
-	class[nclass++] = classp;
-}
-
-int
-classmatch(int classno, int c, int negate)
-{
-	Rune *p;
-
-	p = class[classno];
-	while(*p){
-		if(*p == Runemax){
-			if(p[1]<=c && c<=p[2])
-				return !negate;
-			p += 3;
-		}else if(*p++ == c)
-			return !negate;
-	}
-	return negate;
-}
-
-/*
- * Note optimization in addinst:
- * 	*l must be pending when addinst called; if *l has been looked
- *		at already, the optimization is a bug.
- */
-int
-addinst(Ilist *l, Inst *inst, Rangeset *sep)
-{
-	Ilist *p;
-
-	for(p = l; p->inst; p++){
-		if(p->inst==inst){
-			if((sep)->r[0].q0 < p->se.r[0].q0)
-				p->se= *sep;	/* this would be bug */
-			return 0;	/* It's already there */
-		}
-	}
-	p->inst = inst;
-	p->se= *sep;
-	(p+1)->inst = nil;
-	return 1;
-}
-
-int
-rxnull(void)
-{
-	return startinst==nil || bstartinst==nil;
-}
-
-/* either t!=nil or r!=nil, and we match the string in the appropriate place */
-int
-rxexecute(Text *t, Rune *r, uint startp, uint eof, Rangeset *rp)
-{
-	int flag;
-	Inst *inst;
-	Ilist *tlp;
-	uint p;
-	int nnl, ntl;
-	int nc, c;
-	int wrapped;
-	int startchar;
-
-	flag = 0;
-	p = startp;
-	startchar = 0;
-	wrapped = 0;
-	nnl = 0;
-	if(startinst->type<OPERATOR)
-		startchar = startinst->type;
-	list[0][0].inst = list[1][0].inst = nil;
-	sel.r[0].q0 = -1;
-	if(t != nil)
-		nc = t->file->nc;
-	else
-		nc = runestrlen(r);
-	/* Execute machine once for each character */
-	for(;;p++){
-	doloop:
-		if(p>=eof || p>=nc){
-			switch(wrapped++){
-			case 0:		/* let loop run one more click */
-			case 2:
-				break;
-			case 1:		/* expired; wrap to beginning */
-				if(sel.r[0].q0>=0 || eof!=Infinity)
-					goto Return;
-				list[0][0].inst = list[1][0].inst = nil;
-				p = 0;
-				goto doloop;
-			default:
-				goto Return;
-			}
-			c = 0;
-		}else{
-			if(((wrapped && p>=startp) || sel.r[0].q0>0) && nnl==0)
-				break;
-			if(t != nil)
-				c = textreadc(t, p);
-			else
-				c = r[p];
-		}
-		/* fast check for first char */
-		if(startchar && nnl==0 && c!=startchar)
-			continue;
-		tl = list[flag];
-		nl = list[flag^=1];
-		nl->inst = nil;
-		ntl = nnl;
-		nnl = 0;
-		if(sel.r[0].q0<0 && (!wrapped || p<startp || startp==eof)){
-			/* Add first instruction to this list */
-			sempty.r[0].q0 = p;
-			if(addinst(tl, startinst, &sempty))
-			if(++ntl >= NLIST){
-	Overflow:
-				warning(nil, "regexp list overflow\n");
-				sel.r[0].q0 = -1;
-				goto Return;
-			}
-		}
-		/* Execute machine until this list is empty */
-		for(tlp = tl; inst = tlp->inst; tlp++){	/* assignment = */
-	Switchstmt:
-			switch(inst->type){
-			default:	/* regular character */
-				if(inst->type==c){
-	Addinst:
-					if(addinst(nl, inst->next, &tlp->se))
-					if(++nnl >= NLIST)
-						goto Overflow;
-				}
-				break;
-			case LBRA:
-				if(inst->subid>=0)
-					tlp->se.r[inst->subid].q0 = p;
-				inst = inst->next;
-				goto Switchstmt;
-			case RBRA:
-				if(inst->subid>=0)
-					tlp->se.r[inst->subid].q1 = p;
-				inst = inst->next;
-				goto Switchstmt;
-			case ANY:
-				if(c!='\n')
-					goto Addinst;
-				break;
-			case BOL:
-				if(p==0 || (t!=nil && textreadc(t, p-1)=='\n') || (r!=nil && r[p-1]=='\n')){
-	Step:
-					inst = inst->next;
-					goto Switchstmt;
-				}
-				break;
-			case EOL:
-				if(c == '\n')
-					goto Step;
-				break;
-			case CCLASS:
-				if(c>=0 && classmatch(inst->class, c, 0))
-					goto Addinst;
-				break;
-			case NCCLASS:
-				if(c>=0 && classmatch(inst->class, c, 1))
-					goto Addinst;
-				break;
-			case OR:
-				/* evaluate right choice later */
-				if(addinst(tlp, inst->right, &tlp->se))
-				if(++ntl >= NLIST)
-					goto Overflow;
-				/* efficiency: advance and re-evaluate */
-				inst = inst->left;
-				goto Switchstmt;
-			case END:	/* Match! */
-				tlp->se.r[0].q1 = p;
-				newmatch(&tlp->se);
-				break;
-			}
-		}
-	}
-    Return:
-	*rp = sel;
-	return sel.r[0].q0 >= 0;
-}
-
-void
-newmatch(Rangeset *sp)
-{
-	if(sel.r[0].q0<0 || sp->r[0].q0<sel.r[0].q0 ||
-	   (sp->r[0].q0==sel.r[0].q0 && sp->r[0].q1>sel.r[0].q1))
-		sel = *sp;
-}
-
-int
-rxbexecute(Text *t, uint startp, Rangeset *rp)
-{
-	int flag;
-	Inst *inst;
-	Ilist *tlp;
-	int p;
-	int nnl, ntl;
-	int c;
-	int wrapped;
-	int startchar;
-
-	flag = 0;
-	nnl = 0;
-	wrapped = 0;
-	p = startp;
-	startchar = 0;
-	if(bstartinst->type<OPERATOR)
-		startchar = bstartinst->type;
-	list[0][0].inst = list[1][0].inst = nil;
-	sel.r[0].q0= -1;
-	/* Execute machine once for each character, including terminal NUL */
-	for(;;--p){
-	doloop:
-		if(p <= 0){
-			switch(wrapped++){
-			case 0:		/* let loop run one more click */
-			case 2:
-				break;
-			case 1:		/* expired; wrap to end */
-				if(sel.r[0].q0>=0)
-					goto Return;
-				list[0][0].inst = list[1][0].inst = nil;
-				p = t->file->nc;
-				goto doloop;
-			case 3:
-			default:
-				goto Return;
-			}
-			c = 0;
-		}else{
-			if(((wrapped && p<=startp) || sel.r[0].q0>0) && nnl==0)
-				break;
-			c = textreadc(t, p-1);
-		}
-		/* fast check for first char */
-		if(startchar && nnl==0 && c!=startchar)
-			continue;
-		tl = list[flag];
-		nl = list[flag^=1];
-		nl->inst = nil;
-		ntl = nnl;
-		nnl = 0;
-		if(sel.r[0].q0<0 && (!wrapped || p>startp)){
-			/* Add first instruction to this list */
-			/* the minus is so the optimizations in addinst work */
-			sempty.r[0].q0 = -p;
-			if(addinst(tl, bstartinst, &sempty))
-			if(++ntl >= NLIST){
-	Overflow:
-				warning(nil, "regexp list overflow\n");
-				sel.r[0].q0 = -1;
-				goto Return;
-			}
-		}
-		/* Execute machine until this list is empty */
-		for(tlp = tl; inst = tlp->inst; tlp++){	/* assignment = */
-	Switchstmt:
-			switch(inst->type){
-			default:	/* regular character */
-				if(inst->type == c){
-	Addinst:
-					if(addinst(nl, inst->next, &tlp->se))
-					if(++nnl >= NLIST)
-						goto Overflow;
-				}
-				break;
-			case LBRA:
-				if(inst->subid>=0)
-					tlp->se.r[inst->subid].q0 = p;
-				inst = inst->next;
-				goto Switchstmt;
-			case RBRA:
-				if(inst->subid >= 0)
-					tlp->se.r[inst->subid].q1 = p;
-				inst = inst->next;
-				goto Switchstmt;
-			case ANY:
-				if(c != '\n')
-					goto Addinst;
-				break;
-			case BOL:
-				if(c=='\n' || p==0){
-	Step:
-					inst = inst->next;
-					goto Switchstmt;
-				}
-				break;
-			case EOL:
-				if(p<t->file->nc && textreadc(t, p)=='\n')
-					goto Step;
-				break;
-			case CCLASS:
-				if(c>0 && classmatch(inst->class, c, 0))
-					goto Addinst;
-				break;
-			case NCCLASS:
-				if(c>0 && classmatch(inst->class, c, 1))
-					goto Addinst;
-				break;
-			case OR:
-				/* evaluate right choice later */
-				if(addinst(tl, inst->right, &tlp->se))
-				if(++ntl >= NLIST)
-					goto Overflow;
-				/* efficiency: advance and re-evaluate */
-				inst = inst->left;
-				goto Switchstmt;
-			case END:	/* Match! */
-				tlp->se.r[0].q0 = -tlp->se.r[0].q0; /* minus sign */
-				tlp->se.r[0].q1 = p;
-				bnewmatch(&tlp->se);
-				break;
-			}
-		}
-	}
-    Return:
-	*rp = sel;
-	return sel.r[0].q0 >= 0;
-}
-
-void
-bnewmatch(Rangeset *sp)
-{
-        int  i;
-
-        if(sel.r[0].q0<0 || sp->r[0].q0>sel.r[0].q1 || (sp->r[0].q0==sel.r[0].q1 && sp->r[0].q1<sel.r[0].q0))
-                for(i = 0; i<NRange; i++){       /* note the reversal; q0<=q1 */
-                        sel.r[i].q0 = sp->r[i].q1;
-                        sel.r[i].q1 = sp->r[i].q0;
-                }
-}
--- a/rows.c
+++ /dev/null
@@ -1,736 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <thread.h>
-#include <cursor.h>
-#include <mouse.h>
-#include <keyboard.h>
-#include <frame.h>
-#include <fcall.h>
-#include <bio.h>
-#include <plumb.h>
-#include "dat.h"
-#include "fns.h"
-
-void
-rowinit(Row *row, Rectangle r)
-{
-	Rectangle r1;
-	Text *t;
-
-	draw(screen, r, display->white, nil, ZP);
-	row->r = r;
-	row->col = nil;
-	row->ncol = 0;
-	r1 = r;
-	r1.max.y = r1.min.y + font->height;
-	t = &row->tag;
-	textinit(t, fileaddtext(nil, t), r1, rfget(FALSE, FALSE, FALSE, nil), tagcols);
-	t->what = Rowtag;
-	t->row = row;
-	t->w = nil;
-	t->col = nil;
-	r1.min.y = r1.max.y;
-	r1.max.y += Border;
-	draw(screen, r1, display->black, nil, ZP);
-	textinsert(t, 0, L"Newcol Kill Putall Dump Exit ", 29, TRUE);
-	textsetselect(t, t->file->nc, t->file->nc);
-}
-
-Column*
-rowadd(Row *row, Column *c, int x)
-{
-	Rectangle r, r1;
-	Column *d;
-	int i;
-
-	d = nil;
-	r = row->r;
-	r.min.y = row->tag.r.max.y+Border;
-	if(x<r.min.x && row->ncol>0){	/*steal 40% of last column by default */
-		d = row->col[row->ncol-1];
-		x = d->r.min.x + 3*Dx(d->r)/5;
-	}
-	/* look for column we'll land on */
-	for(i=0; i<row->ncol; i++){
-		d = row->col[i];
-		if(x < d->r.max.x)
-			break;
-	}
-	if(row->ncol > 0){
-		if(i < row->ncol)
-			i++;	/* new column will go after d */
-		r = d->r;
-		if(Dx(r) < 100)
-			return nil;
-		draw(screen, r, display->white, nil, ZP);
-		r1 = r;
-		r1.max.x = min(x, r.max.x-50);
-		if(Dx(r1) < 50)
-			r1.max.x = r1.min.x+50;
-		colresize(d, r1);
-		r1.min.x = r1.max.x;
-		r1.max.x = r1.min.x+Border;
-		draw(screen, r1, display->black, nil, ZP);
-		r.min.x = r1.max.x;
-	}
-	if(c == nil){
-		c = emalloc(sizeof(Column));
-		colinit(c, r);
-		incref(&reffont);
-	}else
-		colresize(c, r);
-	c->row = row;
-	c->tag.row = row;
-	row->col = realloc(row->col, (row->ncol+1)*sizeof(Column*));
-	memmove(row->col+i+1, row->col+i, (row->ncol-i)*sizeof(Column*));
-	row->col[i] = c;
-	row->ncol++;
-	clearmouse();
-	return c;
-}
-
-void
-rowresize(Row *row, Rectangle r)
-{
-	int i, dx, odx;
-	Rectangle r1, r2;
-	Column *c;
-
-	dx = Dx(r);
-	odx = Dx(row->r);
-	row->r = r;
-	r1 = r;
-	r1.max.y = r1.min.y + font->height;
-	textresize(&row->tag, r1);
-	r1.min.y = r1.max.y;
-	r1.max.y += Border;
-	draw(screen, r1, display->black, nil, ZP);
-	r.min.y = r1.max.y;
-	r1 = r;
-	r1.max.x = r1.min.x;
-	for(i=0; i<row->ncol; i++){
-		c = row->col[i];
-		r1.min.x = r1.max.x;
-		if(i == row->ncol-1)
-			r1.max.x = r.max.x;
-		else
-			r1.max.x = r1.min.x+Dx(c->r)*dx/odx;
-		if(i > 0){
-			r2 = r1;
-			r2.max.x = r2.min.x+Border;
-			draw(screen, r2, display->black, nil, ZP);
-			r1.min.x = r2.max.x;
-		}
-		colresize(c, r1);
-	}
-}
-
-void
-rowdragcol(Row *row, Column *c, int)
-{
-	Rectangle r;
-	int i, b, x;
-	Point p, op;
-	Column *d;
-
-	clearmouse();
-	setcursor(mousectl, &boxcursor);
-	b = mouse->buttons;
-	op = mouse->xy;
-	while(mouse->buttons == b)
-		readmouse(mousectl);
-	setcursor(mousectl, nil);
-	if(mouse->buttons){
-		while(mouse->buttons)
-			readmouse(mousectl);
-		return;
-	}
-
-	for(i=0; i<row->ncol; i++)
-		if(row->col[i] == c)
-			goto Found;
-	error("can't find column");
-
-  Found:
-	p = mouse->xy;
-	if((abs(p.x-op.x)<5 && abs(p.y-op.y)<5))
-		return;
-	if((i>0 && p.x<row->col[i-1]->r.min.x) || (i<row->ncol-1 && p.x>c->r.max.x)){
-		/* shuffle */
-		x = c->r.min.x;
-		rowclose(row, c, FALSE);
-		if(rowadd(row, c, p.x) == nil)	/* whoops! */
-		if(rowadd(row, c, x) == nil)		/* WHOOPS! */
-		if(rowadd(row, c, -1)==nil){		/* shit! */
-			rowclose(row, c, TRUE);
-			return;
-		}
-		colmousebut(c);
-		return;
-	}
-	if(i == 0)
-		return;
-	d = row->col[i-1];
-	if(p.x < d->r.min.x+80+Scrollwid)
-		p.x = d->r.min.x+80+Scrollwid;
-	if(p.x > c->r.max.x-80-Scrollwid)
-		p.x = c->r.max.x-80-Scrollwid;
-	r = d->r;
-	r.max.x = c->r.max.x;
-	draw(screen, r, display->white, nil, ZP);
-	r.max.x = p.x;
-	colresize(d, r);
-	r = c->r;
-	r.min.x = p.x;
-	r.max.x = r.min.x;
-	r.max.x += Border;
-	draw(screen, r, display->black, nil, ZP);
-	r.min.x = r.max.x;
-	r.max.x = c->r.max.x;
-	colresize(c, r);
-	colmousebut(c);
-}
-
-void
-rowclose(Row *row, Column *c, int dofree)
-{
-	Rectangle r;
-	int i;
-
-	for(i=0; i<row->ncol; i++)
-		if(row->col[i] == c)
-			goto Found;
-	error("can't find column");
-  Found:
-	r = c->r;
-	if(dofree)
-		colcloseall(c);
-	memmove(row->col+i, row->col+i+1, (row->ncol-i)*sizeof(Column*));
-	row->ncol--;
-	row->col = realloc(row->col, row->ncol*sizeof(Column*));
-	if(row->ncol == 0){
-		draw(screen, r, display->white, nil, ZP);
-		return;
-	}
-	if(i == row->ncol){		/* extend last column right */
-		c = row->col[i-1];
-		r.min.x = c->r.min.x;
-		r.max.x = row->r.max.x;
-	}else{			/* extend next window left */
-		c = row->col[i];
-		r.max.x = c->r.max.x;
-	}
-	draw(screen, r, display->white, nil, ZP);
-	colresize(c, r);
-}
-
-Column*
-rowwhichcol(Row *row, Point p)
-{
-	int i;
-	Column *c;
-
-	for(i=0; i<row->ncol; i++){
-		c = row->col[i];
-		if(ptinrect(p, c->r))
-			return c;
-	}
-	return nil;
-}
-
-Text*
-rowwhich(Row *row, Point p)
-{
-	Column *c;
-
-	if(ptinrect(p, row->tag.all))
-		return &row->tag;
-	c = rowwhichcol(row, p);
-	if(c)
-		return colwhich(c, p);
-	return nil;
-}
-
-Text*
-rowtype(Row *row, Rune r, Point p)
-{
-	Window *w;
-	Text *t;
-
-	clearmouse();
-	qlock(row);
-	if(bartflag)
-		t = barttext;
-	else
-		t = rowwhich(row, p);
-	if(t!=nil && !(t->what==Tag && ptinrect(p, t->scrollr))){
-		w = t->w;
-		if(w == nil)
-			texttype(t, r);
-		else{
-			winlock(w, 'K');
-			wintype(w, t, r);
-			winunlock(w);
-		}
-	}
-	qunlock(row);
-	return t;
-}
-
-int
-rowclean(Row *row)
-{
-	int clean;
-	int i;
-
-	clean = TRUE;
-	for(i=0; i<row->ncol; i++)
-		clean &= colclean(row->col[i]);
-	return clean;
-}
-
-void
-rowdump(Row *row, char *file)
-{
-	int i, j, fd, m, n, start, dumped;
-	uint q0, q1;
-	Biobuf *b;
-	char *buf, *a, *fontname;
-	Rune *r;
-	Column *c;
-	Window *w, *w1;
-	Text *t;
-
-	if(row->ncol == 0)
-		return;
-	buf = fbufalloc();
-	if(file == nil){
-		if(home == nil){
-			warning(nil, "can't find file for dump: $home not defined\n");
-			goto Rescue;
-		}
-		sprint(buf, "%s/acme.dump", home);
-		file = buf;
-	}
-	fd = create(file, OWRITE, 0600);
-	if(fd < 0){
-		warning(nil, "can't open %s: %r\n", file);
-		goto Rescue;
-	}
-	b = emalloc(sizeof(Biobuf));
-	Binit(b, fd, OWRITE);
-	r = fbufalloc();
-	Bprint(b, "%s\n", wdir);
-	Bprint(b, "%s\n", fontnames[0]);
-	Bprint(b, "%s\n", fontnames[1]);
-	for(i=0; i<row->ncol; i++){
-		c = row->col[i];
-		Bprint(b, "%11d", 100*(c->r.min.x-row->r.min.x)/Dx(row->r));
-		if(i == row->ncol-1)
-			Bputc(b, '\n');
-		else
-			Bputc(b, ' ');
-	}
-	for(i=0; i<row->ncol; i++){
-		c = row->col[i];
-		for(j=0; j<c->nw; j++)
-			c->w[j]->body.file->dumpid = 0;
-	}
-	for(i=0; i<row->ncol; i++){
-		c = row->col[i];
-		for(j=0; j<c->nw; j++){
-			w = c->w[j];
-			wincommit(w, &w->tag);
-			t = &w->body;
-			/* windows owned by others get special treatment */
-			if(w->nopen[QWevent] > 0)
-				if(w->dumpstr == nil)
-					continue;
-			/* zeroxes of external windows are tossed */
-			if(t->file->ntext > 1)
-				for(n=0; n<t->file->ntext; n++){
-					w1 = t->file->text[n]->w;
-					if(w == w1)
-						continue;
-					if(w1->nopen[QWevent])
-						goto Continue2;
-				}
-			fontname = "";
-			if(t->reffont->f != font)
-				fontname = t->reffont->f->name;
-			if(t->file->nname)
-				a = runetobyte(t->file->name, t->file->nname);
-			else
-				a = emalloc(1);
-			if(t->file->dumpid){
-				dumped = FALSE;
-				Bprint(b, "x%11d %11d %11d %11d %11d %s\n", i, t->file->dumpid,
-					w->body.q0, w->body.q1,
-					100*(w->r.min.y-c->r.min.y)/Dy(c->r),
-					fontname);
-			}else if(w->dumpstr){
-				dumped = FALSE;
-				Bprint(b, "e%11d %11d %11d %11d %11d %s\n", i, t->file->dumpid,
-					0, 0,
-					100*(w->r.min.y-c->r.min.y)/Dy(c->r),
-					fontname);
-			}else if((w->dirty==FALSE && access(a, 0)==0) || w->isdir){
-				dumped = FALSE;
-				t->file->dumpid = w->id;
-				Bprint(b, "f%11d %11d %11d %11d %11d %s\n", i, w->id,
-					w->body.q0, w->body.q1,
-					100*(w->r.min.y-c->r.min.y)/Dy(c->r),
-					fontname);
-			}else{
-				dumped = TRUE;
-				t->file->dumpid = w->id;
-				Bprint(b, "F%11d %11d %11d %11d %11d %11d %s\n", i, j,
-					w->body.q0, w->body.q1,
-					100*(w->r.min.y-c->r.min.y)/Dy(c->r),
-					w->body.file->nc, fontname);
-			}
-			free(a);
-			winctlprint(w, buf, 0);
-			Bwrite(b, buf, strlen(buf));
-			m = min(RBUFSIZE, w->tag.file->nc);
-			bufread(w->tag.file, 0, r, m);
-			n = 0;
-			while(n<m) {
-				start = n;
-				while(n<m && r[n]!='\n')
-					n++;
-				Bprint(b, "%.*S", n-start, r+start);
-				if(n<m) {
-					Bputc(b, 0xff);	// \n in tag becomes 0xff byte (invalid UTF)
-					n++;
-				}
-			}
-			Bprint(b, "\n");
-			if(dumped){
-				q0 = 0;
-				q1 = t->file->nc;
-				while(q0 < q1){
-					n = q1 - q0;
-					if(n > BUFSIZE/UTFmax)
-						n = BUFSIZE/UTFmax;
-					bufread(t->file, q0, r, n);
-					Bprint(b, "%.*S", n, r);
-					q0 += n;
-				}
-			}
-			if(w->dumpstr){
-				if(w->dumpdir)
-					Bprint(b, "%s\n%s\n", w->dumpdir, w->dumpstr);
-				else
-					Bprint(b, "\n%s\n", w->dumpstr);
-			}
-    Continue2:;
-		}
-	}
-	Bterm(b);
-	close(fd);
-	free(b);
-	fbuffree(r);
-
-   Rescue:
-	fbuffree(buf);
-}
-
-static
-char*
-rdline(Biobuf *b, int *linep)
-{
-	char *l;
-
-	l = Brdline(b, '\n');
-	if(l)
-		(*linep)++;
-	return l;
-}
-
-/*
- * Get font names from load file so we don't load fonts we won't use
- */
-void
-rowloadfonts(char *file)
-{
-	int i;
-	Biobuf *b;
-	char *l;
-
-	b = Bopen(file, OREAD);
-	if(b == nil)
-		return;
-	/* current directory */
-	l = Brdline(b, '\n');
-	if(l == nil)
-		goto Return;
-	/* global fonts */
-	for(i=0; i<2; i++){
-		l = Brdline(b, '\n');
-		if(l == nil)
-			goto Return;
-		l[Blinelen(b)-1] = 0;
-		if(*l && strcmp(l, fontnames[i])!=0){
-			free(fontnames[i]);
-			fontnames[i] = estrdup(l);
-		}
-	}
-    Return:
-	Bterm(b);
-}
-
-int
-rowload(Row *row, char *file, int initing)
-{
-	int i, j, line, percent, y, nr, nfontr, n, ns, ndumped, dumpid, x, fd;
-	Biobuf *b, *bout;
-	char *buf, *l, *t, *fontname;
-	Rune *r, rune, *fontr;
-	Column *c, *c1, *c2;
-	uint q0, q1;
-	Rectangle r1, r2;
-	Window *w;
-
-	buf = fbufalloc();
-	if(file == nil){
-		if(home == nil){
-			warning(nil, "can't find file for load: $home not defined\n");
-			goto Rescue1;
-		}
-		sprint(buf, "%s/acme.dump", home);
-		file = buf;
-	}
-	b = Bopen(file, OREAD);
-	if(b == nil){
-		warning(nil, "can't open load file %s: %r\n", file);
-		goto Rescue1;
-	}
-	/* current directory */
-	line = 0;
-	l = rdline(b, &line);
-	if(l == nil)
-		goto Rescue2;
-	l[Blinelen(b)-1] = 0;
-	if(chdir(l) < 0){
-		warning(nil, "can't chdir %s\n", l);
-		goto Rescue2;
-	}
-	/* global fonts */
-	for(i=0; i<2; i++){
-		l = rdline(b, &line);
-		if(l == nil)
-			goto Rescue2;
-		l[Blinelen(b)-1] = 0;
-		if(*l && strcmp(l, fontnames[i])!=0)
-			rfget(i, TRUE, i==0 && initing, l);
-	}
-	if(initing && row->ncol==0)
-		rowinit(row, screen->clipr);
-	l = rdline(b, &line);
-	if(l == nil)
-		goto Rescue2;
-	j = Blinelen(b)/12;
-	if(j<=0 || j>10)
-		goto Rescue2;
-	for(i=0; i<j; i++){
-		percent = atoi(l+i*12);
-		if(percent<0 || percent>=100)
-			goto Rescue2;
-		x = row->r.min.x+percent*Dx(row->r)/100;
-		if(i < row->ncol){
-			if(i == 0)
-				continue;
-			c1 = row->col[i-1];
-			c2 = row->col[i];
-			r1 = c1->r;
-			r2 = c2->r;
-			r1.max.x = x;
-			r2.min.x = x+Border;
-			if(Dx(r1) < 50 || Dx(r2) < 50)
-				continue;
-			draw(screen, Rpt(r1.min, r2.max), display->white, nil, ZP);
-			colresize(c1, r1);
-			colresize(c2, r2);
-			r2.min.x = x;
-			r2.max.x = x+Border;
-			draw(screen, r2, display->black, nil, ZP);
-		}
-		if(i >= row->ncol)
-			rowadd(row, nil, x);
-	}
-	for(;;){
-		l = rdline(b, &line);
-		if(l == nil)
-			break;
-		dumpid = 0;
-		switch(l[0]){
-		case 'e':
-			if(Blinelen(b) < 1+5*12+1)
-				goto Rescue2;
-			l = rdline(b, &line);	/* ctl line; ignored */
-			if(l == nil)
-				goto Rescue2;
-			l = rdline(b, &line);	/* directory */
-			if(l == nil)
-				goto Rescue2;
-			l[Blinelen(b)-1] = 0;
-			if(*l == '\0'){
-				if(home == nil)
-					r = bytetorune("./", &nr);
-				else{
-					t = emalloc(strlen(home)+1+1);
-					sprint(t, "%s/", home);
-					r = bytetorune(t, &nr);
-					free(t);
-				}
-			}else
-				r = bytetorune(l, &nr);
-			l = rdline(b, &line);	/* command */
-			if(l == nil)
-				goto Rescue2;
-			t = emalloc(Blinelen(b)+1);
-			memmove(t, l, Blinelen(b));
-			run(nil, t, r, nr, TRUE, nil, nil, FALSE);
-			/* r is freed in run() */
-			continue;
-		case 'f':
-			if(Blinelen(b) < 1+5*12+1)
-				goto Rescue2;
-			fontname = l+1+5*12;
-			ndumped = -1;
-			break;
-		case 'F':
-			if(Blinelen(b) < 1+6*12+1)
-				goto Rescue2;
-			fontname = l+1+6*12;
-			ndumped = atoi(l+1+5*12+1);
-			break;
-		case 'x':
-			if(Blinelen(b) < 1+5*12+1)
-				goto Rescue2;
-			fontname = l+1+5*12;
-			ndumped = -1;
-			dumpid = atoi(l+1+1*12);
-			break;
-		default:
-			goto Rescue2;
-		}
-		l[Blinelen(b)-1] = 0;
-		fontr = nil;
-		nfontr = 0;
-		if(*fontname)
-			fontr = bytetorune(fontname, &nfontr);
-		i = atoi(l+1+0*12);
-		j = atoi(l+1+1*12);
-		q0 = atoi(l+1+2*12);
-		q1 = atoi(l+1+3*12);
-		percent = atoi(l+1+4*12);
-		if(i<0 || i>10)
-			goto Rescue2;
-		if(i > row->ncol)
-			i = row->ncol;
-		c = row->col[i];
-		y = c->r.min.y+(percent*Dy(c->r))/100;
-		if(y<c->r.min.y || y>=c->r.max.y)
-			y = -1;
-		if(dumpid == 0)
-			w = coladd(c, nil, nil, y);
-		else
-			w = coladd(c, nil, lookid(dumpid, TRUE), y);
-		if(w == nil)
-			continue;
-		w->dumpid = j;
-		l = rdline(b, &line);
-		if(l == nil)
-			goto Rescue2;
-		l[Blinelen(b)-1] = 0;
-		/* convert 0xff in multiline tag back to \n */
-		for(i=0; l[i]!=0; i++)
-			if((uchar)l[i] == 0xff)
-				l[i] = '\n';
-		r = bytetorune(l+5*12, &nr);
-		ns = -1;
-		for(n=0; n<nr; n++){
-			if(r[n] == '/')
-				ns = n;
-			if(r[n] == ' ')
-				break;
-		}
-		if(dumpid == 0)
-			winsetname(w, r, n);
-		for(; n<nr; n++)
-			if(r[n] == '|')
-				break;
-		wincleartag(w);
-		textinsert(&w->tag, w->tag.file->nc, r+n+1, nr-(n+1), TRUE);
-		if(ndumped >= 0){
-			/* simplest thing is to put it in a file and load that */
-			sprint(buf, "/tmp/d%d.%.4sacme", getpid(), user);
-			fd = create(buf, OWRITE|ORCLOSE, 0600);
-			if(fd < 0){
-				free(r);
-				warning(nil, "can't create temp file: %r\n");
-				goto Rescue2;
-			}
-			bout = emalloc(sizeof(Biobuf));
-			Binit(bout, fd, OWRITE);
-			for(n=0; n<ndumped; n++){
-				rune = Bgetrune(b);
-				if(rune == '\n')
-					line++;
-				if(rune == (Rune)Beof){
-					free(r);
-					Bterm(bout);
-					free(bout);
-					close(fd);
-					goto Rescue2;
-				}
-				Bputrune(bout, rune);
-			}
-			Bterm(bout);
-			free(bout);
-			textload(&w->body, 0, buf, 1);
-			close(fd);
-			w->body.file->mod = TRUE;
-			for(n=0; n<w->body.file->ntext; n++)
-				w->body.file->text[n]->w->dirty = TRUE;
-			winsettag(w);
-		}else if(dumpid==0 && r[ns+1]!='+' && r[ns+1]!='-')
-			get(&w->body, nil, nil, FALSE, XXX, nil, 0);
-		if(fontr){
-			fontx(&w->body, nil, nil, 0, 0, fontr, nfontr);
-			free(fontr);
-		}
-		free(r);
-		if(q0>w->body.file->nc || q1>w->body.file->nc || q0>q1)
-			q0 = q1 = 0;
-		textshow(&w->body, q0, q1, 1);
-		w->maxlines = min(w->body.nlines, max(w->maxlines, w->body.maxlines));
-		xfidlog(w, "new");
-	}
-	Bterm(b);
-	fbuffree(buf);
-	return TRUE;
-
-Rescue2:
-	warning(nil, "bad load file %s:%d\n", file, line);
-	Bterm(b);
-Rescue1:
-	fbuffree(buf);
-	return FALSE;
-}
-
-void
-allwindows(void (*f)(Window*, void*), void *arg)
-{
-	int i, j;
-	Column *c;
-
-	for(i=0; i<row.ncol; i++){
-		c = row.col[i];
-		for(j=0; j<c->nw; j++)
-			(*f)(c->w[j], arg);
-	}
-}
--- a/scrl.c
+++ /dev/null
@@ -1,153 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <thread.h>
-#include <cursor.h>
-#include <mouse.h>
-#include <keyboard.h>
-#include <frame.h>
-#include <fcall.h>
-#include <plumb.h>
-#include "dat.h"
-#include "fns.h"
-
-static Image *scrtmp;
-
-static
-Rectangle
-scrpos(Rectangle r, uint p0, uint p1, uint tot)
-{
-	Rectangle q;
-	int h;
-
-	q = r;
-	h = q.max.y-q.min.y;
-	if(tot == 0)
-		return q;
-	if(tot > 1024*1024){
-		tot>>=10;
-		p0>>=10;
-		p1>>=10;
-	}
-	if(p0 > 0)
-		q.min.y += h*p0/tot;
-	if(p1 < tot)
-		q.max.y -= h*(tot-p1)/tot;
-	if(q.max.y < q.min.y+2){
-		if(q.min.y+2 <= r.max.y)
-			q.max.y = q.min.y+2;
-		else
-			q.min.y = q.max.y-2;
-	}
-	return q;
-}
-
-void
-scrlresize(void)
-{
-	freeimage(scrtmp);
-	scrtmp = allocimage(display, Rect(0, 0, 32, screen->r.max.y), screen->chan, 0, DNofill);
-	if(scrtmp == nil)
-		error("scroll alloc");
-}
-
-void
-textscrdraw(Text *t)
-{
-	Rectangle r, r1, r2;
-	Image *b;
-
-	if(t->w==nil || t!=&t->w->body)
-		return;
-	if(scrtmp == nil)
-		scrlresize();
-	r = t->scrollr;
-	b = scrtmp;
-	r1 = r;
-	r1.min.x = 0;
-	r1.max.x = Dx(r);
-	r2 = scrpos(r1, t->org, t->org+t->nchars, t->file->nc);
-	if(!eqrect(r2, t->lastsr)){
-		t->lastsr = r2;
-		draw(b, r1, t->cols[BORD], nil, ZP);
-		draw(b, r2, t->cols[BACK], nil, ZP);
-		r2.min.x = r2.max.x-1;
-		draw(b, r2, t->cols[BORD], nil, ZP);
-		draw(t->b, r, b, nil, Pt(0, r1.min.y));
-/*flushimage(display, 1);/*BUG?*/
-	}
-}
-
-void
-scrsleep(uint dt)
-{
-	Timer	*timer;
-	static Alt alts[3];
-
-	timer = timerstart(dt);
-	alts[0].c = timer->c;
-	alts[0].v = nil;
-	alts[0].op = CHANRCV;
-	alts[1].c = mousectl->c;
-	alts[1].v = &mousectl->Mouse;
-	alts[1].op = CHANRCV;
-	alts[2].op = CHANEND;
-	for(;;)
-		switch(alt(alts)){
-		case 0:
-			timerstop(timer);
-			return;
-		case 1:
-			timercancel(timer);
-			return;
-		}
-}
-
-void
-textscroll(Text *t, int but)
-{
-	uint p0, oldp0;
-	Rectangle s;
-	int y, my, h, first;
-
-	s = insetrect(t->scrollr, 1);
-	h = s.max.y-s.min.y;
-	oldp0 = ~0;
-	first = TRUE;
-	do{
-		flushimage(display, 1);
-		my = mouse->xy.y;
-		if(my < s.min.y)
-			my = s.min.y;
-		if(my >= s.max.y)
-			my = s.max.y;
-		if(but == 2){
-			y = my;
-			p0 = (vlong)t->file->nc*(y-s.min.y)/h;
-			if(p0 >= t->q1)
-				p0 = textbacknl(t, p0, 2);
-			if(oldp0 != p0)
-				textsetorigin(t, p0, FALSE);
-			oldp0 = p0;
-			readmouse(mousectl);
-			continue;
-		}
-		if(but == 1)
-			p0 = textbacknl(t, t->org, (my-s.min.y)/t->font->height);
-		else
-			p0 = t->org+frcharofpt(t, Pt(s.max.x, my));
-		if(oldp0 != p0)
-			textsetorigin(t, p0, TRUE);
-		oldp0 = p0;
-		/* debounce */
-		if(first){
-			flushimage(display, 1);
-			sleep(200);
-			nbrecv(mousectl->c, &mousectl->Mouse);
-			first = FALSE;
-		}
-		scrsleep(80);
-	}while(mouse->buttons & (1<<(but-1)));
-	while(mouse->buttons)
-		readmouse(mousectl);
-}
--- a/text.c
+++ /dev/null
@@ -1,1461 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <thread.h>
-#include <cursor.h>
-#include <mouse.h>
-#include <keyboard.h>
-#include <frame.h>
-#include <fcall.h>
-#include <plumb.h>
-#include <complete.h>
-#include "dat.h"
-#include "fns.h"
-
-Image	*tagcols[NCOL];
-Image	*textcols[NCOL];
-
-enum{
-	TABDIR = 3	/* width of tabs in directory windows */
-};
-
-void
-textinit(Text *t, File *f, Rectangle r, Reffont *rf, Image *cols[NCOL])
-{
-	t->file = f;
-	t->all = r;
-	t->scrollr = r;
-	t->scrollr.max.x = r.min.x+Scrollwid;
-	t->lastsr = nullrect;
-	r.min.x += Scrollwid+Scrollgap;
-	t->eq0 = ~0;
-	t->ncache = 0;
-	t->reffont = rf;
-	t->tabstop = maxtab;
-	memmove(t->Frame.cols, cols, sizeof t->Frame.cols);
-	textredraw(t, r, rf->f, screen, -1);
-}
-
-void
-textredraw(Text *t, Rectangle r, Font *f, Image *b, int odx)
-{
-	int maxt;
-	Rectangle rr;
-
-	frinit(t, r, f, b, t->Frame.cols);
-	rr = t->r;
-	rr.min.x -= Scrollwid+Scrollgap;	/* back fill to scroll bar */
-	draw(t->b, rr, t->cols[BACK], nil, ZP);
-	/* use no wider than 3-space tabs in a directory */
-	maxt = maxtab;
-	if(t->what == Body){
-		if(t->w->isdir)
-			maxt = min(TABDIR, maxtab);
-		else
-			maxt = t->tabstop;
-	}
-	t->maxtab = maxt*stringwidth(f, "0");
-	if(t->what==Body && t->w->isdir && odx!=Dx(t->all)){
-		if(t->maxlines > 0){
-			textreset(t);
-			textcolumnate(t, t->w->dlp,  t->w->ndl);
-			textshow(t, 0, 0, 1);
-		}
-	}else{
-		textfill(t);
-		textsetselect(t, t->q0, t->q1);
-	}
-}
-
-int
-textresize(Text *t, Rectangle r)
-{
-	int odx;
-
-	if(Dy(r) > 0)
-		r.max.y -= Dy(r)%t->font->height;
-	else
-		r.max.y = r.min.y;
-	odx = Dx(t->all);
-	t->all = r;
-	t->scrollr = r;
-	t->scrollr.max.x = r.min.x+Scrollwid;
-	t->lastsr = nullrect;
-	r.min.x += Scrollwid+Scrollgap;
-	frclear(t, 0);
-	textredraw(t, r, t->font, t->b, odx);
-	return r.max.y;
-}
-
-void
-textclose(Text *t)
-{
-	free(t->cache);
-	frclear(t, 1);
-	filedeltext(t->file, t);
-	t->file = nil;
-	rfclose(t->reffont);
-	if(argtext == t)
-		argtext = nil;
-	if(typetext == t)
-		typetext = nil;
-	if(seltext == t)
-		seltext = nil;
-	if(mousetext == t)
-		mousetext = nil;
-	if(barttext == t)
-		barttext = nil;
-}
-
-int
-dircmp(void *a, void *b)
-{
-	Dirlist *da, *db;
-	int i, n;
-
-	da = *(Dirlist**)a;
-	db = *(Dirlist**)b;
-	n = min(da->nr, db->nr);
-	i = memcmp(da->r, db->r, n*sizeof(Rune));
-	if(i)
-		return i;
-	return da->nr - db->nr;
-}
-
-void
-textcolumnate(Text *t, Dirlist **dlp, int ndl)
-{
-	int i, j, w, colw, mint, maxt, ncol, nrow;
-	Dirlist *dl;
-	uint q1;
-
-	if(t->file->ntext > 1)
-		return;
-	mint = stringwidth(t->font, "0");
-	/* go for narrower tabs if set more than 3 wide */
-	t->maxtab = min(maxtab, TABDIR)*mint;
-	maxt = t->maxtab;
-	colw = 0;
-	for(i=0; i<ndl; i++){
-		dl = dlp[i];
-		w = dl->wid;
-		if(maxt-w%maxt < mint || w%maxt==0)
-			w += mint;
-		if(w % maxt)
-			w += maxt-(w%maxt);
-		if(w > colw)
-			colw = w;
-	}
-	if(colw == 0)
-		ncol = 1;
-	else
-		ncol = max(1, Dx(t->r)/colw);
-	nrow = (ndl+ncol-1)/ncol;
-
-	q1 = 0;
-	for(i=0; i<nrow; i++){
-		for(j=i; j<ndl; j+=nrow){
-			dl = dlp[j];
-			fileinsert(t->file, q1, dl->r, dl->nr);
-			q1 += dl->nr;
-			if(j+nrow >= ndl)
-				break;
-			w = dl->wid;
-			if(maxt-w%maxt < mint){
-				fileinsert(t->file, q1, L"\t", 1);
-				q1++;
-				w += mint;
-			}
-			do{
-				fileinsert(t->file, q1, L"\t", 1);
-				q1++;
-				w += maxt-(w%maxt);
-			}while(w < colw);
-		}
-		fileinsert(t->file, q1, L"\n", 1);
-		q1++;
-	}
-}
-
-uint
-textload(Text *t, uint q0, char *file, int setqid)
-{
-	Rune *rp;
-	Dirlist *dl, **dlp;
-	int fd, i, j, n, ndl, nulls;
-	uint q, q1;
-	Dir *d, *dbuf;
-	char *tmp;
-	Text *u;
-
-	if(t->ncache!=0 || t->file->nc || t->w==nil || t!=&t->w->body)
-		error("text.load");
-	if(t->w->isdir && t->file->nname==0){
-		warning(nil, "empty directory name\n");
-		return 0;
-	}
-	fd = open(file, OREAD);
-	if(fd < 0){
-		warning(nil, "can't open %s: %r\n", file);
-		return 0;
-	}
-	d = dirfstat(fd);
-	if(d == nil){
-		warning(nil, "can't fstat %s: %r\n", file);
-		goto Rescue;
-	}
-	nulls = FALSE;
-	if(d->qid.type & QTDIR){
-		/* this is checked in get() but it's possible the file changed underfoot */
-		if(t->file->ntext > 1){
-			warning(nil, "%s is a directory; can't read with multiple windows on it\n", file);
-			goto Rescue;
-		}
-		t->w->isdir = TRUE;
-		t->w->filemenu = FALSE;
-		if(t->file->nname > 0 && t->file->name[t->file->nname-1] != '/'){
-			rp = runemalloc(t->file->nname+1);
-			runemove(rp, t->file->name, t->file->nname);
-			rp[t->file->nname] = '/';
-			winsetname(t->w, rp, t->file->nname+1);
-			free(rp);
-		}
-		dlp = nil;
-		ndl = 0;
-		dbuf = nil;
-		while((n=dirread(fd, &dbuf)) > 0){
-			for(i=0; i<n; i++){
-				dl = emalloc(sizeof(Dirlist));
-				j = strlen(dbuf[i].name);
-				tmp = emalloc(j+1+1);
-				memmove(tmp, dbuf[i].name, j);
-				if(dbuf[i].qid.type & QTDIR)
-					tmp[j++] = '/';
-				tmp[j] = '\0';
-				dl->r = bytetorune(tmp, &dl->nr);
-				dl->wid = stringwidth(t->font, tmp);
-				free(tmp);
-				ndl++;
-				dlp = realloc(dlp, ndl*sizeof(Dirlist*));
-				dlp[ndl-1] = dl;
-			}
-			free(dbuf);
-		}
-		qsort(dlp, ndl, sizeof(Dirlist*), dircmp);
-		t->w->dlp = dlp;
-		t->w->ndl = ndl;
-		textcolumnate(t, dlp, ndl);
-		q1 = t->file->nc;
-	}else{
-		t->w->isdir = FALSE;
-		t->w->filemenu = TRUE;
-		q1 = q0 + fileload(t->file, q0, fd, &nulls);
-	}
-	if(setqid){
-		t->file->dev = d->dev;
-		t->file->mtime = d->mtime;
-		t->file->qidpath = d->qid.path;
-	}
-	close(fd);
-	rp = fbufalloc();
-	for(q=q0; q<q1; q+=n){
-		n = q1-q;
-		if(n > RBUFSIZE)
-			n = RBUFSIZE;
-		bufread(t->file, q, rp, n);
-		if(q < t->org)
-			t->org += n;
-		else if(q <= t->org+t->nchars)
-			frinsert(t, rp, rp+n, q-t->org);
-		if(t->lastlinefull)
-			break;
-	}
-	fbuffree(rp);
-	for(i=0; i<t->file->ntext; i++){
-		u = t->file->text[i];
-		if(u != t){
-			if(u->org > u->file->nc)	/* will be 0 because of reset(), but safety first */
-				u->org = 0;
-			textresize(u, u->all);
-			textbacknl(u, u->org, 0);	/* go to beginning of line */
-		}
-		textsetselect(u, q0, q0);
-	}
-	if(nulls)
-		warning(nil, "%s: NUL bytes elided\n", file);
-	free(d);
-	return q1-q0;
-
-    Rescue:
-	close(fd);
-	return 0;
-}
-
-uint
-textbsinsert(Text *t, uint q0, Rune *r, uint n, int tofile, int *nrp)
-{
-	Rune *bp, *tp, *up;
-	int i, initial;
-
-	if(t->what == Tag){	/* can't happen but safety first: mustn't backspace over file name */
-    Err:
-		textinsert(t, q0, r, n, tofile);
-		*nrp = n;
-		return q0;
-	}
-	bp = r;
-	for(i=0; i<n; i++)
-		if(*bp++ == '\b'){
-			--bp;
-			initial = 0;
-			tp = runemalloc(n);
-			runemove(tp, r, i);
-			up = tp+i;
-			for(; i<n; i++){
-				*up = *bp++;
-				if(*up == '\b')
-					if(up == tp)
-						initial++;
-					else
-						--up;
-				else
-					up++;
-			}
-			if(initial){
-				if(initial > q0)
-					initial = q0;
-				q0 -= initial;
-				textdelete(t, q0, q0+initial, tofile);
-			}
-			n = up-tp;
-			textinsert(t, q0, tp, n, tofile);
-			free(tp);
-			*nrp = n;
-			return q0;
-		}
-	goto Err;
-}
-
-void
-textinsert(Text *t, uint q0, Rune *r, uint n, int tofile)
-{
-	int c, i;
-	Text *u;
-
-	if(tofile && t->ncache != 0)
-		error("text.insert");
-	if(n == 0)
-		return;
-	if(tofile){
-		fileinsert(t->file, q0, r, n);
-		if(t->what == Body){
-			t->w->dirty = TRUE;
-			t->w->utflastqid = -1;
-		}
-		if(t->file->ntext > 1)
-			for(i=0; i<t->file->ntext; i++){
-				u = t->file->text[i];
-				if(u != t){
-					u->w->dirty = TRUE;	/* always a body */
-					textinsert(u, q0, r, n, FALSE);
-					textsetselect(u, u->q0, u->q1);
-					textscrdraw(u);
-				}
-			}
-					
-	}
-	if(q0 < t->q1)
-		t->q1 += n;
-	if(q0 < t->q0)
-		t->q0 += n;
-	if(q0 < t->org)
-		t->org += n;
-	else if(q0 <= t->org+t->nchars)
-		frinsert(t, r, r+n, q0-t->org);
-	if(t->w){
-		c = 'i';
-		if(t->what == Body)
-			c = 'I';
-		if(n <= EVENTSIZE)
-			winevent(t->w, "%c%d %d 0 %d %.*S\n", c, q0, q0+n, n, n, r);
-		else
-			winevent(t->w, "%c%d %d 0 0 \n", c, q0, q0+n, n);
-	}
-}
-
-void
-typecommit(Text *t)
-{
-	if(t->w != nil)
-		wincommit(t->w, t);
-	else
-		textcommit(t, TRUE);
-}
-
-void
-textfill(Text *t)
-{
-	Rune *rp;
-	int i, n, m, nl;
-
-	if(t->lastlinefull || t->nofill)
-		return;
-	if(t->ncache > 0)
-		typecommit(t);
-	rp = fbufalloc();
-	do{
-		n = t->file->nc-(t->org+t->nchars);
-		if(n == 0)
-			break;
-		if(n > 2000)	/* educated guess at reasonable amount */
-			n = 2000;
-		bufread(t->file, t->org+t->nchars, rp, n);
-		/*
-		 * it's expensive to frinsert more than we need, so
-		 * count newlines.
-		 */
-		nl = t->maxlines-t->nlines;
-		m = 0;
-		for(i=0; i<n; ){
-			if(rp[i++] == '\n'){
-				m++;
-				if(m >= nl)
-					break;
-			}
-		}
-		frinsert(t, rp, rp+i, t->nchars);
-	}while(t->lastlinefull == FALSE);
-	fbuffree(rp);
-}
-
-void
-textdelete(Text *t, uint q0, uint q1, int tofile)
-{
-	uint n, p0, p1;
-	int i, c;
-	Text *u;
-
-	if(tofile && t->ncache != 0)
-		error("text.delete");
-	n = q1-q0;
-	if(n == 0)
-		return;
-	if(tofile){
-		filedelete(t->file, q0, q1);
-		if(t->what == Body){
-			t->w->dirty = TRUE;
-			t->w->utflastqid = -1;
-		}
-		if(t->file->ntext > 1)
-			for(i=0; i<t->file->ntext; i++){
-				u = t->file->text[i];
-				if(u != t){
-					u->w->dirty = TRUE;	/* always a body */
-					textdelete(u, q0, q1, FALSE);
-					textsetselect(u, u->q0, u->q1);
-					textscrdraw(u);
-				}
-			}
-	}
-	if(q0 < t->q0)
-		t->q0 -= min(n, t->q0-q0);
-	if(q0 < t->q1)
-		t->q1 -= min(n, t->q1-q0);
-	if(q1 <= t->org)
-		t->org -= n;
-	else if(q0 < t->org+t->nchars){
-		p1 = q1 - t->org;
-		if(p1 > t->nchars)
-			p1 = t->nchars;
-		if(q0 < t->org){
-			t->org = q0;
-			p0 = 0;
-		}else
-			p0 = q0 - t->org;
-		frdelete(t, p0, p1);
-		textfill(t);
-	}
-	if(t->w){
-		c = 'd';
-		if(t->what == Body)
-			c = 'D';
-		winevent(t->w, "%c%d %d 0 0 \n", c, q0, q1);
-	}
-}
-
-void
-textconstrain(Text *t, uint q0, uint q1, uint *p0, uint *p1)
-{
-	*p0 = min(q0, t->file->nc);
-	*p1 = min(q1, t->file->nc);
-}
-
-Rune
-textreadc(Text *t, uint q)
-{
-	Rune r;
-
-	if(t->cq0<=q && q<t->cq0+t->ncache)
-		r = t->cache[q-t->cq0];
-	else
-		bufread(t->file, q, &r, 1);
-	return r;
-}
-
-static int
-spacesindentbswidth(Text *t)
-{
-	uint q, col;
-	Rune r;
-
-	col = textbswidth(t, 0x15);
-	q = t->q0;
-	while(q > 0){
-		r = textreadc(t, q-1);
-		if(r != ' ')
-			break;
-		q--;
-		if(--col % t->tabstop == 0)
-			break;
-	}
-	if(t->q0 == q)
-		return 1;
-	return t->q0-q;
-}
-
-int
-textbswidth(Text *t, Rune c)
-{
-	uint q, eq;
-	Rune r;
-	int skipping;
-
-	/* there is known to be at least one character to erase */
-	if(c == 0x08){	/* ^H: erase character */
-		if(t->what == Body && t->w->indent[SPACESINDENT])
-			return spacesindentbswidth(t);
-		return 1;
-	}
-	q = t->q0;
-	skipping = TRUE;
-	while(q > 0){
-		r = textreadc(t, q-1);
-		if(r == '\n'){		/* eat at most one more character */
-			if(q == t->q0)	/* eat the newline */
-				--q;
-			break; 
-		}
-		if(c == 0x17){
-			eq = isalnum(r);
-			if(eq && skipping)	/* found one; stop skipping */
-				skipping = FALSE;
-			else if(!eq && !skipping)
-				break;
-		}
-		--q;
-	}
-	return t->q0-q;
-}
-
-int
-textfilewidth(Text *t, uint q0, int oneelement)
-{
-	uint q;
-	Rune r;
-
-	q = q0;
-	while(q > 0){
-		r = textreadc(t, q-1);
-		if(r <= ' ')
-			break;
-		if(oneelement && r=='/')
-			break;
-		--q;
-	}
-	return q0-q;
-}
-
-Rune*
-textcomplete(Text *t)
-{
-	int i, nstr, npath;
-	uint q;
-	Rune tmp[200];
-	Rune *str, *path;
-	Rune *rp;
-	Completion *c;
-	char *s, *dirs;
-	Runestr dir;
-
-	/* control-f: filename completion; works back to white space or / */
-	if(t->q0<t->file->nc && textreadc(t, t->q0)>' ')	/* must be at end of word */
-		return nil;
-	nstr = textfilewidth(t, t->q0, TRUE);
-	str = runemalloc(nstr);
-	npath = textfilewidth(t, t->q0-nstr, FALSE);
-	path = runemalloc(npath);
-
-	c = nil;
-	rp = nil;
-	dirs = nil;
-
-	q = t->q0-nstr;
-	for(i=0; i<nstr; i++)
-		str[i] = textreadc(t, q++);
-	q = t->q0-nstr-npath;
-	for(i=0; i<npath; i++)
-		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};
-	else{
-		dir = dirname(t, nil, 0);
-		if(dir.nr + 1 + npath > nelem(tmp)){
-			free(dir.r);
-			goto Return;
-		}
-		if(dir.nr == 0){
-			dir.nr = 1;
-			dir.r = runestrdup(L".");
-		}
-		runemove(tmp, dir.r, dir.nr);
-		tmp[dir.nr] = '/';
-		runemove(tmp+dir.nr+1, path, npath);
-		free(dir.r);
-		dir.r = tmp;
-		dir.nr += 1+npath;
-		dir = cleanrname(dir);
-	}
-
-	s = smprint("%.*S", nstr, str);
-	dirs = smprint("%.*S", dir.nr, dir.r);
-	c = complete(dirs, s);
-	free(s);
-	if(c == nil){
-		warning(nil, "error attempting completion: %r\n");
-		goto Return;
-	}
-
-	if(!c->advance){
-		warning(nil, "%.*S%s%.*S*%s\n",
-			dir.nr, dir.r,
-			dir.nr>0 && dir.r[dir.nr-1]!='/' ? "/" : "",
-			nstr, str,
-			c->nmatch? "" : ": no matches in:");
-		for(i=0; i<c->nfile; i++)
-			warning(nil, " %s\n", c->filename[i]);
-	}
-
-	if(c->advance)
-		rp = runesmprint("%s", c->string);
-	else
-		rp = nil;
-  Return:
-	freecompletion(c);
-	free(dirs);
-	free(str);
-	free(path);
-	return rp;
-}
-
-void
-texttype(Text *t, Rune r)
-{
-	uint q0, q1;
-	int nnb, nb, n, i;
-	int nr;
-	Rune rr;
-	Rune *rp;
-	Text *u;
-
-	nr = 1;
-	rp = &r;
-	switch(r){
-	case Kleft:
-		typecommit(t);
-		if(t->q0 > 0)
-			textshow(t, t->q0-1, t->q0-1, TRUE);
-		return;
-	case Kright:
-		typecommit(t);
-		if(t->q1 < t->file->nc)
-			textshow(t, t->q1+1, t->q1+1, TRUE);
-		return;
-	case Kdown:
-		n = t->maxlines/3;
-		goto case_Down;
-	case Kscrollonedown:
-		n = mousescrollsize(t->maxlines);
-		if(n <= 0)
-			n = 1;
-		goto case_Down;
-	case Kpgdown:
-		n = 2*t->maxlines/3;
-	case_Down:
-		q0 = t->org+frcharofpt(t, Pt(t->r.min.x, t->r.min.y+n*t->font->height));
-		if(t->what == Body)
-			textsetorigin(t, q0, TRUE);
-		return;
-	case Kup:
-		n = t->maxlines/3;
-		goto case_Up;
-	case Kscrolloneup:
-		n = mousescrollsize(t->maxlines);
-		goto case_Up;
-	case Kpgup:
-		n = 2*t->maxlines/3;
-	case_Up:
-		q0 = textbacknl(t, t->org, n);
-		if(t->what == Body)
-			textsetorigin(t, q0, TRUE);
-		return;
-	case Khome:
-		typecommit(t);
-		textshow(t, 0, 0, FALSE);
-		return;
-	case Kend:
-		typecommit(t);
-		textshow(t, t->file->nc, t->file->nc, FALSE);
-		return;
-	case 0x01:	/* ^A: beginning of line */
-		typecommit(t);
-		/* go to where ^U would erase, if not already at BOL */
-		nnb = 0;
-		if(t->q0>0 && textreadc(t, t->q0-1)!='\n')
-			nnb = textbswidth(t, 0x15);
-		textshow(t, t->q0-nnb, t->q0-nnb, TRUE);
-		return;
-	case 0x05:	/* ^E: end of line */
-		typecommit(t);
-		q0 = t->q0;
-		while(q0<t->file->nc && textreadc(t, q0)!='\n')
-			q0++;
-		textshow(t, q0, q0, TRUE);
-		return;
-	}
-	if(t->what == Body){
-		seq++;
-		filemark(t->file);
-	}
-	if(t->q1 > t->q0){
-		if(t->ncache != 0)
-			error("text.type");
-		cut(t, t, nil, TRUE, TRUE, nil, 0);
-		t->eq0 = ~0;
-	}
-	textshow(t, t->q0, t->q0, 1);
-	switch(r){
-	case 0x06:
-	case Kins:
-		rp = textcomplete(t);
-		if(rp == nil)
-			return;
-		nr = runestrlen(rp);
-		break;	/* fall through to normal insertion case */
-	case 0x1B:
-		if(t->eq0 != ~0)
-			textsetselect(t, t->eq0, t->q0);
-		if(t->ncache > 0)
-			typecommit(t);
-		return;
-	case 0x08:	/* ^H: erase character */
-	case 0x15:	/* ^U: erase line */
-	case 0x17:	/* ^W: erase word */
-		if(t->q0 == 0)	/* nothing to erase */
-			return;
-		nnb = textbswidth(t, r);
-		q1 = t->q0;
-		q0 = q1-nnb;
-		/* if selection is at beginning of window, avoid deleting invisible text */
-		if(q0 < t->org){
-			q0 = t->org;
-			nnb = q1-q0;
-		}
-		if(nnb <= 0)
-			return;
-		for(i=0; i<t->file->ntext; i++){
-			u = t->file->text[i];
-			u->nofill = TRUE;
-			nb = nnb;
-			n = u->ncache;
-			if(n > 0){
-				if(q1 != u->cq0+n)
-					error("text.type backspace");
-				if(n > nb)
-					n = nb;
-				u->ncache -= n;
-				textdelete(u, q1-n, q1, FALSE);
-				nb -= n;
-			}
-			if(u->eq0==q1 || u->eq0==~0)
-				u->eq0 = q0;
-			if(nb && u==t)
-				textdelete(u, q0, q0+nb, TRUE);
-			if(u != t)
-				textsetselect(u, u->q0, u->q1);
-			else
-				textsetselect(t, q0, q0);
-			u->nofill = FALSE;
-		}
-		for(i=0; i<t->file->ntext; i++)
-			textfill(t->file->text[i]);
-		return;
-	case '\t':
-		if(t->what == Body && t->w->indent[SPACESINDENT]){
-			nnb = textbswidth(t, 0x15);
-			if(nnb == 1 && textreadc(t, t->q0-1) == '\n')
-				nnb = 0;
-			nnb = t->tabstop - nnb % t->tabstop;
-			rp = runemalloc(nnb);
-			for(nr = 0; nr < nnb; nr++)
-				rp[nr] = ' ';
-		}
-		break;
-	case '\n':
-		if(t->what == Body && t->w->indent[AUTOINDENT]){
-			/* find beginning of previous line using backspace code */
-			nnb = textbswidth(t, 0x15); /* ^U case */
-			rp = runemalloc(nnb + 1);
-			nr = 0;
-			rp[nr++] = r;
-			for(i=0; i<nnb; i++){
-				rr = textreadc(t, t->q0-nnb+i);
-				if(rr != ' ' && rr != '\t')
-					break;
-				rp[nr++] = rr;
-			}
-		}
-		break; /* fall through to normal code */
-	}
-	/* otherwise ordinary character; just insert, typically in caches of all texts */
-	for(i=0; i<t->file->ntext; i++){
-		u = t->file->text[i];
-		if(u->eq0 == ~0)
-			u->eq0 = t->q0;
-		if(u->ncache == 0)
-			u->cq0 = t->q0;
-		else if(t->q0 != u->cq0+u->ncache)
-			error("text.type cq1");
-		textinsert(u, t->q0, rp, nr, FALSE);
-		if(u != t)
-			textsetselect(u, u->q0, u->q1);
-		if(u->ncache+nr > u->ncachealloc){
-			u->ncachealloc += 10 + nr;
-			u->cache = runerealloc(u->cache, u->ncachealloc);
-		}
-		runemove(u->cache+u->ncache, rp, nr);
-		u->ncache += nr;
-	}
-	if(rp != &r)
-		free(rp);
-	textsetselect(t, t->q0+nr, t->q0+nr);
-	if(r=='\n' && t->w!=nil)
-		wincommit(t->w, t);
-}
-
-void
-textcommit(Text *t, int tofile)
-{
-	if(t->ncache == 0)
-		return;
-	if(tofile)
-		fileinsert(t->file, t->cq0, t->cache, t->ncache);
-	if(t->what == Body){
-		t->w->dirty = TRUE;
-		t->w->utflastqid = -1;
-	}
-	t->ncache = 0;
-}
-
-static	Text	*clicktext;
-static	uint	clickmsec;
-static	int	clickcount;
-static	Point	clickpt;
-static	Text	*selecttext;
-static	uint	selectq;
-
-/*
- * called from frame library
- */
-void
-framescroll(Frame *f, int dl)
-{
-	if(f != &selecttext->Frame)
-		error("frameselect not right frame");
-	textframescroll(selecttext, dl);
-}
-
-void
-textframescroll(Text *t, int dl)
-{
-	uint q0;
-
-	if(dl == 0){
-		scrsleep(100);
-		return;
-	}
-	if(dl < 0){
-		q0 = textbacknl(t, t->org, -dl);
-		if(selectq > t->org+t->p0)
-			textsetselect(t, t->org+t->p0, selectq);
-		else
-			textsetselect(t, selectq, t->org+t->p0);
-	}else{
-		if(t->org+t->nchars == t->file->nc)
-			return;
-		q0 = t->org+frcharofpt(t, Pt(t->r.min.x, t->r.min.y+dl*t->font->height));
-		if(selectq > t->org+t->p1)
-			textsetselect(t, t->org+t->p1, selectq);
-		else
-			textsetselect(t, selectq, t->org+t->p1);
-	}
-	textsetorigin(t, q0, TRUE);
-	flushimage(display, 1);
-}
-
-
-void
-textselect(Text *t)
-{
-	uint q0, q1;
-	int b, x, y, dx, dy;
-	int state;
-
-	selecttext = t;
-	/*
-	 * To have double-clicking and chording, we double-click
-	 * immediately if it might make sense.
-	 */
-	b = mouse->buttons;
-	q0 = t->q0;
-	q1 = t->q1;
-	dx = abs(clickpt.x - mouse->xy.x);
-	dy = abs(clickpt.y - mouse->xy.y);
-	clickpt = mouse->xy;
-	selectq = t->org+frcharofpt(t, mouse->xy);
-	clickcount++;
-	if(mouse->msec-clickmsec >= 500 || selecttext != t || clickcount > 3 || dx > 3 || dy > 3)
-		clickcount = 0;
-	if(clickcount >= 1 && selecttext==t && mouse->msec-clickmsec < 500){
-		textstretchsel(t, selectq, &q0, &q1, clickcount);
-		textsetselect(t, q0, q1);
-		flushimage(display, 1);
-		x = mouse->xy.x;
-		y = mouse->xy.y;
-		/* stay here until something interesting happens */
-		while(1){
-			readmouse(mousectl);
-			dx = abs(mouse->xy.x - x);
-			dy = abs(mouse->xy.y - y);
-			if(mouse->buttons != b || dx >= 3 || dy >= 3)
-				break;
-			clickcount++;
-			clickmsec = mouse->msec;
-		}
-		mouse->xy.x = x;	/* in case we're calling frselect */
-		mouse->xy.y = y;
-		q0 = t->q0;	/* may have changed */
-		q1 = t->q1;
-		selectq = t->org+frcharofpt(t, mouse->xy);;
-	}
-	if(mouse->buttons == b && clickcount == 0){
-		t->Frame.scroll = framescroll;
-		frselect(t, mousectl);
-		/* horrible botch: while asleep, may have lost selection altogether */
-		if(selectq > t->file->nc)
-			selectq = t->org + t->p0;
-		t->Frame.scroll = nil;
-		if(selectq < t->org)
-			q0 = selectq;
-		else
-			q0 = t->org + t->p0;
-		if(selectq > t->org+t->nchars)
-			q1 = selectq;
-		else
-			q1 = t->org+t->p1;
-	}
-	if(q0 == q1){
-		if(q0==t->q0 && mouse->msec-clickmsec<500)
-			textstretchsel(t, selectq, &q0, &q1, clickcount);
-		else
-			clicktext = t;
-		clickmsec = mouse->msec;
-	}else
-		clicktext = nil;
-	textsetselect(t, q0, q1);
-	flushimage(display, 1);
-	state = 0;	/* undo when possible; +1 for cut, -1 for paste */
-	while(mouse->buttons){
-		mouse->msec = 0;
-		b = mouse->buttons;
-		if((b&1) && (b&6)){
-			if(state==0 && t->what==Body){
-				seq++;
-				filemark(t->w->body.file);
-			}
-			if(b & 2){
-				if(state==-1 && t->what==Body){
-					winundo(t->w, TRUE);
-					textsetselect(t, q0, t->q0);
-					state = 0;
-				}else if(state != 1){
-					cut(t, t, nil, TRUE, TRUE, nil, 0);
-					state = 1;
-				}
-			}else{
-				if(state==1 && t->what==Body){
-					winundo(t->w, TRUE);
-					textsetselect(t, q0, t->q1);
-					state = 0;
-				}else if(state != -1){
-					paste(t, t, nil, TRUE, FALSE, nil, 0);
-					state = -1;
-				}
-			}
-			textscrdraw(t);
-			clearmouse();
-		}
-		flushimage(display, 1);
-		while(mouse->buttons == b)
-			readmouse(mousectl);
-		if(mouse->msec-clickmsec >= 500)
-			clicktext = nil;
-	}
-}
-
-void
-textshow(Text *t, uint q0, uint q1, int doselect)
-{
-	int qe;
-	int nl;
-	uint q;
-
-	if(t->what != Body){
-		if(doselect)
-			textsetselect(t, q0, q1);
-		return;
-	}
-	if(t->w!=nil && t->maxlines==0)
-		colgrow(t->col, t->w, 1);
-	if(doselect)
-		textsetselect(t, q0, q1);
-	qe = t->org+t->nchars;
-	if(t->org<=q0 && (q0<qe || (q0==qe && qe==t->file->nc+t->ncache)))
-		textscrdraw(t);
-	else{
-		if(t->w->nopen[QWevent] > 0)
-			nl = 3*t->maxlines/4;
-		else
-			nl = t->maxlines/4;
-		q = textbacknl(t, q0, nl);
-		/* avoid going backwards if trying to go forwards - long lines! */
-		if(!(q0>t->org && q<t->org))
-			textsetorigin(t, q, TRUE);
-		while(q0 > t->org+t->nchars)
-			textsetorigin(t, t->org+1, FALSE);
-	}
-}
-
-static
-int
-region(int a, int b)
-{
-	if(a < b)
-		return -1;
-	if(a == b)
-		return 0;
-	return 1;
-}
-
-void
-selrestore(Frame *f, Point pt0, uint p0, uint p1)
-{
-	if(p1<=f->p0 || p0>=f->p1){
-		/* no overlap */
-		frdrawsel0(f, pt0, p0, p1, f->cols[BACK], f->cols[TEXT]);
-		return;
-	}
-	if(p0>=f->p0 && p1<=f->p1){
-		/* entirely inside */
-		frdrawsel0(f, pt0, p0, p1, f->cols[HIGH], f->cols[HTEXT]);
-		return;
-	}
-
-	/* they now are known to overlap */
-
-	/* before selection */
-	if(p0 < f->p0){
-		frdrawsel0(f, pt0, p0, f->p0, f->cols[BACK], f->cols[TEXT]);
-		p0 = f->p0;
-		pt0 = frptofchar(f, p0);
-	}
-	/* after selection */
-	if(p1 > f->p1){
-		frdrawsel0(f, frptofchar(f, f->p1), f->p1, p1, f->cols[BACK], f->cols[TEXT]);
-		p1 = f->p1;
-	}
-	/* inside selection */
-	frdrawsel0(f, pt0, p0, p1, f->cols[HIGH], f->cols[HTEXT]);
-}
-
-void
-textsetselect(Text *t, uint q0, uint q1)
-{
-	int p0, p1;
-
-	/* t->p0 and t->p1 are always right; t->q0 and t->q1 may be off */
-	t->q0 = q0;
-	t->q1 = q1;
-	/* compute desired p0,p1 from q0,q1 */
-	p0 = q0-t->org;
-	p1 = q1-t->org;
-	if(p0 < 0)
-		p0 = 0;
-	if(p1 < 0)
-		p1 = 0;
-	if(p0 > t->nchars)
-		p0 = t->nchars;
-	if(p1 > t->nchars)
-		p1 = t->nchars;
-	if(p0==t->p0 && p1==t->p1)
-		return;
-	/* screen disagrees with desired selection */
-	if(t->p1<=p0 || p1<=t->p0 || p0==p1 || t->p1==t->p0){
-		/* no overlap or too easy to bother trying */
-		frdrawsel(t, frptofchar(t, t->p0), t->p0, t->p1, 0);
-		frdrawsel(t, frptofchar(t, p0), p0, p1, 1);
-		goto Return;
-	}
-	/* overlap; avoid unnecessary painting */
-	if(p0 < t->p0){
-		/* extend selection backwards */
-		frdrawsel(t, frptofchar(t, p0), p0, t->p0, 1);
-	}else if(p0 > t->p0){
-		/* trim first part of selection */
-		frdrawsel(t, frptofchar(t, t->p0), t->p0, p0, 0);
-	}
-	if(p1 > t->p1){
-		/* extend selection forwards */
-		frdrawsel(t, frptofchar(t, t->p1), t->p1, p1, 1);
-	}else if(p1 < t->p1){
-		/* trim last part of selection */
-		frdrawsel(t, frptofchar(t, p1), p1, t->p1, 0);
-	}
-
-    Return:
-	t->p0 = p0;
-	t->p1 = p1;
-}
-
-/*
- * Release the button in less than DELAY ms and it's considered a null selection
- * if the mouse hardly moved, regardless of whether it crossed a char boundary.
- */
-enum {
-	DELAY = 2,
-	MINMOVE = 4,
-};
-
-uint
-xselect(Frame *f, Mousectl *mc, Image *col, uint *p1p)	/* when called, button is down */
-{
-	uint p0, p1, q, tmp;
-	ulong msec;
-	Point mp, pt0, pt1, qt;
-	int reg, b;
-
-	mp = mc->xy;
-	b = mc->buttons;
-	msec = mc->msec;
-
-	/* remove tick */
-	if(f->p0 == f->p1)
-		frtick(f, frptofchar(f, f->p0), 0);
-	p0 = p1 = frcharofpt(f, mp);
-	pt0 = frptofchar(f, p0);
-	pt1 = frptofchar(f, p1);
-	reg = 0;
-	frtick(f, pt0, 1);
-	do{
-		q = frcharofpt(f, mc->xy);
-		if(p1 != q){
-			if(p0 == p1)
-				frtick(f, pt0, 0);
-			if(reg != region(q, p0)){	/* crossed starting point; reset */
-				if(reg > 0)
-					selrestore(f, pt0, p0, p1);
-				else if(reg < 0)
-					selrestore(f, pt1, p1, p0);
-				p1 = p0;
-				pt1 = pt0;
-				reg = region(q, p0);
-				if(reg == 0)
-					frdrawsel0(f, pt0, p0, p1, col, display->white);
-			}
-			qt = frptofchar(f, q);
-			if(reg > 0){
-				if(q > p1)
-					frdrawsel0(f, pt1, p1, q, col, display->white);
-
-				else if(q < p1)
-					selrestore(f, qt, q, p1);
-			}else if(reg < 0){
-				if(q > p1)
-					selrestore(f, pt1, p1, q);
-				else
-					frdrawsel0(f, qt, q, p1, col, display->white);
-			}
-			p1 = q;
-			pt1 = qt;
-		}
-		if(p0 == p1)
-			frtick(f, pt0, 1);
-		flushimage(f->display, 1);
-		readmouse(mc);
-	}while(mc->buttons == b);
-	if(mc->msec-msec < DELAY && p0!=p1
-	&& abs(mp.x-mc->xy.x)<MINMOVE
-	&& abs(mp.y-mc->xy.y)<MINMOVE) {
-		if(reg > 0)
-			selrestore(f, pt0, p0, p1);
-		else if(reg < 0)
-			selrestore(f, pt1, p1, p0);
-		p1 = p0;
-	}
-	if(p1 < p0){
-		tmp = p0;
-		p0 = p1;
-		p1 = tmp;
-	}
-	pt0 = frptofchar(f, p0);
-	if(p0 == p1)
-		frtick(f, pt0, 0);
-	selrestore(f, pt0, p0, p1);
-	/* restore tick */
-	if(f->p0 == f->p1)
-		frtick(f, frptofchar(f, f->p0), 1);
-	flushimage(f->display, 1);
-	*p1p = p1;
-	return p0;
-}
-
-int
-textselect23(Text *t, uint *q0, uint *q1, Image *high, int mask)
-{
-	uint p0, p1;
-	int buts;
-	
-	p0 = xselect(t, mousectl, high, &p1);
-	buts = mousectl->buttons;
-	if((buts & mask) == 0){
-		*q0 = p0+t->org;
-		*q1 = p1+t->org;
-	}
-
-	while(mousectl->buttons)
-		readmouse(mousectl);
-	return buts;
-}
-
-int
-textselect2(Text *t, uint *q0, uint *q1, Text **tp)
-{
-	int buts;
-
-	*tp = nil;
-	buts = textselect23(t, q0, q1, but2col, 4);
-	if(buts & 4)
-		return 0;
-	if(buts & 1){	/* pick up argument */
-		*tp = argtext;
-		return 1;
-	}
-	return 1;
-}
-
-int
-textselect3(Text *t, uint *q0, uint *q1)
-{
-	int h;
-
-	h = (textselect23(t, q0, q1, but3col, 1|2) == 0);
-	return h;
-}
-
-static Rune left1[] =  { L'{', L'[', L'(', L'<', L'«', 0 };
-static Rune right1[] = { L'}', L']', L')', L'>', L'»', 0 };
-static Rune left2[] =  { L'\n', 0 };
-static Rune left3[] =  { L'\'', L'"', L'`', 0 };
-
-static
-Rune *left[] = {
-	left1,
-	left2,
-	left3,
-	nil
-};
-static
-Rune *right[] = {
-	right1,
-	left2,
-	left3,
-	nil
-};
-
-int
-inmode(Rune r, int mode)
-{
-	return (mode == 1) ? isalnum(r) : r && !isspace(r);
-}
-
-void
-textstretchsel(Text *t, uint mp, uint *q0, uint *q1, int mode)
-{
-	int c, i;
-	Rune *r, *l, *p;
-	uint q;
-
-	*q0 = mp;
-	*q1 = mp;
-	for(i=0; left[i]!=nil; i++){
-		q = *q0;
-		l = left[i];
-		r = right[i];
-		/* try matching character to left, looking right */
-		if(q == 0)
-			c = '\n';
-		else
-			c = textreadc(t, q-1);
-		p = runestrchr(l, c);
-		if(p != nil){
-			if(textclickmatch(t, c, r[p-l], 1, &q))
-				*q1 = q-(c!='\n');
-			return;
-		}
-		/* try matching character to right, looking left */
-		if(q == t->file->nc)
-			c = '\n';
-		else
-			c = textreadc(t, q);
-		p = runestrchr(r, c);
-		if(p != nil){
-			if(textclickmatch(t, c, l[p-r], -1, &q)){
-				*q1 = *q0+(*q0<t->file->nc && c=='\n');
-				*q0 = q;
-				if(c!='\n' || q!=0 || textreadc(t, 0)=='\n')
-					(*q0)++;
-			}
-			return;
-		}
-	}
-	/* try filling out word to right */
-	while(*q1<t->file->nc && inmode(textreadc(t, *q1), mode))
-		(*q1)++;
-	/* try filling out word to left */
-	while(*q0>0 && inmode(textreadc(t, *q0-1), mode))
-		(*q0)--;
-}
-
-int
-textclickmatch(Text *t, int cl, int cr, int dir, uint *q)
-{
-	Rune c;
-	int nest;
-
-	nest = 1;
-	for(;;){
-		if(dir > 0){
-			if(*q == t->file->nc)
-				break;
-			c = textreadc(t, *q);
-			(*q)++;
-		}else{
-			if(*q == 0)
-				break;
-			(*q)--;
-			c = textreadc(t, *q);
-		}
-		if(c == cr){
-			if(--nest==0)
-				return 1;
-		}else if(c == cl)
-			nest++;
-	}
-	return cl=='\n' && nest==1;
-}
-
-uint
-textbacknl(Text *t, uint p, uint n)
-{
-	int i, j;
-
-	/* look for start of this line if n==0 */
-	if(n==0 && p>0 && textreadc(t, p-1)!='\n')
-		n = 1;
-	i = n;
-	while(i-->0 && p>0){
-		--p;	/* it's at a newline now; back over it */
-		if(p == 0)
-			break;
-		/* at 128 chars, call it a line anyway */
-		for(j=128; --j>0 && p>0; p--)
-			if(textreadc(t, p-1)=='\n')
-				break;
-	}
-	return p;
-}
-
-void
-textsetorigin(Text *t, uint org, int exact)
-{
-	int i, a, fixup;
-	Rune *r;
-	uint n;
-
-	if(org>0 && !exact && textreadc(t, org-1) != '\n'){
-		/* org is an estimate of the char posn; find a newline */
-		/* don't try harder than 256 chars */
-		for(i=0; i<256 && org<t->file->nc; i++){
-			if(textreadc(t, org) == '\n'){
-				org++;
-				break;
-			}
-			org++;
-		}
-	}
-	a = org-t->org;
-	fixup = 0;
-	if(a>=0 && a<t->nchars){
-		frdelete(t, 0, a);
-		fixup = 1;	/* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */
-	}
-	else if(a<0 && -a<t->nchars){
-		n = t->org - org;
-		r = runemalloc(n);
-		bufread(t->file, org, r, n);
-		frinsert(t, r, r+n, 0);
-		free(r);
-	}else
-		frdelete(t, 0, t->nchars);
-	t->org = org;
-	textfill(t);
-	textscrdraw(t);
-	textsetselect(t, t->q0, t->q1);
-	if(fixup && t->p1 > t->p0)
-		frdrawsel(t, frptofchar(t, t->p1-1), t->p1-1, t->p1, 1);
-}
-
-void
-textreset(Text *t)
-{
-	t->file->seq = 0;
-	t->eq0 = ~0;
-	/* do t->delete(0, t->nc, TRUE) without building backup stuff */
-	textsetselect(t, t->org, t->org);
-	frdelete(t, 0, t->nchars);
-	t->org = 0;
-	t->q0 = 0;
-	t->q1 = 0;
-	filereset(t->file);
-	bufreset(t->file);
-}
--- a/time.c
+++ /dev/null
@@ -1,120 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <thread.h>
-#include <cursor.h>
-#include <mouse.h>
-#include <keyboard.h>
-#include <frame.h>
-#include <fcall.h>
-#include <plumb.h>
-#include "dat.h"
-#include "fns.h"
-
-static Channel*	ctimer;	/* chan(Timer*)[100] */
-static Timer *timer;
-
-static
-uint
-msec(void)
-{
-	return nsec()/1000000;
-}
-
-void
-timerstop(Timer *t)
-{
-	t->next = timer;
-	timer = t;
-}
-
-void
-timercancel(Timer *t)
-{
-	t->cancel = TRUE;
-}
-
-static
-void
-timerproc(void*)
-{
-	int i, nt, na, dt, del;
-	Timer **t, *x;
-	uint old, new;
-
-	threadsetname("timerproc");
-	rfork(RFFDG);
-	t = nil;
-	na = 0;
-	nt = 0;
-	old = msec();
-	for(;;){
-		sleep(1);	/* will sleep minimum incr */
-		new = msec();
-		dt = new-old;
-		old = new;
-		if(dt < 0)	/* timer wrapped; go around, losing a tick */
-			continue;
-		for(i=0; i<nt; i++){
-			x = t[i];
-			x->dt -= dt;
-			del = FALSE;
-			if(x->cancel){
-				timerstop(x);
-				del = TRUE;
-			}else if(x->dt <= 0){
-				/*
-				 * avoid possible deadlock if client is
-				 * now sending on ctimer
-				 */
-				if(nbsendul(x->c, 0) > 0)
-					del = TRUE;
-			}
-			if(del){
-				memmove(&t[i], &t[i+1], (nt-i-1)*sizeof t[0]);
-				--nt;
-				--i;
-			}
-		}
-		if(nt == 0){
-			x = recvp(ctimer);
-	gotit:
-			if(nt == na){
-				na += 10;
-				t = realloc(t, na*sizeof(Timer*));
-				if(t == nil)
-					error("timer realloc failed");
-			}
-			t[nt++] = x;
-			old = msec();
-		}
-		if(nbrecv(ctimer, &x) > 0)
-			goto gotit;
-	}
-}
-
-void
-timerinit(void)
-{
-	ctimer = chancreate(sizeof(Timer*), 100);
-	proccreate(timerproc, nil, STACK);
-}
-
-Timer*
-timerstart(int dt)
-{
-	Timer *t;
-
-	t = timer;
-	if(t)
-		timer = timer->next;
-	else{
-		t = emalloc(sizeof(Timer));
-		t->c = chancreate(sizeof(int), 0);
-	}
-	t->next = nil;
-	t->dt = dt;
-	t->cancel = FALSE;
-	sendp(ctimer, t);
-	return t;
-}
--- a/util.c
+++ /dev/null
@@ -1,485 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <thread.h>
-#include <cursor.h>
-#include <mouse.h>
-#include <keyboard.h>
-#include <frame.h>
-#include <fcall.h>
-#include <plumb.h>
-#include "dat.h"
-#include "fns.h"
-
-static	Point		prevmouse;
-static	Window	*mousew;
-
-void
-cvttorunes(char *p, int n, Rune *r, int *nb, int *nr, int *nulls)
-{
-	uchar *q;
-	Rune *s;
-	int j, w;
-
-	/*
-	 * Always guaranteed that n bytes may be interpreted
-	 * without worrying about partial runes.  This may mean
-	 * reading up to UTFmax-1 more bytes than n; the caller
-	 * knows this.  If n is a firm limit, the caller should
-	 * set p[n] = 0.
-	 */
-	q = (uchar*)p;
-	s = r;
-	for(j=0; j<n; j+=w){
-		if(*q < Runeself){
-			w = 1;
-			*s = *q++;
-		}else{
-			w = chartorune(s, (char*)q);
-			q += w;
-		}
-		if(*s)
-			s++;
-		else if(nulls)
-			*nulls = TRUE;
-	}
-	*nb = (char*)q-p;
-	*nr = s-r;
-}
-
-void
-error(char *s)
-{
-	fprint(2, "acme: %s: %r\n", s);
-	remove(acmeerrorfile);
-	abort();
-}
-
-Window*
-errorwin1(Rune *dir, int ndir, Rune **incl, int nincl)
-{
-	Window *w;
-	Rune *r;
-	int i, n;
-
-	r = runemalloc(ndir+8);
-	if(n = ndir){	/* assign = */
-		runemove(r, dir, ndir);
-		r[n++] = L'/';
-	}
-	runemove(r+n, L"+Errors", 7);
-	n += 7;
-	w = lookfile(r, n);
-	if(w == nil){
-		if(row.ncol == 0)
-			if(rowadd(&row, nil, -1) == nil)
-				error("can't create column to make error window");
-		w = coladd(row.col[row.ncol-1], nil, nil, -1);
-		w->filemenu = FALSE;
-		winsetname(w, r, n);
-		xfidlog(w, "new");
-	}
-	free(r);
-	for(i=nincl; --i>=0; ){
-		n = runestrlen(incl[i]);
-		r = runemalloc(n);
-		runemove(r, incl[i], n);
-		winaddincl(w, r, n);
-	}
-	for(i=0; i<NINDENT; i++)
-		w->indent[i] = globalindent[i];
-	return w;
-}
-
-/* make new window, if necessary; return with it locked */
-Window*
-errorwin(Mntdir *md, int owner)
-{
-	Window *w;
-
-	for(;;){
-		if(md == nil)
-			w = errorwin1(nil, 0, nil, 0);
-		else
-			w = errorwin1(md->dir, md->ndir, md->incl, md->nincl);
-		winlock(w, owner);
-		if(w->col != nil)
-			break;
-		/* window was deleted too fast */
-		winunlock(w);
-	}
-	return w;
-}
-
-/*
- * Incoming window should be locked. 
- * It will be unlocked and returned window
- * will be locked in its place.
- */
-Window*
-errorwinforwin(Window *w)
-{
-	int i, n, nincl, owner;
-	Rune **incl;
-	Runestr dir;
-	Text *t;
-
-	t = &w->body;
-	dir = dirname(t, nil, 0);
-	if(dir.nr==1 && dir.r[0]=='.'){	/* sigh */
-		free(dir.r);
-		dir.r = nil;
-		dir.nr = 0;
-	}
-	incl = nil;
-	nincl = w->nincl;
-	if(nincl > 0){
-		incl = emalloc(nincl*sizeof(Rune*));
-		for(i=0; i<nincl; i++){
-			n = runestrlen(w->incl[i]);
-			incl[i] = runemalloc(n+1);
-			runemove(incl[i], w->incl[i], n);
-		}
-	}
-	owner = w->owner;
-	winunlock(w);
-	for(;;){
-		w = errorwin1(dir.r, dir.nr, incl, nincl);
-		winlock(w, owner);
-		if(w->col != nil)
-			break;
-		/* window deleted too fast */
-		winunlock(w);
-	}
-	return w;
-}
-
-typedef struct Warning Warning;
-
-struct Warning{
-	Mntdir *md;
-	Buffer buf;
-	Warning *next;
-};
-
-static Warning *warnings;
-
-static
-void
-addwarningtext(Mntdir *md, Rune *r, int nr)
-{
-	Warning *warn;
-	
-	for(warn = warnings; warn; warn=warn->next){
-		if(warn->md == md){
-			bufinsert(&warn->buf, warn->buf.nc, r, nr);
-			return;
-		}
-	}
-	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(void)
-{
-	Warning *warn, *next;
-	Window *w;
-	Text *t;
-	int owner, nr, q0, n;
-	Rune *r;
-
-	for(warn=warnings; warn; warn=next) {
-		w = errorwin(warn->md, 'E');
-		t = &w->body;
-		owner = w->owner;
-		if(owner == 0)
-			w->owner = 'E';
-		wincommit(w, t);
-		/*
-		 * Most commands don't generate much output. For instance,
-		 * Edit ,>cat goes through /dev/cons and is already in blocks
-		 * because of the i/o system, but a few can.  Edit ,p will
-		 * put the entire result into a single hunk.  So it's worth doing
-		 * this in blocks (and putting the text in a buffer in the first
-		 * place), to avoid a big memory footprint.
-		 */
-		r = fbufalloc();
-		q0 = t->file->nc;
-		for(n = 0; n < warn->buf.nc; n += nr){
-			nr = warn->buf.nc - n;
-			if(nr > RBUFSIZE)
-				nr = RBUFSIZE;
-			bufread(&warn->buf, n, r, nr);
-			textbsinsert(t, t->file->nc, r, nr, TRUE, &nr);
-		}
-		textshow(t, q0, t->file->nc, 1);
-		free(r);
-		winsettag(t->w);
-		textscrdraw(t);
-		w->owner = owner;
-		w->dirty = FALSE;
-		winunlock(w);
-		bufclose(&warn->buf);
-		next = warn->next;
-		if(warn->md)
-			fsysdelid(warn->md);
-		free(warn);
-	}
-	warnings = nil;
-}
-
-void
-warning(Mntdir *md, char *s, ...)
-{
-	Rune *r;
-	va_list arg;
-
-	va_start(arg, s);
-	r = runevsmprint(s, arg);
-	va_end(arg);
-	if(r == nil)
-		error("runevsmprint failed");
-	addwarningtext(md, r, runestrlen(r));
-	free(r);
-}
-
-int
-runeeq(Rune *s1, uint n1, Rune *s2, uint n2)
-{
-	if(n1 != n2)
-		return FALSE;
-	return memcmp(s1, s2, n1*sizeof(Rune)) == 0;
-}
-
-uint
-min(uint a, uint b)
-{
-	if(a < b)
-		return a;
-	return b;
-}
-
-uint
-max(uint a, uint b)
-{
-	if(a > b)
-		return a;
-	return b;
-}
-
-char*
-runetobyte(Rune *r, int n)
-{
-	char *s;
-
-	if(r == nil)
-		return nil;
-	s = emalloc(n*UTFmax+1);
-	setmalloctag(s, getcallerpc(&r));
-	snprint(s, n*UTFmax+1, "%.*S", n, r);
-	return s;
-}
-
-Rune*
-bytetorune(char *s, int *ip)
-{
-	Rune *r;
-	int nb, nr;
-
-	nb = strlen(s);
-	r = runemalloc(nb+1);
-	cvttorunes(s, nb, r, &nb, &nr, nil);
-	r[nr] = '\0';
-	*ip = nr;
-	return r;
-}
-
-int
-isspace(Rune c)
-{
-	return c == 0 || c == ' ' || c == '\t' ||
-		c == '\n' || c == '\r' || c == '\v';
-}
-
-int
-isalnum(Rune c)
-{
-	/*
-	 * Hard to get absolutely right.  Use what we know about ASCII
-	 * and assume anything above the Latin control characters is
-	 * potentially an alphanumeric.
-	 *
-	 * Treat 0xA0 (non-breaking space) as a special alphanumeric
-	 * character [sape]
-	 */
-	if(c <= ' ')
-		return FALSE;
-	if(0x7F<=c && c<0xA0)
-		return FALSE;
-	if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
-		return FALSE;
-	return TRUE;
-}
-
-int
-rgetc(void *v, uint n)
-{
-	return ((Rune*)v)[n];
-}
-
-int
-tgetc(void *a, uint n)
-{
-	Text *t;
-
-	t = a;
-	if(n >= t->file->nc)
-		return 0;
-	return textreadc(t, n);
-}
-
-Rune*
-skipbl(Rune *r, int n, int *np)
-{
-	while(n>0 && (*r==' ' || *r=='\t' || *r=='\n')){
-		--n;
-		r++;
-	}
-	*np = n;
-	return r;
-}
-
-Rune*
-findbl(Rune *r, int n, int *np)
-{
-	while(n>0 && *r!=' ' && *r!='\t' && *r!='\n'){
-		--n;
-		r++;
-	}
-	*np = n;
-	return r;
-}
-
-void
-savemouse(Window *w)
-{
-	prevmouse = mouse->xy;
-	mousew = w;
-}
-
-int
-restoremouse(Window *w)
-{
-	int did;
-
-	did = 0;
-	if(mousew!=nil && mousew==w) {
-		moveto(mousectl, prevmouse);
-		did = 1;
-	}
-	mousew = nil;
-	return did;
-}
-
-void
-clearmouse()
-{
-	mousew = nil;
-}
-
-char*
-estrdup(char *s)
-{
-	char *t;
-
-	t = strdup(s);
-	if(t == nil)
-		error("strdup failed");
-	setmalloctag(t, getcallerpc(&s));
-	return t;
-}
-
-void*
-emalloc(uint n)
-{
-	void *p;
-
-	p = malloc(n);
-	if(p == nil)
-		error("malloc failed");
-	setmalloctag(p, getcallerpc(&n));
-	memset(p, 0, n);
-	return p;
-}
-
-void*
-erealloc(void *p, uint n)
-{
-	p = realloc(p, n);
-	if(p == nil)
-		error("realloc failed");
-	setmalloctag(p, getcallerpc(&n));
-	return p;
-}
-
-/*
- * Heuristic city.
- */
-Window*
-makenewwindow(Text *t)
-{
-	Column *c;
-	Window *w, *bigw, *emptyw;
-	Text *emptyb;
-	int i, y, el;
-
-	if(activecol)
-		c = activecol;
-	else if(seltext && seltext->col)
-		c = seltext->col;
-	else if(t && t->col)
-		c = t->col;
-	else{
-		if(row.ncol==0 && rowadd(&row, nil, -1)==nil)
-			error("can't make column");
-		c = row.col[row.ncol-1];
-	}
-	activecol = c;
-	if(t==nil || t->w==nil || c->nw==0)
-		return coladd(c, nil, nil, -1);
-
-	/* find biggest window and biggest blank spot */
-	emptyw = c->w[0];
-	bigw = emptyw;
-	for(i=1; i<c->nw; i++){
-		w = c->w[i];
-		/* use >= to choose one near bottom of screen */
-		if(w->body.maxlines >= bigw->body.maxlines)
-			bigw = w;
-		if(w->body.maxlines-w->body.nlines >= emptyw->body.maxlines-emptyw->body.nlines)
-			emptyw = w;
-	}
-	emptyb = &emptyw->body;
-	el = emptyb->maxlines-emptyb->nlines;
-	/* if empty space is big, use it */
-	if(el>15 || (el>3 && el>(bigw->body.maxlines-1)/2))
-		y = emptyb->r.min.y+emptyb->nlines*font->height;
-	else{
-		/* if this window is in column and isn't much smaller, split it */
-		if(t->col==c && Dy(t->w->r)>2*Dy(bigw->r)/3)
-			bigw = t->w;
-		y = (bigw->r.min.y + bigw->r.max.y)/2;
-	}
-	w = coladd(c, nil, nil, y);
-	if(w->body.maxlines < 2)
-		colgrow(w->col, w, 1);
-	return w;
-}
--- a/wind.c
+++ /dev/null
@@ -1,697 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <thread.h>
-#include <cursor.h>
-#include <mouse.h>
-#include <keyboard.h>
-#include <frame.h>
-#include <fcall.h>
-#include <plumb.h>
-#include "dat.h"
-#include "fns.h"
-
-int	winid;
-
-void
-wininit(Window *w, Window *clone, Rectangle r)
-{
-	Rectangle r1, br;
-	File *f;
-	Reffont *rf;
-	Rune *rp;
-	int nc, i;
-
-	w->tag.w = w;
-	w->taglines = 1;
-	w->tagexpand = TRUE;
-	w->body.w = w;
-	w->id = ++winid;
-	incref(w);
-	if(globalincref)
-		incref(w);
-	w->ctlfid = ~0;
-	w->utflastqid = -1;
-	r1 = r;
-	r1.max.y = r1.min.y + font->height;
-	w->tagtop = r;
-	w->tagtop.max.y = r.min.y + font->height;
-	incref(&reffont);
-	f = fileaddtext(nil, &w->tag);
-	textinit(&w->tag, f, r1, &reffont, tagcols);
-	w->tag.what = Tag;
-	/* tag is a copy of the contents, not a tracked image */
-	if(clone){
-		textdelete(&w->tag, 0, w->tag.file->nc, TRUE);
-		nc = clone->tag.file->nc;
-		rp = runemalloc(nc);
-		bufread(clone->tag.file, 0, rp, nc);
-		textinsert(&w->tag, 0, rp, nc, TRUE);
-		free(rp);
-		filereset(w->tag.file);
-		textsetselect(&w->tag, nc, nc);
-	}
-	r1 = r;
-	r1.min.y += w->taglines*font->height + 1;
-	if(r1.max.y < r1.min.y)
-		r1.max.y = r1.min.y;
-	f = nil;
-	if(clone){
-		f = clone->body.file;
-		w->body.org = clone->body.org;
-		w->isscratch = clone->isscratch;
-		rf = rfget(FALSE, FALSE, FALSE, clone->body.reffont->f->name);
-	}else
-		rf = rfget(FALSE, FALSE, FALSE, nil);
-	f = fileaddtext(f, &w->body);
-	w->body.what = Body;
-	textinit(&w->body, f, r1, rf, textcols);
-	r1.min.y -= 1;
-	r1.max.y = r1.min.y+1;
-	draw(screen, r1, tagcols[BORD], nil, ZP);
-	textscrdraw(&w->body);
-	w->r = r;
-	w->r.max.y = w->body.r.max.y;
-	br.min = w->tag.scrollr.min;
-	br.max.x = br.min.x + Dx(button->r);
-	br.max.y = br.min.y + Dy(button->r);
-	draw(screen, br, button, nil, button->r.min);
-	w->filemenu = TRUE;
-	w->maxlines = w->body.maxlines;
-	for(i=0; i<NINDENT; i++)
-		w->indent[i] = globalindent[i];
-	if(clone){
-		w->dirty = clone->dirty;
-		for(i=0; i<NINDENT; i++)
-			w->indent[i] = clone->indent[i];
-		textsetselect(&w->body, clone->body.q0, clone->body.q1);
-		winsettag(w);
-	}
-}
-
-int
-tagrunepos(Window *w, Rune *s)
-{
-	int n;
-	Rune *r, *rr;
-
-	if(s == nil)
-		return -1;
-
-	n = w->tag.file->nc;
-	r = runemalloc(n+1);
-	bufread(w->tag.file, 0, r, n);
-	r[n] = L'\0';
-
-	rr = runestrstr(r, s);
-	if(rr == nil || rr == r)
-		return -1;
-	return rr - r;
-}
-
-void
-movetodel(Window *w)
-{
-	int n;
-	
-	n = tagrunepos(w, delcmd);
-	free(delcmd);
-	delcmd = nil;
-	if(n < 0)
-		return;
-	moveto(mousectl, addpt(frptofchar(&w->tag, n), Pt(4, w->tag.font->height-4)));
-}
-
-/*
- * Compute number of tag lines required
- * to display entire tag text.
- */
-int
-wintaglines(Window *w, Rectangle r)
-{
-	int n;
-	Rune rune;
-	Point p;
-
-	if(!w->tagexpand && !w->showdel)
-		return 1;
-	w->showdel = FALSE;
-	w->noredraw = 1;
-	textresize(&w->tag, r);
-	w->noredraw = 0;
-	w->tagsafe = FALSE;
-	
-	if(!w->tagexpand) {
-		/* use just as many lines as needed to show the Del */
-		n = tagrunepos(w, delcmd);
-		if(n < 0)
-			return 1;
-		p = subpt(frptofchar(&w->tag, n), w->tag.r.min);
-		return 1 + p.y / w->tag.font->height;
-	}
-		
-	/* can't use more than we have */
-	if(w->tag.nlines >= w->tag.maxlines)
-		return w->tag.maxlines;
-
-	/* if tag ends with \n, include empty line at end for typing */
-	n = w->tag.nlines;
-	if(w->tag.file->nc > 0){
-		bufread(w->tag.file, w->tag.file->nc-1, &rune, 1);
-		if(rune == '\n')
-			n++;
-	}
-	if(n == 0)
-		n = 1;
-	return n;
-}
-
-int
-winresize(Window *w, Rectangle r, int safe)
-{
-	int oy, mouseintag, mouseinbody;
-	Point p;
-	Rectangle r1;
-	int y;
-	Image *b;
-	Rectangle br;
-
-	mouseintag = ptinrect(mouse->xy, w->tag.all);
-	mouseinbody = ptinrect(mouse->xy, w->body.all);
-
-	w->tagtop = r;
-	w->tagtop.max.y = r.min.y+font->height;
-
-	r1 = r;
-	r1.max.y = r1.min.y + font->height;
-	r1.max.y = min(r.max.y, r1.min.y + w->taglines*font->height);
-
-	if(!safe || !w->tagsafe || !eqrect(w->tag.all, r1)){
-		w->taglines = wintaglines(w, r);
-		r1.max.y = min(r.max.y, r1.min.y + w->taglines*font->height);
-	}
-	if(Dy(r1) < font->height)
-		r1.max.y = r1.min.y+font->height;
-	y = r1.max.y;
-	if(!safe || !eqrect(w->tag.r, r1)){
-		textresize(&w->tag, r1);
-		y = w->tag.r.max.y;
-		b = button;
-		if(w->body.file->mod && !w->isdir && !w->isscratch)
-			b = modbutton;
-		br.min = w->tag.scrollr.min;
-		br.max.x = br.min.x + Dx(b->r);
-		br.max.y = br.min.y + Dy(b->r);
-		draw(screen, br, b, nil, b->r.min);
-
-		w->tagsafe = TRUE;
-
-		/* If mouse is in tag, pull up as tag closes. */
-		if(mouseintag && !ptinrect(mouse->xy, w->tag.all)){
-			p = mouse->xy;
-			p.y = w->tag.all.max.y-3;
-			moveto(mousectl, p);
-		}
-
-		/* If mouse is in body, push down as tag expands. */
-		if(mouseinbody && ptinrect(mouse->xy, w->tag.all)){
-			p = mouse->xy;
-			p.y = w->tag.all.max.y+3;
-			moveto(mousectl, p);
-		}
-
-	}
-	if(!safe || !eqrect(w->body.r, r1)){
-		oy = y;
-		if(y+1+w->body.font->height <= r.max.y){	/* room for one line */
-  			r1.min.y = y;
-			r1.max.y = y+1;
-			draw(screen, r1, tagcols[BORD], nil, ZP);
-			y++;
-			r1.min.y = min(y, r.max.y);
-			r1.max.y = r.max.y;
-		}else{
-			r1.min.y = y;
-			r1.max.y = y;
-		}
-		y = textresize(&w->body, r1);
-		w->r = r;
-		w->r.max.y = y;
-		textscrdraw(&w->body);
-		w->body.all.min.y = oy;
-	}
-	w->maxlines = min(w->body.nlines, max(w->maxlines, w->body.maxlines));
-	return w->r.max.y;
-}
-
-void
-winlock1(Window *w, int owner)
-{
-	incref(w);
-	qlock(w);
-	w->owner = owner;
-}
-
-void
-winlock(Window *w, int owner)
-{
-	int i;
-	File *f;
-
-	f = w->body.file;
-	for(i=0; i<f->ntext; i++)
-		winlock1(f->text[i]->w, owner);
-}
-
-void
-winunlock(Window *w)
-{
-	int i;
-	File *f;
-
-	/*
-	 * subtle: loop runs backwards to avoid tripping over
-	 * winclose indirectly editing f->text and freeing f
-	 * on the last iteration of the loop.
-	 */
-	f = w->body.file;
-	for(i=f->ntext-1; i>=0; i--){
-		w = f->text[i]->w;
-		w->owner = 0;
-		qunlock(w);
-		winclose(w);
-	}
-}
-
-void
-winmousebut(Window *w)
-{
-	moveto(mousectl, addpt(w->tag.scrollr.min, divpt(Pt(Dx(w->tag.scrollr), font->height), 2)));
-}
-
-void
-windirfree(Window *w)
-{
-	int i;
-	Dirlist *dl;
-
-	if(w->isdir){
-		for(i=0; i<w->ndl; i++){
-			dl = w->dlp[i];
-			free(dl->r);
-			free(dl);
-		}
-		free(w->dlp);
-	}
-	w->dlp = nil;
-	w->ndl = 0;
-}
-
-void
-winclose(Window *w)
-{
-	int i;
-
-	if(decref(w) == 0){
-		xfidlog(w, "del");
-		windirfree(w);
-		textclose(&w->tag);
-		textclose(&w->body);
-		if(activewin == w)
-			activewin = nil;
-		for(i=0; i<w->nincl; i++)
-			free(w->incl[i]);
-		free(w->incl);
-		free(w->events);
-		free(w);
-	}
-}
-
-void
-windelete(Window *w)
-{
-	Xfid *x;
-
-	x = w->eventx;
-	if(x){
-		w->nevents = 0;
-		free(w->events);
-		w->events = nil;
-		w->eventx = nil;
-		sendp(x->c, nil);	/* wake him up */
-	}
-}
-
-void
-winundo(Window *w, int isundo)
-{
-	Text *body;
-	int i;
-	File *f;
-	Window *v;
-
-	w->utflastqid = -1;
-	body = &w->body;
-	fileundo(body->file, isundo, &body->q0, &body->q1);
-	textshow(body, body->q0, body->q1, 1);
-	f = body->file;
-	for(i=0; i<f->ntext; i++){
-		v = f->text[i]->w;
-		v->dirty = (f->seq != v->putseq);
-		if(v != w){
-			v->body.q0 = v->body.p0+v->body.org;
-			v->body.q1 = v->body.p1+v->body.org;
-		}
-	}
-	winsettag(w);
-}
-
-void
-winsetname(Window *w, Rune *name, int n)
-{
-	Text *t;
-	Window *v;
-	int i;
-
-	t = &w->body;
-	if(runeeq(t->file->name, t->file->nname, name, n) == TRUE)
-		return;
-	w->isscratch = FALSE;
-	if(n>=6 && runeeq(L"/guide", 6, name+(n-6), 6))
-		w->isscratch = TRUE;
-	else if(n>=7 && runeeq(L"+Errors", 7, name+(n-7), 7))
-		w->isscratch = TRUE;
-	filesetname(t->file, name, n);
-	for(i=0; i<t->file->ntext; i++){
-		v = t->file->text[i]->w;
-		winsettag(v);
-		v->isscratch = w->isscratch;
-	}
-}
-
-void
-wintype(Window *w, Text *t, Rune r)
-{
-	int i;
-
-	texttype(t, r);
-	if(t->what == Tag)
-		w->tagsafe = FALSE;
-	if(t->what == Body)
-		for(i=0; i<t->file->ntext; i++)
-			textscrdraw(t->file->text[i]);
-	winsettag(w);
-}
-
-void
-wincleartag(Window *w)
-{
-	int i, n;
-	Rune *r;
-
-	/* w must be committed */
-	n = w->tag.file->nc;
-	r = runemalloc(n);
-	bufread(w->tag.file, 0, r, n);
-	for(i=0; i<n; i++)
-		if(r[i]==' ' || r[i]=='\t')
-			break;
-	for(; i<n; i++)
-		if(r[i] == '|')
-			break;
-	if(i == n)
-		return;
-	i++;
-	textdelete(&w->tag, i, n, TRUE);
-	free(r);
-	w->tag.file->mod = FALSE;
-	if(w->tag.q0 > i)
-		w->tag.q0 = i;
-	if(w->tag.q1 > i)
-		w->tag.q1 = i;
-	textsetselect(&w->tag, w->tag.q0, w->tag.q1);
-}
-
-void
-winsettag1(Window *w)
-{
-	int i, j, k, n, bar, dirty;
-	Rune *new, *old, *r;
-	Image *b;
-	uint q0, q1;
-	Rectangle br;
-
-	/* there are races that get us here with stuff in the tag cache, so we take extra care to sync it */
-	if(w->tag.ncache!=0 || w->tag.file->mod)
-		wincommit(w, &w->tag);	/* check file name; also guarantees we can modify tag contents */
-	old = runemalloc(w->tag.file->nc+1);
-	bufread(w->tag.file, 0, old, w->tag.file->nc);
-	old[w->tag.file->nc] = '\0';
-	for(i=0; i<w->tag.file->nc; i++)
-		if(old[i]==' ' || old[i]=='\t')
-			break;
-	if(runeeq(old, i, w->body.file->name, w->body.file->nname) == FALSE){
-		textdelete(&w->tag, 0, i, TRUE);
-		textinsert(&w->tag, 0, w->body.file->name, w->body.file->nname, TRUE);
-		free(old);
-		old = runemalloc(w->tag.file->nc+1);
-		bufread(w->tag.file, 0, old, w->tag.file->nc);
-		old[w->tag.file->nc] = '\0';
-		w->tagsafe = FALSE;
-	}
-	new = runemalloc(w->body.file->nname+100);
-	i = 0;
-	runemove(new+i, w->body.file->name, w->body.file->nname);
-	i += w->body.file->nname;
-	runemove(new+i, L" Del Snarf", 10);
-	i += 10;
-	if(w->filemenu){
-		if(w->body.file->delta.nc>0 || w->body.ncache){
-			runemove(new+i, L" Undo", 5);
-			i += 5;
-		}
-		if(w->body.file->epsilon.nc > 0){
-			runemove(new+i, L" Redo", 5);
-			i += 5;
-		}
-		dirty = w->body.file->nname && (w->body.ncache || w->body.file->seq!=w->putseq);
-		if(!w->isdir && dirty){
-			runemove(new+i, L" Put", 4);
-			i += 4;
-		}
-	}
-	if(w->isdir){
-		runemove(new+i, L" Get", 4);
-		i += 4;
-	}
-	runemove(new+i, L" |", 2);
-	i += 2;
-	r = runestrchr(old, '|');
-	if(r)
-		k = r-old+1;
-	else{
-		k = w->tag.file->nc;
-		if(w->body.file->seq == 0){
-			runemove(new+i, L" Look ", 6);
-			i += 6;
-		}
-	}
-
-	new[i] = 0;
-	/* replace tag if the new one is different */
-	if(runeeq(new, i, old, k) == FALSE){
-		n = k;
-		if(n > i)
-			n = i;
-		for(j=0; j<n; j++)
-			if(old[j] != new[j])
-				break;
-		q0 = w->tag.q0;
-		q1 = w->tag.q1;
-		textdelete(&w->tag, j, k, TRUE);
-		textinsert(&w->tag, j, new+j, i-j, TRUE);
-		/* try to preserve user selection */
-		r = runestrchr(old, '|');
-		if(r){
-			bar = r-old;
-			if(q0 > bar){
-				bar = (runestrchr(new, '|')-new)-bar;
-				w->tag.q0 = q0+bar;
-				w->tag.q1 = q1+bar;
-			}
-		}
-		w->tagsafe = FALSE;
-	}
-	free(old);
-	free(new);
-	w->tag.file->mod = FALSE;
-	n = w->tag.file->nc+w->tag.ncache;
-	if(w->tag.q0 > n)
-		w->tag.q0 = n;
-	if(w->tag.q1 > n)
-		w->tag.q1 = n;
-	textsetselect(&w->tag, w->tag.q0, w->tag.q1);
-	b = button;
-	if(!w->isdir && !w->isscratch && (w->body.file->mod || w->body.ncache))
-		b = modbutton;
-	br.min = w->tag.scrollr.min;
-	br.max.x = br.min.x + Dx(b->r);
-	br.max.y = br.min.y + Dy(b->r);
-	draw(screen, br, b, nil, b->r.min);
-	if(w->tagsafe == FALSE)
-		winresize(w, w->r, TRUE);
-}
-
-void
-winsettag(Window *w)
-{
-	int i;
-	File *f;
-	Window *v;
-
-	f = w->body.file;
-	for(i=0; i<f->ntext; i++){
-		v = f->text[i]->w;
-		if(v->col->safe || v->body.maxlines>0)
-			winsettag1(v);
-	}
-}
-
-void
-wincommit(Window *w, Text *t)
-{
-	Rune *r;
-	int i;
-	File *f;
-
-	textcommit(t, TRUE);
-	f = t->file;
-	if(f->ntext > 1)
-		for(i=0; i<f->ntext; i++)
-			textcommit(f->text[i], FALSE);	/* no-op for t */
-	if(t->what == Body)
-		return;
-	r = runemalloc(w->tag.file->nc);
-	bufread(w->tag.file, 0, r, w->tag.file->nc);
-	for(i=0; i<w->tag.file->nc; i++)
-		if(r[i]==' ' || r[i]=='\t')
-			break;
-	if(runeeq(r, i, w->body.file->name, w->body.file->nname) == FALSE){
-		seq++;
-		filemark(w->body.file);
-		w->body.file->mod = TRUE;
-		w->dirty = TRUE;
-		winsetname(w, r, i);
-		winsettag(w);
-	}
-	free(r);
-}
-
-void
-winaddincl(Window *w, Rune *r, int n)
-{
-	char *a;
-	Dir *d;
-	Runestr rs;
-
-	a = runetobyte(r, n);
-	d = dirstat(a);
-	if(d == nil){
-		if(a[0] == '/')
-			goto Rescue;
-		rs = dirname(&w->body, r, n);
-		r = rs.r;
-		n = rs.nr;
-		free(a);
-		a = runetobyte(r, n);
-		d = dirstat(a);
-		if(d == nil)
-			goto Rescue;
-		r = runerealloc(r, n+1);
-		r[n] = 0;
-	}
-	if((d->qid.type&QTDIR) == 0){
-		free(d);
-		warning(nil, "%s: not a directory\n", a);
-		free(r);
-		free(a);
-		return;
-	}
-	free(a);
-	free(d);
-	w->nincl++;
-	w->incl = realloc(w->incl, w->nincl*sizeof(Rune*));
-	memmove(w->incl+1, w->incl, (w->nincl-1)*sizeof(Rune*));
-	w->incl[0] = runemalloc(n+1);
-	runemove(w->incl[0], r, n);
-	free(r);
-	return;
-
-Rescue:
-	warning(nil, "%s: %r\n", a);
-	free(r);
-	free(a);
-	return;
-}
-
-int
-winclean(Window *w, int conservative)
-{
-	if(w->isscratch || w->isdir)	/* don't whine if it's a guide file, error window, etc. */
-		return TRUE;
-	if(!conservative && w->nopen[QWevent]>0)
-		return TRUE;
-	if(w->dirty){
-		if(w->body.file->nname)
-			warning(nil, "%.*S modified\n", w->body.file->nname, w->body.file->name);
-		else{
-			if(w->body.file->nc < 100)	/* don't whine if it's too small */
-				return TRUE;
-			warning(nil, "unnamed file modified\n");
-		}
-		w->dirty = FALSE;
-		return FALSE;
-	}
-	return TRUE;
-}
-
-char*
-winctlprint(Window *w, char *buf, int fonts)
-{
-	sprint(buf, "%11d %11d %11d %11d %11d ", w->id, w->tag.file->nc,
-		w->body.file->nc, w->isdir, w->dirty);
-	if(fonts)
-		return smprint("%s%11d %q %11d " , buf, Dx(w->body.r), 
-			w->body.reffont->f->name, w->body.maxtab);
-	return buf;
-}
-
-void
-winevent(Window *w, char *fmt, ...)
-{
-	int n;
-	char *b;
-	Xfid *x;
-	va_list arg;
-
-	if(w->nopen[QWevent] == 0)
-		return;
-	if(w->owner == 0)
-		error("no window owner");
-	va_start(arg, fmt);
-	b = vsmprint(fmt, arg);
-	va_end(arg);
-	if(b == nil)
-		error("vsmprint failed");
-	n = strlen(b);
-	w->events = erealloc(w->events, w->nevents+1+n);
-	w->events[w->nevents++] = w->owner;
-	memmove(w->events+w->nevents, b, n);
-	free(b);
-	w->nevents += n;
-	x = w->eventx;
-	if(x){
-		w->eventx = nil;
-		sendp(x->c, nil);
-	}
-}
--- a/xfid.c
+++ /dev/null
@@ -1,1082 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <thread.h>
-#include <cursor.h>
-#include <mouse.h>
-#include <keyboard.h>
-#include <frame.h>
-#include <fcall.h>
-#include <plumb.h>
-#include "dat.h"
-#include "fns.h"
-
-enum
-{
-	Ctlsize	= 5*12
-};
-
-char	Edel[]		= "deleted window";
-char	Ebadctl[]		= "ill-formed control message";
-char	Ebadaddr[]	= "bad address syntax";
-char	Eaddr[]		= "address out of range";
-char	Einuse[]		= "already in use";
-char	Ebadevent[]	= "bad event syntax";
-extern char Eperm[];
-
-static
-void
-clampaddr(Window *w)
-{
-	if(w->addr.q0 < 0)
-		w->addr.q0 = 0;
-	if(w->addr.q1 < 0)
-		w->addr.q1 = 0;
-	if(w->addr.q0 > w->body.file->nc)
-		w->addr.q0 = w->body.file->nc;
-	if(w->addr.q1 > w->body.file->nc)
-		w->addr.q1 = w->body.file->nc;
-}
-
-void
-xfidctl(void *arg)
-{
-	Xfid *x;
-	void (*f)(Xfid*);
-
-	threadsetname("xfidctlthread");
-	x = arg;
-	for(;;){
-		f = recvp(x->c);
-		(*f)(x);
-		flushimage(display, 1);
-		sendp(cxfidfree, x);
-	}
-}
-
-void
-xfidflush(Xfid *x)
-{
-	Fcall fc;
-	int i, j;
-	Window *w;
-	Column *c;
-	Xfid *wx;
-
-	xfidlogflush(x);
-
-	/* search windows for matching tag */
-	qlock(&row);
-	for(j=0; j<row.ncol; j++){
-		c = row.col[j];
-		for(i=0; i<c->nw; i++){
-			w = c->w[i];
-			winlock(w, 'E');
-			wx = w->eventx;
-			if(wx!=nil && wx->tag==x->oldtag){
-				w->eventx = nil;
-				wx->flushed = TRUE;
-				sendp(wx->c, nil);
-				winunlock(w);
-				goto out;
-			}
-			winunlock(w);
-		}
-	}
-out:
-	qunlock(&row);
-	respond(x, &fc, nil);
-}
-
-void
-xfidopen(Xfid *x)
-{
-	Fcall fc;
-	Window *w;
-	Text *t;
-	char *s;
-	Rune *r;
-	int m, n, q, q0, q1;
-
-	w = x->f->w;
-	t = &w->body;
-	q = FILE(x->f->qid);
-	if(w){
-		winlock(w, 'E');
-		switch(q){
-		case QWaddr:
-			if(w->nopen[q]++ == 0){
-				w->addr = (Range){0,0};
-				w->limit = (Range){-1,-1};
-			}
-			break;
-		case QWdata:
-		case QWxdata:
-			w->nopen[q]++;
-			break;
-		case QWevent:
-			if(w->nopen[q]++ == 0){
-				if(!w->isdir && w->col!=nil){
-					w->filemenu = FALSE;
-					winsettag(w);
-				}
-			}
-			break;
-		case QWrdsel:
-			/*
-			 * Use a temporary file.
-			 * A pipe would be the obvious, but we can't afford the
-			 * broken pipe notification.  Using the code to read QWbody
-			 * is n², which should probably also be fixed.  Even then,
-			 * though, we'd need to squirrel away the data in case it's
-			 * modified during the operation, e.g. by |sort
-			 */
-			if(w->rdselfd >= 0){
-				winunlock(w);
-				respond(x, &fc, Einuse);
-				return;
-			}
-			w->rdselfd = tempfile();
-			if(w->rdselfd < 0){
-				winunlock(w);
-				respond(x, &fc, "can't create temp file");
-				return;
-			}
-			w->nopen[q]++;
-			q0 = t->q0;
-			q1 = t->q1;
-			r = fbufalloc();
-			s = fbufalloc();
-			while(q0 < q1){
-				n = q1 - q0;
-				if(n > (BUFSIZE-1)/UTFmax)
-					n = (BUFSIZE-1)/UTFmax;
-				bufread(t->file, q0, r, n);
-				m = snprint(s, BUFSIZE, "%.*S", n, r);
-				if(write(w->rdselfd, s, m) != m){
-					warning(nil, "can't write temp file for pipe command %r\n");
-					break;
-				}
-				q0 += n;
-			}
-			fbuffree(s);
-			fbuffree(r);
-			break;
-		case QWwrsel:
-			w->nopen[q]++;
-			seq++;
-			filemark(t->file);
-			cut(t, t, nil, FALSE, TRUE, nil, 0);
-			w->wrselrange = (Range){t->q1, t->q1};
-			w->nomark = TRUE;
-			break;
-		case QWeditout:
-			if(editing == FALSE){
-				winunlock(w);
-				respond(x, &fc, Eperm);
-				return;
-			}
-			w->wrselrange = (Range){t->q1, t->q1};
-			break;
-		}
-		winunlock(w);
-	}
-	else{
-		switch(q){
-		case Qlog:
-			xfidlogopen(x);
-			break;
-		}
-	}
-	fc.qid = x->f->qid;
-	fc.iounit = messagesize-IOHDRSZ;
-	x->f->open = TRUE;
-	respond(x, &fc, nil);
-}
-
-void
-xfidclose(Xfid *x)
-{
-	Fcall fc;
-	Window *w;
-	int q;
-	Text *t;
-
-	w = x->f->w;
-	x->f->busy = FALSE;
-	if(x->f->open == FALSE){
-		if(w != nil)
-			winclose(w);
-		respond(x, &fc, nil);
-		return;
-	}
-
-	x->f->open = FALSE;
-	if(w){
-		winlock(w, 'E');
-		q = FILE(x->f->qid);
-		switch(q){
-		case QWctl:
-			if(w->ctlfid!=~0 && w->ctlfid==x->f->fid){
-				w->ctlfid = ~0;
-				qunlock(&w->ctllock);
-			}
-			break;
-		case QWdata:
-		case QWxdata:
-			w->nomark = FALSE;
-			/* fall through */
-		case QWaddr:
-		case QWevent:	/* BUG: do we need to shut down Xfid? */
-			if(--w->nopen[q] == 0){
-				if(q == QWdata || q == QWxdata)
-					w->nomark = FALSE;
-				if(q==QWevent && !w->isdir && w->col!=nil){
-					w->filemenu = TRUE;
-					winsettag(w);
-				}
-				if(q == QWevent){
-					free(w->dumpstr);
-					free(w->dumpdir);
-					w->dumpstr = nil;
-					w->dumpdir = nil;
-				}
-			}
-			break;
-		case QWrdsel:
-			close(w->rdselfd);
-			w->rdselfd = -1;
-			break;
-		case QWwrsel:
-			w->nomark = FALSE;
-			t = &w->body;
-			/* before: only did this if !w->noscroll, but that didn't seem right in practice */
-			textshow(t, min(w->wrselrange.q0, t->file->nc),
-				min(w->wrselrange.q1, t->file->nc), 1);
-			textscrdraw(t);
-			break;
-		}
-		winunlock(w);
-		winclose(w);
-	}
-	respond(x, &fc, nil);
-}
-
-void
-xfidread(Xfid *x)
-{
-	Fcall fc;
-	int n, q;
-	uint off;
-	char *b;
-	char buf[256];
-	Window *w;
-
-	q = FILE(x->f->qid);
-	w = x->f->w;
-	if(w == nil){
-		fc.count = 0;
-		switch(q){
-		case Qcons:
-		case Qlabel:
-			break;
-		case Qindex:
-			xfidindexread(x);
-			return;
-		case Qlog:
-			xfidlogread(x);
-			return;
-		default:
-			warning(nil, "unknown qid %d\n", q);
-			break;
-		}
-		respond(x, &fc, nil);
-		return;
-	}
-	winlock(w, 'F');
-	if(w->col == nil){
-		winunlock(w);
-		respond(x, &fc, Edel);
-		return;
-	}
-	off = x->offset;
-	switch(q){
-	case QWaddr:
-		textcommit(&w->body, TRUE);
-		clampaddr(w);
-		sprint(buf, "%11d %11d ", w->addr.q0, w->addr.q1);
-		goto Readbuf;
-
-	case QWbody:
-		xfidutfread(x, &w->body, w->body.file->nc, QWbody);
-		break;
-
-	case QWctl:
-		b = winctlprint(w, buf, 1);
-		goto Readb;
-
-	Readbuf:
-		b = buf;
-	Readb:
-		n = strlen(b);
-		if(off > n)
-			off = n;
-		if(off+x->count > n)
-			x->count = n-off;
-		fc.count = x->count;
-		fc.data = b+off;
-		respond(x, &fc, nil);
-		if(b != buf)
-			free(b);
-		break;
-
-	case QWevent:
-		xfideventread(x, w);
-		break;
-
-	case QWdata:
-		/* BUG: what should happen if q1 > q0? */
-		if(w->addr.q0 > w->body.file->nc){
-			respond(x, &fc, Eaddr);
-			break;
-		}
-		w->addr.q0 += xfidruneread(x, &w->body, w->addr.q0, w->body.file->nc);
-		w->addr.q1 = w->addr.q0;
-		break;
-
-	case QWxdata:
-		/* BUG: what should happen if q1 > q0? */
-		if(w->addr.q0 > w->body.file->nc){
-			respond(x, &fc, Eaddr);
-			break;
-		}
-		w->addr.q0 += xfidruneread(x, &w->body, w->addr.q0, w->addr.q1);
-		break;
-
-	case QWtag:
-		xfidutfread(x, &w->tag, w->tag.file->nc, QWtag);
-		break;
-
-	case QWrdsel:
-		seek(w->rdselfd, off, 0);
-		n = x->count;
-		if(n > BUFSIZE)
-			n = BUFSIZE;
-		b = fbufalloc();
-		n = read(w->rdselfd, b, n);
-		if(n < 0){
-			respond(x, &fc, "I/O error in temp file");
-			break;
-		}
-		fc.count = n;
-		fc.data = b;
-		respond(x, &fc, nil);
-		fbuffree(b);
-		break;
-
-	default:
-		sprint(buf, "unknown qid %d in read", q);
-		respond(x, &fc, nil);
-	}
-	winunlock(w);
-}
-
-static Rune*
-fullrunewrite(Xfid *x, int *inr)
-{
-	int q, cnt, c, nb, nr;
-	Rune *r;
-
-	q = x->f->nrpart;
-	cnt = x->count;
-	if(q > 0){
-		memmove(x->data+q, x->data, cnt);	/* there's room; see fsysproc */
-		memmove(x->data, x->f->rpart, q);
-		cnt += q;
-		x->f->nrpart = 0;
-	}
-	r = runemalloc(cnt);
-	cvttorunes(x->data, cnt-UTFmax, r, &nb, &nr, nil);
-	/* approach end of buffer */
-	while(fullrune(x->data+nb, cnt-nb)){
-		c = nb;
-		nb += chartorune(&r[nr], x->data+c);
-		if(r[nr])
-			nr++;
-	}
-	if(nb < cnt){
-		memmove(x->f->rpart, x->data+nb, cnt-nb);
-		x->f->nrpart = cnt-nb;
-	}
-	*inr = nr;
-	return r;
-}
-
-void
-xfidwrite(Xfid *x)
-{
-	Fcall fc;
-	int c, qid, nb, nr, eval;
-	char buf[64], *err;
-	Window *w;
-	Rune *r;
-	Range a;
-	Text *t;
-	uint q0, tq0, tq1;
-
-	qid = FILE(x->f->qid);
-	w = x->f->w;
-	if(w){
-		c = 'F';
-		if(qid==QWtag || qid==QWbody)
-			c = 'E';
-		winlock(w, c);
-		if(w->col == nil){
-			winunlock(w);
-			respond(x, &fc, Edel);
-			return;
-		}
-	}
-	x->data[x->count] = 0;
-	switch(qid){
-	case Qcons:
-		w = errorwin(x->f->mntdir, 'X');
-		t=&w->body;
-		goto BodyTag;
-
-	case Qlabel:
-		fc.count = x->count;
-		respond(x, &fc, nil);
-		break;
-
-	case QWaddr:
-		x->data[x->count] = 0;
-		r = bytetorune(x->data, &nr);
-		t = &w->body;
-		wincommit(w, t);
-		eval = TRUE;
-		a = address(x->f->mntdir, t, w->limit, w->addr, r, 0, nr, rgetc, &eval, (uint*)&nb);
-		free(r);
-		if(nb < nr){
-			respond(x, &fc, Ebadaddr);
-			break;
-		}
-		if(!eval){
-			respond(x, &fc, Eaddr);
-			break;
-		}
-		w->addr = a;
-		fc.count = x->count;
-		respond(x, &fc, nil);
-		break;
-
-	case Qeditout:
-	case QWeditout:
-		r = fullrunewrite(x, &nr);
-		if(w)
-			err = edittext(w, w->wrselrange.q1, r, nr);
-		else
-			err = edittext(nil, 0, r, nr);
-		free(r);
-		if(err != nil){
-			respond(x, &fc, err);
-			break;
-		}
-		fc.count = x->count;
-		respond(x, &fc, nil);
-		break;
-
-	case QWerrors:
-		w = errorwinforwin(w);
-		t = &w->body;
-		goto BodyTag;
-
-	case QWbody:
-	case QWwrsel:
-		t = &w->body;
-		goto BodyTag;
-
-	case QWctl:
-		xfidctlwrite(x, w);
-		break;
-
-	case QWdata:
-		a = w->addr;
-		t = &w->body;
-		wincommit(w, t);
-		if(a.q0>t->file->nc || a.q1>t->file->nc){
-			respond(x, &fc, Eaddr);
-			break;
-		}
-		r = runemalloc(x->count);
-		cvttorunes(x->data, x->count, r, &nb, &nr, nil);
-		if(w->nomark == FALSE){
-			seq++;
-			filemark(t->file);
-		}
-		q0 = a.q0;
-		if(a.q1 > q0){
-			textdelete(t, q0, a.q1, TRUE);
-			w->addr.q1 = q0;
-		}
-		tq0 = t->q0;
-		tq1 = t->q1;
-		textinsert(t, q0, r, nr, TRUE);
-		if(tq0 >= q0)
-			tq0 += nr;
-		if(tq1 >= q0)
-			tq1 += nr;
-		textsetselect(t, tq0, tq1);
-		if(!t->w->noscroll)
-			textshow(t, q0, q0+nr, 0);
-		textscrdraw(t);
-		winsettag(w);
-		free(r);
-		w->addr.q0 += nr;
-		w->addr.q1 = w->addr.q0;
-		fc.count = x->count;
-		respond(x, &fc, nil);
-		break;
-
-	case QWevent:
-		xfideventwrite(x, w);
-		break;
-
-	case QWtag:
-		t = &w->tag;
-		goto BodyTag;
-
-	BodyTag:
-		r = fullrunewrite(x, &nr);
-		if(nr > 0){
-			wincommit(w, t);
-			if(qid == QWwrsel){
-				q0 = w->wrselrange.q1;
-				if(q0 > t->file->nc)
-					q0 = t->file->nc;
-			}else
-				q0 = t->file->nc;
-			if(qid == QWtag)
-				textinsert(t, q0, r, nr, TRUE);
-			else{
-				if(w->nomark == FALSE){
-					seq++;
-					filemark(t->file);
-				}
-				q0 = textbsinsert(t, q0, r, nr, TRUE, &nr);
-				textsetselect(t, t->q0, t->q1);	/* insert could leave it somewhere else */
-				if(qid!=QWwrsel && !t->w->noscroll)
-					textshow(t, q0+nr, q0+nr, 1);
-				textscrdraw(t);
-			}
-			winsettag(w);
-			if(qid == QWwrsel)
-				w->wrselrange.q1 += nr;
-			free(r);
-		}
-		fc.count = x->count;
-		respond(x, &fc, nil);
-		break;
-
-	default:
-		sprint(buf, "unknown qid %d in write", qid);
-		respond(x, &fc, buf);
-		break;
-	}
-	if(w)
-		winunlock(w);
-}
-
-void
-xfidctlwrite(Xfid *x, Window *w)
-{
-	Fcall fc;
-	int i, m, n, nb, nr, nulls;
-	Rune *r;
-	char *err, *p, *pp, *q, *e;
-	int scrdraw, settag;
-	Text *t;
-
-	err = nil;
-	e = x->data+x->count;
-	scrdraw = FALSE;
-	settag = FALSE;
-	r = emalloc(x->count*UTFmax+1);
-	x->data[x->count] = 0;
-	textcommit(&w->tag, TRUE);
-	for(n=0; n<x->count; n+=m){
-		p = x->data+n;
-		if(strncmp(p, "lock", 4) == 0){	/* make window exclusive use */
-			qlock(&w->ctllock);
-			w->ctlfid = x->f->fid;
-			m = 4;
-		}else
-		if(strncmp(p, "unlock", 6) == 0){	/* release exclusive use */
-			w->ctlfid = ~0;
-			qunlock(&w->ctllock);
-			m = 6;
-		}else
-		if(strncmp(p, "clean", 5) == 0){	/* mark window 'clean', seq=0 */
-			t = &w->body;
-			t->eq0 = ~0;
-			filereset(t->file);
-			t->file->mod = FALSE;
-			w->dirty = FALSE;
-			settag = TRUE;
-			m = 5;
-		}else
-		if(strncmp(p, "dirty", 5) == 0){	/* mark window 'dirty' */
-			t = &w->body;
-			/* doesn't change sequence number, so "Put" won't appear.  it shouldn't. */
-			t->file->mod = TRUE;
-			w->dirty = TRUE;
-			settag = TRUE;
-			m = 5;
-		}else
-		if(strncmp(p, "show", 4) == 0){	/* show dot */
-			t = &w->body;
-			textshow(t, t->q0, t->q1, 1);
-			m = 4;
-		}else
-		if(strncmp(p, "name ", 5) == 0){	/* set file name */
-			pp = p+5;
-			m = 5;
-			q = memchr(pp, '\n', e-pp);
-			if(q==nil || q==pp){
-				err = Ebadctl;
-				break;
-			}
-			*q = 0;
-			nulls = FALSE;
-			cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
-			if(nulls){
-				err = "nulls in file name";
-				break;
-			}
-			for(i=0; i<nr; i++)
-				if(r[i] <= ' '){
-					err = "bad character in file name";
-					goto out;
-				}
-out:
-			seq++;
-			filemark(w->body.file);
-			winsetname(w, r, nr);
-			m += (q+1) - pp;
-		}else
-		if(strncmp(p, "dump ", 5) == 0){	/* set dump string */
-			pp = p+5;
-			m = 5;
-			q = memchr(pp, '\n', e-pp);
-			if(q==nil || q==pp){
-				err = Ebadctl;
-				break;
-			}
-			*q = 0;
-			nulls = FALSE;
-			cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
-			if(nulls){
-				err = "nulls in dump string";
-				break;
-			}
-			w->dumpstr = runetobyte(r, nr);
-			m += (q+1) - pp;
-		}else
-		if(strncmp(p, "dumpdir ", 8) == 0){	/* set dump directory */
-			pp = p+8;
-			m = 8;
-			q = memchr(pp, '\n', e-pp);
-			if(q==nil || q==pp){
-				err = Ebadctl;
-				break;
-			}
-			*q = 0;
-			nulls = FALSE;
-			cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
-			if(nulls){
-				err = "nulls in dump directory string";
-				break;
-			}
-			w->dumpdir = runetobyte(r, nr);
-			m += (q+1) - pp;
-		}else
-		if(strncmp(p, "delete", 6) == 0){	/* delete for sure */
-			colclose(w->col, w, TRUE);
-			m = 6;
-		}else
-		if(strncmp(p, "del", 3) == 0){	/* delete, but check dirty */
-			if(!winclean(w, TRUE)){
-				err = "file dirty";
-				break;
-			}
-			colclose(w->col, w, TRUE);
-			m = 3;
-		}else
-		if(strncmp(p, "get", 3) == 0){	/* get file */
-			get(&w->body, nil, nil, FALSE, XXX, nil, 0);
-			m = 3;
-		}else
-		if(strncmp(p, "put", 3) == 0){	/* put file */
-			put(&w->body, nil, nil, XXX, XXX, nil, 0);
-			m = 3;
-		}else
-		if(strncmp(p, "dot=addr", 8) == 0){	/* set dot */
-			textcommit(&w->body, TRUE);
-			clampaddr(w);
-			w->body.q0 = w->addr.q0;
-			w->body.q1 = w->addr.q1;
-			textsetselect(&w->body, w->body.q0, w->body.q1);
-			settag = TRUE;
-			m = 8;
-		}else
-		if(strncmp(p, "addr=dot", 8) == 0){	/* set addr */
-			w->addr.q0 = w->body.q0;
-			w->addr.q1 = w->body.q1;
-			m = 8;
-		}else
-		if(strncmp(p, "limit=addr", 10) == 0){	/* set limit */
-			textcommit(&w->body, TRUE);
-			clampaddr(w);
-			w->limit.q0 = w->addr.q0;
-			w->limit.q1 = w->addr.q1;
-			m = 10;
-		}else
-		if(strncmp(p, "nomark", 6) == 0){	/* turn off automatic marking */
-			w->nomark = TRUE;
-			m = 6;
-		}else
-		if(strncmp(p, "mark", 4) == 0){	/* mark file */
-			seq++;
-			filemark(w->body.file);
-			settag = TRUE;
-			m = 4;
-		}else
-		if(strncmp(p, "nomenu", 6) == 0){	/* turn off automatic menu */
-			w->filemenu = FALSE;
-			m = 6;
-		}else
-		if(strncmp(p, "menu", 4) == 0){	/* enable automatic menu */
-			w->filemenu = TRUE;
-			m = 4;
-		}else
-		if(strncmp(p, "noscroll", 8) == 0){	/* turn off automatic scrolling */
-			w->noscroll = TRUE;
-			m = 8;
-		}else
-		if(strncmp(p, "cleartag", 8) == 0){	/* wipe tag right of bar */
-			wincleartag(w);
-			settag = TRUE;
-			m = 8;
-		}else
-		if(strncmp(p, "scroll", 6) == 0){	/* turn on automatic scrolling (writes to body only) */
-			w->noscroll = FALSE;
-			m = 6;
-		}else
-		if(strncmp(p, "scratch", 7) == 0){ /* mark as a scratch file */
-			w->isscratch = TRUE;
-			m = 7;
-		}else{
-			err = Ebadctl;
-			break;
-		}
-		while(p[m] == '\n')
-			m++;
-	}
-
-	free(r);
-	if(err)
-		n = 0;
-	fc.count = n;
-	respond(x, &fc, err);
-	if(settag)
-		winsettag(w);
-	if(scrdraw)
-		textscrdraw(&w->body);
-}
-
-void
-xfideventwrite(Xfid *x, Window *w)
-{
-	Fcall fc;
-	int m, n;
-	Rune *r;
-	char *err, *p, *q;
-	Text *t;
-	int c;
-	uint q0, q1;
-
-	err = nil;
- 	r = emalloc(x->count*UTFmax+1);
-	for(n=0; n<x->count; n+=m){
-		p = x->data+n;
-		w->owner = *p++;	/* disgusting */
-		c = *p++;
-		while(*p == ' ')
-			p++;
-		q0 = strtoul(p, &q, 10);
-		if(q == p)
-			goto Rescue;
-		p = q;
-		while(*p == ' ')
-			p++;
-		q1 = strtoul(p, &q, 10);
-		if(q == p)
-			goto Rescue;
-		p = q;
-		while(*p == ' ')
-			p++;
-		if(*p++ != '\n')
-			goto Rescue;
-		m = p-(x->data+n);
-		if('a'<=c && c<='z')
-			t = &w->tag;
-		else if('A'<=c && c<='Z')
-			t = &w->body;
-		else
-			goto Rescue;
-		if(q0>t->file->nc || q1>t->file->nc || q0>q1)
-			goto Rescue;
-
-		qlock(&row);	/* just like mousethread */
-		switch(c){
-		case 'x':
-		case 'X':
-			execute(t, q0, q1, TRUE, nil);
-			break;
-		case 'l':
-		case 'L':
-			look3(t, q0, q1, TRUE);
-			break;
-		default:
-			qunlock(&row);
-			goto Rescue;
-		}
-		qunlock(&row);
-
-	}
-
-    Out:
-	free(r);
-	if(err)
-		n = 0;
-	fc.count = n;
-	respond(x, &fc, err);
-	return;
-
-    Rescue:
-	err = Ebadevent;
-	goto Out;
-}
-
-void
-xfidutfread(Xfid *x, Text *t, uint q1, int qid)
-{
-	Fcall fc;
-	Window *w;
-	Rune *r;
-	char *b, *b1;
-	uint q, off, boff;
-	int m, n, nr, nb;
-
-	w = t->w;
-	wincommit(w, t);
-	off = x->offset;
-	r = fbufalloc();
-	b = fbufalloc();
-	b1 = emalloc(x->count);
-	n = 0;
-	if(qid==w->utflastqid && off>=w->utflastboff && w->utflastq<=q1){
-		boff = w->utflastboff;
-		q = w->utflastq;
-	}else{
-		/* BUG: stupid code: scan from beginning */
-		boff = 0;
-		q = 0;
-	}
-	w->utflastqid = qid;
-	while(q<q1 && n<x->count){
-		/*
-		 * Updating here avoids partial rune problem: we're always on a
-		 * char boundary. The cost is we will usually do one more read
-		 * than we really need, but that's better than being n^2.
-		 */
-		w->utflastboff = boff;
-		w->utflastq = q;
-		nr = q1-q;
-		if(nr > (BUFSIZE-1)/UTFmax)
-			nr = (BUFSIZE-1)/UTFmax;
-		bufread(t->file, q, r, nr);
-		nb = snprint(b, BUFSIZE, "%.*S", nr, r);
-		if(boff >= off){
-			m = nb;
-			if(boff+m > off+x->count)
-				m = off+x->count - boff;
-			memmove(b1+n, b, m);
-			n += m;
-		}else if(boff+nb > off){
-			if(n != 0)
-				error("bad count in utfrune");
-			m = nb - (off-boff);
-			if(m > x->count)
-				m = x->count;
-			memmove(b1, b+(off-boff), m);
-			n += m;
-		}
-		boff += nb;
-		q += nr;
-	}
-	fbuffree(r);
-	fbuffree(b);
-	fc.count = n;
-	fc.data = b1;
-	respond(x, &fc, nil);
-	free(b1);
-}
-
-int
-xfidruneread(Xfid *x, Text *t, uint q0, uint q1)
-{
-	Fcall fc;
-	Window *w;
-	Rune *r, junk;
-	char *b, *b1;
-	uint q, boff;
-	int i, rw, m, n, nr, nb;
-
-	w = t->w;
-	wincommit(w, t);
-	r = fbufalloc();
-	b = fbufalloc();
-	b1 = emalloc(x->count);
-	n = 0;
-	q = q0;
-	boff = 0;
-	while(q<q1 && n<x->count){
-		nr = q1-q;
-		if(nr > (BUFSIZE-1)/UTFmax)
-			nr = (BUFSIZE-1)/UTFmax;
-		bufread(t->file, q, r, nr);
-		nb = snprint(b, BUFSIZE, "%.*S", nr, r);
-		m = nb;
-		if(boff+m > x->count){
-			i = x->count - boff;
-			/* copy whole runes only */
-			m = 0;
-			nr = 0;
-			while(m < i){
-				rw = chartorune(&junk, b+m);
-				if(m+rw > i)
-					break;
-				m += rw;
-				nr++;
-			}
-			if(m == 0)
-				break;
-		}
-		memmove(b1+n, b, m);
-		n += m;
-		boff += nb;
-		q += nr;
-	}
-	fbuffree(r);
-	fbuffree(b);
-	fc.count = n;
-	fc.data = b1;
-	respond(x, &fc, nil);
-	free(b1);
-	return q-q0;
-}
-
-void
-xfideventread(Xfid *x, Window *w)
-{
-	Fcall fc;
-	char *b;
-	int i, n;
-
-	i = 0;
-	x->flushed = FALSE;
-	while(w->nevents == 0){
-		if(i){
-			if(!x->flushed)
-				respond(x, &fc, "window shut down");
-			return;
-		}
-		w->eventx = x;
-		winunlock(w);
-		recvp(x->c);
-		winlock(w, 'F');
-		i++;
-	}
-
-	n = w->nevents;
-	if(n > x->count)
-		n = x->count;
-	fc.count = n;
-	fc.data = w->events;
-	respond(x, &fc, nil);
-	b = w->events;
-	w->events = estrdup(w->events+n);
-	free(b);
-	w->nevents -= n;
-}
-
-void
-xfidindexread(Xfid *x)
-{
-	Fcall fc;
-	int i, j, m, n, nmax, isbuf, cnt, off;
-	Window *w;
-	char *b;
-	Rune *r;
-	Column *c;
-
-	qlock(&row);
-	nmax = 0;
-	for(j=0; j<row.ncol; j++){
-		c = row.col[j];
-		for(i=0; i<c->nw; i++){
-			w = c->w[i];
-			nmax += Ctlsize + w->tag.file->nc*UTFmax + 1;
-		}
-	}
-	nmax++;
-	isbuf = (nmax<=RBUFSIZE);
-	if(isbuf)
-		b = (char*)x->buf;
-	else
-		b = emalloc(nmax);
-	r = fbufalloc();
-	n = 0;
-	for(j=0; j<row.ncol; j++){
-		c = row.col[j];
-		for(i=0; i<c->nw; i++){
-			w = c->w[i];
-			/* only show the currently active window of a set */
-			if(w->body.file->curtext != &w->body)
-				continue;
-			winctlprint(w, b+n, 0);
-			n += Ctlsize;
-			m = min(RBUFSIZE, w->tag.file->nc);
-			bufread(w->tag.file, 0, r, m);
-			m = n + snprint(b+n, nmax-n-1, "%.*S", m, r);
-			while(n<m && b[n]!='\n')
-				n++;
-			b[n++] = '\n';
-		}
-	}
-	qunlock(&row);
-	off = x->offset;
-	cnt = x->count;
-	if(off > n)
-		off = n;
-	if(off+cnt > n)
-		cnt = n-off;
-	fc.count = cnt;
-	memmove(r, b+off, cnt);
-	fc.data = (char*)r;
-	if(!isbuf)
-		free(b);
-	respond(x, &fc, nil);
-	fbuffree(r);
-}