generalize the idea of tokens a bit more, don't restrict it to the rfc822
authorPierre Habouzit <madcoder@debian.org>
Wed, 8 Nov 2006 00:14:58 +0000 (01:14 +0100)
committerPierre Habouzit <madcoder@debian.org>
Wed, 8 Nov 2006 00:14:58 +0000 (01:14 +0100)
headers, use it for all mime tokens we need.

I suppose I'll do a mutt-tokens module at some point

Signed-off-by: Pierre Habouzit <madcoder@debian.org>
lib-mime/.gitignore
lib-mime/Makefile.am
lib-mime/mime-token.def [moved from lib-mime/rfc822hdrs.def with 67% similarity]
lib-mime/mime-token.sh [new file with mode: 0644]
lib-mime/mime-types.h
lib-mime/mime.h
lib-mime/rfc2047.c
lib-mime/rfc2231.c
lib-mime/rfc822hdrs.sh [deleted file]
lib-mime/rfc822parse.c

index 2e9a39f..5d6768d 100644 (file)
@@ -1,2 +1,2 @@
-rfc822hdrs.h
-rfc822hdrs.c
+mime-token.h
+mime-token.c
index 362022e..003aaf3 100644 (file)
@@ -1,4 +1,4 @@
-BUILT_SOURCES = rfc822hdrs.h rfc822hdrs.c
+BUILT_SOURCES = mime-token.h mime-token.c
 CLEANFILES    = $(BUILT_SOURCES)
 
 noinst_LIBRARIES = libmime.a
@@ -9,7 +9,7 @@ libmime_a_SOURCES = mime.h mime-types.h $(BUILT_SOURCES) \
 noinst_HEADERS    = mime.h mime-types.h
 
 
-rfc822hdrs.c rfc822hdrs.h: rfc822hdrs.def
-       sh rfc822hdrs.sh $@ < $<
+mime-token.c mime-token.h: mime-token.def mime-token.sh
+       sh mime-token.sh $@ < $<
 
 -include ../cflags.mk
similarity index 67%
rename from lib-mime/rfc822hdrs.def
rename to lib-mime/mime-token.def
index 1d073e2..06b8ca7 100644 (file)
@@ -1,6 +1,13 @@
+7bit
+8bit
+alternative
 apparently-from
 apparently-to
+application
+audio
+base64
 bcc
+binary
 cc
 content-description
 content-disposition
@@ -8,18 +15,28 @@ content-length
 content-transfer-encoding
 content-type
 date
+digest
 expires
+external-body
 followup-to
 from
+image
 in-reply-to
+news
+rfc822
+iso-2022-jp
 lines
 list-post
 mail-followup-to
 mail-reply-to
+message
 message-id
 mime-version
+model
+multipart
 newsgroups
 organization
+quoted-printable
 received
 references
 reply-to
@@ -29,8 +46,14 @@ status
 subject
 supercedes
 supersedes
+text
 to
+unknown
+us-ascii
+utf-8
+video
 x-comment-to
 x-label
 xref
 x-status
