shithub: drawcpu

Download patch

ref: 50d01b2e88713afb5a1276849b1ae75389263986
parent: a93a7fbe83b91c2275b84796a7e6b1fbb4e69656
author: halfwit <michaelmisch1985@gmail.com>
date: Mon Dec 1 20:29:02 EST 2025

Rebase over modern drawterm for libs and kernel

--- a/TODO
+++ b/TODO
@@ -2,8 +2,8 @@
 
  - [ ] Some people probably want aan?
  - [ ] gui-cocoa try to capture the windows we create so we can send mouse/keyboard to it, plist for launchd
+   - [ ] Use ScreenCaptureKit to get at the video/audio. Handle resizes the other direction
  - [ ] gui-wl cannibalize the wayland shims
- - [ ] Use ScreenCaptureKit to get at the video/audio. Handle resizes the other direction
  - [ ] define the gui interface
  - [ ] Fully fledged note handling
  - [x] up->parent integration into /proc
@@ -14,8 +14,8 @@
  - [x] devcons  - #c: /dev/cons
  - [x] devenv   - #e:/env
  - [x] devfs    - #U: needed, local files
- - [x] devip    - #I: needed, networking
-   - [ ] /net/icmpv6 and friends
+ - [-] devip    - #I: needed, networking
+   - [ ] /net/icmpv6, /net/tls and friends
  - [x] devlfd   - #L: needed, local fd
  - [x] devmnt   - #M: needed, client shares
  - [x] devpipe  - #|: needed, for pipes
@@ -22,6 +22,6 @@
  - [x] devroot  - #/: needed, base of stack
  - [x] devssl   - #D: needed, ssl
  - [x] devtls   - #a: needed, tls
- - [ ] devproc  - #p: needed, /proc, overlay any existing 
+ - [-] devproc  - #p: needed, /proc, overlay any existing
+ - [x] devcap   - #¤: needed, caphash/capuse for factotum
  - [x] devtab   - # meta: needed add/remove any devices that we add/remove, from here as well
- - [ ] devcap   - #¤: needed, caphash/capuse for factotum
--- a/include/draw.h
+++ b/include/draw.h
@@ -4,7 +4,7 @@
 typedef struct	Display Display;
 typedef struct	Font Font;
 typedef struct	Fontchar Fontchar;
-typedef struct	Img Img;
+typedef struct	Image Image;
 typedef struct	Mouse Mouse;
 typedef struct	Point Point;
 typedef struct	Rectangle Rectangle;
@@ -11,6 +11,7 @@
 typedef struct	RGB RGB;
 typedef struct	Screen Screen;
 typedef struct	Subfont Subfont;
+typedef long	Warp[3][3];
 
 extern	int	Rfmt(Fmt*);
 extern	int	Pfmt(Fmt*);
@@ -155,14 +156,14 @@
 	Point	max;
 };
 
-typedef void	(*Reffn)(Img*, Rectangle, void*);
+typedef void	(*Reffn)(Image*, Rectangle, void*);
 
 struct Screen
 {
 	Display	*display;	/* display holding data */
 	int	id;		/* id of system-held Screen */
-	Img	*image;		/* unused; for reference only */
-	Img	*fill;		/* color to paint behind windows */
+	Image	*image;		/* unused; for reference only */
+	Image	*fill;		/* color to paint behind windows */
 };
 
 struct Display
@@ -180,25 +181,25 @@
 	char		*windir;
 	char		oldlabel[64];
 	ulong		dataqid;
-	Img		*white;
-	Img		*black;
-	Img		*opaque;
-	Img		*transparent;
-	Img		*image;
+	Image		*white;
+	Image		*black;
+	Image		*opaque;
+	Image		*transparent;
+	Image		*image;
 	uchar		*buf;
 	int		bufsize;
 	uchar		*bufp;
 	Font		*defaultfont;
 	Subfont		*defaultsubfont;
-	Img		*windows;
-	Img		*screenimage;
+	Image		*windows;
+	Image		*screenimage;
 	int		_isnewdisplay;
 };
 
-struct Img
+struct Image
 {
 	Display		*display;	/* display holding data */
-	int		id;		/* id of system-held Img */
+	int		id;		/* id of system-held Image */
 	Rectangle	r;		/* rectangle in data area, local coords */
 	Rectangle 	clipr;		/* clipping region */
 	int		depth;		/* number of bits per pixel */
@@ -205,7 +206,7 @@
 	ulong		chan;
 	int		repl;		/* flag: data replicates to tile clipr */
 	Screen		*screen;	/* 0 if not a window */
-	Img		*next;	/* next in list of windows */
+	Image		*next;	/* next in list of windows */
 };
 
 struct RGB
@@ -224,7 +225,7 @@
  *		p.x+i->left+((i+1)->x-i->x), p.y+i->bottom),
  *		color, f->bits, Pt(i->x, i->top));
  *	p.x += i->width;
- * to draw characters in the specified color (itself an Img) in Img b.
+ * to draw characters in the specified color (itself an Image) in Image b.
  */
 
 struct	Fontchar
@@ -243,7 +244,7 @@
 	uchar		height;		/* height of image */
 	char		ascent;		/* top of image to baseline */
 	Fontchar 	*info;		/* n+1 character descriptors */
-	Img		*bits;		/* of font */
+	Image		*bits;		/* of font */
 	int		ref;
 };
 
@@ -304,7 +305,7 @@
 	Cacheinfo	*cache;
 	Cachesubf	*subf;
 	Cachefont	**sub;	/* as read from file */
-	Img		*cacheimage;
+	Image		*cacheimage;
 };
 
 #define	Dx(r)	((r).max.x-(r).min.x)
@@ -311,33 +312,33 @@
 #define	Dy(r)	((r).max.y-(r).min.y)
 
 /*
- * Img management
+ * Image management
  */
-extern Img*	_allocimage(Img*, Display*, Rectangle, ulong, int, ulong, int, int);
-extern Img*	allocimage(Display*, Rectangle, ulong, int, ulong);
+extern Image*	_allocimage(Image*, Display*, Rectangle, ulong, int, ulong, int, int);
+extern Image*	allocimage(Display*, Rectangle, ulong, int, ulong);
 extern uchar*	bufimage(Display*, int);
 extern int	bytesperline(Rectangle, int);
 extern void	closedisplay(Display*);
 extern void	drawerror(Display*, char*);
 extern int	flushimage(Display*, int);
-extern int	freeimage(Img*);
-extern int	_freeimage1(Img*);
+extern int	freeimage(Image*);
+extern int	_freeimage1(Image*);
 extern int	geninitdraw(char*, void(*)(Display*, char*), char*, char*, char*, int);
 extern int	initdraw(void(*)(Display*, char*), char*, char*);
 extern int	newwindow(char*);
 extern Display*	initdisplay(char*, char*, void(*)(Display*, char*));
-extern int	loadimage(Img*, Rectangle, uchar*, int);
-extern int	cloadimage(Img*, Rectangle, uchar*, int);
+extern int	loadimage(Image*, Rectangle, uchar*, int);
+extern int	cloadimage(Image*, Rectangle, uchar*, int);
 extern int	getwindow(Display*, int);
-extern int	gengetwindow(Display*, char*, Img**, Screen**, int);
-extern Img* readimage(Display*, int, int);
-extern Img* creadimage(Display*, int, int);
-extern int	unloadimage(Img*, Rectangle, uchar*, int);
+extern int	gengetwindow(Display*, char*, Image**, Screen**, int);
+extern Image* readimage(Display*, int, int);
+extern Image* creadimage(Display*, int, int);
+extern int	unloadimage(Image*, Rectangle, uchar*, int);
 extern int	wordsperline(Rectangle, int);
-extern int	writeimage(int, Img*, int);
-extern Img*	namedimage(Display*, char*);
-extern int	nameimage(Img*, char*, int);
-extern Img* allocimagemix(Display*, ulong, ulong);
+extern int	writeimage(int, Image*, int);
+extern Image*	namedimage(Display*, char*);
+extern int	nameimage(Image*, char*, int);
+extern Image* allocimagemix(Display*, ulong, ulong);
 
 /*
  * Colors
@@ -349,16 +350,16 @@
 /*
  * Windows
  */
-extern Screen*	allocscreen(Img*, Img*, int);
-extern Img*	_allocwindow(Img*, Screen*, Rectangle, int, ulong);
-extern Img*	allocwindow(Screen*, Rectangle, int, ulong);
-extern void	bottomnwindows(Img**, int);
-extern void	bottomwindow(Img*);
+extern Screen*	allocscreen(Image*, Image*, int);
+extern Image*	_allocwindow(Image*, Screen*, Rectangle, int, ulong);
+extern Image*	allocwindow(Screen*, Rectangle, int, ulong);
+extern void	bottomnwindows(Image**, int);
+extern void	bottomwindow(Image*);
 extern int	freescreen(Screen*);
 extern Screen*	publicscreen(Display*, int, ulong);
-extern void	topnwindows(Img**, int);
-extern void	topwindow(Img*);
-extern int	originwindow(Img*, Point, Point);
+extern void	topnwindows(Image**, int);
+extern void	topwindow(Image*);
+extern int	originwindow(Image*, Point, Point);
 
 /*
  * Geometry
@@ -381,7 +382,7 @@
 extern void		combinerect(Rectangle*, Rectangle);
 extern int		rectclip(Rectangle*, Rectangle);
 extern int		ptinrect(Point, Rectangle);
-extern void		replclipr(Img*, int, Rectangle);
+extern void		replclipr(Image*, int, Rectangle);
 extern int		drawreplxy(int, int, int);	/* used to be drawsetxy */
 extern Point	drawrepl(Rectangle, Point);
 extern int		rgb2cmap(int, int, int);
@@ -394,53 +395,55 @@
 /*
  * Graphics
  */
-extern void	draw(Img*, Rectangle, Img*, Img*, Point);
-extern void	drawop(Img*, Rectangle, Img*, Img*, Point, Drawop);
-extern void	gendraw(Img*, Rectangle, Img*, Point, Img*, Point);
-extern void	gendrawop(Img*, Rectangle, Img*, Point, Img*, Point, Drawop);
-extern void	line(Img*, Point, Point, int, int, int, Img*, Point);
-extern void	lineop(Img*, Point, Point, int, int, int, Img*, Point, Drawop);
-extern void	poly(Img*, Point*, int, int, int, int, Img*, Point);
-extern void	polyop(Img*, Point*, int, int, int, int, Img*, Point, Drawop);
-extern void	fillpoly(Img*, Point*, int, int, Img*, Point);
-extern void	fillpolyop(Img*, Point*, int, int, Img*, Point, Drawop);
-extern Point	string(Img*, Point, Img*, Point, Font*, char*);
-extern Point	stringop(Img*, Point, Img*, Point, Font*, char*, Drawop);
-extern Point	stringn(Img*, Point, Img*, Point, Font*, char*, int);
-extern Point	stringnop(Img*, Point, Img*, Point, Font*, char*, int, Drawop);
-extern Point	runestring(Img*, Point, Img*, Point, Font*, Rune*);
-extern Point	runestringop(Img*, Point, Img*, Point, Font*, Rune*, Drawop);
-extern Point	runestringn(Img*, Point, Img*, Point, Font*, Rune*, int);
-extern Point	runestringnop(Img*, Point, Img*, Point, Font*, Rune*, int, Drawop);
-extern Point	stringbg(Img*, Point, Img*, Point, Font*, char*, Img*, Point);
-extern Point	stringbgop(Img*, Point, Img*, Point, Font*, char*, Img*, Point, Drawop);
-extern Point	stringnbg(Img*, Point, Img*, Point, Font*, char*, int, Img*, Point);
-extern Point	stringnbgop(Img*, Point, Img*, Point, Font*, char*, int, Img*, Point, Drawop);
-extern Point	runestringbg(Img*, Point, Img*, Point, Font*, Rune*, Img*, Point);
-extern Point	runestringbgop(Img*, Point, Img*, Point, Font*, Rune*, Img*, Point, Drawop);
-extern Point	runestringnbg(Img*, Point, Img*, Point, Font*, Rune*, int, Img*, Point);
-extern Point	runestringnbgop(Img*, Point, Img*, Point, Font*, Rune*, int, Img*, Point, Drawop);
-extern Point	_string(Img*, Point, Img*, Point, Font*, char*, Rune*, int, Rectangle, Img*, Point, Drawop);
-extern Point	stringsubfont(Img*, Point, Img*, Subfont*, char*);
-extern int		bezier(Img*, Point, Point, Point, Point, int, int, int, Img*, Point);
-extern int		bezierop(Img*, Point, Point, Point, Point, int, int, int, Img*, Point, Drawop);
-extern int		bezspline(Img*, Point*, int, int, int, int, Img*, Point);
-extern int		bezsplineop(Img*, Point*, int, int, int, int, Img*, Point, Drawop);
+extern void	draw(Image*, Rectangle, Image*, Image*, Point);
+extern void	drawop(Image*, Rectangle, Image*, Image*, Point, Drawop);
+extern void	gendraw(Image*, Rectangle, Image*, Point, Image*, Point);
+extern void	gendrawop(Image*, Rectangle, Image*, Point, Image*, Point, Drawop);
+extern void	line(Image*, Point, Point, int, int, int, Image*, Point);
+extern void	lineop(Image*, Point, Point, int, int, int, Image*, Point, Drawop);
+extern void	poly(Image*, Point*, int, int, int, int, Image*, Point);
+extern void	polyop(Image*, Point*, int, int, int, int, Image*, Point, Drawop);
+extern void	fillpoly(Image*, Point*, int, int, Image*, Point);
+extern void	fillpolyop(Image*, Point*, int, int, Image*, Point, Drawop);
+extern Point	string(Image*, Point, Image*, Point, Font*, char*);
+extern Point	stringop(Image*, Point, Image*, Point, Font*, char*, Drawop);
+extern Point	stringn(Image*, Point, Image*, Point, Font*, char*, int);
+extern Point	stringnop(Image*, Point, Image*, Point, Font*, char*, int, Drawop);
+extern Point	runestring(Image*, Point, Image*, Point, Font*, Rune*);
+extern Point	runestringop(Image*, Point, Image*, Point, Font*, Rune*, Drawop);
+extern Point	runestringn(Image*, Point, Image*, Point, Font*, Rune*, int);
+extern Point	runestringnop(Image*, Point, Image*, Point, Font*, Rune*, int, Drawop);
+extern Point	stringbg(Image*, Point, Image*, Point, Font*, char*, Image*, Point);
+extern Point	stringbgop(Image*, Point, Image*, Point, Font*, char*, Image*, Point, Drawop);
+extern Point	stringnbg(Image*, Point, Image*, Point, Font*, char*, int, Image*, Point);
+extern Point	stringnbgop(Image*, Point, Image*, Point, Font*, char*, int, Image*, Point, Drawop);
+extern Point	runestringbg(Image*, Point, Image*, Point, Font*, Rune*, Image*, Point);
+extern Point	runestringbgop(Image*, Point, Image*, Point, Font*, Rune*, Image*, Point, Drawop);
+extern Point	runestringnbg(Image*, Point, Image*, Point, Font*, Rune*, int, Image*, Point);
+extern Point	runestringnbgop(Image*, Point, Image*, Point, Font*, Rune*, int, Image*, Point, Drawop);
+extern Point	_string(Image*, Point, Image*, Point, Font*, char*, Rune*, int, Rectangle, Image*, Point, Drawop);
+extern Point	stringsubfont(Image*, Point, Image*, Subfont*, char*);
+extern int		bezier(Image*, Point, Point, Point, Point, int, int, int, Image*, Point);
+extern int		bezierop(Image*, Point, Point, Point, Point, int, int, int, Image*, Point, Drawop);
+extern int		bezspline(Image*, Point*, int, int, int, int, Image*, Point);
+extern int		bezsplineop(Image*, Point*, int, int, int, int, Image*, Point, Drawop);
 extern int		bezsplinepts(Point*, int, Point**);
-extern int		fillbezier(Img*, Point, Point, Point, Point, int, Img*, Point);
-extern int		fillbezierop(Img*, Point, Point, Point, Point, int, Img*, Point, Drawop);
-extern int		fillbezspline(Img*, Point*, int, int, Img*, Point);
-extern int		fillbezsplineop(Img*, Point*, int, int, Img*, Point, Drawop);
-extern void	ellipse(Img*, Point, int, int, int, Img*, Point);
-extern void	ellipseop(Img*, Point, int, int, int, Img*, Point, Drawop);
-extern void	fillellipse(Img*, Point, int, int, Img*, Point);
-extern void	fillellipseop(Img*, Point, int, int, Img*, Point, Drawop);
-extern void	arc(Img*, Point, int, int, int, Img*, Point, int, int);
-extern void	arcop(Img*, Point, int, int, int, Img*, Point, int, int, Drawop);
-extern void	fillarc(Img*, Point, int, int, Img*, Point, int, int);
-extern void	fillarcop(Img*, Point, int, int, Img*, Point, int, int, Drawop);
-extern void	border(Img*, Rectangle, int, Img*, Point);
-extern void	borderop(Img*, Rectangle, int, Img*, Point, Drawop);
+extern int		fillbezier(Image*, Point, Point, Point, Point, int, Image*, Point);
+extern int		fillbezierop(Image*, Point, Point, Point, Point, int, Image*, Point, Drawop);
+extern int		fillbezspline(Image*, Point*, int, int, Image*, Point);
+extern int		fillbezsplineop(Image*, Point*, int, int, Image*, Point, Drawop);
+extern void	ellipse(Image*, Point, int, int, int, Image*, Point);
+extern void	ellipseop(Image*, Point, int, int, int, Image*, Point, Drawop);
+extern void	fillellipse(Image*, Point, int, int, Image*, Point);
+extern void	fillellipseop(Image*, Point, int, int, Image*, Point, Drawop);
+extern void	arc(Image*, Point, int, int, int, Image*, Point, int, int);
+extern void	arcop(Image*, Point, int, int, int, Image*, Point, int, int, Drawop);
+extern void	fillarc(Image*, Point, int, int, Image*, Point, int, int);
+extern void	fillarcop(Image*, Point, int, int, Image*, Point, int, int, Drawop);
+extern void	border(Image*, Rectangle, int, Image*, Point);
+extern void	borderop(Image*, Rectangle, int, Image*, Point, Drawop);
+extern void	mkwarp(Warp, double[3][3]);
+extern void	affinewarp(Image*, Rectangle, Image*, Point, Warp, int);
 
 /*
  * Font management
@@ -451,13 +454,13 @@
 extern Font*	mkfont(Subfont*, Rune);
 extern int	cachechars(Font*, char**, Rune**, ushort*, int, int*, char**);
 extern void	agefont(Font*);
-extern Subfont*	allocsubfont(char*, int, int, int, Fontchar*, Img*);
+extern Subfont*	allocsubfont(char*, int, int, int, Fontchar*, Image*);
 extern Subfont*	lookupsubfont(Display*, char*);
 extern void	installsubfont(char*, Subfont*);
 extern void	uninstallsubfont(Subfont*);
 extern void	freesubfont(Subfont*);
 extern Subfont*	readsubfont(Display*, char*, int, int);
-extern Subfont*	readsubfonti(Display*, char*, int, Img*, int);
+extern Subfont*	readsubfonti(Display*, char*, int, Image*, int);
 extern int	writesubfont(int, Subfont*);
 extern void	_unpackinfo(Fontchar*, uchar*, int);
 extern Point	stringsize(Font*, char*);
@@ -487,7 +490,7 @@
  */
 extern	Display	*display;
 extern	Font		*font;
