Merge branch 'format-memory-rework'
authorKen Hornstein <kenh@pobox.com>
Mon, 19 Nov 2012 20:31:21 +0000 (15:31 -0500)
committerKen Hornstein <kenh@pobox.com>
Mon, 19 Nov 2012 20:31:21 +0000 (15:31 -0500)
h/fmt_scan.h
sbr/fmt_compile.c
uip/ap.c
uip/comp.c
uip/dp.c
uip/fmtdump.c
uip/forwsbr.c
uip/mhlsbr.c
uip/rcvdist.c
uip/replsbr.c
uip/scansbr.c

index 837780c..8de6f23 100644 (file)
@@ -11,6 +11,9 @@
  * "wantcomp".  All format entries that reference a particular component
  * point to its comp struct (so we only have to do component specific
  * processing once.  e.g., parse an address.).
+ *
+ * In previous implementations "wantcomp" was made available to other
+ * functions, but now it's private and is accessed via functions.
  */
 struct comp {
     char        *c_name;       /* component name (in lower case) */
@@ -22,6 +25,7 @@ struct comp {
        struct tws *c_u_tws;
        struct mailname *c_u_mn;
     } c_un;
+    int          c_refcount;   /* Reference count                */
 };
 
 #define c_tws c_un.c_u_tws