+x-uuencode
diff --git a/lib-mime/mime-token.sh b/lib-mime/mime-token.sh
new file mode 100644 (file)
index 0000000..bb55179
--- /dev/null
@@ -0,0 +1,87 @@
+#! /bin/sh -e
+
+die() {
+    echo "$@" 1>&2
+    exit 2
+}
+
+do_hdr() {
+    cat <<EOF
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or (at
+ *  your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA 02110-1301, USA.
+ *
+ *  Copyright © 2006 Pierre Habouzit
+ */
+
+/*****     THIS FILE IS AUTOGENERATED DO NOT MODIFY DIRECTLY !    *****/
+
+EOF
+}
+
+do_h() {
+    do_hdr
+    cat <<EOF
+#ifndef MUTT_LIB_MIME_MIME_TOKEN_H
+#define MUTT_LIB_MIME_MIME_TOKEN_H
+
+enum mime_token {
+    MUTT_MIME_TOKEN_UNKNOWN,
+`tr 'a-z-' 'A-Z_' | sed -e 's/.*/    MIME_&,/'`
+};
+
+__attribute__((pure))
+enum mime_token mime_which_token(const char *s, ssize_t len);
+#endif /* MUTT_LIB_MIME_MIME_TOKEN_H */
+EOF
+}
+
+do_c() {
+    cat <<EOF | gperf --ignore-case -l -t -C -F,0
+%{
+`do_hdr`
+
+#include <lib-lib/str.h>
+#include "mime-token.h"
+
+%}
+struct tok { const char *name; int val; };
+%%
+`awk '{print $0 ", " NR }'`
+%%
+
+enum mime_token mime_which_token(const char *s, ssize_t len) {
+    const struct tok *res;
+
+    if (len < 0)
+        len = m_strlen(s);
+    if (!len)
+        return MUTT_MIME_TOKEN_UNKNOWN;
+
+    res = in_word_set(s, len);
+    return res ? res->val : MUTT_MIME_TOKEN_UNKNOWN;
+}
+EOF
+}
+
+trap "rm -f $1" 1 2 3 15
+
+case "$1" in
+    *.h) do_h > "$1";;
+    *.c) do_c > "$1";;
+    *)  die "you must ask for the 'h' or 'c' generation";;
+esac
+
+exit 0
index 474a223..59fe54e 100644 (file)
@@ -60,7 +60,7 @@ enum {
     ENCQUOTEDPRINTABLE,
     ENCBASE64,
     ENCBINARY,
-    ENCUUENCODED
+    ENCUUENCODED,
 };
 
 /* Content-Disposition values */
index b235acc..8c0d3b8 100644 (file)
@@ -34,6 +34,7 @@
 #include <stdio.h>
 
 #include "mime-types.h"
+#include "mime-token.h"
 
 extern const char MimeSpecials[];
 extern const char *BodyTypes[];
@@ -43,8 +44,9 @@ extern const char *BodyEncodings[];
 
 #define is_multipart(x) \
     ((x)->type == TYPEMULTIPART \
-     || ((x)->type == TYPEMESSAGE && (!strcasecmp((x)->subtype, "rfc822") \
-                                      || !strcasecmp((x)->subtype, "news"))))
+     || ((x)->type == TYPEMESSAGE && \
+         (!mime_which_token((x)->subtype, -1) == MIME_RFC822) \
+         || !mime_which_token((x)->subtype, -1) == MIME_NEWS))
 #define TYPE(X) ((X->type == TYPEOTHER) && (X->xtype != NULL) ? X->xtype : BodyTypes[(X->type)])
 #define ENCODING(X) BodyEncodings[(X)]
 
