check for linecap and trailing space in mhbuild for text/plain
[mmh] / uip / mhbuild.c
index 06a353a..ba4428f 100644 (file)
 
 #include <h/mh.h>
 #include <fcntl.h>
-#include <h/signals.h>
-#include <h/md5.h>
 #include <errno.h>
-#include <signal.h>
 #include <h/tws.h>
 #include <h/mime.h>
 #include <h/mhparse.h>
-#include <h/mhcachesbr.h>
 #include <h/utils.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <locale.h>
+#include <sysexits.h>
 
-#ifdef TIME_WITH_SYS_TIME
+#ifdef HAVE_SYS_TIME_H
 # include <sys/time.h>
-# include <time.h>
-#else
-# ifdef TM_IN_SYS_TIME
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
 #endif
+#include <time.h>
 
 static struct swit switches[] = {
-#define CHECKSW  0
-       { "check", 0 },
-#define NCHECKSW  1
-       { "nocheck", 0 },
-#define EBCDICSW  2
-       { "ebcdicsafe", 0 },
-#define NEBCDICSW  3
-       { "noebcdicsafe", 0 },
-#define HEADSW  4
-       { "headers", 0 },
-#define NHEADSW  5
-       { "noheaders", 0 },
-#define LISTSW  6
-       { "list", 0 },
-#define NLISTSW  7
-       { "nolist", 0 },
-#define SIZESW  8
-       { "realsize", 0 },
-#define NSIZESW  9
-       { "norealsize", 0 },
-#define RFC934SW  10
-       { "rfc934mode", 0 },
-#define NRFC934SW  11
-       { "norfc934mode", 0 },
-#define VERBSW  12
+#define VERBSW  0
        { "verbose", 0 },
-#define NVERBSW  13
-       { "noverbose", 0 },
-#define RCACHESW  14
-       { "rcache policy", 0 },
-#define WCACHESW  15
-       { "wcache policy", 0 },
-#define CONTENTIDSW  16
-       { "contentid", 0 },
-#define NCONTENTIDSW  17
-       { "nocontentid", 0 },
-#define VERSIONSW  18
-       { "version", 0 },
-#define HELPSW  19
+#define NVERBSW  1
+       { "noverbose", 2 },
+#define VERSIONSW  2
+       { "Version", 0 },
+#define HELPSW  3
        { "help", 0 },
-#define DEBUGSW  20
+#define DEBUGSW  4
        { "debug", -5 },
        { NULL, 0 }
 };
@@ -102,12 +65,6 @@ static char prefix[] = "----- =_aaaaaaaaaa";
 int make_intermediates(char *);
 void content_error(char *, CT, char *, ...);
 
-/* mhcachesbr.c */
-int find_cache(CT, int, int *, char *, char *, int);
-
-/* ftpsbr.c */
-int ftp_get(char *, char *, char *, char *, char *, char *, int, int);
-
 /* mhfree.c */
 void free_content(CT);
 void free_ctinfo(CT);
@@ -123,25 +80,12 @@ static void set_id(CT, int);
 static int compose_content(CT);
 static int scan_content(CT);
 static int build_headers(CT);
-static char *calculate_digest(CT, int);
 static CT build_mime(char *);
 
 
-
-/* mhcachesbr.c */
-extern int rcachesw;
-extern int wcachesw;
-extern char *cache_public;
-extern char *cache_private;
-
 int debugsw = 0;
 int verbosw = 0;
 
-int ebcdicsw = 0;
-int listsw   = 0;
-int rfc934sw = 0;
-int contentidsw = 1;
-
 /*
 ** Temporary files
 */
@@ -151,15 +95,12 @@ static int unlink_infile  = 0;
 static char outfile[BUFSIZ];
 static int unlink_outfile = 0;
 
-static void unlink_done(int) NORETURN;
+void unlink_done();
 
 /* mhoutsbr.c */
 int output_message(CT, char *);
 int output_message_fp(CT, FILE *, char*);
 
-/* mhlistsbr.c */
-int list_all_messages(CT *, int, int, int, int);
-
 /* mhmisc.c */
 void set_endian(void);
 
@@ -170,20 +111,18 @@ void free_content(CT);
 int
 main(int argc, char **argv)
 {
-       int sizesw = 1, headsw = 1;
-       int *icachesw;
        char *cp, buf[BUFSIZ];
        char buffer[BUFSIZ], *compfile = NULL;
        char **argp, **arguments;
-       CT ct, cts[2];
+       CT ct;
        FILE *fp = NULL;
        FILE *fp_out = NULL;
 
-       done = unlink_done;
+       if (atexit(unlink_done) != 0) {
+               adios(EX_OSERR, NULL, "atexit failed");
+       }
 
-#ifdef LOCALE
        setlocale(LC_ALL, "");
-#endif
        invo_name = mhbasename(argv[0]);
 
        /* read user profile/context */
@@ -195,10 +134,9 @@ main(int argc, char **argv)
        while ((cp = *argp++)) {
                if (cp[0] == '-' && cp[1] == '\0') {
                        if (compfile)
-                               adios(NULL, "cannot specify both standard input and a file");
+                               adios(EX_USAGE, NULL, "cannot specify both standard input and a file");
                        else
                                compfile = cp;
-                       listsw = 0;  /* turn off -list if using std in/out */
                        verbosw = 0;  /* turn off -verbose listings */
                        break;
                }
@@ -206,86 +144,17 @@ main(int argc, char **argv)
                        switch (smatch(++cp, switches)) {
                        case AMBIGSW:
                                ambigsw(cp, switches);
-                               done(1);
+                               exit(EX_USAGE);
                        case UNKWNSW:
-                               adios(NULL, "-%s unknown", cp);
+                               adios(EX_USAGE, NULL, "-%s unknown", cp);
 
                        case HELPSW:
                                snprintf(buf, sizeof(buf), "%s [switches] file", invo_name);
                                print_help(buf, switches, 1);
-                               done(1);
+                               exit(argc == 2 ? EX_OK : EX_USAGE);
                        case VERSIONSW:
                                print_version(invo_name);
-                               done(1);
-
-                       case RCACHESW:
-                               icachesw = &rcachesw;
-                               goto do_cache;
-                       case WCACHESW:
-                               icachesw = &wcachesw;
-                       do_cache: ;
-                               if (!(cp = *argp++) || *cp == '-')
-                                       adios(NULL, "missing argument to %s",
-                                                       argp[-2]);
-                               switch (*icachesw = smatch(cp, caches)) {
-                               case AMBIGSW:
-                                       ambigsw(cp, caches);
-                                       done(1);
-                               case UNKWNSW:
-                                       adios(NULL, "%s unknown", cp);
-                               default:
-                                       break;
-                               }
-                               continue;
-
-                       case CHECKSW:
-                               checksw++;
-                               continue;
-                       case NCHECKSW:
-                               checksw = 0;
-                               continue;
-
-                       case EBCDICSW:
-                               ebcdicsw++;
-                               continue;
-                       case NEBCDICSW:
-                               ebcdicsw = 0;
-                               continue;
-
-                       case HEADSW:
-                               headsw++;
-                               continue;
-                       case NHEADSW:
-                               headsw = 0;
-                               continue;
-
-                       case LISTSW:
-                               listsw++;
-                               continue;
-                       case NLISTSW:
-                               listsw = 0;
-                               continue;
-
-                       case RFC934SW:
-                               rfc934sw++;
-                               continue;
-                       case NRFC934SW:
-                               rfc934sw = 0;
-                               continue;
-
-                       case SIZESW:
-                               sizesw++;
-                               continue;
-                       case NSIZESW:
-                               sizesw = 0;
-                               continue;
-
-                       case CONTENTIDSW:
-                               contentidsw = 1;
-                               continue;
-                       case NCONTENTIDSW:
-                               contentidsw = 0;
-                               continue;
+                               exit(argc == 2 ? EX_OK : EX_USAGE);
 
                        case VERBSW:
                                verbosw++;
@@ -299,16 +168,13 @@ main(int argc, char **argv)
                        }
                }
                if (compfile)
-                       adios(NULL, "only one composition file allowed");
+                       adios(EX_USAGE, NULL, "only one composition file allowed");
                else
                        compfile = cp;
        }
 
        set_endian();
 
