tttm

git clone https://orangeshoelaces.net/git/tttm.git

a3bdd7bc77dad0da212ebb015db3a588b953f4b8

Author: Vasily Kolobkov on 07/16/2017

Committer: Vasily Kolobkov on 07/16/2017

Do away with stowing responses away to disk

Stats

Makefile  |  14 +-
errors.c  |   5 -
errors.h  |   5 -
imap.c    | 173 ++-
imap.h    |  12 +-
laxsrc.c  | 139 ---
laxsrc.h  |  13 -
parser.c  | 434 ++------
parser.h  |   4 +-
pshades.c |  12 +-
tttm.c    |  83 +-
11 files changed, 234 insertions(+), 660 deletions(-)

Patch

diff --git a/Makefile b/Makefile
index fbb224b..cc99401 100644
--- a/Makefile
+++ b/Makefile
@@ -4,10 +4,10 @@ PREFIX  ?= /usr/local
 BINDIR  ?= ${PREFIX}/bin
 MAN1DIR ?= ${PREFIX}/man/man1
 
-TTTMSRC = tttm.c errors.c laxsrc.c parser.c imap.c
+TTTMSRC = tttm.c errors.c parser.c imap.c
 TTTMOBJ = ${TTTMSRC:.c=.o}
 
-PSHADESSRC = pshades.c errors.c laxsrc.c parser.c
+PSHADESSRC = pshades.c errors.c parser.c
 PSHADESOBJ = ${PSHADESSRC:.c=.o}
 
 .SUFFIXES: .c .o
@@ -36,14 +36,12 @@ uninstall:
 
 errors.o: errors.c errors.h
 
-laxsrc.o: laxsrc.c errors.h laxsrc.h
+parser.o: parser.c errors.h parser.h
 
-parser.o: parser.c errors.h laxsrc.h parser.h
+imap.o: imap.c errors.h parser.h imap.h
 
-imap.o: imap.c errors.h laxsrc.h parser.h imap.h
+pshades.o: pshades.c errors.h parser.h
 
-pshades.o: pshades.c errors.h laxsrc.h parser.h
-
-tttm.o: tttm.c errors.h laxsrc.h parser.h imap.h
+tttm.o: tttm.c errors.h parser.h imap.h
 
 .PHONY: all clean install uninstall
