* "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) */
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
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 {
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") */
#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);
#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>
/*
* 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 */
{ 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];\
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)
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);
/*
*/
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));
if (next_fp == NULL)
adios (NULL, "unable to allocate format storage");
- ncomp = 0;
infunction = 0;
cp = compile(format_string);
LV(FT_DONE, 0); /* really done */
*fmt = formatvec;
+ free(format_string);
return (ncomp);
}
* 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)
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);
+ }
+}
if (width > WBUFSIZ)
width = WBUFSIZ;
fmt_norm = normalize;
- fmt_compile (nfs, &fmt);
+ fmt_compile (nfs, &fmt, 1);
dat[0] = 0;
dat[1] = 0;
for (addrp = 0; addrs[addrp]; addrp++)
status += process (addrs[addrp], width, normalize);
+ fmt_free (fmt, 1);
done (status);
return 1;
}
}
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;
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;
}
}
if (width > WBUFSIZ)
width = WBUFSIZ;
- fmt_compile (nfs, &fmt);
+ fmt_compile (nfs, &fmt, 1);
dat[0] = 0;
dat[1] = 0;
status += process (dates[datep], width);
context_save (); /* save the context file */
+ fmt_free (fmt, 1);
done (status);
return 1;
}
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);
* 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;
}
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;
}
* 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;
/*
* 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);
* 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;
}
#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 {
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;
};
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 {
static FILE *(*mhl_action) () = (FILE *(*) ()) 0;
-static struct comp *mhlcomp[128];
-
/*
* Redefine a couple of functions.
* These are undefined later in the code.
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
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) {
process (folder, files[i], i + 1, vecp);
}
- freecomps();
-
if (forwall) {
if (digest) {
printf ("%s", delim4);
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;
}
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;
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;
}
}
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;
}
}
+/*
+ * Process one file/message
+ */
+
static void
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:
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);
break;
}
- freecomptext();
}
struct mcomp *c1, *c2, *c3;
char **ip, name[NAMESZ], buf[BUFSIZ];
+ compile_filterargs();
+
if (forwall) {
if (digest)
printf ("%s", ofilen == 1 ? delim3 : delim4);
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;
}
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;
int dat[5];
char *ap, *cp;
char buffer[BUFSIZ], error[BUFSIZ];
- struct comp *cptr;
struct pqpair *p, *q;
struct pqpair pq;
struct mailname *mp;
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;
/* 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;
}
}
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) {
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;
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);
}
SIGNAL_HANDLER istat = NULL, pstat = NULL, qstat = NULL;
char *cp = NULL;
struct mcomp *c1;
+ struct arglist *a, *a2;
switch (setjmp (mhlenv)) {
case OK:
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;
}
}
/*
- * 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;
}
}
* 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;
* 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);
/*
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) {
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[] = {
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")))
/* 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;
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);
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);
}
*/
#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",
{
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);
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);
}
* 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:
* 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);
/*
* 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;
/* 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 */
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");
* 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) {
if (noisy)
fputs (scanl, stdout);
- FINDCOMP (cptr, "encrypted");
+ cptr = fmt_findcomp ("encrypted");
encrypted = cptr && cptr->c_text;
/* return dynamically allocated buffers to pool */