-
/*
- * fmt_compile.c -- "compile" format strings for fmt_scan
- *
- * $Id$
- */
+** 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
+** complete copyright information.
+**
+** This code compiles the format strings (documented in mh-format(5)) into
+** an internal form to be later processed by fmt_scan.c.
+**
+** What happens here is that the format strings are parsed and an array
+** of struct format structures are returned. Each format structure is
+** a single operation interpreted by the the routines in fmt_scan.c.
+**
+** 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.
+**
+** A list of format instructions can be found in fmt_compile.h.
+**
+** If you wish to add a new function, you will need to do the following
+** things:
+**
+** - Add a new instruction to the list of instructions in fmt_compile.h.
+** Note that test instructions (starting with FT_IF_S_NULL) have special
+** handling, so if you are NOT writing a test function then you need
+** to insert it into the list before that _and_ bump all of the
+** following instruction numbers.
+**
+** - Add the function name to the functable[] array below, and write any
+** special code that your function may require in terms of parsing
+** (it very well may not need anything).
+**
+** - Add the code in fmt_scan.c to handle your new function.
+**
+** - Document the new function in the mh-format(5) man page.
+**
+*/
#include <h/mh.h>
+#include <h/utils.h>
#include <h/addrsbr.h>
-#include <zotnet/tws/tws.h>
+#include <h/tws.h>
#include <h/fmt_scan.h>
#include <h/fmt_compile.h>
+#include <ctype.h>
+#include <sysexits.h>
-#ifdef TIME_WITH_SYS_TIME
+#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
-# include <time.h>
-#else
-# ifdef TM_IN_SYS_TIME
-# include <sys/time.h>
-# else
-# include <time.h>
-# endif
#endif
+#include <time.h>
/*
- * hash table for deciding if a component is "interesting"
- */
+** hash table for deciding if a component is "interesting"
+*/
struct comp *wantcomp[128];
-static struct format *formatvec; /* array to hold formats */
-static struct format *next_fp; /* next free format slot */
-static struct format *fp; /* current format slot */
-static struct comp *cm; /* most recent comp ref */
-static struct ftable *ftbl; /* most recent func ref */
+static struct format *formatvec; /* array to hold formats */
+static struct format *next_fp; /* next free format slot */
+static struct format *fp; /* current format slot */
+static struct comp *cm; /* most recent comp ref */
+static struct ftable *ftbl; /* most recent func ref */
static int ncomp;
-static int infunction; /* function nesting cnt */
+static int infunction; /* function nesting cnt */
extern struct mailname fmt_mnull;
/* ftable->type (argument type) */
-#define TF_COMP 0 /* component expected */
-#define TF_NUM 1 /* number expected */
-#define TF_STR 2 /* string expected */
-#define TF_EXPR 3 /* component or func. expected */
-#define TF_NONE 4 /* no argument */
-#define TF_MYBOX 5 /* special - get current user's mbox */
-#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_COMP 0 /* component expected */
+#define TF_NUM 1 /* number expected */
+#define TF_STR 2 /* string expected */
+#define TF_EXPR 3 /* component or func. expected */
+#define TF_NONE 4 /* no argument */
+#define TF_MYBOX 5 /* special - get current user's mbox */
+#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 */
/* ftable->flags */
-#define TFL_PUTS 1 /* implicit putstr if top level */
-#define TFL_PUTN 2 /* implicit putnum if top level */
+/*
+** NB that TFL_PUTS is also used to decide whether the test
+** in a "%<(function)..." should be a string or numeric one.
+*/
+#define TFL_PUTS 1 /* implicit putstr if top level */
+#define TFL_PUTN 2 /* implicit putnum if top level */
+/*
+** The functable array maps between the text names of format functions and
+** the format instructions interpreted by the engine in fmt_scan.c.
+**
+** The elements of this structure are as follows:
+**
+** name: The name of the function as seen in the format string. This is
+** 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).
+** 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.
+** Uses include:
+** - Providing an alternate instruction to combine a load
+** and test operation (see do_if()).
+** - Passed in f_value in the format instruction to provide
+** extra information for the engine (see FT_LV_DAT handling
+** in fmt_scan.c).
+** - Provide a hint as to preprocessing that is required for
+** this instruction (see do_name()).
+** flags: See the definitions for TFL_PUTS & TFL_PUTN above.
+*/
struct ftable {
- char *name; /* function name */
- char type; /* argument type */
- char f_type; /* fmt type */
- char extra; /* arg. type dependent extra info */
- char flags;
+ char *name; /* function name */
+ char type; /* argument type */
+ char f_type; /* fmt type */
+ char extra; /* arg. type dependent extra info */
+ char flags;
};
static struct ftable functable[] = {
- { "nonzero", TF_EXPR, FT_V_NE, FT_IF_V_NE, 0 },
- { "zero", TF_EXPR, FT_V_EQ, FT_IF_V_EQ, 0 },
- { "eq", TF_NUM, FT_V_EQ, FT_IF_V_EQ, 0 },
- { "ne", TF_NUM, FT_V_NE, FT_IF_V_NE, 0 },
- { "gt", TF_NUM, FT_V_GT, FT_IF_V_GT, 0 },
- { "null", TF_EXPR, FT_S_NULL, FT_IF_S_NULL, 0 },
- { "nonnull", TF_EXPR, FT_S_NONNULL, FT_IF_S, 0 },
- { "match", TF_STR, FT_V_MATCH, FT_IF_MATCH, 0 },
- { "amatch", TF_STR, FT_V_AMATCH, FT_IF_AMATCH, 0 },
-
- { "putstr", TF_EXPR, FT_STR, 0, 0 },
- { "putstrf", TF_EXPR, FT_STRF, 0, 0 },
- { "putnum", TF_EXPR, FT_NUM, 0, 0 },
- { "putnumf", TF_EXPR, FT_NUMF, 0, 0 },
- { "putaddr", TF_STR, FT_PUTADDR, 0, 0 },
- { "void", TF_NOP, 0, 0, 0 },
-
- { "comp", TF_COMP, FT_LS_COMP, 0, TFL_PUTS },
- { "lit", TF_STR, FT_LS_LIT, 0, TFL_PUTS },
- { "getenv", TF_STR, FT_LS_GETENV, 0, TFL_PUTS },
- { "profile", TF_STR, FT_LS_CFIND, 0, TFL_PUTS },
- { "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 },
- { "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 },
- { "msg", TF_NONE, FT_LV_DAT, 0, TFL_PUTN },
- { "cur", TF_NONE, FT_LV_DAT, 1, TFL_PUTN },
- { "size", TF_NONE, FT_LV_DAT, 2, TFL_PUTN },
- { "width", TF_NONE, FT_LV_DAT, 3, TFL_PUTN },
- { "unseen", TF_NONE, FT_LV_DAT, 4, TFL_PUTN },
- { "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 },
- { "plus", TF_NUM, FT_LV_PLUS_L, 0, TFL_PUTN },
- { "minus", TF_NUM, FT_LV_MINUS_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 },
- { "timenow", TF_NOW, FT_LV_LIT, 0, TFL_PUTN },
-
- { "month", TF_COMP, FT_LS_MONTH, FT_PARSEDATE, TFL_PUTS },
- { "lmonth", TF_COMP, FT_LS_LMONTH, FT_PARSEDATE, TFL_PUTS },
- { "tzone", TF_COMP, FT_LS_ZONE, FT_PARSEDATE, TFL_PUTS },
- { "day", TF_COMP, FT_LS_DAY, FT_PARSEDATE, TFL_PUTS },
- { "weekday", TF_COMP, FT_LS_WEEKDAY, FT_PARSEDATE, TFL_PUTS },
- { "tws", TF_COMP, FT_LS_822DATE, FT_PARSEDATE, TFL_PUTS },
- { "sec", TF_COMP, FT_LV_SEC, FT_PARSEDATE, TFL_PUTN },
- { "min", TF_COMP, FT_LV_MIN, FT_PARSEDATE, TFL_PUTN },
- { "hour", TF_COMP, FT_LV_HOUR, FT_PARSEDATE, TFL_PUTN },
- { "mday", TF_COMP, FT_LV_MDAY, FT_PARSEDATE, TFL_PUTN },
- { "mon", TF_COMP, FT_LV_MON, FT_PARSEDATE, TFL_PUTN },
- { "year", TF_COMP, FT_LV_YEAR, FT_PARSEDATE, TFL_PUTN },
- { "yday", TF_COMP, FT_LV_YDAY, FT_PARSEDATE, TFL_PUTN },
- { "wday", TF_COMP, FT_LV_WDAY, FT_PARSEDATE, TFL_PUTN },
- { "zone", TF_COMP, FT_LV_ZONE, FT_PARSEDATE, TFL_PUTN },
- { "clock", TF_COMP, FT_LV_CLOCK, FT_PARSEDATE, TFL_PUTN },
- { "rclock", TF_COMP, FT_LV_RCLOCK, FT_PARSEDATE, TFL_PUTN },
- { "sday", TF_COMP, FT_LV_DAYF, FT_PARSEDATE, TFL_PUTN },
- { "szone", TF_COMP, FT_LV_ZONEF, FT_PARSEDATE, TFL_PUTN },
- { "dst", TF_COMP, FT_LV_DST, FT_PARSEDATE, TFL_PUTN },
- { "pretty", TF_COMP, FT_LS_PRETTY, FT_PARSEDATE, TFL_PUTS },
- { "nodate", TF_COMP, FT_LV_COMPFLAG, FT_PARSEDATE, TFL_PUTN },
- { "date2local", TF_COMP, FT_LOCALDATE, FT_PARSEDATE, 0 },
- { "date2gmt", TF_COMP, FT_GMTDATE, FT_PARSEDATE, 0 },
-
- { "pers", TF_COMP, FT_LS_PERS, FT_PARSEADDR, TFL_PUTS },
- { "mbox", TF_COMP, FT_LS_MBOX, FT_PARSEADDR, TFL_PUTS },
- { "host", TF_COMP, FT_LS_HOST, FT_PARSEADDR, TFL_PUTS },
- { "path", TF_COMP, FT_LS_PATH, FT_PARSEADDR, TFL_PUTS },
- { "gname", TF_COMP, FT_LS_GNAME, FT_PARSEADDR, TFL_PUTS },
- { "note", TF_COMP, FT_LS_NOTE, FT_PARSEADDR, TFL_PUTS },
- { "addr", TF_COMP, FT_LS_ADDR, FT_PARSEADDR, TFL_PUTS },
- { "proper", TF_COMP, FT_LS_822ADDR, FT_PARSEADDR, TFL_PUTS },
- { "type", TF_COMP, FT_LV_HOSTTYPE, FT_PARSEADDR, TFL_PUTN },
- { "ingrp", TF_COMP, FT_LV_INGRPF, FT_PARSEADDR, TFL_PUTN },
- { "nohost", TF_COMP, FT_LV_NOHOSTF, FT_PARSEADDR, TFL_PUTN },
- { "formataddr", TF_EXPR_SV,FT_FORMATADDR, FT_FORMATADDR, 0 },
- { "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 },
-
- { NULL, 0, 0, 0, 0 }
+ { "nonzero", TF_EXPR, FT_V_NE, FT_IF_V_NE, 0 },
+ { "zero", TF_EXPR, FT_V_EQ, FT_IF_V_EQ, 0 },
+ { "eq", TF_NUM, FT_V_EQ, FT_IF_V_EQ, 0 },
+ { "ne", TF_NUM, FT_V_NE, FT_IF_V_NE, 0 },
+ { "gt", TF_NUM, FT_V_GT, FT_IF_V_GT, 0 },
+ { "null", TF_EXPR, FT_S_NULL, FT_IF_S_NULL, 0 },
+ { "nonnull", TF_EXPR, FT_S_NONNULL, FT_IF_S, 0 },
+ { "match", TF_STR, FT_V_MATCH, FT_IF_MATCH, 0 },
+ { "amatch", TF_STR, FT_V_AMATCH, FT_IF_AMATCH, 0 },
+
+ { "putstr", TF_EXPR, FT_STR, 0, 0 },
+ { "putstrf", TF_EXPR, FT_STRF, 0, 0 },
+ { "putnum", TF_EXPR, FT_NUM, 0, 0 },
+ { "putnumf", TF_EXPR, FT_NUMF, 0, 0 },
+ { "putaddr", TF_STR, FT_PUTADDR, 0, 0 },
+ { "void", TF_NOP, 0, 0, 0 },
+
+ { "comp", TF_COMP, FT_LS_COMP, 0, TFL_PUTS },
+ { "lit", TF_STR, FT_LS_LIT, 0, TFL_PUTS },
+ { "getenv", TF_STR, FT_LS_GETENV, 0, TFL_PUTS },
+ { "profile", TF_STR, FT_LS_CFIND, 0, TFL_PUTS },
+ { "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 },
+ { "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 },
+ { "msg", TF_NONE, FT_LV_DAT, 0, TFL_PUTN },
+ { "cur", TF_NONE, FT_LV_DAT, 1, TFL_PUTN },
+ { "size", TF_NONE, FT_LV_DAT, 2, TFL_PUTN },
+ { "width", TF_NONE, FT_LV_DAT, 3, TFL_PUTN },
+ { "unseen", TF_NONE, FT_LV_DAT, 4, TFL_PUTN },
+ { "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 },
+ { "plus", TF_NUM, FT_LV_PLUS_L, 0, TFL_PUTN },
+ { "minus", TF_NUM, FT_LV_MINUS_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 },
+ { "timenow", TF_NOW, FT_LV_LIT, 0, TFL_PUTN },
+
+ { "month", TF_COMP, FT_LS_MONTH, FT_PARSEDATE, TFL_PUTS },
+ { "lmonth", TF_COMP, FT_LS_LMONTH, FT_PARSEDATE, TFL_PUTS },
+ { "tzone", TF_COMP, FT_LS_ZONE, FT_PARSEDATE, TFL_PUTS },
+ { "day", TF_COMP, FT_LS_DAY, FT_PARSEDATE, TFL_PUTS },
+ { "weekday", TF_COMP, FT_LS_WEEKDAY, FT_PARSEDATE, TFL_PUTS },
+ { "tws", TF_COMP, FT_LS_822DATE, FT_PARSEDATE, TFL_PUTS },
+ { "sec", TF_COMP, FT_LV_SEC, FT_PARSEDATE, TFL_PUTN },
+ { "min", TF_COMP, FT_LV_MIN, FT_PARSEDATE, TFL_PUTN },
+ { "hour", TF_COMP, FT_LV_HOUR, FT_PARSEDATE, TFL_PUTN },
+ { "mday", TF_COMP, FT_LV_MDAY, FT_PARSEDATE, TFL_PUTN },
+ { "mon", TF_COMP, FT_LV_MON, FT_PARSEDATE, TFL_PUTN },
+ { "year", TF_COMP, FT_LV_YEAR, FT_PARSEDATE, TFL_PUTN },
+ { "yday", TF_COMP, FT_LV_YDAY, FT_PARSEDATE, TFL_PUTN },
+ { "wday", TF_COMP, FT_LV_WDAY, FT_PARSEDATE, TFL_PUTN },
+ { "zone", TF_COMP, FT_LV_ZONE, FT_PARSEDATE, TFL_PUTN },
+ { "clock", TF_COMP, FT_LV_CLOCK, FT_PARSEDATE, TFL_PUTN },
+ { "rclock", TF_COMP, FT_LV_RCLOCK, FT_PARSEDATE, TFL_PUTN },
+ { "sday", TF_COMP, FT_LV_DAYF, FT_PARSEDATE, TFL_PUTN },
+ { "szone", TF_COMP, FT_LV_ZONEF, FT_PARSEDATE, TFL_PUTN },
+ { "dst", TF_COMP, FT_LV_DST, FT_PARSEDATE, TFL_PUTN },
+ { "pretty", TF_COMP, FT_LS_PRETTY, FT_PARSEDATE, TFL_PUTS },
+ { "nodate", TF_COMP, FT_LV_COMPFLAG, FT_PARSEDATE, TFL_PUTN },
+ { "date2local", TF_COMP, FT_LOCALDATE, FT_PARSEDATE, 0 },
+ { "date2gmt", TF_COMP, FT_GMTDATE, FT_PARSEDATE, 0 },
+
+ { "pers", TF_COMP, FT_LS_PERS, FT_PARSEADDR, TFL_PUTS },
+ { "mbox", TF_COMP, FT_LS_MBOX, FT_PARSEADDR, TFL_PUTS },
+ { "host", TF_COMP, FT_LS_HOST, FT_PARSEADDR, TFL_PUTS },
+ { "path", TF_COMP, FT_LS_PATH, FT_PARSEADDR, TFL_PUTS },
+ { "gname", TF_COMP, FT_LS_GNAME, FT_PARSEADDR, TFL_PUTS },
+ { "note", TF_COMP, FT_LS_NOTE, FT_PARSEADDR, TFL_PUTS },
+ { "addr", TF_COMP, FT_LS_ADDR, FT_PARSEADDR, TFL_PUTS },
+ { "proper", TF_COMP, FT_LS_822ADDR, FT_PARSEADDR, TFL_PUTS },
+ { "type", TF_COMP, FT_LV_HOSTTYPE, FT_PARSEADDR, TFL_PUTN },
+ { "ingrp", TF_COMP, FT_LV_INGRPF, FT_PARSEADDR, TFL_PUTN },
+ { "nohost", TF_COMP, FT_LV_NOHOSTF, FT_PARSEADDR, TFL_PUTN },
+ { "formataddr", TF_EXPR_SV, FT_FORMATADDR, FT_FORMATADDR, 0 },
+ { "friendly", TF_COMP, FT_LS_FRIENDLY, FT_PARSEADDR, TFL_PUTS },
+
+ { "mymbox", TF_COMP, FT_LV_COMPFLAG, FT_MYMBOX, TFL_PUTN },
+
+ { "unquote", TF_EXPR, FT_LS_UNQUOTE, 0, TFL_PUTS},
+ { "unmailto", TF_EXPR, FT_LS_UNMAILTO, 0, TFL_PUTS},
+
+ { NULL, 0, 0, 0, 0 }
};
/* Add new component to the hash table */
-#define NEWCOMP(cm,name)\
- cm = ((struct comp *) calloc(1, sizeof (struct comp)));\
- cm->c_name = name;\
- ncomp++;\
- i = CHASH(name);\
- cm->c_next = wantcomp[i];\
- wantcomp[i] = cm;
+#define NEWCOMP(cm,name) do { \
+ cm = (mh_xcalloc(1, sizeof (struct comp)));\
+ cm->c_name = name;\
+ ncomp++;\
+ i = CHASH(name);\
+ cm->c_next = wantcomp[i];\
+ wantcomp[i] = cm; \
+ } while (0)
#define NEWFMT (next_fp++)
-#define NEW(type,fill,wid)\
- fp=NEWFMT; fp->f_type=(type); fp->f_fill=(fill); fp->f_width=(wid);
+#define NEW(type,fill,wid) do {\
+ fp=NEWFMT; fp->f_type=(type); fp->f_fill=(fill); fp->f_width=(wid); \
+ } while (0)
/* Add (possibly new) component to the hash table */
-#define ADDC(name)\
- FINDCOMP(cm, name);\
- if (!cm) {\
- NEWCOMP(cm,name);\
- }\
- fp->f_comp = cm;
+#define ADDC(name) do { \
+ FINDCOMP(cm, name);\
+ if (!cm) {\
+ NEWCOMP(cm,name);\
+ }\
+ fp->f_comp = cm; \
+ } while (0)
-#define LV(type, value) NEW(type,0,0); fp->f_value = (value);
-#define LS(type, str) NEW(type,0,0); fp->f_text = (str);
+#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 PUTCOMP(comp) NEW(FT_COMP,0,0); ADDC(comp);
-#define PUTLIT(str) NEW(FT_LIT,0,0); fp->f_text = (str);
-#define PUTC(c) NEW(FT_CHAR,0,0); fp->f_char = (c);
+#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)
static char *format_string;
-static char *usr_fstring; /* for CERROR */
+static unsigned char *usr_fstring; /* for CERROR */
-#define CERROR(str) compile_error (str, cp)
-
-/*
- * external prototypes
- */
-extern char *getusername(void);
+#define CERROR(str) compile_error(str, cp)
/*
- * static prototypes
- */
+** static prototypes
+*/
static struct ftable *lookup(char *);
static void compile_error(char *, char *);
-static char *compile (char *);
+static char *compile(char *);
static char *do_spec(char *);
static char *do_name(char *, int);
static char *do_func(char *);
-static char *do_expr (char *, int);
+static char *do_expr(char *, int);
static char *do_loop(char *);
static char *do_if(char *);
+/*
+** Lookup a function name in the functable
+*/
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);
+ while ((nm = t->name)) {
+ if (*nm == c && strcmp(nm, name) == 0)
+ return (ftbl = t);
- t++;
- }
- return (struct ftable *) 0;
+ t++;
+ }
+ return (struct ftable *) 0;
}
static void
compile_error(char *str, char *cp)
{
- int i, errpos, errctx;
+ int i, errpos, errctx;
- errpos = cp - format_string;
- errctx = errpos > 20 ? 20 : errpos;
- usr_fstring[errpos] = '\0';
+ errpos = cp - format_string;
+ errctx = errpos > 20 ? 20 : errpos;
+ 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
- usr_fstring[i] = '_';
- }
+ for (i = errpos-errctx; i < errpos; i++) {
+ if (iscntrl(usr_fstring[i]))
+ usr_fstring[i] = '_';
+ }
- advise(NULL, "\"%s\": format compile error - %s",
- &usr_fstring[errpos-errctx], str);
- adios (NULL, "%*s", errctx+1, "^");
+ advise(NULL, "\"%s\": format compile error - %s",
+ &usr_fstring[errpos-errctx], str);
+ adios(EX_SOFTWARE, NULL, "%*s", errctx+1, "^");
}
/*
- * Compile format string "fstring" into format list "fmt".
- * Return the number of header components found in the format
- * string.
- */
-
+** Compile format string "fstring" into format list "fmt".
+** Return the number of header components found in the format
+** string.
+*/
int
fmt_compile(char *fstring, struct format **fmt)
{
- register char *cp;
- int i;
-
- 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;
-
- memset((char *) &fmt_mnull, 0, sizeof(fmt_mnull));
-
- /* it takes at least 4 char to generate one format so we
- * allocate a worst-case format array using 1/4 the length
- * of the format string. We actually need twice this much
- * to handle both pre-processing (e.g., address parsing) and
- * normal processing.
- */
- 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;
- infunction = 0;
-
- cp = compile(format_string);
- if (*cp) {
- CERROR("extra '%>', '%|' or '%?'");
- }
- LV(FT_DONE, 0); /* really done */
- *fmt = formatvec;
-
- return (ncomp);
+ char *cp;
+ size_t i;
+
+ if (format_string)
+ mh_free0(&format_string);
+ 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));
+
+ /*
+ ** it takes at least 4 char to generate one format so we
+ ** allocate a worst-case format array using 1/4 the length
+ ** of the format string. We actually need twice this much
+ ** to handle both pre-processing (e.g., address parsing) and
+ ** normal processing.
+ */
+ i = strlen(fstring)/2 + 1;
+ if (i == 1)
+ i++;
+ next_fp = formatvec = mh_xcalloc(i, sizeof(struct format));
+ if (next_fp == NULL)
+ adios(EX_OSERR, NULL, "unable to allocate format storage");
+
+ ncomp = 0;
+ infunction = 0;
+
+ cp = compile(format_string);
+ if (*cp) {
+ CERROR("extra '%>', '%|' or '%?'");
+ }
+ LV(FT_DONE, 0); /* really done */
+ *fmt = formatvec;
+
+ return (ncomp);
}
static char *
-compile (char *sp)
+compile(char *sp)
{
- register char *cp = sp;
- register int c;
-
- for (;;) {
- sp = cp;
- while ((c = *cp) && c != '%')
- cp++;
- *cp = 0;
- switch (cp-sp) {
- case 0:
- break;
- case 1:
- PUTC(*sp);
- break;
- default:
- PUTLIT(sp);
- break;
- }
- if (c == 0)
- return (cp);
-
- switch (c = *++cp) {
- case '%':
- PUTC (*cp);
- cp++;
- break;
-
- case '|':
- case '>':
- case '?':
- case ']':
- return (cp);
-
- case '<':
- cp = do_if(++cp);
- break;
-
- case '[': /* ] */
- cp = do_loop(++cp);
- break;
-
- case ';': /* comment line */
- cp++;
- while ((c = *cp++) && c != '\n')
- continue;
- break;
-
- default:
- cp = do_spec(cp);
- break;
+ char *cp = sp;
+ int c;
+
+ for (;;) {
+ sp = cp;
+ while ((c = *cp) && c != '%')
+ cp++;
+ *cp = 0;
+ switch (cp-sp) {
+ case 0:
+ break;
+ case 1:
+ PUTC(*sp);
+ break;
+ default:
+ PUTLIT(sp);
+ break;
+ }
+ if (c == 0)
+ return (cp);
+
+ switch (c = *++cp) {
+ case '%':
+ PUTC(*cp);
+ cp++;
+ break;
+
+ case '|':
+ case '>':
+ case '?':
+ case ']':
+ return (cp);
+
+ case '<':
+ cp = do_if(++cp);
+ break;
+
+ case '[': /* ] */
+ cp = do_loop(++cp);
+ break;
+
+ case ';': /* comment line */
+ cp++;
+ while ((c = *cp++) && c != '\n')
+ continue;
+ break;
+
+ default:
+ cp = do_spec(cp);
+ break;
+ }
}
- }
}
+/*
+** Process functions & components (handle field width here as well
+*/
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 = ' ';
-
- c = *cp++;
- if (c == '-') {
- ljust++;
- c = *cp++;
- }
- if (c == '0') {
- fill = c;
- c = *cp++;
- }
- while (isdigit(c)) {
- wid = wid*10 + (c - '0');
+ char *cp = sp;
+ int c;
+ int ljust = 0;
+ int wid = 0;
+ char fill = ' ';
+
c = *cp++;
- }
- if (c == '{') {
- cp = do_name(cp, 0);
- if (! infunction)
- fp->f_type = wid? FT_COMPF : FT_COMP;
- }
- else if (c == '(') {
- cp = do_func(cp);
- if (! infunction) {
- if (ftbl->flags & TFL_PUTS) {
- LV( wid? FT_STRF : FT_STR, ftbl->extra);
- }
- else if (ftbl->flags & TFL_PUTN) {
- LV( wid? FT_NUMF : FT_NUM, ftbl->extra);
- }
+ if (c == '-') {
+ ljust++;
+ c = *cp++;
+ }
+ if (c == '0') {
+ fill = c;
+ c = *cp++;
}
- }
- else {
- CERROR("component or function name expected");
- }
- if (ljust)
- wid = -wid;
- fp->f_width = wid;
- fp->f_fill = fill;
-
- return (cp);
+ while (isdigit(c)) {
+ wid = wid*10 + (c - '0');
+ c = *cp++;
+ }
+ if (c == '{') {
+ cp = do_name(cp, 0);
+ if (! infunction)
+ fp->f_type = wid? FT_COMPF : FT_COMP;
+ } else if (c == '(') {
+ cp = do_func(cp);
+ if (! infunction) {
+ if (ftbl->flags & TFL_PUTS) {
+ LV( wid? FT_STRF : FT_STR, ftbl->extra);
+ } else if (ftbl->flags & TFL_PUTN) {
+ LV( wid? FT_NUMF : FT_NUM, ftbl->extra);
+ }
+ }
+ } else {
+ CERROR("component or function name expected");
+ }
+ if (ljust)
+ wid = -wid;
+ fp->f_width = wid;
+ fp->f_fill = fill;
+
+ return (cp);
}
+
+/*
+** Process a component name. Normally this involves generating an FT_COMP
+** instruction for the specified component. If preprocess is set, then we
+** do some extra processing.
+*/
static char *
do_name(char *sp, int preprocess)
{
- register char *cp = sp;
- register int c;
- register int i;
- static int primed = 0;
-
- while (isalnum(c = *cp++) || c == '-' || c == '_')
- ;
- if (c != '}') {
- CERROR("'}' expected");
- }
- cp[-1] = '\0';
- PUTCOMP(sp);
- switch (preprocess) {
-
- case FT_PARSEDATE:
- if (cm->c_type & CT_ADDR) {
- CERROR("component used as both date and address");
- }
- if (! (cm->c_type & CT_DATE)) {
- cm->c_tws = (struct tws *)
- calloc((size_t) 1, sizeof(*cm->c_tws));
- fp->f_type = preprocess;
- PUTCOMP(sp);
- cm->c_type |= CT_DATE;
- }
- break;
-
- case FT_MYMBOX:
- if (!primed) {
- ismymbox ((struct mailname *) 0);
- primed++;
+ char *cp = sp;
+ int c;
+ int i;
+ static int primed = 0;
+
+ while (isalnum(c = *cp++) || c == '-' || c == '_')
+ ;
+ if (c != '}') {
+ CERROR("'}' expected");
}
- cm->c_type |= CT_MYMBOX;
- /* fall through */
- case FT_PARSEADDR:
- if (cm->c_type & CT_DATE) {
- CERROR("component used as both date and address");
- }
- if (! (cm->c_type & CT_ADDRPARSE)) {
- cm->c_mn = &fmt_mnull;
- fp->f_type = preprocess;
- PUTCOMP(sp);
- cm->c_type |= (CT_ADDR | CT_ADDRPARSE);
- }
- break;
+ cp[-1] = '\0';
+ PUTCOMP(sp);
+ switch (preprocess) {
- case FT_FORMATADDR:
- if (cm->c_type & CT_DATE) {
- CERROR("component used as both date and address");
+ case FT_PARSEDATE:
+ if (cm->c_type & CT_ADDR) {
+ CERROR("component used as both date and address");
+ }
+ cm->c_tws = mh_xcalloc(1, sizeof(*cm->c_tws));
+ fp->f_type = preprocess;
+ PUTCOMP(sp);
+ cm->c_type |= CT_DATE;
+ break;
+
+ case FT_MYMBOX:
+ if (!primed) {
+ ismymbox((struct mailname *) 0);
+ primed++;
+ }
+ /* fall through */
+ case FT_PARSEADDR:
+ if (cm->c_type & CT_DATE) {
+ CERROR("component used as both date and address");
+ }
+ cm->c_mn = &fmt_mnull;
+ fp->f_type = preprocess;
+ PUTCOMP(sp);
+ cm->c_type |= CT_ADDR;
+ break;
+
+ case FT_FORMATADDR:
+ if (cm->c_type & CT_DATE) {
+ CERROR("component used as both date and address");
+ }
+ cm->c_type |= CT_ADDR;
+ break;
}
- cm->c_type |= CT_ADDR;
- break;
- }
- return (cp);
+ return (cp);
}
+
+/*
+** Generate one or more instructions corresponding to the named function.
+** The different type of function arguments are handled here.
+*/
static char *
do_func(char *sp)
{
- register char *cp = sp;
- register int c;
- register struct ftable *t;
- register int n;
- int mflag; /* minus sign in NUM */
-
- infunction++;
-
- while (isalnum(c = *cp++))
- ;
- if (c != '(' && c != '{' && c != ' ' && c != ')') {
- CERROR("'(', '{', ' ' or ')' expected");
- }
- cp[-1] = '\0';
- if ((t = lookup (sp)) == 0) {
- CERROR("unknown function");
- }
- if (isspace(c))
- c = *cp++;
+ char *cp = sp;
+ int c;
+ struct ftable *t;
+ int n;
+ int mflag; /* minus sign in NUM */
+
+ infunction++;
+
+ while (isalnum(c = *cp++))
+ ;
+ if (c != '(' && c != '{' && c != ' ' && c != ')') {
+ CERROR("'(', '{', ' ' or ')' expected");
+ }
+ cp[-1] = '\0';
+ if ((t = lookup(sp)) == 0) {
+ CERROR("unknown function");
+ }
+ if (isspace(c))
+ c = *cp++;
- switch (t->type) {
+ switch (t->type) {
- case TF_COMP:
- if (c != '{') {
- CERROR("component name expected");
+ case TF_COMP:
+ if (c != '{') {
+ CERROR("component name expected");
+ }
+ cp = do_name(cp, t->extra);
+ fp->f_type = t->f_type;
+ c = *cp++;
+ break;
+
+ case TF_NUM:
+ if ((mflag = (c == '-')))
+ c = *cp++;
+ n = 0;
+ while (isdigit(c)) {
+ n = n*10 + (c - '0');
+ c = *cp++;
+ }
+ if (mflag)
+ n = (-n);
+ LV(t->f_type,n);
+ break;
+
+ case TF_STR:
+ sp = cp - 1;
+ while (c && c != ')')
+ c = *cp++;
+ cp[-1] = '\0';
+ LS(t->f_type,sp);
+ break;
+
+ case TF_NONE:
+ LV(t->f_type,t->extra);
+ break;
+
+ case TF_MYBOX:
+ LS(t->f_type, getusername());
+ break;
+
+ case TF_NOW:
+ LV(t->f_type, time((time_t *) 0));
+ break;
+
+ case TF_EXPR_SV:
+ LV(FT_SAVESTR, 0);
+ /* fall through */
+ case TF_EXPR:
+ *--cp = c;
+ cp = do_expr(cp, t->extra);
+ LV(t->f_type, 0);
+ c = *cp++;
+ ftbl = t;
+ break;
+
+ case TF_NOP:
+ *--cp = c;
+ cp = do_expr(cp, t->extra);
+ c = *cp++;
+ ftbl = t;
+ break;
}
- cp = do_name(cp, t->extra);
- fp->f_type = t->f_type;
- c = *cp++;
- break;
-
- case TF_NUM:
- if ((mflag = (c == '-')))
- c = *cp++;
- n = 0;
- while (isdigit(c)) {
- n = n*10 + (c - '0');
- c = *cp++;
+ if (c != ')') {
+ CERROR("')' expected");
}
- if (mflag)
- n = (-n);
- LV(t->f_type,n);
- break;
-
- case TF_STR:
- sp = cp - 1;
- while (c && c != ')')
- c = *cp++;
- cp[-1] = '\0';
- LS(t->f_type,sp);
- break;
-
- case TF_NONE:
- LV(t->f_type,t->extra);
- break;
-
- case TF_MYBOX:
- LS(t->f_type, getusername());
- break;
-
- case TF_NOW:
- LV(t->f_type, time((time_t *) 0));
- break;
-
- case TF_EXPR_SV:
- LV(FT_SAVESTR, 0);
- /* fall through */
- case TF_EXPR:
- *--cp = c;
- cp = do_expr(cp, t->extra);
- LV(t->f_type, 0);
- c = *cp++;
- ftbl = t;
- break;
-
- case TF_NOP:
- *--cp = c;
- cp = do_expr(cp, t->extra);
- c = *cp++;
- ftbl = t;
- break;
- }
- if (c != ')') {
- CERROR("')' expected");
- }
- --infunction;
- return (cp);
+ --infunction;
+ return (cp);
}
+
+/*
+** Handle an expression as an argument. Basically we call one of do_name(),
+** do_func(), or do_if()
+*/
static char *
-do_expr (char *sp, int preprocess)
+do_expr(char *sp, int preprocess)
{
- register char *cp = sp;
- register int c;
-
- if ((c = *cp++) == '{') {
- cp = do_name (cp, preprocess);
- fp->f_type = FT_LS_COMP;
- } else if (c == '(') {
- cp = do_func (cp);
- } else if (c == ')') {
- return (--cp);
- } else if (c == '%' && *cp == '<') {
- cp = do_if (cp+1);
- } else {
- CERROR ("'(', '{', '%<' or ')' expected");
- }
- return (cp);
+ char *cp = sp;
+ int c;
+
+ if ((c = *cp++) == '{') {
+ cp = do_name(cp, preprocess);
+ fp->f_type = FT_LS_COMP;
+ } else if (c == '(') {
+ cp = do_func(cp);
+ } else if (c == ')') {
+ return (--cp);
+ } else if (c == '%' && *cp == '<') {
+ cp = do_if(cp+1);
+ } else {
+ CERROR("'(', '{', '%<' or ')' expected");
+ }
+ return (cp);
}
+
+/*
+** I am guessing this was for some kind of loop statement, which would have
+** looked like %[ .... %]. It looks like the way this would have worked
+** is that the format engine would have seen that FT_DONE had a 1 in the
+** f_un.f_un_value and then decided whether or not to continue the loop.
+** 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.
+*/
static char *
do_loop(char *sp)
{
- register char *cp = sp;
- struct format *floop;
+ char *cp = sp;
+ struct format *floop;
- floop = next_fp;
- cp = compile (cp);
- if (*cp++ != ']')
- CERROR ("']' expected");
+ floop = next_fp;
+ cp = compile(cp);
+ if (*cp++ != ']')
+ CERROR("']' expected");
- LV(FT_DONE, 1); /* not yet done */
- LV(FT_GOTO, 0);
- fp->f_skip = floop - fp; /* skip backwards */
+ LV(FT_DONE, 1); /* not yet done */
+ LV(FT_GOTO, 0);
+ fp->f_skip = floop - fp; /* skip backwards */
- return cp;
+ return cp;
}
+
+/*
+** Handle an if-elsif-endif statement. Note here that the branching
+** is handled by the f_skip member of the struct format (which is really
+** just f_width overloaded). This number controls how far to move forward
+** (or back) in the format instruction array.
+*/
static char *
do_if(char *sp)
{
- register char *cp = sp;
- register struct format *fexpr,
- *fif = (struct format *)NULL;
- register int c = '<';
-
- for (;;) {
- if (c == '<') { /* doing an IF */
- if ((c = *cp++) == '{') /*}*/{
- cp = do_name(cp, 0);
- fp->f_type = FT_LS_COMP;
- LV (FT_IF_S, 0);
- }
- else if (c == '(') {
- cp = do_func(cp);
- /* see if we can merge the load and the "if" */
- if (ftbl->f_type >= IF_FUNCS)
- fp->f_type = ftbl->extra;
- else {
- LV (FT_IF_V_NE, 0);
+ char *cp = sp;
+ struct format *fexpr, *fif = (struct format *)NULL;
+ int c = '<';
+
+ for (;;) {
+ if (c == '<') { /* doing an IF */
+ if ((c = *cp++) == '{') /*}*/{
+ cp = do_name(cp, 0);
+ fp->f_type = FT_LS_COMP;
+ LV(FT_IF_S, 0);
+ } else if (c == '(') {
+ cp = do_func(cp);
+ /* see if we can merge the load and the "if" */
+ if (ftbl->f_type >= IF_FUNCS)
+ fp->f_type = ftbl->extra;
+ else {
+ /*
+ ** Put out a string test or a value
+ ** test depending on what this
+ ** function 's return type is.
+ */
+ if (ftbl->flags & TFL_PUTS) {
+ LV(FT_IF_S, 0);
+ } else {
+ LV(FT_IF_V_NE, 0);
+ }
+ }
+ } else {
+ CERROR("'(' or '{' expected"); /*}*/
+ }
}
- }
- else {
- CERROR("'(' or '{' expected"); /*}*/
- }
- }
- fexpr = fp; /* loc of [ELS]IF */
- cp = compile (cp); /* compile IF TRUE stmts */
- if (fif)
- fif->f_skip = next_fp - fif;
+ fexpr = fp; /* loc of [ELS]IF */
+ cp = compile(cp); /* compile IF TRUE stmts */
+ if (fif)
+ fif->f_skip = next_fp - fif;
- if ((c = *cp++) == '|') { /* the last ELSE */
- LV(FT_GOTO, 0);
- fif = fp; /* loc of GOTO */
- fexpr->f_skip = next_fp - fexpr;
+ if ((c = *cp++) == '|') { /* the last ELSE */
+ LV(FT_GOTO, 0);
+ fif = fp; /* loc of GOTO */
+ fexpr->f_skip = next_fp - fexpr;
- fexpr = (struct format *)NULL;/* no extra ENDIF */
+ fexpr = (struct format *)NULL;/* no extra ENDIF */
- cp = compile (cp); /* compile ELSE stmts */
- fif->f_skip = next_fp - fif;
- c = *cp++;
- }
- else if (c == '?') { /* another ELSIF */
- LV(FT_GOTO, 0);
- fif = fp; /* loc of GOTO */
- fexpr->f_skip = next_fp - fexpr;
+ cp = compile(cp); /* compile ELSE stmts */
+ fif->f_skip = next_fp - fif;
+ c = *cp++;
+ } else if (c == '?') { /* another ELSIF */
+ LV(FT_GOTO, 0);
+ fif = fp; /* loc of GOTO */
+ fexpr->f_skip = next_fp - fexpr;
- c = '<'; /* impersonate an IF */
- continue;
+ c = '<'; /* impersonate an IF */
+ continue;
+ }
+ break;
}
- break;
- }
- if (c != '>') {
- CERROR("'>' expected.");
- }
+ if (c != '>') {
+ CERROR("'>' expected.");
+ }
- if (fexpr) /* IF ... [ELSIF ...] ENDIF */
- fexpr->f_skip = next_fp - fexpr;
+ if (fexpr) /* IF ... [ELSIF ...] ENDIF */
+ fexpr->f_skip = next_fp - fexpr;
- return (cp);
+ return (cp);
}