@@ -44,28 +48,6 @@ struct comp {
 extern int fmt_norm;
 
 /*
- * Hash table for deciding if a component is "interesting".
- */
-extern struct comp *wantcomp[128];
-
-/* 
- * Hash function for component name.  The function should be
- * case independent and probably shouldn't involve a routine
- * call.  This function is pretty good but will not work on
- * single character component names.  
- */
-#define        CHASH(nm) (((((nm)[0]) - ((nm)[1])) & 0x1f) + (((nm)[2]) & 0x5f))
-
-/*
- * Find a component in the hash table.
- */
-#define FINDCOMP(comp,name) \
-               for (comp = wantcomp[CHASH(name)]; \
-                    comp && strcmp(comp->c_name,name); \
-                    comp = comp->c_next) \
-               ;
-
-/*
  * This structure defines one formatting instruction.
  */
 struct format {
@@ -78,6 +60,7 @@ struct format {
        char         f_u_char;  /* literal character    */
        int          f_u_value; /* literal value        */
     } f_un;
+    short         f_flags;     /* misc. flags          */
 };
 
 #define f_skip f_width         /* instr to skip (false "if") */
@@ -88,11 +71,189 @@ struct format {
 #define f_value f_un.f_u_value
 
 /*
- * prototypes
+ * f_flags bits
+ */
+
+#define FF_STRALLOC    (1<<0)  /* String has been allocated */
+#define FF_COMPREF     (1<<1)  /* Component reference */
+
+/*
+ * prototypes used by the format engine
+ */
+
+/*
+ * Create a new format string.  Arguments are:
+ *
+ * form                - Name of format file.  Will be searched by etcpath(), see that
+ *               function for details.
+ * format      - The format string to be used if no format file is given
+ * default_fs  - The default format string to be used if neither form nor
+ *               format is given
+ *
+ * This function also takes care of processing \ escapes like \n, \t, etc.
+ *
+ * Returns an allocated format string.
+ */
+
+char *new_fs (char *form, char *format, char *default_fs);
+
+/*
+ * Compile a format string into a set of format instructions.  Arguments are:
+ *
+ * fstring     - The format string (the "source code").
+ * fmt         - Returns an allocated array of "struct fmt" elements.  Each
+ *               struct fmt is one format instruction interpreted by the
+ *               format engine.
+ * reset       - If set to true, the format compiler will reset the
+ *               component hash table.  The component hash table contains
+ *               all of the references to message components refered to in
+ *               the format instructions.  If you have multiple format
+ *               strings that you want to compile and operate on the
+ *               same message, this should be set to false.
+ *
+ * Returns the total number of components referenced by all format instructions
+ * since the last reset of the hash table.
+ */
+
+int fmt_compile (char *fstring, struct format **fmt, int reset);
+
+/*
+ * Interpret a sequence of compiled format instructions.  Arguments are:
+ *
+ * format      - Array of format instructions generated by fmt_compile()
+ * scanl       - Passed-in character array that will contain the output
+ *               of the format instructions.  Is always terminated with
+ *               a newline (\n).
+ * max         - Maximum number of bytes to be written to "scanl" (in other
+ *               words, the buffer size).  Includes the trailing NUL.
+ * width       - Maximum number of displayed characters.  Does not include
+ *               characters marked as nonprinting or (depending on the
+ *               encoding) bytes in a multibyte encoding that exceed the
+ *               character's column width.
+ * dat         - An integer array that contains data used by certain format
+ *               functions.  Currently the following instructions use
+ *               dat[]:
+ *
+ *             dat[0] - %(msg), %(dat)
+ *             dat[1] - %(cur)
+ *             dat[2] - %(size)
+ *             dat[3] - %(width)
+ *             dat[4] - %(unseen)
+ *
+ * The return value is a pointer to the next format instruction to
+ * execute, which is currently always NULL.
+ */
+
+struct format *fmt_scan (struct format *format, char *scanl, size_t max,
+                        int width, int *dat);
+
+/*
+ * Free a format structure and/or component hash table.  Arguments are:
+ *
+ * format      - An array of format structures allocated by fmt_compile,
+ *               or NULL.
+ * reset       - If true, reset and remove all references in the component
+ *               hash table.
+ */
+
+void fmt_free (struct format *fmt, int reset);
+
+/*
+ * Search for a component structure in the component hash table.  Arguments are:
+ *
+ * component   - The name of the component to search for.  By convention
+ *               all component names used in format strings are lower case,
+ *               but for backwards compatibility this search is done in
+ *               a case-SENSITIVE manner.
+ *
+ * This function returns a "struct comp" corresponding to the named component,
+ * or NULL if the component is not found in the hash table.
+ */
+
+struct comp *fmt_findcomp(char *component);
+
+/*
+ * Search for a component structure in the component hash table.
+ *
+ * Identical to fmd_findcomp(), but is case-INSENSITIVE.
+ */
+
+struct comp *fmt_findcasecomp(char *component);
+
+/*
+ * Add a component entry to the component hash table
+ *
+ * component   - The name of the component to add to the hash table.
+ *
+ * If the component is already in the hash table, this function will do
+ * nothing.  Returns 1 if a component was added, 0 if it already existed.
+ */
+
+int fmt_addcompentry(char *component);
+
+/*
+ * Add a string to a component hash table entry.  Arguments are:
+ *
+ * component   - The name of the component to add text to.  The component
+ *               is searched for in a case-INSENSITIVE manner (note that
+ *               this is different than fmt_findcomp()).  If the component
+ *               is not found in the hash table, this function will silently
+ *               return.
+ * text                - The text to add to a component hash table entry.  Note that
+ *                if the last character of the existing component
+ *                text is a newline AND it is marked as an address
+ *                component (the the CT_ADDR flag is set) existing
+ *                component buffer is a newline, it will be separated
+ *                from previous text by ",\n\t"; otherwise if the last
+ *                character of the previous text is a newline it will
+ *                simply be seperated by a "\t".  This unusual processing
+ *               is designed to handle the case where you have multiple
+ *               headers with the same name (e.g.: multiple "cc:" headers,
+ *               even though that isn't technically allowed in the RFCs).
+ *
+ * This function is designed to be called when you start processing a new
+ * component.  The function returns the integer value of the hash table
+ * bucket corresponding to this component.  If there was no entry found
+ * in the component hash table, this function will return -1.
+ */
+
+int fmt_addcomptext(char *component, char *text);
+
+/*
+ * Append to an existing component.  Arguments are:
+ *
+ * bucket      - The hash table bucket corresponding to this component,
+ *               as returned by fmt_addcomp().  If -1, this function will
+ *               return with no actions performed.
+ * component   - The component to append text to.  Like fmt_addcomp, the
+ *               component is searched case-INSENSITIVELY.
+ * text                - The text to append to the component.  No special processing
+ *               is done.
+ *
+ * This function is designed to be called when you are processing continuation
+ * lines on the same header (state == FLDPLUS).
+ */
+
+void fmt_appendcomp(int bucket, char *component, char *text);
+
+/*
+ * The implementation of the %(formataddr) function.  This is available for
+ * programs to provide their own local implementation if they wish to do
+ * special processing (see uip/replsbr.c for an example).  Arguments are:
+ *
+ * orig                - Existing list of addresses
+ * str         - New address(es) to append to list.
+ *
+ * This function returns an allocated string containing the new list of
+ * addresses.
  */
-struct format *fmt_scan (struct format *, char *, size_t, int, int *);
-char *new_fs (char *, char *, char *);
-int fmt_compile (char *, struct format **);
-char *formataddr(char *, char *);
-char *concataddr(char *, char *);
-extern char *format_string;
+
+char *formataddr(char *orig, char *str);
+
+/*
+ * The implementation of the %(concataddr) function.  Arguments and behavior
+ * are the same as %(formataddr).  Again, see uip/replsbr.c to see how you
+ * can override this behavior.
+ */
+
+char *concataddr(char *orig, char *str);
index f4667a8..3a6202d 100644 (file)
@@ -47,6 +47,7 @@
 #include <h/fmt_scan.h>
 #include <h/fmt_compile.h>
 #include <h/mts.h>
+#include <h/utils.h>
 
 #ifdef HAVE_SYS_TIME_H
 # include <sys/time.h>
@@ -56,7 +57,7 @@
 /*
  * hash table for deciding if a component is "interesting"
  */
-struct comp *wantcomp[128];
+static struct comp *wantcomp[128];
 
 static struct format *formatvec;       /* array to hold formats */
 static struct format *next_fp;         /* next free format slot */
@@ -218,10 +219,28 @@ static struct ftable functable[] = {
      { NULL,         0,                0,              0,              0 }
 };
 
+/* 
+ * Hash function for component name.  The function should be
+ * case independent and probably shouldn't involve a routine
+ * call.  This function is pretty good but will not work on
+ * single character component names.  
+ */
+#define        CHASH(nm) (((((nm)[0]) - ((nm)[1])) & 0x1f) + (((nm)[2]) & 0x5f))
+
+/*
+ * Find a component in the hash table.
+ */
+#define FINDCOMP(comp,name) \
+               for (comp = wantcomp[CHASH(name)]; \
+                    comp && strcmp(comp->c_name,name); \
+                    comp = comp->c_next) \
+               ;
+
 /* Add new component to the hash table */
 #define NEWCOMP(cm,name) do { \
        cm = ((struct comp *) calloc(1, sizeof (struct comp)));\
-       cm->c_name = name;\
+       cm->c_name = getcpy(name);\
+       cm->c_refcount++;\
        ncomp++;\
        i = CHASH(name);\
        cm->c_next = wantcomp[i];\
@@ -240,16 +259,18 @@ static struct ftable functable[] = {
            NEWCOMP(cm,name);\
        }\
        fp->f_comp = cm; \
+       fp->f_flags |= FF_COMPREF; \
+       cm->c_refcount++; \
        } while (0)
 
 #define LV(type, value)                do { NEW(type,0,0); fp->f_value = (value); } while (0)
-#define LS(type, str)          do { NEW(type,0,0); fp->f_text = (str); } while (0)
+#define LS(type, str)          do { NEW(type,0,0); fp->f_text = getcpy(str); fp->f_flags |= FF_STRALLOC; } while (0)
 
 #define PUTCOMP(comp)          do { NEW(FT_COMP,0,0); ADDC(comp); } while (0)
-#define PUTLIT(str)            do { NEW(FT_LIT,0,0); fp->f_text = (str); } while (0)
+#define PUTLIT(str)            do { NEW(FT_LIT,0,0); fp->f_text = getcpy(str); fp->f_flags |= FF_STRALLOC; } while (0)
 #define PUTC(c)                        do { NEW(FT_CHAR,0,0); fp->f_char = (c); } while (0)
 
-char *format_string;
+static char *format_string;
 static unsigned char *usr_fstring;     /* for CERROR */
 
 #define CERROR(str) compile_error (str, cp)
@@ -266,6 +287,8 @@ static char *do_func(char *);
 static char *do_expr (char *, int);
 static char *do_loop(char *);
 static char *do_if(char *);
+static void free_component(struct comp *);
+static void free_comptable(void);
 
 
 /*
@@ -318,19 +341,19 @@ compile_error(char *str, char *cp)
  */
 
 int
-fmt_compile(char *fstring, struct format **fmt)
+fmt_compile(char *fstring, struct format **fmt, int reset_comptable)
 {
     register char *cp;
     size_t i;
+    static int comptable_initialized = 0;
 
-    if (format_string)
-       free (format_string);
     format_string = getcpy (fstring);
     usr_fstring = fstring;
 
-    /* init the component hash table. */
-    for (i = 0; i < sizeof(wantcomp)/sizeof(wantcomp[0]); i++)
-       wantcomp[i] = 0;
+    if (reset_comptable || !comptable_initialized) {
+       free_comptable();
+       comptable_initialized = 1;
+    }
 
     memset((char *) &fmt_mnull, 0, sizeof(fmt_mnull));
 
@@ -347,7 +370,6 @@ fmt_compile(char *fstring, struct format **fmt)
     if (next_fp == NULL)
        adios (NULL, "unable to allocate format storage");
 
-    ncomp = 0;
     infunction = 0;
 
     cp = compile(format_string);
@@ -357,6 +379,7 @@ fmt_compile(char *fstring, struct format **fmt)
     LV(FT_DONE, 0);            /* really done */
     *fmt = formatvec;
 
+    free(format_string);
     return (ncomp);
 }
 
@@ -672,6 +695,37 @@ do_expr (char *sp, int preprocess)
  * There is no support for this in the format engine, so right now if
  * you try using it you will reach the FT_DONE and simply stop.  I'm leaving
  * this here in case someone wants to continue the work.
+ *
+ * Okay, got some more information on this from John L. Romine!  From an
+ * email he sent to the nmh-workers mailing list on December 2, 2010, he
+ * explains it thusly:
+ *
+ *    In this case (scan, formatsbr) it has to do with an extension to
+ *    the mh-format syntax to allow for looping.
+ *
+ *    The scan format is processed once for each message.  Those #ifdef
+ *    JLR changes allowed for the top part of the format file to be
+ *    processed once, then a second, looping part to be processed
+ *    once per message.  As I recall, there were new mh-format escape
+ *    sequences to delimit the loop.  This would have allowed for things
+ *    like per-format column headings in the scan output.
+ *
+ *    Since existing format files didn't include the scan listing
+ *    header (it was hard-coded in scan.c) it would not have been
+ *    backward-compatible.  All existing format files (including any
+ *    local ones) would have needed to be changed to include the format
+ *    codes for a header.  The practice at the time was not to introduce
+ *    incompatible changes in a minor release, and I never managed to
+ *    put out a newer major release.
+ *
+ * I can see how this would work, and I suspect part of the motivation was
+ * because the format compiler routines (at the time) couldn't really be
+ * called multiple times on the same message because the memory management
+ * was so lousy.  That's been reworked and things are now a lot cleaner,
+ * so I suspect if we're going to allow a format string to be used for the
+ * scan header it might be simpler to have a separate format string just
+ * for the header.  But I'll leave this code in for now just in case we
+ * decide that we want some kind of looping support.
  */
 static char *
 do_loop(char *sp)
@@ -769,3 +823,198 @@ do_if(char *sp)
 
     return (cp);
 }
