Use _exit() instead of exit() so our writer process doesn't flush out
[mmh] / uip / mhlsbr.c
index 58b2978..0798746 100644 (file)
 #include <h/fmt_scan.h>
 #include <h/tws.h>
 #include <h/utils.h>
-#include <setjmp.h>
+#include <h/m_setjmp.h>
 #include <signal.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <sys/types.h>
 
 /*
  * MAJOR BUG:
@@ -88,6 +91,10 @@ static struct swit mhlswitches[] = {
     { "issue number", -5 },
 #define NBODYSW       22
     { "nobody", -6 },
+#define FMTPROCSW     23
+    { "fmtproc program", 0 },
+#define NFMTPROCSW    24
+    { "nofmtproc", 0 },
     { NULL, 0 }
 };
 
@@ -109,8 +116,20 @@ static struct swit mhlswitches[] = {
 #define        FACEDFLT    0x008000    /* default for face                  */
 #define        SPLIT       0x010000    /* split headers (don't concatenate) */
 #define        NONEWLINE   0x020000    /* don't write trailing newline      */
-#define        LBITS   "\020\01NOCOMPONENT\02UPPERCASE\03CENTER\04CLEARTEXT\05EXTRA\06HDROUTPUT\07CLEARSCR\010LEFTADJUST\011COMPRESS\012ADDRFMT\013BELL\014DATEFMT\015FORMAT\016INIT\017FACEFMT\020FACEDFLT\021SPLIT\022NONEWLINE"
-#define        GFLAGS  (NOCOMPONENT | UPPERCASE | CENTER | LEFTADJUST | COMPRESS | SPLIT)
+#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        GFLAGS  (NOCOMPONENT | UPPERCASE | CENTER | LEFTADJUST | COMPRESS | SPLIT | NOWRAP)
+
+/*
+ * A list of format arguments
+ */
+
+struct arglist {
+    struct format *a_fmt;
+    char *a_nfs;
+    struct arglist *a_next;
+};
 
 struct mcomp {
     char *c_name;              /* component name          */
@@ -125,6 +144,9 @@ struct mcomp {
     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;
 };
 
@@ -134,11 +156,11 @@ static struct mcomp *fmthd = NULL;
 static struct mcomp *fmttl = NULL;
 
 static struct mcomp global = {
-    NULL, NULL, "", NULL, NULL, 0, -1, 80, -1, 40, BELL, 0
+    NULL, NULL, NULL, NULL, NULL, NULL, 0, -1, 80, -1, 40, BELL, NULL
 };
 
 static struct mcomp holder = {
-    NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, NOCOMPONENT, 0
+    NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, NOCOMPONENT, NULL
 };
 
 struct pair {
@@ -192,6 +214,10 @@ static struct triple triples[] = {
     { "datefield",     DATEFMT,     ADDRFMT },
     { "newline",       0,           NONEWLINE },
     { "nonewline",     NONEWLINE,   0 },
+    { "wrap",          0,           NOWRAP },
+    { "nowrap",        NOWRAP,      0 },
+    { "format",        FMTFILTER,   0 },
+    { "noformat",      0,           FMTFILTER },
     { NULL,            0,           0 }
 };
 
@@ -218,13 +244,13 @@ static int mhldebug = 0;
 static int ontty = NOTTY;
 
 static int row;
-static int column;
+static unsigned int column;
 
 static int lm;
 static int llim;
 static int ovoff;
 static int term;
-static int wid;
+static unsigned int wid;
 
 static char *ovtxt;
 
@@ -244,6 +270,7 @@ static char delim4[] = "\n------------------------------\n\n";
 
 static FILE *(*mhl_action) () = (FILE *(*) ()) 0;
 
+static struct comp *mhlcomp[128];
 
 /*
  * Redefine a couple of functions.
@@ -269,26 +296,22 @@ static struct mcomp *add_queue (struct mcomp **, struct mcomp **, char *, char *
 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 *);
-static void putch (char);
-static RETSIGTYPE intrser (int);
-static RETSIGTYPE pipeser (int);
-static RETSIGTYPE quitser (int);
+static void putstr (char *, long);
+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 *);
-
-int mhl (int, char **);
-int mhlsbr (int, char **, FILE *(*)());
-void m_pclose (void);
-
-void clear_screen (void);             /* from termsbr.c */
-int SOprintf (char *, ...);           /* from termsbr.c */
-int sc_width (void);                  /* from termsbr.c */
-int sc_length (void);                 /* from termsbr.c */
-int sc_hardcopy (void);               /* from termsbr.c */
+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);
 
 
 int
@@ -375,6 +398,14 @@ 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]);
@@ -448,6 +479,9 @@ 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) {
@@ -457,6 +491,8 @@ mhl (int argc, char **argv)
            process (folder, files[i], i + 1, vecp);
     }
 
