]> diplodocus.org Git - nmh/blobdiff - uip/mhlsbr.c
Reverted commit 9a4b4a3d3b27fe4a7ff6d0b8724ce1c06b5917eb.
[nmh] / uip / mhlsbr.c
index e961b9b0e0f6058c765c4f72d6334d9b7320e38c..c56acff973cf8c236beb49a0b39e6eb3e02231f3 100644 (file)
 #include <h/fmt_scan.h>
 #include <h/tws.h>
 #include <h/utils.h>
-#include <h/m_setjmp.h>
-#include <signal.h>
-#include <errno.h>
-#include <sys/wait.h>
+#include <setjmp.h>
 #include <sys/types.h>
 
 /*
 
 #define        QUOTE   '\\'
 
-static struct swit mhlswitches[] = {
-#define        BELLSW         0
-    { "bell", 0 },
-#define        NBELLSW        1
-    { "nobell", 0 },
-#define        CLRSW          2
-    { "clear", 0 },
-#define        NCLRSW         3
-    { "noclear", 0 },
-#define        FACESW         4
-    { "faceproc program", 0 },
-#define        NFACESW        5
-    { "nofaceproc", 0 },
-#define        FOLDSW         6
-    { "folder +folder", 0 },
-#define        FORMSW         7
-    { "form formfile", 0 },
-#define        PROGSW         8
-    { "moreproc program", 0 },
-#define        NPROGSW        9
-    { "nomoreproc", 0 },
-#define        LENSW         10
-    { "length lines", 0 },
-#define        WIDTHSW       11
-    { "width columns", 0 },
-#define        SLEEPSW       12
-    { "sleep seconds",  0 },
-#define        BITSTUFFSW    13
-    { "dashstuffing", -12 },   /* interface from forw */
-#define        NBITSTUFFSW   14
-    { "nodashstuffing", -14 }, /* interface from forw */
-#define VERSIONSW     15
-    { "version", 0 },
-#define        HELPSW        16
-    { "help", 0 },
-#define        FORW1SW       17
-    { "forward", -7 },         /* interface from forw */
-#define        FORW2SW       18
-    { "forwall", -7 },         /* interface from forw */
-#define        DGSTSW        19
-    { "digest list", -6 },
-#define VOLUMSW       20
-    { "volume number", -6 },
-#define ISSUESW       21
-    { "issue number", -5 },
-#define NBODYSW       22
-    { "nobody", -6 },
-    { NULL, 0 }
-};
+#define MHL_SWITCHES \
+    X("bell", 0, BELLSW) \
+    X("nobell", 0, NBELLSW) \
+    X("clear", 0, CLRSW) \
+    X("noclear", 0, NCLRSW) \
+    X("folder +folder", 0, FOLDSW) \
+    X("form formfile", 0, FORMSW) \
+    X("moreproc program", 0, PROGSW) \
+    X("nomoreproc", 0, NPROGSW) \
+    X("length lines", 0, LENSW) \
+    X("width columns", 0, WIDTHSW) \
+    X("sleep seconds", 0, SLEEPSW) \
+    X("dashstuffing", -12, BITSTUFFSW)    /* interface from forw */ \
+    X("nodashstuffing", -14, NBITSTUFFSW) /* interface from forw */ \
+    X("version", 0, VERSIONSW) \
+    X("help", 0, HELPSW) \
+    X("forward", -7, FORW1SW)             /* interface from forw */ \
+    X("forwall", -7, FORW2SW)             /* interface from forw */ \
+    X("digest list", -6, DGSTSW) \
+    X("volume number", -6, VOLUMSW) \
+    X("issue number", -5, ISSUESW) \
+    X("nobody", -6, NBODYSW) \
+    X("fmtproc program", 0, FMTPROCSW) \
+    X("nofmtproc", 0, NFMTPROCSW) \
+
+#define X(sw, minchars, id) id,
+DEFINE_SWITCH_ENUM(MHL);
+#undef X
+
+#define X(sw, minchars, id) { sw, minchars, id },
+DEFINE_SWITCH_ARRAY(MHL, mhlswitches);
+#undef X
 
 #define NOCOMPONENT 0x000001   /* don't show component name         */
 #define UPPERCASE   0x000002   /* display in all upper case         */