+
+/*
+ * Free a set of format instructions.
+ *
+ * What we do here is:
+ *
+ * - Iterate through the list of format instructions, freeing any references
+ *   to allocated memory in each instruction.
+ * - Free component references.
+ * - If requested, reset the component hash table; that will also free any
+ *   references to components stored there.
+ *
+ */
+
+void
+fmt_free(struct format *fmt, int reset_comptable)
+{
+    struct format *fp = fmt;
+
+    if (fp) {
+       while (! (fp->f_type == FT_DONE && fp->f_value == 0)) {
+           if (fp->f_flags & FF_STRALLOC)
+               free(fp->f_text);
+           if (fp->f_flags & FF_COMPREF)
+               free_component(fp->f_comp);
+           fp++;
+       }
+       free(fmt);
+    }
+
+    if (reset_comptable)
+       free_comptable();
+}
+
+/*
+ * Find a component in our hash table.  This is just a public interface to
+ * the FINDCOMP macro, so we don't have to expose our hash table.
+ */
+
+struct comp *
+fmt_findcomp(char *component)
+{
+    struct comp *cm;
+
+    FINDCOMP(cm, component);
+
+    return cm;
+}
+
+/*
+ * Like fmt_findcomp, but case-insensitive.
+ */
+
+struct comp *
+fmt_findcasecomp(char *component)
+{
+    struct comp *cm;
+
+    for (cm = wantcomp[CHASH(component)]; cm; cm = cm->c_next)
+       if (mh_strcasecmp(component, cm->c_name) == 0)
+           break;
+
+    return cm;
+}
+
+/*
+ * Add an entry to the component hash table
+ *
+ * Returns true if the component was added, 0 if it already existed.
+ *
+ */
+
+int
+fmt_addcompentry(char *component)
+{
+    struct comp *cm;
+    int i;
+
+    FINDCOMP(cm, component);
+
+    if (cm)
+       return 0;
+
+    NEWCOMP(cm, component);
+
+    /*
+     * ncomp is really meant for fmt_compile() and this function is
+     * meant to be used outside of it.  So decrement it just to be safe
+     * (internal callers should be using NEWCOMP()).
+     */
+
+    ncomp--;
+
+    return 1;
+}
+
+/*
+ * Add a string to a component hash table entry.
+ *
+ * Note the special handling for components marked with CT_ADDR.  The comments
+ * in fmt_scan.h explain this in more detail.
+ */
+
+int
+fmt_addcomptext(char *component, char *text)
+{
+    int i, found = 0, bucket = CHASH(component);
+    struct comp *cptr = wantcomp[bucket];
+    char *cp;
+
+    while (cptr) {
+       if (mh_strcasecmp(component, cptr->c_name) == 0) {
+           found++;
+           if (! cptr->c_text) {
+               cptr->c_text = getcpy(text);
+           } else {
+               i = strlen(cp = cptr->c_text) - 1;
+               if (cp[i] == '\n') {
+                   if (cptr->c_type & CT_ADDR) {
+                       cp[i] = '\0';
+                       cp = add(",\n\t", cp);
+                   } else {
+                       cp = add("\t", cp);
+                   }
+               }
+               cptr->c_text = add(text, cp);
+           }
+       }
+       cptr = cptr->c_next;
+    }
+
+    return found ? bucket : -1;
+}
+
+/*
+ * Append text to a component we've already found.  See notes in fmt_scan.h
+ * for more information.
+ */
+
+void
+fmt_appendcomp(int bucket, char *component, char *text)
+{
+    struct comp *cptr;
+
+    if (bucket != -1) {
+       for (cptr = wantcomp[bucket]; cptr; cptr = cptr->c_next)
+           if (mh_strcasecmp(component, cptr->c_name) == 0)
+               cptr->c_text = add(text, cptr->c_text);
+    }
+}
+
+/*
+ * Free and reset our component hash table
+ */
+
+static void
+free_comptable(void)
+{
+    int i;
+    struct comp *cm, *cm2;
+
+    for (i = 0; i < sizeof(wantcomp)/sizeof(wantcomp[0]); i++) {
+       cm = wantcomp[i];
+       while (cm != NULL) {
+           cm2 = cm->c_next;
+           free_component(cm);
+           cm = cm2;
+       }
+       wantcomp[i] = 0;
+    }
+
+    ncomp = 0;
+}
+
+/*
+ * Decrement the reference count of a component structure.  If it reaches
+ * zero, free it
+ */
+
+static void
+free_component(struct comp *cm)
+{
+    if (--cm->c_refcount <= 0) {
+       /* Shouldn't ever be NULL, but just in case ... */
+       if (cm->c_name)
+           free(cm->c_name);
+       if (cm->c_text)
+           free(cm->c_text);
+       if (cm->c_type & CT_DATE)
+           free(cm->c_tws);
+       if (cm->c_type & CT_ADDR && cm->c_mn && cm->c_mn != &fmt_mnull)
+           mnfree(cm->c_mn);
+       free(cm);
+    }
+}
index fdb47e2..80ce8a3 100644 (file)
--- a/uip/ap.c
+++ b/uip/ap.c
@@ -133,7 +133,7 @@ main (int argc, char **argv)
     if (width > WBUFSIZ)
        width = WBUFSIZ;
     fmt_norm = normalize;
