rfc822 final touch
[apps/madmutt.git] / lib-mime / rfc822.c
index 708955b..22e54a4 100644 (file)
@@ -84,45 +84,51 @@ address_t *address_list_dup(address_t *addr)
 }
 
 
 }
 
 
-static void rfc822_dequote_comment(char *s)
-{
-    char *w = s;
+/****************************************************************************/
+/* Parsing functions                                                        */
+/****************************************************************************/
 
 
-    for (; *s; s++) {
-        if (*s == '\\') {
-            /* if *++s is NUL that's an error, but we don't care */
-            *w++ = *++s;
-        } else
-        if (*s != '\"') {
-            *w++ = *s;
-        }
+typedef struct static_buf {
+    char buf[STRING];
+    int  len;
+} static_buf;
+
+static inline void stbuf_append(static_buf *buf, int c) {
+    if (buf->len < ssizeof(buf->buf) - 1) {
+        buf->buf[buf->len++] = c;
+        buf->buf[buf->len]   = '\0';
     }
     }
-    *w = 0;
 }
 
 }
 
+static inline void stbuf_append_sp(static_buf *buf) {
+    if (buf->len)
+        stbuf_append(buf, ' ');
+}
 
 
-/****************************************************************************/
-/* Parsing functions                                                        */
-/****************************************************************************/
-
-struct rfc822_parse_ctx {
-    address_t *cur;
+static char *rfc822_dequote_comment(static_buf *buf)
+{
+    char *res = p_new(char, buf->len + 1);
+    char *q   = res;
+    char *p   = buf->buf;
+    int i;
 
 
-    char comment[STRING];
-    size_t commentlen;
+    for (i = 0; i < buf->len; i++) {
+        if (p[i] == '\"')
+            continue;
 
 
-    char phrase[STRING];
-    size_t phraselen;
-};
+        if (p[i] == '\\') {
+            if (++i >= buf->len) /* should not happen */
+                break;
+        }
 
 
-#define is_special(x) strchr(RFC822Specials,x)
-#define terminate_string(a, b, c)  (a[MIN(b, c)] = 0)
-#define terminate_buffer(a)        terminate_string(a, a##len, sizeof (a) - 1)
+        *q++ = p[i];
+    }
 
 
+    *q++ = '\0';
+    return res;
+}
 
 
-static const char *
-parse_comment(const char *s, char *comment, size_t *commentlen,
-              size_t commentmax)
+static const char *parse_comment(const char *s, static_buf *buf)
 {
     int level = 1;
 
 {
     int level = 1;
 
@@ -146,70 +152,60 @@ parse_comment(const char *s, char *comment, size_t *commentlen,
             break;
         }
 
             break;
         }
 
-        if (*commentlen < commentmax)
-            comment[(*commentlen)++] = *s;
+        stbuf_append(buf, *s);
     }
 
     return NULL;
 }
 
     }
 
     return NULL;
 }
 
-static const char *
-parse_quote(const char *s, char *token, size_t *tokenlen, size_t tokenmax)
+static const char *parse_quote(const char *s, static_buf *buf)
 {
 {
-    if (*tokenlen < tokenmax)
-        token[(*tokenlen)++] = '"';
-
     for (; *s; s++) {
     for (; *s; s++) {
-        if (*tokenlen < tokenmax)
-            token[*tokenlen] = *s;
-
-        if (*s == '"') {
-            (*tokenlen)++;
+        switch (*s) {
+          case '"':
+            stbuf_append(buf, *s);
             return s + 1;
             return s + 1;
-        }
 
 
-        if (*s == '\\') {
+          case '\\':
             if (!*++s)
             if (!*++s)
-                break;
+                return NULL;
+            /* fallthrough */
 
 
-            if (*tokenlen < tokenmax)
-                token[*tokenlen] = *s;
+          default:
+            stbuf_append(buf, *s);
+            break;
         }
         }
-        (*tokenlen)++;
     }
 
     return NULL;
 }
 
     }
 
     return NULL;
 }
 
