X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/c3ed95b3c5530b84a73dbdd531cb9aca9ba8dea0..6fd1d18f:/sbr/fmt_compile.c?ds=sidebyside diff --git a/sbr/fmt_compile.c b/sbr/fmt_compile.c index f643421a..2ad46cf5 100644 --- a/sbr/fmt_compile.c +++ b/sbr/fmt_compile.c @@ -1,6 +1,4 @@ - -/* - * fmt_compile.c -- "compile" format strings for fmt_scan +/* fmt_compile.c -- "compile" format strings for fmt_scan * * This code is Copyright (c) 2002, by the authors of nmh. See the * COPYRIGHT file in the root directory of the nmh distribution for @@ -16,7 +14,7 @@ * There is a NOT a one-to-one correspondence between format strings and * format instructions; some functions have side effects that can result * in multiple instructions being generated. The exact list of instructions - * generated by a format string can be seem with the nmh fmtdump utility. + * generated by a format string can be seem with the nmh fmttest utility. * * A list of format instructions can be found in fmt_compile.h. * @@ -35,7 +33,9 @@ * * - Add the code in fmt_scan.c to handle your new function. * - * - Document the new function in the mh-format(5) man page. + * - Add code to fmttest.c to display your new function. + * + * - Document the new function in mh-format(5). * */ @@ -44,6 +44,9 @@ #include #include #include +#include +#include +#include "terminal.h" #ifdef HAVE_SYS_TIME_H # include @@ -53,7 +56,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 */ @@ -75,6 +78,16 @@ extern struct mailname fmt_mnull; #define TF_NOW 6 /* special - get current unix time */ #define TF_EXPR_SV 7 /* like expr but save current str reg */ #define TF_NOP 8 /* like expr but no result */ +#define TF_MYNAME 9 /* special - get current name of user */ +#define TF_MYHOST 10 /* special - get "local" hostname */ +#define TF_LMBOX 11 /* special - get full local mailbox */ +#define TF_BOLD 12 /* special - enter terminal bold mode */ +#define TF_UNDERLN 13 /* special - enter underline mode */ +#define TF_STNDOUT 14 /* special - enter underline mode */ +#define TF_RESET 15 /* special - reset terminal modes */ +#define TF_HASCLR 16 /* special - terminal have color? */ +#define TF_FGCOLR 17 /* special - foreground term color */ +#define TF_BGCOLR 18 /* special - background term color */ /* ftable->flags */ /* NB that TFL_PUTS is also used to decide whether the test @@ -93,7 +106,9 @@ extern struct mailname fmt_mnull; * what maps a particular function name into a format instruction. * type - The type of argument this function expects. Those types are * listed above (with the TF_ prefix). This affects what gets - * placed in the format instruction (the f_un union). + * placed in the format instruction (the f_un union). Also, + * instructions that require special handling are distinguished + * here (TF_MYMBOX is one example). * f_type - The instruction corresponding to this function (from the list * in fmt_compile.h). * extra - Used by some functions to provide extra data to the compiler. @@ -132,7 +147,8 @@ static struct ftable functable[] = { { "putnum", TF_EXPR, FT_NUM, 0, 0 }, { "putnumf", TF_EXPR, FT_NUMF, 0, 0 }, { "putaddr", TF_STR, FT_PUTADDR, 0, 0 }, - { "putlit", TF_STR, FT_STRLIT, 0, 0 }, + { "putlit", TF_EXPR, FT_STRLIT, 0, 0 }, + { "zputlit", TF_EXPR, FT_STRLITZ, 0, 0 }, { "void", TF_NOP, 0, 0, 0 }, { "comp", TF_COMP, FT_LS_COMP, 0, TFL_PUTS }, @@ -142,6 +158,8 @@ static struct ftable functable[] = { { "decodecomp", TF_COMP, FT_LS_DECODECOMP, 0, TFL_PUTS }, { "decode", TF_EXPR, FT_LS_DECODE, 0, TFL_PUTS }, { "trim", TF_EXPR, FT_LS_TRIM, 0, 0 }, + { "kilo", TF_EXPR, FT_LS_KILO, 0, TFL_PUTS }, + { "kibi", TF_EXPR, FT_LS_KIBI, 0, TFL_PUTS }, { "compval", TF_COMP, FT_LV_COMP, 0, TFL_PUTN }, { "compflag", TF_COMP, FT_LV_COMPFLAG, 0, TFL_PUTN }, { "num", TF_NUM, FT_LV_LIT, 0, TFL_PUTN }, @@ -153,8 +171,12 @@ static struct ftable functable[] = { { "dat", TF_NUM, FT_LV_DAT, 0, TFL_PUTN }, { "strlen", TF_NONE, FT_LV_STRLEN, 0, TFL_PUTN }, { "me", TF_MYBOX, FT_LS_LIT, 0, TFL_PUTS }, + { "myname", TF_MYNAME, FT_LS_LIT, 0, TFL_PUTS }, + { "myhost", TF_MYHOST, FT_LS_LIT, 0, TFL_PUTS }, + { "localmbox", TF_LMBOX, FT_LS_LIT, 0, TFL_PUTS }, { "plus", TF_NUM, FT_LV_PLUS_L, 0, TFL_PUTN }, { "minus", TF_NUM, FT_LV_MINUS_L, 0, TFL_PUTN }, + { "multiply", TF_NUM, FT_LV_MULTIPLY_L, 0, TFL_PUTN }, { "divide", TF_NUM, FT_LV_DIVIDE_L, 0, TFL_PUTN }, { "modulo", TF_NUM, FT_LV_MODULO_L, 0, TFL_PUTN }, { "charleft", TF_NONE, FT_LV_CHAR_LEFT, 0, TFL_PUTN }, @@ -201,26 +223,83 @@ static struct ftable functable[] = { { "friendly", TF_COMP, FT_LS_FRIENDLY, FT_PARSEADDR, TFL_PUTS }, { "mymbox", TF_COMP, FT_LV_COMPFLAG, FT_MYMBOX, TFL_PUTN }, - { "addtoseq", TF_STR, FT_ADDTOSEQ, 0, 0 }, + { "getmymbox", TF_COMP, FT_STR, FT_GETMYMBOX, 0 }, + { "getmyaddr", TF_COMP, FT_LS_ADDR, FT_GETMYADDR, TFL_PUTS }, - { "unquote", TF_EXPR, FT_LS_UNQUOTE, 0, TFL_PUTS}, + { "unquote", TF_EXPR, FT_LS_UNQUOTE, 0, TFL_PUTS }, + + { "bold", TF_BOLD, FT_LS_LIT, 0, TFL_PUTS }, + { "underline", TF_UNDERLN,FT_LS_LIT, 0, TFL_PUTS }, + { "standout", TF_STNDOUT,FT_LS_LIT, 0, TFL_PUTS }, + { "resetterm", TF_RESET, FT_LS_LIT, 0, TFL_PUTS }, + { "hascolor", TF_HASCLR, FT_LV_LIT, 0, 0 }, + { "fgcolor", TF_FGCOLR, FT_LS_LIT, 0, TFL_PUTS }, + { "bgcolor", TF_BGCOLR, FT_LS_LIT, 0, TFL_PUTS }, { NULL, 0, 0, 0, 0 } }; +/* + * A mapping of color names to terminfo color numbers. + * + * There are two sets of terminal-setting codes: 'setaf/setab' (ANSI) and + * 'setf/setb'. Different terminals support different capabilities, so + * we provide a mapping for both. I'm not crazy about putting numbers + * directly in here, but it seems these are well defined by terminfo + * so it should be okay. + */ + +struct colormap { + char *colorname; /* Name of color */ + int ansinum; /* The ANSI escape color number */ + int nonansinum; /* The non-ANSI escape color number */ +}; + +static struct colormap colortable[] = { + { "black", 0, 0 }, + { "red", 1, 4 }, + { "green", 2, 2 }, + { "yellow", 3, 6 }, + { "blue", 4, 1 }, + { "magenta", 5, 5 }, + { "cyan", 6, 3 }, + { "white", 7, 7 }, + { NULL, 0, 0 } +}; + +/* Hash function for component name. Deliberately avoids a function + * call. Is case independent. Covers interval [0, 126] so never uses + * the last element of wantcomp[]. This function is "pretty good". */ +#define CHASH(nm) ( \ + (( \ + ((nm)[0]) - ((nm)[0] ? ((nm)[1]) : 0) \ + ) & 0x1f) + \ + ((nm[1]) ? (((nm)[2]) & 0x5f) : 0) \ + ) + +/* + * Find a component in the hash table. + */ +#define FINDCOMP(comp,name) do { \ + for (comp = wantcomp[CHASH(name)]; \ + comp && strcmp(comp->c_name,name); \ + comp = comp->c_next) \ + ; \ + } while (0) + /* Add new component to the hash table */ #define NEWCOMP(cm,name) do { \ - cm = ((struct comp *) calloc(1, sizeof (struct comp)));\ - cm->c_name = name;\ + NEW0(cm);\ + cm->c_name = mh_xstrdup(name);\ + cm->c_refcount++;\ ncomp++;\ i = CHASH(name);\ cm->c_next = wantcomp[i];\ wantcomp[i] = cm; \ } while (0) -#define NEWFMT (next_fp++) -#define NEW(type,fill,wid) do {\ - fp=NEWFMT; fp->f_type=(type); fp->f_fill=(fill); fp->f_width=(wid); \ +#define NEW_FP(type,fill,wid) do {\ + fp=next_fp++; fp->f_type=(type); fp->f_fill=(fill); fp->f_width=(wid); \ } while (0) /* Add (possibly new) component to the hash table */ @@ -230,25 +309,22 @@ 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 LV(type, value) do { NEW_FP(type,0,0); fp->f_value = (value); } while (0) +#define LS(type, str) do { NEW_FP(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 PUTC(c) do { NEW(FT_CHAR,0,0); fp->f_char = (c); } while (0) +#define PUTCOMP(comp) do { NEW_FP(FT_COMP,0,0); ADDC(comp); } while (0) +#define PUTLIT(str) do { NEW_FP(FT_LIT,0,0); fp->f_text = getcpy(str); fp->f_flags |= FF_STRALLOC; } while (0) +#define PUTC(c) do { NEW_FP(FT_CHAR,0,0); fp->f_char = (c); } while (0) static char *format_string; -static unsigned char *usr_fstring; /* for CERROR */ +static char *usr_fstring; /* for CERROR */ #define CERROR(str) compile_error (str, cp) -/* - * external prototypes - */ -extern char *getusername(void); - /* * static prototypes */ @@ -261,7 +337,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); /* * Lookup a function name in the functable @@ -269,17 +346,17 @@ static char *do_if(char *); static struct ftable * lookup(char *name) { - register struct ftable *t = functable; - register char *nm; - register char c = *name; + struct ftable *t = functable; + char *nm; + char c = *name; while ((nm = t->name)) { if (*nm == c && strcmp (nm, name) == 0) - return (ftbl = t); + return ftbl = t; t++; } - return (struct ftable *) 0; + return NULL; } @@ -289,19 +366,15 @@ compile_error(char *str, char *cp) int i, errpos, errctx; errpos = cp - format_string; - errctx = errpos > 20 ? 20 : errpos; + errctx = min(errpos, 20); usr_fstring[errpos] = '\0'; for (i = errpos-errctx; i < errpos; i++) { -#ifdef LOCALE - if (iscntrl(usr_fstring[i])) -#else - if (usr_fstring[i] < 32) -#endif + if (iscntrl((unsigned char) usr_fstring[i])) usr_fstring[i] = '_'; } - advise(NULL, "\"%s\": format compile error - %s", + inform("\"%s\": format compile error - %s", &usr_fstring[errpos-errctx], str); adios (NULL, "%*s", errctx+1, "^"); } @@ -313,21 +386,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; - int i; + char *cp; + size_t i; + static int comptable_initialized = 0; - if (format_string) - free (format_string); - format_string = getcpy (fstring); + format_string = mh_xstrdup(fstring); usr_fstring = fstring; - /* init the component hash table. */ - for (i = 0; i < sizeof(wantcomp)/sizeof(wantcomp[0]); i++) - wantcomp[i] = 0; - - memset((char *) &fmt_mnull, 0, sizeof(fmt_mnull)); + if (reset_comptable || !comptable_initialized) { + free_comptable(); + comptable_initialized = 1; + } /* it takes at least 4 char to generate one format so we * allocate a worst-case format array using 1/4 the length @@ -337,12 +408,7 @@ fmt_compile(char *fstring, struct format **fmt) */ i = strlen(fstring)/2 + 1; if (i==1) i++; - next_fp = formatvec = (struct format *)calloc ((size_t) i, - sizeof(struct format)); - if (next_fp == NULL) - adios (NULL, "unable to allocate format storage"); - - ncomp = 0; + next_fp = formatvec = mh_xcalloc(i, sizeof *next_fp); infunction = 0; cp = compile(format_string); @@ -352,14 +418,15 @@ fmt_compile(char *fstring, struct format **fmt) LV(FT_DONE, 0); /* really done */ *fmt = formatvec; - return (ncomp); + free(format_string); + return ncomp; } static char * compile (char *sp) { - register char *cp = sp; - register int c; + char *cp = sp; + int c; for (;;) { sp = cp; @@ -377,7 +444,7 @@ compile (char *sp) break; } if (c == 0) - return (cp); + return cp; switch (c = *++cp) { case '%': @@ -389,7 +456,7 @@ compile (char *sp) case '>': case '?': case ']': - return (cp); + return cp; case '<': cp = do_if(++cp); @@ -419,13 +486,11 @@ compile (char *sp) static char * do_spec(char *sp) { - register char *cp = sp; - register int c; -#ifndef lint - register int ljust = 0; -#endif /* not lint */ - register int wid = 0; - register char fill = ' '; + char *cp = sp; + int c; + int ljust = 0; + int wid = 0; + char fill = ' '; c = *cp++; if (c == '-') { @@ -464,7 +529,7 @@ do_spec(char *sp) fp->f_width = wid; fp->f_fill = fill; - return (cp); + return cp; } /* @@ -475,9 +540,9 @@ do_spec(char *sp) static char * do_name(char *sp, int preprocess) { - register char *cp = sp; - register int c; - register int i; + char *cp = sp; + int c; + int i; static int primed = 0; while (isalnum(c = *cp++) || c == '-' || c == '_') @@ -493,19 +558,24 @@ do_name(char *sp, int preprocess) if (cm->c_type & CT_ADDR) { CERROR("component used as both date and address"); } - cm->c_tws = (struct tws *) - calloc((size_t) 1, sizeof(*cm->c_tws)); + if (cm->c_tws) { + ZERO(cm->c_tws); + } else { + NEW0(cm->c_tws); + } fp->f_type = preprocess; PUTCOMP(sp); cm->c_type |= CT_DATE; break; case FT_MYMBOX: + case FT_GETMYMBOX: + case FT_GETMYADDR: if (!primed) { - ismymbox ((struct mailname *) 0); + ismymbox(NULL); primed++; } - /* fall through */ + /* FALLTHRU */ case FT_PARSEADDR: if (cm->c_type & CT_DATE) { CERROR("component used as both date and address"); @@ -523,7 +593,7 @@ do_name(char *sp, int preprocess) cm->c_type |= CT_ADDR; break; } - return (cp); + return cp; } /* @@ -533,10 +603,10 @@ do_name(char *sp, int preprocess) static char * do_func(char *sp) { - register char *cp = sp; - register int c; - register struct ftable *t; - register int n; + char *cp = sp; + int c; + struct ftable *t; + int n; int mflag; /* minus sign in NUM */ infunction++; @@ -593,13 +663,81 @@ do_func(char *sp) LS(t->f_type, getusername()); break; + case TF_MYNAME: + LS(t->f_type, getfullname()); + break; + + case TF_MYHOST: + LS(t->f_type, LocalName(0)); + break; + + case TF_LMBOX: + LS(t->f_type, getlocalmbox()); + break; + + case TF_BOLD: + LS(t->f_type, get_term_stringcap("bold")); + break; + + case TF_UNDERLN: + LS(t->f_type, get_term_stringcap("smul")); + break; + + case TF_STNDOUT: + LS(t->f_type, get_term_stringcap("smso")); + break; + + case TF_RESET: + LS(t->f_type, get_term_stringcap("sgr0")); + break; + + case TF_HASCLR: + LV(t->f_type, get_term_numcap("colors") > 1); + break; + + case TF_FGCOLR: + case TF_BGCOLR: { + struct colormap *cmap = colortable; + char *code; + + sp = cp - 1; + while (c && c != ')') + c = *cp++; + cp[-1] = '\0'; + + while (cmap->colorname != NULL) { + if (strcasecmp(sp, cmap->colorname) == 0) + break; + cmap++; + } + + if (cmap->colorname == NULL) { + CERROR("Unknown color name"); + break; + } + + code = get_term_stringparm(t->type == TF_FGCOLR ? "setaf" : "setab", + cmap->ansinum, 0); + + /* + * If this doesn't have anything, try falling back to setf/setb + */ + + if (! code) + code = get_term_stringparm(t->type == TF_FGCOLR ? "setf" : "setb", + cmap->nonansinum, 0); + + LS(t->f_type, code); + break; + } + case TF_NOW: - LV(t->f_type, time((time_t *) 0)); + LV(t->f_type, time(NULL)); break; case TF_EXPR_SV: LV(FT_SAVESTR, 0); - /* fall through */ + /* FALLTHRU */ case TF_EXPR: *--cp = c; cp = do_expr(cp, t->extra); @@ -619,7 +757,7 @@ do_func(char *sp) CERROR("')' expected"); } --infunction; - return (cp); + return cp; } /* @@ -629,8 +767,8 @@ do_func(char *sp) static char * do_expr (char *sp, int preprocess) { - register char *cp = sp; - register int c; + char *cp = sp; + int c; if ((c = *cp++) == '{') { cp = do_name (cp, preprocess); @@ -638,13 +776,13 @@ do_expr (char *sp, int preprocess) } else if (c == '(') { cp = do_func (cp); } else if (c == ')') { - return (--cp); + return --cp; } else if (c == '%' && *cp == '<') { cp = do_if (cp+1); } else { CERROR ("'(', '{', '%<' or ')' expected"); } - return (cp); + return cp; } /* @@ -655,11 +793,42 @@ 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 so: + * + * 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) { - register char *cp = sp; + char *cp = sp; struct format *floop; floop = next_fp; @@ -683,10 +852,10 @@ do_loop(char *sp) static char * do_if(char *sp) { - register char *cp = sp; - register struct format *fexpr, - *fif = (struct format *)NULL; - register int c = '<'; + char *cp = sp; + struct format *fexpr, + *fif = NULL; + int c = '<'; for (;;) { if (c == '<') { /* doing an IF */ @@ -726,7 +895,7 @@ do_if(char *sp) fif = fp; /* loc of GOTO */ fexpr->f_skip = next_fp - fexpr; - fexpr = (struct format *)NULL;/* no extra ENDIF */ + fexpr = NULL; /* no extra ENDIF */ cp = compile (cp); /* compile ELSE stmts */ fif->f_skip = next_fp - fif; @@ -750,5 +919,234 @@ do_if(char *sp) if (fexpr) /* IF ... [ELSIF ...] ENDIF */ fexpr->f_skip = next_fp - fexpr; - return (cp); + 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(); +} + +/* + * Free just the text strings from all of the component hash table entries + */ + +void +fmt_freecomptext(void) +{ + unsigned int i; + struct comp *cm; + + for (i = 0; i < DIM(wantcomp); i++) + for (cm = wantcomp[i]; cm; cm = cm->c_next) { + free(cm->c_text); + cm->c_text = NULL; + } +} + +/* + * 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 (strcasecmp(component, FENDNULL(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 (strcasecmp(component, FENDNULL(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 (strcasecmp(component, FENDNULL(cptr->c_name)) == 0) + cptr->c_text = add(text, cptr->c_text); + } +} + +/* + * Iterate over our component hash table + */ + +struct comp * +fmt_nextcomp(struct comp *comp, unsigned int *bucket) +{ + if (comp == NULL) + *bucket = 0; + else + comp = comp->c_next; + + while (comp == NULL && *bucket < DIM(wantcomp)) { + comp = wantcomp[(*bucket)++]; + } + + return comp; +} + +/* + * Free and reset our component hash table + */ + +static void +free_comptable(void) +{ + unsigned int i; + struct comp *cm, *cm2; + + for (i = 0; i < DIM(wantcomp); 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 ... */ + free(cm->c_name); + 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); + } }