-    fmt_compile (nfs, &fmt);
+    fmt_compile (nfs, &fmt, 1);
 
     dat[0] = 0;
     dat[1] = 0;
@@ -144,6 +144,7 @@ main (int argc, char **argv)
     for (addrp = 0; addrs[addrp]; addrp++)
        status += process (addrs[addrp], width, normalize);
 
+    fmt_free (fmt, 1);
     done (status);
     return 1;
 }
@@ -183,17 +184,26 @@ process (char *arg, int length, int norm)
     }
 
     for (p = pq.pq_next; p; p = q) {
-       FINDCOMP (cptr, "text");
-       if (cptr)
+       cptr = fmt_findcomp ("text");
+       if (cptr) {
+           if (cptr->c_text)
+               free(cptr->c_text);
            cptr->c_text = p->pq_text;
-       FINDCOMP (cptr, "error");
-       if (cptr)
+           p->pq_text = NULL;
+       }
+       cptr = fmt_findcomp ("error");
+       if (cptr) {
+           if (cptr->c_text)
+               free(cptr->c_text);
            cptr->c_text = p->pq_error;
+           p->pq_error = NULL;
+       }
 
        fmt_scan (fmt, buffer, sizeof buffer - 1, length, dat);
        fputs (buffer, stdout);
 
-       free (p->pq_text);
+       if (p->pq_text)
+           free (p->pq_text);
        if (p->pq_error)
            free (p->pq_error);
        q = p->pq_next;
index f7a8f8f..65b213a 100644 (file)
@@ -294,34 +294,34 @@ main (int argc, char **argv)
 
         cp = new_fs(form, NULL, NULL);
        format_len = strlen(cp);
-       fmt_compile(cp, &fmt);
+       fmt_compile(cp, &fmt, 1);
 
        /*
         * Set up any components that were fed to us on the command line
         */
 
        if (from) {
-           FINDCOMP(cptr, "from");
+           cptr = fmt_findcomp("from");
            if (cptr)
                cptr->c_text = from;
        }
        if (to) {
-           FINDCOMP(cptr, "to");
+           cptr = fmt_findcomp("to");
            if (cptr)
                cptr->c_text = to;
        }
        if (cc) {
-           FINDCOMP(cptr, "cc");
+           cptr = fmt_findcomp("cc");
            if (cptr)
                cptr->c_text = cc;
        }
        if (fcc) {
-           FINDCOMP(cptr, "fcc");
+           cptr = fmt_findcomp("fcc");
            if (cptr)
                cptr->c_text = fcc;
        }
        if (subject) {
-           FINDCOMP(cptr, "subject");
+           cptr = fmt_findcomp("subject");
            if (cptr)
                cptr->c_text = subject;
        }
index 95a7163..b915845 100644 (file)
--- a/uip/dp.c
+++ b/uip/dp.c
@@ -117,7 +117,7 @@ main (int argc, char **argv)
     }
     if (width > WBUFSIZ)
        width = WBUFSIZ;
-    fmt_compile (nfs, &fmt);
+    fmt_compile (nfs, &fmt, 1);
 
     dat[0] = 0;
     dat[1] = 0;
@@ -129,6 +129,7 @@ main (int argc, char **argv)
        status += process (dates[datep], width);
 
     context_save ();   /* save the context file */
+    fmt_free (fmt, 1);
     done (status);
     return 1;
 }
@@ -141,9 +142,12 @@ process (char *date, int length)
     char buffer[WBUFSIZ + 1];
     register struct comp *cptr;
 
-    FINDCOMP (cptr, "text");
-    if (cptr)
-       cptr->c_text = date;
+    cptr = fmt_findcomp ("text");
+    if (cptr) {
+       if (cptr->c_text)
+           free(cptr->c_text);
+       cptr->c_text = getcpy(date);
+    }
     fmt_scan (fmt, buffer, sizeof buffer - 1, length, dat);
     fputs (buffer, stdout);
 
index d69fb8c..2ec0a80 100644 (file)
@@ -100,9 +100,12 @@ main (int argc, char **argv)
      * Get new format string.  Must be before chdir().
      */
     nfs = new_fs (form, format, FORMAT);