-static const char *
-next_token(const char *s, char *token, size_t *tokenlen, size_t tokenmax)
-{
-    if (*s == '(')
-        return parse_comment(s + 1, token, tokenlen, tokenmax);
+#define is_special(x)         strchr(RFC822Specials,x)
 
 
-    if (*s == '"')
-        return parse_quote(s + 1, token, tokenlen, tokenmax);
+static const char *next_phrase(const char *s, static_buf *buf)
+{
+    if (*s == '"') {
+        stbuf_append(buf, '"');
+        return parse_quote(s + 1, buf);
+    }
 
     if (is_special(*s)) {
 
     if (is_special(*s)) {
-        if (*tokenlen < tokenmax)
-            token[(*tokenlen)++] = *s;
+        stbuf_append(buf, *s);
         return s + 1;
     }
 
     while (*s) {
         if (ISSPACE(*s) || is_special(*s))
             break;
         return s + 1;
     }
 
     while (*s) {
         if (ISSPACE(*s) || is_special(*s))
             break;
-        if (*tokenlen < tokenmax)
-            token[(*tokenlen)++] = *s;
-        s++;
+        stbuf_append(buf, *s++);
     }
     }
+
     return s;
 }
 
 static const char *
     return s;
 }
 
 static const char *
-parse_mailboxdomain(const char *s, const char *nonspecial,
-                    char *mailbox, size_t *mailboxlen, size_t mailboxmax,
-                    struct rfc822_parse_ctx *ctx)
+parse_mailboxdomain(const char *s, const char *nonspecial, static_buf *mbox,
+                    static_buf *comment)
 {
     while (*s) {
         s = skipspaces(s);
 {
     while (*s) {
         s = skipspaces(s);
@@ -218,11 +214,10 @@ parse_mailboxdomain(const char *s, const char *nonspecial,
             return s;
 
         if (*s == '(') {
             return s;
 
         if (*s == '(') {
-            if (ctx->commentlen && ctx->commentlen < sizeof(ctx->comment) - 1)
-                ctx->comment[ctx->commentlen++] = ' ';
-            s = next_token(s, ctx->comment, &ctx->commentlen, sizeof(ctx->comment) - 1);
+            stbuf_append_sp(comment);
+            s = parse_comment(s + 1, comment);
         } else {
         } else {
-            s = next_token(s, mailbox, mailboxlen, mailboxmax);
+            s = next_phrase(s, mbox);
         }
 
         if (!s)
         }
 
         if (!s)
@@ -233,81 +228,68 @@ parse_mailboxdomain(const char *s, const char *nonspecial,
 }
 
 static const char *
 }
 
 static const char *
-parse_address(const char *s, struct rfc822_parse_ctx *ctx)
+parse_address(const char *s, static_buf *comment, address_t *cur)
 {
 {
-    char token[STRING];
-    size_t tokenlen = 0;
+    static_buf token = {"", 0};
 
 
-    s = parse_mailboxdomain(s, ".\"(\\",
-                            token, &tokenlen, sizeof(token) - 1, ctx);
+    s = parse_mailboxdomain(s, ".\"(\\", &token, comment);
     if (!s)
         return NULL;
 
     if (*s == '@') {
     if (!s)
         return NULL;
 
     if (*s == '@') {
-        if (tokenlen < sizeof(token) - 1)
-            token[tokenlen++] = '@';
-        s = parse_mailboxdomain(s + 1, ".([]\\",
-                                token, &tokenlen, sizeof(token) - 1, ctx);
+        stbuf_append(&token, '@');
+        s = parse_mailboxdomain(s + 1, ".([]\\", &token, comment);
         if (!s)
             return NULL;
     }
 
         if (!s)
             return NULL;
     }
 
-    terminate_buffer(token);
-    ctx->cur->mailbox = m_strdup(token);
+    cur->mailbox = p_dupstr(token.buf, token.len);
 
 
-    if (ctx->commentlen && !ctx->cur->personal) {
-        terminate_buffer(ctx->comment);
-        ctx->cur->personal = m_strdup(ctx->comment);
+    if (comment->len && !cur->personal) {
+        cur->personal = p_dupstr(comment->buf, comment->len);
     }
 
     return s;
 }
 
     }
 
     return s;
 }
 
-address_t **add_addrspec(address_t **last, struct rfc822_parse_ctx *ctx)
+address_t **rfc822_eotoken(address_t **last, static_buf *phrase, static_buf *comment)
 {
 {
-    const char *s;
+    if (phrase->len) {
+        const char *s;
+        address_t *cur = address_new();
+
+        s = parse_address(phrase->buf, comment, cur);
+        if (s && *s && *s != ',' && *s != ';') {
+            address_delete(&cur);
+            return last;
+        }
 
 
-    ctx->cur = address_new();
-    s = parse_address(ctx->phrase, ctx);
-    if (s && *s && *s != ',' && *s != ';') {
-        address_delete(&ctx->cur);
-        return last;
+        *last = cur;
+        return &(*last)->next;
     }
 
     }
 
-    fprintf(stderr, "ADD [%s]\n", ctx->cur->mailbox);
-    *last = ctx->cur;
-    return &(*last)->next;
+    return last;
 }
 
 address_t *rfc822_parse_adrlist(address_t *top, const char *s)
 {
 }
 
 address_t *rfc822_parse_adrlist(address_t *top, const char *s)
 {
-    struct rfc822_parse_ctx ctx = { NULL, "", 0, "", 0 };
-    int ws_pending = 0;
-    address_t **last;
+    static_buf comment = {"", 0};
+    static_buf phrase  = {"", 0};
 
 
-    last = address_list_last(&top);
+    address_t **last = address_list_last(&top);
+    int ws_pending = 0;
 
     for (;;) {
         ws_pending = ISSPACE(*s);
         s = skipspaces(s);
 
         switch (*s) {
 
     for (;;) {
         ws_pending = ISSPACE(*s);
         s = skipspaces(s);
 
         switch (*s) {
-          case '\0':
-            if (ctx.phraselen) {
-                terminate_buffer(ctx.phrase);
-                terminate_buffer(ctx.comment);
-                last = add_addrspec(last, &ctx);
-            } else
-            if (ctx.commentlen && ctx.cur && !ctx.cur->personal) {
-                terminate_buffer(ctx.comment);
-                ctx.cur->personal = m_strdup(ctx.comment);
-            }
-            return top;
+            address_t *cur;
 
           default:
 
           default:
-            if (ctx.phraselen && ctx.phraselen < sizeof(ctx.phrase) - 1 && ws_pending)
-                ctx.phrase[ctx.phraselen++] = ' ';
-            s = next_token(s, ctx.phrase, &ctx.phraselen, sizeof(ctx.phrase) - 1);
+            if (ws_pending)
+                stbuf_append_sp(&phrase);
+            s = next_phrase(s, &phrase);
             if (!s) {
                 address_delete(&top);
                 return NULL;
             if (!s) {
                 address_delete(&top);
                 return NULL;
@@ -315,70 +297,57 @@ address_t *rfc822_parse_adrlist(address_t *top, const char *s)
             continue;
 
           case '(':
             continue;
 
           case '(':
-            if (ctx.commentlen && ctx.commentlen < sizeof(ctx.comment) - 1)
-                ctx.comment[ctx.commentlen++] = ' ';
-            s = next_token(s, ctx.comment, &ctx.commentlen, sizeof(ctx.comment) - 1);
+            stbuf_append_sp(&comment);
+            s = parse_comment(s + 1, &comment);
             if (!s) {
             if (!s) {
-                address_delete (&top);
+                address_delete(&top);
                 return NULL;
             }
             continue;
 
                 return NULL;
             }
             continue;
 
-          case ',':
-            if (ctx.phraselen) {
-                terminate_buffer(ctx.phrase);
-                last = add_addrspec(last, &ctx);
-            } else
-            if (ctx.commentlen && ctx.cur && !ctx.cur->personal) {
-                terminate_buffer(ctx.comment);
-                ctx.cur->personal = m_strdup(ctx.comment);
+
+          case '<':
+            cur = address_new();
+            if (phrase.len) {
+                /* if we get something like "Michael R. Elkins" remove the quotes */
+                cur->personal = rfc822_dequote_comment(&phrase);
             }
             }
+
+            s = parse_address(skipspaces(s + 1), &comment, cur);
+            if (!s || *s != '>' || !cur->mailbox) {
+                address_delete(&top);
+                address_delete(&cur);
+                return NULL;
+            }
+
+            *last = cur;
+            last = &(*last)->next;
+            break;
+
+          case ',':
+            last = rfc822_eotoken(last, &phrase, &comment);
             break;
 
             break;
 
-          case ':':
-            terminate_buffer(ctx.phrase);
+          case ':': /* group start */
             *last = address_new();
             *last = address_new();
-            (*last)->mailbox = m_strdup(ctx.phrase);
+            (*last)->mailbox = p_dupstr(phrase.buf, phrase.len);
             (*last)->group = 1;
             last = &(*last)->next;
             break;
 
           case ';':
             (*last)->group = 1;
             last = &(*last)->next;
             break;
 
           case ';':
-            if (ctx.phraselen) {
-                terminate_buffer(ctx.phrase);
-                last = add_addrspec(last, &ctx);
-            } else
-            if (ctx.commentlen && ctx.cur && !ctx.cur->personal) {
-                terminate_buffer(ctx.comment);
-                ctx.cur->personal = m_strdup(ctx.comment);
-            }
-
+            last = rfc822_eotoken(last, &phrase, &comment);
             /* add group terminator */
             *last = address_new();
             /* add group terminator */
             *last = address_new();
-            return top;
-
-          case '<':
-            terminate_buffer(ctx.phrase);
-            ctx.cur = address_new ();
-            if (ctx.phraselen) {
-                /* if we get something like "Michael R. Elkins" remove the quotes */
-                rfc822_dequote_comment(ctx.phrase);
-                ctx.cur->personal = m_strdup(ctx.phrase);
-            }
-
-            s = parse_address(skipspaces(s + 1), &ctx);
-            if (!s || *s != '>' || !ctx.cur->mailbox) {
-                address_delete(&top);
-                address_delete(&ctx.cur);
-                return NULL;
-            }
-
-            *last = ctx.cur;
+            last = &(*last)->next;
             break;
             break;
+
+          case '\0':
+            last = rfc822_eotoken(last, &phrase, &comment);
+            return top;
         }
 
         }
 
-        ctx.commentlen = 0;
-        ctx.phraselen = 0;
+        comment.len = phrase.len  = 0;
         s++;
     }
 
         s++;
     }
 
@@ -390,185 +359,119 @@ address_t *rfc822_parse_adrlist(address_t *top, const char *s)
 /* Output functions                                                         */
 /****************************************************************************/
 
 /* Output functions                                                         */
 /****************************************************************************/
 
-void
-rfc822_cat(char *buf, size_t buflen, const char *value, const char *specials)
+ssize_t
+rfc822_strcpy(char *buf, ssize_t buflen, const char *p, const char *specials)
 {
 {
-    if (strpbrk(value, specials)) {
-        char tmp[256], *pc = tmp;
-        size_t tmplen = sizeof (tmp) - 3;
-
-        *pc++ = '"';
-        for (; *value && tmplen > 1; value++) {
-            if (*value == '\\' || *value == '"') {
-                *pc++ = '\\';
-                tmplen--;
+    if (strpbrk(p, specials)) {
+        ssize_t pos = 0;
+
+        buf[pos++] = '"';
+
+        while (*p && pos < buflen - 2) {
+            if (*p == '\\' || *p == '"') {
+                if (pos >= buflen - 4)
+                    break;
+                buf[pos++] = '\\';
             }
             }
-            *pc++ = *value;
-            tmplen--;
+
+            buf[pos++] = *p++;
         }
         }
-        *pc++ = '"';
-        *pc = 0;
-        m_strcpy(buf, buflen, tmp);
+
+        buf[pos++] = '"';
+        buf[pos]   = '\0';
+        return pos;
     } else {
     } else {
-        m_strcpy(buf, buflen, value);
+        return m_strcpy(buf, buflen, p);
     }
 }
 
     }
 }
 
-void rfc822_write_address_single(char *buf, size_t buflen, address_t * addr,
-                                 int display)
+ssize_t rfc822_write_address_single(char *buf, ssize_t buflen,
+                                    address_t *addr, int display)
 {
 {
-    size_t len;
-    char *pbuf = buf;
-    char *pc;
+    ssize_t pos = 0;
 
     if (!addr)
 
     if (!addr)
-        return;
+        return 0;
 
     buflen--;                     /* save room for the terminal nul */
 
     if (addr->personal) {
 
     buflen--;                     /* save room for the terminal nul */
 
     if (addr->personal) {
-        if (strpbrk (addr->personal, RFC822Specials)) {
-            if (!buflen)
-                goto done;
-            *pbuf++ = '"';
-            buflen--;
-            for (pc = addr->personal; *pc && buflen > 0; pc++) {
-                if (*pc == '"' || *pc == '\\') {
-                    if (!buflen)
-                        goto done;
-                    *pbuf++ = '\\';
-                    buflen--;
-                }
-                if (!buflen)
-                    goto done;
-                *pbuf++ = *pc;
-                buflen--;
-            }
-            if (!buflen)
-                goto done;
-            *pbuf++ = '"';
-            buflen--;
-        }
-        else {
-            if (!buflen)
-                goto done;
-            m_strcpy(pbuf, buflen, addr->personal);
-            len = m_strlen(pbuf);
-            pbuf += len;
-            buflen -= len;
-        }
-
-        if (!buflen)
+        pos = rfc822_strcpy(buf, buflen, addr->personal, RFC822Specials);
+        if (pos + 2 >= buflen)
             goto done;
             goto done;
-        *pbuf++ = ' ';
-        buflen--;
-    }
 
 
-    if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
-        if (!buflen)
-            goto done;
-        *pbuf++ = '<';
-        buflen--;
+        buf[pos++] = ' ';
+        buf[pos++] = '<';
     }
 
     if (addr->mailbox) {
     }
 
     if (addr->mailbox) {
-        if (!buflen)
-            goto done;
-        if (ascii_strcmp (addr->mailbox, "@") && !display) {
-            m_strcpy(pbuf, buflen, addr->mailbox);
-            len = m_strlen(pbuf);
-        }
-        else if (ascii_strcmp (addr->mailbox, "@") && display) {
-            m_strcpy(pbuf, buflen, mutt_addr_for_display(addr));
-            len = m_strlen(pbuf);
-        }
-        else {
-            *pbuf = '\0';
-            len = 0;
+        if (!display) {
+            pos += m_strcpy(buf + pos, buflen - pos, addr->mailbox);
+        } else {
+            pos += m_strcpy(buf + pos, buflen - pos, mutt_addr_for_display(addr));
         }
         }
-        pbuf += len;
-        buflen -= len;
 
 
-        if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
-            if (!buflen)
+        if (addr->personal) {
+            if (pos + 1 >= buflen)
                 goto done;
                 goto done;
-            *pbuf++ = '>';
-            buflen--;
+            buf[pos++] = '>';
         }
 
         if (addr->group) {
         }
 
         if (addr->group) {
-            if (!buflen)
+            if (pos + 1 >= buflen)
                 goto done;
                 goto done;
-            *pbuf++ = ':';
-            buflen--;
-            if (!buflen)
-                goto done;
-            *pbuf++ = ' ';
-            buflen--;
+            buf[pos++] = ':';
         }
         }
-    }
-    else {
-        if (!buflen)
+    } else {
+        if (pos + 1 >= buflen)
             goto done;
             goto done;
-        *pbuf++ = ';';
-        buflen--;
+        buf[pos++] = ';';
     }
     }
-done:
+
+  done:
     /* no need to check for length here since we already save space at the
        beginning of this routine */
     /* no need to check for length here since we already save space at the
        beginning of this routine */
-    *pbuf = 0;
+    buf[pos] = 0;
+    return pos;
 }
 
 /* note: it is assumed that `buf' is nul terminated! */
 }
 
 /* note: it is assumed that `buf' is nul terminated! */
-void rfc822_write_address (char *buf, size_t buflen, address_t * addr,
-                           int display)
+ssize_t
+rfc822_write_address(char *buf, ssize_t buflen, address_t *addr, int display)
 {
 {
-    char *pbuf = buf;
-    size_t len = m_strlen(buf);
+    ssize_t pos;
 
     buflen--;                     /* save room for the terminal nul */
 
     buflen--;                     /* save room for the terminal nul */
+    pos = m_strnlen(buf, buflen);
 
 
-    if (len > 0) {
-        if (len > buflen)
-            return;                   /* safety check for bogus arguments */
-
-        pbuf += len;
-        buflen -= len;
-        if (!buflen)
-            goto done;
-        *pbuf++ = ',';
-        buflen--;
-        if (!buflen)
+    if (pos) {
+        if (pos + 2 >= buflen)
             goto done;
             goto done;
-        *pbuf++ = ' ';
-        buflen--;
-    }
 
 
-    for (; addr && buflen > 0; addr = addr->next) {
-        /* use buflen+1 here because we already saved space for the trailing
-           nul char, and the subroutine can make use of it */
-        rfc822_write_address_single (pbuf, buflen + 1, addr, display);
-
-        /* this should be safe since we always have at least 1 char passed into
-           the above call, which means `pbuf' should always be nul terminated */
-        len = m_strlen(pbuf);
-        pbuf += len;
-        buflen -= len;
+        buf[pos++] = ',';
+        buf[pos++] = ' ';
+    }
 
 
+    while (addr) {
+        pos += rfc822_write_address_single(buf + pos, buflen + 1 - pos,
+                                           addr, display);
         /* if there is another address, and its not a group mailbox name or
            group terminator, add a comma to separate the addresses */
         /* if there is another address, and its not a group mailbox name or
            group terminator, add a comma to separate the addresses */
-        if (addr->next && addr->next->mailbox && !addr->group) {
-            if (!buflen)
+        if (!addr->group && addr->next && addr->next->mailbox) {
+            if (pos + 2 >= buflen)
                 goto done;
                 goto done;
-            *pbuf++ = ',';
-            buflen--;
             if (!buflen)
                 goto done;
             if (!buflen)
                 goto done;
-            *pbuf++ = ' ';
-            buflen--;
+
+            buf[pos++] = ',';
+            buf[pos++] = ' ';
         }
         }
+
+        addr = addr->next;
     }
     }
-done:
-    *pbuf = 0;
+
+  done:
+    buf[pos] = '\0';
+    return pos;
 }
 
 }