]> diplodocus.org Git - nmh/blobdiff - uip/mhlsbr.c
Add basic support for the STLS command in POP
[nmh] / uip / mhlsbr.c
index f24292009a779709d810bb71442994052557fbb6..86a1f41d3215a861db2d65e308d48987ad9b45b2 100644 (file)
@@ -1,20 +1,42 @@
-
-/*
- * mhlsbr.c -- main routines for nmh message lister
+/* mhlsbr.c -- main routines for nmh message lister
  *
  * This code is Copyright (c) 2002, by the authors of nmh.  See the
  * COPYRIGHT file in the root directory of the nmh distribution for
  * complete copyright information.
  */
 
-#include <h/mh.h>
-#include <h/signals.h>
-#include <h/addrsbr.h>
-#include <h/fmt_scan.h>
-#include <h/tws.h>
-#include <h/utils.h>
+#include "h/mh.h"
+#include "sbr/fmt_new.h"
+#include "mhlsbr.h"
+#include "sbr/m_getfld.h"
+#include "sbr/getarguments.h"
+#include "sbr/concat.h"
+#include "sbr/smatch.h"
+#include "sbr/r1bindex.h"
+#include "sbr/snprintb.h"
+#include "sbr/copyip.h"
+#include "sbr/discard.h"
+#include "sbr/trimcpy.h"
+#include "sbr/vfgets.h"
+#include "sbr/check_charset.h"
+#include "sbr/getcpy.h"
+#include "sbr/brkstring.h"
+#include "sbr/ambigsw.h"
+#include "sbr/pidstatus.h"
+#include "sbr/print_version.h"
+#include "sbr/print_help.h"
+#include "sbr/arglist.h"
+#include "sbr/error.h"
+#include "h/signals.h"
+#include "h/addrsbr.h"
+#include "h/fmt_scan.h"
+#include "h/tws.h"
+#include "h/done.h"
+#include "h/utils.h"
+#include "sbr/m_popen.h"
 #include <setjmp.h>
 #include <sys/types.h>
+#include "sbr/terminal.h"
 
 /*
  * MAJOR BUG:
@@ -22,7 +44,7 @@
  * set, then addresses get split wrong (not at the spaces between commas).
  * To fix this correctly, putstr() should know about "atomic" strings that
  * must NOT be broken across lines.  That's too difficult for right now
- * (it turns out that there are a number of degernate cases), so in
+ * (it turns out that there are a number of degenerate cases), so in
  * oneline(), instead of
  *
  *       (*onelp == '\n' && !onelp[1])
@@ -93,7 +115,9 @@ DEFINE_SWITCH_ARRAY(MHL, mhlswitches);
 #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\017RTRIM\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)
 
 /*
@@ -161,7 +185,7 @@ struct mcomp {
     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;
 };
 
@@ -180,7 +204,7 @@ static struct mcomp holder = {
 
 struct pair {
     char *p_name;
-    long p_flags;
+    unsigned long p_flags;
 };
 
 static struct pair pairs[] = {
@@ -203,8 +227,8 @@ static struct pair pairs[] = {
 
 struct triple {
     char *t_name;
-    long t_on;
-    long t_off;
+    unsigned long t_on;
+    unsigned long t_off;
 };
 
 static struct triple triples[] = {
@@ -257,9 +281,9 @@ static char *addrcomps[] = {
 static int bellflg   = 0;
 static int clearflg  = 0;
 static int dashstuff = 0;
-static int dobody    = 1;
-static int forwflg   = 0;
-static int forwall   = 0;
+static bool dobody = true;
+static bool forwflg;
+static bool forwall;
 
 static int sleepsw = NOTOK;
 
@@ -268,7 +292,7 @@ static int volume = 0;
 static int issue = 0;
 
 static int exitstat = 0;
-static int mhldebug = 0;
+static bool mhldebug;
 
 static int filesize = 0;
 
@@ -296,21 +320,11 @@ static int num_ignores = 0;
 static char *ignores[MAXARGS];
 
 static  jmp_buf env;
-static  jmp_buf mhlenv;
 
 static char delim3[] =         /* from forw.c */
     "\n----------------------------------------------------------------------\n\n";
 static char delim4[] = "\n------------------------------\n\n";
 
-static FILE *(*mhl_action) () = (FILE *(*) ()) 0;
-
-/*
- * Redefine a couple of functions.
- * These are undefined later in the code.
- */
-#define        adios mhladios
-#define        done  mhldone
-
 /*
  * prototypes
  */