-    (void) fmt_compile(nfs, &fmt);
+    (void) fmt_compile(nfs, &fmt, 1);
 
     fmt_dump(fmt);
+
+    fmt_free(fmt, 1);
+
     done(0);
     return 1;
 }
index 9abf20d..355adff 100644 (file)
@@ -67,14 +67,14 @@ build_form (char *form, char *digest, int *dat, char *from, char *to,
     fmtsize = strlen (nfs) + 256;
 
     /* Compile format string */
-    (void) fmt_compile (nfs, &fmt);
+    (void) fmt_compile (nfs, &fmt, 1);
 
     /*
      * Mark any components tagged as address components
      */
 
     for (ap = addrcomps; *ap; ap++) {
-       FINDCOMP (cptr, *ap);
+       cptr = fmt_findcomp (*ap);
        if (cptr)
            cptr->c_type |= CT_ADDR;
     }
@@ -96,35 +96,17 @@ build_form (char *form, char *digest, int *dat, char *from, char *to,
                 * a copy.  We don't do all of that weird buffer switching
                 * that replout does.
                 */
-               if ((cptr = wantcomp[CHASH(name)]))
-                   do {
-                       if (mh_strcasecmp(name, cptr->c_name) == 0) {
-                           char_read += msg_count;
-                           if (! cptr->c_text) {
-                               cptr->c_text = strdup(msgbuf);
-                           } else {
-                               i = strlen(cptr->c_text) - 1;
-                               if (cptr->c_text[i] == '\n') {
-                                   if (cptr->c_type & CT_ADDR) {
-                                       cptr->c_text[i] = '\0';
-                                       cptr->c_text = add(",\n\t",
-                                                              cptr->c_text);
-                                   } else {
-                                       cptr->c_text = add ("\t", cptr->c_text);
-                                   }
-                               }
-                               cptr->c_text = add(msgbuf, cptr->c_text);
-                           }
-                           while (state == FLDPLUS) {
-                               state = m_getfld(state, name, msgbuf,
-                                                sizeof(msgbuf), tmp);
-                               cptr->c_text = add(msgbuf, cptr->c_text);
-                               char_read += msg_count;
-                           }
-                           break;
-                       }
-                   } while ((cptr = cptr->c_next));
 
+               i = fmt_addcomptext(name, msgbuf);
+               if (i != -1) {
+                   char_read += msg_count;
+                   while (state == FLDPLUS) {
+                       state = m_getfld(state, name, msgbuf,
+                                        sizeof(msgbuf), tmp);
+                       fmt_appendcomp(i, name, msgbuf);
+                       char_read += msg_count;
+                   }
+               }
                while (state == FLDPLUS)
                    state = m_getfld(state, name, msgbuf, sizeof(msgbuf), tmp);
                break;
@@ -143,44 +125,49 @@ build_form (char *form, char *digest, int *dat, char *from, char *to,
     /*
      * Override any components just in case they were included in the
      * input message.  Also include command-line components given here
+     *
+     * With the memory rework I've changed things so we always get copies
+     * of these strings; I don't like the idea that the caller of this
+     * function has to know to pass in already-allocated memory (and that
+     * it will be free()'d by us).
      */
 
 finished:
 
-    FINDCOMP (cptr, "digest");
+    cptr = fmt_findcomp ("digest");
     if (cptr) {
        COMPFREE(cptr);
-       cptr->c_text = digest;
+       cptr->c_text = getcpy(digest);
     }
-    FINDCOMP (cptr, "nmh-date");
+    cptr = fmt_findcomp ("nmh-date");
     if (cptr) {
        COMPFREE(cptr);
        cptr->c_text = getcpy(dtimenow (0));
     }
-    FINDCOMP (cptr, "nmh-from");
+    cptr = fmt_findcomp ("nmh-from");
     if (cptr) {
        COMPFREE(cptr);
-       cptr->c_text = from;
+       cptr->c_text = getcpy(from);
     }
-    FINDCOMP (cptr, "nmh-to");
+    cptr = fmt_findcomp ("nmh-to");
     if (cptr) {
        COMPFREE(cptr);
-       cptr->c_text = to;
+       cptr->c_text = getcpy(to);
     }
-    FINDCOMP (cptr, "nmh-cc");
+    cptr = fmt_findcomp ("nmh-cc");
     if (cptr) {
        COMPFREE(cptr);
-       cptr->c_text = cc;
+       cptr->c_text = getcpy(cc);
     }
-    FINDCOMP (cptr, "nmh-subject");
+    cptr = fmt_findcomp ("nmh-subject");
     if (cptr) {
        COMPFREE(cptr);
-       cptr->c_text = subject;
+       cptr->c_text = getcpy(subject);
     }
-    FINDCOMP (cptr, "fcc");
+    cptr = fmt_findcomp ("fcc");
     if (cptr) {
        COMPFREE(cptr);
-       cptr->c_text = fcc;
+       cptr->c_text = getcpy(fcc);
     }
 
     cp = m_mktemp2(NULL, invo_name, NULL, &tmp);
@@ -203,9 +190,7 @@ finished:
      * Free any component buffers that we allocated
      */
 
-    for (i = 0; i < (sizeof(wantcomp) / sizeof(struct comp)); i++)
-       for (cptr = wantcomp[i]; cptr != NULL; cptr = cptr->c_next)
-           COMPFREE(cptr);
+    fmt_free(fmt, 1);
 
     return in;
 }
index 9964fa0..45d99b0 100644 (file)
@@ -116,7 +116,8 @@ static struct swit mhlswitches[] = {
 #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 {
@@ -125,21 +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;      /*   ..                    */
+    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;
 };
 
@@ -149,13 +190,11 @@ static struct mcomp *fmthd = NULL;
 static struct mcomp *fmttl = NULL;
 
 static struct mcomp global = {
-    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, 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 {
@@ -282,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.
@@ -317,11 +354,8 @@ 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
@@ -480,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) {
@@ -492,8 +523,6 @@ mhl (int argc, char **argv)
            process (folder, files[i], i + 1, vecp);
     }
 
-    freecomps();
-
     if (forwall) {
        if (digest) {
            printf ("%s", delim4);
@@ -573,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;
@@ -632,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;
@@ -713,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;
     }
@@ -753,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;
 }
@@ -840,6 +884,10 @@ parse (void)
 }
 
 
+/*
+ * Process one file/message
+ */
+
 static void
 process (char *folder, char *fname, int ofilen, int ofilec)
 {
@@ -847,6 +895,7 @@ process (char *folder, char *fname, int ofilen, int ofilec)
     FILE *fp = NULL;
     struct mcomp *c1;
     struct stat st;
+    struct arglist *ap;
 
     switch (setjmp (env)) {
        case OK: 
@@ -871,6 +920,14 @@ process (char *folder, char *fname, int ofilen, int ofilec)
                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);
@@ -887,7 +944,6 @@ process (char *folder, char *fname, int ofilen, int ofilec)
            break;
     }
 
-    freecomptext();
 }
 
 
@@ -898,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);
@@ -960,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_addcomptext(name, buf);
                for (ip = ignores; *ip; ip++)
                    if (!mh_strcasecmp (name, *ip)) {
                        while (state == FLDPLUS) {
                            state = m_getfld (state, name, buf, sizeof(buf), fp);
-                           addcomp(bucket, name, buf);
+                           fmt_appendcomp(bucket, name, buf);
                        }
                        break;
                    }
