Added argument to fmt_scan() to specify the buffer size.
[mmh] / uip / mhlsbr.c
index 6335463..9964fa0 100644 (file)
 #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>
+#include <sys/types.h>
 
 /*
  * MAJOR BUG:
@@ -50,44 +53,44 @@ 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     21
+    { "fmtproc program", 0 },
+#define NFMTPROCSW    22
+    { "nofmtproc", 0 },
     { NULL, 0 }
 };
 
@@ -105,12 +108,22 @@ 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        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\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          */
@@ -118,13 +131,15 @@ struct mcomp {
     char *c_ovtxt;             /* text overflow indicator */
     char *c_nfs;               /* iff FORMAT              */
     struct format *c_fmt;      /*   ..                    */
-    char *c_face;              /* face designator         */
     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;
 };
 
@@ -134,11 +149,13 @@ static struct mcomp *fmthd = NULL;
 static struct mcomp *fmttl = NULL;
 
 static struct mcomp global = {
-    NULL, NULL, NULL, NULL, NULL, NULL, 0, -1, 80, -1, 40, BELL, NULL
+    NULL, NULL, NULL, NULL, NULL, 0, -1, 80, -1, 40, BELL, NULL, NULL, 0,
+    NULL
 };
 
 static struct mcomp holder = {
-    NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, NOCOMPONENT, NULL
+    NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, NOCOMPONENT, NULL, NULL, 0,
+    NULL
 };
 
 struct pair {
@@ -148,7 +165,7 @@ struct pair {
 
 static struct pair pairs[] = {
     { "Date",            DATEFMT },
-    { "From",            ADDRFMT|FACEDFLT },
+    { "From",            ADDRFMT },
     { "Sender",          ADDRFMT },
     { "Reply-To",        ADDRFMT },
     { "To",              ADDRFMT },
@@ -161,7 +178,6 @@ static struct pair pairs[] = {
     { "Resent-To",       ADDRFMT },
     { "Resent-cc",       ADDRFMT },
     { "Resent-Bcc",      ADDRFMT },
-    { "Face",            FACEFMT },
     { NULL,              0 }
 };
 
@@ -192,9 +208,29 @@ 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 }
 };
 
+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;
@@ -212,6 +248,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
@@ -244,6 +282,7 @@ static char delim4[] = "\n------------------------------\n\n";
 
 static FILE *(*mhl_action) () = (FILE *(*) ()) 0;
 
+static struct comp *mhlcomp[128];
 
 /*
  * Redefine a couple of functions.
@@ -269,33 +308,28 @@ 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 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
 mhl (int argc, char **argv)
 {
     int length = 0, nomore = 0;
-    int i, width = 0, vecp = 0;
+    unsigned int i, vecp = 0;
+    int width = 0;
     char *cp, *folder = NULL, *form = NULL;
     char buf[BUFSIZ], *files[MAXARGS];
     char **argp, **arguments;
@@ -311,9 +345,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)) {
@@ -326,10 +357,10 @@ mhl (int argc, char **argv)
                case HELPSW: 
                    snprintf (buf, sizeof(buf), "%s [switches] [files ...]", invo_name);
                    print_help (buf, mhlswitches, 1);
-                   done (1);
+                   done (0);
                case VERSIONSW:
                    print_version(invo_name);
-                   done (1);
+                   done (0);
 
                case BELLSW: 
                    bellflg = 1;
@@ -354,13 +385,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]);
@@ -375,6 +399,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 +480,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 +492,8 @@ mhl (int argc, char **argv)
            process (folder, files[i], i + 1, vecp);
     }
 
+    freecomps();
+
     if (forwall) {
        if (digest) {
            printf ("%s", delim4);
@@ -474,7 +511,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 +752,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;
 }
 
@@ -795,8 +846,9 @@ process (char *folder, char *fname, int ofilen, int ofilec)
     char *cp = NULL;
     FILE *fp = NULL;
     struct mcomp *c1;
+    struct stat st;
 
-    switch (m_setjmp (env)) {
+    switch (setjmp (env)) {
        case OK: 
            if (fname) {
                fp = mhl_action ? (*mhl_action) (fname) : fopen (fname, "r");
@@ -809,6 +861,11 @@ 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);
@@ -829,13 +886,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 +960,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 +988,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 +1017,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)
@@ -971,17 +1039,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;
 
@@ -1048,7 +1105,7 @@ 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);
@@ -1061,7 +1118,7 @@ mcomp_format (struct mcomp *c1, struct mcomp *c2)
            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);
 
@@ -1078,15 +1135,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 ();
-               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);
        }
@@ -1101,7 +1149,7 @@ mcomp_format (struct mcomp *c1, struct mcomp *c2)
        if (cptr)
            cptr->c_text = p->pq_error;
 
-       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);
@@ -1170,8 +1218,17 @@ free_queue (struct mcomp **head, struct mcomp **tail)
            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);
+       }
        free ((char *) c1);
     }
 
@@ -1199,21 +1256,11 @@ 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;
     }
 
-    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);
 
@@ -1233,9 +1280,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 +1290,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 +1303,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++)
@@ -1283,18 +1330,18 @@ putcomp (struct mcomp *c1, struct mcomp *c2, int flag)
     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 +1401,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 +1478,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;
     }
 
@@ -1475,206 +1522,6 @@ quitser (int i)
 }
 
 
-static void
-face_format (struct mcomp *c1)
-{
-    char *cp;
-    struct mailname *mp;
-
-    if ((cp = c1->c_text) == NULL)
-       return;
-
-    if ((cp = getname (cp))) {
-       if ((mp = getm (cp, NULL, 0, AD_NAME, NULL))) {
-           char *h, *o;
-           if ((h = mp->m_host) == NULL)
-               h = LocalName ();
-           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)())
 {
@@ -1682,7 +1529,7 @@ mhlsbr (int argc, char **argv, FILE *(*action)())
     char *cp = NULL;
     struct mcomp *c1;
 
-    switch (m_setjmp (mhlenv)) {
+    switch (setjmp (mhlenv)) {
        case OK: 
            cp = invo_name;
            sleepsw = 0;        /* XXX */
@@ -1810,3 +1657,381 @@ 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;
+    char **ap;
+    struct comp *cptr;
+    unsigned int i;
+
+    i = fmt_compile(nfs, &fmt);
+
+    /*
+     * Search through and mark any components that are address components
+     */
+
+    for (ap = addrcomps; *ap; ap++) {
+       FINDCOMP (cptr, *ap);
+       if (cptr)
+           cptr->c_type |= CT_ADDR;
+    }
+
+    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) {
+                   c->c_text = strdup(buf);
+               } 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;
+    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);
+           }
+    }
+}
+
+/*
+ * Just free up the component text.
+ */
+
+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;
+               }
+           }
+    }
+}
+
+/*
+ * 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 - 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.
+            */
+           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]);
+}