+    freecomps();
+
     if (forwall) {
        if (digest) {
            printf ("%s", delim4);
@@ -474,7 +510,7 @@ mhl (int argc, char **argv)
            printf ("%s", buf);
        }
        else
-           printf ("\n------- End of Forwarded Message%s\n\n",
+           printf ("\n------- End of Forwarded Message%s\n",
                    vecp > 1 ? "s" : "");
     }
 
@@ -715,6 +751,20 @@ evalvar (struct mcomp *c1)
            return 0;
        }
 
+   if (!mh_strcasecmp (name, "formatarg")) {
+       char *nfs;
+       int rc;
+
+       if (ptos (name, &cp))
+           return 1;
+       nfs = new_fs (NULL, NULL, cp);
+
+       rc = compileargs(c1, nfs);
+
+       return rc;
+    }
+
+
     return 1;
 }
 
@@ -796,7 +846,7 @@ process (char *folder, char *fname, int ofilen, int ofilec)
     FILE *fp = NULL;
     struct mcomp *c1;
 
-    switch (setjmp (env)) {
+    switch (m_setjmp (env)) {
        case OK: 
            if (fname) {
                fp = mhl_action ? (*mhl_action) (fname) : fopen (fname, "r");
@@ -829,13 +879,15 @@ process (char *folder, char *fname, int ofilen, int ofilec)
                c1->c_flags &= ~HDROUTPUT;
            break;
     }
+
+    freecomptext();
 }
 
 
 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];
 
@@ -901,10 +953,13 @@ 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);
                for (ip = ignores; *ip; ip++)
                    if (!mh_strcasecmp (name, *ip)) {
-                       while (state == FLDPLUS)
+                       while (state == FLDPLUS) {
                            state = m_getfld (state, name, buf, sizeof(buf), fp);
+                           addcomp(bucket, name, buf);
+                       }
                        break;
                    }
                if (*ip)
@@ -926,6 +981,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);
                }
                if (c2 == NULL)
                    c1->c_flags |= EXTRA;
@@ -954,15 +1010,20 @@ mhlfile (FILE *fp, char *mname, int ofilen, int ofilec)
                        continue;
                    }
                    if (dobody && !mh_strcasecmp (c1->c_name, "body")) {
-                       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);
+                       if (c1->c_flags & FMTFILTER && state == BODY &&
+                                                       formatproc != NULL) {
+                           filterbody(c1, buf, sizeof(buf), state, fp);
+                       } 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);
+                           }
+                           free (holder.c_text);
+                           holder.c_text = NULL;
                        }
-                       free (holder.c_text);
-                       holder.c_text = NULL;
                        continue;
                    }
                    for (c2 = msghd; c2; c2 = c2->c_next)
@@ -1081,7 +1142,7 @@ mcomp_format (struct mcomp *c1, struct mcomp *c2)
            if ((c1->c_flags & FACEDFLT) && c2->c_face == NULL) {
                char   *h, *o;
                if ((h = mp->m_host) == NULL)
-                   h = LocalName ();
+                   h = LocalName (0);
                if ((o = OfficialName (h)))
                    h = o;
                c2->c_face = concat ("address ", h, " ", mp->m_mbox,
@@ -1172,6 +1233,17 @@ free_queue (struct mcomp **head, struct mcomp **tail)
            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);
+       }
        free ((char *) c1);
     }
 
@@ -1199,8 +1271,8 @@ putcomp (struct mcomp *c1, struct mcomp *c2, int flag)
     onelp = NULL;
 
     if (c1->c_flags & CLEARTEXT) {
-       putstr (c1->c_text);
-       putstr ("\n");
+       putstr (c1->c_text, c1->c_flags);
+       putstr ("\n", c1->c_flags);
        return;
     }
 