@@ -988,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;
@@ -1096,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;
@@ -1108,12 +1165,10 @@ mcomp_format (struct mcomp *c1, struct mcomp *c2)
     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 = ap;
        if ((cp = strrchr(ap, '\n')))   /* drop ending newline */
            if (!cp[1])
                *cp = 0;
@@ -1122,7 +1177,7 @@ mcomp_format (struct mcomp *c1, struct mcomp *c2)
        /* Don't need to append a newline, dctime() already did */
        c2->c_text = getcpy (buffer);
 
-       free (ap);
+       /* ap is now owned by the component struct, so do NOT free it here */
        return;
     }
 
@@ -1142,12 +1197,14 @@ 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, sizeof buffer - 1, dat);
        if (*buffer) {
@@ -1158,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;
@@ -1217,18 +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_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);
     }
 
@@ -1528,6 +1575,7 @@ 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 (setjmp (mhlenv)) {
        case OK: 
@@ -1564,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;
     }
 }
@@ -1660,179 +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;
-    char **ap;
-    struct comp *cptr;
-    unsigned int i;
-
-    i = fmt_compile(nfs, &fmt);
+    fmt_compile(c1->c_nfs, &c1->c_fmt, 1);
 
     /*
-     * 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
+     * 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;
-}
-
-/*
- * 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;
+    c1->c_c_text = fmt_findcomp("text");
+    c1->c_c_error = fmt_findcomp("error");
 }
 
 /*
- * 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;
     }
 }
 
@@ -1930,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;
@@ -1943,7 +1886,7 @@ 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 - 1, BUFSIZ, dat);
            /*
@@ -1953,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) {
index 4061735..d3f36ca 100644 (file)
@@ -146,10 +146,6 @@ static int outputlinelen = OUTPUTLINELEN;
 
 static struct format *fmt;
 
-static int ncomps = 0;
-static char **compbuffers = 0;
-static struct comp **used_buf = 0;
-
 static int dat[5];
 
 static char *addrcomps[] = {
@@ -173,9 +169,9 @@ static void
 rcvdistout (FILE *inb, char *form, char *addrs)
 {
     register int char_read = 0, format_len, i, state;
-    register char *tmpbuf, **nxtbuf, **ap;
-    char *cp, *scanl, name[NAMESZ];
-    register struct comp *cptr, **savecomp;
+    register char **ap;
+    char *cp, *scanl, name[NAMESZ], tmpbuf[SBUFSIZ];
+    register struct comp *cptr;
     FILE *out;
 
     if (!(out = fopen (drft, "w")))
@@ -184,26 +180,15 @@ rcvdistout (FILE *inb, char *form, char *addrs)
     /* get new format string */
     cp = new_fs (form ? form : rcvdistcomps, NULL, NULL);
     format_len = strlen (cp);
-    ncomps = fmt_compile (cp, &fmt) + 1;
-    if (!(nxtbuf = compbuffers = (char **) calloc ((size_t) ncomps, sizeof(char *))))
-       adios (NULL, "unable to allocate component buffers");
-    if (!(savecomp = used_buf = (struct comp **) calloc ((size_t) (ncomps + 1), sizeof(struct comp *))))
-       adios (NULL, "unable to allocate component buffer stack");
-    savecomp += ncomps + 1;
-    *--savecomp = 0;
-
-    for (i = ncomps; i--;)
-       *nxtbuf++ = mh_xmalloc (SBUFSIZ);
-    nxtbuf = compbuffers;
-    tmpbuf = *nxtbuf++;
+    fmt_compile (cp, &fmt, 1);
 
     for (ap = addrcomps; *ap; ap++) {
-       FINDCOMP (cptr, *ap);
+       cptr = fmt_findcomp (*ap);
        if (cptr)
            cptr->c_type |= CT_ADDR;
     }
 
-    FINDCOMP (cptr, "addresses");
+    cptr = fmt_findcomp ("addresses");
     if (cptr)
        cptr->c_text = addrs;
 
@@ -211,36 +196,15 @@ rcvdistout (FILE *inb, char *form, char *addrs)
        switch (state = m_getfld (state, name, tmpbuf, SBUFSIZ, inb)) {
            case FLD: 
            case FLDPLUS: 
-               if ((cptr = wantcomp[CHASH (name)]))
-                   do {
-                       if (!mh_strcasecmp (name, cptr->c_name)) {
-                           char_read += msg_count;
-                           if (!cptr->c_text) {
-                               cptr->c_text = tmpbuf;
-                               *--savecomp = cptr;
-                               tmpbuf = *nxtbuf++;
-                           }
-                           else {
-                               i = strlen (cp = cptr->c_text) - 1;
-                               if (cp[i] == '\n') {
-                                   if (cptr->c_type & CT_ADDR) {
-                                       cp[i] = 0;
-                                       cp = add (",\n\t", cp);
-                                   }
-                                   else
-                                       cp = add ("\t", cp);
-                               }
-                               cptr->c_text = add (tmpbuf, cp);
-                           }
-                           while (state == FLDPLUS) {
-                               state = m_getfld (state, name, tmpbuf,
-                                                 SBUFSIZ, inb);
-                               cptr->c_text = add (tmpbuf, cptr->c_text);
-                               char_read += msg_count;
-                           }
-                           break;
-                       }
-                   } while ((cptr = cptr->c_next));
+               i = fmt_addcomptext(name, tmpbuf);
+               if (i != -1) {
+                   char_read += msg_count;
+                   while (state == FLDPLUS) {
+                       state = m_getfld (state, name, tmpbuf, SBUFSIZ, inb);
+                       fmt_appendcomp(i, name, tmpbuf);
+                       char_read += msg_count;
+                   }
+               }
 
                while (state == FLDPLUS)
                    state = m_getfld (state, name, tmpbuf, SBUFSIZ, inb);
@@ -270,12 +234,7 @@ finished: ;
     fclose (out);
 
     free (scanl);
-    for (nxtbuf = compbuffers, i = ncomps; (cptr = *savecomp++); nxtbuf++, i--)
-       free (cptr->c_text);
-    while (i-- > 0)
-        free (*nxtbuf++);
-    free ((char *) compbuffers);
-    free ((char *) used_buf);
+    fmt_free(fmt, 1);
 }
 
 