-// extern	Img	*screen;
+// extern	Image	*screen;
 extern	Screen	*_screen;
 extern	int	_cursorfd;
 extern	void	_setdrawop(Display*, Drawop);
--- a/include/lib.h
+++ b/include/lib.h
@@ -350,8 +350,6 @@
 extern	double	__Inf(int);
 extern	int	__isInf(double, int);
 
-extern int (*fmtdoquote)(int);
-
 /*
  * Time-of-day
  */
--- a/kern/Makefile
+++ b/kern/Makefile
@@ -35,7 +35,6 @@
 	postnote.$O\
 	procinit.$O\
 	notify.$O\
-	rendez.$O\
 	rwlock.$O\
 	seg.$O\
 	sema.$O\
--- a/kern/chan.c
+++ b/kern/chan.c
@@ -506,9 +506,9 @@
  *	- the mhead in chans returned from createdir:
  *	  used in the open/create race protect, which is gone.
  *
- * The RWlock in the Mhead protects the mount list it contains.
+ * The RWLock in the Mhead protects the mount list it contains.
  * The mount list is deleted in cunmount() and closepgrp().
- * The RWlock ensures that nothing is using the mount list at that time.
+ * The RWLock ensures that nothing is using the mount list at that time.
  *
  * It is okay to replace c->mh with whatever you want as 
  * long as you are sure you have a unique reference to it.
@@ -830,7 +830,6 @@
  * Either walks all the way or not at all.  No partial results in *cp.
  * *nerror is the number of names to display in an error message.
  */
-static char Edoesnotexist[] = "does not exist";
 int
 walk(Chan **cp, char **names, int nnames, int nomount, int *nerror)
 {
@@ -946,7 +945,7 @@
 					if(wq->nqid == 0 || (wq->qid[wq->nqid-1].type&QTDIR) != 0){
 						if(nerror)
 							*nerror = nhave+wq->nqid+1;
-						kstrcpy(up->errstr, Edoesnotexist, ERRMAX);
+						kstrcpy(up->errstr, Enonexist, ERRMAX);
 					}else{
 						if(nerror)
 							*nerror = nhave+wq->nqid;
@@ -1084,7 +1083,7 @@
 	}
 }
 
-static void
+void
 namelenerror(char *aname, int len, char *err)
 {
 	char *ename, *name, *next;
@@ -1127,16 +1126,10 @@
 		snprint(up->genbuf, sizeof up->genbuf, "...%.*s",
 			utfnlen(name, ename-name), name);
 	}				
-	snprint(up->errstr, ERRMAX, "%#q %s", up->genbuf, err);
+	snprint(up->errstr, ERRMAX, "%s: %#q", err, up->genbuf);
 	nexterror();
 }
 
-void
-nameerror(char *name, char *err)
-{
-	namelenerror(name, strlen(name), err);
-}
-
 /*
  * Turn a name into a channel.
  * &name[0] is known to be a valid address.  It may be a kernel address.
@@ -1158,7 +1151,7 @@
 {
 	int len, n, t, nomount;
 	Chan *c;
-	Chan */*volatile*/ cnew;
+	Chan * cnew;
 	Path */*volatile*/ path;
 	Elemlist e;
 	Rune r;
@@ -1189,7 +1182,6 @@
 	
 	case '#':
 		nomount = 1;
-		up->genbuf[0] = '\0';
 		n = 0;
 		while(*name != '\0' && (*name != '/' || n < 2)){
 			if(n >= sizeof(up->genbuf)-1)
@@ -1212,7 +1204,7 @@
 		n = chartorune(&r, up->genbuf+1)+1;
 		if(up->pgrp->noattach && utfrune("|decp", r)==nil)
 			error(Enoattach);
-		t = devno(r, 1);
+		t = devno(r);
 		if(t == -1)
 			error(Ebadsharp);
 		c = devtab[t]->attach(up->genbuf+n);
@@ -1285,6 +1277,14 @@
 		error("cannot exec directory");
 
 	switch(amode){
+	case Aunmount:
+		/*
+		 * When unmounting, the channel must be opend when not a directory
+		 * in order to get the real chan from something like /srv/cs or /fd/0.
+		 */ 
+		if((c->qid.type&QTDIR) == 0)
+			goto Open;
+		/* wet floor */
 	case Abind:
 		/* no need to maintain path - cannot dotdot an Abind */
 		m = nil;
@@ -1337,6 +1337,7 @@
 
 		case Aopen:
 		case Acreate:
+		case Aunmount:
 			/* only save the mount head if it's a multiple element union */
 			if(m != nil) {
 				rlock(&m->lock);
@@ -1471,6 +1472,7 @@
 
 
 		/* create failed */
+		cclose(cnew);
 		putmhead(m);
 		if(omode & OEXCL)
 			nexterror();
--- a/kern/dat.h
+++ b/kern/dat.h
@@ -30,7 +30,7 @@
 typedef struct Ref	Ref;
 typedef struct Rendez	Rendez;
 typedef struct Rgrp	Rgrp;
-typedef struct RWlock	RWlock;
+typedef struct RWLock	RWLock;
 typedef struct Segment  Segment;
 typedef struct Ureg		Ureg;
 typedef struct Waitq	Waitq;
@@ -69,7 +69,7 @@
 	Proc	*p;
 };
 
-struct RWlock	/* changed from kernel */
+struct RWLock	/* changed from kernel */
 {
 	int	readers;
 	Lock	lk;
@@ -101,6 +101,7 @@
 	Amount,				/* to be mounted or mounted upon */
 	Acreate,			/* is to be created */
 	Aremove,			/* will be removed by caller */
+	Aunmount,			/* unmount arg */
 
 	COPEN	= 0x0001,		/* for i/o */
 	CMSG	= 0x0002,		/* the message channel for a mount */
@@ -260,7 +261,7 @@
 struct Mhead
 {
 	Ref ref;
-	RWlock	lock;
+	RWLock	lock;
 	Chan*	from;			/* channel mounted upon */
 	Mount*	mount;			/* what's mounted upon it */
 	Mhead*	hash;			/* Hash chain */
@@ -311,7 +312,7 @@
 	Ref ref;				/* also used as a lock when mounting */
 	int	noattach;
 	QLock	debug;			/* single access via devproc.c */
-	RWlock	ns;			/* Namespace n read/one write lock */
+	RWLock	ns;			/* Namespace n read/one write lock */
 	Mhead	*mnthash[MNTHASH];
 	u64int	notallowed[4];	
 };
@@ -352,7 +353,7 @@
 struct Egrp
 {
 	Ref ref;
-	RWlock lk;
+	RWLock lk;
 	Evalue	**ent;
 	int nent;
 	int ment;
@@ -376,7 +377,7 @@
 	int	nfd;			/* number allocated */
 	int	maxfd;			/* highest fd in use */
 	int	exceed;			/* debugging */
-	RWlock lk;
+	RWLock lk;
 };
 
 enum
@@ -433,7 +434,7 @@
 
 struct Fd
 {
-	RWlock rw;
+	RWLock rw;
 	Ref ref;
 	uint *fds;
 	int nfds;
@@ -457,7 +458,7 @@
 	Lock	rlock;		/* sync sleep/wakeup with postnote */
 	Lock    exl;        /* rfork, etc */
 	Rendez	*r;		/* rendezvous point slept on */
-	Rendez	rsleep;		/* place for syssleep/debug */
+	Rendez	sleep;		/* place for syssleep/debug */
 	int	notepending;	/* note issued but not acted on */
 	int	kp;		/* true if a kernel process */
 
@@ -522,7 +523,7 @@
 struct Segment {
 	Ref ref;
 	int flags;
-	RWlock rw; /* lock for SEGFLLOCK segments */
+	RWLock rw; /* lock for SEGFLLOCK segments */
 	u32int start, size;
 	void *data;
 	Ref *dref;
@@ -539,7 +540,6 @@
 	READSTR =	1000,		/* temporary buffer size for device reads */
 };
 
-extern	int	cpuserver;
 extern	Dev*	devtab[];
 extern  char	*eve;
 extern	char	hostdomain[];
--- a/kern/data.c
+++ b/kern/data.c
@@ -4,6 +4,7 @@
 #include "fns.h"
 #include "error.h"
 
+// TODO: Actually get this information
 Conf conf = 
 {
 	1,	/* processors */
@@ -13,5 +14,4 @@
 
 char *eve = "eve";
 ulong kerndate;
-int cpuserver;
 char hostdomain[] = "9front";
--- a/kern/dev.c
+++ b/kern/dev.c
@@ -15,7 +15,7 @@
 }
 
 int
-devno(int c, int user)
+devno(int c)
 {
 	int i;
 
@@ -23,9 +23,6 @@
 		if(devtab[i]->dc == c)
 			return i;
 	}
-	if(user == 0)
-		panic("devno %C %#ux", c, c);
-
 	return -1;
 }
 
@@ -130,7 +127,7 @@
 
 	c = newchan();
 	mkqid(&c->qid, 0, 0, QTDIR);
-	c->type = devno(tc, 0);
+	c->type = devno(tc);
 	if(spec == nil)
 		spec = "";
 	n = 1+UTFmax+strlen(spec)+1;
--- a/kern/devcons.c
+++ b/kern/devcons.c
@@ -6,6 +6,8 @@
 
 #include 	"keyboard.h"
 
+#include	<authsrv.h>
+
 #undef write
 #undef read
 
@@ -180,6 +182,14 @@
 	return n;
 }
 
+static void
+hang(void)
+{
+	Rendez z = {0};
+	while(up == nil) osyield();
+	for(;;) sleep(&z, return0, 0);
+}
+
 void
 panic(char *fmt, ...)
 {
@@ -189,8 +199,7 @@
 
 	kprintoq = nil;	/* don't try to write to /dev/kprint */
 
-	if(panicking++)
-		for(;;) osyield();
+	if(panicking++) hang();
 	splhi();
 	strcpy(buf, "panic: ");
 	va_start(arg, fmt);
@@ -199,8 +208,7 @@
 	buf[n] = '\n';
 	spllo();
 	putstrn(buf, n+1);
-	while(screenputs != 0)
-		osyield();
+	if(screenputs) hang();
 	setterm(0);
 	exit(1);
 }
@@ -363,7 +371,7 @@
 	"cons",		{Qcons},	0,		0660,
 	"consctl",	{Qconsctl},	0,		0220,
 	"drivers",	{Qdrivers},	0,		0444,
-	"hostdomain",	{Qhostdomain},	48,		0664,
+	"hostdomain",	{Qhostdomain},	DOMLEN,		0664,
 	"hostowner",	{Qhostowner},	0,	0664,
 	"kmesg",	{Qkmesg},	0,		0440,
 	"kprint",	{Qkprint, 0, QTEXCL},	0,	DMEXCL|0440,
--- a/kern/devfs-posix.c
+++ b/kern/devfs-posix.c
@@ -231,7 +231,12 @@
 		}
 	}
 	else {
-		int m = fsomode(omode & 3) | O_CREAT | O_TRUNC;
+		int m = omode & 3;
+		/*
+		 * POSIX specifies that O_TRUNC with O_RDONLY is undefined.
+		 * OpenBSD chooses to return an error.
+		 */
+		m = fsomode(m == OREAD ? ORDWR : m) | O_CREAT | O_TRUNC;
 		if(mode & OEXCL)
 			m |= O_EXCL;
 		if((fd = open(path, m, uif->mode & perm & 0777)) < 0)
--- a/kern/devip.c
+++ b/kern/devip.c
@@ -40,6 +40,8 @@
 #define QID(p, c, y) 	(((p)<<16) | ((c)<<4) | (y))
 #define ipzero(x)	memset(x, 0, IPaddrlen)
 
+#define DEVDOT -2
+
 typedef struct Proto	Proto;
 typedef struct Conv	Conv;
 struct Conv
@@ -55,6 +57,7 @@
 	uchar	raddr[IPaddrlen];
 	ushort	rport;
 	int	restricted;
+	ulong	ctime;
 	char	cerr[KNAMELEN];
 	Proto*	p;
 };
@@ -94,13 +97,13 @@
 		return -1;
 	case Qctl:
 		devdir(c, q, "ctl", 0, cv->owner, cv->perm, dp);
-		return 1;
+		goto done;
 	case Qdata:
 		devdir(c, q, "data", 0, cv->owner, cv->perm, dp);
-		return 1;
+		goto done;
 	case Qlisten:
 		devdir(c, q, "listen", 0, cv->owner, cv->perm, dp);
-		return 1;
+		goto done;
 	case Qlocal:
 		p = "local";
 		break;
@@ -112,6 +115,8 @@
 		break;
 	}
 	devdir(c, q, p, 0, cv->owner, 0444, dp);
+done:
+	dp->mtime = cv->ctime;
 	return 1;
 }
 
@@ -159,7 +164,7 @@
 
 	switch(TYPE(c->qid)) {
 	case Qtopdir:
-		if(s == DEVDOTDOT){
+		if(s == DEVDOTDOT || s == DEVDOT){
 			mkqid(&q, QID(0, 0, Qtopdir), 0, QTDIR);
 			snprint(up->genbuf, sizeof up->genbuf, "#I%lud", c->dev);
 			devdir(c, q, up->genbuf, 0, network, 0555, dp);
@@ -166,6 +171,7 @@
 			return 1;
 		}
 		if(s < np) {
+	Protodir:
 			mkqid(&q, QID(s, 0, Qprotodir), 0, QTDIR);
 			devdir(c, q, proto[s].name, 0, network, 0555, dp);
 			return 1;
@@ -181,11 +187,17 @@
 			devdir(c, q, up->genbuf, 0, network, 0555, dp);
 			return 1;
 		}
+		if(s == DEVDOT){
+			s = PROTO(c->qid);
+			goto Protodir;
+		}
 		if(s < proto[PROTO(c->qid)].nc) {
+	Convdir:
 			cv = proto[PROTO(c->qid)].conv[s];
 			snprint(up->genbuf, sizeof up->genbuf, "%d", s);
 			mkqid(&q, QID(PROTO(c->qid), s, Qconvdir), 0, QTDIR);
 			devdir(c, q, up->genbuf, 0, cv->owner, 0555, dp);
+			dp->mtime = cv->ctime;
 			return 1;
 		}
 		s -= proto[PROTO(c->qid)].nc;
@@ -199,6 +211,10 @@
 			devdir(c, q, proto[s].name, 0, network, 0555, dp);
 			return 1;
 		}
+		if(s == DEVDOT){
+			s = CONV(c->qid);
+			goto Convdir;
+		}
 		return ip3gen(c, s+Qconvbase, dp);
 	case Qctl:
 	case Qdata:
@@ -269,6 +285,16 @@
 int
 ipstat(Chan *c, uchar *dp, int n)
 {
+	Dir d;
+
+	if(c->qid.type == QTDIR){
+		ipgen(c, nil, nil, 0, DEVDOT, &d);
+		n = convD2M(&d, dp, n);
+		if(n == 0)
+			error(Ebadarg);
+		return n;
+	}
+
 	return devstat(c, dp, n, 0, 0, ipgen);
 }
 
@@ -631,6 +657,7 @@
 	c->lport = 0;
 	c->rport = 0;
 	c->sfd = nfd;
+	c->ctime = seconds();
 
 	unlock(&c->r.lk);
 	unlock(&p->l);
--- a/kern/devlfd-posix.c
+++ b/kern/devlfd-posix.c
@@ -14,7 +14,7 @@
     Chan *c;
     
     c = newchan();
-    c->type = devno('L', 0);
+    c->type = devno('L');
     c->aux = fd;
     c->path = newpath("/fd");
     c->mode = ORDWR;
--- a/kern/devlfd-win32.c
+++ b/kern/devlfd-win32.c
@@ -11,7 +11,7 @@
     Chan *c;
     
     c = newchan();
-    c->type = devno('L', 0);
+    c->type = devno('L');
     c->aux = fd;
     c->path = newpath("/fd");
     c->mode = ORDWR;
--- a/kern/devmnt.c
+++ b/kern/devmnt.c
@@ -744,7 +744,7 @@
 	}
 
 	lock(&m->lk);
-	r->z = &up->rsleep;
+	r->z = &up->sleep;
 	r->m = m;
 	r->list = m->queue;
 	m->queue = r;
@@ -821,8 +821,8 @@
 
 	/* read in the rest of the message, avoid ridiculous (for now) message sizes */
 	len = GBIT32(nb->rp);
