move more things in the lib-mime
[apps/madmutt.git] / lib-mime / rfc2047.c
index 6e79bee..71bf5c7 100644 (file)
 
 #include <lib-mime/mime.h>
 
-#include "mutt.h"
 #include "charset.h"
 #include "thread.h"
 
+#include <assert.h>
 #include <ctype.h>
 #include <errno.h>
 #include <stdio.h>
 #ifdef NDEBUG
 #define assert(x)
 #else
-#include <assert.h>
 #endif
 
 #define ENCWORD_LEN_MAX 75
 #define ENCWORD_LEN_MIN 9       /* m_strlen("=?.?.?.?=") */
 
 #define HSPACE(x) ((x) == '\0' || (x) == ' ' || (x) == '\t')
-
 #define CONTINUATION_BYTE(c) (((c) & 0xc0) == 0x80)
 
-typedef size_t (*encoder_t) (char *, const char *, size_t,
-                             const char *);
-
 /* converts f of len flen and charset from
        into *t of len *tlen and charset to 
 
@@ -70,16 +65,15 @@ typedef size_t (*encoder_t) (char *, const char *, size_t,
  */
 static ssize_t
 convert_string(const char *from, const char *f, ssize_t flen,
-               const char *to,   char **t, size_t *tlen)
+               const char *to,   char **t, ssize_t *tlen)
 {
     iconv_t cd;
     char *buf, *ob;
-    size_t obl, n;
-    int e;
+    ssize_t obl, n;
 
     cd = mutt_iconv_open(to, from, 0);
 
-    if (cd == (iconv_t)(-1))
+    if (cd == MUTT_ICONV_ERROR)
         return -1;
 
     obl = 4 * flen + 1;
@@ -87,109 +81,134 @@ convert_string(const char *from, const char *f, ssize_t flen,
     n = my_iconv(cd, &f, &flen, &ob, &obl);
 
     if (n < 0 || my_iconv(cd, 0, 0, &ob, &obl) < 0) {
-        e = errno;
-        p_delete(&buf);
-        iconv_close (cd);
+        int e = errno;
+        iconv_close(cd);
         errno = e;
+        p_delete(&buf);
         return -1;
     }
+    iconv_close(cd);
 
     *ob = '\0';
     *tlen = ob - buf;
-
-    p_realloc(&buf, ob - buf + 1);
-    *t = buf;
-    iconv_close (cd);
+    *t  = buf;
 
     return n;
 }
 
+/* choose the shortest encoding for u */
 char *mutt_choose_charset(const char *fromcode, const char *charsets,
-                          char *u, size_t ulen, char **d, size_t *dlen)
+                          char *u, ssize_t ulen, char **dst, ssize_t *dlen)
 {
-    char canonical_buff[LONG_STRING];
-    char *e = 0, *tocode = 0;
-    size_t elen = 0, bestn = 0;
-    const char *p, *q;
+    char *res = NULL;
+    ssize_t reslen = 0;
+
+    char *tocode = NULL;
+    ssize_t bestn = 0;
 
-    for (p = charsets; p; p = q ? q + 1 : 0) {
-        char *s, *t;
-        size_t slen, n;
+    const char *p = charsets;
 
-        q = strchr (p, ':');
+    while (*p) {
+        char cset[SHORT_STRING];
+        const char *q;
+        char *s;
+        ssize_t slen, n;
 
-        n = q ? q - p : m_strlen(p);
+        q = strchr(p, ':');
+        if (q) {
+            n = m_strncpy(cset, sizeof(cset), p, q - p);
+            p = ++q;
+        } else {
+            n = m_strcpy(cset, sizeof(cset), p);
+            p += n;
+        }
 
-        if (!n ||
+        if (!n || n > (ENCWORD_LEN_MAX - ENCWORD_LEN_MIN + 2 - 12)) {
             /* Assume that we never need more than 12 characters of
                encoded-text to encode a single character. */
-            n > (ENCWORD_LEN_MAX - ENCWORD_LEN_MIN + 2 - 12))
             continue;
+        }
 
-        t = p_dupstr(p, n);
-
-        n = convert_string(fromcode, u, ulen, t, &s, &slen);
-        if (n == (size_t) (-1))
+        n = convert_string(fromcode, u, ulen, cset, &s, &slen);
+        if (n < 0)
             continue;
 
         if (!tocode || n < bestn) {
+            m_strreplace(&tocode, cset);
             bestn = n;
-            p_delete(&tocode);
-            tocode = t;
-            if (d) {
-                p_delete(&e);
-                e = s;
-            } else {
-                p_delete(&s);
-            }
-            elen = slen;
+
+            p_delete(&res);
+            res = s;
+            reslen = slen;
             if (!bestn)
                 break;
         } else {
-            p_delete(&t);
             p_delete(&s);
         }
     }
 
     if (tocode) {
-        if (d)
-            *d = e;
-        if (dlen)
-            *dlen = elen;
+        char buf[LONG_STRING];
 
-        mutt_canonical_charset(canonical_buff, sizeof(canonical_buff), tocode);
-        m_strreplace(&tocode, canonical_buff);
+        if (dst && dlen) {
+            *dst  = res;
+            *dlen = reslen;
+        } else {
+            p_delete(&res);
+        }
+
+        charset_canonicalize(buf, sizeof(buf), tocode);
+        m_strreplace(&tocode, buf);
     }
 
     return tocode;
 }
 
-static size_t b_encoder (char *s, const char *d, size_t dlen,
-                         const char *tocode)
+
+/****************************************************************************/
+/* Encoding functions                                                       */
+/****************************************************************************/
+
+static const char __qp_special[128] = {
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
+    1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+typedef size_t (encoder_t)(char *, const char *, ssize_t, const char *);
+
+static size_t
+b_encoder(char *s, const char *d, ssize_t dlen, const char *tocode)
 {
     char *s0 = s;
 
-    memcpy (s, "=?", 2), s += 2;
-    memcpy (s, tocode, m_strlen(tocode)), s += m_strlen(tocode);
-    memcpy (s, "?B?", 3), s += 3;
+    s += sprintf(s, "=?%s?B?", tocode);
+
     for (;;) {
-        if (!dlen)
-            break;
-        else if (dlen == 1) {
+        switch (dlen) {
+          case 0:
+            goto done;
+
+          case 1:
             *s++ = __m_b64chars[(*d >> 2) & 0x3f];
             *s++ = __m_b64chars[(*d & 0x03) << 4];
             *s++ = '=';
             *s++ = '=';
-            break;
-        }
-        else if (dlen == 2) {
+            goto done;
+
+          case 2:
             *s++ = __m_b64chars[(*d >> 2) & 0x3f];
             *s++ = __m_b64chars[((*d & 0x03) << 4) | ((d[1] >> 4) & 0x0f)];
             *s++ = __m_b64chars[(d[1] & 0x0f) << 2];
             *s++ = '=';
-            break;
-        }
-        else {
+            goto done;
+
+          default:
             *s++ = __m_b64chars[(*d >> 2) & 0x3f];
             *s++ = __m_b64chars[((*d & 0x03) << 4) | ((d[1] >> 4) & 0x0f)];
             *s++ = __m_b64chars[((d[1] & 0x0f) << 2) | ((d[2] >> 6) & 0x03)];
@@ -197,32 +216,36 @@ static size_t b_encoder (char *s, const char *d, size_t dlen,
             d += 3, dlen -= 3;
         }
     }
-    memcpy (s, "?=", 2), s += 2;
+
+  done:
+    *s++ = '?';
+    *s++ = '=';
     return s - s0;
 }
 
-static size_t q_encoder (char *s, const char *d, size_t dlen,
-                         const char *tocode)
+static size_t
+q_encoder(char *s, const char *d, ssize_t dlen, const char *tocode)
 {
     char *s0 = s;
 
-    memcpy (s, "=?", 2), s += 2;
-    memcpy (s, tocode, m_strlen(tocode)), s += m_strlen(tocode);
-    memcpy (s, "?Q?", 3), s += 3;
+    s += sprintf(s, "=?%s?Q?", tocode);
     while (dlen--) {
         unsigned char c = *d++;
 
-        if (c == ' ')
+        if (c == ' ') {
             *s++ = '_';
-        else if (c >= 0x7f || c < 0x20 || c == '_' || strchr (MimeSpecials, c)) {
+        } else
+        if (c & 0x80 || __qp_special[c]) {
             *s++ = '=';
             *s++ = __m_b36chars_upper[c >> 4];
             *s++ = __m_b36chars_upper[c & 0xf];
-        }
-        else
+        } else {
             *s++ = c;
+        }
     }
-    memcpy (s, "?=", 2), s += 2;
+
+    *s++ = '?';
+    *s++ = '=';
     return s - s0;
 }
 
@@ -235,95 +258,96 @@ static size_t q_encoder (char *s, const char *d, size_t dlen,
  * tocode, unless fromcode is 0, in which case the data is assumed to
  * be already in tocode, which should be 8-bit and stateless.
  */
-static size_t try_block (const char *d, size_t dlen,
-                         const char *fromcode, const char *tocode,
-                         encoder_t * encoder, size_t * wlen)
+static size_t try_block(const char *d, ssize_t dlen,
+                        const char *fromcode, const char *tocode,
+                        encoder_t **encoder, ssize_t *wlen)
 {
     char buf1[ENCWORD_LEN_MAX - ENCWORD_LEN_MIN + 1];
-    iconv_t cd;
-    const char *ib;
-    char *ob, *p;
-    size_t ibl, obl;
-    int count, len, len_b, len_q;
+    ssize_t obl = sizeof(buf1) - m_strlen(tocode);
+    char *ob;
 
     if (fromcode) {
-        cd = mutt_iconv_open (tocode, fromcode, 0);
-        assert (cd != (iconv_t) (-1));
-        ib = d, ibl = dlen, ob = buf1, obl = sizeof (buf1) - m_strlen(tocode);
-        if (my_iconv(cd, &ib, &ibl, &ob, &obl) == (size_t) (-1) ||
-            my_iconv(cd, 0, 0, &ob, &obl) == (size_t) (-1)) {
-            assert (errno == E2BIG);
-            iconv_close (cd);
-            assert (ib > d);
+        const char *ib = d;
+        ssize_t ibl = dlen;
+        iconv_t cd = mutt_iconv_open(tocode, fromcode, 0);
+
+        assert (cd != MUTT_ICONV_ERROR);
+
+        ob = buf1;
+
+        if (my_iconv(cd, &ib, &ibl, &ob, &obl) < 0
+        ||  my_iconv(cd, 0, 0, &ob, &obl) < 0)
+        {
+            assert (errno == E2BIG && ib > d);
+            iconv_close(cd);
             return (ib - d == dlen) ? dlen : ib - d + 1;
         }
         iconv_close (cd);
-    }
-    else {
-        if (dlen > sizeof (buf1) - m_strlen(tocode))
-            return sizeof (buf1) - m_strlen(tocode) + 1;
-        memcpy (buf1, d, dlen);
+    } else {
+        if (dlen > obl)
+            return obl + 1;
+        memcpy(buf1, d, dlen);
         ob = buf1 + dlen;
     }
 
-    count = 0;
-    for (p = buf1; p < ob; p++) {
-        unsigned char c = *p;
+    {
+        const char *p;
+        int count, len, len_b, len_q;
 
-        assert (strchr (MimeSpecials, '?'));
-        if (c >= 0x7f || c < 0x20 || *p == '_' ||
-            (c != ' ' && strchr (MimeSpecials, *p)))
-            ++count;
-    }
-
-    len = ENCWORD_LEN_MIN - 2 + m_strlen(tocode);
-    len_b = len + (((ob - buf1) + 2) / 3) * 4;
-    len_q = len + (ob - buf1) + 2 * count;
-
-    /* Apparently RFC 1468 says to use B encoding for iso-2022-jp. */
-    if (!ascii_strcasecmp (tocode, "ISO-2022-JP"))
-        len_q = ENCWORD_LEN_MAX + 1;
+        count = 0;
+        for (p = buf1; p < ob; p++) {
+            count += (*p & 0x80 || __qp_special[(int)*p]);
+        }
 
-    if (len_b < len_q && len_b <= ENCWORD_LEN_MAX) {
-        *encoder = b_encoder;
-        *wlen = len_b;
-        return 0;
-    }
-    else if (len_q <= ENCWORD_LEN_MAX) {
-        *encoder = q_encoder;
-        *wlen = len_q;
-        return 0;
+        len = ENCWORD_LEN_MIN - 2 + m_strlen(tocode);
+        len_b = len + (((ob - buf1) + 2) / 3) * 4;
+        len_q = len + (ob - buf1) + 2 * count;
+
+        /* Apparently RFC 1468 says to use B encoding for iso-2022-jp. */
+        if (mime_which_token(tocode, -1) == MIME_ISO_2022_JP)
+            len_q = ENCWORD_LEN_MAX + 1;
+
+        if (len_b < len_q && len_b <= ENCWORD_LEN_MAX) {
+            *encoder = b_encoder;
+            *wlen = len_b;
+            return 0;
+        } else
+        if (len_q <= ENCWORD_LEN_MAX) {
+            *encoder = q_encoder;
+            *wlen = len_q;
+            return 0;
+        } else {
+            return dlen;
+        }
     }
-    else
-        return dlen;
 }
 
 /*
  * Encode the data (d, dlen) into s using the encoder.
  * Return the length of the encoded word.
  */
-static size_t encode_block (char *s, char *d, size_t dlen,
-                            const char *fromcode, const char *tocode,
-                            encoder_t encoder)
+static size_t
+encode_block(char *s, char *d, ssize_t dlen,
+             const char *fromcode, const char *tocode, encoder_t *encoder)
 {
     char buf1[ENCWORD_LEN_MAX - ENCWORD_LEN_MIN + 1];
+    ssize_t ibl, obl, n1, n2;
     iconv_t cd;
     const char *ib;
     char *ob;
-    size_t ibl, obl, n1, n2;
 
     if (fromcode) {
-        cd = mutt_iconv_open (tocode, fromcode, 0);
-        assert (cd != (iconv_t) (-1));
-        ib = d, ibl = dlen, ob = buf1, obl = sizeof (buf1) - m_strlen(tocode);
+        cd = mutt_iconv_open(tocode, fromcode, 0);
+        assert (cd != MUTT_ICONV_ERROR);
+        ib = d, ibl = dlen, ob = buf1, obl = sizeof(buf1) - m_strlen(tocode);
         n1 = my_iconv(cd, &ib, &ibl, &ob, &obl);
         n2 = my_iconv(cd, 0, 0, &ob, &obl);
-        assert (n1 != (size_t) (-1) && n2 != (size_t) (-1));
+        assert (n1 >= 0 && n2 >= 0);
         iconv_close (cd);
-        return (*encoder) (s, buf1, ob - buf1, tocode);
+        return (*encoder)(s, buf1, ob - buf1, tocode);
+    } else {
+        return (*encoder)(s, d, dlen, tocode);
     }
-    else
-        return (*encoder) (s, d, dlen, tocode);
 }
 
 /*
@@ -332,24 +356,25 @@ static size_t encode_block (char *s, char *d, size_t dlen,
  * and set the length *wlen of the encoded word and *encoder.
  * We start in column col, which limits the length of the word.
  */
-static size_t choose_block (char *d, size_t dlen, int col,
-                            const char *fromcode, const char *tocode,
-                            encoder_t * encoder, size_t * wlen)
+static size_t choose_block(char *d, size_t dlen, int col,
+                           const char *fromcode, const char *tocode,
+                           encoder_t **encoder, ssize_t *wlen)
 {
     size_t n, nn;
-    int utf8 = fromcode && !ascii_strcasecmp (fromcode, "UTF-8");
+    int utf8 = mime_which_token(fromcode, -1) == MIME_UTF_8;
 
     n = dlen;
     for (;;) {
         assert (d + n > d);
-        nn = try_block (d, n, fromcode, tocode, encoder, wlen);
+        nn = try_block(d, n, fromcode, tocode, encoder, wlen);
         if (!nn && (col + *wlen <= ENCWORD_LEN_MAX + 1 || n <= 1))
             break;
         n = (nn ? nn : n) - 1;
         assert (n > 0);
-        if (utf8)
-            while (n > 1 && CONTINUATION_BYTE (d[n]))
+        if (utf8) {
+            while (n > 1 && CONTINUATION_BYTE(d[n]))
                 --n;
+        }
     }
     return n;
 }
@@ -364,30 +389,31 @@ static size_t choose_block (char *d, size_t dlen, int col,
  * The input data is assumed to be a single line starting at column col;
  * if col is non-zero, the preceding character was a space.
  */
-static int rfc2047_encode (const char *d, size_t dlen, int col,
-                           const char *fromcode, const char *charsets,
-                           char **e, size_t * elen, char *specials)
+/*** XXX: simplify that one day ***/
+static int rfc2047_encode(const char *d, ssize_t dlen, int col,
+                          const char *fromcode, const char *charsets,
+                          char **e, ssize_t *elen, const char *specials)
 {
     int ret = 0;
     char *buf;
-    size_t bufpos, buflen;
-    char *u, *t0, *t1, *t;
-    char *s0, *s1;
-    size_t ulen, r, n, wlen;
-    encoder_t encoder;
+    ssize_t bufpos, buflen;
+    char *u, *t;
+    char *s0, *s1, *t0, *t1;
     char *tocode1 = 0;
     const char *tocode;
     const char *icode = "UTF-8";
+    ssize_t ulen, r, n, wlen;
+    encoder_t *encoder;
 
     /* Try to convert to UTF-8. */
     if (convert_string(fromcode, d, dlen, icode, &u, &ulen)) {
         ret = 1;
-        icode = 0;
+        icode = NULL;
         u = p_dupstr(d, ulen = dlen);
     }
 
     /* Find earliest and latest things we must encode. */
-    s0 = s1 = t0 = t1 = 0;
+    s0 = s1 = t0 = t1 = NULL;
     for (t = u; t < u + ulen; t++) {
         if ((*t & 0x80) ||
             (*t == '=' && t[1] == '?' && (t == u || HSPACE (*(t - 1))))) {
@@ -418,14 +444,15 @@ static int rfc2047_encode (const char *d, size_t dlen, int col,
     /* Choose target charset. */
     tocode = fromcode;
     if (icode) {
-        if ((tocode1 = mutt_choose_charset (icode, charsets, u, ulen, 0, 0)))
+        if ((tocode1 = mutt_choose_charset(icode, charsets, u, ulen,
+                                           NULL, NULL)))
             tocode = tocode1;
         else
             ret = 2, icode = 0;
     }
 
     /* Hack to avoid labelling 8-bit data as us-ascii. */
-    if (!icode && mutt_is_us_ascii (tocode))
+    if (!icode && charset_is_us_ascii(tocode))
         tocode = "unknown-8bit";
 
     /* Adjust t0 for maximum length of line. */
@@ -438,27 +465,29 @@ static int rfc2047_encode (const char *d, size_t dlen, int col,
 
     /* Adjust t0 until we can encode a character after a space. */
     for (; t0 > u; t0--) {
-        if (!HSPACE (*(t0 - 1)))
+        if (!HSPACE(t0[-1]))
             continue;
         t = t0 + 1;
-        if (icode)
-            while (t < u + ulen && CONTINUATION_BYTE (*t))
+        if (icode) {
+            while (t < u + ulen && CONTINUATION_BYTE(*t))
                 ++t;
-        if (!try_block (t0, t - t0, icode, tocode, &encoder, &wlen) &&
-            col + (t0 - u) + wlen <= ENCWORD_LEN_MAX + 1)
+        }
+        if (!try_block(t0, t - t0, icode, tocode, &encoder, &wlen)
+        &&  col + (t0 - u) + wlen <= ENCWORD_LEN_MAX + 1)
             break;
     }
 
     /* Adjust t1 until we can encode a character before a space. */
     for (; t1 < u + ulen; t1++) {
-        if (!HSPACE (*t1))
+        if (!HSPACE(*t1))
             continue;
         t = t1 - 1;
-        if (icode)
-            while (CONTINUATION_BYTE (*t))
+        if (icode) {
+            while (CONTINUATION_BYTE(*t))
                 --t;
-        if (!try_block (t, t1 - t, icode, tocode, &encoder, &wlen) &&
-            1 + wlen + (u + ulen - t1) <= ENCWORD_LEN_MAX + 1)
+        }
+        if (!try_block (t, t1 - t, icode, tocode, &encoder, &wlen)
+        &&  1 + wlen + (u + ulen - t1) <= ENCWORD_LEN_MAX + 1)
             break;
     }
 
@@ -468,7 +497,7 @@ static int rfc2047_encode (const char *d, size_t dlen, int col,
     buflen = 2 * ulen;
     buf = p_new(char, buflen);
     bufpos = t0 - u;
-    memcpy (buf, u, t0 - u);
+    memcpy(buf, u, t0 - u);
 
     col += t0 - u;
 
@@ -500,8 +529,8 @@ static int rfc2047_encode (const char *d, size_t dlen, int col,
 
         /* Add to output buffer. */
 #define LINEBREAK "\n\t"
-        if (bufpos + wlen + m_strlen(LINEBREAK) > buflen) {
-            buflen = bufpos + wlen + m_strlen(LINEBREAK);
+        if (bufpos + wlen + 2 > buflen) {
+            buflen = bufpos + wlen + 2;
             p_realloc(&buf, buflen);
         }
         r = encode_block (buf + bufpos, t, n, icode, tocode, encoder);
@@ -534,118 +563,132 @@ static int rfc2047_encode (const char *d, size_t dlen, int col,
     return ret;
 }
 
-void _rfc2047_encode_string (char **pd, int encode_specials, int col)
+
+void _rfc2047_encode_string(char **pd, int encode_specials, int col)
 {
     char *e;
-    size_t elen;
+    ssize_t elen;
     const char *charsets;
 
     if (!Charset || !*pd)
         return;
 
-    charsets = SendCharset;
-    if (!charsets || !*charsets)
-        charsets = "UTF-8";
+    charsets = m_strisempty(SendCharset) ? "UTF-8" : SendCharset;
 
-    rfc2047_encode (*pd, m_strlen(*pd), col,
-                    Charset, charsets, &e, &elen,
-                    encode_specials ? RFC822Specials : NULL);
+    rfc2047_encode(*pd, m_strlen(*pd), col,
+                   Charset, charsets, &e, &elen,
+                   encode_specials ? RFC822Specials : NULL);
 
     p_delete(pd);
     *pd = e;
 }
 
 void rfc2047_encode_string(char **pd) {
-    _rfc2047_encode_string(a, 0, 32);
+    _rfc2047_encode_string(pd, 0, 32);
 }
 
-void rfc2047_encode_adrlist (address_t * addr, const char *tag)
+void rfc2047_encode_adrlist(address_t *addr, const char *tag)
 {
     address_t *ptr = addr;
     int col = tag ? m_strlen(tag) + 2 : 32;
 
     while (ptr) {
         if (ptr->personal)
-            _rfc2047_encode_string (&ptr->personal, 1, col);
+            _rfc2047_encode_string(&ptr->personal, 1, col);
         ptr = ptr->next;
     }
 }
 
-static int rfc2047_decode_word (char *d, const char *s, size_t len)
+
+/****************************************************************************/
+/* Decoding functions                                                       */
+/****************************************************************************/
+
+/* decode one word into d[len] */
+static int rfc2047_decode_word(char *d, size_t len, const char *s)
 {
-    const char *pp, *pp1;
-    char *pd, *d0;
-    const char *t, *t1;
-    int enc = 0, count = 0;
+    const char *p, *eotoken;
     char *charset = NULL;
+    int enc = 0, count = 0;
+    char *d0;
 
-    pd = d0 = p_new(char, m_strlen(s));
+    /* =?[QB]?cset?.?= */
+    for (p = s; (eotoken = strchr(p, '?')); p = eotoken + 1) {
+        switch (++count) {
+            const char *t;
+            char *q;
 
-    for (pp = s; (pp1 = strchr (pp, '?')); pp = pp1 + 1) {
-        count++;
-        switch (count) {
           case 2:
             /* ignore language specification a la RFC 2231 */
-            t = pp1;
-            if ((t1 = memchr (pp, '*', t - pp)))
-                t = t1;
-            charset = p_dupstr(pp, t - pp);
+            t = memchr(p, '*', eotoken - p) ?: eotoken;
+            charset = p_dupstr(p, t - p);
             break;
+
           case 3:
-            if (toupper ((unsigned char) *pp) == 'Q')
+            switch (*p) {
+              case 'q': case 'Q':
                 enc = ENCQUOTEDPRINTABLE;
-            else if (toupper ((unsigned char) *pp) == 'B')
+                break;
+
+              case 'b': case 'B':
                 enc = ENCBASE64;
-            else {
+                break;
+
+              default:
                 p_delete(&charset);
-                p_delete(&d0);
-                return (-1);
+                return -1;
             }
             break;
+
           case 4:
+            d0 = q = p_new(char, m_strlen(s) + 1);
+
             if (enc == ENCQUOTEDPRINTABLE) {
-                for (; pp < pp1; pp++) {
-                    if (*pp == '_')
-                        *pd++ = ' ';
-                    else if (*pp == '=' && hexval(pp[1]) >= 0 && hexval(pp[2]) >= 0) {
-                        *pd++ = (hexval (pp[1]) << 4) | hexval (pp[2]);
-                        pp += 2;
+                while (p < eotoken) {
+                    if (*p == '=' && hexval(p[1]) >= 0 && hexval(p[2]) >= 0) {
+                        *q++ = (hexval (p[1]) << 4) | hexval (p[2]);
+                        p += 3;
+                    } else
+                    if (*p == '_') {
+                        *q++ = ' ';
+                        p++;
+                    } else {
+                        *q++ = *p++;
                     }
-                    else
-                        *pd++ = *pp;
                 }
-                *pd = 0;
-            }
-            else if (enc == ENCBASE64) {
+                *q = 0;
+            } else { /* enc == ENCBASE64 */
                 int c, b = 0, k = 0;
 
-                for (; pp < pp1; pp++) {
-                    if (*pp == '=')
+                while (p < eotoken) {
+                    if (*p == '=')
                         break;
-                    if ((c = base64val(*pp)) < 0)
+
+                    c = base64val(*p++);
+                    if (c < 0)
                         continue;
+
                     if (k + 6 >= 8) {
                         k -= 2;
-                        *pd++ = b | (c >> k);
+                        *q++ = b | (c >> k);
                         b = c << (8 - k);
-                    }
-                    else {
+                    } else {
                         b |= c << (k + 2);
                         k += 6;
                     }
                 }
-                *pd = 0;
+                *q = 0;
             }
             break;
         }
     }
 
     if (charset)
-        mutt_convert_string (&d0, charset, Charset, M_ICONV_HOOK_FROM);
+        mutt_convert_string(&d0, charset, Charset, M_ICONV_HOOK_FROM);
     m_strcpy(d, len, d0);
     p_delete(&charset);
     p_delete(&d0);
-    return (0);
+    return 0;
 }
 
 /*
@@ -654,50 +697,60 @@ static int rfc2047_decode_word (char *d, const char *s, size_t len)
  * must be B or Q. Also, we don't require the encoded word to be
  * separated by linear-white-space (section 5(1)).
  */
-static const char *find_encoded_word (const char *s, const char **x)
+static const char *find_encoded_word(const char *s, const char **x)
 {
-    const char *p, *q;
+    const char *p;
+
+    while ((p = strstr(s, "=?"))) {
+        s = p + 2;
+        while (0x20 < *s && *s < 0x7f && !strchr ("()<>@,;:\"/[]?.=", *s)) {
+            s++;
+        }
 
-    q = s;
-    while ((p = strstr (q, "=?"))) {
-        for (q = p + 2;
-             0x20 < *q && *q < 0x7f && !strchr ("()<>@,;:\"/[]?.=", *q); q++);
-        if (q[0] != '?' || !strchr ("BbQq", q[1]) || q[2] != '?')
+        if (s[0] != '?' || !strchr("BbQq", s[1]) || s[2] != '?')
             continue;
-        for (q = q + 3; 0x20 <= *q && *q < 0x7f && *q != '?'; q++);
-        if (q[0] != '?' || q[1] != '=') {
-            --q;
+
+        s += 3;
+        while (0x20 <= *s && *s < 0x7f && *s != '?') {
+            s++;
+        }
+
+        if (s[0] != '?' || s[1] != '=') {
+            --s;
             continue;
         }
 
-        *x = q + 2;
+        *x = s + 2;
         return p;
     }
 
-    return 0;
+    return NULL;
 }
 
 /* return length of linear white space */
-static size_t lwslen (const char *s, size_t n)
+static ssize_t lwslen(const char *s, ssize_t n)
 {
-    const char *p = s;
-    size_t len = n;
+    const char *p;
+    ssize_t len = n;
 
     if (n <= 0)
         return 0;
 
-    for (; p < s + n; p++)
+    for (p = s; p < s + n; p++) {
         if (!strchr (" \t\r\n", *p)) {
-            len = (size_t) (p - s);
+            len = p - s;
             break;
         }
-    if (strchr ("\r\n", *(p - 1)))        /* LWS doesn't end with CRLF */
-        len = (size_t) 0;
+    }
+
+    if (p[-1] == '\r' || p[-1] == '\n')  /* LWS cannot end with CRLF */
+        return 0;
+
     return len;
 }
 
 /* return length of linear white space : reverse */
-static size_t lwsrlen (const char *s, size_t n)
+static ssize_t lwsrlen(const char *s, ssize_t n)
 {
     const char *p = s + n - 1;
     size_t len = n;
@@ -705,28 +758,31 @@ static size_t lwsrlen (const char *s, size_t n)
     if (n <= 0)
         return 0;
 
-    if (strchr ("\r\n", *p))      /* LWS doesn't end with CRLF */
-        return (size_t) 0;
+    if (*p == '\r' || *p == '\n')   /* LWS doesn't end with CRLF */
+        return 0;
 
-    for (; p >= s; p--)
-        if (!strchr (" \t\r\n", *p)) {
-            len = (size_t) (s + n - 1 - p);
+    while (p >= s) {
+        if (!strchr(" \t\r\n", *p)) {
+            len = s + n - 1 - p;
             break;
         }
+        p--;
+    }
+
     return len;
 }
 
 /* try to decode anything that looks like a valid RFC2047 encoded
  * header field, ignoring RFC822 parsing rules
  */
-void rfc2047_decode (char **pd)
+void rfc2047_decode(char **pd)
 {
-    const char *p, *q;
-    size_t m, n;
-    int found_encoded = 0;
-    char *d0, *d;
+    const int strict_mime = option(OPTSTRICTMIME);
+
     const char *s = *pd;
-    size_t dlen;
+    char *d0, *d;
+    ssize_t dlen;
+    int found_encoded = 0;
 
     if (!s || !*s)
         return;
@@ -735,108 +791,103 @@ void rfc2047_decode (char **pd)
     d = d0 = p_new(char, dlen + 1);
 
     while (*s && dlen > 0) {
-        if (!(p = find_encoded_word (s, &q))) {
+        const char *p, *q;
+
+        p = find_encoded_word(s, &q);
+
+        if (!p) {
             /* no encoded words */
-            if (!option (OPTSTRICTMIME)) {
+            if (!strict_mime) {
+                ssize_t m, n;
+
                 n = m_strlen(s);
-                if (found_encoded && (m = lwslen (s, n)) != 0) {
+                if (found_encoded && (m = lwslen(s, n)) != 0) {
                     if (m != n)
-                        *d = ' ', d++, dlen--;
+                        *d++ = ' ', dlen--;
                     n -= m, s += m;
                 }
-                if (ascii_strcasecmp (AssumedCharset, "us-ascii")) {
+
+                if (mime_which_token(AssumedCharset, -1) == MIME_US_ASCII) {
                     char *t;
-                    size_t tlen;
 
                     t = p_dupstr(s, n);
-                    if (mutt_convert_nonmime_string (&t) == 0) {
-                        tlen = m_strlen(t);
-                        strncpy (d, t, tlen);
-                        d += tlen;
-                    }
-                    else {
-                        strncpy (d, s, n);
-                        d += n;
+                    if (mutt_convert_nonmime_string(&t) == 0) {
+                        d += m_strcpy(d, dlen, t);
+                    } else {
+                        d += m_strcpy(d, dlen, s);
                     }
                     p_delete(&t);
                     break;
                 }
             }
-            strncpy (d, s, dlen);
-            d += dlen;
+            d += m_strcpy(d, dlen, s);
             break;
         }
 
         if (p != s) {
-            n = (size_t) (p - s);
+            ssize_t m, n;
+
+            n = (p - s);
             /* ignore spaces between encoded words
              * and linear white spaces between encoded word and *text */
-            if (!option (OPTSTRICTMIME)) {
-                if (found_encoded && (m = lwslen (s, n)) != 0) {
+            if (!strict_mime) {
+                if (found_encoded && (m = lwslen(s, n)) != 0) {
                     if (m != n)
-                        *d = ' ', d++, dlen--;
+                        *d++ = ' ', dlen--;
                     n -= m, s += m;
                 }
 
-                if ((m = n - lwsrlen (s, n)) != 0) {
-                    if (m > dlen)
-                        m = dlen;
-                    memcpy (d, s, m);
+                if ((m = n - lwsrlen(s, n)) != 0) {
+                    m  = m_strncpy(d, dlen, s, m);
                     d += m;
                     dlen -= m;
                     if (m != n)
-                        *d = ' ', d++, dlen--;
+                        *d++ = ' ', dlen--;
                 }
-            }
-            else if (!found_encoded || strspn (s, " \t\r\n") != n) {
-                if (n > dlen)
-                    n = dlen;
-                memcpy (d, s, n);
+            } else
+            if (!found_encoded || (ssize_t)strspn(s, " \t\r\n") != n) {
+                n  = m_strncpy(d, dlen, s, n);
                 d += n;
                 dlen -= n;
             }
         }
 
-        rfc2047_decode_word (d, p, dlen);
+        rfc2047_decode_word(d, dlen, p);
         found_encoded = 1;
         s = q;
-        n = m_strlen(d);
-        dlen -= n;
-        d += n;
+        while (*d && dlen)
+            d++, dlen--;
     }
-    *d = 0;
 
     p_delete(pd);
     *pd = d0;
-    str_adjust (pd);
 }
 
-void rfc2047_decode_adrlist (address_t * a)
+void rfc2047_decode_adrlist(address_t *a)
 {
     while (a) {
         if (a->personal)
-            rfc2047_decode (&a->personal);
+            rfc2047_decode(&a->personal);
         a = a->next;
     }
 }
 
-void rfc2047_decode_envelope (ENVELOPE* e) {
-
-    if (!e)
-        return;
+void rfc2047_decode_envelope(ENVELOPE* e)
+{
+    assert (e);
 
     /* do RFC2047 decoding */
-    rfc2047_decode_adrlist (e->from);
-    rfc2047_decode_adrlist (e->to);
-    rfc2047_decode_adrlist (e->cc);
-    rfc2047_decode_adrlist (e->bcc);
-    rfc2047_decode_adrlist (e->reply_to);
-    rfc2047_decode_adrlist (e->mail_followup_to);
-    rfc2047_decode_adrlist (e->return_path);
-    rfc2047_decode_adrlist (e->sender);
+    rfc2047_decode_adrlist(e->from);
+    rfc2047_decode_adrlist(e->to);
+    rfc2047_decode_adrlist(e->cc);
+    rfc2047_decode_adrlist(e->bcc);
+    rfc2047_decode_adrlist(e->reply_to);
+    rfc2047_decode_adrlist(e->mail_followup_to);
+    rfc2047_decode_adrlist(e->return_path);
+    rfc2047_decode_adrlist(e->sender);
 
     if (e->subject) {
-        rfc2047_decode (&e->subject);
-        mutt_adjust_subject (e);
+        rfc2047_decode(&e->subject);
+        mutt_adjust_subject(e);
     }
 }