}
-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;
break;
}
- if (*commentlen < commentmax)
- comment[(*commentlen)++] = *s;
+ stbuf_append(buf, *s);
}
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++) {
- if (*tokenlen < tokenmax)
- token[*tokenlen] = *s;
-
- if (*s == '"') {
- (*tokenlen)++;
+ switch (*s) {
+ case '"':
+ stbuf_append(buf, *s);
return s + 1;
- }
- if (*s == '\\') {
+ case '\\':
if (!*++s)
- break;
+ return NULL;
+ /* fallthrough */
- if (*tokenlen < tokenmax)
- token[*tokenlen] = *s;
+ default:
+ stbuf_append(buf, *s);
+ break;
}
- (*tokenlen)++;
}
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 (*tokenlen < tokenmax)
- token[(*tokenlen)++] = *s;
+ stbuf_append(buf, *s);
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 *
-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);
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 {
- s = next_token(s, mailbox, mailboxlen, mailboxmax);
+ s = next_phrase(s, mbox);
}
if (!s)
}
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 (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;
}
- 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;
}
-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)
{
- 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) {
- 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:
- 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;
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) {
- address_delete (&top);
+ address_delete(&top);
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;
- case ':':
- terminate_buffer(ctx.phrase);
+ case ':': /* group start */
*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 ';':
- 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();
- 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;
+
+ case '\0':
+ last = rfc822_eotoken(last, &phrase, &comment);
+ return top;
}
- ctx.commentlen = 0;
- ctx.phraselen = 0;
+ comment.len = phrase.len = 0;
s++;
}
/* 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 {
- 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)
- return;
+ return 0;
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;
- *pbuf++ = ' ';
- buflen--;
- }
- if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
- if (!buflen)
- goto done;
- *pbuf++ = '<';
- buflen--;
+ buf[pos++] = ' ';
+ buf[pos++] = '<';
}
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;
- *pbuf++ = '>';
- buflen--;
+ buf[pos++] = '>';
}
if (addr->group) {
- if (!buflen)
- goto done;
- *pbuf++ = ':';
- buflen--;
- if (!buflen)
+ if (pos + 1 >= buflen)
goto done;
- *pbuf++ = ' ';
- buflen--;
+ buf[pos++] = ':';
}
- }
- else {
- if (!buflen)
+ } else {
+ if (pos + 1 >= buflen)
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 */
- *pbuf = 0;
+ buf[pos] = 0;
+ return pos;
}
/* 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 */
+ pos = m_strnlen(buf, buflen);
- if (len > 0) {
- if (len > buflen)
- return; /* safety check for bogus arguments */
-
- pbuf += len;
- buflen -= len;
- if (!buflen)
+ if (pos) {
+ if (pos + 2 >= buflen)
goto done;
- *pbuf++ = ',';
- buflen--;
- if (!buflen)
- goto done;
- *pbuf++ = ' ';
- buflen--;
+
+ buf[pos++] = ',';
+ buf[pos++] = ' ';
}
- 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;
-
- /* 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)
- goto done;
- *pbuf++ = ',';
- buflen--;
- if (!buflen)
- goto done;
- *pbuf++ = ' ';
- buflen--;
+ for (; addr; addr = addr->next) {
+ pos += rfc822_write_address_single(buf + pos, buflen + 1 - pos,
+ addr, display);
+
+ if (!addr->group && addr->next && addr->next->mailbox) {
+ /* if there is another address, and its not a group mailbox name or
+ group terminator, add a comma to separate the addresses */
+ if (pos + 2 >= buflen)
+ break;
+
+ buf[pos++] = ',';
+ buf[pos++] = ' ';
}
}
-done:
- *pbuf = 0;
+
+ done:
+ buf[pos] = '\0';
+ return pos;
}