#include <h/mts.h>
#include <h/utils.h>
-#include <term.h>
-
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
static struct ftable *ftbl; /* most recent func ref */
static int ncomp;
static int infunction; /* function nesting cnt */
-static int termstatus = 0; /* 0=uninit,1=ok,-1=fail */
-static char *termcbuf = NULL; /* Capability output str */
-static char *termcbufp = NULL; /* Capability buf ptr */
-static size_t termcbufsz = 0; /* Size of termcbuf */
extern struct mailname fmt_mnull;
#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_RESET 14 /* special - reset terminal modes */
+#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
* 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.
{ "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 },
{ "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 },
{ "friendly", TF_COMP, FT_LS_FRIENDLY, FT_PARSEADDR, TFL_PUTS },
{ "mymbox", TF_COMP, FT_LV_COMPFLAG, FT_MYMBOX, TFL_PUTN },
+ { "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 },
{ "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 }
};
-/*
- * 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.
+/*
+ * 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.
*/
-#define CHASH(nm) (((((nm)[0]) - ((nm)[1])) & 0x1f) + (((nm)[2]) & 0x5f))
+
+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.
/* Add new component to the hash table */
#define NEWCOMP(cm,name) do { \
- cm = ((struct comp *) calloc(1, sizeof (struct comp)));\
- cm->c_name = getcpy(name);\
+ NEW0(cm);\
+ cm->c_name = mh_xstrdup(name);\
cm->c_refcount++;\
ncomp++;\
i = CHASH(name);\
} while (0)
#define NEWFMT (next_fp++)
-#define NEW(type,fill,wid) do {\
+#define NEW_FP(type,fill,wid) do {\
fp=NEWFMT; fp->f_type=(type); fp->f_fill=(fill); fp->f_width=(wid); \
} while (0)
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 = getcpy(str); fp->f_flags |= FF_STRALLOC; } 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 = getcpy(str); fp->f_flags |= FF_STRALLOC; } 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 char *usr_fstring; /* for CERROR */
static char *do_if(char *);
static void free_component(struct comp *);
static void free_comptable(void);
-static void initialize_terminfo(void);
-static void get_term_stringcap(struct ftable *, char *);
-static int termbytes(int);
/*
* 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)
usr_fstring[errpos] = '\0';
for (i = errpos-errctx; i < errpos; i++) {
-#ifdef LOCALE
if (iscntrl((unsigned char) usr_fstring[i]))
-#else
- if (usr_fstring[i] < 32)
-#endif
usr_fstring[i] = '_';
}
int
fmt_compile(char *fstring, struct format **fmt, int reset_comptable)
{
- register char *cp;
+ char *cp;
size_t i;
static int comptable_initialized = 0;
- format_string = getcpy (fstring);
+ format_string = mh_xstrdup(fstring);
usr_fstring = fstring;
if (reset_comptable || !comptable_initialized) {
comptable_initialized = 1;
}
- 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
*/
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");
-
+ next_fp = formatvec = mh_xcalloc(i, sizeof *next_fp);
infunction = 0;
cp = compile(format_string);
static char *
compile (char *sp)
{
- register char *cp = sp;
- register int c;
+ char *cp = sp;
+ int c;
for (;;) {
sp = cp;
static char *
do_spec(char *sp)
{
- register char *cp = sp;
- register int c;
+ char *cp = sp;
+ int c;
#ifndef lint
- register int ljust = 0;
+ int ljust = 0;
#endif /* not lint */
- register int wid = 0;
- register char fill = ' ';
+ int wid = 0;
+ char fill = ' ';
c = *cp++;
if (c == '-') {
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 == '_')
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) {
+ memset (cm->c_tws, 0, sizeof *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);
primed++;
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++;
break;
case TF_BOLD:
- get_term_stringcap(t, "bold");
+ LS(t->f_type, get_term_stringcap("bold"));
break;
case TF_UNDERLN:
- get_term_stringcap(t, "smul");
+ LS(t->f_type, get_term_stringcap("smul"));
+ break;
+
+ case TF_STNDOUT:
+ LS(t->f_type, get_term_stringcap("smso"));
break;
case TF_RESET:
- get_term_stringcap(t, "sgr0");
+ 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));
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);
static char *
do_loop(char *sp)
{
- register char *cp = sp;
+ char *cp = sp;
struct format *floop;
floop = next_fp;
static char *
do_if(char *sp)
{
- register char *cp = sp;
- register struct format *fexpr,
+ char *cp = sp;
+ struct format *fexpr,
*fif = (struct format *)NULL;
- register int c = '<';
+ int c = '<';
for (;;) {
if (c == '<') { /* doing an IF */
struct comp *cm;
for (i = 0; i < sizeof(wantcomp)/sizeof(wantcomp[0]); i++)
- for (cm = wantcomp[i]; cm; cm = cm->c_next)
- if (cm->c_text) {
- free(cm->c_text);
- cm->c_text = NULL;
- }
+ for (cm = wantcomp[i]; cm; cm = cm->c_next) {
+ mh_xfree(cm->c_text);
+ cm->c_text = NULL;
+ }
}
/*
}
}
+/*
+ * 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 < sizeof(wantcomp)/sizeof(wantcomp[0])) {
+ comp = wantcomp[(*bucket)++];
+ }
+
+ return comp;
+}
+
/*
* Free and reset our component hash table
*/
{
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);
+ mh_xfree(cm->c_name);
+ mh_xfree(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)
free(cm);
}
}
-
-/*
- * These functions handles the case of outputting terminal strings depending
- * on the terminfo setting.
- */
-
-/*
- * We should only be called if we haven't yet called setupterm()
- */
-
-void
-initialize_terminfo(void)
-{
- int errret, rc;
-
- rc = setupterm(NULL, fileno(stdout), &errret);
-
- if (rc != 0 || errret != 1)
- termstatus = -1;
- else
- termstatus = 1;
-}
-
-/*
- * Place the results of the specified string entry into the str register.
- *
- * Arguments are:
- *
- * t - Pointer to instruction table entry (used to create fmt instruction)
- * cap - Name of terminfo capability to insert (e.g., "bold", or "sgr0").
- *
- * This ended up being more complicated than I hoped. You need to fetch the
- * entry via tigetstr(), but there MAY be a padding format embedded in what
- * gets returned by tigetstr(), so you have to run it through tputs().
- * And of course tputs() is designed to output to a terminal, so you have
- * capture every byte output by the tputs() callback to get the final
- * string to write to the format engine.
- *
- * If padding bytes are NULs that will be a problem for us, but some quick
- * experimentation suggests that padding bytes are mostly a non-issue anymore.
- * If they still crop up we'll have to figure out how to deal with them.
- */
-
-void
-get_term_stringcap(struct ftable *t, char *cap)
-{
- char *parm;
-
- /*
- * Common to all functions; initialize the termcap if we need it.
- * If it didn't initialize successfully, return silently
- */
-
- if (termstatus == 0)
- initialize_terminfo();
-
- if (termstatus == -1)
- return;
-
- parm = tigetstr(cap);
-
- if (parm == (char *) -1 || parm == NULL) {
- return;
- }
-
- termcbufp = termcbuf;
-
- tputs(parm, 1, termbytes);
-
- termcbufp = '\0';
-
- LS(t->f_type, termcbuf);
-}
-
-/*
- * Store a sequence of characters in our local buffer
- */
-
-static int
-termbytes(int c)
-{
- size_t offset;
-
- /*
- * Bump up the buffer size if we've reached the end (leave room for
- * a trailing NUL)
- */
-
- if ((offset = termcbufp - termcbuf) - 1 >= termcbufsz) {
- termcbufsz += 64;
- termcbuf = mh_xrealloc(termcbuf, termcbufsz);
- termcbufp = termcbuf + offset;
- }
-
- *termcbufp++ = c;
-
- return 0;
-}