index 3bd353c..264caff 100644 (file)
@@ -304,7 +304,7 @@ static size_t try_block(const char *d, ssize_t dlen,
         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"))
+        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) {
@@ -361,7 +361,7 @@ static size_t choose_block(char *d, size_t dlen, int col,
                            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 (;;) {
@@ -807,7 +807,7 @@ void rfc2047_decode(char **pd)
                     n -= m, s += m;
                 }
 
-                if (ascii_strcasecmp(AssumedCharset, "us-ascii")) {
+                if (mime_which_token(AssumedCharset, -1) == MIME_US_ASCII) {
                     char *t;
 
                     t = p_dupstr(s, n);
index ff6a02c..e7c1053 100644 (file)
@@ -242,8 +242,8 @@ void rfc2231_decode_parameters (PARAMETER ** headp)
             if (option (OPTRFC2047PARAMS) && p->value && strstr (p->value, "=?"))
                 rfc2047_decode (&p->value);
             else if (!option (OPTSTRICTMIME)) {
-                if (ascii_strcasecmp (AssumedCharset, "us-ascii"))
-                    mutt_convert_nonmime_string (&p->value);
+                if (mime_which_token(AssumedCharset, -1) == MIME_US_ASCII)
+                    mutt_convert_nonmime_string(&p->value);
             }
 
             *last = p;
diff --git a/lib-mime/rfc822hdrs.sh b/lib-mime/rfc822hdrs.sh
deleted file mode 100644 (file)
index 1124536..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#! /bin/sh
-
-die() {
-    echo "$@" 1>&2
-    exit 2
-}
-
-do_h() {
-    echo "#ifndef MUTT_LIB_MIME_RFC822HDRS_H"
-    echo "#define MUTT_LIB_MIME_RFC822HDRS_H"
-    echo "/* THIS FILE IS AUTOGENERATED FROM $< DO NOT MODIFY */"
-    echo "enum rfc822hdr {"
-    echo "    HDR_UNKNOWN,"
-    tr 'a-z-' 'A-Z_' | sed -e 's/.*/    HDR_&,/'
-    echo "};"
-    echo
-    echo "enum rfc822hdr rfc822_well_known(const char *s);"
-    echo "#endif /* MUTT_LIB_MIME_RFC822HDRS_H */"
-}
-
-do_c() {
-    echo "%{"
-    echo "#include <string.h>"
-    echo "#include \"rfc822hdrs.h\""
-    echo "%}"
-    echo "struct hdr { const char *name; int val; };"
-    echo "%%"
-    awk '{print $$0 ", " NR }'
-    echo "%%"
-    echo "enum rfc822hdr rfc822_well_known(const char *s) {"
-    echo "    const struct hdr *res = in_word_set(s, strlen(s));"
-    echo "    return res ? res->val : HDR_UNKNOWN;"
-    echo "}"
-}
-
-case "$1" in
-    *.h) do_h > "$1" ;;
-    *.c) do_c | gperf --ignore-case -t -C -F,0 > "$1" ;;
-    *)  die "you must ask for the 'h' or 'c' generation" ;;
-esac
-
-exit 0
index 70f405e..40414d9 100644 (file)
@@ -162,40 +162,44 @@ LIST *mutt_parse_references(char *s, int in_reply_to)
 
 int mutt_check_encoding(const char *s)
 {
-#define COMPARE(tok, value)                             \
-    if (!ascii_strncasecmp(tok, s, sizeof(tok) - 1)) {  \
-        return value;                                   \
+    int tok = mime_which_token(s, -1);
+    switch (tok) {
+      case MIME_7BIT:
+        return ENC7BIT;
+      case MIME_8BIT:
+        return ENC8BIT;
+      case MIME_BINARY:
+        return ENCBINARY;
+      case MIME_QUOTED_PRINTABLE:
+        return ENCQUOTEDPRINTABLE;
+      case MIME_BASE64:
+        return ENCBASE64;
+      case MIME_X_UUENCODE:
+        return ENCUUENCODED;
+      default:
+        return ENCOTHER;
     }
-    COMPARE("7bit", ENC7BIT);
-    COMPARE("8bit", ENC8BIT);
-    COMPARE("binary", ENCBINARY);
-    COMPARE("quoted-printable", ENCQUOTEDPRINTABLE);
-    COMPARE("base64", ENCBASE64);
-    COMPARE("x-uuencode", ENCUUENCODED);
-#undef COMPARE
-
-    return ENCOTHER;
 }
 
 int mutt_check_mime_type(const char *s)
 {
-#define COMPARE(tok, value)                             \
-    if (!ascii_strncasecmp(tok, s, sizeof(tok) - 1)) {  \
-        return value;                                   \
+    int tok;
+
+    if (!m_strcmp(s, "*") || !m_strcmp(s, ".*"))
+        return TYPEANY;
+
+    tok = mime_which_token(s, -1);
+    switch (tok) {
+      case MIME_TEXT:        return TYPETEXT;
+      case MIME_MULTIPART:   return TYPEMULTIPART;
+      case MIME_APPLICATION: return TYPEAPPLICATION;
+      case MIME_MESSAGE:     return TYPEMESSAGE;
+      case MIME_IMAGE:       return TYPEIMAGE;
+      case MIME_AUDIO:       return TYPEAUDIO;
+      case MIME_VIDEO:       return TYPEVIDEO;
+      case MIME_MODEL:       return TYPEMODEL;
+      default:               return TYPEOTHER;
     }
-  COMPARE("text", TYPETEXT);
-  COMPARE("multipart", TYPEMULTIPART);
-  COMPARE("application", TYPEAPPLICATION);
-  COMPARE("message", TYPEMESSAGE);
-  COMPARE("image", TYPEIMAGE);
-  COMPARE("audio", TYPEAUDIO);
-  COMPARE("video", TYPEVIDEO);
-  COMPARE("model", TYPEMODEL);
-  COMPARE("*",  TYPEANY);
-  COMPARE(".*", TYPEANY);
-#undef COMPARE
-
-  return TYPEOTHER;
 }
 
 static PARAMETER *parse_parameters(const char *s)
@@ -403,17 +407,25 @@ BODY *mutt_read_mime_header(FILE *fp, int digest)
             break;
         }
 
-        if (!ascii_strncasecmp(line, "content-", 8)) {
-            if (!ascii_strcasecmp("type", line + 8))
-                mutt_parse_content_type (p, body);
-            else if (!ascii_strcasecmp ("transfer-encoding", line + 8))
-                body->encoding = mutt_check_encoding (p);
-            else if (!ascii_strcasecmp ("disposition", line + 8))
-                parse_content_disposition(p, body);
-            else if (!ascii_strcasecmp ("description", line + 8)) {
-                m_strreplace(&body->description, p);
-                rfc2047_decode(&body->description);
-            }
+        switch (mime_which_token(line, -1)) {
+          case MIME_CONTENT_TYPE:
+            mutt_parse_content_type (p, body);
+            break;
+
+          case MIME_CONTENT_TRANSFER_ENCODING:
+            body->encoding = mutt_check_encoding (p);
+            break;
+
+          case MIME_CONTENT_DISPOSITION:
+            parse_content_disposition(p, body);
+            break;
+
+          case MIME_CONTENT_DESCRIPTION:
+            m_strreplace(&body->description, p);
+            rfc2047_decode(&body->description);
+            break;
+
+          default: break;
         }
     }
 
@@ -438,7 +450,7 @@ void mutt_parse_part(FILE *fp, BODY *b)
         bound = mutt_get_parameter("boundary", b->parameter);
         fseeko(fp, b->offset, SEEK_SET);
         b->parts = mutt_parse_multipart(fp, bound, b->offset + b->length,
-                           !ascii_strcasecmp("digest", b->subtype));
+                                        mime_which_token(b->subtype, -1) == MIME_DIGEST);
         break;
 
       case TYPEMESSAGE:
@@ -448,7 +460,7 @@ void mutt_parse_part(FILE *fp, BODY *b)
             if (mutt_is_message_type(b->type, b->subtype)) {
                 b->parts = mutt_parse_messageRFC822(fp, b);
             } else
-            if (!ascii_strcasecmp(b->subtype, "external-body") == 0) {
+            if (mime_which_token(b->subtype, -1) == MIME_EXTERNAL_BODY) {
                 b->parts = mutt_read_mime_header(fp, 0);
             } else {
                 return;
@@ -783,70 +795,68 @@ time_t mutt_parse_date(const char *s, HEADER *h)
     return mutt_mktime(&tm, 0) + (zoccident ? 1 : -1) * (zhours * 3600 + zminutes * 60);
 }
 
-#include "rfc822hdrs.h"
-
 LIST **mutt_parse_rfc822_line(ENVELOPE *e, HEADER *hdr, char *line, char *p,
                               short weed, short do_2047, LIST **user_hdrs)
 {
-    switch (rfc822_well_known(line)) {
-      case HDR_APPARENTLY_FROM:
+    switch (mime_which_token(line, -1)) {
+      case MIME_APPARENTLY_FROM:
         e->from = rfc822_parse_adrlist (e->from, p);
         break;
 
-      case HDR_APPARENTLY_TO:
+      case MIME_APPARENTLY_TO:
         e->to = rfc822_parse_adrlist (e->to, p);
         break;
 
-      case HDR_BCC:
+      case MIME_BCC:
         e->bcc = rfc822_parse_adrlist (e->bcc, p);
         break;
 
-      case HDR_CC:
+      case MIME_CC:
         e->cc = rfc822_parse_adrlist (e->cc, p);
         break;
 
-      case HDR_CONTENT_DESCRIPTION:
+      case MIME_CONTENT_DESCRIPTION:
         if (hdr) {
             m_strreplace(&hdr->content->description, p);
             rfc2047_decode(&hdr->content->description);
         }
         break;
 
-      case HDR_CONTENT_DISPOSITION:
+      case MIME_CONTENT_DISPOSITION:
         if (hdr)
             parse_content_disposition(p, hdr->content);
         break;
 
-      case HDR_CONTENT_LENGTH:
+      case MIME_CONTENT_LENGTH:
         if (hdr) {
             if ((hdr->content->length = atoi(p)) < 0)
                 hdr->content->length = -1;
         }
         break;
 
-      case HDR_CONTENT_TRANSFER_ENCODING:
+      case MIME_CONTENT_TRANSFER_ENCODING:
         if (hdr)
             hdr->content->encoding = mutt_check_encoding(p);
         break;
 
-      case HDR_CONTENT_TYPE:
+      case MIME_CONTENT_TYPE:
         if (hdr)
             mutt_parse_content_type (p, hdr->content);
         break;
 
-      case HDR_DATE:
+      case MIME_DATE:
         m_strreplace(&e->date, p);
         if (hdr)
             hdr->date_sent = mutt_parse_date (p, hdr);
         break;
 
-      case HDR_EXPIRES:
+      case MIME_EXPIRES:
         if (hdr && mutt_parse_date (p, NULL) < time (NULL))
             hdr->expired = 1;
         break;
 
 #ifdef USE_NNTP
-      case HDR_FOLLOWUP_TO:
+      case MIME_FOLLOWUP_TO:
         if (!e->followup_to) {
             m_strrtrim(p);
             e->followup_to = m_strdup(skipspaces(p));
@@ -854,7 +864,7 @@ LIST **mutt_parse_rfc822_line(ENVELOPE *e, HEADER *hdr, char *line, char *p,
         break;
 #endif
 
-      case HDR_FROM:
+      case MIME_FROM:
         e->from = rfc822_parse_adrlist(e->from, p);
         /* don't leave from info NULL if there's an invalid address (or
          * whatever) in From: field; mutt would just display it as empty
@@ -866,12 +876,12 @@ LIST **mutt_parse_rfc822_line(ENVELOPE *e, HEADER *hdr, char *line, char *p,
         }
         break;
 
-      case HDR_IN_REPLY_TO:
+      case MIME_IN_REPLY_TO:
         mutt_free_list(&e->in_reply_to);
         e->in_reply_to = mutt_parse_references(p, 1);
         break;
 
-      case HDR_LINES:
+      case MIME_LINES:
         if (hdr) {
             /* HACK - mutt has, for a very short time, produced negative
                Lines header values.  Ignore them. */
@@ -879,7 +889,7 @@ LIST **mutt_parse_rfc822_line(ENVELOPE *e, HEADER *hdr, char *line, char *p,
         }
         break;
 
-      case HDR_LIST_POST:
+      case MIME_LIST_POST:
         /* RFC 2369.  FIXME: We should ignore whitespace, but don't. */
         if (strncmp(p, "NO", 2)) {
             char *beg, *end;
@@ -899,16 +909,16 @@ LIST **mutt_parse_rfc822_line(ENVELOPE *e, HEADER *hdr, char *line, char *p,
         }
         break;
 
-      case HDR_MAIL_FOLLOWUP_TO:
+      case MIME_MAIL_FOLLOWUP_TO:
         e->mail_followup_to = rfc822_parse_adrlist(e->mail_followup_to, p);
         break;
 
-      case HDR_MAIL_REPLY_TO:
+      case MIME_MAIL_REPLY_TO:
         address_delete (&e->reply_to);
         e->reply_to = rfc822_parse_adrlist(e->reply_to, p);
         break;
 
-      case HDR_MESSAGE_ID:
+      case MIME_MESSAGE_ID:
         {
             const char *beg, *end;
 
@@ -920,25 +930,25 @@ LIST **mutt_parse_rfc822_line(ENVELOPE *e, HEADER *hdr, char *line, char *p,
         }
         break;
 
-      case HDR_MIME_VERSION:
+      case MIME_MIME_VERSION:
         if (hdr)
             hdr->mime = 1;
         break;
 
 #ifdef USE_NNTP
-      case HDR_NEWSGROUPS:
+      case MIME_NEWSGROUPS:
         p_delete(&e->newsgroups);
         m_strrtrim(p);
         e->newsgroups = m_strdup(skipspaces(p));
         break;
 #endif
 
-      case HDR_ORGANIZATION:
-        if (!e->organization && m_strcasecmp(p, "unknown"))
+      case MIME_ORGANIZATION:
+        if (!e->organization && mime_which_token(p, -1) == MIME_UNKNOWN)
             e->organization = m_strdup(p);
         break;
 
-      case HDR_RECEIVED:
+      case MIME_RECEIVED:
         if (hdr && !hdr->received) {
             char *d = strchr(p, ';');
             if (d)
@@ -946,24 +956,24 @@ LIST **mutt_parse_rfc822_line(ENVELOPE *e, HEADER *hdr, char *line, char *p,
         }
         break;
 
-      case HDR_REFERENCES:
+      case MIME_REFERENCES:
         mutt_free_list(&e->references);
         e->references = mutt_parse_references(p, 0);
         break;
 
-      case HDR_REPLY_TO:
+      case MIME_REPLY_TO:
         e->reply_to = rfc822_parse_adrlist(e->reply_to, p);
         break;
 
-      case HDR_RETURN_PATH:
+      case MIME_RETURN_PATH:
         e->return_path = rfc822_parse_adrlist(e->return_path, p);
         break;
 
-      case HDR_SENDER:
+      case MIME_SENDER:
         e->sender = rfc822_parse_adrlist (e->sender, p);
         break;
 
-      case HDR_STATUS:
+      case MIME_STATUS:
         if (hdr) {
             while (*p) {
                 switch (*p) {
@@ -982,40 +992,40 @@ LIST **mutt_parse_rfc822_line(ENVELOPE *e, HEADER *hdr, char *line, char *p,
         }
         break;
 
-      case HDR_SUBJECT:
+      case MIME_SUBJECT:
         if (!e->subject)
             e->subject = m_strdup(p);
         break;
 
-      case HDR_SUPERCEDES:
-      case HDR_SUPERSEDES:
+      case MIME_SUPERCEDES:
+      case MIME_SUPERSEDES:
         if (hdr)
             e->supersedes = m_strdup(p);
         break;
 
-      case HDR_TO:
+      case MIME_TO:
         e->to = rfc822_parse_adrlist(e->to, p);
         break;
 
 #ifdef USE_NNTP
-      case HDR_X_COMMENT_TO:
+      case MIME_X_COMMENT_TO:
         if (!e->x_comment_to)
             e->x_comment_to = m_strdup(p);
         break;
 #endif
 
-      case HDR_X_LABEL:
+      case MIME_X_LABEL:
         e->x_label = m_strdup(p);
         break;
 
 #ifdef USE_NNTP
-      case HDR_XREF:
+      case MIME_XREF:
         if (!e->xref)
             e->xref = m_strdup(p);
         break;
 #endif
 
-      case HDR_X_STATUS:
+      case MIME_X_STATUS:
         if (hdr) {
             while (*p) {
                 switch (*p) {
@@ -1221,7 +1231,7 @@ int count_body_parts (BODY *body, int flags) {
       shallrecurse = 1;
 
       /* If it's an external body pointer, don't recurse it. */
-      if (!ascii_strcasecmp (bp->subtype, "external-body"))
+      if (mime_which_token(bp->subtype, -1) == MIME_EXTERNAL_BODY)
         shallrecurse = 0;
 
       /* Don't count containers if they're top-level. */
@@ -1230,7 +1240,7 @@ int count_body_parts (BODY *body, int flags) {
     } else if (bp->type == TYPEMULTIPART) {
       /* Always recurse multiparts, except multipart/alternative. */
       shallrecurse = 1;
-      if (!m_strcasecmp(bp->subtype, "alternative"))
+      if (mime_which_token(bp->subtype, -1) == MIME_ALTERNATIVE)
         shallrecurse = 0;
 
       /* Don't count containers if they're top-level. */