* 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.
*
*
* - Add the code in fmt_scan.c to handle your new function.
*
- * - Add code to fmtdump.c to display your new function.
+ * - Add code to fmttest.c to display your new function.
*
* - Document the new function in the mh-format(5) man page.
*
#include <h/mts.h>
#include <h/utils.h>
+#include <curses.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_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_RESET 14 /* special - reset terminal modes */
/* ftable->flags */
/* NB that TFL_PUTS is also used to decide whether the test
{ "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 },
- { "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 },
+ { "resetterm", TF_RESET, FT_LS_LIT, 0, TFL_PUTS },
{ NULL, 0, 0, 0, 0 }
};
} 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); fp->f_flags |= FF_STRALLOC; } 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 = getcpy(str); fp->f_flags |= FF_STRALLOC; } while (0)
#define PUTC(c) do { NEW(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)
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
for (i = errpos-errctx; i < errpos; i++) {
#ifdef LOCALE
- if (iscntrl(usr_fstring[i]))
+ if (iscntrl((unsigned char) usr_fstring[i]))
#else
if (usr_fstring[i] < 32)
#endif
LS(t->f_type, getlocalmbox());
break;
+ case TF_BOLD:
+ get_term_stringcap(t, "bold");
+ break;
+
+ case TF_UNDERLN:
+ get_term_stringcap(t, "smul");
+ break;
+
+ case TF_RESET:
+ get_term_stringcap(t, "sgr0");
+ break;
+
case TF_NOW:
LV(t->f_type, time((time_t *) 0));
break;
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 < 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;
+ }
+}
+
/*
* 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.
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, cm->c_name ? 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.
*
*/
int
-fmt_addcomp(char *component, char *text)
+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) {
+ if (strcasecmp(component, cptr->c_name ? cptr->c_name : "") == 0) {
found++;
if (! cptr->c_text) {
- cptr->c_text = getcpy(text);
+ cptr->c_text = getcpy(text);
} else {
- i = strlen(cp = cptr->c_text) - 1;
+ i = strlen(cp = cptr->c_text) - 1;
if (cp[i] == '\n') {
if (cptr->c_type & CT_ADDR) {
- cp[i] = '\0';
+ cp[i] = '\0';
cp = add(",\n\t", cp);
} else {
- cp = add("\t", cp);
+ cp = add("\t", cp);
}
}
cptr->c_text = add(text, cp);
if (bucket != -1) {
for (cptr = wantcomp[bucket]; cptr; cptr = cptr->c_next)
- if (mh_strcasecmp(component, cptr->c_name) == 0)
+ if (strcasecmp(component, cptr->c_name ? cptr->c_name : "") == 0)
cptr->c_text = add(text, cptr->c_text);
}
}
static void
free_comptable(void)
{
- int i;
+ unsigned int i;
struct comp *cm, *cm2;
for (i = 0; i < sizeof(wantcomp)/sizeof(wantcomp[0]); i++) {
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;
+}