@@ -1233,9 +1305,9 @@ putcomp (struct mcomp *c1, struct mcomp *c2, int flag)
            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);
+       putstr (c1->c_text ? c1->c_text : c1->c_name, c1->c_flags);
        if (flag != BODYCOMP) {
-           putstr (": ");
+           putstr (": ", c1->c_flags);
            if (!(c1->c_flags & SPLIT))
                c1->c_flags |= HDROUTPUT;
 
@@ -1243,7 +1315,7 @@ putcomp (struct mcomp *c1, struct mcomp *c2, int flag)
        if ((count = c1->c_cwidth -
                strlen (c1->c_text ? c1->c_text : c1->c_name) - 2) > 0)
            while (count--)
-               putstr (" ");
+               putstr (" ", c1->c_flags);
        }
        else
            c1->c_flags |= HDROUTPUT;           /* for BODYCOMP */
@@ -1256,15 +1328,15 @@ putcomp (struct mcomp *c1, struct mcomp *c2, int flag)
            for (cp = c2->c_name; *cp; cp++)
                if (islower (*cp))
                    *cp = toupper (*cp);
-       putstr (c2->c_name);
-       putstr (": ");
+       putstr (c2->c_name, c1->c_flags);
+       putstr (": ", c1->c_flags);
        if (!(c1->c_flags & SPLIT))
            c2->c_flags |= HDROUTPUT;
 
        cchdr++;
        if ((count = c1->c_cwidth - strlen (c2->c_name) - 2) > 0)
            while (count--)
-               putstr (" ");
+               putstr (" ", c1->c_flags);
     }
     if (c1->c_flags & UPPERCASE)
        for (cp = c2->c_text; *cp; cp++)
@@ -1275,26 +1347,26 @@ putcomp (struct mcomp *c1, struct mcomp *c2, int flag)
     if (cchdr) {
        if (flag == TWOCOMP)
            count = (c1->c_cwidth >= 0) ? c1->c_cwidth
-                       : strlen (c2->c_name) + 2;
+                       : (int) strlen (c2->c_name) + 2;
        else
-           count = (c1->c_cwidth >= 0) ? c1->c_cwidth
+           count = (c1->c_cwidth >= 0) ? (size_t) c1->c_cwidth
                        : strlen (c1->c_text ? c1->c_text : c1->c_name) + 2;
     }
     count += c1->c_offset;
 
     if ((cp = oneline (c2->c_text, c1->c_flags)))
-       putstr(cp);
+       putstr(cp, c1->c_flags);
     if (term == '\n')
-       putstr ("\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);
+           putstr (c1->c_text ? c1->c_text : c1->c_name, c1->c_flags);
        if (*cp)
-           putstr (cp);
+           putstr (cp, c1->c_flags);
        if (term == '\n')
-           putstr ("\n");
+           putstr ("\n", c1->c_flags);
     }
     if (flag == BODYCOMP && term == '\n')
        c1->c_flags &= ~HDROUTPUT;              /* Buffer ended on a newline */
@@ -1354,27 +1426,27 @@ oneline (char *stuff, long flags)
 
 
 static void
-putstr (char *string)
+putstr (char *string, long flags)
 {
     if (!column && lm > 0) {
        while (lm > 0)
            if (lm >= 8) {
-               putch ('\t');
+               putch ('\t', flags);
                lm -= 8;
            }
            else {
-               putch (' ');
+               putch (' ', flags);
                lm--;
            }
     }
     lm = 0;
     while (*string)
-       putch (*string++);
+       putch (*string++, flags);
 }
 
 
 static void
-putch (char ch)
+putch (char ch, long flags)
 {
     char buf[BUFSIZ];
 
@@ -1431,12 +1503,12 @@ putch (char ch)
            break;
     }
 
-    if (column >= wid) {
-       putch ('\n');
+    if (column >= wid && (flags & NOWRAP) == 0) {
+       putch ('\n', flags);
        if (ovoff > 0)
            lm = ovoff;
-       putstr (ovtxt ? ovtxt : "");
-       putch (ch);
+       putstr (ovtxt ? ovtxt : "", flags);
+       putch (ch, flags);
        return;
     }
 
@@ -1444,12 +1516,10 @@ putch (char ch)
 }
 
 
