=================================================================== RCS file: /cvs/mandoc/roff.c,v retrieving revision 1.84 retrieving revision 1.87 diff -u -p -r1.84 -r1.87 --- mandoc/roff.c 2010/05/24 23:54:18 1.84 +++ mandoc/roff.c 2010/06/09 20:00:38 1.87 @@ -1,4 +1,4 @@ -/* $Id: roff.c,v 1.84 2010/05/24 23:54:18 schwarze Exp $ */ +/* $Id: roff.c,v 1.87 2010/06/09 20:00:38 kristaps Exp $ */ /* * Copyright (c) 2010 Kristaps Dzonsons * @@ -19,6 +19,7 @@ #endif #include +#include #include #include #include @@ -91,6 +92,7 @@ struct roffmac { 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); @@ -103,33 +105,66 @@ static enum rofferr roff_cond_text(ROFF_ARGS); static enum rofferr roff_cond_sub(ROFF_ARGS); static enum rofferr roff_line(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 }, - { "ds", roff_line, NULL, NULL, 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 }, - { "rm", roff_line, NULL, NULL, 0 }, - { "tr", roff_line, NULL, NULL, 0 }, - { ".", roff_cblock, NULL, NULL, 0 }, - { "\\}", roff_ccond, NULL, NULL, 0 }, +/* 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 * the nil-terminated string name could be found. @@ -137,14 +172,27 @@ static enum rofft roff_parse(const char *, int *); 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. + */ - for (i = 0; i < (int)ROFF_MAX; i++) - if (0 == strcmp(roffs[i].name, p)) - return((enum rofft)i); + if (p[0] < ASCII_LO || p[0] > ASCII_HI) + return(ROFF_MAX); + 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); } @@ -236,6 +284,8 @@ roff_alloc(const mandocmsg msg, void *data) r->msg = msg; r->data = data; r->rstackpos = -1; + + roff_hash_init(); return(r); } @@ -571,12 +621,22 @@ roff_cond_sub(ROFF_ARGS) { enum rofft t; enum roffrule rr; + struct roffnode *l; ppos = pos; rr = r->last->rule; - roff_cond_text(r, tok, bufp, szp, ln, ppos, pos, offs); + /* + * Clean out scope. If we've closed ourselves, then don't + * continue. + */ + l = r->last; + roffnode_cleanscope(r); + + if (l != r->last) + return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT); + if (ROFF_MAX == (t = roff_parse(*bufp, &pos))) return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT); @@ -616,10 +676,8 @@ roff_cond_text(ROFF_ARGS) return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT); } - if (ep > st && '\\' != *(ep - 1)) { - ep = '\0'; + if (ep == st || (ep > st && '\\' != *(ep - 1))) roffnode_pop(r); - } roffnode_cleanscope(r); return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);