git clone https://orangeshoelaces.net/git/tttm.git
Author: Vasily Kolobkov on 04/27/2016
Committer: Vasily Kolobkov on 04/27/2016
Extract parser public interface
imap.c | 795 --------
parse.c | 752 +++++++
parse.h | 33 +
3 files changed, 785 insertions(+), 795 deletions(-)
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)