index f6ccd35..3dbc303 100644 (file)
@@ -36,14 +36,6 @@ static int nodupcheck = 0;           /* If set, no check for duplicates */
  */
 #define SBUFSIZ 256            
 
-static struct format *fmt;
-
-static int ncomps = 0;                 /* # of interesting components */
-static char **compbuffers = NULL;      /* buffers for component text */
-static struct comp **used_buf = NULL;  /* stack for comp that use buffers */
-
-static int dat[5];                     /* aux. data for format routine */
-
 static char *addrcomps[] = {
     "from",
     "sender",
@@ -73,13 +65,14 @@ replout (FILE *inb, char *msg, char *drft, struct msgs *mp, int outputlinelen,
 {
     register int state, i;
     register struct comp *cptr;
-    register char *tmpbuf;
-    register char **nxtbuf;
+    char tmpbuf[SBUFSIZ];
+    struct format *fmt;
     register char **ap;
-    register struct comp **savecomp;
     int        char_read = 0, format_len, mask;
     char name[NAMESZ], *scanl;
     unsigned char *cp;
+    static int dat[5];                 /* aux. data for format routine */
+
     FILE *out;
     NMH_UNUSED (msg);
 
@@ -94,50 +87,41 @@ replout (FILE *inb, char *msg, char *drft, struct msgs *mp, int outputlinelen,
     format_len = strlen (cp);
 
     /* compile format string */
-    ncomps = fmt_compile (cp, &fmt) + 1;
-
-    if (!(nxtbuf = compbuffers = (char **)
-           calloc((size_t) ncomps, sizeof(char *))))
-       adios (NULL, "unable to allocate component buffers");
-    if (!(savecomp = used_buf = (struct comp **)
-           calloc((size_t) (ncomps+1), sizeof(struct comp *))))
-       adios (NULL, "unable to allocate component buffer stack");
-    savecomp += ncomps + 1;
-    *--savecomp = NULL;                /* point at zero'd end minus 1 */
-
-    for (i = ncomps; i--; )
-       *nxtbuf++ = mh_xmalloc(SBUFSIZ);
-
-    nxtbuf = compbuffers;              /* point at start */
-    tmpbuf = *nxtbuf++;
+    fmt_compile (cp, &fmt, 1);
 
     for (ap = addrcomps; *ap; ap++) {
-       FINDCOMP (cptr, *ap);
+       cptr = fmt_findcomp (*ap);
        if (cptr)
            cptr->c_type |= CT_ADDR;
     }
 
     /*
      * ignore any components killed by command line switches
+     *
+     * This prevents the component from being found via fmt_findcomp(),
+     * which makes sure no text gets added to it when the message is processed.
+     *
+     * getcpy(NULL) returns a malloc'd zero-length string, so it can safely
+     * be free()'d later.
      */
     if (!ccto) {
-       FINDCOMP (cptr, "to");
+       cptr = fmt_findcomp ("to");
        if (cptr)
-           cptr->c_name = "";
+           cptr->c_name = getcpy(NULL);
     }
     if (!cccc) {
-       FINDCOMP (cptr, "cc");
+        cptr = fmt_findcomp("cc");
        if (cptr)
-           cptr->c_name = "";
+           cptr->c_name = getcpy(NULL);
     }
     /* set up the "fcc" pseudo-component */
     if (fcc) {
-       FINDCOMP (cptr, "fcc");
+       cptr = fmt_findcomp ("fcc");
        if (cptr)
            cptr->c_text = getcpy (fcc);
     }
     if ((cp = getenv("USER"))) {
-       FINDCOMP (cptr, "user");
+       cptr = fmt_findcomp ("user");
        if (cptr)
            cptr->c_text = getcpy(cp);
     }
@@ -148,7 +132,7 @@ replout (FILE *inb, char *msg, char *drft, struct msgs *mp, int outputlinelen,
      * pick any interesting stuff out of msg "inb"
      */
     for (state = FLD;;) {
-       state = m_getfld (state, name, tmpbuf, SBUFSIZ, inb);
+       state = m_getfld (state, name, tmpbuf, sizeof(tmpbuf), inb);
        switch (state) {
            case FLD: 
            case FLDPLUS: 
@@ -158,35 +142,17 @@ replout (FILE *inb, char *msg, char *drft, struct msgs *mp, int outputlinelen,
                 * buffer as the component temp buffer (buffer switching
                 * saves an extra copy of the component text).
                 */
-               if ((cptr = wantcomp[CHASH(name)]))
-                   do {
-                       if (!mh_strcasecmp(name, cptr->c_name)) {
-                           char_read += msg_count;
-                           if (! cptr->c_text) {
-                               cptr->c_text = tmpbuf;
-                               *--savecomp = cptr;
-                               tmpbuf = *nxtbuf++;
-                           } else {
-                               i = strlen (cp = cptr->c_text) - 1;
-                               if (cp[i] == '\n') {
-                                   if (cptr->c_type & CT_ADDR) {
-                                       cp[i] = '\0';
-                                       cp = add (",\n\t", cp);
-                                   } else {
-                                       cp = add ("\t", cp);
-                                   }
-                               }
-                               cptr->c_text = add (tmpbuf, cp);
-                           }
-                           while (state == FLDPLUS) {
-                               state = m_getfld (state, name, tmpbuf,
-                                                 SBUFSIZ, inb);
-                               cptr->c_text = add (tmpbuf, cptr->c_text);
-                               char_read += msg_count;
-                           }
-                           break;
-                       }
-                   } while ((cptr = cptr->c_next));
+
+               i = fmt_addcomptext(name, tmpbuf);
+               if (i != -1) {
+                   char_read += msg_count;
+                   while (state == FLDPLUS) {
+                       state = m_getfld(state, name, tmpbuf,
+                                        sizeof(tmpbuf), inb);
+                       fmt_appendcomp(i, name, tmpbuf);
+                       char_read += msg_count;
+                   }
+               }
 
                while (state == FLDPLUS)
                    state = m_getfld (state, name, tmpbuf, SBUFSIZ, inb);
@@ -211,7 +177,7 @@ finished:
     /*
      * if there's a "Subject" component, strip any "Re:"s off it
      */
-    FINDCOMP (cptr, "subject")
+    cptr = fmt_findcomp ("subject");
     if (cptr && (cp = cptr->c_text)) {
        register char *sp = cp;
 
@@ -266,12 +232,7 @@ finished:
 
     /* return dynamically allocated buffers */
     free (scanl);
-    for (nxtbuf = compbuffers, i = ncomps; (cptr = *savecomp++); nxtbuf++, i--)
-       free (cptr->c_text);    /* if not nxtbuf, nxtbuf already freed */
-    while ( i-- > 0)
-        free (*nxtbuf++);      /* free unused nxtbufs */
-    free ((char *) compbuffers);
-    free ((char *) used_buf);
+    fmt_free(fmt, 1);
 }
 
 static char *buf;              /* our current working buffer */
index 480cab5..d186845 100644 (file)
@@ -93,24 +93,52 @@ scan (FILE *inb, int innum, int outnum, char *nfs, int width, int curflg,
            umask(~m_gmprot());
 
        /* Compile format string */
-       ncomps = fmt_compile (nfs, &fmt) + 1;
+       ncomps = fmt_compile (nfs, &fmt, 1) + 1;
 
-       FINDCOMP(bodycomp, "body");
-       FINDCOMP(datecomp, "date");
-       FINDCOMP(cptr, "folder");
+       bodycomp = fmt_findcomp("body");
+       datecomp = fmt_findcomp("date");
+       cptr = fmt_findcomp("folder");
        if (cptr && folder)
-           cptr->c_text = folder;
-       FINDCOMP(cptr, "encrypted");
-       if (!cptr)
-           if ((cptr = (struct comp *) calloc (1, sizeof(*cptr)))) {
-               cptr->c_name = "encrypted";
-               cptr->c_next = wantcomp[i = CHASH (cptr->c_name)];
-               wantcomp[i] = cptr;
+           cptr->c_text = getcpy(folder);
+       if (fmt_addcompentry("encrypted")) {
                ncomps++;
        }
-       FINDCOMP (cptr, "dtimenow");
+       cptr =  fmt_findcomp("dtimenow");
        if (cptr)
            cptr->c_text = getcpy(dtimenow (0));
+
+       /*
+        * In other programs I got rid of this complicated buffer switching,
+        * but since scan reads lots of messages at once and this complicated
+        * memory management, I decided to keep it; otherwise there was
+        * the potential for a lot of malloc() and free()s, and I could
+        * see the malloc() pool really getting fragmented.  Maybe it
+        * wouldn't be an issue in practice; perhaps this will get
+        * revisited someday.
+        *
+        * So, some notes for what's going on:
+        *
+        * nxtbuf is an array of pointers that contains malloc()'d buffers
+        * to hold our component text.  used_buf is an array of struct comp
+        * pointers that holds pointers to component structures we found while
+        * processing a message.
+        *
+        * We read in the message with m_getfld(), using "tmpbuf" as our
+        * input buffer.  tmpbuf is set at the start of message processing
+        * to the first buffer in our buffer pool (nxtbuf).
+        *
+        * Every time we find a component we care about, we set that component's
+        * text buffer to the current value of tmpbuf, and then switch tmpbuf
+        * to the next buffer in our pool.  We also add that component to
+        * our used_buf pool.
+        *
+        * When we're done, we go back and zero out all of the component
+        * text buffer pointers that we saved in used_buf.
+        *
+        * Note that this means c_text memory is NOT owned by the fmt_module
+        * and it's our responsibility to free it.
+        */
+
        nxtbuf = compbuffers = (char **) calloc((size_t) ncomps, sizeof(char *));
        if (nxtbuf == NULL)
            adios (NULL, "unable to allocate component buffers");
@@ -176,23 +204,18 @@ scan (FILE *inb, int innum, int outnum, char *nfs, int width, int curflg,
                 * buffer as the component temp buffer (buffer switching
                 * saves an extra copy of the component text).
                 */
-               if ((cptr = wantcomp[CHASH(name)])) {
-                   do {
-                       if (!mh_strcasecmp(name, cptr->c_name)) {
-                           if (! cptr->c_text) {
-                               cptr->c_text = tmpbuf;
-                               for (cp = tmpbuf + strlen (tmpbuf) - 1; 
+               if ((cptr = fmt_findcasecomp(name))) {
+                   if (! cptr->c_text) {
+                       cptr->c_text = tmpbuf;
+                       for (cp = tmpbuf + strlen (tmpbuf) - 1; 
                                        cp >= tmpbuf; cp--)
-                                   if (isspace (*cp))
-                                       *cp = 0;
-                                   else
-                                       break;
-                               *--savecomp = cptr;
-                               tmpbuf = *nxtbuf++;
-                           }
-                           break;
-                       }
-                   } while ((cptr = cptr->c_next));
+                           if (isspace (*cp))
+                               *cp = 0;
+                           else
+                               break;
+                       *--savecomp = cptr;
+                       tmpbuf = *nxtbuf++;
+                   }
                }
 
                while (state == FLDPLUS) {
@@ -335,7 +358,7 @@ finished:
     if (noisy)
        fputs (scanl, stdout);
 
-    FINDCOMP (cptr, "encrypted");
+    cptr = fmt_findcomp ("encrypted");
     encrypted = cptr && cptr->c_text;
 
     /* return dynamically allocated buffers to pool */