tttm

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

84f0e9d8dd946650170a1fac866b82014513eec2

Author: Vasily Kolobkov on 04/27/2016

Committer: Vasily Kolobkov on 04/27/2016

Extract parser public interface

Stats

imap.c  | 795 --------
parse.c | 752 +++++++
parse.h |  33 +
3 files changed, 785 insertions(+), 795 deletions(-)

Patch

diff --git a/imap.c b/imap.c
deleted file mode 100644
index 5374836..0000000
--- a/imap.c
+++ /dev/null
@@ -1,795 +0,0 @@
-/*
-   Be warned! Most internal parser functions bear unconventional call
-   semantics returning 1 on success and 0 otherwise. Where applicable
-   specific error code or 0 is dispensed to the parse context.
-*/
-
-#include <stdarg.h>
-#include <strings.h>
-
-#define LEN(x) (sizeof x / sizeof x[0])
-#define INPUT_BUFFER_LENGTH 2048
-
-struct imap_con {
-	int in;
-	int out;
-	char inbuf[INPUT_BUFFER_LENGTH];
-	int inlen;
-};
-
-static struct literal {
-	enum { OK, NO, BAD, SP } kind;
-	char  *val;
-	size_t len;
-} literals[] = {
-	{ ALERT, "ALERT", 5 },
-	{ ANSWERED, "\\Answered", 9 },
-	{ ANYFL, "\\*", 2 },
-	{ ASTERISK, "*", 1 },
-	{ AUTHEQ, "AUTH=", 5 },
-	{ BAD, "BAD", 3 },
-	{ BADCS, "BADCHARSET", 10 },
-	{ CAP, "CAPABILITY", 10 },
-	{ CBRACK, "]", 1 },
-	{ CPAR, ")", 1 },
-	{ DELETED, "\\Deleted", 8 },
-	{ DQUOTE, "\"", 1 },
-	{ DRAFT, "\\Draft", 6 },
-	{ EOL, "\r\n", 2 },
-	{ FLAGGED, "\\Flagged", 8 },
-	{ FLAGS, "FLAGS", 5 },
-	{ LIST, "LIST", 4 },
-	{ NIL, "NIL", 3 },
-	{ NO, "NO", 2 },
-	{ OBRACK, "[", 1 },
-	{ OK, "OK", 2 },
-	{ OPAR, "(", 1 },
-	{ PARSE, "PARSE", 5 },
-	{ PERMFL, "PERMANENTFLAGS", 14 },
-	{ PLUS, "+", 1 },
-	{ RO, "READ-ONLY", 9 },
-	{ RW, "READ-WRITE", 10 },
-	{ SEEN, "\\Seen", 5 },
-	{ SP, " ", 1 },
-	{ TRYC, "TRYCREATE", 9 },
-	{ UIDNEXT, "UIDNEXT", 7 },
-	{ UIDVAL, "UIDVALIDITY", 11 },
-	{ UNSEEN, "UNSEEN", 6 },
-};
-
-struct parse_cur {
-	const char   *tok;
-	union marker *pt;
-};
-
-enum { MK_INT, MK_LEAF, MK_ATOM, ... };
-
-union marker {
-	struct {
-		int    type;
-		size_t len;
-	} hdr;
-	struct {
-		char  *tok;
-		size_t len;
-	} str;
-	struct {
-		char  *tok;
-		size_t len;
-	} lstr;
-	struct {
-		size_t iob;
-		size_t len;
-	} oblstr;
-	struct {
-		char  *tok;
-		size_t len;
-	} qstr;
-	struct {
-		size_t val;
-	} num;
-};
-
-enum { PE_OK, PE_PARSE, PE_NOTENMK };
-
-struct parse_ctx {
-	const char   *tok;
-	const char   *tokend;
-	size_t        toklen;
-	union marker *pt;
-	union marker *ptend;
-	size_t        ptlen;
-	struct parse_cur cur;
-	size_t        oblth;
-	int           e;
-};
-
-typedef int parsefn(struct parse_ctx *);
-
-int
-read_respln(const char *tok, size_t toklen, union marker *pt,
-    size_t ptlen, size_t oblth)
-{
-	struct parse_ctx p = { tok, tok + toklen, toklen, pt,
-	    pt + ptlen, ptlen, { tok, pt }, oblth, 0 };
-	parse_respln(&p);
-	return p->e;
-}
-
-int
-imap_resptype(union marker *pt)
-{
-	return pt[0].hdr.type ^ MK_INT;
-}
-
-/*
-  Combinators and misc meta critters
-*/
-
-int
-chkpoint(struct parse_ctx *p, struct parse_cur *cp)
-{
-	*cp = p->cur;
-	return 1;
-}
-
-int
-markhdr(struct parse_ctx *p, int type)
-{
-	if (p->cur.pt == p->ptend) {
-		p->e = ENOTENMK;
-		return 0;
-	}
-	p->cur.pt.hdr.type = type;
-	p->cur.pt++;
-	return 1;
-}
-
-int
-intbeg(struct parse_ctx *p, struct parse_cur *beg, int type)
-{
-	return chkpoint(p, beg) && markhdr(p, MK_INT | type);
-}
-
-int
-intend(struct parse_ctx *p, struct parse_cur *beg)
-{
-	beg->len = p->cur.mark - beg->mark;
-	return 1;
-}
-
-int
-rollback(struct parse_ctx *p, struct parse_cur *cp)
-{
-	p->cur = *cp;
-	if (!p->e)
-		p->e = EPARSE;
-
-	return 0;
-}
-
-int
-opt(struct parse_ctx *p)
-{
-	if (p->e == EPARSE)
-		p->e = 0;
-
-	return !p->e;
-}
-
-/* prod *(SP prod) */
-int
-parse_list(struct parse_ctx *p, parsefn *prod)
-{
-	int res;
-
-	res = prod(p);
-	while(res && parse_2xcombo(p, SP, prod))
-		;
-	return res && opt(p);
-}
-
-/* 1*prod */
-int
-parse_rep(struct parse_ctx *p, parsefn *prod)
-{
-	int res;
-
-	res = prod(p);
-	while(res && prod(p))
-		;
-	return res && opt(p);
-}
-
-/* 1*<any char except those in `except'> */
-int
-parse_repchr(struct parse_ctx *p, const char *except, int type)
-{
-	char *t;
-	union marker *pt;
-
-	t = p->cur.tok;
-	pt = p->cur.pt;
-
-	while (t != p->tokend && binsearch(except, *t) == -1)
-		t++;
-
-	if (!(t > p->cur.tok)) {
-		p->e = PE_PARSE;
-		goto exit;
-	} else if (p->ptlen < 2 || p->ptend - p->cur.pt < 2) {
-		p->e = PE_NOTENMK;
-		goto exit;
-	}
-
-	pt->hdr.type = MK_LEAF | type;
-	pt->hdr.len = 2;
-	pt++;
-	pt->str.tok = p->cur.tok;
-	pt->str.len = t - p->cur.tok;
-	pt++;
-
-	p->cur.tok = t;
-	p->cur.mark = m;
-	p->e = PE_OK;
-
- exit:
-	return !p->e;
-}
-
-int
-parse_2xcombo(struct parse_ctx *p, int ilit, parsefn *prod)
-{
-	struct parse_cur b;
-
-	return chkpoint(p, &b) && parse_lit(p, ilit) &&
-	    prod(p) || rollback(p, &b);
-}
-
-int
-parse_3xcombo(struct parse_ctx *p, int ilit1, int ilit2, parsefn *prod)
-{
-	struct parse_cur b;
-
-	return chkpoint(p, &b) && parse_lit(p, ilit1) &&
-	    parse_lit(p, ilit2) && prod(p) || rollback(p, &b);
-}
-
-int
-parse_3xcombor(struct parse_ctx *p, parsefn *prod, int ilit1, int ilit2)
-{
-	struct parse_cur b;
-
-	return chkpoint(p, &b) && prod(p) && parse_lit(p, ilit1) &&
-	    parse_lit(p, ilit2) || rollback(p, &b);
-}
-
-/*
-  IMAP parsers
-*/
-
-int
-parse_astring(struct parse_ctx *p)
-{
-	struct parse_cur b;
-
-	return intbeg(p, &b, MK_ASTRING) &&
-	    (parse_repchr(p, astr_specials, MK_ASTRLIT) || parse_string(p)) &&
-	    intend(p, &b) || rollback(p, &b);
-}
-
-int
-parse_atom(struct parse_ctx *p)
-{
-	return parse_repchr(p, atom_specials, MK_ATOM);
-}
-
-int
-parse_authtype(struct parse_ctx *p)
-{
-	return parse_atom(p);
-}
-
-int
-parse_badcscode(struct parse_ctx *p)
-{
-	struct parse_cur b;
-
-	return intbeg(p, &b, MK_BADCSCODE) &&
-	    parse_lit(p, BADCS) && (parse_badcsopt(p) || opt(p)) &&
-	    intend(p, &b) || rollback(p, &b);
-}
-
-int
-parse_badcsopt(struct parse_ctx *p)
-{
-	struct parse_cur b;
-
-	return chkpoint(p, &b) && parse_lit(p, SP) &&
-	    parse_lit(p, OPAR) && parse_list(p, parse_astring) &&
-	    parse_lit(p, CPAR) || rollback(p, &b);
-}
-
-int
-parse_base64(struct parse_ctx *p)
-{
-	/* DO ME */
-}
-
-int
-parse_cap(struct parse_ctx *p)
-{
-	struct parse_cur b;
-
-	return chkpoint(p, &b) && parse_lit(p, SP) &&
-	    (parse_2xcombo(AUTHEQ, parse_authtype, p) || parse_atom(p)) ||
-	    rollback(p, &b);
-}
-
-int
-parse_capdata(struct parse_ctx *p)
-{
-	struct parse_cur b;
-
-	return chkpoint(p, &b) && parse_lit(p, CAP) &&
-	    parse_rep(p, parse_cap) || rollback(p, &b);
-}
-
-int
-parse_contreq(struct parse_ctx *p)
-{
-	struct parse_cur b;
-
-	return chkpoint(p, &b) &&
-	    parse_lit(p, PLUS) && parse_lit(p, SP) &&
-	    (parse_resptext(p) || parse_base64(p)) &&
-	    parse_lit(p, EOL) || rollback(p, &b);
-}
-
-int
-parse_dquotedchar(struct parse_ctx *p)
-{
-	struct parse_cur b;
-
-	return chkpoint(p, &b) && parse_lit(p, DQUOTE) &&
-	    parse_quotedchar(p) && parse_lit(p, DQUOTE) ||
-	    rollback(p, &b);
-}
-
-int
-parse_escqspec(struct parse_ctx *p)
-{
-	struct parse_cur b;
-
-	return chkpoint(p, &b) && parse_lit(p, BSLASH) &&
-	    (parse_lit(p, DQUOTE) || parse_lit(p, BSLASH)) ||
-	    rollback(p, &b);
-}
-
-int
-parse_flag(struct parse_ctx *p)
-{
-	return parse_lit(p, ANSWERED) || parse_lit(p, FLAGGED) ||
-	    parse_lit(p, DELETED) || parse_lit(p, SEEN) ||
-	    parse_lit(p, DRAFT) || parse_flkeyword(p) ||
-	    parse_flext(p);
-}
-
-int
-parse_flext(struct parse_ctx *p)
-{
-	struct parse_cur b;
-
-	return chkpoint(p, &b) && parse_lit(p, BSLASH) &&
-	    parse_atom(p) || rollback(p, &b);
-}
-
-int
-parse_flkeyword(struct parse_ctx *p)
-{
-	return parse_atom(p);
-}
-
-int
-parse_gencode(struct parse_ctx *p)
-{
-	struct parse_cur opt;
-
-	return parse_atom(p) && (chkpoint(p, &opt) &&
-	    parse_lit(p, SP) && parse_genctext(p) ||
-	    rollback(p, &opt) || opt(p));
-}
-
-int
-parse_genctext(struct parse_ctx *p)
-{
-	int res;
-	char *t;
-
-	t = p->cur.ntok;
-
-	while (t != p->tokend && *t >= '\1' && *t <= '\x7f' &&
-	    *t != ']' && *t != '\r' && *t != '\n')
-		t++;
-
-	res = t > p->cur.ntok;
-	if (res)
-		p->cur.ntok = t;
-
-	p->e = !res;
-	return res;
-}
-
-int
-parse_mailbox(struct parse_ctx *p)
-{
-	return parse_lit(p, INBOX) || parse_astring(p);
-}
-
-int
-parse_mbdata(struct parse_ctx *p)
-{
-	return parse_mbflags(p) || parse_mblist(p) ||
-	    parse_mblsub(p) || parse_mbsearch(p) ||
-	    parse_mbstatus(p) || parse_mbexists(p) ||
-	    parse_mbrecent(p);
-}
-
-int
-parse_mbexists(struct parse_ctx *p)
-{
-	return parse_3xcombor(p, parse_number, SP, EXISTS);
-}
-
-int
-parse_mbflags(struct parse_ctx *p)
-{
-	struct parse_cur b;
-
-	rerurn chkpoint(p, &b) && parse_lit(p, FLAGS) &&
-	    parse_lit(p, SP) && parse_lit(p, OPAR) &&
-	    (parse_list(p, parse_flag) || opt(p)) &&
-	    parse_lit(p, CPAR) || rollback(p, &b);
-}
-
-int
-parse_mblist(struct parse_ctx *p)
-{
-	struct parse_cur b;
-
-	return chkpoint(p, &b) && parse_lit(p, LIST) &&
-	    parse_lit(p, SP) && parse_mblistdata(p) ||
-	    rollback(p, &b);
-}
-
-int
-parse_mblistdata(struct parse_ctx *p)
-{
-	struct parse_cur b;
-
-	return chkpoint(p, &b) && parse_lit(p, OPAR) &&
-	    (parse_list(p, parse_mblistfl) || opt(p)) &&
-	    parse_lit(p, CPAR) && parse_lit(p, SP) &&
-	    (parse_dquotedchar(p) || parse_lit(NIL)) &&
-	    parse_lit(p, SP) && parse_mailbox(p) || rollback(p, &b);
-}
-
-/*
-   TODO: will 1*(SFL|OFL) with a soundness check afterwards
-   be simpler and more readable?
-*/
-int
-parse_mblistfl(struct parse_ctx *p)
-{
-	return parse_mblistosfl(p) || parse_list(p, parse_mblistofl);
-}
-
-int
-parse_mblistofl(struct parse_ctx *p)
-{
-	return parse_lit(p, NOINFERIORS) || parse_flext(p);
-}
-
-int
-parse_mblistosfl(struc parse_ctx *p)
-{
-	int res;
-	struct parse_cur b;
-
-	checkpoint(p, &b);
-
-	res = parse_list(p, parse_mblistofl) &&
-	    parse_lit(p, SP) || rollback(p, &b) || opt(p);
-
-	res = res && parse_mblistsfl(p);
-
-	res = res && chkpoint(p, &s) && parse_lit(p, SP) &&
-	    parse_list(p, parse_mblistofl) || rollback(p, &s) || opt(p);
-
-	return res || rollback(p, &b);
-}
-
-int
-parse_mblistsfl(struct parse_ctx *p)
-{
-	return parse_lit(p, NOSEL) || parse_lit(p, MARKED) ||
-	    parse_lit(p, UNMARKED);
-}
-
-int
-parse_mblsub(struct parse_ctx *p)
-{
-	struct parse_cur b;
-
-	return chkpoint(p, &b) && parse_lit(p, LSUB) &&
-	    parse_lit(p, SP) && parse_mblistdata(p) ||
-	    rollback(p, &b);
-}
-
-int
-parse_mbrecent(struct parse_ctx *p)
-{
-	return parse_3xcombor(p, parse_number, SP, RECENT);
-}
-
-int
-parse_mbsearch(struct parse_ctx *p)
-{
-	struct parse_cur b, o;
-
-	return chkpoint(p, &b) && parse_lit(p, SEARCH) &&
-	    (chkpoint(p, &o) && parse_lit(p, SP) &&
-	    parse_list(p, parse_nznumber) || rollback(p, &o) || opt(p)) ||
-	    rollback(p, &b);
-}
-
-int
-parse_mbstatus(struct parse_ctx *p)
-{
-	struct parse_cur b;
-
-	return chkpoint(p, &b) && parse_lit(p, STATUS) && parse_lit(p, SP) &&
-	    parse_mailbox(p) && parse_lit(p, SP) && parse_lit(p, OPAR) &&
-	    (parse_list(p, parse_stattrec) || opt(p)) && parse_lit(p, CPAR) ||
-	    rollback(p, &b);
-}
-
-int
-parse_number(struct parse_ctx *p)
-{
-	/* DO ME */
-}
-
-int
-parse_nznumber(struct parse_ctx *p)
-{
-	int res;
-	char *t;
-
-	t = p->cur.ntok;
-
-	if (t == p->tokend || *t < '1' || *t > '9')
-		goto exit;
-
-	while (t != p->tokend && *t >= '0' && *t <= '9')
-		t++;
-
- exit:
-	res = t > p->cur.ntok;
-	if (res)
-		p->cur.ntok = t;
-
-	p->e = !res;
-	return res;
-}
-
-int
-parse_permfl(struct parse_ctx *p)
-{
-	return parse_flag(p) || parse_lit(p, ANYFL);
-}
-
-int
-parse_permflcode(struct parse_ctx *p)
-{
-	struct parse_cur b;
-
-	return chkpoint(p, &b) && parse_lit(p, PERMFL) &&
-	    parse_lit(p, SP) && parse_lit(p, OPAR) &&
-	    (parse_list(p, parse_permfl) || opt(p)) &&
-	    parse_lit(p, CPAR) || rollback(p, &b);
-}
-
-int
-parse_quotedchar(struct parse_ctx *p)
-{
-	return parse_quotedcharclean(p) || parse_escqspec(p);
-}
-
-int
-parse_quotedcharclean(struct parse_ctx *p)
-{
-	if (p->cur.ntok != p->tokend &&
-	    binsearch(quotedclean, *p->cur.ntok) == -1) {
-		p->cur.ntok++;
-		p->e = 0;
-	} else {
-		p->e = 1;
-	}
-	return !p->e;
-}
-
-int
-parse_quotstr(struct parse_ctx *p)
-{
-	struct parse_cur b;
-
-	return intbeg(p, &b, MK_QUOTSTR) && parse_lit(p, DQUOTE) &&
-	    (parse_rep(p, parse_quotedchar) || opt(p)) &&
-	    parse_lit(p, DQUOTE) || rollback(p, &b);
-}
-
-int
-parse_respbye(struct parse_ctx *p)
-{
-	struct parse_cur b;
-
-	return chkpoint(p, &b) && parse_lit(p, BYE) && parse_lit(p, SP) &&
-	    parse_resptext(p) || rollback(p, &b);
-}
-
-int
-parse_respdata(struct parse_ctx *p)
-{
-	struct parse_cur b;
-
-	return chkpoint(p, &b) && parse_lit(p, ASTERISK) &&
-	    parse_lit(p, SP) && (parse_respstate(p) ||
-	    parse_respbye(p) || parse_mbdata(p) || parse_msgdata(p) ||
-	    parse_capdata(p)) && parse_eol(p) || rollback(p &s);
-}
-
-int
-parse_respln(struct parse_ctx *p)
-{
-	return parse_contreq(p) ||
-	    parse_respdata(p) ||
-	    parse_taggedresp(p);
-}
-
-int
-parse_respstate(struct parse_ctx *p)
-{
-	struct parse_cur b;
-
-	return chkpoint(p, &b) && (parse_lit(p, OK) ||
-	    parse_lit(p, NO) || parse_lit(p, BAD)) &&
-	    parse_lit(p, SP) && parse_resptext(p) ||
-	    rollback(p, &b);
-}
-
-int
-parse_resptext(struct parse_ctx *p)
-{
-	int res;
-	struct parse_cur b;
-
-	res = chkpoint(p, &b) && parse_lit(p, OBRACK) &&
-	    parse_resptextcode(p) && parse_lit(p, CBRACK) &&
-	    parse_lit(p, SP) || rollback(p, &b) || opt(p);
-
-	return res && parse_text(p);
-}
-
-int
-parse_resptextcode(struct parse_ctx *p)
-{
-	return parse_lit(p, ALERT) || parse_badcscode(p) ||
-	    parse_capdata(p) || parse_lit(p, PARSE) ||
-	    parse_permflcode(p) || parse_lit(p, RO) ||
-	    parse_lit(p, RW) || parse_lit(p, TRYC) ||
-	    parse_uidncode(p) || parse_uidvcode(p) ||
-	    parse_unseencode(p) || parse_gencode(p);
-}
-
-int
-parse_statt(struct parse_ctx *p)
-{
-	return parse_lit(p, MESSAGES) || parse_lit(p, RECENT) ||
-	    parse_lit(p, UIDNEXT) || parse_lit(p, UIDVAL) ||
-	    parse_lit(p, UNSEEN);
-}
-
-int
-parse_stattrec(struct parse_ctx *p)
-{
-	struct parse_cur b;
-
-	return chkpoint(p, &b) && parse_statt(p) && parse_lit(p, SP) &&
-	    parse_number(p) || rollback(p, &b);
-}
-
-int
-parse_string(struct parse_ctx *p)
-{
-	return parse_qoutstr(p) || parse_litstr(p);
-}
-
-int
-parse_tag(struct parse_ctx *p)
-{
-	return parse_repchr(p, tag_specials);
-}
-
-int
-parse_taggedresp(struct parse_ctx *p)
-{
-	struct parse_cur b;
-
-	return chkpoint(p, &b) && parse_tag(p) && parse_lit(p, SP) &&
-	    parse_respstate(p) && parse_eol(p) ||
-	    rollback(p, &b);
-}
-
-int
-parse_lit(int i, struct parse_ctx *p)
-{
-	size_t left;
-
-	left = p->toklen - (p->cur.ntok - p->tokens);
-	if (left < literals[i].len) {
-		p->e = 1;
-		goto exit;
-	}
-	if (strncasecmp(p->cur.ntok, literals[i].val, literals[i].len) == 0) {
-		if (p->cur.nact == p->actend)) {
-			p->e = 2;
-			goto exit;
-		}
-		*p->cur.nact++ = { ... };
-		p->cur.ntok += literals[i].len;
-		p->e = 0;
-	} else {
-		p->e = 1;
-		goto exit;
-	}
- exit:
-	return !p->e;
-}
-
-int
-parse_litstr(struct parse_ctx *p)
-{
-	int res;
-	size_t len;
-	struct parse_cur b;
-
-	res = intbeg(p, MK_LITSTR, &b) && par_lit(p, OBRACE) &&
-	    par_number(p) && par_lit(p, CBRACE) && par_lit(p, EOL) &&
-	    intend(p, &b) || rollback(p, &b);
-
-	if (res) {
-		len = b.pt[4].num.val;
-		if (len < p->oblth)
-			p->cur.tok += len;
-	}
-	return res;
-}
-
-int
-parse_uidncode(struct parse_ctx *p)
-{
-	return parse_3xcombo(UIDNEXT, SP, parse_nznumber);
-}
-
-int
-parse_uidvcode(struct parse_ctx *p)
-{
-	return parse_3xcombo(UIDVAL, SP, parse_nznumber);
-}
-
-int
-parse_unseencode(struct parse_ctx *p)
-{
-	return parse_3xcombo(UNSEEN, SP, parse_nznumber);
-}
diff --git a/parse.c b/parse.c
new file mode 100644
index 0000000..7ff67d2
--- /dev/null
+++ b/parse.c
@@ -0,0 +1,752 @@
+/*
+   Be warned! Most internal parser functions bear unconventional call
+   semantics returning 1 on success and 0 otherwise. Where applicable
+   specific error code or 0 is dispensed to the parse context.
+*/
+
+#include <strings.h>
+
+#include "parse.h"
+
+#define LEN(x) (sizeof x / sizeof x[0])
+
+static struct literal {
+	enum { OK, NO, BAD, SP } kind;
+	char  *val;
+	size_t len;
+} literals[] = {
+	{ ALERT, "ALERT", 5 },
+	{ ANSWERED, "\\Answered", 9 },
+	{ ANYFL, "\\*", 2 },
+	{ ASTERISK, "*", 1 },
+	{ AUTHEQ, "AUTH=", 5 },
+	{ BAD, "BAD", 3 },
+	{ BADCS, "BADCHARSET", 10 },
+	{ CAP, "CAPABILITY", 10 },
+	{ CBRACK, "]", 1 },
+	{ CPAR, ")", 1 },
+	{ DELETED, "\\Deleted", 8 },
+	{ DQUOTE, "\"", 1 },
+	{ DRAFT, "\\Draft", 6 },
+	{ EOL, "\r\n", 2 },
+	{ FLAGGED, "\\Flagged", 8 },
+	{ FLAGS, "FLAGS", 5 },
+	{ LIST, "LIST", 4 },
+	{ NIL, "NIL", 3 },
+	{ NO, "NO", 2 },
+	{ OBRACK, "[", 1 },
+	{ OK, "OK", 2 },
+	{ OPAR, "(", 1 },
+	{ PARSE, "PARSE", 5 },
+	{ PERMFL, "PERMANENTFLAGS", 14 },
+	{ PLUS, "+", 1 },
+	{ RO, "READ-ONLY", 9 },
+	{ RW, "READ-WRITE", 10 },
+	{ SEEN, "\\Seen", 5 },
+	{ SP, " ", 1 },
+	{ TRYC, "TRYCREATE", 9 },
+	{ UIDNEXT, "UIDNEXT", 7 },
+	{ UIDVAL, "UIDVALIDITY", 11 },
+	{ UNSEEN, "UNSEEN", 6 },
+};
+
+struct parse_cur {
+	const char   *tok;
+	union marker *pt;
+};
+
+struct parse_ctx {
+	const char   *tok;
+	const char   *tokend;
+	size_t        toklen;
+	union marker *pt;
+	union marker *ptend;
+	size_t        ptlen;
+	struct parse_cur cur;
+	size_t        oblth;
+	int           e;
+};
+
+typedef int parsefn(struct parse_ctx *);
+
+int
+parse_respln(const char *tok, size_t toklen, union marker *pt,
+    size_t ptlen, size_t oblth)
+{
+	struct parse_ctx p = { tok, tok + toklen, toklen, pt,
+	    pt + ptlen, ptlen, { tok, pt }, oblth, 0 };
+	parse_respln(&p);
+	return p->e;
+}
+
+/*
+  Combinators and misc meta critters
+*/
+
+int
+chkpoint(struct parse_ctx *p, struct parse_cur *cp)
+{
+	*cp = p->cur;
+	return 1;
+}
+
+int
+markhdr(struct parse_ctx *p, int type)
+{
+	if (p->cur.pt == p->ptend) {
+		p->e = ENOTENMK;
+		return 0;
+	}
+	p->cur.pt.hdr.type = type;
+	p->cur.pt++;
+	return 1;
+}
+
+int
+intbeg(struct parse_ctx *p, struct parse_cur *beg, int type)
+{
+	return chkpoint(p, beg) && markhdr(p, MK_INT | type);
+}
+
+int
+intend(struct parse_ctx *p, struct parse_cur *beg)
+{
+	beg->len = p->cur.mark - beg->mark;
+	return 1;
+}
+
+int
+rollback(struct parse_ctx *p, struct parse_cur *cp)
+{
+	p->cur = *cp;
+	if (!p->e)
+		p->e = EPARSE;
+
+	return 0;
+}
+
+int
+opt(struct parse_ctx *p)
+{
+	if (p->e == EPARSE)
+		p->e = 0;
+
+	return !p->e;
+}
+
+/* prod *(SP prod) */
+int
+parse_list(struct parse_ctx *p, parsefn *prod)
+{
+	int res;
+
+	res = prod(p);
+	while(res && parse_2xcombo(p, SP, prod))
+		;
+	return res && opt(p);
+}
+
+/* 1*prod */
+int
+parse_rep(struct parse_ctx *p, parsefn *prod)
+{
+	int res;
+
+	res = prod(p);
+	while(res && prod(p))
+		;
+	return res && opt(p);
+}
+
+/* 1*<any char except those in `except'> */
+int
+parse_repchr(struct parse_ctx *p, const char *except, int type)
+{
+	char *t;
+	union marker *pt;
+
+	t = p->cur.tok;
+	pt = p->cur.pt;
+
+	while (t != p->tokend && binsearch(except, *t) == -1)
+		t++;
+
+	if (!(t > p->cur.tok)) {
+		p->e = PE_PARSE;
+		goto exit;
+	} else if (p->ptlen < 2 || p->ptend - p->cur.pt < 2) {
+		p->e = PE_NOTENMK;
+		goto exit;
+	}
+
+	pt->hdr.type = MK_LEAF | type;
+	pt->hdr.len = 2;
+	pt++;
+	pt->str.tok = p->cur.tok;
+	pt->str.len = t - p->cur.tok;
+	pt++;
+
+	p->cur.tok = t;
+	p->cur.mark = m;
+	p->e = PE_OK;
+
+ exit:
+	return !p->e;
+}
+
+int
+parse_2xcombo(struct parse_ctx *p, int ilit, parsefn *prod)
+{
+	struct parse_cur b;
+
+	return chkpoint(p, &b) && parse_lit(p, ilit) &&
+	    prod(p) || rollback(p, &b);
+}
+
+int
+parse_3xcombo(struct parse_ctx *p, int ilit1, int ilit2, parsefn *prod)
+{
+	struct parse_cur b;
+
+	return chkpoint(p, &b) && parse_lit(p, ilit1) &&
+	    parse_lit(p, ilit2) && prod(p) || rollback(p, &b);
+}
+
+int
+parse_3xcombor(struct parse_ctx *p, parsefn *prod, int ilit1, int ilit2)
+{
+	struct parse_cur b;
+
+	return chkpoint(p, &b) && prod(p) && parse_lit(p, ilit1) &&
+	    parse_lit(p, ilit2) || rollback(p, &b);
+}
+
+/*
+  IMAP parsers
+*/
+
+int
+parse_astring(struct parse_ctx *p)
+{
+	struct parse_cur b;
+
+	return intbeg(p, &b, MK_ASTRING) &&
+	    (parse_repchr(p, astr_specials, MK_ASTRLIT) || parse_string(p)) &&
+	    intend(p, &b) || rollback(p, &b);
+}
+
+int
+parse_atom(struct parse_ctx *p)
+{
+	return parse_repchr(p, atom_specials, MK_ATOM);
+}
+
+int
+parse_authtype(struct parse_ctx *p)
+{
+	return parse_atom(p);
+}
+
+int
+parse_badcscode(struct parse_ctx *p)
+{
+	struct parse_cur b;
+
+	return intbeg(p, &b, MK_BADCSCODE) &&
+	    parse_lit(p, BADCS) && (parse_badcsopt(p) || opt(p)) &&
+	    intend(p, &b) || rollback(p, &b);
+}
+
+int
+parse_badcsopt(struct parse_ctx *p)
+{
+	struct parse_cur b;
+
+	return chkpoint(p, &b) && parse_lit(p, SP) &&
+	    parse_lit(p, OPAR) && parse_list(p, parse_astring) &&
+	    parse_lit(p, CPAR) || rollback(p, &b);
+}
+
+int
+parse_base64(struct parse_ctx *p)
+{
+	/* DO ME */
+}
+
+int
+parse_cap(struct parse_ctx *p)
+{
+	struct parse_cur b;
+
+	return chkpoint(p, &b) && parse_lit(p, SP) &&
+	    (parse_2xcombo(AUTHEQ, parse_authtype, p) || parse_atom(p)) ||
+	    rollback(p, &b);
+}
+
+int
+parse_capdata(struct parse_ctx *p)
+{
+	struct parse_cur b;
+
+	return chkpoint(p, &b) && parse_lit(p, CAP) &&
+	    parse_rep(p, parse_cap) || rollback(p, &b);
+}
+
+int
+parse_contreq(struct parse_ctx *p)
+{
+	struct parse_cur b;
+
+	return chkpoint(p, &b) &&
+	    parse_lit(p, PLUS) && parse_lit(p, SP) &&
+	    (parse_resptext(p) || parse_base64(p)) &&
+	    parse_lit(p, EOL) || rollback(p, &b);
+}
+
+int
+parse_dquotedchar(struct parse_ctx *p)
+{
+	struct parse_cur b;
+
+	return chkpoint(p, &b) && parse_lit(p, DQUOTE) &&
+	    parse_quotedchar(p) && parse_lit(p, DQUOTE) ||
+	    rollback(p, &b);
+}
+
+int
+parse_escqspec(struct parse_ctx *p)
+{
+	struct parse_cur b;
+
+	return chkpoint(p, &b) && parse_lit(p, BSLASH) &&
+	    (parse_lit(p, DQUOTE) || parse_lit(p, BSLASH)) ||
+	    rollback(p, &b);
+}
+
+int
+parse_flag(struct parse_ctx *p)
+{
+	return parse_lit(p, ANSWERED) || parse_lit(p, FLAGGED) ||
+	    parse_lit(p, DELETED) || parse_lit(p, SEEN) ||
+	    parse_lit(p, DRAFT) || parse_flkeyword(p) ||
+	    parse_flext(p);
+}
+
+int
+parse_flext(struct parse_ctx *p)
+{
+	struct parse_cur b;
+
+	return chkpoint(p, &b) && parse_lit(p, BSLASH) &&
+	    parse_atom(p) || rollback(p, &b);
+}
+
+int
+parse_flkeyword(struct parse_ctx *p)
+{
+	return parse_atom(p);
+}
+
+int
+parse_gencode(struct parse_ctx *p)
+{
+	struct parse_cur opt;
+
+	return parse_atom(p) && (chkpoint(p, &opt) &&
+	    parse_lit(p, SP) && parse_genctext(p) ||
+	    rollback(p, &opt) || opt(p));
+}
+
+int
+parse_genctext(struct parse_ctx *p)
+{
+	int res;
+	char *t;
+
+	t = p->cur.ntok;
+
+	while (t != p->tokend && *t >= '\1' && *t <= '\x7f' &&
+	    *t != ']' && *t != '\r' && *t != '\n')
+		t++;
+
+	res = t > p->cur.ntok;
+	if (res)
+		p->cur.ntok = t;
+
+	p->e = !res;
+	return res;
+}
+
+int
+parse_mailbox(struct parse_ctx *p)
+{
+	return parse_lit(p, INBOX) || parse_astring(p);
+}
+
+int
+parse_mbdata(struct parse_ctx *p)
+{
+	return parse_mbflags(p) || parse_mblist(p) ||
+	    parse_mblsub(p) || parse_mbsearch(p) ||
+	    parse_mbstatus(p) || parse_mbexists(p) ||
+	    parse_mbrecent(p);
+}
+
+int
+parse_mbexists(struct parse_ctx *p)
+{
+	return parse_3xcombor(p, parse_number, SP, EXISTS);
+}
+
+int
+parse_mbflags(struct parse_ctx *p)
+{
+	struct parse_cur b;
+
+	rerurn chkpoint(p, &b) && parse_lit(p, FLAGS) &&
+	    parse_lit(p, SP) && parse_lit(p, OPAR) &&
+	    (parse_list(p, parse_flag) || opt(p)) &&
+	    parse_lit(p, CPAR) || rollback(p, &b);
+}
+
+int
+parse_mblist(struct parse_ctx *p)
+{
+	struct parse_cur b;
+
+	return chkpoint(p, &b) && parse_lit(p, LIST) &&
+	    parse_lit(p, SP) && parse_mblistdata(p) ||
+	    rollback(p, &b);
+}
+
+int
+parse_mblistdata(struct parse_ctx *p)
+{
+	struct parse_cur b;
+
+	return chkpoint(p, &b) && parse_lit(p, OPAR) &&
+	    (parse_list(p, parse_mblistfl) || opt(p)) &&
+	    parse_lit(p, CPAR) && parse_lit(p, SP) &&
+	    (parse_dquotedchar(p) || parse_lit(NIL)) &&
+	    parse_lit(p, SP) && parse_mailbox(p) || rollback(p, &b);
+}
+
+/*
+   TODO: will 1*(SFL|OFL) with a soundness check afterwards
+   be simpler and more readable?
+*/
+int
+parse_mblistfl(struct parse_ctx *p)
+{
+	return parse_mblistosfl(p) || parse_list(p, parse_mblistofl);
+}
+
+int
+parse_mblistofl(struct parse_ctx *p)
+{
+	return parse_lit(p, NOINFERIORS) || parse_flext(p);
+}
+
+int
+parse_mblistosfl(struc parse_ctx *p)
+{
+	int res;
+	struct parse_cur b;
+
+	checkpoint(p, &b);
+
+	res = parse_list(p, parse_mblistofl) &&
+	    parse_lit(p, SP) || rollback(p, &b) || opt(p);
+
+	res = res && parse_mblistsfl(p);
+
+	res = res && chkpoint(p, &s) && parse_lit(p, SP) &&
+	    parse_list(p, parse_mblistofl) || rollback(p, &s) || opt(p);
+
+	return res || rollback(p, &b);
+}
+
+int
+parse_mblistsfl(struct parse_ctx *p)
+{
+	return parse_lit(p, NOSEL) || parse_lit(p, MARKED) ||
+	    parse_lit(p, UNMARKED);
+}
+
+int
+parse_mblsub(struct parse_ctx *p)
+{
+	struct parse_cur b;
+
+	return chkpoint(p, &b) && parse_lit(p, LSUB) &&
+	    parse_lit(p, SP) && parse_mblistdata(p) ||
+	    rollback(p, &b);
+}
+
+int
+parse_mbrecent(struct parse_ctx *p)
+{
+	return parse_3xcombor(p, parse_number, SP, RECENT);
+}
+
+int
+parse_mbsearch(struct parse_ctx *p)
+{
+	struct parse_cur b, o;
+
+	return chkpoint(p, &b) && parse_lit(p, SEARCH) &&
+	    (chkpoint(p, &o) && parse_lit(p, SP) &&
+	    parse_list(p, parse_nznumber) || rollback(p, &o) || opt(p)) ||
+	    rollback(p, &b);
+}
+
+int
+parse_mbstatus(struct parse_ctx *p)
+{
+	struct parse_cur b;
+
+	return chkpoint(p, &b) && parse_lit(p, STATUS) && parse_lit(p, SP) &&
+	    parse_mailbox(p) && parse_lit(p, SP) && parse_lit(p, OPAR) &&
+	    (parse_list(p, parse_stattrec) || opt(p)) && parse_lit(p, CPAR) ||
+	    rollback(p, &b);
+}
+
+int
+parse_number(struct parse_ctx *p)
+{
+	/* DO ME */
+}
+
+int
+parse_nznumber(struct parse_ctx *p)
+{
+	int res;
+	char *t;
+
+	t = p->cur.ntok;
+
+	if (t == p->tokend || *t < '1' || *t > '9')
+		goto exit;
+
+	while (t != p->tokend && *t >= '0' && *t <= '9')
+		t++;
+
+ exit:
+	res = t > p->cur.ntok;
+	if (res)
+		p->cur.ntok = t;
+
+	p->e = !res;
+	return res;
+}
+
+int
+parse_permfl(struct parse_ctx *p)
+{
+	return parse_flag(p) || parse_lit(p, ANYFL);
+}
+
+int
+parse_permflcode(struct parse_ctx *p)
+{
+	struct parse_cur b;
+
+	return chkpoint(p, &b) && parse_lit(p, PERMFL) &&
+	    parse_lit(p, SP) && parse_lit(p, OPAR) &&
+	    (parse_list(p, parse_permfl) || opt(p)) &&
+	    parse_lit(p, CPAR) || rollback(p, &b);
+}
+
+int
+parse_quotedchar(struct parse_ctx *p)
+{
+	return parse_quotedcharclean(p) || parse_escqspec(p);
+}
+
+int
+parse_quotedcharclean(struct parse_ctx *p)
+{
+	if (p->cur.ntok != p->tokend &&
+	    binsearch(quotedclean, *p->cur.ntok) == -1) {
+		p->cur.ntok++;
+		p->e = 0;
+	} else {
+		p->e = 1;
+	}
+	return !p->e;
+}
+
+int
+parse_quotstr(struct parse_ctx *p)
+{
+	struct parse_cur b;
+
+	return intbeg(p, &b, MK_QUOTSTR) && parse_lit(p, DQUOTE) &&
+	    (parse_rep(p, parse_quotedchar) || opt(p)) &&
+	    parse_lit(p, DQUOTE) || rollback(p, &b);
+}
+
+int
+parse_respbye(struct parse_ctx *p)
+{
+	struct parse_cur b;
+
+	return chkpoint(p, &b) && parse_lit(p, BYE) && parse_lit(p, SP) &&
+	    parse_resptext(p) || rollback(p, &b);
+}
+
+int
+parse_respdata(struct parse_ctx *p)
+{
+	struct parse_cur b;
+
+	return chkpoint(p, &b) && parse_lit(p, ASTERISK) &&
+	    parse_lit(p, SP) && (parse_respstate(p) ||
+	    parse_respbye(p) || parse_mbdata(p) || parse_msgdata(p) ||
+	    parse_capdata(p)) && parse_eol(p) || rollback(p &s);
+}
+
+int
+parse_respln(struct parse_ctx *p)
+{
+	return parse_contreq(p) ||
+	    parse_respdata(p) ||
+	    parse_taggedresp(p);
+}
+
+int
+parse_respstate(struct parse_ctx *p)
+{
+	struct parse_cur b;
+
+	return chkpoint(p, &b) && (parse_lit(p, OK) ||
+	    parse_lit(p, NO) || parse_lit(p, BAD)) &&
+	    parse_lit(p, SP) && parse_resptext(p) ||
+	    rollback(p, &b);
+}
+
+int
+parse_resptext(struct parse_ctx *p)
+{
+	int res;
+	struct parse_cur b;
+
+	res = chkpoint(p, &b) && parse_lit(p, OBRACK) &&
+	    parse_resptextcode(p) && parse_lit(p, CBRACK) &&
+	    parse_lit(p, SP) || rollback(p, &b) || opt(p);
+
+	return res && parse_text(p);
+}
+
+int
+parse_resptextcode(struct parse_ctx *p)
+{
+	return parse_lit(p, ALERT) || parse_badcscode(p) ||
+	    parse_capdata(p) || parse_lit(p, PARSE) ||
+	    parse_permflcode(p) || parse_lit(p, RO) ||
+	    parse_lit(p, RW) || parse_lit(p, TRYC) ||
+	    parse_uidncode(p) || parse_uidvcode(p) ||
+	    parse_unseencode(p) || parse_gencode(p);
+}
+
+int
+parse_statt(struct parse_ctx *p)
+{
+	return parse_lit(p, MESSAGES) || parse_lit(p, RECENT) ||
+	    parse_lit(p, UIDNEXT) || parse_lit(p, UIDVAL) ||
+	    parse_lit(p, UNSEEN);
+}
+
+int
+parse_stattrec(struct parse_ctx *p)
+{
+	struct parse_cur b;
+
+	return chkpoint(p, &b) && parse_statt(p) && parse_lit(p, SP) &&
+	    parse_number(p) || rollback(p, &b);
+}
+
+int
+parse_string(struct parse_ctx *p)
+{
+	return parse_qoutstr(p) || parse_litstr(p);
+}
+
+int
+parse_tag(struct parse_ctx *p)
+{
+	return parse_repchr(p, tag_specials);
+}
+
+int
+parse_taggedresp(struct parse_ctx *p)
+{
+	struct parse_cur b;
+
+	return chkpoint(p, &b) && parse_tag(p) && parse_lit(p, SP) &&
+	    parse_respstate(p) && parse_eol(p) ||
+	    rollback(p, &b);
+}
+
+int
+parse_lit(int i, struct parse_ctx *p)
+{
+	size_t left;
+
+	left = p->toklen - (p->cur.ntok - p->tokens);
+	if (left < literals[i].len) {
+		p->e = 1;
+		goto exit;
+	}
+	if (strncasecmp(p->cur.ntok, literals[i].val, literals[i].len) == 0) {
+		if (p->cur.nact == p->actend)) {
+			p->e = 2;
+			goto exit;
+		}
+		*p->cur.nact++ = { ... };
+		p->cur.ntok += literals[i].len;
+		p->e = 0;
+	} else {
+		p->e = 1;
+		goto exit;
+	}
+ exit:
+	return !p->e;
+}
+
+int
+parse_litstr(struct parse_ctx *p)
+{
+	int res;
+	size_t len;
+	struct parse_cur b;
+
+	res = intbeg(p, MK_LITSTR, &b) && par_lit(p, OBRACE) &&
+	    par_number(p) && par_lit(p, CBRACE) && par_lit(p, EOL) &&
+	    intend(p, &b) || rollback(p, &b);
+
+	if (res) {
+		len = b.pt[4].num.val;
+		if (len < p->oblth)
+			p->cur.tok += len;
+	}
+	return res;
+}
+
+int
+parse_uidncode(struct parse_ctx *p)
+{
+	return parse_3xcombo(UIDNEXT, SP, parse_nznumber);
+}
+
+int
+parse_uidvcode(struct parse_ctx *p)
+{
+	return parse_3xcombo(UIDVAL, SP, parse_nznumber);
+}
+
+int
+parse_unseencode(struct parse_ctx *p)
+{
+	return parse_3xcombo(UNSEEN, SP, parse_nznumber);
+}
diff --git a/parse.h b/parse.h
new file mode 100644
index 0000000..e576636
--- /dev/null
+++ b/parse.h
@@ -0,0 +1,33 @@
+union marker {
+	struct {
+		int    type;
+		int    aux;
+		size_t len;
+	} hdr;
+	struct {
+		char  *tok;
+		size_t len;
+	} str;
+	struct {
+		char  *tok;
+		size_t len;
+	} lstr;
+	struct {
+		size_t iob;
+		size_t len;
+	} oblstr;
+	struct {
+		char  *tok;
+		size_t len;
+	} qstr;
+	struct {
+		size_t val;
+	} num;
+};
+
+enum { PE_OK, PE_PARSE, PE_NOTENMK };
+
+enum { MK_INT, MK_LEAF, MK_ATOM, ... };
+
+int parse_respln(const char *tok, size_t toklen, union marker *pt,
+    size_t ptlen, size_t oblth)