-	if(len > m->msize){
-		qdiscard(m->q, qlen(m->q));
+	if(len < BIT32SZ+BIT8SZ+BIT16SZ || len > m->msize){
+		qflush(m->q);
 		return -1;
 	}
 	if(doread(m, len) < 0)
--- a/kern/exportfs.c
+++ /dev/null
@@ -1,821 +1,0 @@
-#include	"u.h"
-#include	"lib.h"
-#include	"dat.h"
-#include	"fns.h"
-#include	"error.h"
-
-typedef	struct Fid	Fid;
-typedef	struct Export	Export;
-typedef	struct Exq	Exq;
-
-#define	nil	((void*)0)
-
-enum
-{
-	Nfidhash	= 1,
-	MAXRPC		= MAXMSG+MAXFDATA,
-	MAXDIRREAD	= (MAXFDATA/DIRLEN)*DIRLEN
-};
-
-struct Export
-{
-	Ref	r;
-	Exq*	work;
-	Lock	fidlock;
-	Fid*	fid[Nfidhash];
-	Chan*	root;
-	Chan*	io;
-	Pgrp*	pgrp;
-	int	npart;
-	char	part[MAXRPC];
-};
-
-struct Fid
-{
-	Fid*	next;
-	Fid**	last;
-	Chan*	chan;
-	long	offset;
-	int	fid;
-	int	ref;		/* fcalls using the fid; locked by Export.Lock */
-	int	attached;	/* fid attached or cloned but not clunked */
-};
-
-struct Exq
-{
-	Lock	lk;
-	int	nointr;
-	int	noresponse;	/* don't respond to this one */
-	Exq*	next;
-	int	shut;		/* has been noted for shutdown */
-	Export*	export;
-	void*	slave;
-	Fcall	rpc;
-	char	buf[MAXRPC];
-};
-
-struct
-{
-	Lock	l;
-	Qlock	qwait;
-	Rendez	rwait;
-	Exq	*head;		/* work waiting for a slave */
-	Exq	*tail;
-}exq;
-
-static void	exshutdown(Export*);
-static void	exflush(Export*, int, int);
-static void	exslave(void*);
-static void	exfree(Export*);
-static void	exportproc(Export*);
-
-static char*	Exauth(Export*, Fcall*);
-static char*	Exattach(Export*, Fcall*);
-static char*	Exclunk(Export*, Fcall*);
-static char*	Excreate(Export*, Fcall*);
-static char*	Exopen(Export*, Fcall*);
-static char*	Exread(Export*, Fcall*);
-static char*	Exremove(Export*, Fcall*);
-static char*	Exstat(Export*, Fcall*);
-static char*	Exwalk(Export*, Fcall*);
-static char*	Exwrite(Export*, Fcall*);
-static char*	Exwstat(Export*, Fcall*);
-static char*	Exversion(Export*, Fcall*);
-
-static char	*(*fcalls[Tmax])(Export*, Fcall*);
-
-static char	Enofid[]   = "no such fid";
-static char	Eseekdir[] = "can't seek on a directory";
-static char	Ereaddir[] = "unaligned read of a directory";
-static int	exdebug = 0;
-
-int
-sysexport(int fd)
-{
-	Chan *c;
-	Export *fs;
-
-	if(waserror())
-		return -1;
-
-	c = fdtochan(fd, ORDWR, 1, 1);
-	poperror();
-	c->flag |= CMSG;
-
-	fs = mallocz(sizeof(Export));
-	fs->r.ref = 1;
-	fs->pgrp = up->pgrp;
-	refinc(&fs->pgrp->r);
-	refinc(&up->slash->r);
-	fs->root = up->slash;
-	refinc(&fs->root->r);
-	fs->root = domount(fs->root);
-	fs->io = c;
-
-	exportproc(fs);
-
-	return 0;
-}
-
-static void
-exportinit(void)
-{
-	lock(&exq.l);
-	if(fcalls[Tversion] != nil) {
-		unlock(&exq.l);
-		return;
-	}
-
-	fmtinstall('F', fcallfmt);
-	fmtinstall('D', dirfmt);
-	fmtinstall('M', dirmodefmt);
-	fcalls[Tversion] = Exversion;
-	fcalls[Tauth] = Exauth;
-	fcalls[Tattach] = Exattach;
-	fcalls[Twalk] = Exwalk;
-	fcalls[Topen] = Exopen;
-	fcalls[Tcreate] = Excreate;
-	fcalls[Tread] = Exread;
-	fcalls[Twrite] = Exwrite;
-	fcalls[Tclunk] = Exclunk;
-	fcalls[Tremove] = Exremove;
-	fcalls[Tstat] = Exstat;
-	fcalls[Twstat] = Exwstat;
-	unlock(&exq.l);
-}
-
-void
-exportproc(Export *fs)
-{
-	Exq *q;
-	char *buf;
-	int n, cn, len;
-
-	exportinit();
-
-	for(;;){
-		q = mallocz(sizeof(Exq));
-		if(q == 0)
-			panic("no memory");
-
-		q->rpc.data = q->buf + MAXMSG;
-
-		buf = q->buf;
-		len = MAXRPC;
-		if(fs->npart) {
-			memmove(buf, fs->part, fs->npart);
-			buf += fs->npart;
-			len -= fs->npart;
-			goto chk;
-		}
-		for(;;) {
-			if(waserror())
-				goto bad;
-
-			n = (*devtab[fs->io->type].read)(fs->io, buf, len, 0);
-			poperror();
-
-			if(n <= 0)
-				goto bad;
-
-			buf += n;
-			len -= n;
-	chk:
-			n = buf - q->buf;
-
-			/* convM2S returns size of correctly decoded message */
-			cn = convM2S(q->buf, &q->rpc, n);
-			if(cn < 0){
-				iprint("bad message type in devmnt\n");
-				goto bad;
-			}
-			if(cn > 0) {
-				n -= cn;
-				if(n < 0){
-					iprint("negative size in devmnt");
-					goto bad;
-				}
-				fs->npart = n;
-				if(n != 0)
-					memmove(fs->part, q->buf+cn, n);
-				break;
-			}
-		}
-		if(exdebug)
-			iprint("export %d <- %F\n", getpid(), &q->rpc);
-
-		if(q->rpc.type == Tflush){
-			exflush(fs, q->rpc.tag, q->rpc.oldtag);
-			free(q);
-			continue;
-		}
-
-		q->export = fs;
-		refinc(&fs->r);
-
-		lock(&exq.l);
-		if(exq.head == nil)
-			exq.head = q;
-		else
-			exq.tail->next = q;
-		q->next = nil;
-		exq.tail = q;
-		unlock(&exq.l);
-		if(exq.qwait.first == nil) {
-			n = thread("exportfs", exslave, nil);
-/* iprint("launch export (pid=%ux)\n", n); */
-		}
-		rendwakeup(&exq.rwait);
-	}
-bad:
-	free(q);
-	exshutdown(fs);
-	exfree(fs);
-}
-
-void
-exflush(Export *fs, int flushtag, int tag)
-{
-	Exq *q, **last;
-	int n;
-	Fcall fc;
-	char buf[MAXMSG];
-
-	/* hasn't been started? */
-	lock(&exq.l);
-	last = &exq.head;
-	for(q = exq.head; q != nil; q = q->next){
-		if(q->export == fs && q->rpc.tag == tag){
-			*last = q->next;
-			unlock(&exq.l);
-			exfree(fs);
-			free(q);
-			goto Respond;
-		}
-		last = &q->next;
-	}
-	unlock(&exq.l);
-
-	/* in progress? */
-	lock(&fs->r.l);
-	for(q = fs->work; q != nil; q = q->next){
-		if(q->rpc.tag == tag && !q->noresponse){
-			lock(&q->lk);
-			q->noresponse = 1;
-			if(!q->nointr)
-				intr(q->slave);
-			unlock(&q->lk);
-			unlock(&fs->r.l);
-			goto Respond;
-			return;
-		}
-	}
-	unlock(&fs->r.l);
-
-if(exdebug) iprint("exflush: did not find rpc: %d\n", tag);
-
-Respond:
-	fc.type = Rflush;
-	fc.tag = flushtag;
-	n = convS2M(&fc, buf);
-if(exdebug) iprint("exflush -> %F\n", &fc);
-	if(!waserror()){
-		(*devtab[fs->io->type].write)(fs->io, buf, n, 0);
-		poperror();
-	}
-}
-
-void
-exshutdown(Export *fs)
-{
-	Exq *q, **last;
-
-	lock(&exq.l);
-	last = &exq.head;
-	for(q = exq.head; q != nil; q = *last){
-		if(q->export == fs){
-			*last = q->next;
-			exfree(fs);
-			free(q);
-			continue;
-		}
-		last = &q->next;
-	}
-	unlock(&exq.l);
-
-	lock(&fs->r.l);
-	q = fs->work;
-	while(q != nil){
-		if(q->shut){
-			q = q->next;
-			continue;
-		}
-		q->shut = 1;
-		unlock(&fs->r.l);
-	/*	postnote(q->slave, 1, "interrupted", NUser);	*/
-		iprint("postnote 2!\n");
-		lock(&fs->r.l);
-		q = fs->work;
-	}
-	unlock(&fs->r.l);
-}
-
-void
-exfree(Export *fs)
-{
-	Fid *f, *n;
-	int i;
-
-	if(refdec(&fs->r) != 0)
-		return;
-	closepgrp(fs->pgrp);
-	cclose(fs->root);
-	cclose(fs->io);
-	for(i = 0; i < Nfidhash; i++){
-		for(f = fs->fid[i]; f != nil; f = n){
-			if(f->chan != nil)
-				cclose(f->chan);
-			n = f->next;
-			free(f);
-		}
-	}
-	free(fs);
-}
-
-int
-exwork(void *a)
-{
-	return exq.head != nil;
-}
-
-void
-exslave(void *a)
-{
-	Export *fs;
-	Exq *q, *t, **last;
-	char *err;
-	int n;
-/*
-	closepgrp(up->pgrp);
-	up->pgrp = nil;
-*/
-	for(;;){
-		qlock(&exq.qwait);
-		rendsleep(&exq.rwait, exwork, nil);
-
-		lock(&exq.l);
-		q = exq.head;
-		if(q == nil) {
-			unlock(&exq.l);
-			qunlock(&exq.qwait);
-			continue;
-		}
-		exq.head = q->next;
-		q->slave = curthread();
-		unlock(&exq.l);
-
-		qunlock(&exq.qwait);
-
-		q->noresponse = 0;
-		q->nointr = 0;
-		fs = q->export;
-		lock(&fs->r.l);
-		q->next = fs->work;
-		fs->work = q;
-		unlock(&fs->r.l);
-
-		up->pgrp = q->export->pgrp;
-
-		if(exdebug > 1)
-			iprint("exslave dispatch %d %F\n", getpid(), &q->rpc);
-
-		if(waserror()){
-			iprint("exslave err %r\n");
-			err = up->errstr;
-			goto Err;
-		}
-		if(q->rpc.type >= Tmax || !fcalls[q->rpc.type])
-			err = "bad fcall type";
-		else
-			err = (*fcalls[q->rpc.type])(fs, &q->rpc);
-
-		poperror();
-		Err:;
-		q->rpc.type++;
-		if(err){
-			q->rpc.type = Rerror;
-			strncpy(q->rpc.ename, err, ERRLEN);
-		}
-		n = convS2M(&q->rpc, q->buf);
-
-		if(exdebug)
-			iprint("exslve %d -> %F\n", getpid(), &q->rpc);
-
-		lock(&q->lk);
-		if(q->noresponse == 0){
-			q->nointr = 1;
-			clearintr();
-			if(!waserror()){
-				(*devtab[fs->io->type].write)(fs->io, q->buf, n, 0);
-				poperror();
-			}
-		}
-		unlock(&q->lk);
-
-		/*
-		 * exflush might set noresponse at this point, but
-		 * setting noresponse means don't send a response now;
-		 * it's okay that we sent a response already.
-		 */
-		if(exdebug > 1)
-			iprint("exslave %d written %d\n", getpid(), q->rpc.tag);
-
-		lock(&fs->r.l);
-		last = &fs->work;
-		for(t = fs->work; t != nil; t = t->next){
-			if(t == q){
-				*last = q->next;
-				break;
-			}
-			last = &t->next;
-		}
-		unlock(&fs->r.l);
-
-		exfree(q->export);
-		free(q);
-	}
-	iprint("exslave shut down");
-	threadexit();
-}
-
-Fid*
-Exmkfid(Export *fs, int fid)
-{
-	ulong h;
-	Fid *f, *nf;
-
-	nf = mallocz(sizeof(Fid));
-	if(nf == nil)
-		return nil;
-	lock(&fs->fidlock);
-	h = fid % Nfidhash;
-	for(f = fs->fid[h]; f != nil; f = f->next){
-		if(f->fid == fid){
-			unlock(&fs->fidlock);
-			free(nf);
-			return nil;
-		}
-	}
-
-	nf->next = fs->fid[h];
-	if(nf->next != nil)
-		nf->next->last = &nf->next;
-	nf->last = &fs->fid[h];
-	fs->fid[h] = nf;
-
-	nf->fid = fid;
-	nf->ref = 1;
-	nf->attached = 1;
-	nf->offset = 0;
-	nf->chan = nil;
-	unlock(&fs->fidlock);
-	return nf;
-}
-
-Fid*
-Exgetfid(Export *fs, int fid)
-{
-	Fid *f;
-	ulong h;
-
-	lock(&fs->fidlock);
-	h = fid % Nfidhash;
-	for(f = fs->fid[h]; f; f = f->next) {
-		if(f->fid == fid){
-			if(f->attached == 0)
-				break;
-			f->ref++;
-			unlock(&fs->fidlock);
-			return f;
-		}
-	}
-	unlock(&fs->fidlock);
-	return nil;
-}
-
-void
-Exputfid(Export *fs, Fid *f)
-{
-	lock(&fs->fidlock);
-	f->ref--;
-	if(f->ref == 0 && f->attached == 0){
-		if(f->chan != nil)
-			cclose(f->chan);
-		f->chan = nil;
-		*f->last = f->next;
-		if(f->next != nil)
-			f->next->last = f->last;
-		unlock(&fs->fidlock);
-		free(f);
-		return;
-	}
-	unlock(&fs->fidlock);
-}
-
-char*
-Exsession(Export *e, Fcall *rpc)
-{
-	memset(rpc->authid, 0, sizeof(rpc->authid));
-	memset(rpc->authdom, 0, sizeof(rpc->authdom));
-	memset(rpc->chal, 0, sizeof(rpc->chal));
-	return nil;
-}
-
-char*
-Exauth(Export *e, Fcall *f)
-{
-	return "authentication not required";
-}
-
-char*
-Exattach(Export *fs, Fcall *rpc)
-{
-	Fid *f;
-
-	f = Exmkfid(fs, rpc->fid);
-	if(f == nil)
-		return Einuse;
-	if(waserror()){
-		f->attached = 0;
-		Exputfid(fs, f);
-		return up->errstr;
-	}
-	f->chan = clone(fs->root, nil);
-	poperror();
-	rpc->qid = f->chan->qid;
-	Exputfid(fs, f);
-	return nil;
-}
-
-char*
-Exclone(Export *fs, Fcall *rpc)
-{
-	Fid *f, *nf;
-
-	if(rpc->fid == rpc->newfid)
-		return Einuse;
-	f = Exgetfid(fs, rpc->fid);
-	if(f == nil)
-		return Enofid;
-	nf = Exmkfid(fs, rpc->newfid);
-	if(nf == nil){
-		Exputfid(fs, f);
-		return Einuse;
-	}
-	if(waserror()){
-		Exputfid(fs, f);
-		Exputfid(fs, nf);
-		return up->errstr;
-	}
-	nf->chan = clone(f->chan, nil);
-	poperror();
-	Exputfid(fs, f);
-	Exputfid(fs, nf);
-	return nil;
-}
-
-char*
-Exclunk(Export *fs, Fcall *rpc)
-{
-	Fid *f;
-
-	f = Exgetfid(fs, rpc->fid);
-	if(f != nil){
-		f->attached = 0;
-		Exputfid(fs, f);
-	}
-	return nil;
-}
-
-char*
-Exwalk(Export *fs, Fcall *rpc)
-{
-	Fid *f;
-	Chan *c;
-
-	f = Exgetfid(fs, rpc->fid);
-	if(f == nil)
-		return Enofid;
-	if(waserror()){
-		Exputfid(fs, f);
-		return up->errstr;
-	}
-	c = walk(f->chan, rpc->name, 1);
-	if(c == nil)
-		error(Enonexist);
-	poperror();
-
-	f->chan = c;
-	rpc->qid = c->qid;
-	Exputfid(fs, f);
-	return nil;
-}
-
-char*
-Exopen(Export *fs, Fcall *rpc)
-{
-	Fid *f;
-	Chan *c;
-
-	f = Exgetfid(fs, rpc->fid);
-	if(f == nil)
-		return Enofid;
-	if(waserror()){
-		Exputfid(fs, f);
-		return up->errstr;
-	}
-	c = f->chan;
-	c = (*devtab[c->type].open)(c, rpc->mode);
-	poperror();
-
-	f->chan = c;
-	f->offset = 0;
-	rpc->qid = f->chan->qid;
-	Exputfid(fs, f);
-	return nil;
-}
-
-char*
-Excreate(Export *fs, Fcall *rpc)
-{
-	Fid *f;
-	Chan *c;
-
-	f = Exgetfid(fs, rpc->fid);
-	if(f == nil)
-		return Enofid;
-	if(waserror()){
-		Exputfid(fs, f);
-		return up->errstr;
-	}
-	c = f->chan;
-	if(c->mnt && !(c->flag&CCREATE))
-		c = createdir(c);
-	c = (*devtab[c->type].create)(c, rpc->name, rpc->mode, rpc->perm);
-	poperror();
-
-	f->chan = c;
-	rpc->qid = f->chan->qid;
-	Exputfid(fs, f);
-	return nil;
-}
-
-char*
-Exread(Export *fs, Fcall *rpc)
-{
-	Fid *f;
-	Chan *c;
-	long off;
-	int dir, n, seek;
-
-	f = Exgetfid(fs, rpc->fid);
-	if(f == nil)
-		return Enofid;
-
-	c = f->chan;
-	dir = c->qid.path & CHDIR;
-	if(dir){
-		rpc->count -= rpc->count%DIRLEN;
-		if(rpc->offset%DIRLEN || rpc->count==0){
-			Exputfid(fs, f);
-			return Ereaddir;
-		}
-		if(f->offset > rpc->offset){
-			Exputfid(fs, f);
-			return Eseekdir;
-		}
-	}
-
-	if(waserror()) {
-		Exputfid(fs, f);
-		return up->errstr;
-	}
-
-	for(;;){
-		n = rpc->count;
-		seek = 0;
-		off = rpc->offset;
-		if(dir && f->offset != off){
-			off = f->offset;
-			n = rpc->offset - off;
-			if(n > MAXDIRREAD)
-				n = MAXDIRREAD;
-			seek = 1;
-		}
-		if(dir && c->mnt != nil)
-			n = unionread(c, rpc->data, n);
-		else{
-			c->offset = off;
-			n = (*devtab[c->type].read)(c, rpc->data, n, off);
-		}
-		if(n == 0 || !seek)
-			break;
-		f->offset = off + n;
-		c->offset += n;
-	}
-	rpc->count = n;
-	poperror();
-	Exputfid(fs, f);
-	return nil;
-}
-
-char*
-Exwrite(Export *fs, Fcall *rpc)
-{
-	Fid *f;
-	Chan *c;
-
-	f = Exgetfid(fs, rpc->fid);
-	if(f == nil)
-		return Enofid;
-	if(waserror()){
-		Exputfid(fs, f);
-		return up->errstr;
-	}
-	c = f->chan;
-	if(c->qid.path & CHDIR)
-		error(Eisdir);
-	rpc->count = (*devtab[c->type].write)(c, rpc->data, rpc->count, rpc->offset);
-	poperror();
-	Exputfid(fs, f);
-	return nil;
-}
-
-char*
-Exstat(Export *fs, Fcall *rpc)
-{
-	Fid *f;
-	Chan *c;
-
-	f = Exgetfid(fs, rpc->fid);
-	if(f == nil)
-		return Enofid;
-	if(waserror()){
-		Exputfid(fs, f);
-		return up->errstr;
-	}
-	c = f->chan;
-	(*devtab[c->type].stat)(c, rpc->stat);
-	poperror();
-	Exputfid(fs, f);
-	return nil;
-}
-
-char*
-Exwstat(Export *fs, Fcall *rpc)
-{
-	Fid *f;
-	Chan *c;
-
-	f = Exgetfid(fs, rpc->fid);
-	if(f == nil)
-		return Enofid;
-	if(waserror()){
-		Exputfid(fs, f);
-		return up->errstr;
-	}
-	c = f->chan;
-	(*devtab[c->type].wstat)(c, rpc->stat);
-	poperror();
-	Exputfid(fs, f);
-	return nil;
-}
-
-char*
-Exremove(Export *fs, Fcall *rpc)
-{
-	Fid *f;
-	Chan *c;
-
-	f = Exgetfid(fs, rpc->fid);
-	if(f == nil)
-		return Enofid;
-	if(waserror()){
-		Exputfid(fs, f);
-		return up->errstr;
-	}
-	c = f->chan;
-	(*devtab[c->type].remove)(c);
-	poperror();
-
-	/*
-	 * chan is already clunked by remove.
-	 * however, we need to recover the chan,
-	 * and follow sysremove's lead in making to point to root.
-	 */
-	c->type = 0;
-
-	f->attached = 0;
-	Exputfid(fs, f);
-	return nil;
-}
--- a/kern/fns.h
+++ b/kern/fns.h
@@ -10,7 +10,7 @@
 int 		canlock(Lock*);
 int		    canputc(void*);
 int	    	canqlock(QLock*);
-int 		canrlock(RWlock*);
+int 		canrlock(RWLock*);
 void		chandevinit(void);
 void		chandevreset(void);
 void		chandevshutdown(void);
@@ -45,7 +45,7 @@
 long		devdirread(Chan*, char*, long, Dirtab*, int, Devgen*);
 Devgen		devgen;
 void		devinit(void);
-int		    devno(int, int);
+int		    devno(int);
 Chan*		devopen(Chan*, int, Dirtab*, int, Devgen*);
 void		devpermcheck(char*, ulong, int);
 void		devpower(int);
@@ -71,7 +71,6 @@
 void		exit(int);
 Fd*         copyfd(Fd*);
 void        fddecref(Fd*);
-void        fdclose(int, int);
 Chan*		fdtochan(int, int, int, int);
 void        fpatransfer(u32int);
 void        fpaoperation(u32int);
@@ -151,6 +150,7 @@
 void		pexit(char*, int);
 void		printinit(void);
 int	    	procindex(ulong);
+void		procinterrupt(Proc*);
 void		pgrpcpy(Pgrp*, Pgrp*);
 void		pgrpnote(ulong, char*, long, int);
 Pgrp*		pgrptab(int);
@@ -207,8 +207,8 @@
 int		    readnum(ulong, char*, ulong, ulong, int);
 int 		readstr(ulong, char*, ulong, char*);
 int	    	return0(void*);
-void		rlock(RWlock*);
-void		runlock(RWlock*);
+void		rlock(RWLock*);
+void		runlock(RWLock*);
 extern void	(*screenputs)(char*, int);
 void*		secalloc(ulong);
 void		secfree(void*);
@@ -244,8 +244,8 @@
 Proc*		wakeup(Rendez*);
 int		    walk(Chan**, char**, int, int, int*);
 #define	    waserror()	(setjmp(pwaserror()->buf))
-void		wlock(RWlock*);
-void		wunlock(RWlock*);
+void		wlock(RWLock*);
+void		wunlock(RWLock*);
 void		osyield(void);
 void		osmsleep(int);
 ulong   	ticks(void);
--- a/kern/notify.c
+++ b/kern/notify.c
@@ -19,6 +19,12 @@
  * useful.
  */
 
+
+ /* TODO: This goes away
+  * The notes here are for taking host notes and delivering to 
+  * the kernel. We will be going the other way, from the 
+  * rc + kernelspace to the running processes. 
+  */
 #include <u.h>
 #include <signal.h>
 #include <lib.h>
--- a/kern/parse.c
+++ b/kern/parse.c
@@ -35,7 +35,7 @@
 Cmdbuf*
 parsecmd(char *p, int n)
 {
-	Cmdbuf */*volatile*/ cb;
+	Cmdbuf *volatile cb;
 	int nf;
 	char *sp;
 
--- a/kern/posix.c
+++ b/kern/posix.c
@@ -13,19 +13,6 @@
 #include <sys/wait.h>
 #include <sys/time.h>
 #include <sys/select.h>
-
-#if defined (__APPLE__)
-#include <mach/mach.h>
-#include <mach/vm_map.h>
-#include <mach/mach_error.h>
-#include <mach/mach_vm.h> 
-#ifdef panic
-#undef panic
-#endif
-mach_port_t task;
-#endif
-
-#include <sys/mman.h>
 #include <signal.h>
 #include <pwd.h>
 #include <errno.h>
@@ -72,10 +59,6 @@
 	if(pthread_key_create(&prdakey, 0))
 		panic("cannot pthread_key_create");
 
-#if defined __APPLE__
-    task = mach_task_self();
-#endif
-
 	signal(SIGPIPE, SIG_IGN);
 }
 
@@ -150,6 +133,7 @@
 tramp(void *vp)
 {
 	Proc *p;
+
 	p = vp;
 	if(pthread_setspecific(prdakey, p))
 		panic("cannot setspecific");
--- a/kern/qio.c
+++ b/kern/qio.c
@@ -4,13 +4,6 @@
 #include	"fns.h"
 #include	"error.h"
 
-static ulong padblockcnt;
-static ulong concatblockcnt;
-static ulong pullupblockcnt;
-static ulong copyblockcnt;
-static ulong consumecnt;
-static ulong producecnt;
-
 #define QDEBUG	if(0)
 
 /*
@@ -17,28 +10,28 @@
  *  IO queues
  */
 typedef struct Queue	Queue;
-
 struct Queue
 {
 	Lock	lk;
 
+	int	state;
+	int	dlen;		/* data length in bytes */
+	uint	rp, wp;		/* read/write position (counting BALLOC() bytes) */
+	int	limit;		/* max BALLOC() bytes in queue */
+	int	inilim;		/* initial limit */
+	uchar	noblock;	/* true if writes return immediately when q full */
+	uchar	eof;		/* number of eofs read by user */
+
 	Block*	bfirst;		/* buffer */
 	Block*	blast;
 
-	int	len;		/* bytes allocated to queue */
-	int	dlen;		/* data bytes in queue */
-	int	limit;		/* max bytes in queue */
-	int	inilim;		/* initial limit */
-	int	state;
-	int	noblock;	/* true if writes return immediately when q full */
-	int	eof;		/* number of eofs read by user */
-
+	void*	arg;		/* argument to kick and bypass */
 	void	(*kick)(void*);	/* restart output */
 	void	(*bypass)(void*, Block*);	/* bypass queue altogether */
-	void*	arg;		/* argument to kick */
 
 	QLock	rlock;		/* mutex for reading processes */
 	Rendez	rr;		/* process waiting to read */
+
 	QLock	wlock;		/* mutex for writing processes */
 	Rendez	wr;		/* process waiting to write */
 
@@ -68,44 +61,6 @@
 }
 
 /*
- *  pad a block to the front (or the back if size is negative)
- */
-Block*
-padblock(Block *bp, int size)
-{
-	int n;
-	Block *nbp;
-
-	QDEBUG checkb(bp, "padblock 0");
-	if(size >= 0){
-		if(bp->rp - bp->base >= size){
-			bp->rp -= size;
-			return bp;
-		}
-		n = BLEN(bp);
-		nbp = allocb(size+n);
-		nbp->rp += size;
-		nbp->wp = nbp->rp;
-		memmove(nbp->wp, bp->rp, n);
-		nbp->wp += n;
-		nbp->rp -= size;
-	} else {
-		size = -size;
-		if(bp->lim - bp->wp >= size)
-			return bp;
-		n = BLEN(bp);
-		nbp = allocb(n+size);
-		memmove(nbp->wp, bp->rp, n);
-		nbp->wp += n;
-	}
-	nbp->next = bp->next;
-	freeb(bp);
-	padblockcnt++;
-	QDEBUG checkb(nbp, "padblock 1");
-	return nbp;
-}
-
-/*
  *  return count of bytes in a string of blocks
  */
 int
@@ -122,19 +77,33 @@
 }
 
 /*
- * return count of space in blocks
+ *  copy the contents of a string of blocks into
+ *  memory from an offset. blocklist kept unchanged.
+ *  return number of copied bytes.
  */
-int
-blockalloclen(Block *bp)
+long
+readblist(Block *b, uchar *p, long n, ulong o)
 {
-	int len;
+	ulong m, r;
 
-	len = 0;
-	while(bp != nil) {
-		len += BALLOC(bp);
-		bp = bp->next;
+	r = 0;
+	while(n > 0 && b != nil){
+		m = BLEN(b);
+		if(o >= m)
+			o -= m;
+		else {
+			m -= o;
+			if(n < m)
+				m = n;
+			memmove(p, b->rp + o, m);
+			p += m;
+			r += m;
+			n -= m;
+			o = 0;
+		}
+		b = b->next;
 	}
-	return len;
+	return r;
 }
 
 /*
@@ -149,7 +118,6 @@
 	if(bp->next == nil)
 		return bp;
 	len = blocklen(bp);
-	concatblockcnt += len;
 	return pullupblock(bp, len);
 }
 
@@ -162,6 +130,8 @@
 	Block *nbp;
 	int i;
 
+	assert(n >= 0);
+
 	/*
 	 *  this should almost always be true, it's
 	 *  just to avoid every caller checking.
@@ -184,7 +154,6 @@
 	 */
 	n -= BLEN(bp);
 	while((nbp = bp->next) != nil){
-		pullupblockcnt++;
 		i = BLEN(nbp);
 		if(i > n) {
 			memmove(bp->wp, nbp->rp, n);
@@ -223,6 +192,8 @@
 {
 	Block *b;
 
+	assert(n >= 0);
+
 	if(BLEN(q->bfirst) >= n)
 		return q->bfirst;
 	q->bfirst = pullupblock(q->bfirst, n);
@@ -241,8 +212,14 @@
 	ulong l;
 	Block *nb, *startb;
 
+	assert(len >= 0);
+	assert(offset >= 0);
+
 	QDEBUG checkb(bp, "trimblock 1");
-	if(blocklen(bp) < offset+len) {
+	l = blocklen(bp);
+	if(offset == 0 && len == l)
+		return bp;
+	if(l < offset+len) {
 		freeblist(bp);
 		return nil;
 	}
@@ -274,6 +251,43 @@
 }
 
 /*
+ *  pad a block to the front (or the back if size is negative)
+ */
+Block*
+padblock(Block *bp, int size)
+{
+	int n;
+	Block *nbp;
+
+	QDEBUG checkb(bp, "padblock 0");
+	if(size >= 0){
+		if(bp->rp - bp->base >= size){
+			bp->rp -= size;
+			return bp;
+		}
+		n = BLEN(bp);
+		nbp = allocb(size+n);
+		nbp->rp += size;
+		nbp->wp = nbp->rp;
+		memmove(nbp->wp, bp->rp, n);
+		nbp->wp += n;
+		nbp->rp -= size;
+	} else {
+		size = -size;
+		if(bp->lim - bp->wp >= size)
+			return bp;
+		n = BLEN(bp);
+		nbp = allocb(n+size);
+		memmove(nbp->wp, bp->rp, n);
+		nbp->wp += n;
+	}
+	nbp->next = bp->next;
+	freeb(bp);
+	QDEBUG checkb(nbp, "padblock 1");
+	return nbp;
+}
+
+/*
  *  copy 'count' bytes into a new block
  */
 Block*
@@ -282,6 +296,8 @@
 	int l;
 	Block *nbp;
 
+	assert(count >= 0);
+
 	QDEBUG checkb(bp, "copyblock 0");
 	nbp = allocb(count);
 	for(; count > 0 && bp != nil; bp = bp->next){
@@ -296,7 +312,6 @@
 		memset(nbp->wp, 0, count);
 		nbp->wp += count;
 	}
-	copyblockcnt++;
 	QDEBUG checkb(nbp, "copyblock 1");
 
 	return nbp;
@@ -330,7 +345,30 @@
 	return bp;
 }
 
+/*
+ *  if the allocated space is way out of line with the used
+ *  space, reallocate to a smaller block
+ */
+Block*
+packblock(Block *bp)
+{
+	Block **l, *nbp;
+	int n;
 
+	for(l = &bp; (nbp = *l) != nil; l = &(*l)->next){
+		n = BLEN(nbp);
+		if((n<<2) < BALLOC(nbp)){
+			*l = allocb(n);
+			memmove((*l)->wp, nbp->rp, n);
+			(*l)->wp += n;
+			(*l)->next = nbp->next;
+			freeb(nbp);
+		}
+	}
+
+	return bp;
+}
+
 /*
  *  throw away up to count bytes from a
  *  list of blocks.  Return count of bytes
@@ -346,8 +384,8 @@
 	if(bph == nil)
 		return 0;
 
-	while(*bph != nil && count != 0) {
-		bp = *bph;
+	while((bp = *bph) != nil && count > 0) {
+		QDEBUG checkb(bp, "pullblock ");
 		n = BLEN(bp);
 		if(count < n)
 			n = count;
@@ -354,7 +392,6 @@
 		bytes += n;
 		count -= n;
 		bp->rp += n;
-		QDEBUG checkb(bp, "pullblock ");
 		if(BLEN(bp) == 0) {
 			*bph = bp->next;
 			bp->next = nil;
@@ -365,103 +402,145 @@
 }
 
 /*
- *  get next block from a queue, return null if nothing there
+ *  remove a block from the front of the queue
  */
 Block*
-qget(Queue *q)
+qremove(Queue *q)
 {
-	int dowakeup;
 	Block *b;
 
-	/* sync with qwrite */
-	ilock(&q->lk);
-
 	b = q->bfirst;
-	if(b == nil){
-		q->state |= Qstarve;
-		iunlock(&q->lk);
+	if(b == nil)
 		return nil;
-	}
-	QDEBUG checkb(b, "qget");
+	QDEBUG checkb(b, "qremove");
 	q->bfirst = b->next;
 	b->next = nil;
-	q->len -= BALLOC(b);
 	q->dlen -= BLEN(b);
+	q->rp += BALLOC(b);
+	return b;
+}
 
-	/* if writer flow controlled, restart */
-	if((q->state & Qflow) && q->len < q->limit/2){
-		q->state &= ~Qflow;
-		dowakeup = 1;
-	} else
-		dowakeup = 0;
+/*
+ *  put a block back to the front of the queue
+ */
+void
+qputback(Queue *q, Block *b)
+{
+	QDEBUG checkb(b, "qputback");
+	b->next = q->bfirst;
+	if(q->bfirst == nil)
+		q->blast = b;
+	q->bfirst = b;
+	q->dlen += BLEN(b);
+	q->rp -= BALLOC(b);
+}
 
+/*
+ *  after removing data from the queue,
+ *  unlock queue and wakeup blocked writer.
+ *  called at interrupt level.
+ */
+static int
+iunlock_consumer(Queue *q)
+{
+	int s = q->state;
+
+	/* stop flow control when back at or below the limit */
+	if((int)(q->wp - q->rp) <= q->limit)
+		q->state = s & ~Qflow;
+
 	iunlock(&q->lk);
 
-	if(dowakeup)
+	if(s & Qflow){
+		/*
+		 * wakeup flow controlled writers.
+		 * note that this is done even when q->state
+		 * still has Qflow set, as the unblocking
+		 * condition depends on the writers local queuing
+		 * position, not on the global queue length.
+		 */
 		wakeup(&q->wr);
-
-	return b;
+	}
+	return s;
 }
 
 /*
- *  throw away the next 'len' bytes in the queue
+ *  after removing data from the queue,
+ *  unlock queue and wakeup blocked writer.
+ *  get output going again when it was blocked.
+ *  called at process level.
  */
-int
-qdiscard(Queue *q, int len)
+static int
+iunlock_reader(Queue *q)
 {
-	Block *b, *tofree = nil;
-	int dowakeup, n, sofar;
+	int s = iunlock_consumer(q);
 
-	ilock(&q->lk);
-	for(sofar = 0; sofar < len; sofar += n){
-		b = q->bfirst;
-		if(b == nil)
-			break;
-		QDEBUG checkb(b, "qdiscard");
-		n = BLEN(b);
-		if(n <= len - sofar){
-			q->bfirst = b->next;
-			q->len -= BALLOC(b);
-			q->dlen -= BLEN(b);
+	if(q->kick != nil && s & Qflow)
+		(*q->kick)(q->arg);
 
-			/* remember to free this */
-			b->next = tofree;
-			tofree = b;
-		} else {
-			n = len - sofar;
-			b->rp += n;
-			q->dlen -= n;
-		}
-	}
+	return s;
+}
 
-	/*
-	 *  if writer flow controlled, restart
-	 *
-	 *  This used to be
-	 *	q->len < q->limit/2
-	 *  but it slows down tcp too much for certain write sizes.
-	 *  I really don't understand it completely.  It may be
-	 *  due to the queue draining so fast that the transmission
-	 *  stalls waiting for the app to produce more data.  - presotto
-	 */
-	if((q->state & Qflow) && q->len < q->limit){
-		q->state &= ~Qflow;
-		dowakeup = 1;
-	} else
-		dowakeup = 0;
+/*
+ *  after inserting into queue,
+ *  unlock queue and wakeup starved reader.
+ *  called at interrupt level.
+ */
+static int
+iunlock_producer(Queue *q)
+{
+	int s = q->state;
 
+	/* start flow control when above the limit */
+	if((int)(q->wp - q->rp) > q->limit)
+		s |= Qflow;
+
+	q->state = s & ~Qstarve;
 	iunlock(&q->lk);
 
-	if(dowakeup)
-		wakeup(&q->wr);
+	if(s & Qstarve)
+		wakeup(&q->rr);
 
-	if(tofree != nil)
-		freeblist(tofree);
+	return s;
+}
 
-	return sofar;
+/*
+ *  unlock queue and wakeup starved reader.
+ *  get output going again when it was starved.
+ *  called at process level.
+ */
+static int
+iunlock_writer(Queue *q)
+{
+	int s = iunlock_producer(q);
+
+	if(q->kick != nil && s & (Qstarve|Qkick))
+		(*q->kick)(q->arg);
+
+	return s;
 }
 
 /*
+ *  get next block from a queue, return null if nothing there
+ *  called at interrupt level.
+ */
+Block*
+qget(Queue *q)
+{
+	Block *b;
+
+	ilock(&q->lk);
+	if((b = qremove(q)) == nil){
+		q->state |= Qstarve;
+		iunlock(&q->lk);
+		return nil;
+	}
+	iunlock_consumer(q);
+
+	return b;
+}
+
+/*
  *  Interrupt level copy out of a queue, return # bytes copied.
  */
 int
@@ -468,12 +547,11 @@
 qconsume(Queue *q, void *vp, int len)
 {
 	Block *b, *tofree = nil;
-	int n, dowakeup;
-	uchar *p = vp;
+	int n;
 
-	/* sync with qwrite */
-	ilock(&q->lk);
+	assert(len >= 0);
 
+	ilock(&q->lk);
 	for(;;) {
 		b = q->bfirst;
 		if(b == nil){
@@ -486,8 +564,10 @@
 		n = BLEN(b);
 		if(n > 0)
 			break;
+
+		/* get rid of zero-length blocks */
 		q->bfirst = b->next;
-		q->len -= BALLOC(b);
+		q->rp += BALLOC(b);
 
 		/* remember to free this */
 		b->next = tofree;
@@ -494,10 +574,9 @@
 		tofree = b;
 	};
 
-	consumecnt += n;
 	if(n < len)
 		len = n;
-	memmove(p, b->rp, len);
+	memmove(vp, b->rp, len);
 	b->rp += len;
 	q->dlen -= len;
 
@@ -504,7 +583,7 @@
 	/* discard the block if we're done with it */
 	if((q->state & Qmsg) || len == n){
 		q->bfirst = b->next;
-		q->len -= BALLOC(b);
+		q->rp += BALLOC(b);
 		q->dlen -= BLEN(b);
 
 		/* remember to free this */
@@ -511,23 +590,40 @@
 		b->next = tofree;
 		tofree = b;
 	}
-
 out:
-	/* if writer flow controlled, restart */
-	if((q->state & Qflow) && q->len < q->limit/2){
-		q->state &= ~Qflow;
-		dowakeup = 1;
-	} else
-		dowakeup = 0;
+	iunlock_consumer(q);
 
-	iunlock(&q->lk);
+	freeblist(tofree);
 
-	if(dowakeup)
-		wakeup(&q->wr);
+	return len;
+}
 
-	if(tofree != nil)
-		freeblist(tofree);
+/*
+ *  add a block list to a queue, return bytes added
+ */
+int
+qaddlist(Queue *q, Block *b)
+{
+	int len;
 
+	QDEBUG checkb(b, "qaddlist 1");
+
+	/* queue the block */
+	if(q->bfirst != nil)
+		q->blast->next = b;
+	else
+		q->bfirst = b;
+
+	len = BLEN(b);
+	q->wp += BALLOC(b);
+	while(b->next != nil){
+		b = b->next;
+		QDEBUG checkb(b, "qaddlist 2");
+		len += BLEN(b);
+		q->wp += BALLOC(b);
+	}
+	q->dlen += len;
+	q->blast = b;
 	return len;
 }
 
@@ -534,36 +630,22 @@
 int
 qpass(Queue *q, Block *b)
 {
-	int len, dowakeup;
+	int len;
 
-	/* sync with qread */
-	dowakeup = 0;
 	ilock(&q->lk);
-	if(q->len >= q->limit){
+	if(q->state & Qclosed){
 		iunlock(&q->lk);
 		freeblist(b);
-		return -1;
+		return 0;
 	}
-	if(q->state & Qclosed){
+	if(q->state & Qflow){
 		iunlock(&q->lk);
 		freeblist(b);
-		return 0;
+		return -1;
 	}
-
 	len = qaddlist(q, b);
+	iunlock_producer(q);
 
-	if(q->len >= q->limit/2)
-		q->state |= Qflow;
-
-	if(q->state & Qstarve){
-		q->state &= ~Qstarve;
-		dowakeup = 1;
-	}
-	iunlock(&q->lk);
-
-	if(dowakeup)
-		wakeup(&q->rr);
-
 	return len;
 }
 
@@ -570,101 +652,36 @@
 int
 qpassnolim(Queue *q, Block *b)
 {
-	int len, dowakeup;
+	int len;
 
-	/* sync with qread */
-	dowakeup = 0;
 	ilock(&q->lk);
-
 	if(q->state & Qclosed){
 		iunlock(&q->lk);
 		freeblist(b);
 		return 0;
 	}
-
 	len = qaddlist(q, b);
+	iunlock_producer(q);
 
-	if(q->len >= q->limit/2)
-		q->state |= Qflow;
-
-	if(q->state & Qstarve){
-		q->state &= ~Qstarve;
-		dowakeup = 1;
-	}
-	iunlock(&q->lk);
-
-	if(dowakeup)
-		wakeup(&q->rr);
-
 	return len;
 }
 
-/*
- *  if the allocated space is way out of line with the used
- *  space, reallocate to a smaller block
- */
-Block*
-packblock(Block *bp)
-{
-	Block **l, *nbp;
-	int n;
-
-	for(l = &bp; (nbp = *l) != nil; l = &(*l)->next){
-		n = BLEN(nbp);
-		if((n<<2) < BALLOC(nbp)){
-			*l = allocb(n);
-			memmove((*l)->wp, nbp->rp, n);
-			(*l)->wp += n;
-			(*l)->next = nbp->next;
-			freeb(nbp);
-		}
-	}
-
-	return bp;
-}
-
 int
 qproduce(Queue *q, void *vp, int len)
 {
 	Block *b;
-	int dowakeup;
-	uchar *p = vp;
 
+	assert(len >= 0);
+
 	b = iallocb(len);
 	if(b == nil)
 		return 0;
 
-	/* sync with qread */
-	dowakeup = 0;
-	ilock(&q->lk);
-
-	/* no waiting receivers, room in buffer? */
-	if(q->len >= q->limit){
-		q->state |= Qflow;
-		iunlock(&q->lk);
-		freeb(b);
-		return -1;
-	}
-	producecnt += len;
-
 	/* save in buffer */
-	memmove(b->wp, p, len);
+	memmove(b->wp, vp, len);
 	b->wp += len;
-	qaddlist(q, b);
 
-	if(q->state & Qstarve){
-		q->state &= ~Qstarve;
-		dowakeup = 1;
-	}
-
-	if(q->len >= q->limit)
-		q->state |= Qflow;
-	iunlock(&q->lk);
-
-	if(dowakeup)
-		wakeup(&q->rr);
-
-	return len;
+	return qpass(q, b);
 }
 
 /*
@@ -675,6 +692,8 @@
 {
 	Block *b;
 
+	assert(len >= 0);
+
 	b = allocb(len);
 	ilock(&q->lk);
 	b->wp += readblist(q->bfirst, b->wp, len, offset);
@@ -690,16 +709,18 @@
 {
 	Queue *q;
 
+	assert(limit >= 0);
+
 	q = malloc(sizeof(Queue));
 	if(q == nil)
 		return nil;
 
+	q->dlen = 0;
+	q->wp = q->rp = 0;
 	q->limit = q->inilim = limit;
 	q->kick = kick;
 	q->arg = arg;
-	q->state = msg;
-	
-	q->state |= Qstarve;
+	q->state = msg | Qstarve;
 	q->eof = 0;
 	q->noblock = 0;
 
@@ -716,10 +737,14 @@
 	if(q == nil)
 		return nil;
 
+	q->dlen = 0;
+	q->wp = q->rp = 0;
 	q->limit = 0;
 	q->arg = arg;
 	q->bypass = bypass;
 	q->state = 0;
+	q->eof = 0;
+	q->noblock = 0;
 
 	return q;
 }
@@ -729,7 +754,7 @@
 {
 	Queue *q = a;
 
-	return (q->state & Qclosed) || q->bfirst != nil;
+	return q->bfirst != nil || (q->state & Qclosed);
 }
 
 /*
@@ -745,10 +770,9 @@
 			break;
 
 		if(q->state & Qclosed){
-			if(++q->eof > 3)
+			if(q->eof >= 3 || (*q->err && strcmp(q->err, Ehungup) != 0))
 				return -1;
-			if(*q->err && strcmp(q->err, Ehungup) != 0)
-				return -1;
+			q->eof++;
 			return 0;
 		}
 
@@ -761,101 +785,6 @@
 }
 
 /*
- * add a block list to a queue, return bytes added
- */
-int
-qaddlist(Queue *q, Block *b)
-{
-	int len, dlen;
-
-	QDEBUG checkb(b, "qaddlist 1");
-
-	/* queue the block */
-	if(q->bfirst != nil)
-		q->blast->next = b;
-	else
-		q->bfirst = b;
-
-	len = BALLOC(b);
-	dlen = BLEN(b);
-	while(b->next != nil){
-		b = b->next;
-		QDEBUG checkb(b, "qaddlist 2");
-
-		len += BALLOC(b);
-		dlen += BLEN(b);
-	}
-	q->blast = b;
-	q->len += len;
-	q->dlen += dlen;
-	return dlen;
-}
-
-/*
- *  called with q ilocked
- */
-Block*
-qremove(Queue *q)
-{
-	Block *b;
-
-	b = q->bfirst;
-	if(b == nil)
-		return nil;
-	QDEBUG checkb(b, "qremove");
-	q->bfirst = b->next;
-	b->next = nil;
-	q->dlen -= BLEN(b);
-	q->len -= BALLOC(b);
-	return b;
-}
-
-/*
- *  copy the contents of a string of blocks into
- *  memory from an offset. blocklist kept unchanged.
- *  return number of copied bytes.
- */
-long
-readblist(Block *b, uchar *p, long n, ulong o)
-{
-	ulong m, r;
-
-	r = 0;
-	while(n > 0 && b != nil){
-		m = BLEN(b);
-		if(o >= m)
-			o -= m;
-		else {
-			m -= o;
-			if(n < m)
-				m = n;
-			memmove(p, b->rp + o, m);
-			p += m;
-			r += m;
-			n -= m;
-			o = 0;
-		}
-		b = b->next;
-	}
-	return r;
-}
-
-/*
- *  put a block back to the front of the queue
- *  called with q ilocked
- */
-void
-qputback(Queue *q, Block *b)
-{
-	b->next = q->bfirst;
-	if(q->bfirst == nil)
-		q->blast = b;
-	q->bfirst = b;
-	q->len += BALLOC(b);
-	q->dlen += BLEN(b);
-}
-
-/*
  *  cut off n bytes from the end of *h. return a new
  *  block with the tail and change *h to refer to the
  *  head.
@@ -885,31 +814,6 @@
 }
 
 /*
- *  flow control, get producer going again
- *  called with q ilocked
- */
-static void
-qwakeup_iunlock(Queue *q)
-{
-	int dowakeup = 0;
-
-	/* if writer flow controlled, restart */
-	if((q->state & Qflow) && q->len < q->limit/2){
-		q->state &= ~Qflow;
-		dowakeup = 1;
-	}
-
-	iunlock(&q->lk);
-
-	/* wakeup flow controlled writers */
-	if(dowakeup){
-		if(q->kick != nil)
-			q->kick(q->arg);
-		wakeup(&q->wr);
-	}
-}
-
-/*
  *  get next block from a queue (up to a limit)
  */
 Block*
@@ -918,6 +822,8 @@
 	Block *b;
 	int n;
 
+	assert(len >= 0);
+
 	qlock(&q->rlock);
 	if(waserror()){
 		qunlock(&q->rlock);
@@ -950,10 +856,8 @@
 		else
 			b->wp -= n;
 	}
+	iunlock_reader(q);
 
-	/* restart producer */
-	qwakeup_iunlock(q);
-
 	qunlock(&q->rlock);
 	poperror();
 
@@ -970,6 +874,8 @@
 	Block *b, *first, **last;
 	int m, n;
 
+	assert(len >= 0);
+
 	qlock(&q->rlock);
 	if(waserror()){
 		qunlock(&q->rlock);
@@ -1001,8 +907,8 @@
 			freeb(qremove(q));
 			goto again;
 		}
-
-		/*  grab the first block plus as many
+		/*
+		 *  grab the first block plus as many
 		 *  following blocks as will partially
 		 *  fit in the read.
 		 */
@@ -1025,8 +931,7 @@
 	if(n > len && (q->state & Qmsg) == 0)
 		qputback(q, splitblock(last, n - len));
 
-	/* restart producer */
-	qwakeup_iunlock(q);
+	iunlock_reader(q);
 
 	qunlock(&q->rlock);
 	poperror();
@@ -1042,34 +947,39 @@
 	return n;
 }
 
+/*
+ *  a Flow represens a flow controlled
+ *  writer on queue q with position p.
+ */
+typedef struct {
+	Queue*	q;
+	uint	p;
+} Flow;
+
 static int
-qnotfull(void *a)
+unblocked(void *a)
 {
-	Queue *q = a;
+	Flow *f = a;
+	Queue *q = f->q;
 
-	return q->len < q->limit || (q->state & Qclosed);
+	return q->noblock || (int)(f->p - q->rp) <= q->limit || (q->state & Qclosed);
 }
 
 /*
- *  flow control, wait for queue to get below the limit
+ *  flow control, wait for queue to drain back to the limit
  */
 static void
-qflow(Queue *q)
+qflow(Flow *f)
 {
-	for(;;){
-		if(q->noblock || qnotfull(q))
-			break;
+	Queue *q = f->q;
 
-		ilock(&q->lk);
-		q->state |= Qflow;
-		iunlock(&q->lk);
-
+	while(!unblocked(f)){
 		qlock(&q->wlock);
 		if(waserror()){
 			qunlock(&q->wlock);
 			nexterror();
 		}
-		sleep(&q->wr, qnotfull, q);
+		sleep(&q->wr, unblocked, f);
 		qunlock(&q->wlock);
 		poperror();
 	}
@@ -1081,7 +991,8 @@
 long
 qbwrite(Queue *q, Block *b)
 {
-	int len, dowakeup;
+	Flow flow;
+	int len;
 
 	if(q->bypass != nil){
 		len = blocklen(b);
@@ -1089,7 +1000,6 @@
 		return len;
 	}
 
-	dowakeup = 0;
 	if(waserror()){
 		freeblist(b);
 		nexterror();
@@ -1101,9 +1011,11 @@
 		iunlock(&q->lk);
 		error(q->err);
 	}
-
-	/* don't queue over the limit */
-	if(q->len >= q->limit && q->noblock){
+	/*
+	 * if the queue is full,
+	 * silently discard when non-blocking
+	 */
+	if(q->state & Qflow && q->noblock){
 		iunlock(&q->lk);
 		poperror();
 		len = blocklen(b);
@@ -1110,33 +1022,25 @@
 		freeblist(b);
 		return len;
 	}
-
 	len = qaddlist(q, b);
-
-	/* make sure other end gets awakened */
-	if(q->state & Qstarve){
-		q->state &= ~Qstarve;
-		dowakeup = 1;
-	}
-	iunlock(&q->lk);
 	poperror();
 
-	/*  get output going again */
-	if(q->kick != nil && (dowakeup || (q->state&Qkick)))
-		q->kick(q->arg);
-
-	/* wakeup anyone consuming at the other end */
-	if(dowakeup)
-		wakeup(&q->rr);
-
 	/*
-	 *  flow control, before allowing the process to continue and
-	 *  queue more. We do this here so that postnote can only
-	 *  interrupt us after the data has been queued.  This means that
-	 *  things like 9p flushes and ssl messages will not be disrupted
-	 *  by software interrupts.
+	 * save our current position in queue
+	 * for flow control below.
 	 */
-	qflow(q);
+	flow.q = q;
+	flow.p = q->wp;
+	if(iunlock_writer(q) & Qflow){
+		/*
+		 *  flow control, before allowing the process to continue and
+		 *  queue more. We do this here so that postnote can only
+		 *  interrupt us after the data has been queued.  This means that
+		 *  things like 9p flushes and ssl messages will not be disrupted
+		 *  by software interrupts.
+		 */
+		qflow(&flow);
+	}
 
 	return len;
 }
@@ -1151,17 +1055,11 @@
 	Block *b;
 	uchar *p = vp;
 
+	assert(len >= 0);
+
 	QDEBUG if(!islo())
 		print("qwrite hi %#p\n", getcallerpc(&q));
 
-	/* stop queue bloat before allocating blocks */
-	if(q->len/2 >= q->limit && q->noblock == 0 && q->bypass == nil){
-		while(waserror())
-			;
-		qflow(q);
-		poperror();
-	}
-
 	sofar = 0;
 	do {
 		n = len-sofar;
@@ -1190,11 +1088,11 @@
 int
 qiwrite(Queue *q, void *vp, int len)
 {
-	int n, sofar, dowakeup;
+	int n, sofar;
 	Block *b;
 	uchar *p = vp;
 
-	dowakeup = 0;
+	assert(len >= 0);
 
 	sofar = 0;
 	do {
@@ -1209,46 +1107,72 @@
 		b->wp += n;
 
 		ilock(&q->lk);
-
-		/* we use an artificially high limit for kernel prints since anything
-		 * over the limit gets dropped
-		 */
-		if((q->state & Qclosed) != 0 || q->len/2 >= q->limit){
+		if(q->state & (Qflow|Qclosed)){
 			iunlock(&q->lk);
 			freeb(b);
 			break;
 		}
+		sofar += qaddlist(q, b);
+		iunlock_writer(q);
+	} while(sofar < len && (q->state & Qmsg) == 0);
 
-		qaddlist(q, b);
+	return sofar;
+}
 
-		if(q->state & Qstarve){
-			q->state &= ~Qstarve;
-			dowakeup = 1;
-		}
+/*
+ *  throw away the next 'len' bytes in the queue
+ */
+int
+qdiscard(Queue *q, int len)
+{
+	Block *b, *tofree = nil;
+	int n, sofar;
 
-		iunlock(&q->lk);
+	assert(len >= 0);
 
-		if(dowakeup){
-			if(q->kick != nil)
-				q->kick(q->arg);
-			wakeup(&q->rr);
+	ilock(&q->lk);
+	for(sofar = 0; sofar < len; sofar += n){
+		b = q->bfirst;
+		if(b == nil)
+			break;
+		QDEBUG checkb(b, "qdiscard");
+		n = BLEN(b);
+		if(n <= len - sofar){
+			q->bfirst = b->next;
+			q->rp += BALLOC(b);
+
+			/* remember to free this */
+			b->next = tofree;
+			tofree = b;
+		} else {
+			n = len - sofar;
+			b->rp += n;
 		}
+		q->dlen -= n;
+	}
+	iunlock_consumer(q);
 
-		sofar += n;
-	} while(sofar < len && (q->state & Qmsg) == 0);
+	freeblist(tofree);
 
 	return sofar;
 }
 
 /*
- *  be extremely careful when calling this,
- *  as there is no reference accounting
+ *  flush the output queue
  */
 void
-qfree(Queue *q)
+qflush(Queue *q)
 {
-	qclose(q);
-	free(q);
+	Block *tofree;
+
+	ilock(&q->lk);
+	tofree = q->bfirst;
+	q->bfirst = nil;
+	q->rp = q->wp;
+	q->dlen = 0;
+	iunlock_consumer(q);
+
+	freeblist(tofree);
 }
 
 /*
@@ -1258,32 +1182,42 @@
 void
 qclose(Queue *q)
 {
-	Block *bfirst;
+	Block *tofree;
 
 	if(q == nil)
 		return;
 
-	/* mark it */
 	ilock(&q->lk);
 	q->state |= Qclosed;
 	q->state &= ~(Qflow|Qstarve);
 	kstrcpy(q->err, Ehungup, ERRMAX);
-	bfirst = q->bfirst;
+	tofree = q->bfirst;
 	q->bfirst = nil;
-	q->len = 0;
+	q->rp = q->wp;
 	q->dlen = 0;
 	q->noblock = 0;
 	iunlock(&q->lk);
 
-	/* free queued blocks */
-	freeblist(bfirst);
-
 	/* wake up readers/writers */
 	wakeup(&q->rr);
 	wakeup(&q->wr);
+
+	/* free queued blocks */
+	freeblist(tofree);
 }
 
 /*
+ *  be extremely careful when calling this,
+ *  as there is no reference accounting
+ */
+void
+qfree(Queue *q)
+{
+	qclose(q);
+	free(q);
+}
+
+/*
  *  Mark a queue as closed.  Wakeup any readers.  Don't remove queued
  *  blocks.
  */
@@ -1290,7 +1224,6 @@
 void
 qhangup(Queue *q, char *msg)
 {
-	/* mark it */
 	ilock(&q->lk);
 	q->state |= Qclosed;
 	if(msg == nil || *msg == '\0')
@@ -1336,26 +1269,21 @@
 }
 
 /*
- * return space remaining before flow control
+ *  return true if we can read without blocking
  */
 int
-qwindow(Queue *q)
+qcanread(Queue *q)
 {
-	int l;
-
-	l = q->limit - q->len;
-	if(l < 0)
-		l = 0;
-	return l;
+	return q->bfirst != nil;
 }
 
 /*
- *  return true if we can read without blocking
+ *  return non-zero when the queue is full
  */
 int
-qcanread(Queue *q)
+qfull(Queue *q)
 {
-	return q->bfirst != nil;
+	return q->state & Qflow;
 }
 
 /*
@@ -1364,7 +1292,11 @@
 void
 qsetlimit(Queue *q, int limit)
 {
+	assert(limit >= 0);
+
+	ilock(&q->lk);
 	q->limit = limit;
+	iunlock_consumer(q);
 }
 
 /*
@@ -1373,34 +1305,7 @@
 void
 qnoblock(Queue *q, int onoff)
 {
-	q->noblock = onoff;
-}
-
-/*
- *  flush the output queue
- */
-void
-qflush(Queue *q)
-{
-	Block *bfirst;
-
-	/* mark it */
 	ilock(&q->lk);
-	bfirst = q->bfirst;
-	q->bfirst = nil;
-	q->len = 0;
-	q->dlen = 0;
-	iunlock(&q->lk);
-
-	/* free queued blocks */
-	freeblist(bfirst);
-
-	/* wake up writers */
-	wakeup(&q->wr);
-}
-
-int
-qfull(Queue *q)
-{
-	return q->state & Qflow;
+	q->noblock = onoff;
+	iunlock_consumer(q);
 }
--- a/kern/qlock.c
+++ b/kern/qlock.c
@@ -72,7 +72,7 @@
 
 	lock(&q->lk);
 	/* 
-	 * Can't assert this because of RWlock
+	 * Can't assert this because of RWLock
 	assert(q->hold == CT);
 	 */
 	p = dequeue((Proc**)&q->first, (Proc**)&q->last);
--- a/kern/rendez.c
+++ /dev/null
@@ -1,89 +1,0 @@
-#include	"u.h"
-#include	"lib.h"
-#include	"dat.h"
-#include	"fns.h"
-#include	"error.h"
-
-void
-sleep(Rendez *r, int (*f)(void*), void *arg)
-{
-	int s;
-
-	s = splhi();
-
-	lock(&r->lk);
-	lock(&up->rlock);
-	if(r->p){
-		print("double sleep %lud %lud\n", r->p->pid, up->pid);
-	}
-
-	/*
-	 *  Wakeup only knows there may be something to do by testing
-	 *  r->p in order to get something to lock on.
-	 *  Flush that information out to memory in case the sleep is
-	 *  committed.
-	 */
-	r->p = up;
-
-	if((*f)(arg) || up->notepending){
-		/*
-		 *  if condition happened or a note is pending
-		 *  never mind
-		 */
-		r->p = nil;
-		unlock(&up->rlock);
-		unlock(&r->lk);
-	} else {
-		/*
-		 *  now we are committed to
-		 *  change state and call scheduler
-		 */
-		up->state = Wakeme;
-		up->r = r;
-
-		/* statistics */
-		/* m->cs++; */
-
-		unlock(&up->rlock);
-		unlock(&r->lk);
-
-		procsleep();
-	}
-
-	if(up->notepending) {
-		up->notepending = 0;
-		splx(s);
-		error(Eintr);
-	}
-
-	splx(s);
-}
-
-Proc*
-wakeup(Rendez *r)
-{
-	Proc *p;
-	int s;
-
-	s = splhi();
-
-	lock(&r->lk);
-	p = r->p;
-
-	if(p != nil){
-		lock(&p->rlock);
-		if(p->state != Wakeme || p->r != r)
-			panic("wakeup: state");
-		r->p = nil;
-		p->r = nil;
-		p->state = Running;
-		procwakeup(p);
-		unlock(&p->rlock);
-	}
-	unlock(&r->lk);
-
-	splx(s);
-
-	return p;
-}
-
--- a/kern/rwlock.c
+++ b/kern/rwlock.c
@@ -5,7 +5,7 @@
 #include "error.h"
 
 void
-rlock(RWlock *l)
+rlock(RWLock *l)
 {
 	qlock(&l->x);		/* wait here for writers and exclusion */
 	lock(&l->lk);
@@ -16,7 +16,7 @@
 }
 
 void
-runlock(RWlock *l)
+runlock(RWLock *l)
 {
 	lock(&l->lk);
 	if(--l->readers == 0)	/* last reader out allows writers */
@@ -25,7 +25,7 @@
 }
 
 void
-wlock(RWlock *l)
+wlock(RWLock *l)
 {
 	qlock(&l->x);		/* wait here for writers and exclusion */
 	qlock(&l->k);		/* wait here for last reader */
@@ -32,7 +32,7 @@
 }
 
 void
-wunlock(RWlock *l)
+wunlock(RWLock *l)
 {
 	qunlock(&l->k);
 	qunlock(&l->x);
--- a/kern/sleep.c
+++ b/kern/sleep.c
@@ -70,20 +70,105 @@
 	lock(&r->lk);
 	p = r->p;
 
-	if(p != nil){
+	if(p == nil)
+		unlock(&r->lk);
+	else {
 		lock(&p->rlock);
 		if(p->state != Wakeme || p->r != r)
 			panic("wakeup: state");
+		p->state = Running;
 		r->p = nil;
 		p->r = nil;
-		p->state = Running;
-		procwakeup(p);
 		unlock(&p->rlock);
+		unlock(&r->lk);
+		procwakeup(p);
 	}
-	unlock(&r->lk);
-
 	splx(s);
 
 	return p;
 }
 
+/*
+ *  if waking a sleeping process, this routine must hold both
+ *  p->rlock and r->lock.  However, it can't know them in
+ *  the same order as wakeup causing a possible lock ordering
+ *  deadlock.  We break the deadlock by giving up the p->rlock
+ *  lock if we can't get the r->lock and retrying.
+ */
+void
+procinterrupt(Proc *p)
+{
+	int s;
+
+	p->notepending = 1;
+
+	/* this loop is to avoid lock ordering problems. */
+	for(;;){
+		Rendez *r;
+
+		s = splhi();
+		lock(&p->rlock);
+		r = p->r;
+
+		/* waiting for a wakeup? */
+		if(r == nil)
+			break;	/* no */
+
+		/* try for the second lock */
+		if(canlock(&r->lk)){
+			if(p->state != Wakeme || r->p != p)
+				panic("procinterrupt: state %d %d %d",
+					r->p != p, p->r != r, p->state);
+			p->r = nil;
+			r->p = nil;
+			unlock(&p->rlock);
+			unlock(&r->lk);
+			/* hands off r */
+			procwakeup(p);
+			splx(s);
+			return;
+		}
+
+		/* give other process time to get out of critical section and try again */
+		unlock(&p->rlock);
+		splx(s);
+
+		osyield();
+	}
+	unlock(&p->rlock);
+	splx(s);
+
+	switch(p->state){
+	case Rendezvous:
+		/* Try and pull out of a rendezvous */
+		lock(&p->rgrp->ref.lk);
+		if(p->state == Rendezvous) {
+			Proc *d, **l;
+
+			l = &REND(p->rgrp, (uintptr)p->rendtag);
+			for(d = *l; d != nil; d = d->rendhash) {
+				if(d == p) {
+					*l = p->rendhash;
+					p->rendval = (void*)~(uintptr)0;
+					unlock(&p->rgrp->ref.lk);
+					/* hands off p->rgrp */
+					procwakeup(p);
+					return;
+				}
+				l = &d->rendhash;
+			}
+		}
+		unlock(&p->rgrp->ref.lk);
+		break;
+	}
+}
+
+int
+postnote(Proc *p, int x, char *msg, int flag)
+{
+	USED(x);
+	USED(msg);
+	USED(flag);
+	procinterrupt(p);
+	return 0;
+}
--- a/kern/stub.c
+++ b/kern/stub.c
@@ -56,17 +56,6 @@
 	USED(tag);
 }
 
-/* TODO: We need this very soon */
-int
-postnote(Proc *p, int x, char *msg, int flag)
-{
-	USED(p);
-	USED(x);
-	USED(msg);
-	USED(flag);
-	return 0;
-}
-
 void
 exhausted(char *s)
 {
--- a/kern/sysfile.c
+++ b/kern/sysfile.c
@@ -18,8 +18,6 @@
 #undef fstat
 #undef fwstat
 #undef iounit
-#undef validaddr
-#define validaddr(a, b, c)
 
 /*
  * The sys*() routines needn't poperror() as they return directly to syscall().
@@ -205,18 +203,16 @@
 _syspipe(int fd[2])
 {
 	Chan *c[2];
-	Dev *d;
 	static char *datastr[] = {"data", "data1"};
 
-	d = devtab[devno('|', 0)];
 	c[0] = namec("#|", Atodir, 0, 0);
-	c[1] = 0;
+	c[1] = nil;
 	fd[0] = -1;
 	fd[1] = -1;
 
 	if(waserror()){
 		cclose(c[0]);
-		if(c[1])
+		if(c[1] != nil)
 			cclose(c[1]);
 		nexterror();
 	}
@@ -225,8 +221,8 @@
 		error(Egreg);
 	if(walk(&c[1], datastr+1, 1, 1, nil) < 0)
 		error(Egreg);
-	c[0] = d->open(c[0], ORDWR);
-	c[1] = d->open(c[1], ORDWR);
+	c[0] = devtab[c[0]->type]->open(c[0], ORDWR);
+	c[1] = devtab[c[1]->type]->open(c[1], ORDWR);
 	if(newfd2(fd, c) < 0)
 		error(Enofd);
 	poperror();
@@ -295,8 +291,8 @@
 	return fd;
 }
 
-void
-fdclose(int fd, int flag)
+static void
+_fdclose(int fd, int flag)
 {
 	int i;
 	Chan *c;
@@ -328,7 +324,7 @@
 _sysclose(int fd)
 {
 	fdtochan(fd, -1, 0, 0);
-	fdclose(fd, 0);
+	_fdclose(fd, 0);
 
 	return 0;
 }
@@ -591,7 +587,7 @@
 	uint l;
 
 	l = n;
-	validaddr((uintptr)buf, l, 1);
+	validaddr(buf, l, 1);
 	c = fdtochan(fd, -1, 0, 1);
 	if(waserror()) {
 		cclose(c);
@@ -610,8 +606,8 @@
 	uint l;
 
 	l = n;
-	validaddr((uintptr)buf, l, 1);
-	validaddr((uintptr)name, 1, 0);
+	validaddr(buf, l, 1);
+	validaddr(name, 1, 0);
 	c = namec(name, Aaccess, 0, 0);
 	if(waserror()){
 		cclose(c);
@@ -628,7 +624,7 @@
 {
 	Chan *c;
 
-	validaddr((uintptr)name, 1, 0);
+	validaddr(name, 1, 0);
 
 	c = namec(name, Atodir, 0, 0);
 	cclose(up->dot);
@@ -698,7 +694,7 @@
 	poperror();
 	cclose(c0);
 	if(ismount){
-		fdclose(fd, 0);
+		_fdclose(fd, 0);
 		poperror();
 		free(spec);
 	}
@@ -722,7 +718,7 @@
 {
 	Chan *cmount, *cmounted;
 
-	cmounted = 0;
+	cmounted = nil;
 
 	cmount = namec(new, Amount, 0, 0);
 
@@ -731,14 +727,8 @@
 			cclose(cmount);
 			nexterror();
 		}
-		validaddr((uintptr)old, 1, 0);
-		/*
-		 * This has to be namec(..., Aopen, ...) because
-		 * if arg[0] is something like /srv/cs or /fd/0,
-		 * opening it is the only way to get at the real
-		 * Chan underneath.
-		 */
-		cmounted = namec(old, Aopen, OREAD, 0);
+		validaddr(old, 1, 0);
+		cmounted = namec(old, Aunmount, OREAD, 0);
 		poperror();
 	}
 
@@ -769,7 +759,7 @@
 			cclose(c);
 		nexterror();
 	}
-	validaddr((uintptr)name, 1, 0);
+	validaddr(name, 1, 0);
 	c = namec(name, Acreate, mode, perm);
 	fd = newfd(c);
 	if(fd < 0)
@@ -808,7 +798,7 @@
 
 	l = n;
 	validstat(buf, l);
-	validaddr((uintptr)name, 1, 0);
+	validaddr(name, 1, 0);
 	c = namec(name, Aaccess, 0, 0);
 	if(waserror()){
 		cclose(c);
@@ -827,7 +817,7 @@
 	uint l;
 
 	l = n;
-	validaddr((uintptr)buf, l, 0);
+	validaddr(buf, l, 0);
 	validstat(buf, l);
 	c = fdtochan(fd, -1, 1, 1);
 	if(waserror()) {
@@ -851,6 +841,8 @@
 {
 	assert(up->nerrlab == 1);
 	poperror();
+
+	up->notepending = 0;	/* theres no popnote */
 }
 
 static void
@@ -1253,4 +1245,17 @@
 sysgetpid(void)
 {
 	return up->pid;
+}
+
+void*
+sysgetkproc(void)
+{
+	return (void*)up;
+}
+
+void
+syskprocint(void *p)
+{
+	if(p != nil)
+		procinterrupt((Proc*)p);
 }
--- a/kern/sysproc.c
+++ b/kern/sysproc.c
@@ -34,7 +34,7 @@
 	w = sizeof mask[0] * 8;
 	for(p = devs; *p != '\0';){
 		p += chartorune(&r, p);
-		t = devno(r, 1);
+		t = devno(r);
 		if(t == -1)
 			continue;
 		if(invert)
--- a/libc/Makefile
+++ b/libc/Makefile
@@ -11,6 +11,7 @@
 	convM2S.$O\
 	convS2M.$O\
 	ctime.$O\
+	crypt.$O\
 	dial.$O\
 	dirfstat.$O\
 	dirfwstat.$O\
@@ -40,6 +41,8 @@
 	netmkaddr.$O\
 	nsec.$O\
 	pow10.$O\
+	pushssl.$O\
+	pushtls.$O\
 	read9pmsg.$O\
 	readn.$O\
 	rune.$O\
--- /dev/null
+++ b/libc/crypt.c
@@ -1,0 +1,67 @@
+/*
+ *	Data Encryption Standard
+ *	D.P.Mitchell  83/06/08.
+ *
+ *	block_cipher(key, block, decrypting)
+ *
+ *	these routines use the non-standard 7 byte format
+ *	for DES keys.
+ */
+#include <u.h>
+#include <libc.h>
+#include <libsec.h>
+
+/*
+ * destructively encrypt the buffer, which
+ * must be at least 8 characters long.
+ */
+int
+encrypt(void *key, void *vbuf, int n)
+{
+	ulong ekey[32];
+	uchar *buf;
+	int i, r;
+
+	if(n < 8)
+		return 0;
+	key_setup(key, ekey);
+	buf = vbuf;
+	n--;
+	r = n % 7;
+	n /= 7;
+	for(i = 0; i < n; i++){
+		block_cipher(ekey, buf, 0);
+		buf += 7;
+	}
+	if(r)
+		block_cipher(ekey, buf - 7 + r, 0);
+	return 1;
+}
+
+/*
+ * destructively decrypt the buffer, which
+ * must be at least 8 characters long.
+ */
+int
+decrypt(void *key, void *vbuf, int n)
+{
+	ulong ekey[128];
+	uchar *buf;
+	int i, r;
+
+	if(n < 8)
+		return 0;
+	key_setup(key, ekey);
+	buf = vbuf;
+	n--;
+	r = n % 7;
+	n /= 7;
+	buf += n * 7;
+	if(r)
+		block_cipher(ekey, buf - 7 + r, 1);
+	for(i = 0; i < n; i++){
+		buf -= 7;
+		block_cipher(ekey, buf, 1);
+	}
+	return 1;
+}
--- a/libc/fmt.c
+++ b/libc/fmt.c
@@ -64,9 +64,6 @@
 	0,	0,
 };
 
-
-int	(*fmtdoquote)(int);
-
 /*
  * __fmtlock() must be set
  */
--- a/libc/fmtquote.c
+++ b/libc/fmtquote.c
@@ -2,6 +2,16 @@
 #include <libc.h>
 #include "fmtdef.h"
 
+static int
+fmtdoquote(int c)
+{
+	if(c <= ' ')
+		return 1;
+	if(utfrune("`^#*[]=|\\?${}()'<>&;", c))
+		return 1;
+	return 0;
+}
+
 /*
  * How many bytes of output UTF will be produced by quoting (if necessary) this string?
  * How many runes? How much of the input will be consumed?
@@ -51,7 +61,7 @@
 				break;
 		}
 
-		if((c <= L' ') || (c == L'\'') || (fmtdoquote!=0 && fmtdoquote(c))){
+		if((c <= L' ') || (c == L'\'') || fmtdoquote(c)){
 			if(!q->quoted){
 				if(runesout){
 					if(1+q->nrunesout+1+1 > nout)	/* no room for quotes */
--- /dev/null
+++ b/libc/pushssl.c
@@ -1,0 +1,44 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * Since the SSL device uses decimal file descriptors to name channels,
+ * it is impossible for a user-level file server to stand in for the kernel device.
+ * Thus we hard-code #D rather than use /net/ssl.
+ */
+
+int
+pushssl(int fd, char *alg, char *secin, char *secout, int *cfd)
+{
+	char buf[8];
+	char dname[64];
+	int n, data, ctl;
+
+	ctl = open("#D/ssl/clone", ORDWR);
+	if(ctl < 0)
+		return -1;
+	n = read(ctl, buf, sizeof(buf)-1);
+	if(n < 0)
+		goto error;
+	buf[n] = 0;
+	sprint(dname, "#D/ssl/%s/data", buf);
+	data = open(dname, ORDWR);
+	if(data < 0)
+		goto error;
+	if(fprint(ctl, "fd %d", fd) < 0 ||
+	   fprint(ctl, "secretin %s", secin) < 0 ||
+	   fprint(ctl, "secretout %s", secout) < 0 ||
+	   fprint(ctl, "alg %s", alg) < 0){
+		close(data);
+		goto error;
+	}
+	close(fd);
+	if(cfd != 0)
+		*cfd = ctl;
+	else
+		close(ctl);
+	return data;
+error:
+	close(ctl);
+	return -1;
+}
--- /dev/null
+++ b/libc/pushtls.c
@@ -1,0 +1,99 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <mp.h>
+#include <libsec.h>
+
+enum {
+	TLSFinishedLen = 12,
+	HFinished = 20,
+};
+
+static int
+finished(int hand, int isclient)
+{
+	int i, n;
+	uchar buf[500], buf2[500];
+
+	buf[0] = HFinished;
+	buf[1] = TLSFinishedLen>>16;
+	buf[2] = TLSFinishedLen>>8;
+	buf[3] = TLSFinishedLen;
+	n = TLSFinishedLen+4;
+
+	for(i=0; i<2; i++){
+		if(i==0)
+			memmove(buf+4, "client finished", TLSFinishedLen);
+		else
+			memmove(buf+4, "server finished", TLSFinishedLen);
+		if(isclient == 1-i){
+			if(write(hand, buf, n) != n)
+				return -1;
+		}else{
+			if(readn(hand, buf2, n) != n || memcmp(buf,buf2,n) != 0)
+				return -1;
+		}
+	}
+	return 1;
+}
+
+
+// given a plain fd and secrets established beforehand, return encrypted connection
+int
+pushtls(int fd, char *hashalg, char *encalg, int isclient, char *secret, char *dir)
+{
+	char buf[8];
+	char dname[64];
+	int n, data, ctl, hand;
+
+	// open a new filter; get ctl fd
+	data = hand = -1;
+	// /net/tls uses decimal file descriptors to name channels, hence a
+	// user-level file server can't stand in for #a; may as well hard-code it.
+	ctl = open("#a/tls/clone", ORDWR);
+	if(ctl < 0)
+		goto error;
+	n = read(ctl, buf, sizeof(buf)-1);
+	if(n < 0)
+		goto error;
+	buf[n] = 0;
+	if(dir)
+		sprint(dir, "#a/tls/%s", buf);
+
+	// get application fd
+	sprint(dname, "#a/tls/%s/data", buf);
+	data = open(dname, ORDWR);
+	if(data < 0)
+		goto error;
+
+	// get handshake fd
+	sprint(dname, "#a/tls/%s/hand", buf);
+	hand = open(dname, ORDWR);
+	if(hand < 0)
+		goto error;
+
+	// speak a minimal handshake
+	if(fprint(ctl, "fd %d 0x301", fd) < 0 ||
+	   fprint(ctl, "version 0x301") < 0 ||
+	   fprint(ctl, "secret %s %s %d %s", hashalg, encalg, isclient, secret) < 0 ||
+	   fprint(ctl, "changecipher") < 0 ||
+	   finished(hand, isclient) < 0 ||
+	   fprint(ctl, "opened") < 0){
+		close(hand);
+		hand = -1;
+		goto error;
+	}
+	close(ctl);
+	close(hand);
+	close(fd);
+	return data;
+
+error:
+	if(data>=0)
+		close(data);
+	if(ctl>=0)
+		close(ctl);
+	if(hand>=0)
+		close(hand);
+	return -1;
+}
--- a/libdraw/Makefile
+++ b/libdraw/Makefile
@@ -14,7 +14,9 @@
 	icossin.$O\
 	icossin2.$O\
 	rectclip.$O\
-	rgb.$O
+	rgb.$O\
+	warp.$O\
+	mkwarp.$O
 
 default: $(LIB)
 $(LIB): $(OFILES)
--- a/libdraw/alloc.c
+++ b/libdraw/alloc.c
@@ -2,10 +2,10 @@
 #include <libc.h>
 #include <draw.h>
 
-Img*
+Image*
 allocimage(Display *d, Rectangle r, ulong chan, int repl, ulong col)
 {
-	Img *i;
+	Image *i;
 
 	i = _allocimage(nil, d, r, chan, repl, col, 0, 0);
 	if(i != nil)
@@ -13,12 +13,12 @@
 	return i;
 }
 
-Img*
-_allocimage(Img *ai, Display *d, Rectangle r, ulong chan, int repl, ulong col, int screenid, int refresh)
+Image*
+_allocimage(Image *ai, Display *d, Rectangle r, ulong chan, int repl, ulong col, int screenid, int refresh)
 {
 	uchar *a;
 	char *err;
-	Img *i;
+	Image *i;
 	Rectangle clipr;
 	int id;
 	int depth;
@@ -80,7 +80,7 @@
 	if(ai != nil)
 		i = ai;
 	else{
-		i = malloc(sizeof(Img));
+		i = malloc(sizeof(Image));
 		if(i == nil){
 			a = bufimage(d, 1+4);
 			if(a != nil){
@@ -103,12 +103,12 @@
 	return i;
 }
 
-Img*
+Image*
 namedimage(Display *d, char *name)
 {
 	uchar *a;
 	char *err, buf[12*12+1];
-	Img *i;
+	Image *i;
 	int id, n;
 	ulong chan;
 
@@ -144,7 +144,7 @@
 		goto Error;
 	buf[12*12] = '\0';
 
-	i = malloc(sizeof(Img));
+	i = malloc(sizeof(Image));
 	if(i == nil){
 	Error1:
 		a = bufimage(d, 1+4);
@@ -178,7 +178,7 @@
 }
 
 int
-nameimage(Img *i, char *name, int in)
+nameimage(Image *i, char *name, int in)
 {
 	uchar *a;
 	int n;
@@ -198,11 +198,11 @@
 }
 
 int
-_freeimage1(Img *i)
+_freeimage1(Image *i)
 {
 	uchar *a;
 	Display *d;
-	Img *w;
+	Image *w;
 
 	if(i == nil || i->display == nil)
 		return 0;
@@ -233,7 +233,7 @@
 }
 
 int
-freeimage(Img *i)
+freeimage(Image *i)
 {
 	int ret;
 
--- a/libdraw/bytesperline.c
+++ b/libdraw/bytesperline.c
@@ -6,19 +6,9 @@
 int
 unitsperline(Rectangle r, int d, int bitsperunit)
 {
-	ulong l, t;
-
 	if(d <= 0 || d > 32)	/* being called wrong.  d is image depth. */
 		abort();
-
-	if(r.min.x >= 0){
-		l = (r.max.x*d+bitsperunit-1)/bitsperunit;
-		l -= (r.min.x*d)/bitsperunit;
-	}else{			/* make positive before divide */
-		t = (-r.min.x*d+bitsperunit-1)/bitsperunit;
-		l = t+(r.max.x*d+bitsperunit-1)/bitsperunit;
-	}
-	return l;
+	return (r.max.x*d - (r.min.x*d & -bitsperunit) + bitsperunit - 1) / bitsperunit;
 }
 
 int
--- a/libdraw/icossin2.c
+++ b/libdraw/icossin2.c
@@ -227,6 +227,7 @@
 {
 	int sinsign, cossign, tan, tan10, rem;
 	short *stp, *ctp;
+	uint ux, uy;
 
 	if(x == 0){
 		if(y >= 0)
@@ -238,19 +239,23 @@
 	sinsign = cossign = 1;
 	if(x < 0){
 		cossign = -1;
-		x = -x;
+		ux = -x;
+	} else {
+		ux = x;
 	}
 	if(y < 0){
 		sinsign = -1;
-		y = -y;
+		uy = -y;
+	} else {
+		uy = y;
 	}
-	if(y > x){
-		tan = 1000*x/y;
+	if(uy > ux){
+		tan = 1000*(uvlong)ux/uy;
 		tan10 = tan/10;
 		stp = &cosinus[tan10];
 		ctp = &sinus[tan10];
 	}else{
-		tan = 1000*y/x;
+		tan = 1000*(uvlong)uy/ux;
 		tan10 = tan/10;
 		stp = &sinus[tan10];
 		ctp = &cosinus[tan10];
--- /dev/null
+++ b/libdraw/mkwarp.c
@@ -1,0 +1,24 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+typedef double Matrix[3][3];
+
+void invm(Matrix);
+
+/* 25.7 fixed-point number operations */
+
+#define flt2fix(n)	((long)((n)*(1<<7) + ((n) < 0? -0.5: 0.5)))
+
+void
+mkwarp(Warp w, double m0[3][3])
+{
+	Matrix m;
+
+	memmove(m, m0, sizeof(Matrix));
+	invm(m);
+
+	w[0][0] = flt2fix(m[0][0]); w[0][1] = flt2fix(m[0][1]); w[0][2] = flt2fix(m[0][2]);
+	w[1][0] = flt2fix(m[1][0]); w[1][1] = flt2fix(m[1][1]); w[1][2] = flt2fix(m[1][2]);
+	w[2][0] = 0; w[2][1] = 0; w[2][2] = 1<<7;
+}
--- /dev/null
+++ b/libdraw/warp.c
@@ -1,0 +1,37 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+static void
+putwarp(uchar *a, Warp w)
+{
+	BPLONG(a+0*3*4+0*4, w[0][0]); BPLONG(a+0*3*4+1*4, w[0][1]); BPLONG(a+0*3*4+2*4, w[0][2]);
+	BPLONG(a+1*3*4+0*4, w[1][0]); BPLONG(a+1*3*4+1*4, w[1][1]); BPLONG(a+1*3*4+2*4, w[1][2]);
+	BPLONG(a+2*3*4+0*4, w[2][0]); BPLONG(a+2*3*4+1*4, w[2][1]); BPLONG(a+2*3*4+2*4, w[2][2]);
+}
+
+void
+affinewarp(Image *dst, Rectangle r, Image *src, Point p, Warp w, int smooth)
+{
+	uchar *a;
+
+	if(dst == nil || src == nil)
+		return;
+
+	a = bufimage(dst->display, 1+4+4*4+4+2*4+3*3*4+1);
+	if(a == nil){
+		fprint(2, "affinewarp: %r\n");
+		return;
+	}
+	a[0] = 'a';
+	BPLONG(a+1, dst->id);
+	BPLONG(a+5, r.min.x);
+	BPLONG(a+9, r.min.y);
+	BPLONG(a+13, r.max.x);
+	BPLONG(a+17, r.max.y);
+	BPLONG(a+21, src->id);
+	BPLONG(a+25, p.x);
+	BPLONG(a+29, p.y);
+	putwarp(a+33, w);
+	a[33+3*3*4] = smooth;
+}
--- a/libmemdraw/Makefile
+++ b/libmemdraw/Makefile
@@ -21,7 +21,8 @@
 	string.$O\
 	subfont.$O\
 	unload.$O\
-	write.$O
+	write.$O\
+	warp.$O
 
 default: $(LIB)
 $(LIB): $(OFILES)
--- a/libmemdraw/alloc.c
+++ b/libmemdraw/alloc.c
@@ -26,7 +26,6 @@
 allocmemimaged(Rectangle r, ulong chan, Memdata *md)
 {
 	int d;
-	ulong l;
 	Memimage *i;
 
 	if((d = chantodepth(chan)) == 0) {
@@ -38,21 +37,15 @@
 		return nil;
 	}
 
-	l = wordsperline(r, d);
-
 	i = mallocz(sizeof(Memimage), 1);
 	if(i == nil)
 		return nil;
 
 	i->data = md;
-	i->zero = sizeof(ulong)*l*r.min.y;
-	
-	if(r.min.x >= 0)
-		i->zero += (r.min.x*d)/8;
-	else
-		i->zero -= (-r.min.x*d+7)/8;
+	i->width = wordsperline(r, d);
+	i->zero = r.min.y*(int)(sizeof(ulong)*i->width) + ((r.min.x*d) >> 3);
 	i->zero = -i->zero;
-	i->width = l;
+
 	i->r = r;
 	i->clipr = r;
 	i->flags = 0;
@@ -69,8 +62,8 @@
 allocmemimage(Rectangle r, ulong chan)
 {
 	int d;
+	ulong nw;
 	uchar *p;
-	ulong l, nw;
 	Memdata *md;
 	Memimage *i;
 
@@ -82,10 +75,8 @@
 		werrstr("bad rectangle %R", r);
 		return nil;
 	}
+	nw = wordsperline(r, d)*Dy(r);
 
-	l = wordsperline(r, d);
-
-	nw = l*Dy(r);
 	md = malloc(sizeof(Memdata));
 	if(md == nil)
 		return nil;
@@ -143,23 +134,8 @@
 uchar*
 byteaddr(Memimage *i, Point p)
 {
-	uchar *a;
-
-	a = i->data->bdata+i->zero+(int)(sizeof(ulong)*p.y*i->width);
-	if(i->depth < 8){
-		/*
-		 * We need to always round down,
-		 * but C rounds toward zero.
-		 */
-		int np;
-		np = 8/i->depth;
-		if(p.x < 0)
-			return a+(p.x-np+1)/np;
-		else
-			return a+p.x/np;
-	}
-	else
-		return a+p.x*(i->depth/8);
+	uchar *a = i->data->bdata+i->zero;
+	return a + p.y*(int)(sizeof(ulong)*i->width) + ((p.x*i->depth) >> 3);
 }
 
 int
--- /dev/null
+++ b/libmemdraw/warp.c
@@ -1,0 +1,849 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+
+/* perfect approximation to NTSC = .299r+.587g+.114b when 0 ≤ r,g,b < 256 */
+#define RGB2K(r,g,b)	((156763*(r)+307758*(g)+59769*(b))>>19)
+
+/* 25.7 fixed-point number operations */
+
+#define FMASK		((1<<7) - 1)
+#define flt2fix(n)	((long)((n)*(1<<7) + ((n) < 0? -0.5: 0.5)))
+#define fix2flt(n)	((n)/128.0)
+#define int2fix(n)	((vlong)(n)<<7)
+#define fix2int(n)	((n)>>7)
+#define fixmul(a,b)	((vlong)(a)*(vlong)(b) >> 7)
+#define fixdiv(a,b)	(((vlong)(a) << 7)/(vlong)(b))
+#define fixfrac(n)	((n)&FMASK)
+#define lerp(a,b,t)	((a) + ((((b) - (a))*(t))>>7))
+
+#define clamp(a,b,c)	((a)<(b)?(b):((a)>(c)?(c):(a)))
+
+typedef struct Sampler Sampler;
+typedef struct Blitter Blitter;
+
+struct Sampler
+{
+	Memimage *i;
+	uchar *a;
+	Rectangle r;
+	int bpl;
+	int cmask;
+	long Δx, Δy;
+	Memimage *k;			/* filtering kernel */
+	Point kcp;			/* kernel center point */
+	ulong (*fn)(Sampler*, Point);
+};
+
+struct Blitter
+{
+	Memimage *i;
+	uchar *a;
+	int bpl;
+	int cmask;
+	void (*fn)(Blitter*, Point, ulong);
+};
+
+static void *getsampfn(ulong);
+static void *getblitfn(ulong);
+
+static void
+initsampler(Sampler *s, Memimage *i)
+{
+	s->i = i;
+	s->a = i->data->bdata + i->zero;
+	s->bpl = sizeof(ulong)*i->width;
+	s->cmask = (1ULL << i->depth) - 1;
+	s->fn = getsampfn(i->chan);
+}
+
+static void
+initblitter(Blitter *b, Memimage *i)
+{
+	b->i = i;
+	b->a = i->data->bdata + i->zero;
+	b->bpl = sizeof(ulong)*i->width;
+	b->cmask = (1ULL << i->depth) - 1;
+	b->fn = getblitfn(i->chan);
+}
+
+static Point
+xform(Point p, Warp m)
+{
+	return (Point){
+		fixmul(p.x, m[0][0]) + fixmul(p.y, m[0][1]) + m[0][2],
+		fixmul(p.x, m[1][0]) + fixmul(p.y, m[1][1]) + m[1][2]
+	};
+}
+
+static ulong
+getpixel_k1(Sampler *s, Point pt)
+{
+	uchar *p;
+	ulong off, npack, v;
+
+	p = s->a + pt.y*s->bpl + (pt.x >> 3);
+	npack = 8;
+	off = pt.x % npack;
+	v = p[0] >> (npack-1-off) & 0x1;
+	return v*0xFFFFFF00 | 0xFF;
+}
+
+static ulong
+getpixel_k2(Sampler *s, Point pt)
+{
+	uchar *p, v;
+	ulong off, npack;
+
+	p = s->a + pt.y*s->bpl + (pt.x*2 >> 3);
+	npack = 8/2;
+	off = pt.x % npack;
+	v = p[0] >> 2*(npack-1-off) & 0x3;
+	return v*0x55555500 | 0xFF;
+}
+
+static ulong
+getpixel_k4(Sampler *s, Point pt)
+{
+	uchar *p, v;
+	ulong off, npack;
+
+	p = s->a + pt.y*s->bpl + (pt.x*4 >> 3);
+	npack = 8/4;
+	off = pt.x % npack;
+	v = p[0] >> 4*(npack-1-off) & 0xF;
+	return v*0x11111100 | 0xFF;
+}
+
+static ulong
+getpixel_k8(Sampler *s, Point pt)
+{
+	uchar *p;
+
+	p = s->a + pt.y*s->bpl + pt.x;
+	return p[0]*0x01010100 | 0xFF;
+}
+
+static ulong
+getpixel_m8(Sampler *s, Point pt)
+{
+	uchar *p, m;
+
+	p = s->a + pt.y*s->bpl + pt.x;
+	m = p[0];
+	p = s->i->cmap->cmap2rgb+3*m;
+	return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|0xFF;
+}
+
+static ulong
+getpixel_x1r5g5b5(Sampler *s, Point pt)
+{
+	uchar *p, r, g, b;
+	ulong val;
+
+	p = s->a + pt.y*s->bpl + pt.x*2;
+	val = p[0]|(p[1]<<8);
+	b = val&0x1F; b = (b<<3)|(b>>2);
+	val >>= 5;
+	g = val&0x1F; g = (g<<3)|(g>>2);
+	val >>= 5;
+	r = val&0x1F; r = (r<<3)|(r>>2);
+	return (r<<24)|(g<<16)|(b<<8)|0xFF;
+}
+
+static ulong
+getpixel_r5g6b5(Sampler *s, Point pt)
+{
+	uchar *p, r, g, b;
+	ulong val;
+
+	p = s->a + pt.y*s->bpl + pt.x*2;
+	val = p[0]|(p[1]<<8);
+	b = val&0x1F; b = (b<<3)|(b>>2);
+	val >>= 5;
+	g = val&0x3F; g = (g<<2)|(g>>4);
+	val >>= 6;
+	r = val&0x1F; r = (r<<3)|(r>>2);
+	return (r<<24)|(g<<16)|(b<<8)|0xFF;
+}
+
+static ulong
+getpixel_r8g8b8(Sampler *s, Point pt)
+{
+	uchar *p;
+
+	p = s->a + pt.y*s->bpl + pt.x*3;
+	return (p[2]<<24)|(p[1]<<16)|(p[0]<<8)|0xFF;
+}
+
+static ulong
+getpixel_r8g8b8a8(Sampler *s, Point pt)
+{
+	uchar *p;
+
+	p = s->a + pt.y*s->bpl + pt.x*4;
+	return (p[3]<<24)|(p[2]<<16)|(p[1]<<8)|p[0];
+}
+
+static ulong
+getpixel_a8r8g8b8(Sampler *s, Point pt)
+{
+	uchar *p;
+
+	p = s->a + pt.y*s->bpl + pt.x*4;
+	return (p[2]<<24)|(p[1]<<16)|(p[0]<<8)|p[3];
+}
+
+static ulong
+getpixel_x8r8g8b8(Sampler *s, Point pt)
+{
+	uchar *p;
+
+	p = s->a + pt.y*s->bpl + pt.x*4;
+	return (p[2]<<24)|(p[1]<<16)|(p[0]<<8)|0xFF;
+}
+
+static ulong
+getpixel_b8g8r8(Sampler *s, Point pt)
+{
+	uchar *p;
+
+	p = s->a + pt.y*s->bpl + pt.x*3;
+	return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|0xFF;
+}
+
+static ulong
+getpixel_a8b8g8r8(Sampler *s, Point pt)
+{
+	uchar *p;
+
+	p = s->a + pt.y*s->bpl + pt.x*4;
+	return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
+}
+
+static ulong
+getpixel_x8b8g8r8(Sampler *s, Point pt)
+{
+	uchar *p;
+
+	p = s->a + pt.y*s->bpl + pt.x*4;
+	return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|0xFF;
+}
+
+static ulong
+getpixel(Sampler *s, Point pt)
+{
+	uchar *p, r, g, b, a;
+	ulong val, chan, ctype, ov, v;
+	int nb, off, bpp, npack;
+
+	val = 0;
+	a = 0xFF;
+	r = g = b = 0xAA;	/* garbage */
+	p = s->a + pt.y*s->bpl + (pt.x*s->i->depth >> 3);
+
+	/* pixelbits() */
+	switch(bpp = s->i->depth){
+	case 1:
+	case 2:
+	case 4:
+		npack = 8/bpp;
+		off = pt.x%npack;
+		val = p[0] >> bpp*(npack-1-off);
+		val &= s->cmask;
+		break;
+	case 8:
+		val = p[0];
+		break;
+	case 16:
+		val = p[0]|(p[1]<<8);
+		break;
+	case 24:
+		val = p[0]|(p[1]<<8)|(p[2]<<16);
+		break;
+	case 32:
+		val = p[0]|(p[1]<<8)|(p[2]<<16)|(p[3]<<24);
+		break;
+	}
+
+	while(bpp < 32){
+		val |= val<<bpp;
+		bpp *= 2;
+	}
+
+	/* imgtorgba() */
+	for(chan = s->i->chan; chan; chan >>= 8){
+		if((ctype = TYPE(chan)) == CIgnore){
+			val >>= s->i->nbits[ctype];
+			continue;
+		}
+		nb = s->i->nbits[ctype];
+		ov = v = val & s->i->mask[ctype];
+		val >>= nb;
+
+		while(nb < 8){
+			v |= v<<nb;
+			nb *= 2;
+		}
+		v >>= nb-8;
+
+		switch(ctype){
+		case CRed:
+			r = v;
+			break;
+		case CGreen:
+			g = v;
+			break;
+		case CBlue:
+			b = v;
+			break;
+		case CAlpha:
+			a = v;
+			break;
+		case CGrey:
+			r = g = b = v;
+			break;
+		case CMap:
+			p = s->i->cmap->cmap2rgb+3*ov;
+			r = p[0];
+			g = p[1];
+			b = p[2];
+			break;
+		}
+	}
+	return (r<<24)|(g<<16)|(b<<8)|a;
+}
+
+static void
+putpixel_k1(Blitter *blt, Point dp, ulong rgba)
+{
+	uchar *p, r, g, b, m;
+	int off, npack, sh, mask;
+
+	p = blt->a + dp.y*blt->bpl + (dp.x >> 3);
+
+	r = rgba>>24;
+	g = rgba>>16;
+	b = rgba>>8;
+	m = RGB2K(r,g,b);
+	m >>= 8-1;
+
+	mask = 0x1;
+	npack = 8;
+	off = dp.x%npack;
+	sh = npack-1-off;
+	mask <<= sh;
+	m <<= sh;
+	p[0] = (p[0] ^ m) & mask ^ p[0];
+}
+
+static void
+putpixel_k2(Blitter *blt, Point dp, ulong rgba)
+{
+	uchar *p, r, g, b, m;
+	int off, npack, sh, mask;
+
+	p = blt->a + dp.y*blt->bpl + (dp.x*2 >> 3);
+
+	r = rgba>>24;
+	g = rgba>>16;
+	b = rgba>>8;
+	m = RGB2K(r,g,b);
+	m >>= 8-2;
+
+	mask = 0x3;
+	npack = 8/2;
+	off = dp.x%npack;
+	sh = 2*(npack-1-off);
+	mask <<= sh;
+	m <<= sh;
+	p[0] = (p[0] ^ m) & mask ^ p[0];
+}
+
+static void
+putpixel_k4(Blitter *blt, Point dp, ulong rgba)
+{
+	uchar *p, r, g, b, m;
+	int off, npack, sh, mask;
+
+	p = blt->a + dp.y*blt->bpl + (dp.x*4 >> 3);
+
+	r = rgba>>24;
+	g = rgba>>16;
+	b = rgba>>8;
+	m = RGB2K(r,g,b);
+	m >>= 8-4;
+
+	mask = 0xF;
+	npack = 8/4;
+	off = dp.x%npack;
+	sh = 4*(npack-1-off);
+	mask <<= sh;
+	m <<= sh;
+	p[0] = (p[0] ^ m) & mask ^ p[0];
+}
+
+static void
+putpixel_k8(Blitter *blt, Point dp, ulong rgba)
+{
+	uchar *p, r, g, b, m;
+
+	r = rgba>>24;
+	g = rgba>>16;
+	b = rgba>>8;
+	m = RGB2K(r,g,b);
+	p = blt->a + dp.y*blt->bpl + dp.x;
+	p[0] = m;
+}
+
+static void
+putpixel_m8(Blitter *blt, Point dp, ulong rgba)
+{
+	uchar *p, r, g, b, m;
+
+	r = rgba>>24;
+	g = rgba>>16;
+	b = rgba>>8;
+	m = blt->i->cmap->rgb2cmap[(r>>4)*256+(g>>4)*16+(b>>4)];
+	p = blt->a + dp.y*blt->bpl + dp.x;
+	p[0] = m;
+}
+
+static void
+putpixel_x1r5g5b5(Blitter *blt, Point dp, ulong rgba)
+{
+	uchar *p, r, g, b;
+	ushort v;
+
+	r = rgba>>24;
+	g = rgba>>16;
+	b = rgba>>8;
+	v = r>>(8-5);
+	v = (v<<5)|(g>>(8-5));
+	v = (v<<5)|(b>>(8-5));
+	p = blt->a + dp.y*blt->bpl + dp.x*2;
+	p[0] = v;
+	p[1] = v>>8;
+}
+
+static void
+putpixel_r5g6b5(Blitter *blt, Point dp, ulong rgba)
+{
+	uchar *p, r, g, b;
+	ushort v;
+
+	r = rgba>>24;
+	g = rgba>>16;
+	b = rgba>>8;
+	v = r>>(8-5);
+	v = (v<<6)|(g>>(8-6));
+	v = (v<<5)|(b>>(8-5));
+	p = blt->a + dp.y*blt->bpl + dp.x*2;
+	p[0] = v;
+	p[1] = v>>8;
+}
+
+static void
+putpixel_r8g8b8(Blitter *blt, Point dp, ulong rgba)
+{
+	uchar *p;
+
+	p = blt->a + dp.y*blt->bpl + dp.x*3;
+	p[0] = rgba>>8;
+	p[1] = rgba>>16;
+	p[2] = rgba>>24;
+}
+
+static void
+putpixel_r8g8b8a8(Blitter *blt, Point dp, ulong rgba)
+{
+	uchar *p;
+
+	p = blt->a + dp.y*blt->bpl + dp.x*4;
+	p[0] = rgba;
+	p[1] = rgba>>8;
+	p[2] = rgba>>16;
+	p[3] = rgba>>24;
+}
+
+static void
+putpixel_a8r8g8b8(Blitter *blt, Point dp, ulong rgba)
+{
+	uchar *p;
+
+	p = blt->a + dp.y*blt->bpl + dp.x*4;
+	p[0] = rgba>>8;
+	p[1] = rgba>>16;
+	p[2] = rgba>>24;
+	p[3] = rgba;
+}
+
+static void
+putpixel_x8r8g8b8(Blitter *blt, Point dp, ulong rgba)
+{
+	uchar *p;
+
+	p = blt->a + dp.y*blt->bpl + dp.x*4;
+	p[0] = rgba>>8;
+	p[1] = rgba>>16;
+	p[2] = rgba>>24;
+}
+
+static void
+putpixel_b8g8r8(Blitter *blt, Point dp, ulong rgba)
+{
+	uchar *p;
+
+	p = blt->a + dp.y*blt->bpl + dp.x*3;
+	p[0] = rgba>>24;
+	p[1] = rgba>>16;
+	p[2] = rgba>>8;
+}
+
+static void
+putpixel_a8b8g8r8(Blitter *blt, Point dp, ulong rgba)
+{
+	uchar *p;
+
+	p = blt->a + dp.y*blt->bpl + dp.x*4;
+	p[0] = rgba>>24;
+	p[1] = rgba>>16;
+	p[2] = rgba>>8;
+	p[3] = rgba;
+}
+
+static void
+putpixel_x8b8g8r8(Blitter *blt, Point dp, ulong rgba)
+{
+	uchar *p;
+
+	p = blt->a + dp.y*blt->bpl + dp.x*4;
+	p[0] = rgba>>24;
+	p[1] = rgba>>16;
+	p[2] = rgba>>8;
+}
+
+static void
+putpixel(Blitter *blt, Point dp, ulong rgba)
+{
+	uchar *p, r, g, b, a, m;
+	ulong chan, ov, v;
+	int off, npack, sh, mask;
+
+	p = blt->a + dp.y*blt->bpl + (dp.x*blt->i->depth >> 3);
+
+	/* rgbatoimg() */
+	v = 0;
+	r = rgba>>24;
+	g = rgba>>16;
+	b = rgba>>8;
+	a = rgba;
+	for(chan=blt->i->chan; chan; chan>>=8){
+		switch(TYPE(chan)){
+		case CIgnore:
+			continue;
+		case CRed:
+			v |= (r>>(8-blt->i->nbits[CRed])) << blt->i->shift[CRed];
+			break;
+		case CGreen:
+			v |= (g>>(8-blt->i->nbits[CGreen])) << blt->i->shift[CGreen];
+			break;
+		case CBlue:
+			v |= (b>>(8-blt->i->nbits[CBlue])) << blt->i->shift[CBlue];
+			break;
+		case CAlpha:
+			v |= (a>>(8-blt->i->nbits[CAlpha])) << blt->i->shift[CAlpha];
+			break;
+		case CMap:
+			m = blt->i->cmap->rgb2cmap[(r>>4)*256+(g>>4)*16+(b>>4)];
+			v |= (m>>(8-blt->i->nbits[CMap])) << blt->i->shift[CMap];
+			break;
+		case CGrey:
+			m = RGB2K(r,g,b);
+			v |= (m>>(8-blt->i->nbits[CGrey])) << blt->i->shift[CGrey];
+			break;
+		}
+	}
+
+	/* blit op */
+	ov = p[0]|p[1]<<8|p[2]<<16|p[3]<<24;
+
+	mask = blt->cmask;
+	if(blt->i->depth < 8){
+		npack = 8/blt->i->depth;
+		off = dp.x%npack;
+		sh = blt->i->depth*(npack-1-off);
+		mask <<= sh;
+		v <<= sh;
+	}
+	v = (ov ^ v) & mask ^ ov;	/* ≡ (ov &~ mask) | (v & mask) */
+	p[0] = v;
+	p[1] = v>>8;
+	p[2] = v>>16;
+	p[3] = v>>24;
+}
+
+static void *
+getsampfn(ulong chan)
+{
+	switch(chan){
+	case GREY1: return getpixel_k1;
+	case GREY2: return getpixel_k2;
+	case GREY4: return getpixel_k4;
+	case GREY8: return getpixel_k8;
+	case CMAP8: return getpixel_m8;
+	case RGB15: return getpixel_x1r5g5b5;
+	case RGB16: return getpixel_r5g6b5;
+	case RGB24: return getpixel_r8g8b8;
+	case RGBA32: return getpixel_r8g8b8a8;
+	case ARGB32: return getpixel_a8r8g8b8;
+	case XRGB32: return getpixel_x8r8g8b8;
+	case BGR24: return getpixel_b8g8r8;
+	case ABGR32: return getpixel_a8b8g8r8;
+	case XBGR32: return getpixel_x8b8g8r8;
+	}
+	return getpixel;
+}
+
+static void *
+getblitfn(ulong chan)
+{
+	switch(chan){
+	case GREY1: return putpixel_k1;
+	case GREY2: return putpixel_k2;
+	case GREY4: return putpixel_k4;
+	case GREY8: return putpixel_k8;
+	case CMAP8: return putpixel_m8;
+	case RGB15: return putpixel_x1r5g5b5;
+	case RGB16: return putpixel_r5g6b5;
+	case RGB24: return putpixel_r8g8b8;
+	case RGBA32: return putpixel_r8g8b8a8;
+	case ARGB32: return putpixel_a8r8g8b8;
+	case XRGB32: return putpixel_x8r8g8b8;
+	case BGR24: return putpixel_b8g8r8;
+	case ABGR32: return putpixel_a8b8g8r8;
+	case XBGR32: return putpixel_x8b8g8r8;
+	}
+	return putpixel;
+}
+
+static ulong
+sample1(Sampler *s, Point p)
+{
+	if(p.x >= s->r.min.x && p.x < s->r.max.x
+	&& p.y >= s->r.min.y && p.y < s->r.max.y)
+		return s->fn(s, p);
+	else if(s->i->flags & Frepl){
+		p = drawrepl(s->r, p);
+		return s->fn(s, p);
+	}
+	/* edge handler: constant */
+	return 0;
+}
+
+static ulong
+bilinear(Sampler *s, Point p)
+{
+	ulong c00, c01, c10, c11;
+	uchar c0₀, c0₁, c0₂, c0₃, c1₀, c1₁, c1₂, c1₃;
+
+	c00 = sample1(s, p);
+	p.x++;
+	c01 = sample1(s, p);
+	p.x--; p.y++;
+	c10 = sample1(s, p);
+	p.x++;
+	c11 = sample1(s, p);
+
+	c0₀ = c00>>24;
+	c0₁ = c00>>16;
+	c0₂ = c00>>8;
+	c0₃ = c00;
+	c1₀ = c10>>24;
+	c1₁ = c10>>16;
+	c1₂ = c10>>8;
+	c1₃ = c10;
+	c0₀ = lerp(c0₀, c01>>24 & 0xFF, s->Δx);
+	c0₁ = lerp(c0₁, c01>>16 & 0xFF, s->Δx);
+	c0₂ = lerp(c0₂, c01>>8  & 0xFF, s->Δx);
+	c0₃ = lerp(c0₃, c01     & 0xFF, s->Δx);
+	c1₀ = lerp(c1₀, c11>>24 & 0xFF, s->Δx);
+	c1₁ = lerp(c1₁, c11>>16 & 0xFF, s->Δx);
+	c1₂ = lerp(c1₂, c11>>8  & 0xFF, s->Δx);
+	c1₃ = lerp(c1₃, c11     & 0xFF, s->Δx);
+	return    (lerp(c0₀, c1₀, s->Δy)) << 24
+		| (lerp(c0₁, c1₁, s->Δy)) << 16
+		| (lerp(c0₂, c1₂, s->Δy)) << 8
+		| (lerp(c0₃, c1₃, s->Δy));
+}
+
+static ulong
+correlate(Sampler *s, Point p)
+{
+	Point sp;
+	int r, g, b, a, Σr, Σg, Σb, Σa;
+	long *kp, kv;
+	ulong v;
+
+	kp = (long*)(s->k->data->bdata + s->k->zero);
+	Σr = Σg = Σb = Σa = 0;
+
+	for(sp.y = 0; sp.y < s->k->r.max.y; sp.y++)
+	for(sp.x = 0; sp.x < s->k->r.max.x; sp.x++){
+		v = sample1(s, addpt(p, subpt(sp, s->kcp)));
+		r = v>>24 & 0xFF; r = int2fix(r);
+		g = v>>16 & 0xFF; g = int2fix(g);
+		b = v>>8  & 0xFF; b = int2fix(b);
+		a = v>>0  & 0xFF; a = int2fix(a);
+
+		kv = kp[sp.y*s->k->r.max.x + sp.x];
+		r = fixmul(r, kv);
+		g = fixmul(g, kv);
+		b = fixmul(b, kv);
+		a = fixmul(a, kv);
+
+		Σr += r; Σg += g; Σb += b; Σa += a;
+	}
+	r = clamp(fix2int(Σr), 0, 0xFF);
+	g = clamp(fix2int(Σg), 0, 0xFF);
+	b = clamp(fix2int(Σb), 0, 0xFF);
+	a = clamp(fix2int(Σa), 0, 0xFF);
+
+	return r<<24|g<<16|b<<8|a;
+}
+
+int
+memaffinewarp(Memimage *d, Rectangle r, Memimage *s, Point sp0, Warp m, int smooth)
+{
+	ulong (*sample)(Sampler*, Point) = sample1;
+	Sampler samp;
+	Blitter blit;
+	Point sp, dp, p2, p2₀;
+	Rectangle dr;
+	ulong c;
+
+	dr = d->clipr;
+	rectclip(&dr, d->r);
+	if(rectclip(&r, dr) == 0)
+		return 0;
+
+	samp.r = s->clipr;
+	if(rectclip(&samp.r, s->r) == 0)
+		return 0;
+
+	if(smooth)
+		sample = bilinear;
+
+	initsampler(&samp, s);
+	initblitter(&blit, d);
+
+	/*
+	 * incremental affine warping technique from:
+	 * 	“Fast Affine Transform for Real-Time Machine Vision Applications”,
+	 * 	Lee, S., Lee, GG., Jang, E.S., Kim, WY,
+	 * 	Intelligent Computing.  ICIC 2006. LNCS, vol 4113.
+	 */
+	p2 = p2₀ = xform((Point){int2fix(r.min.x - dr.min.x) + (1<<6), int2fix(r.min.y - dr.min.y) + (1<<6)}, m);
+	for(dp.y = r.min.y; dp.y < r.max.y; dp.y++){
+	for(dp.x = r.min.x; dp.x < r.max.x; dp.x++){
+		samp.Δx = fixfrac(p2.x);
+		samp.Δy = fixfrac(p2.y);
+
+		sp.x = sp0.x + fix2int(p2.x);
+		sp.y = sp0.y + fix2int(p2.y);
+
+		c = sample(&samp, sp);
+		blit.fn(&blit, dp, c);
+
+		p2.x += m[0][0];
+		p2.y += m[1][0];
+	}
+		p2.x = p2₀.x += m[0][1];
+		p2.y = p2₀.y += m[1][1];
+	}
+	return 0;
+}
+
+static double
+coeffsum(double *m, int len)
+{
+	double *e, Σ;
+
+	e = m + len;
+	Σ = 0;
+	while(m < e)
+		Σ += *m++;
+	return Σ;
+}
+
+Memimage *
+allocmemimagekernel(double *k, int dx, int dy, double denom)
+{
+	Memimage *ik;
+	long *kern;
+	double t;
+	int x, y;
+
+	ik = allocmemimage(Rect(0,0,dx,dy), RGBA32); /* any 32-bit depth chan would do */
+	if(ik == nil){
+		werrstr("could not allocate image kernel: %r");
+		return nil;
+	}
+
+	if(denom == 0)
+		denom = coeffsum(k, dx*dy);
+	denom = denom? 1/denom: 1;
+
+	kern = (long*)(ik->data->bdata + ik->zero);
+	for(y = 0; y < dy; y++)
+	for(x = 0; x < dx; x++){
+		t = k[y*dx + x];
+		t *= denom;
+		kern[y*dx + x] = flt2fix(t);
+	}
+
+	return ik;
+}
+
+int
+memimagecorrelate(Memimage *d, Rectangle r, Memimage *s, Point sp0, Memimage *k)
+{
+	Sampler samp;
+	Blitter blit;
+	Point sp, dp;
+	Rectangle dr;
+	ulong c;
+
+	dr = d->clipr;
+	rectclip(&dr, d->r);
+	if(rectclip(&r, dr) == 0)
+		return 0;
+
+	samp.r = s->clipr;
+	if(rectclip(&samp.r, s->r) == 0)
+		return 0;
+
+	initsampler(&samp, s);
+	initblitter(&blit, d);
+
+	if(k == nil){
+		werrstr("kernel image is nil");
+		return -1;
+	}
+	if(k->depth != 32){
+		werrstr("kernel image depth is not 32");
+		return -1;
+	}
+	samp.k = k;
+	samp.kcp = Pt(Dx(k->r)/2, Dy(k->r)/2);
+
+	for(dp.y = r.min.y; dp.y < r.max.y; dp.y++)
+	for(dp.x = r.min.x; dp.x < r.max.x; dp.x++){
+		sp.x = sp0.x + dp.x - dr.min.x;
+		sp.y = sp0.y + dp.y - dr.min.y;
+		c = correlate(&samp, sp);
+		blit.fn(&blit, dp, c);
+	}
+	return 0;
+}
--- a/libmemlayer/draw.c
+++ b/libmemlayer/draw.c
@@ -121,7 +121,7 @@
 				tr.min.x = srcr.min.x;
 			}
 			if(srcr.min.y < tr.min.y){
-				p1.y += tr.min.x - srcr.min.x;
+				p1.y += tr.min.y - srcr.min.y;
 				tr.min.y = srcr.min.y;
 			}
 			if(srcr.max.x > tr.max.x)
--- a/libsec/tlshand.c
+++ b/libsec/tlshand.c
@@ -404,6 +404,7 @@
 
 static void* emalloc(int);
 static void* erealloc(void*, int);
+static void put32(uchar *p, u32int);
 static void put24(uchar *p, int);
 static void put16(uchar *p, int);
 static int get24(uchar *p);
@@ -2903,6 +2904,15 @@
 		sysfatal("out of memory");
 	setrealloctag(ReallocP, getcallerpc(&ReallocP));
 	return(ReallocP);
+}
+
+static void
+put32(uchar *p, u32int x)
+{
+	p[0] = x>>24;
+	p[1] = x>>16;
+	p[2] = x>>8;
+	p[3] = x;
 }
 
 static void
--