diff --git a/errors.c b/errors.c
index 27e56f9..20992f6 100644
--- a/errors.c
+++ b/errors.c
@@ -4,9 +4,6 @@ const char *errmsgs[] = {
 	[TE_PARSE]      = "input not recognized",
 	[TE_EOF]        = "unexpected eof",
 	[TE_TIMEOUT]    = "source read timed out",
-	[TE_BBOFLOW]    = "source back buffer overflown",
-	[TE_BUFOFLOW]   = "response buffer overflown",
-	[TE_CACHEOFLOW] = "cache overflown",
 	[TE_PTOFLOW]    = "cannot fit parse tree within supplied space",
 	[TE_XLSTR]      = "stalled upon a string longer than 2^32 - 1",
 	[TE_PROTO]      = "IMAP protocol violated",
@@ -19,6 +16,4 @@ const char *errmsgs[] = {
 	[TE_NOMEM]      = "not enough memory",
 	[TE_IN]         = "input error",
 	[TE_OUT]        = "output error",
-	[TE_VM]         = "cache vm mapping",
-	[TE_CACHEIO]    = "cache i/o",
 };
diff --git a/errors.h b/errors.h
index 0da8aa2..98385ec 100644
--- a/errors.h
+++ b/errors.h
@@ -5,10 +5,7 @@ enum {
 
 	TE_EOF,
 	TE_TIMEOUT,
-	TE_BBOFLOW,
 
-	TE_BUFOFLOW,
-	TE_CACHEOFLOW,
 	TE_PTOFLOW,
 	TE_XLSTR,
 
@@ -24,8 +21,6 @@ enum {
 	TE_NOMEM = TE_ERRNO,
 	TE_IN,
 	TE_OUT,
-	TE_VM,
-	TE_CACHEIO,
 };
 
 extern const char *errmsgs[];
diff --git a/imap.c b/imap.c
index 85794b8..3e0eb32 100644
--- a/imap.c
+++ b/imap.c
@@ -1,5 +1,6 @@
 #include <errno.h>
 #include <limits.h>
+#include <poll.h>
 #include <stdarg.h>
 #include <stddef.h>
 #include <stdint.h>
@@ -10,7 +11,6 @@
 #include <unistd.h>
 
 #include "errors.h"
-#include "laxsrc.h"
 #include "parser.h"
 #include "imap.h"
 
@@ -32,7 +32,7 @@ static struct capmap {
 
 struct respcard {
 	int   sel;
-	int (*handler)(struct imapctx *, int, void *);
+	int (*handler)(struct imapctx *, void *);
 	int   req:1;
 	int   met:1;
 	void *ctx;
@@ -41,30 +41,29 @@ struct respcard {
 struct fetargs {
 	uint32_t lo;
 	uint32_t hi;
-	int      stor;
 	struct msgd *bag;
 };
 
 static int imap_cmd(struct imapctx *, int, const char *, struct respcard *,
-    struct respcard *, int, ...);
+    struct respcard *, ...);
 static int imap_confirm(struct imapctx *, const char *, struct respcard *,
     va_list);
 static int imap_cooloff(struct imapctx *);
-static int imap_matchres(struct imapctx *, int, int *);
-static int imap_matchresp(struct imapctx *, struct respcard *, int, int *);
+static int imap_ishot(struct imapctx *);
+static int imap_matchres(struct imapctx *, int *);
+static int imap_matchresp(struct imapctx *, struct respcard *, int *);
 static int imap_parcaps(struct imapctx *, union parnode *);
 static int imap_readln(struct imapctx *);
-static int imap_readlnc(struct imapctx *, int, int *);
 static int imap_recvgreets(struct imapctx *);
 static int imap_send(struct imapctx *, const char *, struct respcard *,
     va_list);
 
-static int dig_bye(struct imapctx *, int, void *);
-static int dig_uxbye(struct imapctx *, int, void *);
-static int dig_caps(struct imapctx *, int, void *);
-static int dig_exists(struct imapctx *, int, void *);
-static int dig_expunge(struct imapctx *, int, void *);
-static int dig_fetch(struct imapctx *, int, void *);
+static int dig_bye(struct imapctx *, void *);
+static int dig_uxbye(struct imapctx *, void *);
+static int dig_caps(struct imapctx *, void *);
+static int dig_exists(struct imapctx *, void *);
+static int dig_expunge(struct imapctx *, void *);
+static int dig_fetch(struct imapctx *, void *);
 
 static int state2res(union parnode *);
 static int str2cap(struct capmap *, char *, size_t);
@@ -92,16 +91,13 @@ static struct respcard fetresp[] = { { IP_FETCH, dig_fetch }, { -1 } };
 static struct respcard byeresp[] = { { IP_RESPBYE, dig_bye, 1 }, { -1 } };
 
 int
-imap_init(struct imapctx *con, int in, int out, size_t buflen)
+imap_init(struct imapctx *con, int in, int out)
 {
 	int e;
 
 	bzero(con, sizeof(*con));
-	if (!(con->rep = malloc(buflen)))
-		goto emem;
-	con->replen = buflen;
 	con->state = IS_GREET;
-	laxsrc_init(&con->in, in, buflen);
+	con->in = in;
 	con->out = out;
 	if ((e = imap_recvgreets(con)))
 		goto exit;
@@ -117,34 +113,31 @@ imap_init(struct imapctx *con, int in, int out, size_t buflen)
 void
 imap_free(struct imapctx *con)
 {
-	laxsrc_free(&con->in);
-	free(con->rep);
+	free(con->resp);
 }
 
 int
 imap_discocap(struct imapctx *con)
 {
-	return imap_cmd(con, -1, "%u CAPABILITY\r\n", capresp, unilat,
-	    -1, con->tag++);
+	return imap_cmd(con, -1, "%u CAPABILITY\r\n", capresp, unilat, con->tag++);
 }
 
 int
 imap_login(struct imapctx *con, const char *user, const char *pass)
 {
 	return imap_cmd(con, IS_AUTH, "%u LOGIN %LS %LS\r\n", 0, unilat,
-	    -1, con->tag++, user, pass);
+	    con->tag++, user, pass);
 }
 
 int
 imap_select(struct imapctx *con, const char *mb)
 {
 	return imap_cmd(con, IS_SEL, "%u SELECT %LS\r\n", selresp, unilat,
-	    -1, con->tag++, mb);
+	    con->tag++, mb);
 }
 
 int
-imap_fetch(struct imapctx *con, uint32_t lo, uint32_t hi, int stor,
-    struct msgd *bag)
+imap_fetch(struct imapctx *con, uint32_t lo, uint32_t hi, struct msgd *bag)
 {
 	int e;
 	struct msgd *m, *bend;
@@ -154,10 +147,10 @@ imap_fetch(struct imapctx *con, uint32_t lo, uint32_t hi, int stor,
 	for (m = bag; m < bend; m++) {
 		m->off = -1;
 	}
-	args = (struct fetargs){ lo, hi, stor, bag };
+	args = (struct fetargs){ lo, hi, bag };
 	fetresp->ctx = &args;
-	e = imap_cmd(con, -1, "%u FETCH %u:%u RFC822\r\n", fetresp,
-	    unisexp, stor, con->tag++, (unsigned)lo, (unsigned)hi);
+	e = imap_cmd(con, -1, "%u FETCH %u:%u RFC822\r\n", fetresp, unisexp,
+	    con->tag++, (unsigned)lo, (unsigned)hi);
 	if (e)
 		goto exit;
 	for (m = bag; m < bend; m++) {
@@ -175,45 +168,50 @@ int
 imap_delete(struct imapctx *con, uint32_t lo, uint32_t hi)
 {
 	return imap_cmd(con, -1, "%u STORE %u:%u +FLAGS.SILENT (\\Deleted)\r\n",
-	    0, unisexp, -1, con->tag++, (unsigned)lo, (unsigned)hi);
+	    0, unisexp, con->tag++, (unsigned)lo, (unsigned)hi);
 }
 
 int
 imap_expunge(struct imapctx *con)
 {
-	return imap_cmd(con, -1, "%u EXPUNGE\r\n", 0, unilat, -1, con->tag++);
+	return imap_cmd(con, -1, "%u EXPUNGE\r\n", 0, unilat, con->tag++);
 }
 
 int
 imap_nop(struct imapctx *con)
 {
-	return imap_cmd(con, -1, "%u NOOP\r\n", 0, unilat, -1, con->tag++);
+	return imap_cmd(con, -1, "%u NOOP\r\n", 0, unilat, con->tag++);
 }
 
 int
 imap_close(struct imapctx *con)
 {
-	return imap_cmd(con, IS_AUTH, "%u CLOSE\r\n", 0, unilat,
-	    -1, con->tag++);
+	return imap_cmd(con, IS_AUTH, "%u CLOSE\r\n", 0, unilat, con->tag++);
 }
 
 int
 imap_logout(struct imapctx *con)
 {
 	return imap_cmd(con, IS_LOGOUT, "%u LOGOUT\r\n", byeresp, unilat,
-	    -1, con->tag++);
+	    con->tag++);
 }
 
 int
 imap_cmd(struct imapctx *con, int nstate, const char *cmd,
-    struct respcard *cmdresp, struct respcard *unilat, int cache, ...)
+    struct respcard *cmdresp, struct respcard *unilat, ...)
 {
-	int e, b;
+	int e;
 	va_list ap;
 	union parnode *s;
 	struct respcard *c;
 
-	va_start(ap, cache);
+	va_start(ap, unilat);
+	if (con->rpar > con->rcap / 2) {
+		memmove(con->resp, con->resp + con->rpar,
+		    con->rlen - con->rpar);
+		con->rlen -= con->rpar;
+		con->rpar = 0;
+	}
 	if ((e = imap_cooloff(con))) {
 		goto exit;
 	}
@@ -223,16 +221,14 @@ imap_cmd(struct imapctx *con, int nstate, const char *cmd,
 	for (c = cmdresp; c && c->sel != -1; c++) {
 		c->met = 0;
 	}
-	while (!(e = imap_readlnc(con, cache, &b))) {
-		if (imap_matchres(con, b, &e)) {
+	while (!(e = imap_readln(con))) {
+		if (imap_matchres(con, &e)) {
 			break;
 		}
-		if ((imap_matchresp(con, cmdresp, b, &e) ||
-		    imap_matchresp(con, unilat, b, &e)) && e) {
+		if ((imap_matchresp(con, cmdresp, &e) ||
+		    imap_matchresp(con, unilat, &e)) && e) {
 			break;
 		}
-		if (cache != -1 && lseek(cache, 0, SEEK_END) == -1)
-			goto eio;
 	}
 	if (e) {
 		goto exit;
@@ -248,9 +244,6 @@ imap_cmd(struct imapctx *con, int nstate, const char *cmd,
  exit:
 	va_end(ap);
 	return e;
- eio:
-	e = TE_CACHEIO;
-	goto exit;
  eproto:
 	e = TE_PROTO;
 	goto exit;
@@ -267,10 +260,10 @@ imap_confirm(struct imapctx *con, const char *excerpt,
 
 	while (!(e = imap_readln(con))) {
 		if (isprod(con->par, IP_CONTREQ) ||
-		    imap_matchres(con, 1, &e)) {
+		    imap_matchres(con, &e)) {
 			break;
 		}
-		if (imap_matchresp(con, unilat, 1, &e) && e) {
+		if (imap_matchresp(con, unilat, &e) && e) {
 			break;
 		}
 	}
@@ -286,10 +279,10 @@ imap_cooloff(struct imapctx *con)
 {
 	int e;
 
-	while (!(e = laxsrc_ishot(&con->in))) {
+	while (!(e = imap_ishot(con))) {
 		if ((e = imap_readln(con)))
 			goto exit;
-		if (imap_matchresp(con, unisexp, 1, &e) && e)
+		if (imap_matchresp(con, unisexp, &e) && e)
 			goto exit;
 	}
 	if (e == TE_TIMEOUT) {
@@ -300,13 +293,34 @@ imap_cooloff(struct imapctx *con)
 }
 
 int
-imap_matchres(struct imapctx *con, int buf, int *e)
+imap_ishot(struct imapctx *con)
+{
+	int e, ready;
+	struct pollfd pd;
+
+	e = 0;
+	if (con->rlen > con->rpar)
+		goto exit;
+
+	pd.fd = con->in;
+	pd.events = POLLIN | POLLHUP;
+	if ((ready = poll(&pd, 1, 0)) == -1) {
+		e = TE_IN;
+	} else if (ready == 0) {
+		e = TE_TIMEOUT;
+	}
+
+ exit:
+	return e;
+}
+
+int
+imap_matchres(struct imapctx *con, int *e)
 {
 	union parnode *s, *c;
 
 	if ((s = par_sel(con->par, IP_TAGGEDRESP, IP_RESPSTATE, -1))) {
-		c = par_seld(s, IP_RESPTXTCODE, IP_CAPDATA, -1);
-		if (buf && c) {
+		if ((c = par_seld(s, IP_RESPTXTCODE, IP_CAPDATA, -1))) {
 			imap_parcaps(con, c);
 		}
 		*e = state2res(s);
@@ -315,7 +329,7 @@ imap_matchres(struct imapctx *con, int buf, int *e)
 }
 
 int
-imap_matchresp(struct imapctx *con, struct respcard *set, int buf, int *e)
+imap_matchresp(struct imapctx *con, struct respcard *set, int *e)
 {
 	int match;
 	struct respcard *c;
@@ -324,7 +338,7 @@ imap_matchresp(struct imapctx *con, struct respcard *set, int buf, int *e)
 	for (c = set; c && c->sel != -1; c++) {
 		if (par_sel(con->par, IP_RESPDATA, c->sel, -1)) {
 			c->met = match = 1;
-			*e = c->handler ? c->handler(con, buf, c->ctx) : 0;
+			*e = c->handler ? c->handler(con, c->ctx) : 0;
 			break;
 		}
 	}
@@ -342,7 +356,7 @@ imap_parcaps(struct imapctx *con, union parnode *n)
 		if (!isprod(n, IP_CAP))
 			continue;
 		atom = par_seld(n, IP_ATOM, -1);
-		str = con->rep + (atom + 1)->str.off;
+		str = con->resp + (atom + 1)->str.off;
 		len = (atom + 1)->str.len;
 		if (islit(n + 1, IL_AUTHEQ)) {
 			con->caps |= str2cap(authcaps, str, len);
@@ -357,15 +371,8 @@ imap_parcaps(struct imapctx *con, union parnode *n)
 int
 imap_readln(struct imapctx *con)
 {
-	return par_readln(&con->in, con->rep, con->replen,
-	    -1, 0, con->par, LEN(con->par));
-}
-
-int
-imap_readlnc(struct imapctx *con, int cache, int *buf)
-{
-	return par_readln(&con->in, con->rep, con->replen,
-	    cache, buf, con->par, LEN(con->par));
+	return par_parseln(con->in, &con->resp, &con->rcap, &con->rlen,
+	    &con->rpar, con->par, LEN(con->par));
 }
 
 int
@@ -468,30 +475,30 @@ imap_send(struct imapctx *con, const char *cmd, struct respcard *unilat,
 }
 
 int
-dig_bye(struct imapctx *con, int buf, void *ctx)
+dig_bye(struct imapctx *con, void *ctx)
 {
 	con->state = IS_LOGOUT;
 	return 0;
 }
 
 int
-dig_uxbye(struct imapctx *con, int buf, void *ctx)
+dig_uxbye(struct imapctx *con, void *ctx)
 {
 	con->state = IS_LOGOUT;
 	return TE_BYE;
 }
 
 int
-dig_caps(struct imapctx *con, int buf, void *ctx)
+dig_caps(struct imapctx *con, void *ctx)
 {
 	union parnode *c;
 
 	c = par_sel(con->par, IP_RESPDATA, IP_CAPDATA, -1);
-	return (c && buf) ? imap_parcaps(con, c) : 0;
+	return c ? imap_parcaps(con, c) : 0;
 }
 
 int
-dig_exists(struct imapctx *con, int buf, void *ctx)
+dig_exists(struct imapctx *con, void *ctx)
 {
 	union parnode *ex;
 
@@ -501,7 +508,7 @@ dig_exists(struct imapctx *con, int buf, void *ctx)
 }
 
 int
-dig_expunge(struct imapctx *con, int buf, void *ctx)
+dig_expunge(struct imapctx *con, void *ctx)
 {
 	if (con->mb.exists > 0)
 		con->mb.exists--;
@@ -509,7 +516,7 @@ dig_expunge(struct imapctx *con, int buf, void *ctx)
 }
 
 int
-dig_fetch(struct imapctx *con, int buf, void *ctx)
+dig_fetch(struct imapctx *con, void *ctx)
 {
 	int e;
 	struct fetargs *args;
@@ -534,30 +541,10 @@ dig_fetch(struct imapctx *con, int buf, void *ctx)
 	m = args->bag + (msn - args->lo);
 	m->off = nstr->str.off;
 	m->len = nstr->str.len;
-	if (buf) {
-		if ((m->off = lseek(args->stor, 0, SEEK_END)) == -1)
-			goto eio;
-		bcur = con->rep + nstr->str.off;
-		blen = nstr->str.len;
-		while (blen) {
-			if ((n = write(args->stor, bcur, blen)) == 0) {
-				goto eio;
-			} else if (n == -1) {
-				if (errno == EINTR || errno == EAGAIN)
-					continue;
-				else goto eio;
-			}
-			blen -= n;
-			bcur += n;
-		}
-	}
 	if (nstr->type != PN_LSTR)
 		goto eproto;
  exit:
 	return e;
- eio:
-	e = TE_CACHEIO;
-	goto exit;
  enil:
 	e = TE_NIL;
 	goto exit;
diff --git a/imap.h b/imap.h
index 6c465d9..3cb8d91 100644
--- a/imap.h
+++ b/imap.h
@@ -4,12 +4,14 @@ enum { CAP_KNOWN = 1, CAP_IMAP4R1, CAP_NOLOGIN, CAP_AUTHPLAIN };
 
 struct imapctx {
 	int           state;
-	struct laxsrc in;
+	int           in;
 	int           out;
 
 	unsigned int  tag;
-	char         *rep;
-	size_t        replen;
+	char         *resp;
+	size_t        rlen;
+	size_t        rcap;
+	size_t        rpar;
 	union parnode par[1024];
 
 	int           caps;
@@ -23,12 +25,12 @@ struct msgd {
 	size_t len;
 };
 
-int	imap_init(struct imapctx *, int, int, size_t);
+int	imap_init(struct imapctx *, int, int);
 void	imap_free(struct imapctx *);
 int	imap_discocap(struct imapctx *);
 int	imap_login(struct imapctx *, const char *, const char *);
 int	imap_select(struct imapctx *, const char *);
-int	imap_fetch(struct imapctx *, uint32_t, uint32_t, int, struct msgd *);
+int	imap_fetch(struct imapctx *, uint32_t, uint32_t, struct msgd *);
 int	imap_delete(struct imapctx *, uint32_t, uint32_t);
 int	imap_expunge(struct imapctx *);
 int	imap_nop(struct imapctx *);
diff --git a/laxsrc.c b/laxsrc.c
deleted file mode 100644
index 9a34b6b..0000000
--- a/laxsrc.c
+++ /dev/null
@@ -1,139 +0,0 @@
-#include <errno.h>
-#include <poll.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include <unistd.h>
-
-#include "errors.h"
-#include "laxsrc.h"
-
-#define MIN(a, b) ((a) <= (b) ? (a) : (b))
-
-static int timeout = 2 * 60 * 1000;
-
-int
-laxsrc_init(struct laxsrc *s, int fd, size_t buflen)
-{
-	int e;
-
-	e = 0;
-	bzero(s, sizeof(*s));
-	s->fd = fd;
-	if (!(s->buf = malloc(buflen)))
-		goto emem;
-	s->buflen = buflen;
- exit:
-	return e;
- emem:
-	e = TE_NOMEM;
-	goto exit;
-}
-
-void
-laxsrc_free(struct laxsrc *s)
-{
-	free(s->buf);
-}
-
-int
-laxsrc_ishot(struct laxsrc *s)
-{
-	int e, ready;
-	struct pollfd pd;
-
-	e = 0;
-	if (s->bufill > 0)
-		goto exit;
-	pd.fd = s->fd;
-	pd.events = POLLIN | POLLHUP;
-	if ((ready = poll(&pd, 1, 0)) == -1) {
-		e = TE_IN;
-	} else if (ready == 0) {
-		e = TE_TIMEOUT;
-	}
- exit:
-	return e;
-}
-
-int
-laxsrc_read(struct laxsrc *s, char *dst, size_t lo, size_t hi, int *e)
-{
-	size_t boff;
-	size_t len;
-	struct pollfd pd;
-	int ready;
-	ssize_t n;
-
-	*e = TE_OK;
-	len = 0;
-	pd.fd = s->fd;
-	pd.events = POLLIN | POLLHUP;
-
-	if (s->bufill > 0) {
-		boff = s->buflen - s->bufill;
-		len = MIN(s->bufill, hi);
-		memcpy(dst, s->buf + boff, len);
-		s->bufill -= len;
-	}
-
-	if (s->eof) goto eof;
-
-	while (len < lo) {
-		ready = poll(&pd, 1, timeout);
-
-		if (ready == -1) goto ein;
-		if (ready == 0) goto eto;
-
-		if (pd.revents & (POLLIN | POLLHUP)) {
-			if ((n = read(pd.fd, dst + len, hi - len)) == 0) {
-				goto eof;
-			} else if (n == -1) {
-				if (errno == EINTR || errno == EAGAIN)
-					continue;
-				else goto ein;
-			}
-			len += n;
-		}
-	}
-
- exit:
-	return len;
- ein:
-	*e = TE_IN;
-	len = 0;
-	goto exit;
- eto:
-	*e = TE_TIMEOUT;
-	len = 0;
-	goto exit;
- eof:
-	if (len < lo) {
-		*e = TE_EOF;
-		len = 0;
-	} else {
-		*e = TE_OK;
-	}
-	s->eof = 1;
-	goto exit;
-}
-
-int
-laxsrc_putback(struct laxsrc *s, char *chunk, size_t len)
-{
-	int e;
-	size_t boff;
-
-	e = TE_OK;
-	if (s->bufill + len > s->buflen)
-		goto eoflow;
-	s->bufill += len;
-	boff = s->buflen - s->bufill;
-	memcpy(s->buf + boff, chunk, len);
- exit:
-	return e;
- eoflow:
-	e = TE_BBOFLOW;
-	goto exit;
-}
diff --git a/laxsrc.h b/laxsrc.h
deleted file mode 100644
index 04f524b..0000000
--- a/laxsrc.h
+++ /dev/null
@@ -1,13 +0,0 @@
-struct laxsrc {
-	int    fd;
-	int    eof;
-	char  *buf;
-	size_t buflen;
-	size_t bufill;
-};
-
-int	laxsrc_init(struct laxsrc *, int, size_t);
-void	laxsrc_free(struct laxsrc *);
-int	laxsrc_ishot(struct laxsrc *);
-int	laxsrc_read(struct laxsrc *, char *, size_t, size_t, int *);
-int	laxsrc_putback(struct laxsrc *, char *, size_t);
diff --git a/parser.c b/parser.c
index 51482c5..1d7733f 100644
--- a/parser.c
+++ b/parser.c
@@ -1,10 +1,11 @@
 /*
    Be warned! p_* functions bear unconventional call semantics
    returning 1 on success and 0 otherwise. Specific error code
-   or 0 is dispensed to the parse context.
+   is dispensed to the parse context.
 */
 
 #include <errno.h>
+#include <poll.h>
 #include <stdarg.h>
 #include <stdint.h>
 #include <stdlib.h>
@@ -15,7 +16,6 @@
 #include <sys/stat.h>
 
 #include "errors.h"
-#include "laxsrc.h"
 #include "parser.h"
 
 #define LEN(x) (sizeof(x) / sizeof((x)[0]))
@@ -26,6 +26,9 @@
 	"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
 	"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
 
+static int timeout = 2 * 60 * 1000;
+static size_t minstrcap = 1 << 13;
+
 static struct literal {
 	char  *srep;
 	size_t slen;
@@ -137,22 +140,16 @@ struct parcur {
 };
 
 struct parctx {
-	struct parcur  cur;
 	int            e;
-	char          *wnd;
-	size_t         wndcap;
-	size_t         wndlee;
-	size_t         wndoff;
+	int            src;
+	char          *str;
+	size_t         strcap;
+	size_t         strlen;
+	size_t         stroff;
+	struct parcur  cur;
 	union parnode *pt;
 	union parnode *ptend;
 	size_t         ptlen;
-	char          *buf;
-	size_t         buflen;
-	int            cache;
-	off_t          corig;
-	struct laxsrc *in;
-	size_t         strlen;
-	enum { SS_MEM, SS_CACHE } strstor;
 };
 
 typedef int parfn(struct parctx *);
@@ -161,17 +158,9 @@ static union parnode * par_vseld(union parnode *, va_list);
 static int cmpchr(const void *, const void *);
 static int contains(const char *, size_t, char);
 
-static void par_init(struct parctx *, struct laxsrc *,
-    char *, size_t, int, union parnode *, size_t);
-static int par_free(struct parctx *);
-static int par_procure(struct parctx *, size_t, size_t);
-static int par_movewnd(struct parctx *, size_t);
-static int par_primecache(struct parctx *);
-static void par_offsetpt(union parnode *, union parnode *, off_t);
-static int par_prycache(struct parctx *, size_t);
-static int par_readnup(struct parctx *, size_t);
-static int par_peek(struct parctx *, size_t, char *);
-static int par_backup(struct parctx *);
+static void par_init(struct parctx *, int,
+    char *, size_t, size_t, size_t, union parnode *, size_t);
+static int par_fillstr(struct parctx *, size_t);
 
 static int p_chk(struct parctx *, struct parcur *);
 static int p_insint(struct parctx *, int, size_t);
@@ -311,18 +300,18 @@ static int p_opar(struct parctx *);
 static int p_sp(struct parctx *);
 
 int
-par_readln(struct laxsrc *in, char *buf, size_t buflen, int cache,
-    int *bufd, union parnode *pt, size_t ptlen)
+par_parseln(int src, char **str, size_t *strcap, size_t *strlen, size_t *stroff,
+    union parnode *pt, size_t ptlen)
 {
-	int e;
 	struct parctx p;
 
-	par_init(&p, in, buf, buflen, cache, pt, ptlen);
+	par_init(&p, src, *str, *strcap, *strlen, *stroff, pt, ptlen);
 	p_respln(&p);
-	if (bufd)
-		*bufd = p.strstor == SS_MEM;
-	e = par_free(&p);
-	return e ? e : p.e;
+	*str = p.str;
+	*strcap = p.strcap;
+	*strlen = p.strlen;
+	*stroff = p.cur.off;
+	return p.e;
 }
 
 union parnode *
@@ -402,267 +391,82 @@ contains(const char *s, size_t slen, char c)
 }
 
 void
-par_init(struct parctx *p, struct laxsrc *in, char *buf, size_t buflen,
-    int cache, union parnode *pt, size_t ptlen)
+par_init(struct parctx *p, int src, char *str, size_t strcap, size_t strlen,
+    size_t stroff, union parnode *pt, size_t ptlen)
 {
 	bzero(p, sizeof(*p));
+	p->src = src;
+	p->str = str;
+	p->strcap = strcap;
+	p->strlen = strlen;
+	p->stroff = stroff;
+	p->cur.off = stroff;
 	p->cur.pt = pt;
-	p->wnd = buf;
-	p->wndcap = buflen;
 	p->pt = pt;
 	p->ptend = pt + ptlen;
 	p->ptlen = ptlen;
-	p->buf = buf;
-	p->buflen = buflen;
-	p->cache = cache;
-	p->in = in;
-	p->strstor = SS_MEM;
-}
-
-int
-par_free(struct parctx *p)
-{
-	int e;
-
-	if (e = par_backup(p))
-		goto exit;
-	if (p->strstor == SS_CACHE && munmap(p->wnd, p->wndcap) == -1)
-		goto evm;
- exit:
-	return e;
- evm:
-	e = TE_VM;
-	goto exit;
 }
 
-/*
-   Prepare window for accessing string symbols with indexes [off..off + len).
-   When reading from source to fulfill the request, no less than the needed
-   amount and anything over that that fits the window is transfered.
-
-   precond: len <= p->wndcap
-*/
 int
-par_procure(struct parctx *p, size_t off, size_t len)
+par_fillstr(struct parctx *p, size_t len)
 {
-	int e;
-	size_t send, wndshort;
-
-	e = TE_OK;
-	send = off + len;
+	char *newstr;
+	struct pollfd pd;
+	int ready;
+	ssize_t n;
 
-	if (off >= p->wndoff && send <= p->wndoff + p->wndlee)
+	if (len <= p->strlen) {
+		p->e = TE_OK;
 		goto exit;
-
-	while (send > p->strlen) {
-		if ((p->wndlee == p->wndcap) && (e = par_movewnd(p, p->strlen)))
-			goto exit;
-		wndshort = MIN(send - p->strlen, p->wndcap - p->wndlee);
-		if ((e = par_readnup(p, wndshort)))
-			goto exit;
-	}
-
-	if (off < p->wndoff || send > p->wndoff + p->wndcap)
-	    e = par_movewnd(p, off);
- exit:
-	return e;
-}
-
-int
-par_movewnd(struct parctx *p, size_t off)
-{
-	int e;
-	char *ci;
-
-	e = TE_OK;
-	if (p->strstor == SS_MEM && off + p->wndcap <= p->buflen) {
-		p->wnd = p->buf + off;
-		goto finwnd;
 	}
-
-	if (p->cache == -1)
-		goto ebufoflow;
-
-	if (p->strstor == SS_MEM) {
-		if ((e = par_primecache(p)))
-			goto exit;
-	} else {
-		if (munmap(p->wnd, p->wndcap) == -1)
-			goto evm;
-	}
-	if ((e = par_prycache(p, off + p->wndcap)))
-		goto exit;
-
-	ci = mmap(0, p->wndcap, PROT_READ | PROT_WRITE,
-	    MAP_SHARED, p->cache, p->corig + off);
-	if (ci == MAP_FAILED)
-		goto evm;
-
-	p->wnd = ci;
-	goto finwnd;
-
- finwnd:
-	p->wndlee = MIN(p->wndcap, p->strlen - off);
-	p->wndoff = off;
- exit:
-	return e;
- ebufoflow:
-	e = TE_BUFOFLOW;
-	goto exit;
- evm:
-	e = TE_VM;
-	goto exit;
-}
-
-int
-par_primecache(struct parctx *p)
-{
-	int e;
-	off_t off;
-	void *ci;
-
-	e = TE_OK;
-	if ((off = lseek(p->cache, 0, SEEK_CUR)) == -1)
-		goto eio;
-
-	p->corig = off;
-	if ((e = par_prycache(p, p->buflen)))
-		goto exit;
-
-	p->strstor = SS_CACHE;
-	if (p->buflen == 0)
-		goto exit;
-
-	ci = mmap(0, p->buflen, PROT_WRITE, MAP_SHARED, p->cache, p->corig);
-	if (ci == MAP_FAILED)
-		goto evm;
-
-	memcpy(ci, p->buf, p->buflen);
-	if (munmap(ci, p->buflen) == -1)
-		goto evm;
-
-	par_offsetpt(p->pt, p->ptend, off);
-
- exit:
-	return e;
- eio:
-	e = TE_CACHEIO;
-	goto exit;
- evm:
-	e = TE_VM;
-	goto exit;
-}
-
-void
-par_offsetpt(union parnode *cur, union parnode *end, off_t off)
-{
-	union parnode *iend;
-
-	while (cur < end) {
-		switch (cur->type) {
-		case PN_INTER:
-			iend = cur + cur->inter.len + 1;
-			par_offsetpt(cur + 1, MIN(iend, end), off);
-			cur = iend;
-			break;
-		case PN_STR:
-		case PN_QSTR:
-		case PN_LSTR:
-			cur->str.off += off;
-			/* fall through */
-		default:
-			cur++;
+	if (len > p->strcap) {
+		p->strcap = MAX(minstrcap, p->strcap);
+		do {
+			p->strcap *= 2;
+		} while (len > p->strcap);
+		newstr = realloc(p->str, p->strcap);
+		if (newstr == 0) {
+			goto emem;
 		}
+		p->str = newstr;
 	}
-}
 
-int
-par_prycache(struct parctx *p, size_t len)
-{
-	int e;
-	size_t flen;
-	ssize_t n;
-	struct stat cs;
+	pd.fd = p->src;
+	pd.events = POLLIN | POLLHUP;
 
-	e = TE_OK;
-	if (fstat(p->cache, &cs) == -1)
-		goto eio;
+	while (len > p->strlen) {
+		ready = poll(&pd, 1, timeout);
 
-	flen = len + p->corig;
-	if (flen < MAX(len, p->corig) || flen > INT64_MAX)
-		goto eover;
+		if (ready == -1) goto ein;
+		if (ready == 0) goto eto;
 
-	if (cs.st_size < flen) {
-		while ((n = pwrite(p->cache, "", 1, flen - 1)) <= 0) {
-			if (n == -1 && (errno == EINTR || errno == EAGAIN))
-				continue;
-			else goto eio;
+		if (pd.revents & (POLLIN | POLLHUP)) {
+			n = read(pd.fd, p->str + p->strlen, p->strcap - p->strlen);
+			if (n == 0) {
+				goto eof;
+			} else if (n == -1) {
+				if (errno == EINTR || errno == EAGAIN)
+					continue;
+				else goto ein;
+			}
+			p->strlen += n;
 		}
 	}
 
  exit:
-	return e;
- eio:
-	e = TE_CACHEIO;
+	return p->e;
+ emem:
+	p->e = TE_NOMEM;
 	goto exit;
- eover:
-	e = TE_CACHEOFLOW;
+ ein:
+	p->e = TE_IN;
+	goto exit;
+ eto:
+	p->e = TE_TIMEOUT;
+	goto exit;
+ eof:
+	p->e = TE_EOF;
 	goto exit;
-}
-
-int
-par_readnup(struct parctx *p, size_t n)
-{
-	int e;
-	size_t hi, nr;
-
-	hi = p->wndcap - p->wndlee;
-	nr = laxsrc_read(p->in, p->wnd + p->wndlee, n, hi, &e);
-	if (e == TE_OK && nr > 0) {
-		p->wndlee += nr;
-		p->strlen += nr;
-	}
-
-	return e;
-}
-
-int
-par_peek(struct parctx *p, size_t off, char *c)
-{
-	int e;
-	size_t wndlag;
-
-	if ((e = par_procure(p, off, 1)))
-		goto exit;
-
-	wndlag = off - p->wndoff;
-	*c = p->wnd[wndlag];
- exit:
-	return e;
-}
-
-int
-par_backup(struct parctx *p)
-{
-	int e;
-	size_t w, shed, nwnd;
-	size_t off, wndlag;
-
-	e = TE_OK;
-	shed = p->cur.off / p->wndcap + 1;
-	nwnd = p->strlen / p->wndcap + (p->strlen % p->wndcap > 0);
-	off = (nwnd - 1) * p->wndcap;
-
-	for (w = nwnd; w >= shed; w--, off -= p->wndcap) {
-		if ((e = par_movewnd(p, off)))
-			break;
-
-		wndlag = MAX(off, p->cur.off) - p->wndoff;
-		e = laxsrc_putback(p->in, p->wnd + wndlag, p->wndlee - wndlag);
-		if (e)
-			break;
-	}
-	return e;
 }
 
 /*
@@ -813,19 +617,16 @@ p_repchr(struct parctx *p, const char *except, size_t elen, int strtype)
 {
 	size_t pr;
 	char *prp;
-	size_t wndlag;
-	size_t leeway;
 	size_t len;
 
 	if (p->e > TE_PARSE)
 		goto exit;
 
+	p->e = TE_OK;
 	pr = p->cur.off;
-	while ((p->e = par_procure(p, pr, 1)) == TE_OK) {
-		wndlag = pr - p->wndoff;
-		leeway = p->wndoff + p->wndlee;
 
-		for (prp = p->wnd + wndlag; pr < leeway; pr++, prp++) {
+	while (par_fillstr(p, pr + 1) == TE_OK) {
+		for (prp = p->str + pr; pr < p->strlen; ++pr, ++prp) {
 			if (contains(except, elen, *prp))
 				goto stop;
 		}
@@ -1193,8 +994,6 @@ p_dig(struct parctx *p, size_t count)
 {
 	size_t pr;
 	char *prp;
-	size_t wndlag;
-	size_t leeway;
 	size_t len;
 	uint32_t n, prev;
 	size_t d;
@@ -1202,19 +1001,17 @@ p_dig(struct parctx *p, size_t count)
 	if (p->e > TE_PARSE)
 		goto exit;
 
+	p->e = TE_OK;
 	pr = p->cur.off;
 	n = prev = 0;
 	d = count == SIZE_MAX ? 0 : 1;
 
-	while ((p->e = par_procure(p, pr, 1)) == TE_OK) {
-		wndlag = pr - p->wndoff;
-		leeway = p->wndoff + p->wndlee;
-
-		for (prp = p->wnd + wndlag; pr < leeway; pr++, prp++) {
+	while (par_fillstr(p, pr + 1) == TE_OK) {
+		for (prp = p->str + pr; pr < p->strlen; ++pr, ++prp) {
 			if (count == 0 || *prp < '0' || *prp > '9')
 				goto stop;
 			prev = n;
-			n = UINT32_MAX & (10 * n + (*prp - 48));
+			n = UINT32_MAX & (10 * n + (*prp - '0'));
 			if (prev > n) {
 				p->e = TE_PARSE;
 				goto stop;
@@ -1335,19 +1132,16 @@ p_genctext(struct parctx *p)
 {
 	size_t pr;
 	char *prp;
-	size_t wndlag;
-	size_t leeway;
 	size_t len;
 
 	if (p->e > TE_PARSE)
 		goto exit;
 
+	p->e = TE_OK;
 	pr = p->cur.off;
-	while ((p->e = par_procure(p, pr, 1)) == TE_OK) {
-		wndlag = pr - p->wndoff;
-		leeway = p->wndoff + p->wndlee;
 
-		for (prp = p->wnd + wndlag; pr < leeway; pr++, prp++) {
+	while (par_fillstr(p, pr + 1) == TE_OK) {
+		for (prp = p->str + pr; pr < p->strlen; ++pr, ++prp) {
 			if (*prp == 0 || *prp > '\x7f' || *prp == ']' ||
 			    *prp == '\r' || *prp == '\n')
 				goto stop;
@@ -1393,26 +1187,17 @@ int
 p_lit(struct parctx *p, int val)
 {
 	struct literal *lit;
-	size_t match, chunk, wndlag;
 
 	if (p->e > TE_PARSE)
 		goto exit;
 
+	p->e = TE_OK;
 	lit = literals + val;
-	match = 0;
-
-	while (match < lit->slen) {
-		if ((p->e = par_procure(p, p->cur.off + match, 1)))
-			goto exit;
-		wndlag = p->cur.off + match - p->wndoff;
-		chunk = MIN(lit->slen - match, p->wndlee - wndlag);
-		if (strncasecmp(p->wnd + wndlag, lit->srep + match,
-		    chunk) == 0) {
-			match += chunk;
-		} else {
-			goto eparse;
-		}
-	}
+
+	if (par_fillstr(p, p->cur.off + lit->slen) != TE_OK)
+		goto exit;
+	if (strncasecmp(p->str + p->cur.off, lit->srep, lit->slen) != 0)
+		goto eparse;
 
 	if (p_inslit(p, val))
 		p->cur.off += lit->slen;
@@ -1437,13 +1222,14 @@ p_lstr(struct parctx *p)
 	if (!prefix)
 		goto exit;
 
+	p->e = TE_OK;
 	len = n.pt->num.val;
 	p->cur.pt = b.pt;
 
 	if (len > UINT32_MAX) goto exl;
-	if (len > 0 && (p->e = par_procure(p, p->cur.off + len - 1, 1)))
+	if (par_fillstr(p, p->cur.off + len) != TE_OK)
 		goto exit;
-	if (!p_insstr(p, PN_LSTR, p->corig + p->cur.off, len))
+	if (!p_insstr(p, PN_LSTR, p->cur.off, len))
 		goto exit;
 
 	p->cur.off += len;
@@ -1743,18 +1529,17 @@ p_num(struct parctx *p)
 int
 p_nznum(struct parctx *p)
 {
-	size_t wndlag;
 	const char *prp;
 
 	if (p->e > TE_PARSE)
 		goto exit;
 
-	if ((p->e = par_procure(p, p->cur.off, 1)))
-		goto exit;
+	p->e = TE_OK;
 
-	wndlag = p->cur.off - p->wndoff;
-	prp = p->wnd + wndlag;
+	if (par_fillstr(p, p->cur.off + 1) != TE_OK)
+		goto exit;
 
+	prp = p->str + p->cur.off;
 	if (*prp < '1' || *prp > '9')
 		goto eparse;
 
@@ -1792,15 +1577,19 @@ p_qchar(struct parctx *p)
 	if (p->e > TE_PARSE)
 		goto exit;
 
-	if ((p->e = par_peek(p, p->cur.off, &x)))
+	p->e = TE_OK;
+
+	if (par_fillstr(p, p->cur.off + 1) != TE_OK)
 		goto exit;
+	x = p->str[p->cur.off];
 	if (x >= '\1' && x <= '\x7f' && x != '\r' &&
 	    x != '\n' && x != '"' && x != '\\') {
 		len = 1;
 		goto ins;
 	}
-	if ((p->e = par_peek(p, p->cur.off + 1, &y)))
+	if (par_fillstr(p, p->cur.off + 2) != TE_OK)
 		goto exit;
+	y = p->str[p->cur.off + 1];
 	if (x == '\\' && (y == '"' || y == '\\')) {
 		len = 2;
 		goto ins;
@@ -1824,22 +1613,18 @@ p_qchars(struct parctx *p)
 {
 	size_t pr;
 	char *prp;
-	size_t wndlag;
-	size_t leeway;
 	size_t len;
 	int esc;
 
 	if (p->e > TE_PARSE)
 		goto exit;
 
+	p->e = TE_OK;
 	pr = p->cur.off;
 	esc = 0;
 
-	while ((p->e = par_procure(p, pr, 1)) == TE_OK) {
-		wndlag = pr - p->wndoff;
-		leeway = p->wndoff + p->wndlee;
-
-		for (prp = p->wnd + wndlag; pr < leeway; pr++, prp++) {
+	while (par_fillstr(p, pr + 1) == TE_OK) {
+		for (prp = p->str + pr; pr < p->strlen; ++pr, ++prp) {
 			if (esc) {
 				if (*prp == '"' || *prp == '\\') {
 					esc = 0;
@@ -2041,19 +1826,16 @@ p_text(struct parctx *p)
 {
 	size_t pr;
 	char *prp;
-	size_t wndlag;
-	size_t leeway;
 	size_t len;
 
 	if (p->e > TE_PARSE)
 		goto exit;
 
+	p->e = TE_OK;
 	pr = p->cur.off;
-	while ((p->e = par_procure(p, pr, 1)) == TE_OK) {
-		wndlag = pr - p->wndoff;
-		leeway = p->wndoff + p->wndlee;
 
-		for (prp = p->wnd + wndlag; pr < leeway; pr++, prp++) {
+	while (par_fillstr(p, pr + 1) == TE_OK) {
+		for (prp = p->str + pr; pr < p->strlen; ++pr, ++prp) {
 			if (*prp == 0 || *prp > '\x7f' ||
 			    *prp == '\r' || *prp == '\n')
 				goto stop;
diff --git a/parser.h b/parser.h
index 061d427..d897d94 100644
--- a/parser.h
+++ b/parser.h
@@ -56,9 +56,7 @@ union parnode {
 	} str;
 };
 
-struct laxsrc;
-
-int	par_readln(struct laxsrc *, char *, size_t, int, int *,
+int	par_parseln(int, char **, size_t *, size_t *, size_t *,
     union parnode *, size_t);
 union parnode *par_nn(union parnode *);
 union parnode *par_sel(union parnode *, ...);
diff --git a/pshades.c b/pshades.c
index 97b66ca..e6c1645 100644
--- a/pshades.c
+++ b/pshades.c
@@ -4,10 +4,10 @@
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <unistd.h>
 
 #include "errors.h"
-#include "laxsrc.h"
 #include "parser.h"
 
 #define LEN(a) (sizeof(a) / sizeof(a)[0])
@@ -51,16 +51,16 @@ int
 main(int argc, char **argv)
 {
 	int e;
-	struct laxsrc in;
-	char ln[1024];
+	char *ln;
+	size_t cap, len, parsed;
 	union parnode pt[1024];
 
-	laxsrc_init(&in, STDIN_FILENO, LEN(ln));
-	if ((e = par_readln(&in, ln, LEN(ln), -1, 0, pt, LEN(pt))))
+	cap = len = parsed = 0;
+	if ((e = par_parseln(STDIN_FILENO, &ln, &cap, &len, &parsed, pt, LEN(pt))))
 		errx(1, "error parsing input: %s",  errmsgs[e]);
 
 	ptprint(pt, ln, 0);
-	laxsrc_free(&in);
+	free(ln);
 	return 0;
 }
 
diff --git a/tttm.c b/tttm.c
index c876fa5..74ddc80 100644
--- a/tttm.c
+++ b/tttm.c
@@ -10,7 +10,6 @@
 #include <sys/stat.h>
 
 #include "errors.h"
-#include "laxsrc.h"
 #include "parser.h"
 #include "imap.h"
 
@@ -22,7 +21,7 @@
 /* Retry fetching mail at most this many times after getting a nil fetch.  */
 #define MAXREFC 2
 
-static int	pipemsg(struct msgd *, int, char **);
+static int	pipemsg(struct msgd *, char *, char **);
 static int	sink_init(char **, pid_t *, int *);
 static int	sink_free(pid_t, int);
 static void	fin(struct imapctx *, int, const char *);
@@ -30,8 +29,6 @@ static void	terr(int, const char *);
 static void	twarn(int, const char *);
 static void	usage(void);
 
-static size_t	 page;
-static char	 stortpl[] = "/tmp/tttm.XXXXXX";
 static char	*userkey = "IMAP_USER";
 static char	*passkey = "IMAP_PASS";
 
@@ -41,7 +38,6 @@ main(int argc, char **argv)
 	int e;
 	struct imapctx con;
 	char *user, *pass;
-	int stor;
 	int refetchc, batchsz, mpiped;
 	struct msgd bag[BATCHSZ], *m, *bend;
 	long sysvar;
@@ -50,16 +46,7 @@ main(int argc, char **argv)
 		usage();
 	}
 
-	if ((sysvar = sysconf(_SC_PAGESIZE)) == -1)
-		err(1, "failed to find out page size");
-	page = sysvar;
-
-	if ((stor = mkstemp(stortpl)) == -1)
-		err(1, "failed to set up temporary storage");
-	if (unlink(stortpl) == -1)
-		warn("failed to unlink temporary storage");
-
-	if ((e = imap_init(&con, 0, 1, page))) {
+	if ((e = imap_init(&con, STDIN_FILENO, STDOUT_FILENO))) {
 		terr(e, "failed to initialize session");
 	}
 	if (con.state == IS_AUTH) {
@@ -97,13 +84,9 @@ main(int argc, char **argv)
 	}
 	refetchc = 0;
 	while (con.mb.exists > 0) {
-		if (lseek(stor, 0, SEEK_SET) == -1) {
-			warn("failed to rewind temporary storage");
-			goto logout;
-		}
 		batchsz = MIN(BATCHSZ, con.mb.exists);
 		bend = bag + batchsz;
-		e = imap_fetch(&con, 1, batchsz, stor, bag);
+		e = imap_fetch(&con, 1, batchsz, bag);
 		if (e == TE_NIL && refetchc < MAXREFC) {
 			if ((e = imap_nop(&con))) {
 				fin(&con, e, "failed to receive updates");
@@ -115,7 +98,7 @@ main(int argc, char **argv)
 		}
 		refetchc = 0;
 		for (m = bag; m < bend; m++) {
-			if (pipemsg(m, stor, argv + 1) == -1)
+			if (pipemsg(m, con.resp, argv + 1) == -1)
 				break;
 		}
 		mpiped = m - bag;
@@ -145,20 +128,20 @@ main(int argc, char **argv)
 }
 
 int
-pipemsg(struct msgd *m, int stor, char **argv)
+pipemsg(struct msgd *m, char *stor, char **argv)
 {
 	int e, sin;
 	void (*origpipe)(int);
-	size_t scur, send, wlen, wlee;
-	char *wnd, *wcur;
+	char *cur;
+	size_t len;
 	pid_t sid;
 	ssize_t n;
 
 	e = -1;
-	if (lseek(stor, m->off, SEEK_SET) == -1) {
-		warn("failed to seek in temporary file");
-		goto exit;
-	}
+	cur = stor + m->off;
+	len = m->len;
+	n = -2;
+
 	if ((origpipe = signal(SIGPIPE, SIG_IGN)) == SIG_ERR) {
 		warn("failed to install SIGPIPE handler");
 		goto exit;
@@ -166,37 +149,23 @@ pipemsg(struct msgd *m, int stor, char **argv)
 	if (sink_init(argv, &sid, &sin) == -1)
 		goto csig;
 
-	for (scur = m->off, send = scur + m->len; scur < send; scur += wlen) {
-		wlen = MIN(send - scur, page);
-		wnd = mmap(0, wlen, PROT_READ, MAP_PRIVATE, stor, scur);
-		if (wnd == MAP_FAILED) {
-			warn("failed to map temporary file");
-			goto csink;
-		}
-		wcur = wnd;
-		wlee = wlen;
-		while (wlee) {
-			if ((n = write(sin, wcur, wlee)) == 0) {
-				break;
-			} else if (n == -1) {
-				if (errno == EINTR || errno == EAGAIN)
-					continue;
-				else break;
-			}
-			wcur += n;
-			wlee -= n;
-		}
-		if (munmap(wnd, wlen) == -1) {
-			warn("failed to unmap temporary file");
-			goto csink;
-		}
-		if (n == 0) {
-			warn("sink shut down unexpectedly");
-			goto csink;
+	while (len > 0) {
+		if ((n = write(sin, cur, len)) == 0) {
+			break;
 		} else if (n == -1) {
-			warn("failed to write to sink");
-			goto csink;
+			if (errno == EINTR || errno == EAGAIN)
+				continue;
+			else break;
 		}
+		cur += n;
+		len -= n;
+	}
+	if (n == 0) {
+		warn("sink shut down unexpectedly");
+		goto csink;
+	} else if (n == -1) {
+		warn("failed to write to sink");
+		goto csink;
 	}
 	e = 0;
  csink: