More (and hopefully for now) the final cleanup for mhl.
[mmh] / uip / mhlsbr.c
index 7abbf43..53b61f9 100644 (file)
@@ -13,7 +13,7 @@
 #include <h/fmt_scan.h>
 #include <h/tws.h>
 #include <h/utils.h>
-#include <h/m_setjmp.h>
+#include <setjmp.h>
 #include <signal.h>
 #include <errno.h>
 #include <sys/wait.h>
@@ -53,47 +53,43 @@ static struct swit mhlswitches[] = {
     { "clear", 0 },
 #define        NCLRSW         3
     { "noclear", 0 },
-#define        FACESW         4
-    { "faceproc program", 0 },
-#define        NFACESW        5
-    { "nofaceproc", 0 },
-#define        FOLDSW         6
+#define        FOLDSW         4
     { "folder +folder", 0 },
-#define        FORMSW         7
+#define        FORMSW         5
     { "form formfile", 0 },
-#define        PROGSW         8
+#define        PROGSW         6
     { "moreproc program", 0 },
-#define        NPROGSW        9
+#define        NPROGSW        7
     { "nomoreproc", 0 },
-#define        LENSW         10
+#define        LENSW          8
     { "length lines", 0 },
-#define        WIDTHSW       11
+#define        WIDTHSW        9
     { "width columns", 0 },
-#define        SLEEPSW       12
+#define        SLEEPSW       10
     { "sleep seconds",  0 },
-#define        BITSTUFFSW    13
+#define        BITSTUFFSW    11
     { "dashstuffing", -12 },   /* interface from forw */
-#define        NBITSTUFFSW   14
+#define        NBITSTUFFSW   12
     { "nodashstuffing", -14 }, /* interface from forw */
-#define VERSIONSW     15
+#define VERSIONSW     13
     { "version", 0 },
-#define        HELPSW        16
+#define        HELPSW        14
     { "help", 0 },
-#define        FORW1SW       17
+#define        FORW1SW       15
     { "forward", -7 },         /* interface from forw */
-#define        FORW2SW       18
+#define        FORW2SW       16
     { "forwall", -7 },         /* interface from forw */
-#define        DGSTSW        19
+#define        DGSTSW        17
     { "digest list", -6 },
-#define VOLUMSW       20
+#define VOLUMSW       18
     { "volume number", -6 },
-#define ISSUESW       21
+#define ISSUESW       19
     { "issue number", -5 },
-#define NBODYSW       22
+#define NBODYSW       20
     { "nobody", -6 },
-#define FMTPROCSW     23
+#define FMTPROCSW     21
     { "fmtproc program", 0 },
-#define NFMTPROCSW    24
+#define NFMTPROCSW    22
     { "nofmtproc", 0 },
     { NULL, 0 }
 };
@@ -112,17 +108,16 @@ 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        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        LBITS   "\020\01NOCOMPONENT\02UPPERCASE\03CENTER\04CLEARTEXT\05EXTRA\06HDROUTPUT\07CLEARSCR\010LEFTADJUST\011COMPRESS\012ADDRFMT\013BELL\014DATEFMT\015FORMAT\016INIT\021SPLIT\022NONEWLINE\023NOWRAP\024FMTFILTER"
 #define        GFLAGS  (NOCOMPONENT | UPPERCASE | CENTER | LEFTADJUST | COMPRESS | SPLIT | NOWRAP)
 
 /*
- * A list of format arguments
+ * A format string to be used as a command-line argument to the body
+ * format filter.
  */
 
 struct arglist {
@@ -131,22 +126,61 @@ struct arglist {
     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;
-    struct arglist *c_f_args;  /* Argument list for filter*/
-    struct arglist *c_f_tail;  /* Pointer to tail of list */
-    int c_nargs;               /* Number of arguments     */
     struct mcomp *c_next;
 };
 
@@ -156,13 +190,11 @@ 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,
-    0, 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,
-    0, NULL
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, NOCOMPONENT, NULL
 };
 
 struct pair {
@@ -172,7 +204,7 @@ struct pair {
 
 static struct pair pairs[] = {
     { "Date",            DATEFMT },
-    { "From",            ADDRFMT|FACEDFLT },
+    { "From",            ADDRFMT },
     { "Sender",          ADDRFMT },
     { "Reply-To",        ADDRFMT },
     { "To",              ADDRFMT },
@@ -185,7 +217,6 @@ static struct pair pairs[] = {
     { "Resent-To",       ADDRFMT },
     { "Resent-cc",       ADDRFMT },
     { "Resent-Bcc",      ADDRFMT },
-    { "Face",            FACEFMT },
     { NULL,              0 }
 };
 
@@ -223,6 +254,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;
@@ -240,6 +287,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
@@ -272,8 +321,6 @@ static char delim4[] = "\n------------------------------\n\n";
 
 static FILE *(*mhl_action) () = (FILE *(*) ()) 0;
 
-static struct comp *mhlcomp[128];
-
 /*
  * Redefine a couple of functions.
  * These are undefined later in the code.
@@ -303,24 +350,19 @@ static void putch (char, 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 int compileargs (struct mcomp *, char *);
-static int checkcomp (char *, char *);
-static void addcomp (int, char *, char *);
-static void freecomps (void);
-static void freecomptext (void);
+static void compile_formatfield(struct mcomp *);
+static void compile_filterargs (void);
 
 
 int
 mhl (int argc, char **argv)
 {
     int length = 0, nomore = 0;
-    unsigned int i, vecp = 0;;
+    unsigned int i, vecp = 0;
     int width = 0;
     char *cp, *folder = NULL, *form = NULL;
     char buf[BUFSIZ], *files[MAXARGS];
@@ -337,9 +379,6 @@ mhl (int argc, char **argv)
     if ((cp = getenv ("MHLDEBUG")) && *cp)
        mhldebug++;
 
-    if ((cp = getenv ("FACEPROC")))
-       faceproc = cp;
-
     while ((cp = *argp++)) {
        if (*cp == '-') {
            switch (smatch (++cp, mhlswitches)) {
@@ -352,10 +391,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;
@@ -380,13 +419,6 @@ 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]);
@@ -482,9 +514,6 @@ mhl (int argc, char **argv)
        ontty = NOTTY;
     }
 
-    for (i = 0; i < sizeof(mhlcomp)/sizeof(mhlcomp[0]); i++)
-       mhlcomp[i] = NULL;
-
     mhl_format (form ? form : mhlformat, length, width);
 
     if (vecp == 0) {
@@ -494,8 +523,6 @@ mhl (int argc, char **argv)
            process (folder, files[i], i + 1, vecp);
     }
 
-    freecomps();
-
     if (forwall) {
        if (digest) {
            printf ("%s", delim4);
@@ -575,6 +602,7 @@ 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;
@@ -634,13 +662,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)
+                       if (global.c_flags & DATEFMT) {
                            c1->c_nfs = getcpy (global.c_nfs);
+                           compile_formatfield(c1);
+                       }
                    }
                    else
                        if (c1->c_flags & ADDRFMT) {
-                           if (global.c_flags & ADDRFMT)
+                           if (global.c_flags & ADDRFMT) {
                                c1->c_nfs = getcpy (global.c_nfs);
+                               compile_formatfield(c1);
+                           }
                        }
                }
                continue;
@@ -715,21 +747,17 @@ evalvar (struct mcomp *c1)
        return ptos (name, &c1->c_ovtxt);
 
     if (!mh_strcasecmp (name, "formatfield")) {
-       char *nfs;
-
        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);
+       c1->c_nfs = getcpy (new_fs (NULL, NULL, "%(decode{text})"));
+       compile_formatfield(c1);
        c1->c_flags |= FORMAT;
        return 0;
     }
@@ -755,18 +783,32 @@ evalvar (struct mcomp *c1)
        }
 
    if (!mh_strcasecmp (name, "formatarg")) {
-       char *nfs;
-       int rc;
+       struct arglist *args;
 
        if (ptos (name, &cp))
            return 1;
-       nfs = new_fs (NULL, NULL, cp);
 
-       rc = compileargs(c1, nfs);
+       if (mh_strcasecmp (c1->c_name, "body")) {
+           advise (NULL, "format filters are currently only supported on "
+                   "the \"body\" component");
+           return 1;
+       }
 
-       return rc;
-    }
+       args = (struct arglist *) calloc((size_t) 1, sizeof(struct arglist));
 
+       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;
 }
@@ -842,14 +884,20 @@ parse (void)
 }
 
 
+/*
+ * Process one file/message
+ */
+
 static void
 process (char *folder, char *fname, int ofilen, int ofilec)
 {
     char *cp = NULL;
     FILE *fp = NULL;
     struct mcomp *c1;
+    struct stat st;
+    struct arglist *ap;
 
-    switch (m_setjmp (env)) {
+    switch (setjmp (env)) {
        case OK: 
            if (fname) {
                fp = mhl_action ? (*mhl_action) (fname) : fopen (fname, "r");
@@ -862,11 +910,24 @@ process (char *folder, char *fname, int ofilen, int ofilec)
                fname = "(stdin)";
                fp = stdin;
            }
+           if (fstat(fileno(fp), &st) == 0) {
+               filesize = st.st_size;
+           } else {
+               filesize = 0;
+           }
            cp = folder ? concat (folder, ":", fname, NULL) : getcpy (fname);
            if (ontty != PITTY)
                SIGNAL (SIGINT, intrser);
            mhlfile (fp, cp, ofilen, ofilec);  /* FALL THROUGH! */
 
+           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);
@@ -883,7 +944,6 @@ process (char *folder, char *fname, int ofilen, int ofilec)
            break;
     }
 
-    freecomptext();
 }
 
 
@@ -894,6 +954,8 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
     struct mcomp *c1, *c2, *c3;
     char **ip, name[NAMESZ], buf[BUFSIZ];
 
+    compile_filterargs();
+
     if (forwall) {
        if (digest)
            printf ("%s", ofilen == 1 ? delim3 : delim4);
@@ -956,12 +1018,12 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
        switch (state = m_getfld (state, name, buf, sizeof(buf), fp)) {
            case FLD: 
            case FLDPLUS: 
-               bucket = checkcomp(name, buf);
+               bucket = fmt_addcomp(name, buf);
                for (ip = ignores; *ip; ip++)
                    if (!mh_strcasecmp (name, *ip)) {
                        while (state == FLDPLUS) {
                            state = m_getfld (state, name, buf, sizeof(buf), fp);
-                           addcomp(bucket, name, buf);
+                           fmt_appendcomp(bucket, name, buf);
                        }
                        break;
                    }
@@ -984,7 +1046,7 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
                while (state == FLDPLUS) {
                    state = m_getfld (state, name, buf, sizeof(buf), fp);
                    c1->c_text = add (buf, c1->c_text);
-                   addcomp(bucket, name, buf);
+                   fmt_appendcomp(bucket, name, buf);
                }
                if (c2 == NULL)
                    c1->c_flags |= EXTRA;
@@ -1035,17 +1097,6 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
                            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;
-                           }
                }
                return;
 
@@ -1103,7 +1154,6 @@ mcomp_format (struct mcomp *c1, struct mcomp *c2)
     int dat[5];
     char *ap, *cp;
     char buffer[BUFSIZ], error[BUFSIZ];
-    struct comp *cptr;
     struct pqpair *p, *q;
     struct pqpair pq;
     struct mailname *mp;
@@ -1112,20 +1162,18 @@ mcomp_format (struct mcomp *c1, struct mcomp *c2)
     c2->c_text = NULL;
     dat[0] = 0;
     dat[1] = 0;
-    dat[2] = 0;
+    dat[2] = filesize;
     dat[3] = sizeof(buffer) - 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;
+       if (c1->c_c_text)
+           c1->c_c_text->c_text = getcpy (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, buffer, sizeof buffer - 1, sizeof buffer - 1, dat);
        /* Don't need to append a newline, dctime() already did */
        c2->c_text = getcpy (buffer);
 
@@ -1142,15 +1190,6 @@ mcomp_format (struct mcomp *c1, struct mcomp *c2)
            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);
        }
@@ -1158,14 +1197,16 @@ mcomp_format (struct mcomp *c1, struct mcomp *c2)
     }
 
     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;
+       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, buffer, sizeof(buffer) - 1, dat);
+       fmt_scan (c1->c_fmt, buffer, sizeof buffer - 1, sizeof buffer - 1, dat);
        if (*buffer) {
            if (c2->c_text)
                c2->c_text = add (",\n", c2->c_text);
@@ -1174,7 +1215,8 @@ mcomp_format (struct mcomp *c1, struct mcomp *c2)
            c2->c_text = add (buffer, c2->c_text);
        }
 
-       free (p->pq_text);
+       if (p->pq_text)
+           free (p->pq_text);
        if (p->pq_error)
            free (p->pq_error);
        q = p->pq_next;
@@ -1233,20 +1275,7 @@ free_queue (struct mcomp **head, struct mcomp **tail)
        if (c1->c_nfs)
            free (c1->c_nfs);
        if (c1->c_fmt)
-           free ((char *) c1->c_fmt);
-       if (c1->c_face)
-           free (c1->c_face);
-       if (c1->c_f_args) {
-           struct arglist *a1, *a2;
-           for (a1 = c1->c_f_args; a1; a1 = a2) {
-               a2 = a1->a_next;
-               if (a1->a_fmt)
-                   free(a1->a_fmt);
-               if (a1->a_nfs)
-                   free(a1->a_nfs);
-           }
-           free(a1);
-       }
+           fmt_free (c1->c_fmt, 0);
        free ((char *) c1);
     }
 
@@ -1279,16 +1308,6 @@ putcomp (struct mcomp *c1, struct mcomp *c2, int flag)
        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);
 
@@ -1550,214 +1569,15 @@ 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;
+    struct arglist *a, *a2;
 
-    switch (m_setjmp (mhlenv)) {
+    switch (setjmp (mhlenv)) {
        case OK: 
            cp = invo_name;
            sleepsw = 0;        /* XXX */
@@ -1792,6 +1612,15 @@ mhlsbr (int argc, char **argv, FILE *(*action)())
            free_queue (&msghd, &msgtl);
            for (c1 = fmthd; c1; c1 = c1->c_next)
                c1->c_flags &= ~HDROUTPUT;
+
+           a = arglist_head;
+           while (a) {
+               if (a->a_nfs)
+                   free(a->a_nfs);
+               a2 = a->a_next;
+               free(a);
+               a = a2;
+           }
            return exitstat;
     }
 }
@@ -1888,169 +1717,65 @@ m_pclose (void)
 
 
 /*
- * Compile a format string and add it to the list of arguments used by
- * the formatproc.
+ * Compile a format string used by the formatfield option and save it
+ * for later.
  *
- * This deserves some explanation.  Here's the deal:
- *
- * We want to keep track of components used as arguments by formatproc,
- * but the hash table is reset every time fmt_compile is called.  So we
- * iterate through the function list looking for things that use components
- * and save the name.  And because we might get the same components used
- * by different arguments we need to keep track to every reference of
- * every component so we can add them when the message is processed.  So
- * we compile the argument string now (to get the components we use) and
- * save them 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 int
-compileargs (struct mcomp *c1, char *nfs)
+static void
+compile_formatfield(struct mcomp *c1)
 {
-    struct format *fmt;
-    struct arglist *args;
-    unsigned int i;
-
-    i = fmt_compile(nfs, &fmt);
-
-    args = (struct arglist *) mh_xmalloc(sizeof(struct arglist));
-
-    if (! args)
-       adios (NULL, "Unable to allocate formatproc args storage");
-
-    args->a_fmt = fmt;
-    args->a_nfs = format_string;
-    args->a_next = NULL;
-    c1->c_nargs++;
-    format_string = NULL;
-
-    if (c1->c_f_tail)
-       c1->c_f_tail->a_next = args;
-
-    c1->c_f_tail = args;
-
-    if (! c1->c_f_args)
-       c1->c_f_args = args;
-
-    if (i == 0)
-       return 0;
+    fmt_compile(c1->c_nfs, &c1->c_fmt, 1);
 
     /*
-     * If wantcomp ever changes size, we need to change the size
-     * of mhlcomp as well
+     * 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!
      */
 
-    for (i = 0; i < sizeof(wantcomp)/sizeof(wantcomp[0]); i++) {
-       if (wantcomp[i]) {
-           if (mhlcomp[i]) {
-               struct comp *c;
-               for (c = mhlcomp[i]; c->c_next != NULL; c = c->c_next)
-                   ;
-               c->c_next = wantcomp[i];
-           } else
-               mhlcomp[i] = wantcomp[i];
-       }
-    }
-
-    return 0;
+    c1->c_c_text = fmt_findcomp("text");
+    c1->c_c_error = fmt_findcomp("error");
 }
 
 /*
- * Check to see if we are interested in a component.  If we are, save
- * the text.
- */
-
-static int
-checkcomp(char *name, char *buf)
-{
-    int found = 0, i;
-    struct comp *c;
-    int bucket = CHASH(name);
-    char *cp;
-    if ((c = mhlcomp[bucket])) {
-       do {
-           if (mh_strcasecmp(name, c->c_name) == 0) {
-               found++;
-               if (! c->c_text) {
-                   i = strlen(c->c_text = strdup(buf)) - 1;
-                   if (c->c_text[i] == '\n')
-                       c->c_text[i] = '\0';
-               } else {
-                   i = strlen(cp = c->c_text) - 1;
-                   if (cp[i] == '\n') {
-                       if (c->c_type & CT_ADDR) {
-                           cp[i] = '\0';
-                           cp = add (",\n\t", cp);
-                       } else {
-                           cp = add ("\t", cp);
-                       }
-                   }
-                   c->c_text = add (buf, cp);
-               }
-           }
-       } while ((c = c->c_next));
-    }
-
-    return found ? bucket : -1;
-}
-
-/*
- * Add text to an existing component
+ * 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().
+ *
  */
 
 static void
-addcomp(int bucket, char *name, char *buf)
+compile_filterargs (void)
 {
-    struct comp *c;
-
-    if (bucket != -1) {
-       c = mhlcomp[bucket];
-       do {
-           if (mh_strcasecmp(name, c->c_name) == 0)
-               c->c_text = add (buf, c->c_text);
-       } while ((c = c->c_next));
-    }
-}
+    struct arglist *arg = arglist_head;
+    struct comp *cptr;
+    char **ap;
 
-/*
- * Free up saved component structures
- */
+    fmt_free(NULL, 1);
 
-static void
-freecomps(void)
-{
-    struct comp *c1, *c2;
-    unsigned int i;
-
-    for (i = 0; i < sizeof(mhlcomp)/sizeof(mhlcomp[0]); i++) {
-       if ((c1 = mhlcomp[i]))
-           for (; c1; c1 = c2) {
-               c2 = c1->c_next;
-               if (c1->c_text)
-                   free(c1->c_text);
-               free(c1);
-           }
+    while (arg) {
+       fmt_compile(arg->a_nfs, &arg->a_fmt, 0);
+       arg = arg->a_next;
     }
-}
 
-/*
- * Just free up the component text.
- */
+    /*
+     * Search through and mark any components that are address components
+     */
 
-static void
-freecomptext(void)
-{
-    struct comp *c1;
-    unsigned int i;
-
-    for (i = 0; i < sizeof(mhlcomp)/sizeof(mhlcomp[0]); i++) {
-       if ((c1 = mhlcomp[i]))
-           for (; c1; c1 = c1->c_next) {
-               if (c1->c_text) {
-                   free(c1->c_text);
-                   c1->c_text = NULL;
-               }
-           }
+    for (ap = addrcomps; *ap; ap++) {
+       cptr = fmt_findcomp (*ap);
+       if (cptr)
+           cptr->c_type |= CT_ADDR;
     }
 }
 
@@ -2148,9 +1873,9 @@ filterbody (struct mcomp *c1, char *buf, int bufsz, int state, FILE *fp)
         * Allocate an argument array for us
         */
 
-       args = (char **) mh_xmalloc((c1->c_nargs + 2) * sizeof(char *));
+       args = (char **) mh_xmalloc((filter_nargs + 2) * sizeof(char *));
        args[0] = formatproc;
-       args[c1->c_nargs + 1] = NULL;
+       args[filter_nargs + 1] = NULL;
        dat[0] = 0;
        dat[1] = 0;
        dat[2] = 0;
@@ -2161,9 +1886,9 @@ filterbody (struct mcomp *c1, char *buf, int bufsz, int state, FILE *fp)
         * Pull out each argument and scan them.
         */
 
-       for (a = c1->c_f_args, i = 1; a != NULL; a = a->a_next, i++) {
+       for (a = arglist_head, i = 1; a != NULL; a = a->a_next, i++) {
            args[i] = mh_xmalloc(BUFSIZ);
-           fmt_scan(a->a_fmt, args[i], BUFSIZ, dat);
+           fmt_scan(a->a_fmt, args[i], BUFSIZ - 1, BUFSIZ, dat);
            /*
             * fmt_scan likes to put a trailing newline at the end of the
             * format string.  If we have one, get rid of it.
@@ -2171,6 +1896,10 @@ filterbody (struct mcomp *c1, char *buf, int bufsz, int state, FILE *fp)
            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) {