@@ -108,28 +88,82 @@ static struct swit mhlswitches[] = {
 #define        DATEFMT     0x000800    /* contains dates                    */
 #define        FORMAT      0x001000    /* parse address/date/RFC-2047 field */
 #define        INIT        0x002000    /* initialize component              */
-#define        FACEFMT     0x004000    /* contains face                     */
-#define        FACEDFLT    0x008000    /* default for face                  */
+#define RTRIM       0x004000   /* trim trailing whitespace          */
 #define        SPLIT       0x010000    /* split headers (don't concatenate) */
 #define        NONEWLINE   0x020000    /* don't write trailing newline      */
 #define NOWRAP      0x040000   /* Don't wrap lines ever             */
 #define FMTFILTER   0x080000   /* Filter through format filter      */
-#define        LBITS   "\020\01NOCOMPONENT\02UPPERCASE\03CENTER\04CLEARTEXT\05EXTRA\06HDROUTPUT\07CLEARSCR\010LEFTADJUST\011COMPRESS\012ADDRFMT\013BELL\014DATEFMT\015FORMAT\016INIT\017FACEFMT\020FACEDFLT\021SPLIT\022NONEWLINE\023NOWRAP\024FMTFILTER"
+#define INVISIBLE   0x100000   /* count byte in display columns?    */
+#define FORCE7BIT   0x200000   /* don't display 8-bit bytes         */
+#define        LBITS   "\020\01NOCOMPONENT\02UPPERCASE\03CENTER\04CLEARTEXT\05EXTRA\06HDROUTPUT\07CLEARSCR\010LEFTADJUST\011COMPRESS\012ADDRFMT\013BELL\014DATEFMT\015FORMAT\016INIT\017RTRIM\021SPLIT\022NONEWLINE\023NOWRAP\024FMTFILTER\025INVISIBLE\026FORCE7BIT"
 #define        GFLAGS  (NOCOMPONENT | UPPERCASE | CENTER | LEFTADJUST | COMPRESS | SPLIT | NOWRAP)
 
+/*
+ * A format string to be used as a command-line argument to the body
+ * format filter.
+ */
+
+struct arglist {
+    struct format *a_fmt;
+    char *a_nfs;
+    struct arglist *a_next;
+};
+
+/*
+ * Linked list of command line arguments for the body format filter.  This
+ * USED to be in "struct mcomp", but the format API got cleaned up and even
+ * though it reduced the code we had to do, it make things more complicated
+ * for us.  Specifically:
+ *
+ * - The interface to the hash table has been cleaned up, which means the
+ *   rooting around in the hash table is no longer necessary (yay!).  But
+ *   this ALSO means that we have to make sure that we call our format
+ *   compilation routines before we process the message, because the
+ *   components need to be visible in the hash table so we can save them for
+ *   later.  So we moved them out of "mcomp" and now compile them right before
+ *   header processing starts.
+ * - We also use format strings to handle other components in the mhl
+ *   configuration (using "formatfield" and "decode"), but here life
+ *   gets complicated: they aren't dealt with in the normal way.  Instead
+ *   of referring to a component like {from}, each component is processed
+ *   using the special {text} component.  But these format strings need to be
+ *   compiled BEFORE we compile the format arguments; in the previous
+ *   implementation they were compiled and scanned as the headers were
+ *   read, and that would reset the hash table that we need to populate
+ *   the components used by the body format filter.  So we are compiling
+ *   the formatfield component strings ahead of time and then scanning them
+ *   later.
+ *
+ * Okay, fine ... this was broken before.  But you know what?  Fixing this
+ * the right way will make things easier down the road.
+ *
+ * One side-effect to this change: format strings are now compiled only once
+ * for components specified with "formatfield", but they are compiled for
+ * every message for format arguments.
+ */
+
+static struct arglist *arglist_head;
+static struct arglist *arglist_tail;
+static int filter_nargs = 0;
+
+/*
+ * Flags/options for each component
+ */
+
 struct mcomp {
     char *c_name;              /* component name          */
     char *c_text;              /* component text          */
     char *c_ovtxt;             /* text overflow indicator */
     char *c_nfs;               /* iff FORMAT              */
     struct format *c_fmt;      /*   ..                    */
-    char *c_face;              /* face designator         */
+    struct comp *c_c_text;     /* Ref to {text} in FORMAT */
+    struct comp *c_c_error;    /* Ref to {error}          */
     int c_offset;              /* left margin indentation */
     int c_ovoff;               /* overflow indentation    */
     int c_width;               /* width of field          */
     int c_cwidth;              /* width of component      */
     int c_length;              /* length in lines         */
-    long c_flags;
+    unsigned long c_flags;
     struct mcomp *c_next;
 };
 
@@ -139,21 +173,21 @@ static struct mcomp *fmthd = NULL;
 static struct mcomp *fmttl = NULL;
 
 static struct mcomp global = {
-    NULL, NULL, NULL, NULL, NULL, NULL, 0, -1, 80, -1, 40, BELL, NULL
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, -1, 80, -1, 40, BELL, NULL
 };
 
 static struct mcomp holder = {
-    NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, NOCOMPONENT, NULL
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, NOCOMPONENT, NULL
 };
 
 struct pair {
     char *p_name;
-    long p_flags;
+    unsigned long p_flags;
 };
 
 static struct pair pairs[] = {
     { "Date",            DATEFMT },
-    { "From",            ADDRFMT|FACEDFLT },
+    { "From",            ADDRFMT },
     { "Sender",          ADDRFMT },
     { "Reply-To",        ADDRFMT },
     { "To",              ADDRFMT },
@@ -166,14 +200,13 @@ static struct pair pairs[] = {
     { "Resent-To",       ADDRFMT },
     { "Resent-cc",       ADDRFMT },
     { "Resent-Bcc",      ADDRFMT },
-    { "Face",            FACEFMT },
     { NULL,              0 }
 };
 
 struct triple {
     char *t_name;
-    long t_on;
-    long t_off;
+    unsigned long t_on;
+    unsigned long t_off;
 };
 
 static struct triple triples[] = {
@@ -191,6 +224,8 @@ static struct triple triples[] = {
     { "nocompress",    0,           COMPRESS },
     { "split",         SPLIT,       0 },
     { "nosplit",       0,           SPLIT },
+    { "rtrim",         RTRIM,       0 },
+    { "nortrim",       0,           RTRIM },
     { "addrfield",     ADDRFMT,     DATEFMT },
     { "bell",          BELL,        0 },
     { "nobell",        0,           BELL },
@@ -204,6 +239,22 @@ static struct triple triples[] = {
     { NULL,            0,           0 }
 };
 
+static char *addrcomps[] = {
+    "from",
+    "sender",
+    "reply-to",
+    "to",
+    "cc",
+    "bcc",
+    "resent-from",
+    "resent-sender",
+    "resent-reply-to",
+    "resent-to",
+    "resent-cc",
+    "resent-bcc",
+    NULL
+};
+
 
 static int bellflg   = 0;
 static int clearflg  = 0;
@@ -221,6 +272,8 @@ static int issue = 0;
 static int exitstat = 0;
 static int mhldebug = 0;
 
+static int filesize = 0;
+
 #define        PITTY   (-1)
 #define        NOTTY   0
 #define        ISTTY   1
@@ -237,7 +290,7 @@ static unsigned int wid;
 
 static char *ovtxt;
 
-static unsigned char *onelp;
+static char *onelp;
 
 static char *parptr;
 
@@ -253,7 +306,6 @@ static char delim4[] = "\n------------------------------\n\n";
 
 static FILE *(*mhl_action) () = (FILE *(*) ()) 0;
 
-
 /*
  * Redefine a couple of functions.
  * These are undefined later in the code.
@@ -272,48 +324,44 @@ static char *parse (void);
 static void process (char *, char *, int, int);
 static void mhlfile (FILE *, char *, int, int);
 static int mcomp_flags (char *);
-static char *mcomp_add (long, char *, char *);
+static char *mcomp_add (unsigned long, char *, char *);
 static void mcomp_format (struct mcomp *, struct mcomp *);
 static struct mcomp *add_queue (struct mcomp **, struct mcomp **, char *, char *, int);
 static void free_queue (struct mcomp **, struct mcomp **);
 static void putcomp (struct mcomp *, struct mcomp *, int);
-static char *oneline (char *, long);
-static void putstr (char *, long);
-static void putch (char, long);
+static char *oneline (char *, unsigned long);
+static void putstr (char *, unsigned long);
+static void putch (char, unsigned long);
 static void intrser (int);
 static void pipeser (int);
 static void quitser (int);
-static void face_format (struct mcomp *);
-static int doface (struct mcomp *);
 static void mhladios (char *, char *, ...);
 static void mhldone (int);
-static void m_popen (char *);
-static void filterbody (struct mcomp *, char *, int, int, FILE *);
+static void filterbody (struct mcomp *, char *, int, int, FILE *,
+                        m_getfld_state_t);
+static void compile_formatfield(struct mcomp *);
+static void compile_filterargs (void);
 
 
 int
 mhl (int argc, char **argv)
 {
     int length = 0, nomore = 0;
-    int i, width = 0, vecp = 0;
+    unsigned int i, vecp = 0;
+    int width = 0;
     char *cp, *folder = NULL, *form = NULL;
     char buf[BUFSIZ], *files[MAXARGS];
     char **argp, **arguments;
 
+    /* Need this if called from main() of show(1). */
     invo_name = r1bindex (argv[0], '/');
 
-    /* read user profile/context */
-    context_read();
-
     arguments = getarguments (invo_name, argc, argv, 1);
     argp = arguments;
 
     if ((cp = getenv ("MHLDEBUG")) && *cp)
        mhldebug++;
 
-    if ((cp = getenv ("FACEPROC")))
-       faceproc = cp;
-
     while ((cp = *argp++)) {
        if (*cp == '-') {
            switch (smatch (++cp, mhlswitches)) {
@@ -326,10 +374,10 @@ mhl (int argc, char **argv)
                case HELPSW: 
                    snprintf (buf, sizeof(buf), "%s [switches] [files ...]", invo_name);
                    print_help (buf, mhlswitches, 1);
-                   done (1);
+                   done (0);
                case VERSIONSW:
                    print_version(invo_name);
-                   done (1);
+                   done (0);
 
                case BELLSW: 
                    bellflg = 1;
@@ -354,17 +402,11 @@ mhl (int argc, char **argv)
                        adios (NULL, "missing argument to %s", argp[-2]);
                    continue;
 
-               case FACESW:
-                   if (!(faceproc = *argp++) || *faceproc == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
-                   continue;
-               case NFACESW:
-                   faceproc = NULL;
-                   continue;
                case SLEEPSW:
                    if (!(cp = *argp++) || *cp == '-')
                        adios (NULL, "missing argument to %s", argp[-2]);
-                   sleepsw = atoi (cp);/* ZERO ok! */
+                   else
+                       sleepsw = atoi (cp);/* ZERO ok! */
                    continue;
 
                case PROGSW:
@@ -375,16 +417,24 @@ mhl (int argc, char **argv)
                    nomore++;
                    continue;
 
+               case FMTPROCSW:
+                   if (!(formatproc = *argp++) || *formatproc == '-')
+                       adios (NULL, "missing argument to %s", argp[-2]);
+                   continue;
+               case NFMTPROCSW:
+                   formatproc = NULL;
+                   continue;
+
                case LENSW: 
                    if (!(cp = *argp++) || *cp == '-')
                        adios (NULL, "missing argument to %s", argp[-2]);
-                   if ((length = atoi (cp)) < 1)
+                   else if ((length = atoi (cp)) < 1)
                        adios (NULL, "bad argument %s %s", argp[-2], cp);
                    continue;
                case WIDTHSW: 
                    if (!(cp = *argp++) || *cp == '-')
                        adios (NULL, "missing argument to %s", argp[-2]);
-                   if ((width = atoi (cp)) < 1)
+                   else if ((width = atoi (cp)) < 1)
                        adios (NULL, "bad argument %s %s", argp[-2], cp);
                    continue;
 
@@ -395,13 +445,13 @@ mhl (int argc, char **argv)
                case ISSUESW:
                    if (!(cp = *argp++) || *cp == '-')
                        adios (NULL, "missing argument to %s", argp[-2]);
-                   if ((issue = atoi (cp)) < 1)
+                   else if ((issue = atoi (cp)) < 1)
                        adios (NULL, "bad argument %s %s", argp[-2], cp);
                    continue;
                case VOLUMSW:
                    if (!(cp = *argp++) || *cp == '-')
                        adios (NULL, "missing argument to %s", argp[-2]);
-                   if ((volume = atoi (cp)) < 1)
+                   else if ((volume = atoi (cp)) < 1)
                        adios (NULL, "bad argument %s %s", argp[-2], cp);
                    continue;
 
@@ -431,13 +481,13 @@ mhl (int argc, char **argv)
        folder = getenv ("mhfolder");
 
     if (isatty (fileno (stdout))) {
-       if (!nomore && !sc_hardcopy() && moreproc && *moreproc != '\0') {
+       if (!nomore && moreproc && *moreproc != '\0') {
            if (mhl_action) {
                SIGNAL (SIGINT, SIG_IGN);
                SIGNAL2 (SIGQUIT, quitser);
            }
            SIGNAL2 (SIGPIPE, pipeser);
-           m_popen (moreproc);
+           m_popen (moreproc, mhl_action != NULL);
            ontty = PITTY;
        } else {
            SIGNAL (SIGINT, SIG_IGN);
@@ -459,7 +509,7 @@ mhl (int argc, char **argv)
 
     if (forwall) {
        if (digest) {
-           printf ("%s", delim4);
+           fputs(delim4, stdout);
            if (volume == 0) {
                snprintf (buf, sizeof(buf), "End of %s Digest\n", digest);
            } else {
@@ -471,7 +521,7 @@ mhl (int argc, char **argv)
                *cp++ = '*';
            *cp++ = '\n';
            *cp = 0;
-           printf ("%s", buf);
+           fputs(buf, stdout);
        }
        else
            printf ("\n------- End of Forwarded Message%s\n",
@@ -484,7 +534,7 @@ mhl (int argc, char **argv)
     }
     
     if (clearflg > 0 && ontty == NOTTY)
-       clear_screen ();
+       nmh_clear_screen ();
 
     if (ontty == PITTY)
        m_pclose ();
@@ -497,8 +547,8 @@ static void
 mhl_format (char *file, int length, int width)
 {
     int i;
-    char *bp, *cp, **ip;
-    char *ap, buffer[BUFSIZ], name[NAMESZ];
+    char *bp, **ip;
+    char *ap, name[NAMESZ];
     struct mcomp *c1;
     struct stat st;
     FILE *fp;
@@ -512,8 +562,7 @@ mhl_format (char *file, int length, int width)
                && dev == st.st_dev
                && ino == st.st_ino)
            goto out;
-       else
-           free_queue (&fmthd, &fmttl);
+        free_queue (&fmthd, &fmttl);
     }
 
     if ((fp = fopen (etcpath (file), "r")) == NULL)
@@ -536,17 +585,17 @@ mhl_format (char *file, int length, int width)
        global.c_length = i - 1;
     global.c_flags = BELL;             /* BELL is default */
     *(ip = ignores) = NULL;
+    filter_nargs = 0;
 
     while (vfgets (fp, &ap) == OK) {
        bp = ap;
        if (*bp == ';')
            continue;
 
-       if ((cp = strchr(bp, '\n')))
-           *cp = 0;
+        TrimSuffixC(bp, '\n');
 
        if (*bp == ':') {
-           c1 = add_queue (&fmthd, &fmttl, NULL, bp + 1, CLEARTEXT);
+           (void) add_queue (&fmthd, &fmttl, NULL, bp + 1, CLEARTEXT);
            continue;
        }
 
@@ -560,12 +609,12 @@ mhl_format (char *file, int length, int width)
                 * Split this list of fields to ignore, and copy
                 * it to the end of the current "ignores" list.
                 */
-               if (!mh_strcasecmp (name, "ignores")) {
+               if (!strcasecmp (name, "ignores")) {
                    char **tmparray, **p;
                    int n = 0;
 
                    /* split the fields */
-                   tmparray = brkstring (getcpy (++parptr), ",", NULL);
+                   tmparray = brkstring (mh_xstrdup(++parptr), ",", NULL);
 
                    /* count number of fields split */
                    p = tmparray;
@@ -595,13 +644,17 @@ mhl_format (char *file, int length, int width)
                }
                if (!c1->c_nfs && global.c_nfs) {
                    if (c1->c_flags & DATEFMT) {
-                       if (global.c_flags & DATEFMT)
-                           c1->c_nfs = getcpy (global.c_nfs);
+                       if (global.c_flags & DATEFMT) {
+                           c1->c_nfs = mh_xstrdup(global.c_nfs);
+                           compile_formatfield(c1);
+                       }
                    }
                    else
                        if (c1->c_flags & ADDRFMT) {
-                           if (global.c_flags & ADDRFMT)
-                               c1->c_nfs = getcpy (global.c_nfs);
+                           if (global.c_flags & ADDRFMT) {
+                               c1->c_nfs = mh_xstrdup(global.c_nfs);
+                               compile_formatfield(c1);
+                           }
                        }
                }
                continue;
@@ -614,6 +667,8 @@ mhl_format (char *file, int length, int width)
 
     if (mhldebug) {
        for (c1 = fmthd; c1; c1 = c1->c_next) {
+           char buffer[BUFSIZ];
+
            fprintf (stderr, "c1: name=\"%s\" text=\"%s\" ovtxt=\"%s\"\n",
                    c1->c_name, c1->c_text, c1->c_ovtxt);
            fprintf (stderr, "\tnfs=0x%x fmt=0x%x\n",
@@ -665,56 +720,80 @@ evalvar (struct mcomp *c1)
        return 0;
     strncpy (name, parse(), sizeof(name));
 
-    if (!mh_strcasecmp (name, "component")) {
+    if (!strcasecmp (name, "component")) {
        if (ptos (name, &c1->c_text))
            return 1;
        c1->c_flags &= ~NOCOMPONENT;
        return 0;
     }
 
-    if (!mh_strcasecmp (name, "overflowtext"))
+    if (!strcasecmp (name, "overflowtext"))
        return ptos (name, &c1->c_ovtxt);
 
-    if (!mh_strcasecmp (name, "formatfield")) {
-       char *nfs;
-
+    if (!strcasecmp (name, "formatfield")) {
        if (ptos (name, &cp))
            return 1;
-       nfs = new_fs (NULL, NULL, cp);
-       c1->c_nfs = getcpy (nfs);
+       c1->c_nfs = getcpy (new_fs (NULL, NULL, cp));
+       compile_formatfield(c1);
        c1->c_flags |= FORMAT;
        return 0;
     }
 
-    if (!mh_strcasecmp (name, "decode")) {
-       char *nfs;
-
-       nfs = new_fs (NULL, NULL, "%(decode{text})");
-       c1->c_nfs = getcpy (nfs);
+    if (!strcasecmp (name, "decode")) {
+       c1->c_nfs = getcpy (new_fs (NULL, NULL, "%(decode{text})"));
+       compile_formatfield(c1);
        c1->c_flags |= FORMAT;
        return 0;
     }
 
-    if (!mh_strcasecmp (name, "offset"))
+    if (!strcasecmp (name, "offset"))
        return ptoi (name, &c1->c_offset);
-    if (!mh_strcasecmp (name, "overflowoffset"))
+    if (!strcasecmp (name, "overflowoffset"))
        return ptoi (name, &c1->c_ovoff);
-    if (!mh_strcasecmp (name, "width"))
+    if (!strcasecmp (name, "width"))
        return ptoi (name, &c1->c_width);
-    if (!mh_strcasecmp (name, "compwidth"))
+    if (!strcasecmp (name, "compwidth"))
        return ptoi (name, &c1->c_cwidth);
-    if (!mh_strcasecmp (name, "length"))
+    if (!strcasecmp (name, "length"))
        return ptoi (name, &c1->c_length);
-    if (!mh_strcasecmp (name, "nodashstuffing"))
+    if (!strcasecmp (name, "nodashstuffing"))
        return (dashstuff = -1);
 
     for (ap = triples; ap->t_name; ap++)
-       if (!mh_strcasecmp (ap->t_name, name)) {
+       if (!strcasecmp (ap->t_name, name)) {
            c1->c_flags |= ap->t_on;
            c1->c_flags &= ~ap->t_off;
            return 0;
        }
 
+   if (!strcasecmp (name, "formatarg")) {
+       struct arglist *args;
+
+       if (ptos (name, &cp))
+           return 1;
+
+       if (! c1->c_name  ||  strcasecmp (c1->c_name, "body")) {
+           advise (NULL, "format filters are currently only supported on "
+                   "the \"body\" component");
+           return 1;
+       }
+
+       NEW0(args);
+
+       if (arglist_tail)
+           arglist_tail->a_next = args;
+
+       arglist_tail = args;
+
+       if (! arglist_head)
+           arglist_head = args;
+
+       args->a_nfs = getcpy (new_fs (NULL, NULL, cp));
+       filter_nargs++;
+
+       return 0;
+    }
+
     return 1;
 }
 
@@ -757,7 +836,7 @@ ptos (char *name, char **s)
     }
     c = *parptr;
     *parptr = 0;
-    *s = getcpy (cp);
+    *s = mh_xstrdup(cp);
     if ((*parptr = c) == '"')
        parptr++;
     return 0;
@@ -789,14 +868,26 @@ parse (void)
 }
 
 
+/*
+ * Process one file/message
+ */
+
 static void
 process (char *folder, char *fname, int ofilen, int ofilec)
 {
-    char *cp = NULL;
-    FILE *fp = NULL;
+    /* static to prevent "might be clobbered" warning from gcc 4.9.2: */
+    static char *cp;
+    static FILE *fp;
     struct mcomp *c1;
+    struct stat st;
+    struct arglist *ap;
+    /* volatile to prevent "might be clobbered" warning from gcc: */
+    char *volatile fname2 = fname ? fname : "(stdin)";
+
+    cp = NULL;
+    fp = NULL;
 
-    switch (m_setjmp (env)) {
+    switch (setjmp (env)) {
        case OK: 
            if (fname) {
                fp = mhl_action ? (*mhl_action) (fname) : fopen (fname, "r");
@@ -806,42 +897,56 @@ process (char *folder, char *fname, int ofilen, int ofilec)
                    return;
                }
            } else {
-               fname = "(stdin)";
                fp = stdin;
            }
-           cp = folder ? concat (folder, ":", fname, NULL) : getcpy (fname);
+           if (fstat(fileno(fp), &st) == 0) {
+               filesize = st.st_size;
+           } else {
+               filesize = 0;
+           }
+           cp = folder ? concat (folder, ":", fname2, NULL) : mh_xstrdup(fname2);
            if (ontty != PITTY)
                SIGNAL (SIGINT, intrser);
            mhlfile (fp, cp, ofilen, ofilec);  /* FALL THROUGH! */
+            free (cp);
 
-       default: 
+           for (ap = arglist_head; ap; ap = ap->a_next) {
+               fmt_free(ap->a_fmt, 0);
+               ap->a_fmt = NULL;
+           }
+
+           if (arglist_head)
+               fmt_free(NULL, 1);
+
+       default:
            if (ontty != PITTY)
                SIGNAL (SIGINT, SIG_IGN);
-           if (mhl_action == NULL && fp != stdin)
+           if (mhl_action == NULL && fp != stdin && fp != NULL)
                fclose (fp);
-           free (cp);
-           if (holder.c_text) {
-               free (holder.c_text);
-               holder.c_text = NULL;
-           }
+            mh_xfree(holder.c_text);
+            holder.c_text = NULL;
            free_queue (&msghd, &msgtl);
            for (c1 = fmthd; c1; c1 = c1->c_next)
                c1->c_flags &= ~HDROUTPUT;
            break;
     }
+
 }
 
 
 static void
 mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
 {
-    int state;
+    int state, bucket;
     struct mcomp *c1, *c2, *c3;
     char **ip, name[NAMESZ], buf[BUFSIZ];
+    m_getfld_state_t gstate = 0;
+
+    compile_filterargs();
 
     if (forwall) {
        if (digest)
-           printf ("%s", ofilen == 1 ? delim3 : delim4);
+           fputs(ofilen == 1 ? delim3 : delim4, stdout);
        else {
            printf ("\n-------");
            if (ofilen == 1)
@@ -856,7 +961,7 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
                if (ofilec > 1) {
                    if (ofilen > 1) {
                        if ((global.c_flags & CLEARSCR))
-                           clear_screen ();
+                           nmh_clear_screen ();
                        else
                            printf ("\n\n\n");
                    }
@@ -874,14 +979,16 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
                    }
                    fflush (stdout);
                    buf[0] = 0;
-                   read (fileno (stdout), buf, sizeof(buf));
+                   if (read (fileno (stdout), buf, sizeof(buf)) < 0) {
+                       advise ("stdout", "read");
+                   }
                }
                if (strchr(buf, '\n')) {
                    if ((global.c_flags & CLEARSCR))
-                       clear_screen ();
+                       nmh_clear_screen ();
                }
                else
-                   printf ("\n");
+                   putchar('\n');
                break;
 
            default: 
@@ -889,7 +996,7 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
                    if (ofilen > 1) {
                        printf ("\n\n\n");
                        if (clearflg > 0)
-                           clear_screen ();
+                           nmh_clear_screen ();
                    }
                    printf (">>> %s\n\n", mname);
                }
@@ -897,26 +1004,32 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
        }
     }
 
-    for (state = FLD;;) {
-       switch (state = m_getfld (state, name, buf, sizeof(buf), fp)) {
+    for (;;) {
+       int bufsz = sizeof buf;
+       switch (state = m_getfld (&gstate, name, buf, &bufsz, fp)) {
            case FLD: 
            case FLDPLUS: 
+               bucket = fmt_addcomptext(name, buf);
                for (ip = ignores; *ip; ip++)
-                   if (!mh_strcasecmp (name, *ip)) {
-                       while (state == FLDPLUS)
-                           state = m_getfld (state, name, buf, sizeof(buf), fp);
+                   if (!strcasecmp (name, *ip)) {
+                       while (state == FLDPLUS) {
+                           bufsz = sizeof buf;
+                           state = m_getfld (&gstate, name, buf, &bufsz, fp);
+                           fmt_appendcomp(bucket, name, buf);
+                       }
                        break;
                    }
                if (*ip)
                    continue;
 
                for (c2 = fmthd; c2; c2 = c2->c_next)
-                   if (!mh_strcasecmp (c2->c_name, name))
+                   if (!strcasecmp (c2->c_name ? c2->c_name : "", name))
                        break;
                c1 = NULL;
                if (!((c3 = c2 ? c2 : &global)->c_flags & SPLIT))
                    for (c1 = msghd; c1; c1 = c1->c_next)
-                       if (!mh_strcasecmp (c1->c_name, c3->c_name)) {
+                       if (!strcasecmp (c1->c_name ? c1->c_name : "",
+                                        c3->c_name ? c3->c_name : "")) {
                            c1->c_text =
                                mcomp_add (c1->c_flags, buf, c1->c_text);
                            break;
@@ -924,8 +1037,10 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
                if (c1 == NULL)
                    c1 = add_queue (&msghd, &msgtl, name, buf, 0);
                while (state == FLDPLUS) {
-                   state = m_getfld (state, name, buf, sizeof(buf), fp);
+                   bufsz = sizeof buf;
+                   state = m_getfld (&gstate, name, buf, &bufsz, fp);
                    c1->c_text = add (buf, c1->c_text);
+                   fmt_appendcomp(bucket, name, buf);
                }
                if (c2 == NULL)
                    c1->c_flags |= EXTRA;
@@ -939,7 +1054,8 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
                        putcomp (c1, c1, ONECOMP);
                        continue;
                    }
-                   if (!mh_strcasecmp (c1->c_name, "messagename")) {
+                   if (!c1->c_name  ||
+                       !strcasecmp (c1->c_name, "messagename")) {
                        holder.c_text = concat ("(Message ", mname, ")\n",
                                            NULL);
                        putcomp (c1, &holder, ONECOMP);
@@ -947,23 +1063,25 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
                        holder.c_text = NULL;
                        continue;
                    }
-                   if (!mh_strcasecmp (c1->c_name, "extras")) {
+                   if (!c1->c_name  ||  !strcasecmp (c1->c_name, "extras")) {
                        for (c2 = msghd; c2; c2 = c2->c_next)
                            if (c2->c_flags & EXTRA)
                                putcomp (c1, c2, TWOCOMP);
                        continue;
                    }
-                   if (dobody && !mh_strcasecmp (c1->c_name, "body")) {
+                   if (dobody && (!c1->c_name  ||
+                                  !strcasecmp (c1->c_name, "body"))) {
                        if (c1->c_flags & FMTFILTER && state == BODY &&
                                                        formatproc != NULL) {
-                           filterbody(c1, buf, sizeof(buf), state, fp);
+                           filterbody(c1, buf, sizeof(buf), state, fp, gstate);
                        } else {
                            holder.c_text = mh_xmalloc (sizeof(buf));
                            strncpy (holder.c_text, buf, sizeof(buf));
                            while (state == BODY) {
                                putcomp (c1, &holder, BODYCOMP);
-                               state = m_getfld (state, name, holder.c_text,
-                                           sizeof(buf), fp);
+                               bufsz = sizeof buf;
+                               state = m_getfld (&gstate, name, holder.c_text,
+                                           &bufsz, fp);
                            }
                            free (holder.c_text);
                            holder.c_text = NULL;
@@ -971,29 +1089,21 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
                        continue;
                    }
                    for (c2 = msghd; c2; c2 = c2->c_next)
-                       if (!mh_strcasecmp (c2->c_name, c1->c_name)) {
+                       if (!strcasecmp (c2->c_name ? c2->c_name : "",
+                                        c1->c_name ? c1->c_name : "")) {
                            putcomp (c1, c2, ONECOMP);
                            if (!(c1->c_flags & SPLIT))
                                break;
                        }
-                   if (faceproc && c2 == NULL && (c1->c_flags & FACEFMT))
-                       for (c2 = msghd; c2; c2 = c2->c_next)
-                           if (c2->c_flags & FACEDFLT) {
-                               if (c2->c_face == NULL)
-                                   face_format (c2);
-                               if ((holder.c_text = c2->c_face)) {
-                                   putcomp (c1, &holder, ONECOMP);
-                                   holder.c_text = NULL;
-                               }
-                               break;
-                           }
                }
+               m_getfld_state_destroy (&gstate);
                return;
 
            case LENERR: 
            case FMTERR: 
                advise (NULL, "format error in message %s", mname);
                exitstat++;
+               m_getfld_state_destroy (&gstate);
                return;
 
            default: 
@@ -1009,7 +1119,7 @@ mcomp_flags (char *name)
     struct pair *ap;
 
     for (ap = pairs; ap->p_name; ap++)
-       if (!mh_strcasecmp (ap->p_name, name))
+       if (!strcasecmp (ap->p_name, name))
            return (ap->p_flags);
 
     return 0;
@@ -1017,7 +1127,7 @@ mcomp_flags (char *name)
 
 
 static char *
-mcomp_add (long flags, char *s1, char *s2)
+mcomp_add (unsigned long flags, char *s1, char *s2)
 {
     char *dp;
 
@@ -1043,8 +1153,7 @@ mcomp_format (struct mcomp *c1, struct mcomp *c2)
 {
     int dat[5];
     char *ap, *cp;
-    char buffer[BUFSIZ], error[BUFSIZ];
-    struct comp *cptr;
+    char error[BUFSIZ];
     struct pqpair *p, *q;
     struct pqpair pq;
     struct mailname *mp;
@@ -1053,60 +1162,56 @@ mcomp_format (struct mcomp *c1, struct mcomp *c2)
     c2->c_text = NULL;
     dat[0] = 0;
     dat[1] = 0;
-    dat[2] = 0;
-    dat[3] = sizeof(buffer) - 1;
+    dat[2] = filesize;
+    dat[3] = BUFSIZ - 1;
     dat[4] = 0;
-    fmt_compile (c1->c_nfs, &c1->c_fmt);
 
     if (!(c1->c_flags & ADDRFMT)) {
-       FINDCOMP (cptr, "text");
-       if (cptr)
-           cptr->c_text = ap;
+       charstring_t scanl = charstring_create (BUFSIZ);
+
+       if (c1->c_c_text)
+           c1->c_c_text->c_text = ap;
        if ((cp = strrchr(ap, '\n')))   /* drop ending newline */
            if (!cp[1])
                *cp = 0;
 
-       fmt_scan (c1->c_fmt, buffer, sizeof(buffer) - 1, dat);
+       fmt_scan (c1->c_fmt, scanl, BUFSIZ - 1, dat, NULL);
        /* Don't need to append a newline, dctime() already did */
-       c2->c_text = getcpy (buffer);
+       c2->c_text = charstring_buffer_copy (scanl);
+       charstring_free (scanl);
 
-       free (ap);
+       /* ap is now owned by the component struct, so do NOT free it here */
        return;
     }
 
     (q = &pq)->pq_next = NULL;
     while ((cp = getname (ap))) {
-       if ((p = (struct pqpair *) calloc ((size_t) 1, sizeof(*p))) == NULL)
-           adios (NULL, "unable to allocate pqpair memory");
-
-       if ((mp = getm (cp, NULL, 0, AD_NAME, error)) == NULL) {
-           p->pq_text = getcpy (cp);
-           p->pq_error = getcpy (error);
-       } else {
-           if ((c1->c_flags & FACEDFLT) && c2->c_face == NULL) {
-               char   *h, *o;
-               if ((h = mp->m_host) == NULL)
-                   h = LocalName (0);
-               if ((o = OfficialName (h)))
-                   h = o;
-               c2->c_face = concat ("address ", h, " ", mp->m_mbox,
-                                   NULL);
-           }
-           p->pq_text = getcpy (mp->m_text);
-           mnfree (mp);
-       }
-       q = (q->pq_next = p);
+       NEW0(p);
+        if ((mp = getm (cp, NULL, 0, error, sizeof(error))) == NULL) {
+            p->pq_text = mh_xstrdup(cp);
+            p->pq_error = mh_xstrdup(error);
+        } else {
+            p->pq_text = getcpy (mp->m_text);
+            mnfree (mp);
+        }
+        q = (q->pq_next = p);
     }
 
     for (p = pq.pq_next; p; p = q) {
-       FINDCOMP (cptr, "text");
-       if (cptr)
-           cptr->c_text = p->pq_text;
-       FINDCOMP (cptr, "error");
-       if (cptr)
-           cptr->c_text = p->pq_error;
+       charstring_t scanl = charstring_create (BUFSIZ);
+       char *buffer;
 
-       fmt_scan (c1->c_fmt, buffer, sizeof(buffer) - 1, dat);
+       if (c1->c_c_text) {
+           c1->c_c_text->c_text = p->pq_text;
+           p->pq_text = NULL;
+       }
+       if (c1->c_c_error) {
+           c1->c_c_error->c_text = p->pq_error;
+           p->pq_error = NULL;
+       }
+
+       fmt_scan (c1->c_fmt, scanl, BUFSIZ - 1, dat, NULL);
+       buffer = charstring_buffer_copy (scanl);
        if (*buffer) {
            if (c2->c_text)
                c2->c_text = add (",\n", c2->c_text);
@@ -1114,12 +1219,12 @@ mcomp_format (struct mcomp *c1, struct mcomp *c2)
                *cp = 0;
            c2->c_text = add (buffer, c2->c_text);
        }
+       charstring_free (scanl);
 
-       free (p->pq_text);
-       if (p->pq_error)
-           free (p->pq_error);
+        mh_xfree(p->pq_text);
+        mh_xfree(p->pq_error);
        q = p->pq_next;
-       free ((char *) p);
+       free(p);
     }
 
     c2->c_text = add ("\n", c2->c_text);
@@ -1132,21 +1237,19 @@ add_queue (struct mcomp **head, struct mcomp **tail, char *name, char *text, int
 {
     struct mcomp *c1;
 
-    if ((c1 = (struct mcomp *) calloc ((size_t) 1, sizeof(*c1))) == NULL)
-       adios (NULL, "unable to allocate comp memory");
-
+    NEW0(c1);
     c1->c_flags = flags & ~INIT;
-    if ((c1->c_name = name ? getcpy (name) : NULL))
-       c1->c_flags |= mcomp_flags (c1->c_name);
-    c1->c_text = text ? getcpy (text) : NULL;
+    if ((c1->c_name = name ? mh_xstrdup(name) : NULL))
+        c1->c_flags |= mcomp_flags (c1->c_name);
+    c1->c_text = text ? mh_xstrdup(text) : NULL;
     if (flags & INIT) {
-       if (global.c_ovtxt)
-           c1->c_ovtxt = getcpy (global.c_ovtxt);
-       c1->c_offset = global.c_offset;
-       c1->c_ovoff = global. c_ovoff;
-       c1->c_width = c1->c_length = 0;
-       c1->c_cwidth = global.c_cwidth;
-       c1->c_flags |= global.c_flags & GFLAGS;
+        if (global.c_ovtxt)
+            c1->c_ovtxt = mh_xstrdup(global.c_ovtxt);
+        c1->c_offset = global.c_offset;
+        c1->c_ovoff = global. c_ovoff;
+        c1->c_width = c1->c_length = 0;
+        c1->c_cwidth = global.c_cwidth;
+        c1->c_flags |= global.c_flags & GFLAGS;
     }
     if (*head == NULL)
        *head = c1;
@@ -1165,19 +1268,13 @@ free_queue (struct mcomp **head, struct mcomp **tail)
 
     for (c1 = *head; c1; c1 = c2) {
        c2 = c1->c_next;
-       if (c1->c_name)
-           free (c1->c_name);
-       if (c1->c_text)
-           free (c1->c_text);
-       if (c1->c_ovtxt)
-           free (c1->c_ovtxt);
-       if (c1->c_nfs)
-           free (c1->c_nfs);
+        mh_xfree(c1->c_name);
+        mh_xfree(c1->c_text);
+        mh_xfree(c1->c_ovtxt);
+        mh_xfree(c1->c_nfs);
        if (c1->c_fmt)
-           free ((char *) c1->c_fmt);
-       if (c1->c_face)
-           free (c1->c_face);
-       free ((char *) c1);
+           fmt_free (c1->c_fmt, 0);
+       free(c1);
     }
 
     *head = *tail = NULL;
@@ -1187,8 +1284,22 @@ free_queue (struct mcomp **head, struct mcomp **tail)
 static void
 putcomp (struct mcomp *c1, struct mcomp *c2, int flag)
 {
+    char *text; /* c1's text, or the name as a fallback. */
+    char *trimmed_prefix;
     int count, cchdr;
-    unsigned char *cp;
+    char *cp;
+    const int utf8 = strcasecmp(get_charset(), "UTF-8") == 0;
+
+    if (! utf8  &&  flag != BODYCOMP) {
+        /* Don't print 8-bit bytes in header field values if not in a
+           UTF-8 locale, as required by RFC 6532. */
+       c1->c_flags |= FORCE7BIT;
+    }
+
+    text = c1->c_text ? c1->c_text : c1->c_name;
+    /* Create a copy with trailing whitespace trimmed, for use with
+     * blank lines. */
+    trimmed_prefix = rtrim(add(text, NULL));
 
     cchdr = 0;
     lm = 0;
@@ -1204,21 +1315,12 @@ putcomp (struct mcomp *c1, struct mcomp *c2, int flag)
     onelp = NULL;
 
     if (c1->c_flags & CLEARTEXT) {
-       putstr (c1->c_text, c1->c_flags);
+       putstr (c1->c_flags & RTRIM ? rtrim (c1->c_text) : c1->c_text,
+               c1->c_flags);
        putstr ("\n", c1->c_flags);
        return;
     }
 
-    if (c1->c_flags & FACEFMT)
-       switch (doface (c2)) {
-           case NOTOK:         /* error */
-           case OK:            /* async faceproc */
-               return;
-
-           default:            /* sync faceproc */
-               break;
-       }
-
     if (c1->c_nfs && (c1->c_flags & (ADDRFMT | DATEFMT | FORMAT)))
        mcomp_format (c1, c2);
 
@@ -1226,7 +1328,7 @@ putcomp (struct mcomp *c1, struct mcomp *c2, int flag)
        count = (c1->c_width ? c1->c_width : global.c_width)
            - c1->c_offset - strlen (c2->c_text);
        if (!(c1->c_flags & HDROUTPUT) && !(c1->c_flags & NOCOMPONENT))
-           count -= strlen (c1->c_text ? c1->c_text : c1->c_name) + 2;
+           count -= strlen(text) + 2;
        lm = c1->c_offset + (count / 2);
     } else {
        if (c1->c_offset)
@@ -1235,18 +1337,15 @@ putcomp (struct mcomp *c1, struct mcomp *c2, int flag)
 
     if (!(c1->c_flags & HDROUTPUT) && !(c1->c_flags & NOCOMPONENT)) {
         if (c1->c_flags & UPPERCASE)           /* uppercase component also */
-           for (cp = (c1->c_text ? c1->c_text : c1->c_name); *cp; cp++)
-               if (islower (*cp))
-                   *cp = toupper (*cp);
-       putstr (c1->c_text ? c1->c_text : c1->c_name, c1->c_flags);
+            ToUpper(text);
+       putstr(text, c1->c_flags);
        if (flag != BODYCOMP) {
            putstr (": ", c1->c_flags);
            if (!(c1->c_flags & SPLIT))
                c1->c_flags |= HDROUTPUT;
 
        cchdr++;
-       if ((count = c1->c_cwidth -
-               strlen (c1->c_text ? c1->c_text : c1->c_name) - 2) > 0)
+       if ((count = c1->c_cwidth - strlen(text) - 2) > 0)
            while (count--)
                putstr (" ", c1->c_flags);
        }
@@ -1258,9 +1357,7 @@ putcomp (struct mcomp *c1, struct mcomp *c2, int flag)
            && !(c2->c_flags & HDROUTPUT)
            && !(c2->c_flags & NOCOMPONENT)) {
         if (c1->c_flags & UPPERCASE)
-           for (cp = c2->c_name; *cp; cp++)
-               if (islower (*cp))
-                   *cp = toupper (*cp);
+            ToUpper(c2->c_name);
        putstr (c2->c_name, c1->c_flags);
        putstr (": ", c1->c_flags);
        if (!(c1->c_flags & SPLIT))
@@ -1272,9 +1369,7 @@ putcomp (struct mcomp *c1, struct mcomp *c2, int flag)
                putstr (" ", c1->c_flags);
     }
     if (c1->c_flags & UPPERCASE)
-       for (cp = c2->c_text; *cp; cp++)
-           if (islower (*cp))
-               *cp = toupper (*cp);
+        ToUpper(c2->c_text);
 
     count = 0;
     if (cchdr) {
@@ -1283,31 +1378,43 @@ putcomp (struct mcomp *c1, struct mcomp *c2, int flag)
                        : (int) strlen (c2->c_name) + 2;
        else
            count = (c1->c_cwidth >= 0) ? (size_t) c1->c_cwidth
-                       : strlen (c1->c_text ? c1->c_text : c1->c_name) + 2;
+                       : strlen(text) + 2;
     }
     count += c1->c_offset;
 
     if ((cp = oneline (c2->c_text, c1->c_flags)))
-       putstr(cp, c1->c_flags);
+       /* Output line, trimming trailing whitespace if requested. */
+       putstr (c1->c_flags & RTRIM ? rtrim (cp) : cp, c1->c_flags);
     if (term == '\n')
        putstr ("\n", c1->c_flags);
     while ((cp = oneline (c2->c_text, c1->c_flags))) {
        lm = count;
        if (flag == BODYCOMP
-               && !(c1->c_flags & NOCOMPONENT))
-           putstr (c1->c_text ? c1->c_text : c1->c_name, c1->c_flags);
-       if (*cp)
-           putstr (cp, c1->c_flags);
+               && !(c1->c_flags & NOCOMPONENT)) {
+           /* Output component, trimming trailing whitespace if there
+              is no text on the line. */
+           if (*cp) {
+               putstr(text, c1->c_flags);
+           } else {
+               putstr (trimmed_prefix, c1->c_flags);
+           }
+       }
+       if (*cp) {
+           /* Output line, trimming trailing whitespace if requested. */
+           putstr (c1->c_flags & RTRIM ? rtrim (cp) : cp, c1->c_flags);
+        }
        if (term == '\n')
            putstr ("\n", c1->c_flags);
     }
     if (flag == BODYCOMP && term == '\n')
        c1->c_flags &= ~HDROUTPUT;              /* Buffer ended on a newline */
+
+    free (trimmed_prefix);
 }
 
 
 static char *
-oneline (char *stuff, long flags)
+oneline (char *stuff, unsigned long flags)
 {
     int spc;
     char *cp, *ret;
@@ -1321,7 +1428,7 @@ oneline (char *stuff, long flags)
     term = 0;
     if (flags & COMPRESS) {
        for (spc = 1, cp = ret; *onelp; onelp++)
-           if (isspace (*onelp)) {
+           if (isspace ((unsigned char) *onelp)) {
                if (*onelp == '\n' && (!onelp[1] || (flags & ADDRFMT))) {
                    term = '\n';
                    *onelp++ = 0;
@@ -1359,8 +1466,12 @@ oneline (char *stuff, long flags)
 
 
 static void
-putstr (char *string, long flags)
+putstr (char *string, unsigned long flags)
 {
+    /* To not count, for the purpose of counting columns, all of
+       the bytes of a multibyte character. */
+    int char_len;
+
     if (!column && lm > 0) {
        while (lm > 0)
            if (lm >= 8) {
@@ -1373,13 +1484,38 @@ putstr (char *string, long flags)
            }
     }
     lm = 0;
-    while (*string)
-       putch (*string++, flags);
+
+#ifdef MULTIBYTE_SUPPORT
+    if (mbtowc (NULL, NULL, 0)) {} /* reset shift state */
+    char_len = 0;
+#else
+    NMH_UNUSED (char_len);
+#endif
+
+    while (*string) {
+        flags &= ~INVISIBLE;
+#ifdef MULTIBYTE_SUPPORT
+        /* mbtowc should never return 0, because *string is non-NULL. */
+        if (char_len <= 0) {
+            /* Find number of bytes in next character. */
+            if ((char_len =
+                 mbtowc (NULL, string, (size_t) MB_CUR_MAX)) == -1) {
+                char_len = 1;
+            }
+        } else {
+            /* Multibyte character, after the first byte. */
+            flags |= INVISIBLE;
+        }
+
+        --char_len;
+#endif
+        putch (*string++, flags);
+    }
 }
 
 
 static void
-putch (char ch, long flags)
+putch (char ch, unsigned long flags)
 {
     char buf[BUFSIZ];
 
@@ -1398,10 +1534,12 @@ putch (char ch, long flags)
                putchar ('\007');
            fflush (stdout);
            buf[0] = 0;
-           read (fileno (stdout), buf, sizeof(buf));
+           if (read (fileno (stdout), buf, sizeof(buf)) < 0) {
+               advise ("stdout", "read");
+           }
            if (strchr(buf, '\n')) {
                if (global.c_flags & CLEARSCR)
-                   clear_screen ();
+                   nmh_clear_screen ();
                row = 0;
            } else {
                putchar ('\n');
@@ -1431,8 +1569,25 @@ putch (char ch, long flags)
                putchar ('-');
                putchar (' ');
            }
-           if (ch >= ' ')
-               column++;
+            /*
+             * Increment the character count, unless
+             * 1) In UTF-8 locale, this is other than the last byte of
+                  a multibyte character, or
+             * 2) In C locale, will print a non-printable character.
+             */
+            if ((flags & FORCE7BIT) == 0) {
+                /* UTF-8 locale */
+                if ((flags & INVISIBLE) == 0) {
+                    /* If multibyte character, its first byte only. */
+                    ++column;
+                }
+            } else {
+                /* If not an ASCII character, the replace character will be
+                   displayed.  Count it. */
+                if (! isascii((unsigned char) ch) || isprint((unsigned char) ch)) {
+                    ++column;
+                }
+            }
            break;
     }
 
@@ -1445,7 +1600,11 @@ putch (char ch, long flags)
        return;
     }
 
-    putchar (ch);
+    if (flags & FORCE7BIT  &&  ! isascii((unsigned char) ch)) {
+        putchar ('?');
+    } else {
+        putchar (ch);
+    }
 }
 
 
@@ -1480,252 +1639,6 @@ quitser (int i)
 }
 
 
-static void
-face_format (struct mcomp *c1)
-{
-    char *cp;
-    struct mailname *mp;
-
-    if ((cp = c1->c_text) == NULL)
-       return;
-
-    if ((cp = getname (cp))) {
-       if ((mp = getm (cp, NULL, 0, AD_NAME, NULL))) {
-           char *h, *o;
-           if ((h = mp->m_host) == NULL)
-               h = LocalName (0);
-           if ((o = OfficialName (h)))
-               h = o;
-           c1->c_face = concat ("address ", h, " ", mp->m_mbox, NULL);
-       }
-
-       while ((cp = getname (cp)))
-           continue;
-    }
-}
-
-
-/*
- * faceproc is two elements defining the image agent's location:
- *     Internet host
- *     UDP port
- */
-
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netdb.h>
-#include <arpa/inet.h>
-
-static int
-doface (struct mcomp *c1)
-{
-    int        result, sd;
-    static int inited = OK;
-    static struct sockaddr_storage ss;
-    static socklen_t socklen;
-    static int socktype;
-    static int protocol;
-
-    if (inited == OK) {
-       char *cp;
-       char **ap = brkstring (cp = getcpy (faceproc), " ", "\n");
-       struct addrinfo hints, *res;
-
-       if (ap[0] == NULL || ap[1] == NULL) {
-bad_faceproc: ;
-           free (cp);
-           return (inited = NOTOK);
-       }
-
-       memset(&hints, 0, sizeof(hints));
-#ifdef AI_ADDRCONFIG
-       hints.ai_flags = AI_ADDRCONFIG;
-#endif
-       hints.ai_family = PF_UNSPEC;
-       hints.ai_socktype = SOCK_DGRAM;
-
-       if (getaddrinfo(ap[0], ap[1], &hints, &res) != 0)
-           goto bad_faceproc;
-
-       memcpy(&ss, res->ai_addr, res->ai_addrlen);
-       socklen = res->ai_addrlen;
-       socktype = res->ai_socktype;
-       protocol = res->ai_protocol;
-       freeaddrinfo(res);
-
-       inited = DONE;
-    }
-    if (inited == NOTOK)
-       return NOTOK;
-
-    if ((sd = socket (ss.ss_family, socktype, protocol)) == NOTOK)
-       return NOTOK;
-
-    result = sendto (sd, c1->c_text, strlen (c1->c_text), 0,
-               (struct sockaddr *) &ss, socklen);
-
-    close (sd);
-
-    return (result != NOTOK ? OK : NOTOK);
-}
-
-/*
- * COMMENTED OUT
- * This version doesn't use sockets
- */
-#if 0
-
-static int
-doface (struct mcomp *c1)
-{
-    int i, len, vecp;
-    pid_t child_id;
-    int result, pdi[2], pdo[2];
-    char *bp, *cp;
-    char buffer[BUFSIZ], *vec[10];
-
-    if (pipe (pdi) == NOTOK)
-       return NOTOK;
-    if (pipe (pdo) == NOTOK) {
-       close (pdi[0]);
-       close (pdi[1]);
-       return NOTOK;
-    }
-
-    for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
-       sleep (5);
-
-    switch (child_id) {
-       case NOTOK: 
-           /* oops... fork error */
-           return NOTOK;
-
-       case OK: 
-           /* child process */
-           SIGNAL (SIGINT, SIG_IGN);
-           SIGNAL (SIGQUIT, SIG_IGN);
-           if (pdi[0] != fileno (stdin)) {
-               dup2 (pdi[0], fileno (stdin));
-               close (pdi[0]);
-           }
-           close (pdi[1]);
-           close (pdo[0]);
-           if (pdo[1] != fileno (stdout)) {
-               dup2 (pdo[1], fileno (stdout));
-               close (pdo[1]);
-           }
-           vecp = 0;
-           vec[vecp++] = r1bindex (faceproc, '/');
-           vec[vecp++] = "-e";
-           if (sleepsw != NOTOK) {
-               vec[vecp++] = "-s";
-               snprintf (buffer, sizeof(buffer), "%d", sleepsw);
-               vec[vecp++] = buffer;
-           }
-           vec[vecp] = NULL;
-           execvp (faceproc, vec);
-           fprintf (stderr, "unable to exec ");
-           perror (faceproc);
-           _exit (-1);         /* NOTREACHED */
-
-       default: 
-           /* parent process */
-           close (pdi[0]);
-           i = strlen (c1->c_text);
-           if (write (pdi[1], c1->c_text, i) != i)
-               adios ("pipe", "error writing to");
-           free (c1->c_text), c1->c_text = NULL;
-           close (pdi[1]);
-
-           close (pdo[1]);
-           cp = NULL, len = 0;
-           result = DONE;
-           while ((i = read (pdo[0], buffer, strlen (buffer))) > 0) {
-               if (cp) {
-                   int j;
-                   char *dp;
-                   dp = mh_xrealloc (cp, (unsigned) (j = len + i));
-                   memcpy(dp + len, buffer, i);
-                   cp = dp, len = j;
-               }
-               else {
-                   cp = mh_xmalloc ((unsigned) i);
-                   memcpy(cp, buffer, i);
-                   len = i;
-               }
-               if (result == DONE)
-                   for (bp = buffer + i - 1; bp >= buffer; bp--)
-                       if (!isascii (*bp) || iscntrl (*bp)) {
-                           result = OK;
-                           break;
-                       }
-           }
-           close (pdo[0]);
-
-/* no waiting for child... */
-
-           if (result == OK) { /* binary */
-               if (write (1, cp, len) != len)
-                   adios ("writing", "error");
-               free (cp);
-           }
-           else                /* empty */
-               if ((c1->c_text = cp) == NULL)
-                   result = OK;
-           break;
-    }
-
-    return result;
-}
-#endif /* COMMENTED OUT */
-
-
-int
-mhlsbr (int argc, char **argv, FILE *(*action)())
-{
-    SIGNAL_HANDLER istat = NULL, pstat = NULL, qstat = NULL;
-    char *cp = NULL;
-    struct mcomp *c1;
-
-    switch (m_setjmp (mhlenv)) {
-       case OK: 
-           cp = invo_name;
-           sleepsw = 0;        /* XXX */
-           bellflg = clearflg = forwflg = forwall = exitstat = 0;
-           digest = NULL;
-           ontty = NOTTY;
-           mhl_action = action;
-
-           /*
-            * If signal is at default action, then start ignoring
-            * it, else let it set to its current action.
-            */
-           if ((istat = SIGNAL (SIGINT, SIG_IGN)) != SIG_DFL)
-               SIGNAL (SIGINT, istat);
-           if ((qstat = SIGNAL (SIGQUIT, SIG_IGN)) != SIG_DFL)
-               SIGNAL (SIGQUIT, qstat);
-           pstat = SIGNAL (SIGPIPE, pipeser);
-           mhl (argc, argv);                  /* FALL THROUGH! */
-
-       default: 
-           SIGNAL (SIGINT, istat);
-           SIGNAL (SIGQUIT, qstat);
-           SIGNAL (SIGPIPE, SIG_IGN);/* should probably change to block instead */
-           if (ontty == PITTY)
-               m_pclose ();
-           SIGNAL (SIGPIPE, pstat);
-           invo_name = cp;
-           if (holder.c_text) {
-               free (holder.c_text);
-               holder.c_text = NULL;
-           }
-           free_queue (&msghd, &msgtl);
-           for (c1 = fmthd; c1; c1 = c1->c_next)
-               c1->c_flags &= ~HDROUTPUT;
-           return exitstat;
-    }
-}
-
 #undef adios
 #undef done
 
@@ -1752,77 +1665,76 @@ mhldone (int status)
 }
 
 
-static int m_pid = NOTOK;
-static  int sd = NOTOK;
+/*
+ * Compile a format string used by the formatfield option and save it
+ * for later.
+ *
+ * We will want the {text} (and possibly {error}) components for later,
+ * so look for them and save them if we find them.
+ */
 
 static void
-m_popen (char *name)
+compile_formatfield(struct mcomp *c1)
 {
-    int pd[2];
-
-    if (mhl_action && (sd = dup (fileno (stdout))) == NOTOK)
-       adios ("standard output", "unable to dup()");
-
-    if (pipe (pd) == NOTOK)
-       adios ("pipe", "unable to");
+    fmt_compile(c1->c_nfs, &c1->c_fmt, 1);
 
-    switch (m_pid = vfork()) {
-       case NOTOK: 
-           adios ("fork", "unable to");
-
-       case OK: 
-           SIGNAL (SIGINT, SIG_DFL);
-           SIGNAL (SIGQUIT, SIG_DFL);
-
-           close (pd[1]);
-           if (pd[0] != fileno (stdin)) {
-               dup2 (pd[0], fileno (stdin));
-               close (pd[0]);
-           }
-           execlp (name, r1bindex (name, '/'), NULL);
-           fprintf (stderr, "unable to exec ");
-           perror (name);
-           _exit (-1);
+    /*
+     * As a note to myself and any other poor bastard who is looking through
+     * this code in the future ....
+     *
+     * When the format hash table is reset later on (as it almost certainly
+     * will be), there will still be references to these components in the
+     * compiled format instructions.  Thus these component references will
+     * be free'd when the format instructions are free'd (by fmt_free()).
+     *
+     * So, in other words ... don't go free'ing them yourself!
+     */
 
-       default: 
-           close (pd[0]);
-           if (pd[1] != fileno (stdout)) {
-               dup2 (pd[1], fileno (stdout));
-               close (pd[1]);
-           }
-    }
+    c1->c_c_text = fmt_findcomp("text");
+    c1->c_c_error = fmt_findcomp("error");
 }
 
+/*
+ * Compile all of the arguments for our format list.
+ *
+ * Iterate through the linked list of format strings and compile them.
+ * Note that we reset the format hash table before we start, but we do NOT
+ * reset it between calls to fmt_compile().
+ *
+ */
 
-void
-m_pclose (void)
+static void
+compile_filterargs (void)
 {
-    if (m_pid == NOTOK)
-       return;
+    struct arglist *arg = arglist_head;
+    struct comp *cptr;
+    char **ap;
 
-    if (sd != NOTOK) {
-       fflush (stdout);
-       if (dup2 (sd, fileno (stdout)) == NOTOK)
-           adios ("standard output", "unable to dup2()");
+    fmt_free(NULL, 1);
 
-       clearerr (stdout);
-       close (sd);
-       sd = NOTOK;
+    while (arg) {
+       fmt_compile(arg->a_nfs, &arg->a_fmt, 0);
+       arg = arg->a_next;
     }
-    else
-       fclose (stdout);
 
-    pidwait (m_pid, OK);
-    m_pid = NOTOK;
-}
+    /*
+     * Search through and mark any components that are address components
+     */
 
+    for (ap = addrcomps; *ap; ap++) {
+       cptr = fmt_findcomp (*ap);
+       if (cptr)
+           cptr->c_type |= CT_ADDR;
+    }
+}
 
 /*
  * Filter the body of a message through a specified format program
  */
 
-void
-filterbody (struct mcomp *c1, char *buf, int bufsz, int state, FILE *fp)
+static void
+filterbody (struct mcomp *c1, char *buf, int bufsz, int state, FILE *fp,
+            m_getfld_state_t gstate)
 {
     struct mcomp holder;
     char name[NAMESZ];
@@ -1876,8 +1788,11 @@ filterbody (struct mcomp *c1, char *buf, int bufsz, int state, FILE *fp)
         */
 
        while (state == BODY) {
-           write(fdinput[1], buf, strlen(buf));
-           state = m_getfld(state, name, buf, bufsz, fp);
+           int bufsz2 = bufsz;
+           if (write(fdinput[1], buf, strlen(buf)) < 0) {
+               advise ("pipe output", "write");
+           }
+           state = m_getfld (&gstate, name, buf, &bufsz2, fp);
        }
 
        /*
@@ -1885,8 +1800,11 @@ filterbody (struct mcomp *c1, char *buf, int bufsz, int state, FILE *fp)
         */
 
        close(fdinput[1]);
-       exit(0);
-       break;
+       /*
+        * Make sure we call _exit(), otherwise we may flush out the stdio
+        * buffers that we have duplicated from the parent.
+        */
+       _exit(0);
     case -1:
        adios(NULL, "Unable to fork for filter writer process");
        break;
@@ -1898,8 +1816,47 @@ filterbody (struct mcomp *c1, char *buf, int bufsz, int state, FILE *fp)
      */
 
     switch (filterpid = fork()) {
+        char **args, *program;
+       struct arglist *a;
+       int i, dat[5], s, argp;
+
     case 0:
-       if (dup2(fdinput[0], STDIN_FILENO) < 0) {
+       /*
+        * Configure an argument array for us
+        */
+
+       args = argsplit(formatproc, &program, &argp);
+       args[argp + filter_nargs] = NULL;
+       dat[0] = 0;
+       dat[1] = 0;
+       dat[2] = 0;
+       dat[3] = BUFSIZ;
+       dat[4] = 0;
+
+       /*
+        * Pull out each argument and scan them.
+        */
+
+       for (a = arglist_head, i = argp; a != NULL; a = a->a_next, i++) {
+           charstring_t scanl = charstring_create (BUFSIZ);
+
+           fmt_scan(a->a_fmt, scanl, BUFSIZ, dat, NULL);
+           args[i] = charstring_buffer_copy (scanl);
+           charstring_free (scanl);
+           /*
+            * fmt_scan likes to put a trailing newline at the end of the
+            * format string.  If we have one, get rid of it.
+            */
+           s = strlen(args[i]);
+           if (args[i][s - 1] == '\n')
+               args[i][s - 1] = '\0';
+
+           if (mhldebug)
+               fprintf(stderr, "filterarg: fmt=\"%s\", output=\"%s\"\n",
+                       a->a_nfs, args[i]);
+       }
+
+       if (dup2(fdinput[0], STDIN_FILENO) < 0) {
            adios("formatproc", "Unable to dup2() standard input");
        }
        if (dup2(fdoutput[1], STDOUT_FILENO) < 0) {
@@ -1917,7 +1874,7 @@ filterbody (struct mcomp *c1, char *buf, int bufsz, int state, FILE *fp)
        close(fdoutput[0]);
        close(fdoutput[1]);
 
-       execlp(formatproc, formatproc, (char *) NULL);
+       execvp(formatproc, args);
 
        adios(formatproc, "Unable to execute filter");