-static RETSIGTYPE
+static void
 intrser (int i)
 {
-#ifndef RELIABLE_SIGNALS
-    SIGNAL (SIGINT, intrser);
-#endif
+    NMH_UNUSED (i);
 
     discard (stdout);
     putchar ('\n');
@@ -1457,23 +1527,19 @@ intrser (int i)
 }
 
 
-static RETSIGTYPE
+static void
 pipeser (int i)
 {
-#ifndef RELIABLE_SIGNALS
-    SIGNAL (SIGPIPE, pipeser);
-#endif
+    NMH_UNUSED (i);
 
     done (NOTOK);
 }
 
 
-static RETSIGTYPE
+static void
 quitser (int i)
 {
-#ifndef RELIABLE_SIGNALS
-    SIGNAL (SIGQUIT, quitser);
-#endif
+    NMH_UNUSED (i);
 
     putchar ('\n');
     fflush (stdout);
@@ -1494,7 +1560,7 @@ face_format (struct mcomp *c1)
        if ((mp = getm (cp, NULL, 0, AD_NAME, NULL))) {
            char *h, *o;
            if ((h = mp->m_host) == NULL)
-               h = LocalName ();
+               h = LocalName (0);
            if ((o = OfficialName (h)))
                h = o;
            c1->c_face = concat ("address ", h, " ", mp->m_mbox, NULL);
@@ -1515,10 +1581,7 @@ face_format (struct mcomp *c1)
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netdb.h>
-
-#ifdef HAVE_ARPA_INET_H
-# include <arpa/inet.h>
-#endif
+#include <arpa/inet.h>
 
 static int
 doface (struct mcomp *c1)
@@ -1691,7 +1754,7 @@ mhlsbr (int argc, char **argv, FILE *(*action)())
     char *cp = NULL;
     struct mcomp *c1;
 
-    switch (setjmp (mhlenv)) {
+    switch (m_setjmp (mhlenv)) {
        case OK: 
            cp = invo_name;
            sleepsw = 0;        /* XXX */
@@ -1770,7 +1833,7 @@ m_popen (char *name)
     if (pipe (pd) == NOTOK)
        adios ("pipe", "unable to");
 
-    switch (m_pid = vfork ()) {
+    switch (m_pid = vfork()) {
        case NOTOK: 
            adios ("fork", "unable to");
 
@@ -1819,3 +1882,371 @@ m_pclose (void)
     pidwait (m_pid, OK);
     m_pid = NOTOK;
 }
+
+
+/*
+ * Compile a format string and add it to the list of arguments used by
+ * the formatproc.
+ *
+ * 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.
+ */
+
+static int
+compileargs (struct mcomp *c1, char *nfs)
+{
+    struct format *fmt;
+    struct arglist *args;
+    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;
+
+    /*
+     * If wantcomp ever changes size, we need to change the size
+     * of mhlcomp as well
+     */
+
+    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;
+}
+
+/*
+ * 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
+ */
+
+static void
+addcomp(int bucket, char *name, char *buf)
+{
+    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));
+    }
+}
+
+/*
+ * Free up saved component structures
+ */
+
+static void
+freecomps(void)
+{
+    struct comp *c1, *c2;
+    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);
+           }
+    }
+}
+
+/*
+ * Just free up the component text.
+ */
+
+static void
+freecomptext(void)
+{
+    struct comp *c1;
+    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;
+               }
+           }
+    }
+}
+
+/*
+ * Filter the body of a message through a specified format program
+ */
+
+static void
+filterbody (struct mcomp *c1, char *buf, int bufsz, int state, FILE *fp)
+{
+    struct mcomp holder;
+    char name[NAMESZ];
+    int fdinput[2], fdoutput[2], waitstat;
+    ssize_t cc;
+    pid_t writerpid, filterpid;
+
+    /*
+     * Create pipes so we can communicate with our filter process.
+     */
+
+    if (pipe(fdinput) < 0) {
+       adios(NULL, "Unable to create input pipe");
+    }
+
+    if (pipe(fdoutput) < 0) {
+       adios(NULL, "Unable to create output pipe");
+    }
+
+    /*
+     * Here's what we're doing to do.
+     *
+     * - Fork ourselves and start writing data to the write side of the
+     *   input pipe (fdinput[1]).
+     *
+     * - Fork and exec our filter program.  We set the standard input of
+     *   our filter program to be the read side of our input pipe (fdinput[0]).
+     *   Standard output is set to the write side of our output pipe
+     *   (fdoutput[1]).
+     *
+     * - We read from the read side of the output pipe (fdoutput[0]).
+     *
+     * We're forking because that's the simplest way to prevent any deadlocks.
+     * (without doing something like switching to non-blocking I/O and using
+     * select or poll, and I'm not interested in doing that).
+     */
+
+    switch (writerpid = fork()) {
+    case 0:
+       /*
+        * Our child process - just write to the filter input (fdinput[1]).
+        * Close all other descriptors that we don't need.
+        */
+
+       close(fdinput[0]);
+       close(fdoutput[0]);
+       close(fdoutput[1]);
+
+       /*
+        * Call m_getfld() until we're no longer in the BODY state
+        */
+
+       while (state == BODY) {
+           write(fdinput[1], buf, strlen(buf));
+           state = m_getfld(state, name, buf, bufsz, fp);
+       }
+
+       /*
+        * We should be done; time to exit.
+        */
+
+       close(fdinput[1]);
+       /*
+        * Make sure we call _exit(), otherwise we may flush out the stdio
+        * buffers that we have duplicated from the parent.
+        */
+       _exit(0);
+       break;
+    case -1:
+       adios(NULL, "Unable to fork for filter writer process");
+       break;
+    }
+
+    /*
+     * Fork and exec() our filter program, after redirecting standard in
+     * and standard out appropriately.
+     */
+
+    switch (filterpid = fork()) {
+        char **args;
+       struct arglist *a;
+       int i, dat[5], s;
+
+    case 0:
+       /*
+        * Allocate an argument array for us
+        */
+
+       args = (char **) mh_xmalloc((c1->c_nargs + 2) * sizeof(char *));
+       args[0] = formatproc;
+       args[c1->c_nargs + 1] = 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 = c1->c_f_args, 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 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 (dup2(fdinput[0], STDIN_FILENO) < 0) {
+           adios("formatproc", "Unable to dup2() standard input");
+       }
+       if (dup2(fdoutput[1], STDOUT_FILENO) < 0) {
+           adios("formatproc", "Unable to dup2() standard output");
+       }
+
+       /*
+        * Close everything (especially the old input and output
+        * descriptors, since they've been dup'd to stdin and stdout),
+        * and exec the formatproc.
+        */
+
+       close(fdinput[0]);
+       close(fdinput[1]);
+       close(fdoutput[0]);
+       close(fdoutput[1]);
+
+       execvp(formatproc, args);
+
+       adios(formatproc, "Unable to execute filter");
+
+       break;
+
+    case -1:
+       adios(NULL, "Unable to fork format program");
+    }
+
+    /*
+     * Close everything except our reader (fdoutput[0]);
+     */
+
+    close(fdinput[0]);
+    close(fdinput[1]);
+    close(fdoutput[1]);
+
+    /*
+     * As we read in this data, send it to putcomp
+     */
+
+    holder.c_text = buf;
+
+    while ((cc = read(fdoutput[0], buf, bufsz - 1)) > 0) {
+       buf[cc] = '\0';
+       putcomp(c1, &holder, BODYCOMP);
+    }
+
+    if (cc < 0) {
+       adios(NULL, "reading from formatproc");
+    }
+
+    /*
+     * See if we got any errors along the way.  I'm a little leery of calling
+     * waitpid() without WNOHANG, but it seems to be the most correct solution.
+     */
+
+    if (waitpid(filterpid, &waitstat, 0) < 0) {
+       if (errno != ECHILD) {
+           adios("filterproc", "Unable to determine status");
+       }
+    } else {
+       if (! (WIFEXITED(waitstat) && WEXITSTATUS(waitstat) == 0)) {
+           pidstatus(waitstat, stderr, "filterproc");
+       }
+    }
+
+    if (waitpid(writerpid, &waitstat, 0) < 0) {
+       if (errno != ECHILD) {
+           adios("writer process", "Unable to determine status");
+           done(1);
+       }
+    } else {
+       if (! (WIFEXITED(waitstat) && WEXITSTATUS(waitstat) == 0)) {
+           pidstatus(waitstat, stderr, "writer process");
+           done(1);
+       }
+    }
+
+    close(fdoutput[0]);
+}