-/* $Id: roff.c,v 1.82 2010/05/17 02:01:05 kristaps Exp $ */
+/* $Id: roff.c,v 1.86 2010/06/01 11:47:28 kristaps Exp $ */
/*
* Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv>
*
#endif
#include <assert.h>
+#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
ROFF_de,
ROFF_dei,
ROFF_de1,
+ ROFF_ds,
ROFF_el,
ROFF_ie,
ROFF_if,
ROFF_ig,
+ ROFF_rm,
+ ROFF_tr,
ROFF_cblock,
ROFF_ccond,
ROFF_MAX
roffproc sub; /* process as child of macro */
int flags;
#define ROFFMAC_STRUCT (1 << 0) /* always interpret */
+ struct roffmac *next;
};
static enum rofferr roff_block(ROFF_ARGS);
static enum rofferr roff_cond(ROFF_ARGS);
static enum rofferr roff_cond_text(ROFF_ARGS);
static enum rofferr roff_cond_sub(ROFF_ARGS);
-
-const struct roffmac roffs[ROFF_MAX] = {
- { "am", roff_block, roff_block_text, roff_block_sub, 0 },
- { "ami", roff_block, roff_block_text, roff_block_sub, 0 },
- { "am1", roff_block, roff_block_text, roff_block_sub, 0 },
- { "de", roff_block, roff_block_text, roff_block_sub, 0 },
- { "dei", roff_block, roff_block_text, roff_block_sub, 0 },
- { "de1", roff_block, roff_block_text, roff_block_sub, 0 },
- { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },
- { "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },
- { "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },
- { "ig", roff_block, roff_block_text, roff_block_sub, 0 },
- { ".", roff_cblock, NULL, NULL, 0 },
- { "\\}", roff_ccond, NULL, NULL, 0 },
+static enum rofferr roff_line(ROFF_ARGS);
+
+/* See roff_hash_find() */
+
+#define ASCII_HI 126
+#define ASCII_LO 33
+#define HASHWIDTH (ASCII_HI - ASCII_LO + 1)
+
+static struct roffmac *hash[HASHWIDTH];
+
+static struct roffmac roffs[ROFF_MAX] = {
+ { "am", roff_block, roff_block_text, roff_block_sub, 0, NULL },
+ { "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL },
+ { "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
+ { "de", roff_block, roff_block_text, roff_block_sub, 0, NULL },
+ { "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL },
+ { "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
+ { "ds", roff_line, NULL, NULL, 0, NULL },
+ { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
+ { "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
+ { "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
+ { "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL },
+ { "rm", roff_line, NULL, NULL, 0, NULL },
+ { "tr", roff_line, NULL, NULL, 0, NULL },
+ { ".", roff_cblock, NULL, NULL, 0, NULL },
+ { "\\}", roff_ccond, NULL, NULL, 0, NULL },
};
static void roff_free1(struct roff *);
static enum rofft roff_hash_find(const char *);
+static void roff_hash_init(void);
static void roffnode_cleanscope(struct roff *);
static int roffnode_push(struct roff *,
enum rofft, int, int);
static void roffnode_pop(struct roff *);
static enum rofft roff_parse(const char *, int *);
+/* See roff_hash_find() */
+#define ROFF_HASH(p) (p[0] - ASCII_LO)
+
+static void
+roff_hash_init(void)
+{
+ struct roffmac *n;
+ int buc, i;
+
+ for (i = 0; i < (int)ROFF_MAX; i++) {
+ assert(roffs[i].name[0] >= ASCII_LO);
+ assert(roffs[i].name[0] <= ASCII_HI);
+
+ buc = ROFF_HASH(roffs[i].name);
+
+ if (NULL != (n = hash[buc])) {
+ for ( ; n->next; n = n->next)
+ /* Do nothing. */ ;
+ n->next = &roffs[i];
+ } else
+ hash[buc] = &roffs[i];
+ }
+}
+
/*
* Look up a roff token by its name. Returns ROFF_MAX if no macro by
static enum rofft
roff_hash_find(const char *p)
{
- int i;
+ int buc;
+ struct roffmac *n;
- /* FIXME: make this be fast and efficient. */
+ /*
+ * libroff has an extremely simple hashtable, for the time
+ * being, which simply keys on the first character, which must
+ * be printable, then walks a chain. It works well enough until
+ * optimised.
+ */
+
+ if (p[0] < ASCII_LO || p[0] > ASCII_HI)
+ return(ROFF_MAX);
- for (i = 0; i < (int)ROFF_MAX; i++)
- if (0 == strcmp(roffs[i].name, p))
- return((enum rofft)i);
+ buc = ROFF_HASH(p);
+
+ if (NULL == (n = hash[buc]))
+ return(ROFF_MAX);
+ for ( ; n; n = n->next)
+ if (0 == strcmp(n->name, p))
+ return((enum rofft)(n - roffs));
return(ROFF_MAX);
}
r->msg = msg;
r->data = data;
r->rstackpos = -1;
+
+ roff_hash_init();
return(r);
}
ppos = pos;
rr = r->last->rule;
- roffnode_cleanscope(r);
+ roff_cond_text(r, tok, bufp, szp, ln, ppos, pos, offs);
if (ROFF_MAX == (t = roff_parse(*bufp, &pos)))
return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
}
- if (ep > st && '\\' != *(ep - 1))
+ if (ep == st || (ep > st && '\\' != *(ep - 1)))
roffnode_pop(r);
roffnode_cleanscope(r);
static enum rofferr
roff_cond(ROFF_ARGS)
{
+ int cpos; /* position of the condition */
int sv;
/* Stack overflow! */
return(ROFF_ERR);
}
+ cpos = pos;
+
if (ROFF_if == tok || ROFF_ie == tok) {
/*
* Read ahead past the conditional. FIXME: this does
if ( ! roffnode_push(r, tok, ln, ppos))
return(ROFF_ERR);
- /* TODO: here we would evaluate the conditional. */
+ /* XXX: Implement more conditionals. */
- if (ROFF_el == tok) {
+ if (ROFF_if == tok || ROFF_ie == tok)
+ r->last->rule = 'n' == (*bufp)[cpos] ?
+ ROFFRULE_ALLOW : ROFFRULE_DENY;
+ else if (ROFF_el == tok) {
/*
* An `.el' will get the value of the current rstack
* entry set in prior `ie' calls or defaults to DENY.
r->last->rule = ROFFRULE_DENY;
else
r->last->rule = r->rstack[r->rstackpos];
- } else if (ROFF_ie == tok) {
+ }
+ if (ROFF_ie == tok) {
/*
* An if-else will put the NEGATION of the current
* evaluated conditional into the stack.
else
r->rstack[r->rstackpos] = ROFFRULE_DENY;
}
+ if (r->last->parent && ROFFRULE_DENY == r->last->parent->rule)
+ r->last->rule = ROFFRULE_DENY;
r->last->endspan = 1;
*offs = pos;
return(ROFF_RERUN);
}
+
+
+/* ARGSUSED */
+static enum rofferr
+roff_line(ROFF_ARGS)
+{
+
+ return(ROFF_IGN);
+}