-       if ((cp = getenv("MM_NOASK")) && strcmp(cp, "1")==0)
-               listsw  = 0;
-
        /*
        ** Check if we've specified an additional profile
        */
@@ -330,15 +196,6 @@ main(int argc, char **argv)
                fclose(fp);
        }
 
-       /* Check for public cache location */
-       if ((cache_public = context_find(nmhcache)) && *cache_public != '/')
-               cache_public = NULL;
-
-       /* Check for private cache location */
-       if (!(cache_private = context_find(nmhprivcache)))
-               cache_private = ".cache";
-       cache_private = getcpy(toabsdir(cache_private));
-
        /*
        ** Check for storage directory.  If defined, we
        ** will store temporary files there.  Else we
@@ -347,11 +204,11 @@ main(int argc, char **argv)
        if ((cp = context_find(nmhstorage)) && *cp)
                tmp = concat(cp, "/", invo_name, NULL);
        else
-               tmp = getcpy(toabsdir(invo_name));
+               tmp = mh_xstrdup(toabsdir(invo_name));
 
        /* Check if we have a file to process */
        if (!compfile)
-               adios(NULL, "need to specify a %s composition file",
+               adios(EX_USAGE, NULL, "need to specify a %s composition file",
                                invo_name);
 
        /*
@@ -368,8 +225,6 @@ main(int argc, char **argv)
 
                /* build the content structures for MIME message */
                ct = build_mime(infile);
-               cts[0] = ct;
-               cts[1] = NULL;
 
                /* output MIME message to this temporary file */
                strncpy(outfile, m_mktemp(invo_name, NULL, &fp_out),
@@ -382,7 +237,7 @@ main(int argc, char **argv)
 
                /* output the temp file to standard output */
                if ((fp = fopen(outfile, "r")) == NULL)
-                       adios(outfile, "unable to open");
+                       adios(EX_IOERR, outfile, "unable to open");
                while (fgets(buffer, BUFSIZ, fp))
                        fputs(buffer, stdout);
                fclose(fp);
@@ -394,7 +249,7 @@ main(int argc, char **argv)
                unlink_outfile = 0;
 
                free_content(ct);
-               done(0);
+               exit(EX_OK);
        }
 
        /*
@@ -403,8 +258,6 @@ main(int argc, char **argv)
 
        /* build the content structures for MIME message */
        ct = build_mime(compfile);
-       cts[0] = ct;
-       cts[1] = NULL;
 
        /* output MIME message to this temporary file */
        strncpy(outfile, m_mktemp2(compfile, invo_name, NULL, &fp_out),
@@ -415,44 +268,37 @@ main(int argc, char **argv)
        output_message_fp(ct, fp_out, outfile);
        fclose(fp_out);
 
-       /*
-       ** List the message info
-       */
-       if (listsw)
-               list_all_messages(cts, headsw, sizesw, verbosw, debugsw);
-
        /* Rename composition draft */
-       snprintf(buffer, sizeof(buffer), "%s.orig", m_backup(compfile));
+       snprintf(buffer, sizeof(buffer), "%s.orig", compfile);
        if (rename(compfile, buffer) == NOTOK) {
-               adios(compfile, "unable to rename comp draft %s to", buffer);
+               adios(EX_IOERR, buffer, "unable to rename draft %s to", compfile);
        }
 
        /* Rename output file to take its place */
        if (rename(outfile, compfile) == NOTOK) {
-               advise(outfile, "unable to rename output %s to", compfile);
+               advise(compfile, "unable to rename output %s to", outfile);
                rename(buffer, compfile);
-               done(1);
+               exit(EX_IOERR);
        }
        unlink_outfile = 0;
 
        free_content(ct);
-       done(0);
-       return 1;
+       return 0;
 }
 
 
-static void
-unlink_done(int status)
+void
+unlink_done()
 {
        /*
        ** Check if we need to remove stray temporary files.
        */
-       if (unlink_infile)
+       if (unlink_infile) {
                unlink(infile);
-       if (unlink_outfile)
+       }
+       if (unlink_outfile) {
                unlink(outfile);
-
-       exit(status);
+       }
 }
 
 /*
@@ -466,25 +312,28 @@ unlink_done(int status)
 static CT
 build_mime(char *infile)
 {
-       int compnum, state;
-       char buf[BUFSIZ], name[NAMESZ];
+       enum state state;
+       struct field f = {{0}};
+       int compnum;
+       char buf[BUFSIZ];
        char *cp, *np, *vp;
        struct multipart *m;
        struct part **pp;
        CT ct;
        FILE *in;
+       HF hp;
 
        umask(~m_gmprot());
 
        /* open the composition draft */
-       if ((in = fopen(infile, "r")) == NULL)
-               adios(infile, "unable to open for reading");
+       if ((in = fopen(infile, "r")) == NULL) {
+               adios(EX_IOERR, infile, "unable to open for reading");
+       }
 
        /*
        ** Allocate space for primary (outside) content
        */
-       if ((ct = (CT) calloc(1, sizeof(*ct))) == NULL)
-               adios(NULL, "out of memory");
+       ct = mh_xcalloc(1, sizeof(*ct));
 
        /*
        ** Allocate structure for handling decoded content
@@ -498,77 +347,69 @@ build_mime(char *infile)
        ** draft into the linked list of header fields for
        ** the new MIME message.
        */
-       for (compnum = 1, state = FLD;;) {
-               switch (state = m_getfld(state, name, buf, sizeof(buf), in)) {
-               case FLD:
-               case FLDPLUS:
-               case FLDEOF:
+       for (compnum = 1, state = FLD2;;) {
+               switch (state = m_getfld2(state, &f, in)) {
+               case FLD2:
                        compnum++;
 
                        /* abort if draft has Mime-Version header field */
-                       if (!mh_strcasecmp(name, VRSN_FIELD))
-                               adios(NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
+                       if (!mh_strcasecmp(f.name, VRSN_FIELD)) {
+                               adios(EX_CONFIG, NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
+                       }
 
                        /*
                        ** abort if draft has Content-Transfer-Encoding
                        ** header field
                        */
-                       if (!mh_strcasecmp(name, ENCODING_FIELD))
-                               adios(NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
+                       if (!mh_strcasecmp(f.name, ENCODING_FIELD)) {
+                               adios(EX_CONFIG, NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
+                       }
 
                        /* ignore any Content-Type fields in the header */
-                       if (!mh_strcasecmp(name, TYPE_FIELD)) {
-                               while (state == FLDPLUS)
-                                       state = m_getfld(state, name, buf,
-                                                       sizeof(buf), in);
-                               goto finish_field;
+                       if (!mh_strcasecmp(f.name, TYPE_FIELD)) {
+                               continue;
                        }
 
-                       /* get copies of the buffers */
-                       np = getcpy(name);
-                       vp = getcpy(buf);
-
-                       /* if necessary, get rest of field */
-                       while (state == FLDPLUS) {
-                               state = m_getfld(state, name, buf,
-                                               sizeof(buf), in);
-                               vp = add(buf, vp);  /* add to prev value */
-                       }
+                       /* add the header data to the list */
+                       add_header(ct, mh_xstrdup(f.name), mh_xstrdup(f.value));
 
-                       /* Now add the header data to the list */
-                       add_header(ct, np, vp);
+                       continue;
 
-finish_field:
-                       /* if this wasn't the last hdr field, then continue */
-                       if (state != FLDEOF)
-                               continue;
-                       /* else fall... */
+               case BODY2:
+                       fseek(in, (long) (-strlen(f.value)), SEEK_CUR);
+                       break;
 
-               case FILEEOF:
-                       adios(NULL, "draft has empty body -- no directives!");
+               case FILEEOF2:
+                       adios(EX_CONFIG, NULL, "draft has empty body -- no directives!");
                        /* NOTREACHED */
 
-               case BODY:
-               case BODYEOF:
-                       fseek(in, (long) (-strlen(buf)), SEEK_CUR);
-                       break;
-
-               case LENERR:
-               case FMTERR:
-                       adios(NULL, "message format error in component #%d",
+               case LENERR2:
+               case FMTERR2:
+               case IOERR2:
+                       adios(EX_CONFIG, NULL, "message format error in component #%d",
                                        compnum);
 
                default:
-                       adios(NULL, "getfld() returned %d", state);
+                       adios(EX_SOFTWARE, NULL, "getfld() returned %d", state);
                }
                break;
        }
 
        /*
+       ** Iterate through the list of headers and call the function to
+       ** MIME-ify them if required.
+       */
+       for (hp = ct->c_first_hf; hp != NULL; hp = hp->next) {
+               if (encode_rfc2047(hp->name, &hp->value, NULL)) {
+                       adios(EX_DATAERR, NULL, "Unable to encode header \"%s\"", hp->name);
+               }
+       }
+
+       /*
        ** Now add the MIME-Version header field
        ** to the list of header fields.
        */
-       np = getcpy(VRSN_FIELD);
+       np = mh_xstrdup(VRSN_FIELD);
        vp = concat(" ", VRSN_VALUE, "\n", NULL);
        add_header(ct, np, vp);
 
@@ -577,14 +418,14 @@ finish_field:
        ** draft.  So create a multipart/mixed content to hold everything.
        ** We can remove this later, if it is not needed.
        */
-       if (get_ctinfo("multipart/mixed", ct, 0) == NOTOK)
-               done(1);
+       if (get_ctinfo("multipart/mixed", ct, 0) == NOTOK) {
+               exit(EX_DATAERR);
+       }
        ct->c_type = CT_MULTIPART;
        ct->c_subtype = MULTI_MIXED;
-       ct->c_file = getcpy(infile);
+       ct->c_file = mh_xstrdup(infile);
 
-       if ((m = (struct multipart *) calloc(1, sizeof(*m))) == NULL)
-               adios(NULL, "out of memory");
+       m = (struct multipart *) mh_xcalloc(1, sizeof(*m));
        ct->c_ctparams = (void *) m;
        pp = &m->mp_parts;
 
@@ -603,8 +444,7 @@ finish_field:
                if (!p)
                        continue;
 
-               if ((part = (struct part *) calloc(1, sizeof(*part))) == NULL)
-                       adios(NULL, "out of memory");
+               part = mh_xcalloc(1, sizeof(*part));
                *pp = part;
                pp = &part->mp_next;
                part->mp_part = p;
@@ -618,7 +458,7 @@ finish_field:
 
        /* check if any contents were found */
        if (!m->mp_parts)
-               adios(NULL, "no content directives found");
+               adios(EX_OSERR, NULL, "no content directives found");
 
        /*
        ** If only one content was found, then remove and
@@ -649,7 +489,7 @@ finish_field:
        compose_content(ct);
 
        if ((cp = strchr(prefix, 'a')) == NULL)
-               adios(NULL, "internal error(4)");
+               adios(EX_SOFTWARE, NULL, "internal error(4)");
 
        /*
        ** Scan the contents.  Choose a transfer encoding, and
@@ -661,7 +501,7 @@ finish_field:
                        (*cp)++;
                } else {
                        if (*++cp == 0)
-                               adios(NULL, "giving up trying to find a unique delimiter string");
+                               adios(EX_SOFTWARE, NULL, "giving up trying to find a unique delimiter string");
                                else
                                (*cp)++;
                }
@@ -684,8 +524,7 @@ init_decoded_content(CT ct)
 {
        CE ce;
 
-       if ((ce = (CE) calloc(1, sizeof(*ce))) == NULL)
-               adios(NULL, "out of memory");
+       ce = mh_xcalloc(1, sizeof(*ce));
 
        ct->c_cefile     = ce;
        ct->c_ceopenfnx  = open7Bit;  /* since unencoded */
@@ -728,13 +567,12 @@ fgetstr(char *s, int n, FILE *stream)
 static int
 user_content(FILE *in, char *file, char *buf, CT *ctp)
 {
-       int extrnal, vrsn;
+       int vrsn;
        unsigned char *cp;
        char **ap;
        char buffer[BUFSIZ];
        struct multipart *m;
        struct part **pp;
-       struct stat st;
        struct str2init *s2i;
        CI ci;
        CT ct;
@@ -746,8 +584,7 @@ user_content(FILE *in, char *file, char *buf, CT *ctp)
        }
 
        /* allocate basic Content structure */
-       if ((ct = (CT) calloc(1, sizeof(*ct))) == NULL)
-               adios(NULL, "out of memory");
+       ct = mh_xcalloc(1, sizeof(*ct));
        *ctp = ct;
 
        /* allocate basic structure for handling decoded content */
@@ -775,10 +612,10 @@ user_content(FILE *in, char *file, char *buf, CT *ctp)
 
                cp = m_mktemp2(NULL, invo_name, NULL, &out);
                if (cp == NULL)
-                       adios("mhbuild", "unable to create temporary file");
+                       adios(EX_CANTCREAT, "mhbuild", "unable to create temporary file");
 
                /* use a temp file to collect the plain text lines */
-               ce->ce_file = getcpy(cp);
+               ce->ce_file = mh_xstrdup(cp);
                ce->ce_unlink = 1;
 
                if (buf[0] == '#' && buf[1] == '<') {
@@ -803,7 +640,7 @@ user_content(FILE *in, char *file, char *buf, CT *ctp)
 again_descr:
                                ct->c_descr = add(buffer + i + 1, ct->c_descr);
                                if (!fgetstr(buffer, sizeof(buffer) - 1, in))
-                                       adios(NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
+                                       adios(EX_DATAERR, NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
                                switch (buffer[0]) {
                                case ' ':
                                case '\t':
@@ -811,7 +648,7 @@ again_descr:
                                        goto again_descr;
 
                                case '#':
-                                       adios(NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
+                                       adios(EX_DATAERR, NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
                                        /* NOTREACHED */
 
                                default:
@@ -826,7 +663,7 @@ again_descr:
 again_dispo:
                                ct->c_dispo = add(buffer + i + 1, ct->c_dispo);
                                if (!fgetstr(buffer, sizeof(buffer) - 1, in))
-                                       adios(NULL, "end-of-file after %s: field in plaintext", DISPO_FIELD);
+                                       adios(EX_DATAERR, NULL, "end-of-file after %s: field in plaintext", DISPO_FIELD);
                                switch (buffer[0]) {
                                case ' ':
                                case '\t':
@@ -834,7 +671,7 @@ again_dispo:
                                        goto again_dispo;
 
                                case '#':
-                                       adios(NULL, "#-directive after %s: field in plaintext", DISPO_FIELD);
+                                       adios(EX_DATAERR, NULL, "#-directive after %s: field in plaintext", DISPO_FIELD);
                                        /* NOTREACHED */
 
                                default:
@@ -862,13 +699,11 @@ rock_and_roll:
                        }
                }
 
-               if (listsw)
-                       ct->c_end = ftell(out);
                fclose(out);
 
                /* parse content type */
                if (get_ctinfo(content, ct, inlineD) == NOTOK)
-                       done(1);
+                       exit(EX_DATAERR);
 
                for (s2i = str2cts; s2i->si_key; s2i++)
                        if (!mh_strcasecmp(ci->ci_type, s2i->si_key))
@@ -887,7 +722,7 @@ rock_and_roll:
                        }
                        /* else fall... */
                case CT_MULTIPART:
-                       adios(NULL, "it doesn't make sense to define an in-line %s content",
+                       adios(EX_DATAERR, NULL, "it doesn't make sense to define an in-line %s content",
                                        ct->c_type == CT_MESSAGE ? "message" :
                                        "multipart");
                        /* NOTREACHED */
@@ -909,12 +744,14 @@ call_init:
        ** must be some type of explicit directive.
        */
 
-       /* check if directive is external-type */
-       extrnal = (buf[1] == '@');
+       if (buf[1] == '@') {
+               adios(EX_DATAERR, NULL, "The #@ directive i.e. message/external-body "
+                               "is not supported anymore.");
+       }
 
        /* parse directive */
-       if (get_ctinfo(buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
-               done(1);
+       if (get_ctinfo(buf+1, ct, 1) == NOTOK)
+               exit(EX_DATAERR);
 
        /* check directive against the list of MIME types */
        for (s2i = str2cts; s2i->si_key; s2i++)
@@ -925,26 +762,26 @@ call_init:
        ** Check if the directive specified a valid type.
        ** This will happen if it was one of the following forms:
        **
-       **    #type/subtype  (or)
-       **    #@type/subtype
+       **    #type/subtype
        */
        if (s2i->si_key) {
                if (!ci->ci_subtype)
-                       adios(NULL, "missing subtype in \"#%s\"", ci->ci_type);
+                       adios(EX_DATAERR, NULL, "missing subtype in \"#%s\"", ci->ci_type);
 
                switch (ct->c_type = s2i->si_val) {
                case CT_MULTIPART:
-                       adios(NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype);
+                       adios(EX_DATAERR, NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype);
                        /* NOTREACHED */
 
                case CT_MESSAGE:
-                       if (!mh_strcasecmp(ci->ci_subtype, "partial"))
-                               adios(NULL, "sorry, \"#%s/%s\" isn't supported", ci->ci_type, ci->ci_subtype);
-                       if (!mh_strcasecmp(ci->ci_subtype, "external-body"))
-                               adios(NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype);
+                       if (!mh_strcasecmp(ci->ci_subtype, "partial") ||
+                                       !mh_strcasecmp(ci->ci_subtype,
+                                       "external-body")) {
+                               adios(EX_DATAERR, NULL, "sorry, \"#%s/%s\" isn't supported", ci->ci_type, ci->ci_subtype);
+                       }
 use_forw:
-                       adios(NULL, "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype);
-                       /* NOTREACHED */
+                       admonish(NULL, "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype);
+                       /* FALL */
 
                default:
                        if ((ct->c_ctinitfnx = s2i->si_init))
@@ -952,52 +789,6 @@ use_forw:
                        break;
                }
 
-               /*
-               ** #@type/subtype (external types directive)
-               */
-               if (extrnal) {
-                       struct exbody *e;
-                       CT p;
-
-                       if (!ci->ci_magic)
-                               adios(NULL, "need external information for \"#@%s/%s\"", ci->ci_type, ci->ci_subtype);
-                       p = ct;
-
-                       snprintf(buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
-                       free(ci->ci_magic);
-                       ci->ci_magic = NULL;
-
-                       /*
-                       ** Since we are using the current Content structure to
-                       ** hold information about the type of the external
-                       ** reference, we need to create another Content
-                       ** structure for the message/external-body to wrap
-                       ** it in.
-                       */
-                       if ((ct = (CT) calloc(1, sizeof(*ct))) == NULL)
-                               adios(NULL, "out of memory");
-                       *ctp = ct;
-                       ci = &ct->c_ctinfo;
-                       if (get_ctinfo(buffer, ct, 0) == NOTOK)
-                               done(1);
-                       ct->c_type = CT_MESSAGE;
-                       ct->c_subtype = MESSAGE_EXTERNAL;
-
-                       if ((e = (struct exbody *)
-                                       calloc(1, sizeof(*e))) == NULL)
-                               adios(NULL, "out of memory");
-                       ct->c_ctparams = (void *) e;
-
-                       e->eb_parent = ct;
-                       e->eb_content = p;
-                       p->c_ctexbody = e;
-
-                       if (params_external(ct, 1) == NOTOK)
-                               done(1);
-
-                       return OK;
-               }
-
                /* Handle [file] argument */
                if (ci->ci_magic) {
                        /* check if specifies command to execute */
@@ -1005,17 +796,15 @@ use_forw:
                                for (cp = ci->ci_magic + 1; isspace(*cp); cp++)
                                        continue;
                                if (!*cp)
-                                       adios(NULL, "empty pipe command for #%s directive", ci->ci_type);
-                               cp = getcpy(cp);
-                               free(ci->ci_magic);
+                                       adios(EX_DATAERR, NULL, "empty pipe command for #%s directive", ci->ci_type);
+                               cp = mh_xstrdup(cp);
+                               mh_free0(&(ci->ci_magic));
                                ci->ci_magic = cp;
                        } else {
                                /* record filename of decoded contents */
                                ce->ce_file = ci->ci_magic;
                                if (access(ce->ce_file, R_OK) == NOTOK)
-                                       adios("reading", "unable to access %s for", ce->ce_file);
-                               if (listsw && stat(ce->ce_file, &st) != NOTOK)
-                                       ct->c_end = (long) st.st_size;
+                                       adios(EX_IOERR, "reading", "unable to access %s for", ce->ce_file);
                                ci->ci_magic = NULL;
                        }
                        return OK;
@@ -1033,17 +822,13 @@ use_forw:
                        if ((cp = context_find(buffer)) == NULL ||
                                        *cp == '\0') {
                                content_error(NULL, ct, "don't know how to compose content");
-                               done(1);
+                               exit(EX_CONFIG);
                        }
                }
-               ci->ci_magic = getcpy(cp);
+               ci->ci_magic = mh_xstrdup(cp);
                return OK;
        }
 
-       if (extrnal)
-               adios(NULL, "external definition not allowed for \"#%s\"",
-                               ci->ci_type);
-
        /*
        ** Message directive
        ** #forw [+folder] [msgs]
@@ -1073,25 +858,25 @@ use_forw:
                        cp = *ap;
                        if (*cp == '+' || *cp == '@') {
                                if (folder)
-                                       adios(NULL, "only one folder per #forw directive");
+                                       adios(EX_USAGE, NULL, "only one folder per #forw directive");
                                else
-                                       folder = getcpy(expandfol(cp));
+                                       folder = mh_xstrdup(expandfol(cp));
                        }
                }
 
                /* else, use the current folder */
                if (!folder)
-                       folder = getcpy(getcurfol());
+                       folder = mh_xstrdup(getcurfol());
 
                if (!(mp = folder_read(folder)))
-                       adios(NULL, "unable to read folder %s", folder);
+                       adios(EX_IOERR, NULL, "unable to read folder %s", folder);
                for (ap = arguments; *ap; ap++) {
                        cp = *ap;
                        if (*cp != '+' && *cp != '@')
                                if (!m_convert(mp, cp))
-                                       done(1);
+                                       exit(EX_USAGE);
                }
-               free(folder);
+               mh_free0(&folder);
                free_ctinfo(ct);
 
                /*
@@ -1103,13 +888,11 @@ use_forw:
                if (mp->numsel > 1) {
                        /* we are forwarding multiple messages */
                        if (get_ctinfo("multipart/digest", ct, 0) == NOTOK)
-                               done(1);
+                               exit(EX_DATAERR);
                        ct->c_type = CT_MULTIPART;
                        ct->c_subtype = MULTI_DIGEST;
 
-                       if ((m = (struct multipart *)
-                                       calloc(1, sizeof(*m))) == NULL)
-                               adios(NULL, "out of memory");
+                       m = mh_xcalloc(1, sizeof(*m));
                        ct->c_ctparams = (void *) m;
                        pp = &m->mp_parts;
 
@@ -1119,27 +902,21 @@ use_forw:
                                        CT p;
                                        CE pe;
 
-                                       if ((p = (CT) calloc(1, sizeof(*p)))
-                                                       == NULL)
-                                               adios(NULL, "out of memory");
+                                       p = mh_xcalloc(1, sizeof(*p));
                                        init_decoded_content(p);
                                        pe = p->c_cefile;
                                        if (get_ctinfo("message/rfc822", p, 0)
                                                        == NOTOK)
-                                               done(1);
+                                               exit(EX_DATAERR);
                                        p->c_type = CT_MESSAGE;
                                        p->c_subtype = MESSAGE_RFC822;
 
                                        snprintf(buffer, sizeof(buffer),
                                                        "%s/%d", mp->foldpath,
                                                        msgnum);
-                                       pe->ce_file = getcpy(buffer);
-                                       if (listsw && stat(pe->ce_file, &st)
-                                                       != NOTOK)
-                                               p->c_end = (long) st.st_size;
+                                       pe->ce_file = mh_xstrdup(buffer);
 
-                                       if ((part = (struct part *) calloc(1, sizeof(*part))) == NULL)
-                                               adios(NULL, "out of memory");
+                                       part = mh_xcalloc(1, sizeof(*part));
                                        *pp = part;
                                        pp = &part->mp_next;
                                        part->mp_part = p;
@@ -1148,16 +925,14 @@ use_forw:
                } else {
                        /* we are forwarding one message */
                        if (get_ctinfo("message/rfc822", ct, 0) == NOTOK)
-                               done(1);
+                               exit(EX_DATAERR);
                        ct->c_type = CT_MESSAGE;
                        ct->c_subtype = MESSAGE_RFC822;
 
                        msgnum = mp->lowsel;
                        snprintf(buffer, sizeof(buffer), "%s/%d",
                                        mp->foldpath, msgnum);
-                       ce->ce_file = getcpy(buffer);
-                       if (listsw && stat(ce->ce_file, &st) != NOTOK)
-                               ct->c_end = (long) st.st_size;
+                       ce->ce_file = mh_xstrdup(buffer);
                }
 
                folder_free(mp);  /* free folder/message structure */
@@ -1196,12 +971,11 @@ use_forw:
                free_ctinfo(ct);
                snprintf(buffer, sizeof(buffer), "multipart/%s", cp);
                if (get_ctinfo(buffer, ct, 0) == NOTOK)
-                       done(1);
+                       exit(EX_DATAERR);
                ct->c_type = CT_MULTIPART;
                ct->c_subtype = vrsn;
 
-               if ((m = (struct multipart *) calloc(1, sizeof(*m))) == NULL)
-                       adios(NULL, "out of memory");
+               m = mh_xcalloc(1, sizeof(*m));
                ct->c_ctparams = (void *) m;
 
                pp = &m->mp_parts;
@@ -1211,15 +985,13 @@ use_forw:
 
                        if (user_content(in, file, buffer, &p) == DONE) {
                                if (!m->mp_parts)
-                                       adios(NULL, "empty \"#begin ... #end\" sequence");
+                                       adios(EX_DATAERR, NULL, "empty \"#begin ... #end\" sequence");
                                return OK;
                        }
                        if (!p)
                                continue;
 
-                       if ((part = (struct part *)
-                                       calloc(1, sizeof(*part))) == NULL)
-                               adios(NULL, "out of memory");
+                       part = mh_xcalloc(1, sizeof(*part));
                        *pp = part;
                        pp = &part->mp_next;
                        part->mp_part = p;
@@ -1231,7 +1003,7 @@ use_forw:
        /*
        ** Unknown directive
        */
-       adios(NULL, "unknown directive \"#%s\"", ci->ci_type);
+       adios(EX_DATAERR, NULL, "unknown directive \"#%s\"", ci->ci_type);
        return NOTOK;  /* NOT REACHED */
 }
 
@@ -1249,49 +1021,13 @@ set_id(CT ct, int top)
                snprintf(msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
                                (int) getpid(), (long) clock, LocalName());
                partno = 0;
-               msgfmt = getcpy(msgid);
+               msgfmt = mh_xstrdup(msgid);
        }
        snprintf(msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
-       ct->c_id = getcpy(msgid);
+       ct->c_id = mh_xstrdup(msgid);
 }
 
 
-static char ebcdicsafe[0x100] = {
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
-       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-       0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-       0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
-       0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-       0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
-
 /*
 ** Fill out, or expand the various contents in the composition
 ** draft.  Read-in any necessary files.  Parse and execute any
@@ -1326,45 +1062,10 @@ compose_content(CT ct)
                        CT p = part->mp_part;
 
                        sprintf(pp, "%d", partnum);
-                       p->c_partno = getcpy(partnam);
+                       p->c_partno = mh_xstrdup(partnam);
                        if (compose_content(p) == NOTOK)
                                return NOTOK;
                }
-
-               /*
-               ** If the -rfc934mode switch is given, then check all
-               ** the subparts of a multipart/digest.  If they are all
-               ** message/rfc822, then mark this content and all
-               ** subparts with the rfc934 compatibility mode flag.
-               */
-               if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
-                       int is934 = 1;
-
-                       for (part = m->mp_parts; part; part = part->mp_next) {
-                               CT p = part->mp_part;
-
-                               if (p->c_subtype != MESSAGE_RFC822) {
-                                       is934 = 0;
-                                       break;
-                               }
-                       }
-                       ct->c_rfc934 = is934;
-                       for (part = m->mp_parts; part; part = part->mp_next) {
-                               CT p = part->mp_part;
-
-                               if ((p->c_rfc934 = is934))
-                                       p->c_end++;
-                       }
-               }
-
-               if (listsw) {
-                       ct->c_end = (partnum = strlen(prefix) + 2) + 2;
-                       if (ct->c_rfc934)
-                               ct->c_end += 1;
-
-                       for (part = m->mp_parts; part; part = part->mp_next)
-                               ct->c_end += part->mp_part->c_end + partnum;
-               }
        }
        break;
 
@@ -1386,13 +1087,13 @@ compose_content(CT ct)
                        char *tfile = NULL;
 
                        if (!(cp = ci->ci_magic))
-                               adios(NULL, "internal error(5)");
+                               adios(EX_SOFTWARE, NULL, "internal error(5)");
 
                        tfile = m_mktemp2(NULL, invo_name, NULL, NULL);
                        if (tfile == NULL) {
-                               adios("mhbuild", "unable to create temporary file");
+                               adios(EX_CANTCREAT, "mhbuild", "unable to create temporary file");
                        }
-                       ce->ce_file = getcpy(tfile);
+                       ce->ce_file = mh_xstrdup(tfile);
                        ce->ce_unlink = 1;
 
                        xstdout = 0;
@@ -1478,11 +1179,11 @@ raw:
                        vec[3] = NULL;
 
                        if ((out = fopen(ce->ce_file, "w")) == NULL)
-                               adios(ce->ce_file, "unable to open for writing");
+                               adios(EX_IOERR, ce->ce_file, "unable to open for writing");
 
                        switch (child_id = fork()) {
                        case NOTOK:
-                               adios("fork", "unable to fork");
+                               adios(EX_OSERR, "fork", "unable to fork");
                                /* NOTREACHED */
 
                        case OK:
@@ -1492,24 +1193,16 @@ raw:
                                execvp("/bin/sh", vec);
                                fprintf(stderr, "unable to exec ");
                                perror("/bin/sh");
-                               _exit(-1);
+                               _exit(EX_OSERR);
                                /* NOTREACHED */
 
                        default:
                                fclose(out);
                                if (pidXwait(child_id, NULL))
-                                       done(1);
+                                       exit(EX_SOFTWARE);
                                break;
                        }
                }
-
-               /* Check size of file */
-               if (listsw && ct->c_end == 0L) {
-                       struct stat st;
-
-                       if (stat(ce->ce_file, &st) != NOTOK)
-                               ct->c_end = (long) st.st_size;
-               }
                break;
        }
 
@@ -1534,11 +1227,10 @@ static int
 scan_content(CT ct)
 {
        int len;
-       int check8bit = 0, contains8bit = 0;  /* check if contains 8bit data */
-       int checklinelen = 0, linelen = 0;  /* check for long lines */
-       int checkboundary = 0, boundaryclash = 0; /* check if clashes with multipart boundary   */
-       int checklinespace = 0, linespace = 0;  /* check if any line ends with space */
-       int checkebcdic = 0, ebcdicunsafe = 0;  /* check if contains ebcdic unsafe characters */
+       int check8bit = 0, contains8bit = 0;
+       int checklinelen = 0, linelen = 0;
+       int checkboundary = 0, boundaryclash = 0;
+       int checklinespace = 0, linespace = 0;  /* trailing whitespace */
        unsigned char *cp = NULL, buffer[BUFSIZ];
        struct text *t = NULL;
        FILE *in = NULL;
@@ -1584,47 +1276,22 @@ scan_content(CT ct)
        case CT_TEXT:
                check8bit = 1;
                checkboundary = 1;
-               if (ct->c_subtype == TEXT_PLAIN) {
-                       checkebcdic = 0;
-                       checklinelen = 0;
-                       checklinespace = 0;
-               } else {
-                       checkebcdic = ebcdicsw;
-                       checklinelen = 1;
-                       checklinespace = 1;
-               }
-               break;
-
-       case CT_APPLICATION:
-               check8bit = 1;
-               checkebcdic = ebcdicsw;
                checklinelen = 1;
                checklinespace = 1;
-               checkboundary = 1;
                break;
 
        case CT_MESSAGE:
                check8bit = 0;
-               checkebcdic = 0;
                checklinelen = 0;
                checklinespace = 0;
-
-               /* don't check anything for message/external */
-               if (ct->c_subtype == MESSAGE_EXTERNAL)
-                       checkboundary = 0;
-               else
-                       checkboundary = 1;
+               checkboundary = 1;
                break;
 
+       case CT_APPLICATION:
        case CT_AUDIO:
        case CT_IMAGE:
        case CT_VIDEO:
-               /*
-               ** Don't check anything for these types,
-               ** since we are forcing use of base64.
-               */
                check8bit = 0;
-               checkebcdic = 0;
                checklinelen = 0;
                checklinespace = 0;
                checkboundary = 0;
@@ -1636,13 +1303,10 @@ scan_content(CT ct)
        */
        if (check8bit || checklinelen || checklinespace || checkboundary) {
                if ((in = fopen(ce->ce_file, "r")) == NULL)
-                       adios(ce->ce_file, "unable to open for reading");
+                       adios(EX_IOERR, ce->ce_file, "unable to open for reading");
                len = strlen(prefix);
 
                while (fgets(buffer, sizeof(buffer) - 1, in)) {
-                       /*
-                       ** Check for 8bit data.
-                       */
                        if (check8bit) {
                                for (cp = buffer; *cp; cp++) {
                                        if (!isascii(*cp)) {
@@ -1650,30 +1314,14 @@ scan_content(CT ct)
                                                /* no need to keep checking */
                                                check8bit = 0;
                                        }
-                                       /*
-                                       ** Check if character is ebcdic-safe.
-                                       ** We only check this if also checking
-                                       ** for 8bit data.
-                                       */
-                                       if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
-                                               ebcdicunsafe = 1;
-                                               /* no need to keep checking */
-                                               checkebcdic = 0;
-                                       }
                                }
                        }
 
-                       /*
-                       ** Check line length.
-                       */
                        if (checklinelen && (strlen(buffer) > CPERLIN + 1)) {
                                linelen = 1;
                                checklinelen = 0;  /* no need to keep checking */
                        }
 
-                       /*
-                       ** Check if line ends with a space.
-                       */
                        if (checklinespace &&
                                        (cp = buffer + strlen(buffer) - 2) >
                                        buffer && isspace(*cp)) {
@@ -1727,7 +1375,7 @@ scan_content(CT ct)
                                                NULL);
                        } else {
                                t->tx_charset = CHARSET_USASCII;
-                               *ap = getcpy("charset=us-ascii");
+                               *ap = mh_xstrdup("charset=us-ascii");
                        }
 
                        cp = strchr(*ap++, '=');
@@ -1736,32 +1384,25 @@ scan_content(CT ct)
                        *ep = cp;
                }
 
-               if (contains8bit || ebcdicunsafe || linelen || linespace ||
-                               checksw)
+               if (contains8bit || linelen || linespace)
                        ct->c_encoding = CE_QUOTED;
                else
                        ct->c_encoding = CE_7BIT;
                break;
 
-       case CT_APPLICATION:
-               /* For application type, use base64, except when postscript */
-               if (contains8bit || ebcdicunsafe || linelen || linespace ||
-                               checksw)
-                       ct->c_encoding = (ct->c_subtype ==
-                                       APPLICATION_POSTSCRIPT) ?
-                                       CE_QUOTED : CE_BASE64;
-               else
-                       ct->c_encoding = CE_7BIT;
-               break;
-
        case CT_MESSAGE:
                ct->c_encoding = CE_7BIT;
                break;
 
+       case CT_APPLICATION:
        case CT_AUDIO:
        case CT_IMAGE:
        case CT_VIDEO:
-               /* For audio, image, and video contents, just use base64 */
+               /*
+               ** Forcing use of base64, because these types likely
+               ** contain binary data and NUL bytes. Don't care about
+               ** files that would be clean.
+               */
                ct->c_encoding = CE_BASE64;
                break;
        }
@@ -1779,7 +1420,7 @@ scan_content(CT ct)
 static int
 build_headers(CT ct)
 {
-       int cc, mailbody, len;
+       int cc, len;
        char **ap, **ep;
        char *np, *vp, buffer[BUFSIZ];
        CI ci = &ct->c_ctinfo;
@@ -1796,44 +1437,27 @@ build_headers(CT ct)
                ep = ci->ci_values;
                snprintf(buffer, sizeof(buffer), "boundary=%s%d",
                                prefix, level++);
-               cp = strchr(*ap++ = getcpy(buffer), '=');
+               cp = strchr(*ap++ = mh_xstrdup(buffer), '=');
                *ap = NULL;
                *cp++ = '\0';
                *ep = cp;
        }
 
        /*
-       ** Skip the output of Content-Type, parameters, content
-       ** description and disposition, and Content-ID if the
-       ** content is of type "message" and the rfc934 compatibility
-       ** flag is set (which means we are inside multipart/digest
-       ** and the switch -rfc934mode was given).
-       */
-       if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
-               goto skip_headers;
-
-       /*
        ** output the content type and subtype
        */
-       np = getcpy(TYPE_FIELD);
+       np = mh_xstrdup(TYPE_FIELD);
        vp = concat(" ", ci->ci_type, "/", ci->ci_subtype, NULL);
 
        /* keep track of length of line */
        len = strlen(TYPE_FIELD) + strlen(ci->ci_type) +
                        strlen(ci->ci_subtype) + 3;
 
-       mailbody = ct->c_type == CT_MESSAGE &&
-                       ct->c_subtype == MESSAGE_EXTERNAL &&
-                       ((struct exbody *) ct->c_ctparams)->eb_body;
-
        /*
        ** Append the attribute/value pairs to
        ** the end of the Content-Type line.
        */
        for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
-               if (mailbody && !mh_strcasecmp(*ap, "body"))
-                       continue;
-
                vp = add(";", vp);
                len++;
 
@@ -1869,10 +1493,10 @@ build_headers(CT ct)
        add_header(ct, np, vp);
 
        /*
-       ** output the Content-ID, unless disabled by -nocontentid
+       ** output the Content-ID
        */
-       if (contentidsw && ct->c_id) {
-               np = getcpy(ID_FIELD);
+       if (ct->c_id) {
+               np = mh_xstrdup(ID_FIELD);
                vp = concat(" ", ct->c_id, NULL);
                add_header(ct, np, vp);
        }
@@ -1881,8 +1505,11 @@ build_headers(CT ct)
        ** output the Content-Description
        */
        if (ct->c_descr) {
-               np = getcpy(DESCR_FIELD);
+               np = mh_xstrdup(DESCR_FIELD);
                vp = concat(" ", ct->c_descr, NULL);
+               if (encode_rfc2047(DESCR_FIELD, &vp, NULL)) {
+                       adios(EX_DATAERR, NULL, "Unable to encode %s header", DESCR_FIELD);
+               }
                add_header(ct, np, vp);
        }
 
@@ -1890,30 +1517,11 @@ build_headers(CT ct)
        ** output the Content-Disposition
        */
        if (ct->c_dispo) {
-               np = getcpy(DISPO_FIELD);
+               np = mh_xstrdup(DISPO_FIELD);
                vp = concat(" ", ct->c_dispo, NULL);
                add_header(ct, np, vp);
        }
 
-skip_headers:
-       /*
-       ** If this is the internal content structure for a
-       ** "message/external", then we are done with the
-       ** headers (since it has no body).
-       */
-       if (ct->c_ctexbody)
-               return OK;
-
-       /*
-       ** output the Content-MD5
-       */
-       if (checksw) {
-               np = getcpy(MD5_FIELD);
-               vp = calculate_digest(ct, (ct->c_encoding == CE_QUOTED) ?
-                               1 : 0);
-               add_header(ct, np, vp);
-       }
-
        /*
        ** output the Content-Transfer-Encoding
        */
@@ -1924,42 +1532,42 @@ skip_headers:
 
        case CE_8BIT:
                if (ct->c_type == CT_MESSAGE)
-                       adios(NULL, "internal error, invalid encoding");
+                       adios(EX_DATAERR, NULL, "internal error, invalid encoding");
 
-               np = getcpy(ENCODING_FIELD);
+               np = mh_xstrdup(ENCODING_FIELD);
                vp = concat(" ", "8bit", "\n", NULL);
                add_header(ct, np, vp);
                break;
 
        case CE_QUOTED:
                if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
-                       adios(NULL, "internal error, invalid encoding");
+                       adios(EX_DATAERR, NULL, "internal error, invalid encoding");
 
-               np = getcpy(ENCODING_FIELD);
+               np = mh_xstrdup(ENCODING_FIELD);
                vp = concat(" ", "quoted-printable", "\n", NULL);
                add_header(ct, np, vp);
                break;
 
        case CE_BASE64:
                if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
-                       adios(NULL, "internal error, invalid encoding");
+                       adios(EX_DATAERR, NULL, "internal error, invalid encoding");
 
-               np = getcpy(ENCODING_FIELD);
+               np = mh_xstrdup(ENCODING_FIELD);
                vp = concat(" ", "base64", "\n", NULL);
                add_header(ct, np, vp);
                break;
 
        case CE_BINARY:
                if (ct->c_type == CT_MESSAGE)
-                       adios(NULL, "internal error, invalid encoding");
+                       adios(EX_DATAERR, NULL, "internal error, invalid encoding");
 
-               np = getcpy(ENCODING_FIELD);
+               np = mh_xstrdup(ENCODING_FIELD);
                vp = concat(" ", "binary", "\n", NULL);
                add_header(ct, np, vp);
                break;
 
        default:
-               adios(NULL, "unknown transfer encoding in content");
+               adios(EX_DATAERR, NULL, "unknown transfer encoding in content");
                break;
        }
 
@@ -1982,15 +1590,6 @@ skip_headers:
        }
                break;
 
-       case CT_MESSAGE:
-               if (ct->c_subtype == MESSAGE_EXTERNAL) {
-                       struct exbody *e;
-
-                       e = (struct exbody *) ct->c_ctparams;
-                       build_headers(e->eb_content);
-               }
-               break;
-
        default:
                /* Nothing to do */
                break;
@@ -1998,96 +1597,3 @@ skip_headers:
 
        return OK;
 }
-
-
-static char nib2b64[0x40+1] =
-       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-static char *
-calculate_digest(CT ct, int asciiP)
-{
-       int cc;
-       char buffer[BUFSIZ], *vp, *op;
-       unsigned char *dp;
-       unsigned char digest[16];
-       unsigned char outbuf[25];
-       FILE *in;
-       MD5_CTX mdContext;
-       CE ce = ct->c_cefile;
-
-       /* open content */
-       if ((in = fopen(ce->ce_file, "r")) == NULL)
-               adios(ce->ce_file, "unable to open for reading");
-
-       /* Initialize md5 context */
-       MD5Init(&mdContext);
-
-       /* calculate md5 message digest */
-       if (asciiP) {
-               while (fgets(buffer, sizeof(buffer) - 1, in)) {
-                       char c, *cp;
-
-                       cp = buffer + strlen(buffer) - 1;
-                       if ((c = *cp) == '\n')
-                               *cp = '\0';
-
-                       MD5Update(&mdContext, (unsigned char *) buffer,
-                                       (unsigned int) strlen(buffer));
-
-                       if (c == '\n')
-                               MD5Update(&mdContext, (unsigned char *) "\r\n",
-                                                2);
-               }
-       } else {
-               while ((cc = fread(buffer, sizeof(*buffer), sizeof(buffer),
-                               in)) > 0)
-                       MD5Update(&mdContext, (unsigned char *) buffer,
-                                       (unsigned int) cc);
-       }
-
-       /* md5 finalization.  Write digest and zero md5 context */
-       MD5Final(digest, &mdContext);
-
-       /* close content */
-       fclose(in);
-
-       /* print debugging info */
-       if (debugsw) {
-               unsigned char *ep;
-
-               fprintf(stderr, "MD5 digest=");
-               for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
-                       dp < ep; dp++)
-                       fprintf(stderr, "%02x", *dp & 0xff);
-               fprintf(stderr, "\n");
-       }
-
-       /* encode the digest using base64 */
-       for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
-               cc > 0; cc -= 3, op += 4) {
-               unsigned long bits;
-               char *bp;
-
-               bits = (*dp++ & 0xff) << 16;
-               if (cc > 1) {
-                       bits |= (*dp++ & 0xff) << 8;
-                       if (cc > 2)
-                               bits |= *dp++ & 0xff;
-               }
-
-               for (bp = op + 4; bp > op; bits >>= 6)
-                       *--bp = nib2b64[bits & 0x3f];
-               if (cc < 3) {
-                       *(op + 3) = '=';
-                       if (cc < 2)
-                               *(op + 2) = '=';
-               }
-       }
-
-       /* null terminate string */
-       outbuf[24] = '\0';
-
-       /* now make copy and return string */
-       vp = concat(" ", outbuf, "\n", NULL);
-       return vp;
-}