@@ -321,21 +335,22 @@ static int ptos (char *, char **);
 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 int mcomp_flags (char *) PURE;
+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 bool linefeed_typed(void);
 static void intrser (int);
 static void pipeser (int);
 static void quitser (int);
-static void mhladios (char *, char *, ...);
-static void mhldone (int);
-static void filterbody (struct mcomp *, char *, int, int, FILE *,
+static void mhladios (char *, char *, ...) CHECK_PRINTF(2, 3) NORETURN;
+static void mhldone (int) NORETURN;
+static void filterbody (struct mcomp *, char *, int, int,
                         m_getfld_state_t);
 static void compile_formatfield(struct mcomp *);
 static void compile_filterargs (void);
@@ -344,7 +359,8 @@ static void compile_filterargs (void);
 int
 mhl (int argc, char **argv)
 {
-    int length = 0, nomore = 0;
+    int length = 0;
+    bool nomore = false;
     unsigned int i, vecp = 0;
     int width = 0;
     char *cp, *folder = NULL, *form = NULL;
@@ -358,24 +374,24 @@ mhl (int argc, char **argv)
     argp = arguments;
 
     if ((cp = getenv ("MHLDEBUG")) && *cp)
-       mhldebug++;
+       mhldebug = true;
 
     while ((cp = *argp++)) {
        if (*cp == '-') {
            switch (smatch (++cp, mhlswitches)) {
                case AMBIGSW: 
                    ambigsw (cp, mhlswitches);
-                   done (1);
+                   mhldone (1);
                case UNKWNSW: 
-                   adios (NULL, "-%s unknown\n", cp);
+                   mhladios (NULL, "-%s unknown\n", cp);
 
                case HELPSW: 
                    snprintf (buf, sizeof(buf), "%s [switches] [files ...]", invo_name);
                    print_help (buf, mhlswitches, 1);
-                   done (0);
+                   mhldone (0);
                case VERSIONSW:
                    print_version(invo_name);
-                   done (0);
+                   mhldone (0);
 
                case BELLSW: 
                    bellflg = 1;
@@ -393,31 +409,30 @@ mhl (int argc, char **argv)
 
                case FOLDSW: 
                    if (!(folder = *argp++) || *folder == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
+                       mhladios (NULL, "missing argument to %s", argp[-2]);
                    continue;
                case FORMSW: 
                    if (!(form = *argp++) || *form == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
+                       mhladios (NULL, "missing argument to %s", argp[-2]);
                    continue;
 
                case SLEEPSW:
                    if (!(cp = *argp++) || *cp == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
-                   else
-                       sleepsw = atoi (cp);/* ZERO ok! */
+                       mhladios (NULL, "missing argument to %s", argp[-2]);
+                    sleepsw = atoi (cp);/* ZERO ok! */
                    continue;
 
                case PROGSW:
                    if (!(moreproc = *argp++) || *moreproc == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
+                       mhladios (NULL, "missing argument to %s", argp[-2]);
                    continue;
                case NPROGSW:
-                   nomore++;
+                   nomore = true;
                    continue;
 
                case FMTPROCSW:
                    if (!(formatproc = *argp++) || *formatproc == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
+                       mhladios (NULL, "missing argument to %s", argp[-2]);
                    continue;
                case NFMTPROCSW:
                    formatproc = NULL;
@@ -425,50 +440,51 @@ mhl (int argc, char **argv)
 
                case LENSW: 
                    if (!(cp = *argp++) || *cp == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
-                   else if ((length = atoi (cp)) < 1)
-                       adios (NULL, "bad argument %s %s", argp[-2], cp);
+                       mhladios (NULL, "missing argument to %s", argp[-2]);
+                   if ((length = atoi (cp)) < 1)
+                       mhladios (NULL, "bad argument %s %s", argp[-2], cp);
                    continue;
                case WIDTHSW: 
                    if (!(cp = *argp++) || *cp == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
-                   else if ((width = atoi (cp)) < 1)
-                       adios (NULL, "bad argument %s %s", argp[-2], cp);
+                       mhladios (NULL, "missing argument to %s", argp[-2]);
+                   if ((width = atoi (cp)) < 1)
+                       mhladios (NULL, "bad argument %s %s", argp[-2], cp);
                    continue;
 
                case DGSTSW: 
                    if (!(digest = *argp++) || *digest == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
+                       mhladios (NULL, "missing argument to %s", argp[-2]);
                    continue;
                case ISSUESW:
                    if (!(cp = *argp++) || *cp == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
-                   else if ((issue = atoi (cp)) < 1)
-                       adios (NULL, "bad argument %s %s", argp[-2], cp);
+                       mhladios (NULL, "missing argument to %s", argp[-2]);
+                   if ((issue = atoi (cp)) < 1)
+                       mhladios (NULL, "bad argument %s %s", argp[-2], cp);
                    continue;
                case VOLUMSW:
                    if (!(cp = *argp++) || *cp == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
-                   else if ((volume = atoi (cp)) < 1)
-                       adios (NULL, "bad argument %s %s", argp[-2], cp);
+                       mhladios (NULL, "missing argument to %s", argp[-2]);
+                   if ((volume = atoi (cp)) < 1)
+                       mhladios (NULL, "bad argument %s %s", argp[-2], cp);
                    continue;
 
                case FORW2SW: 
-                   forwall++;  /* fall */
+                   forwall = true;
+                   /* FALLTHRU */
                case FORW1SW: 
-                   forwflg++;
+                   forwflg = true;
                    clearflg = -1;/* XXX */
                    continue;
 
                case BITSTUFFSW: 
-                   dashstuff = 1;      /* trinary logic */
+                   dashstuff = 1;      /* ternary logic */
                    continue;
                case NBITSTUFFSW: 
-                   dashstuff = -1;     /* trinary logic */
+                   dashstuff = -1;     /* ternary logic */
                    continue;
 
                case NBODYSW: 
-                   dobody = 0;
+                   dobody = false;
                    continue;
            }
        }
@@ -480,12 +496,8 @@ mhl (int argc, char **argv)
 
     if (isatty (fileno (stdout))) {
        if (!nomore && moreproc && *moreproc != '\0') {
-           if (mhl_action) {
-               SIGNAL (SIGINT, SIG_IGN);
-               SIGNAL2 (SIGQUIT, quitser);
-           }
            SIGNAL2 (SIGPIPE, pipeser);
-           m_popen (moreproc, mhl_action != NULL);
+           m_popen(moreproc, false);
            ontty = PITTY;
        } else {
            SIGNAL (SIGINT, SIG_IGN);
@@ -507,7 +519,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 {
@@ -519,16 +531,16 @@ 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",
-                   vecp > 1 ? "s" : "");
+                   PLURALS(vecp));
     }
 
     fflush(stdout);
     if(ferror(stdout)){
-           adios("output", "error writing");
+           mhladios("output", "error writing");
     }
     
     if (clearflg > 0 && ontty == NOTTY)
@@ -564,7 +576,7 @@ mhl_format (char *file, int length, int width)
     }
 
     if ((fp = fopen (etcpath (file), "r")) == NULL)
-       adios (file, "unable to open format file");
+       mhladios (file, "unable to open format file");
 
     if (fstat (fileno (fp), &st) != NOTOK) {
        mtime = st.st_mtime;
@@ -590,7 +602,7 @@ mhl_format (char *file, int length, int width)
        if (*bp == ';')
            continue;
 
-        TrimSuffixC(bp, '\n');
+        trim_suffix_c(bp, '\n');
 
        if (*bp == ':') {
            (void) add_queue (&fmthd, &fmttl, NULL, bp + 1, CLEARTEXT);
@@ -627,7 +639,7 @@ mhl_format (char *file, int length, int width)
                parptr = bp;
                while (*parptr) {
                    if (evalvar (&global))
-                       adios (NULL, "format file syntax error: %s", bp);
+                       mhladios (NULL, "format file syntax error: %s", bp);
                    if (*parptr)
                        parptr++;
                }
@@ -638,7 +650,7 @@ mhl_format (char *file, int length, int width)
                while (*parptr == ':' || *parptr == ',') {
                    parptr++;
                    if (evalvar (c1))
-                       adios (NULL, "format file syntax error: %s", bp);
+                       mhladios (NULL, "format file syntax error: %s", bp);
                }
                if (!c1->c_nfs && global.c_nfs) {
                    if (c1->c_flags & DATEFMT) {
@@ -646,19 +658,17 @@ mhl_format (char *file, int length, int width)
                            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 = mh_xstrdup(global.c_nfs);
-                               compile_formatfield(c1);
-                           }
-                       }
+                   } else if (c1->c_flags & ADDRFMT) {
+                        if (global.c_flags & ADDRFMT) {
+                            c1->c_nfs = mh_xstrdup(global.c_nfs);
+                            compile_formatfield(c1);
+                        }
+                    }
                }
                continue;
 
            default: 
-               adios (NULL, "format file syntax error: %s", bp);
+               mhladios (NULL, "format file syntax error: %s", bp);
        }
     }
     fclose (fp);
@@ -669,9 +679,8 @@ mhl_format (char *file, int length, int width)
 
            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",
-                    (unsigned int)(unsigned long) c1->c_nfs,
-                    (unsigned int)(unsigned long) c1->c_fmt);
+           fprintf(stderr, "\tnfs=%p fmt=%p\n",
+                (void *)c1->c_nfs, (void *)c1->c_fmt);
            fprintf (stderr, "\toffset=%d ovoff=%d width=%d cwidth=%d length=%d\n",
                    c1->c_offset, c1->c_ovoff, c1->c_width,
                    c1->c_cwidth, c1->c_length);
@@ -755,7 +764,7 @@ evalvar (struct mcomp *c1)
     if (!strcasecmp (name, "length"))
        return ptoi (name, &c1->c_length);
     if (!strcasecmp (name, "nodashstuffing"))
-       return (dashstuff = -1);
+       return dashstuff = -1;
 
     for (ap = triples; ap->t_name; ap++)
        if (!strcasecmp (ap->t_name, name)) {
@@ -771,7 +780,7 @@ evalvar (struct mcomp *c1)
            return 1;
 
        if (! c1->c_name  ||  strcasecmp (c1->c_name, "body")) {
-           advise (NULL, "format filters are currently only supported on "
+           inform("format filters are currently only supported on "
                    "the \"body\" component");
            return 1;
        }
@@ -802,7 +811,7 @@ ptoi (char *name, int *i)
     char *cp;
 
     if (*parptr++ != '=' || !*(cp = parse ())) {
-       advise (NULL, "missing argument to variable %s", name);
+       inform("missing argument to variable %s", name);
        return 1;
     }
 
@@ -817,7 +826,7 @@ ptos (char *name, char **s)
     char c, *cp;
 
     if (*parptr++ != '=') {
-       advise (NULL, "missing argument to variable %s", name);
+       inform("missing argument to variable %s", name);
        return 1;
     }
 
@@ -850,15 +859,14 @@ parse (void)
 
     for (cp = result; *parptr && (cp - result < NAMESZ); parptr++) {
        c = *parptr;
-       if (isalnum (c)
-               || c == '.'
-               || c == '-'
-               || c == '_'
-               || c =='['
-               || c == ']')
-           *cp++ = c;
-       else
+       if (!isalnum (c)
+               && c != '.'
+               && c != '-'
+               && c != '_'
+               && c !='['
+               && c != ']')
            break;
+        *cp++ = c;
     }
     *cp = '\0';
 
@@ -888,7 +896,7 @@ process (char *folder, char *fname, int ofilen, int ofilec)
     switch (setjmp (env)) {
        case OK: 
            if (fname) {
-               fp = mhl_action ? (*mhl_action) (fname) : fopen (fname, "r");
+               fp = fopen(fname, "r");
                if (fp == NULL) {
                    advise (fname, "unable to open");
                    exitstat++;
@@ -905,7 +913,7 @@ process (char *folder, char *fname, int ofilen, int ofilec)
            cp = folder ? concat (folder, ":", fname2, NULL) : mh_xstrdup(fname2);
            if (ontty != PITTY)
                SIGNAL (SIGINT, intrser);
-           mhlfile (fp, cp, ofilen, ofilec);  /* FALL THROUGH! */
+           mhlfile (fp, cp, ofilen, ofilec);
             free (cp);
 
            for (ap = arglist_head; ap; ap = ap->a_next) {
@@ -914,14 +922,15 @@ process (char *folder, char *fname, int ofilen, int ofilec)
            }
 
            if (arglist_head)
-               fmt_free(NULL, 1);
+               fmt_free(NULL, 1);
+           /* FALLTHRU */
 
        default:
            if (ontty != PITTY)
                SIGNAL (SIGINT, SIG_IGN);
-           if (mhl_action == NULL && fp != stdin && fp != NULL)
+           if (fp != stdin && fp != NULL)
                fclose (fp);
-            mh_xfree(holder.c_text);
+            free(holder.c_text);
             holder.c_text = NULL;
            free_queue (&msghd, &msgtl);
            for (c1 = fmthd; c1; c1 = c1->c_next)
@@ -937,21 +946,21 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
 {
     int state, bucket;
     struct mcomp *c1, *c2, *c3;
-    char **ip, name[NAMESZ], buf[BUFSIZ];
-    m_getfld_state_t gstate = 0;
+    char **ip, name[NAMESZ], buf[NMH_BUFSIZ];
+    m_getfld_state_t gstate;
 
     compile_filterargs();
 
     if (forwall) {
        if (digest)
-           printf ("%s", ofilen == 1 ? delim3 : delim4);
+           fputs(ofilen == 1 ? delim3 : delim4, stdout);
        else {
-           printf ("\n-------");
+           fputs("\n-------", stdout);
            if (ofilen == 1)
-               printf (" Forwarded Message%s", ofilec > 1 ? "s" : "");
+               printf (" Forwarded Message%s", PLURALS(ofilec));
            else
                printf (" Message %d", ofilen);
-           printf ("\n\n");
+           puts("\n");
        }
     } else {
        switch (ontty) {
@@ -961,27 +970,22 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
                        if ((global.c_flags & CLEARSCR))
                            nmh_clear_screen ();
                        else
-                           printf ("\n\n\n");
+                            puts("\n\n");
                    }
                    printf (">>> %s\n\n", mname);
                }
                break;
 
            case ISTTY: 
-               strncpy (buf, "\n", sizeof(buf));
                if (ofilec > 1) {
                    if (SOprintf ("Press <return> to list \"%s\"...", mname)) {
                        if (ofilen > 1)
-                           printf ("\n\n\n");
+                           puts("\n\n");
                        printf ("Press <return> to list \"%s\"...", mname);
                    }
                    fflush (stdout);
-                   buf[0] = 0;
-                   if (read (fileno (stdout), buf, sizeof(buf)) < 0) {
-                       advise ("stdout", "read");
-                   }
                }
-               if (strchr(buf, '\n')) {
+                if (ofilec == 1 || linefeed_typed()) {
                    if ((global.c_flags & CLEARSCR))
                        nmh_clear_screen ();
                }
@@ -992,7 +996,7 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
            default: 
                if (ofilec > 1) {
                    if (ofilen > 1) {
-                       printf ("\n\n\n");
+                       puts("\n\n");
                        if (clearflg > 0)
                            nmh_clear_screen ();
                    }
@@ -1002,9 +1006,10 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
        }
     }
 
+    gstate = m_getfld_state_init(fp);
     for (;;) {
        int bufsz = sizeof buf;
-       switch (state = m_getfld (&gstate, name, buf, &bufsz, fp)) {
+       switch (state = m_getfld2(&gstate, name, buf, &bufsz)) {
            case FLD: 
            case FLDPLUS: 
                bucket = fmt_addcomptext(name, buf);
@@ -1012,7 +1017,7 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
                    if (!strcasecmp (name, *ip)) {
                        while (state == FLDPLUS) {
                            bufsz = sizeof buf;
-                           state = m_getfld (&gstate, name, buf, &bufsz, fp);
+                           state = m_getfld2(&gstate, name, buf, &bufsz);
                            fmt_appendcomp(bucket, name, buf);
                        }
                        break;
@@ -1021,13 +1026,13 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
                    continue;
 
                for (c2 = fmthd; c2; c2 = c2->c_next)
-                   if (!strcasecmp (c2->c_name ? c2->c_name : "", name))
+                   if (!strcasecmp (FENDNULL(c2->c_name), name))
                        break;
                c1 = NULL;
                if (!((c3 = c2 ? c2 : &global)->c_flags & SPLIT))
                    for (c1 = msghd; c1; c1 = c1->c_next)
-                       if (!strcasecmp (c1->c_name ? c1->c_name : "",
-                                        c3->c_name ? c3->c_name : "")) {
+                       if (!strcasecmp (FENDNULL(c1->c_name),
+                                        FENDNULL(c3->c_name))) {
                            c1->c_text =
                                mcomp_add (c1->c_flags, buf, c1->c_text);
                            break;
@@ -1036,7 +1041,7 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
                    c1 = add_queue (&msghd, &msgtl, name, buf, 0);
                while (state == FLDPLUS) {
                    bufsz = sizeof buf;
-                   state = m_getfld (&gstate, name, buf, &bufsz, fp);
+                   state = m_getfld2(&gstate, name, buf, &bufsz);
                    c1->c_text = add (buf, c1->c_text);
                    fmt_appendcomp(bucket, name, buf);
                }
@@ -1071,15 +1076,16 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
                                   !strcasecmp (c1->c_name, "body"))) {
                        if (c1->c_flags & FMTFILTER && state == BODY &&
                                                        formatproc != NULL) {
-                           filterbody(c1, buf, sizeof(buf), state, fp, gstate);
+                           filterbody(c1, buf, sizeof(buf), state, gstate);
                        } else {
-                           holder.c_text = mh_xmalloc (sizeof(buf));
-                           strncpy (holder.c_text, buf, sizeof(buf));
+                            bufsz = sizeof buf;
+                            holder.c_text = mh_xmalloc(bufsz);
+                            strncpy(holder.c_text, buf, bufsz);
                            while (state == BODY) {
                                putcomp (c1, &holder, BODYCOMP);
                                bufsz = sizeof buf;
-                               state = m_getfld (&gstate, name, holder.c_text,
-                                           &bufsz, fp);
+                               state = m_getfld2(&gstate, name, holder.c_text,
+                                           &bufsz);
                            }
                            free (holder.c_text);
                            holder.c_text = NULL;
@@ -1087,8 +1093,8 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
                        continue;
                    }
                    for (c2 = msghd; c2; c2 = c2->c_next)
-                       if (!strcasecmp (c2->c_name ? c2->c_name : "",
-                                        c1->c_name ? c1->c_name : "")) {
+                       if (!strcasecmp (FENDNULL(c2->c_name),
+                                        FENDNULL(c1->c_name))) {
                            putcomp (c1, c2, ONECOMP);
                            if (!(c1->c_flags & SPLIT))
                                break;
@@ -1099,13 +1105,13 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
 
            case LENERR: 
            case FMTERR: 
-               advise (NULL, "format error in message %s", mname);
+               inform("format error in message %s", mname);
                exitstat++;
                m_getfld_state_destroy (&gstate);
                return;
 
            default: 
-               adios (NULL, "getfld() returned %d", state);
+               mhladios (NULL, "getfld() returned %d", state);
        }
     }
 }
@@ -1118,14 +1124,14 @@ mcomp_flags (char *name)
 
     for (ap = pairs; ap->p_name; ap++)
        if (!strcasecmp (ap->p_name, name))
-           return (ap->p_flags);
+           return ap->p_flags;
 
     return 0;
 }
 
 
 static char *
-mcomp_add (long flags, char *s1, char *s2)
+mcomp_add (unsigned long flags, char *s1, char *s2)
 {
     char *dp;
 
@@ -1219,8 +1225,8 @@ mcomp_format (struct mcomp *c1, struct mcomp *c2)
        }
        charstring_free (scanl);
 
-        mh_xfree(p->pq_text);
-        mh_xfree(p->pq_error);
+        free(p->pq_text);
+        free(p->pq_error);
        q = p->pq_next;
        free(p);
     }
@@ -1266,10 +1272,10 @@ free_queue (struct mcomp **head, struct mcomp **tail)
 
     for (c1 = *head; c1; c1 = c2) {
        c2 = c1->c_next;
-        mh_xfree(c1->c_name);
-        mh_xfree(c1->c_text);
-        mh_xfree(c1->c_ovtxt);
-        mh_xfree(c1->c_nfs);
+        free(c1->c_name);
+        free(c1->c_text);
+        free(c1->c_ovtxt);
+        free(c1->c_nfs);
        if (c1->c_fmt)
            fmt_free (c1->c_fmt, 0);
        free(c1);
@@ -1284,15 +1290,23 @@ 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;
+    int count;
+    bool cchdr;
     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));
+    trimmed_prefix = rtrim(mh_xstrdup(FENDNULL(text)));
 
-    cchdr = 0;
+    cchdr = false;
     lm = 0;
     llim = c1->c_length ? c1->c_length : -1;
     wid = c1->c_width ? c1->c_width : global.c_width;
@@ -1301,7 +1315,7 @@ putcomp (struct mcomp *c1, struct mcomp *c2, int flag)
     if ((ovtxt = c1->c_ovtxt ? c1->c_ovtxt : global.c_ovtxt) == NULL)
        ovtxt = "";
     if (wid < ovoff + strlen (ovtxt) + 5)
-       adios (NULL, "component: %s width(%d) too small for overflow(%d)",
+       mhladios(NULL, "component: %s width(%d) too small for overflow(%zu)",
                c1->c_name, wid, ovoff + strlen (ovtxt) + 5);
     onelp = NULL;
 
@@ -1328,15 +1342,14 @@ 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 = text; *cp; cp++)
-                *cp = toupper ((unsigned char) *cp);
+            to_upper(text);
        putstr(text, c1->c_flags);
        if (flag != BODYCOMP) {
            putstr (": ", c1->c_flags);
            if (!(c1->c_flags & SPLIT))
                c1->c_flags |= HDROUTPUT;
 
-       cchdr++;
+       cchdr = true;
        if ((count = c1->c_cwidth - strlen(text) - 2) > 0)
            while (count--)
                putstr (" ", c1->c_flags);
@@ -1349,21 +1362,19 @@ 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++)
-                *cp = toupper ((unsigned char) *cp);
+            to_upper(c2->c_name);
        putstr (c2->c_name, c1->c_flags);
        putstr (": ", c1->c_flags);
        if (!(c1->c_flags & SPLIT))
            c2->c_flags |= HDROUTPUT;
 
-       cchdr++;
+       cchdr = true;
        if ((count = c1->c_cwidth - strlen (c2->c_name) - 2) > 0)
            while (count--)
                putstr (" ", c1->c_flags);
     }
     if (c1->c_flags & UPPERCASE)
-       for (cp = c2->c_text; *cp; cp++)
-            *cp = toupper ((unsigned char) *cp);
+        to_upper(c2->c_text);
 
     count = 0;
     if (cchdr) {
@@ -1408,35 +1419,34 @@ putcomp (struct mcomp *c1, struct mcomp *c2, int flag)
 
 
 static char *
-oneline (char *stuff, long flags)
+oneline (char *stuff, unsigned long flags)
 {
-    int spc;
+    bool spc;
     char *cp, *ret;
 
     if (onelp == NULL)
        onelp = stuff;
     if (*onelp == 0)
-       return (onelp = NULL);
+       return onelp = NULL;
 
     ret = onelp;
     term = 0;
     if (flags & COMPRESS) {
-       for (spc = 1, cp = ret; *onelp; onelp++)
+       for (spc = true, cp = ret; *onelp; onelp++)
            if (isspace ((unsigned char) *onelp)) {
                if (*onelp == '\n' && (!onelp[1] || (flags & ADDRFMT))) {
                    term = '\n';
                    *onelp++ = 0;
                    break;
                }
-               else
-                   if (!spc) {
-                       *cp++ = ' ';
-                       spc++;
-                   }
+                if (!spc) {
+                    *cp++ = ' ';
+                    spc = true;
+                }
            }
            else {
                *cp++ = *onelp;
-               spc = 0;
+               spc = false;
            }
 
        *cp = 0;
@@ -1460,8 +1470,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) {
@@ -1474,16 +1488,39 @@ 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];
-
     if (llim == 0)
        return;
 
@@ -1498,11 +1535,7 @@ putch (char ch, long flags)
            if (global.c_flags & BELL)
                putchar ('\007');
            fflush (stdout);
-           buf[0] = 0;
-           if (read (fileno (stdout), buf, sizeof(buf)) < 0) {
-               advise ("stdout", "read");
-           }
-           if (strchr(buf, '\n')) {
+            if (linefeed_typed()) {
                if (global.c_flags & CLEARSCR)
                    nmh_clear_screen ();
                row = 0;
@@ -1534,8 +1567,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;
     }
 
@@ -1543,12 +1593,44 @@ putch (char ch, long flags)
        putch ('\n', flags);
        if (ovoff > 0)
            lm = ovoff;
-       putstr (ovtxt ? ovtxt : "", flags);
+       putstr (FENDNULL(ovtxt), flags);
        putch (ch, flags);
        return;
     }
 
-    putchar (ch);
+    if (flags & FORCE7BIT  &&  ! isascii((unsigned char) ch)) {
+        putchar ('?');
+    } else {
+        putchar (ch);
+    }
+}
+
+/* linefeed_typed() makes a single read(2) from stdin and returns true
+ * if a linefeed character is amongst the characters read.
+ * A read error is treated as if linefeed wasn't typed.
+ *
+ * Typing on a TTY can cause read() to return data without typing Enter
+ * by using the TTY's EOF character instead, normally ASCII EOT, Ctrl-D.
+ * The linefeed can also be escaped with the TTY's LNEXT character,
+ * normally ASCII SYN, Ctrl-V, by typing Ctrl-V Ctrl-J.
+ * It's not possible to distinguish between the user typing a buffer's
+ * worth of characters and then EOT, or more than the buffer can hold.
+ * Either way, the result depends on ASCII LF, either from typing Enter
+ * or an escaped Ctrl-J, being amongst the read characters.
+ */
+static bool
+linefeed_typed(void)
+{
+    char buf[128];
+    ssize_t n;
+
+    n = read(0, buf, sizeof buf);
+    if (n == -1) {
+        advise("stdin", "read");
+        return false; /* Treat as EOF. */
+    }
+
+    return memchr(buf, '\n', n);
 }
 
 
@@ -1568,7 +1650,7 @@ pipeser (int i)
 {
     NMH_UNUSED (i);
 
-    done (NOTOK);
+    mhldone (NOTOK);
 }
 
 
@@ -1579,13 +1661,10 @@ quitser (int i)
 
     putchar ('\n');
     fflush (stdout);
-    done (NOTOK);
+    mhldone (NOTOK);
 }
 
 
-#undef adios
-#undef done
-
 static void
 mhladios (char *what, char *fmt, ...)
 {
@@ -1602,10 +1681,7 @@ static void
 mhldone (int status)
 {
     exitstat = status;
-    if (mhl_action)
-       longjmp (mhlenv, DONE);
-    else
-       done (exitstat);
+    done (exitstat);
 }
 
 
@@ -1677,8 +1753,8 @@ compile_filterargs (void)
  */
 
 static void
-filterbody (struct mcomp *c1, char *buf, int bufsz, int state, FILE *fp,
-            m_getfld_state_t gstate)
+filterbody (struct mcomp *c1, char *buf, int bufsz, int state,
+    m_getfld_state_t gstate)
 {
     struct mcomp holder;
     char name[NAMESZ];
@@ -1691,11 +1767,11 @@ filterbody (struct mcomp *c1, char *buf, int bufsz, int state, FILE *fp,
      */
 
     if (pipe(fdinput) < 0) {
-       adios(NULL, "Unable to create input pipe");
+       die("Unable to create input pipe");
     }
 
     if (pipe(fdoutput) < 0) {
-       adios(NULL, "Unable to create output pipe");
+       die("Unable to create output pipe");
     }
 
     /*
@@ -1728,7 +1804,7 @@ filterbody (struct mcomp *c1, char *buf, int bufsz, int state, FILE *fp,
        close(fdoutput[1]);
 
        /*
-        * Call m_getfld() until we're no longer in the BODY state
+        * Call m_getfld2() until we're no longer in the BODY state
         */
 
        while (state == BODY) {
@@ -1736,7 +1812,7 @@ filterbody (struct mcomp *c1, char *buf, int bufsz, int state, FILE *fp,
            if (write(fdinput[1], buf, strlen(buf)) < 0) {
                advise ("pipe output", "write");
            }
-           state = m_getfld (&gstate, name, buf, &bufsz2, fp);
+           state = m_getfld2(&gstate, name, buf, &bufsz2);
        }
 
        /*
@@ -1750,7 +1826,7 @@ filterbody (struct mcomp *c1, char *buf, int bufsz, int state, FILE *fp,
         */
        _exit(0);
     case -1:
-       adios(NULL, "Unable to fork for filter writer process");
+       die("Unable to fork for filter writer process");
        break;
     }
 
@@ -1762,7 +1838,7 @@ 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;
+       int i, dat[5], argp;
 
     case 0:
        /*
@@ -1787,14 +1863,9 @@ filterbody (struct mcomp *c1, char *buf, int bufsz, int state, FILE *fp,
            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';
-
+           /* Trim a trailing linefeed that fmt_scan() likes to put at
+             * the end of the format string. */
+            trim_suffix_c(args[i], '\n');
            if (mhldebug)
                fprintf(stderr, "filterarg: fmt=\"%s\", output=\"%s\"\n",
                        a->a_nfs, args[i]);
@@ -1825,7 +1896,7 @@ filterbody (struct mcomp *c1, char *buf, int bufsz, int state, FILE *fp,
        break;
 
     case -1:
-       adios(NULL, "Unable to fork format program");
+       die("Unable to fork format program");
     }
 
     /*
@@ -1848,7 +1919,7 @@ filterbody (struct mcomp *c1, char *buf, int bufsz, int state, FILE *fp,
     }
 
     if (cc < 0) {
-       adios(NULL, "reading from formatproc");
+       die("reading from formatproc");
     }
 
     /*