Parser unification: use nice ohashes for all three request and macro tables;
authorschwarze <schwarze@openbsd.org>
Sat, 29 Apr 2017 12:43:55 +0000 (12:43 +0000)
committerschwarze <schwarze@openbsd.org>
Sat, 29 Apr 2017 12:43:55 +0000 (12:43 +0000)
no functional change, minus two source files, minus 200 lines of code.

13 files changed:
usr.bin/mandoc/Makefile
usr.bin/mandoc/libman.h
usr.bin/mandoc/libmandoc.h
usr.bin/mandoc/libmdoc.h
usr.bin/mandoc/man.c
usr.bin/mandoc/man_hash.c [deleted file]
usr.bin/mandoc/mdoc.c
usr.bin/mandoc/mdoc_hash.c [deleted file]
usr.bin/mandoc/mdoc_macro.c
usr.bin/mandoc/mdoc_validate.c
usr.bin/mandoc/read.c
usr.bin/mandoc/roff.c
usr.bin/mandoc/roff.h

index 05d3fa5..d5943c1 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: Makefile,v 1.105 2017/03/03 14:21:41 schwarze Exp $
+#      $OpenBSD: Makefile,v 1.106 2017/04/29 12:43:55 schwarze Exp $
 
 .include <bsd.own.mk>
 
@@ -8,9 +8,9 @@ LDADD   += -lutil -lz
 
 SRCS=  mandoc.c mandoc_aux.c mandoc_ohash.c preconv.c read.c \
        roff.c tbl.c tbl_opts.c tbl_layout.c tbl_data.c eqn.c
-SRCS+= mdoc_macro.c mdoc.c mdoc_hash.c \
+SRCS+= mdoc_macro.c mdoc.c \
        mdoc_argv.c mdoc_state.c mdoc_validate.c att.c msec.c st.c
-SRCS+= man_macro.c man.c man_hash.c man_validate.c
+SRCS+= man_macro.c man.c man_validate.c
 SRCS+= main.c mdoc_term.c tag.c chars.c term.c tree.c man_term.c eqn_term.c
 SRCS+= mdoc_man.c
 SRCS+= html.c mdoc_html.c man_html.c out.c eqn_html.c
@@ -42,9 +42,9 @@ CLEANFILES += man.cgi cgi.o
 # To install, run:     sudo make installcgi
 # After that, read:    man man.cgi.8
 
-LIBMDOC_OBJS = mdoc_argv.o mdoc_hash.o mdoc_macro.o mdoc_state.o \
+LIBMDOC_OBJS = mdoc_argv.o mdoc_macro.o mdoc_state.o \
                mdoc_validate.o mdoc.o att.o st.o
-LIBMAN_OBJS =  man.o man_hash.o man_macro.o man_validate.o
+LIBMAN_OBJS =  man.o man_macro.o man_validate.o
 LIBROFF_OBJS = roff.o eqn.o tbl.o tbl_data.o tbl_layout.o tbl_opts.o
 LIBMANDOC_OBJS = ${LIBMDOC_OBJS} ${LIBMAN_OBJS} ${LIBROFF_OBJS} \
                mandoc.o mandoc_aux.o mandoc_ohash.o \
index be75f97..f3ad8d8 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: libman.h,v 1.55 2017/04/24 23:06:09 schwarze Exp $ */
+/*     $OpenBSD: libman.h,v 1.56 2017/04/29 12:43:55 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
@@ -35,7 +35,6 @@ struct        man_macro {
 extern const struct man_macro *const man_macros;
 
 
-enum roff_tok    man_hash_find(const char *);
 void             man_node_validate(struct roff_man *);
 void             man_state(struct roff_man *, struct roff_node *);
 void             man_unscope(struct roff_man *, const struct roff_node *);
index 4f82e71..41d3172 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: libmandoc.h,v 1.51 2017/02/18 13:43:34 schwarze Exp $ */
+/*     $OpenBSD: libmandoc.h,v 1.52 2017/04/29 12:43:55 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2013, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
@@ -50,11 +50,9 @@ int           mandoc_eos(const char *, size_t);
 int             mandoc_strntoi(const char *, size_t, int);
 const char     *mandoc_a2msec(const char*);
 
-void            mdoc_hash_init(void);
 int             mdoc_parseln(struct roff_man *, int, char *, int);
 void            mdoc_endparse(struct roff_man *);
 
-void            man_hash_init(void);
 int             man_parseln(struct roff_man *, int, char *, int);
 void            man_endparse(struct roff_man *);
 
index 8d267bf..708f3a1 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: libmdoc.h,v 1.81 2017/04/24 23:06:09 schwarze Exp $ */
+/*     $OpenBSD: libmdoc.h,v 1.82 2017/04/29 12:43:55 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2013, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
@@ -76,7 +76,6 @@ void            mdoc_node_relink(struct roff_man *, struct roff_node *);
 void             mdoc_node_validate(struct roff_man *);
 void             mdoc_state(struct roff_man *, struct roff_node *);
 void             mdoc_state_reset(struct roff_man *);
-enum roff_tok    mdoc_hash_find(const char *);
 const char      *mdoc_a2arch(const char *);
 const char      *mdoc_a2att(const char *);
 enum roff_sec    mdoc_a2sec(const char *);
index 8246784..1db9e87 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: man.c,v 1.117 2017/04/24 23:06:09 schwarze Exp $ */
+/*     $OpenBSD: man.c,v 1.118 2017/04/29 12:43:55 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2013, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
@@ -143,26 +143,19 @@ man_pmacro(struct roff_man *man, int ln, char *buf, int offs)
 {
        struct roff_node *n;
        const char      *cp;
-       int              tok;
-       int              i, ppos;
+       size_t           sz;
+       enum roff_tok    tok;
+       int              ppos;
        int              bline;
-       char             mac[5];
 
-       ppos = offs;
-
-       /*
-        * Copy the first word into a nil-terminated buffer.
-        * Stop when a space, tab, escape, or eoln is encountered.
-        */
-
-       i = 0;
-       while (i < 4 && strchr(" \t\\", buf[offs]) == NULL)
-               mac[i++] = buf[offs++];
-
-       mac[i] = '\0';
-
-       tok = (i > 0 && i < 4) ? man_hash_find(mac) : TOKEN_NONE;
+       /* Determine the line macro. */
 
+       ppos = offs;
+       tok = TOKEN_NONE;
+       for (sz = 0; sz < 4 && strchr(" \t\\", buf[offs]) == NULL; sz++)
+               offs++;
+       if (sz > 0 && sz < 4)
+               tok = roffhash_find(man->manmac, buf + ppos, sz);
        if (tok == TOKEN_NONE) {
                mandoc_msg(MANDOCERR_MACRO, man->parse,
                    ln, ppos, buf + ppos - 1);
diff --git a/usr.bin/mandoc/man_hash.c b/usr.bin/mandoc/man_hash.c
deleted file mode 100644 (file)
index 8b592f0..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-/*     $OpenBSD: man_hash.c,v 1.26 2017/04/24 23:06:09 schwarze Exp $ */
-/*
- * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#include <sys/types.h>
-
-#include <assert.h>
-#include <ctype.h>
-#include <limits.h>
-#include <string.h>
-
-#include "mandoc.h"
-#include "roff.h"
-#include "man.h"
-#include "libmandoc.h"
-#include "libman.h"
-
-#define        HASH_DEPTH       6
-
-#define        HASH_ROW(x) do { \
-               if (isupper((unsigned char)(x))) \
-                       (x) -= 65; \
-               else \
-                       (x) -= 97; \
-               (x) *= HASH_DEPTH; \
-       } while (/* CONSTCOND */ 0)
-
-/*
- * Lookup table is indexed first by lower-case first letter (plus one
- * for the period, which is stored in the last row), then by lower or
- * uppercase second letter.  Buckets correspond to the index of the
- * macro (the integer value of the enum stored as a char to save a bit
- * of space).
- */
-static unsigned char    table[26 * HASH_DEPTH];
-
-
-void
-man_hash_init(void)
-{
-       int              i, j, x;
-
-       if (*table != '\0')
-               return;
-
-       memset(table, UCHAR_MAX, sizeof(table));
-
-       for (i = 0; i < (int)(MAN_MAX - MAN_TH); i++) {
-               x = *roff_name[MAN_TH + i];
-
-               assert(isalpha((unsigned char)x));
-
-               HASH_ROW(x);
-
-               for (j = 0; j < HASH_DEPTH; j++)
-                       if (UCHAR_MAX == table[x + j]) {
-                               table[x + j] = (unsigned char)i;
-                               break;
-                       }
-
-               assert(j < HASH_DEPTH);
-       }
-}
-
-enum roff_tok
-man_hash_find(const char *tmp)
-{
-       int              x, y, i;
-
-       if ('\0' == (x = tmp[0]))
-               return TOKEN_NONE;
-       if ( ! (isalpha((unsigned char)x)))
-               return TOKEN_NONE;
-
-       HASH_ROW(x);
-
-       for (i = 0; i < HASH_DEPTH; i++) {
-               if (UCHAR_MAX == (y = table[x + i]))
-                       return TOKEN_NONE;
-
-               if (strcmp(tmp, roff_name[MAN_TH + y]) == 0)
-                       return MAN_TH + y;
-       }
-
-       return TOKEN_NONE;
-}
index e3e4d89..7ab4487 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: mdoc.c,v 1.151 2017/04/24 23:06:09 schwarze Exp $ */
+/*     $OpenBSD: mdoc.c,v 1.152 2017/04/29 12:43:55 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2010, 2012-2017 Ingo Schwarze <schwarze@openbsd.org>
@@ -300,25 +300,18 @@ mdoc_pmacro(struct roff_man *mdoc, int ln, char *buf, int offs)
 {
        struct roff_node *n;
        const char       *cp;
+       size_t            sz;
        enum roff_tok     tok;
-       int               i, sv;
-       char              mac[5];
+       int               sv;
 
-       sv = offs;
-
-       /*
-        * Copy the first word into a nil-terminated buffer.
-        * Stop when a space, tab, escape, or eoln is encountered.
-        */
-
-       i = 0;
-       while (i < 4 && strchr(" \t\\", buf[offs]) == NULL)
-               mac[i++] = buf[offs++];
-
-       mac[i] = '\0';
-
-       tok = (i > 1 && i < 4) ? mdoc_hash_find(mac) : TOKEN_NONE;
+       /* Determine the line macro. */
 
+       sv = offs;
+       tok = TOKEN_NONE;
+       for (sz = 0; sz < 4 && strchr(" \t\\", buf[offs]) == NULL; sz++)
+               offs++;
+       if (sz == 2 || sz == 3)
+               tok = roffhash_find(mdoc->mdocmac, buf + sv, sz);
        if (tok == TOKEN_NONE) {
                mandoc_msg(MANDOCERR_MACRO, mdoc->parse,
                    ln, sv, buf + sv - 1);
diff --git a/usr.bin/mandoc/mdoc_hash.c b/usr.bin/mandoc/mdoc_hash.c
deleted file mode 100644 (file)
index 375cd45..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-/*     $OpenBSD: mdoc_hash.c,v 1.23 2017/04/24 23:06:09 schwarze Exp $ */
-/*
- * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#include <sys/types.h>
-
-#include <assert.h>
-#include <ctype.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "mandoc.h"
-#include "roff.h"
-#include "mdoc.h"
-#include "libmandoc.h"
-#include "libmdoc.h"
-
-static unsigned char    table[27 * 12];
-
-
-void
-mdoc_hash_init(void)
-{
-       int              i, j, major;
-       const char      *p;
-
-       if (*table != '\0')
-               return;
-
-       memset(table, UCHAR_MAX, sizeof(table));
-
-       for (i = 0; i < (int)(MDOC_MAX - MDOC_Dd); i++) {
-               p = roff_name[MDOC_Dd + i];
-
-               if (isalpha((unsigned char)p[1]))
-                       major = 12 * (tolower((unsigned char)p[1]) - 97);
-               else
-                       major = 12 * 26;
-
-               for (j = 0; j < 12; j++)
-                       if (UCHAR_MAX == table[major + j]) {
-                               table[major + j] = (unsigned char)i;
-                               break;
-                       }
-
-               assert(j < 12);
-       }
-}
-
-enum roff_tok
-mdoc_hash_find(const char *p)
-{
-       int               major, i, j;
-
-       if (0 == p[0])
-               return TOKEN_NONE;
-       if ( ! isalpha((unsigned char)p[0]) && '%' != p[0])
-               return TOKEN_NONE;
-
-       if (isalpha((unsigned char)p[1]))
-               major = 12 * (tolower((unsigned char)p[1]) - 97);
-       else if ('1' == p[1])
-               major = 12 * 26;
-       else
-               return TOKEN_NONE;
-
-       if (p[2] && p[3])
-               return TOKEN_NONE;
-
-       for (j = 0; j < 12; j++) {
-               if (UCHAR_MAX == (i = table[major + j]))
-                       break;
-               if (strcmp(p, roff_name[MDOC_Dd + i]) == 0)
-                       return MDOC_Dd + i;
-       }
-
-       return TOKEN_NONE;
-}
index 5af20a7..9a9c777 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: mdoc_macro.c,v 1.176 2017/04/24 23:06:09 schwarze Exp $ */
+/*     $OpenBSD: mdoc_macro.c,v 1.177 2017/04/29 12:43:55 schwarze Exp $ */
 /*
  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2010, 2012-2017 Ingo Schwarze <schwarze@openbsd.org>
@@ -245,7 +245,7 @@ lookup(struct roff_man *mdoc, int from, int line, int ppos, const char *p)
                return TOKEN_NONE;
        }
        if (from == TOKEN_NONE || mdoc_macros[from].flags & MDOC_PARSED) {
-               res = mdoc_hash_find(p);
+               res = roffhash_find(mdoc->mdocmac, p, 0);
                if (res != TOKEN_NONE) {
                        if (mdoc_macros[res].flags & MDOC_CALLABLE)
                                return res;
index c699492..2450bfb 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: mdoc_validate.c,v 1.237 2017/04/28 16:23:30 schwarze Exp $ */
+/*     $OpenBSD: mdoc_validate.c,v 1.238 2017/04/29 12:43:55 schwarze Exp $ */
 /*
  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org>
@@ -56,7 +56,7 @@ static        void     check_argv(struct roff_man *,
 static void     check_args(struct roff_man *, struct roff_node *);
 static int      child_an(const struct roff_node *);
 static size_t          macro2len(enum roff_tok);
-static void     rewrite_macro2len(char **);
+static void     rewrite_macro2len(struct roff_man *, char **);
 
 static void     post_an(POST_ARGS);
 static void     post_an_norm(POST_ARGS);
@@ -449,7 +449,7 @@ post_bl_norm(POST_ARGS)
                                    mdoc->parse, argv->line,
                                    argv->pos, "Bl -width %s",
                                    argv->value[0]);
-                       rewrite_macro2len(argv->value);
+                       rewrite_macro2len(mdoc, argv->value);
                        n->norm->Bl.width = argv->value[0];
                        break;
                case MDOC_Offset:
@@ -464,7 +464,7 @@ post_bl_norm(POST_ARGS)
                                    mdoc->parse, argv->line,
                                    argv->pos, "Bl -offset %s",
                                    argv->value[0]);
-                       rewrite_macro2len(argv->value);
+                       rewrite_macro2len(mdoc, argv->value);
                        n->norm->Bl.offs = argv->value[0];
                        break;
                default:
@@ -591,7 +591,7 @@ post_bd(POST_ARGS)
                                    mdoc->parse, argv->line,
                                    argv->pos, "Bd -offset %s",
                                    argv->value[0]);
-                       rewrite_macro2len(argv->value);
+                       rewrite_macro2len(mdoc, argv->value);
                        n->norm->Bd.offs = argv->value[0];
                        break;
                case MDOC_Compact:
@@ -1326,8 +1326,8 @@ post_bl_block(POST_ARGS)
  * If the argument of -offset or -width is a macro,
  * replace it with the associated default width.
  */
-void
-rewrite_macro2len(char **arg)
+static void
+rewrite_macro2len(struct roff_man *mdoc, char **arg)
 {
        size_t            width;
        enum roff_tok     tok;
@@ -1336,7 +1336,7 @@ rewrite_macro2len(char **arg)
                return;
        else if ( ! strcmp(*arg, "Ds"))
                width = 6;
-       else if ((tok = mdoc_hash_find(*arg)) == TOKEN_NONE)
+       else if ((tok = roffhash_find(mdoc->mdocmac, *arg, 0)) == TOKEN_NONE)
                return;
        else
                width = macro2len(tok);
index f316ba3..fe2aad9 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: read.c,v 1.135 2017/03/07 20:00:02 schwarze Exp $ */
+/*     $OpenBSD: read.c,v 1.136 2017/04/29 12:43:55 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org>
@@ -289,14 +289,15 @@ choose_parser(struct mparse *curp)
        }
 
        if (format == MPARSE_MDOC) {
-               mdoc_hash_init();
                curp->man->macroset = MACROSET_MDOC;
-               curp->man->first->tok = TOKEN_NONE;
+               if (curp->man->mdocmac == NULL)
+                       curp->man->mdocmac = roffhash_alloc(MDOC_Dd, MDOC_MAX);
        } else {
-               man_hash_init();
                curp->man->macroset = MACROSET_MAN;
-               curp->man->first->tok = TOKEN_NONE;
+               if (curp->man->manmac == NULL)
+                       curp->man->manmac = roffhash_alloc(MAN_TH, MAN_MAX);
        }
+       curp->man->first->tok = TOKEN_NONE;
 }
 
 /*
@@ -801,11 +802,13 @@ mparse_alloc(int options, enum mandoclevel wlevel, mandocmsg mmsg,
        curp->man = roff_man_alloc( curp->roff, curp, curp->defos,
                curp->options & MPARSE_QUICK ? 1 : 0);
        if (curp->options & MPARSE_MDOC) {
-               mdoc_hash_init();
                curp->man->macroset = MACROSET_MDOC;
+               if (curp->man->mdocmac == NULL)
+                       curp->man->mdocmac = roffhash_alloc(MDOC_Dd, MDOC_MAX);
        } else if (curp->options & MPARSE_MAN) {
-               man_hash_init();
                curp->man->macroset = MACROSET_MAN;
+               if (curp->man->manmac == NULL)
+                       curp->man->manmac = roffhash_alloc(MAN_TH, MAN_MAX);
        }
        curp->man->first->tok = TOKEN_NONE;
        return curp;
@@ -831,6 +834,8 @@ void
 mparse_free(struct mparse *curp)
 {
 
+       roffhash_free(curp->man->mdocmac);
+       roffhash_free(curp->man->manmac);
        roff_man_free(curp->man);
        roff_free(curp->roff);
        if (curp->secondary)
index be902c3..5ca2cbf 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: roff.c,v 1.166 2017/04/24 23:06:09 schwarze Exp $ */
+/*     $OpenBSD: roff.c,v 1.167 2017/04/29 12:43:55 schwarze Exp $ */
 /*
  * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2010-2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
 #include <assert.h>
 #include <ctype.h>
 #include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "mandoc.h"
 #include "mandoc_aux.h"
+#include "mandoc_ohash.h"
 #include "roff.h"
 #include "libmandoc.h"
 #include "roff_int.h"
@@ -62,10 +65,19 @@ struct      roffreg {
        struct roffreg  *next;
 };
 
+/*
+ * Association of request and macro names with token IDs.
+ */
+struct roffreq {
+       enum roff_tok    tok;
+       char             name[];
+};
+
 struct roff {
        struct mparse   *parse; /* parse point */
        struct roffnode *last; /* leaf of stack */
        int             *rstack; /* stack of inverted `ie' values */
+       struct ohash    *reqtab; /* request lookup table */
        struct roffreg  *regtab; /* number registers */
        struct roffkv   *strtab; /* user-defined strings & macros */
        struct roffkv   *xmbtab; /* multi-byte trans table (`tr') */
@@ -108,13 +120,11 @@ struct    roffnode {
 typedef        enum rofferr (*roffproc)(ROFF_ARGS);
 
 struct roffmac {
-       const char      *name; /* macro name */
        roffproc         proc; /* process new macro */
        roffproc         text; /* process as child text of macro */
        roffproc         sub; /* process as child of macro */
        int              flags;
 #define        ROFFMAC_STRUCT  (1 << 0) /* always interpret */
-       struct roffmac  *next;
 };
 
 struct predef {
@@ -127,8 +137,6 @@ struct      predef {
 
 /* --- function prototypes ------------------------------------------------ */
 
-static enum roff_tok    roffhash_find(const char *, size_t);
-static void             roffhash_init(void);
 static void             roffnode_cleanscope(struct roff *);
 static void             roffnode_pop(struct roff *);
 static void             roffnode_push(struct roff *, enum roff_tok,
@@ -195,12 +203,6 @@ static     enum rofferr     roff_userdef(ROFF_ARGS);
 
 /* --- constant data ------------------------------------------------------ */
 
-/* See roffhash_find() */
-
-#define        ASCII_HI         126
-#define        ASCII_LO         33
-#define        HASHWIDTH       (ASCII_HI - ASCII_LO + 1)
-
 #define        ROFFNUM_SCALE   (1 << 0)  /* Honour scaling in roff_getnum(). */
 #define        ROFFNUM_WHITE   (1 << 1)  /* Skip whitespace in roff_evalnum(). */
 
@@ -309,245 +311,243 @@ const char *__roff_name[MAN_MAX + 1] = {
 };
 const  char *const *roff_name = __roff_name;
 
-static struct roffmac  *hash[HASHWIDTH];
-
 static struct roffmac   roffs[TOKEN_NONE] = {
-       { "ab", roff_unsupp, NULL, NULL, 0, NULL },
-       { "ad", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "af", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "aln", roff_unsupp, NULL, NULL, 0, NULL },
-       { "als", roff_unsupp, NULL, NULL, 0, NULL },
-       { "am", roff_block, roff_block_text, roff_block_sub, 0, NULL },
-       { "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
-       { "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL },
-       { "ami1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
-       { "as", roff_ds, NULL, NULL, 0, NULL },
-       { "as1", roff_ds, NULL, NULL, 0, NULL },
-       { "asciify", roff_unsupp, NULL, NULL, 0, NULL },
-       { "backtrace", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "bd", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "bleedat", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "blm", roff_unsupp, NULL, NULL, 0, NULL },
-       { "box", roff_unsupp, NULL, NULL, 0, NULL },
-       { "boxa", roff_unsupp, NULL, NULL, 0, NULL },
-       { "bp", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "BP", roff_unsupp, NULL, NULL, 0, NULL },
-       { "break", roff_unsupp, NULL, NULL, 0, NULL },
-       { "breakchar", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "brnl", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "brp", roff_brp, NULL, NULL, 0, NULL },
-       { "brpnl", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "c2", roff_unsupp, NULL, NULL, 0, NULL },
-       { "cc", roff_cc, NULL, NULL, 0, NULL },
-       { "ce", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "cf", roff_insec, NULL, NULL, 0, NULL },
-       { "cflags", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "ch", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "char", roff_unsupp, NULL, NULL, 0, NULL },
-       { "chop", roff_unsupp, NULL, NULL, 0, NULL },
-       { "class", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "close", roff_insec, NULL, NULL, 0, NULL },
-       { "CL", roff_unsupp, NULL, NULL, 0, NULL },
-       { "color", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "composite", roff_unsupp, NULL, NULL, 0, NULL },
-       { "continue", roff_unsupp, NULL, NULL, 0, NULL },
-       { "cp", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "cropat", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "cs", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "cu", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "da", roff_unsupp, NULL, NULL, 0, NULL },
-       { "dch", roff_unsupp, NULL, NULL, 0, NULL },
-       { "Dd", roff_Dd, NULL, NULL, 0, NULL },
-       { "de", roff_block, roff_block_text, roff_block_sub, 0, NULL },
-       { "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
-       { "defcolor", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL },
-       { "dei1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
-       { "device", roff_unsupp, NULL, NULL, 0, NULL },
-       { "devicem", roff_unsupp, NULL, NULL, 0, NULL },
-       { "di", roff_unsupp, NULL, NULL, 0, NULL },
-       { "do", roff_unsupp, NULL, NULL, 0, NULL },
-       { "ds", roff_ds, NULL, NULL, 0, NULL },
-       { "ds1", roff_ds, NULL, NULL, 0, NULL },
-       { "dwh", roff_unsupp, NULL, NULL, 0, NULL },
-       { "dt", roff_unsupp, NULL, NULL, 0, NULL },
-       { "ec", roff_unsupp, NULL, NULL, 0, NULL },
-       { "ecr", roff_unsupp, NULL, NULL, 0, NULL },
-       { "ecs", roff_unsupp, NULL, NULL, 0, NULL },
-       { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
-       { "em", roff_unsupp, NULL, NULL, 0, NULL },
-       { "EN", roff_EN, NULL, NULL, 0, NULL },
-       { "eo", roff_unsupp, NULL, NULL, 0, NULL },
-       { "EP", roff_unsupp, NULL, NULL, 0, NULL },
-       { "EQ", roff_EQ, NULL, NULL, 0, NULL },
-       { "errprint", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "ev", roff_unsupp, NULL, NULL, 0, NULL },
-       { "evc", roff_unsupp, NULL, NULL, 0, NULL },
-       { "ex", roff_unsupp, NULL, NULL, 0, NULL },
-       { "fallback", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "fam", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "fc", roff_unsupp, NULL, NULL, 0, NULL },
-       { "fchar", roff_unsupp, NULL, NULL, 0, NULL },
-       { "fcolor", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "fdeferlig", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "feature", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "fkern", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "fl", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "flig", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "fp", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "fps", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "fschar", roff_unsupp, NULL, NULL, 0, NULL },
-       { "fspacewidth", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "fspecial", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "ftr", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "fzoom", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "gcolor", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "hc", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "hcode", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "hidechar", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "hla", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "hlm", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "hpf", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "hpfa", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "hpfcode", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "hw", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "hy", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "hylang", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "hylen", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "hym", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "hypp", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "hys", roff_line_ignore, NULL, NULL, 0, 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 },
-       { "index", roff_unsupp, NULL, NULL, 0, NULL },
-       { "it", roff_it, NULL, NULL, 0, NULL },
-       { "itc", roff_unsupp, NULL, NULL, 0, NULL },
-       { "IX", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "kern", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "kernafter", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "kernbefore", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "kernpair", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "lc", roff_unsupp, NULL, NULL, 0, NULL },
-       { "lc_ctype", roff_unsupp, NULL, NULL, 0, NULL },
-       { "lds", roff_unsupp, NULL, NULL, 0, NULL },
-       { "length", roff_unsupp, NULL, NULL, 0, NULL },
-       { "letadj", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "lf", roff_insec, NULL, NULL, 0, NULL },
-       { "lg", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "lhang", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "linetabs", roff_unsupp, NULL, NULL, 0, NULL },
-       { "lnr", roff_unsupp, NULL, NULL, 0, NULL },
-       { "lnrf", roff_unsupp, NULL, NULL, 0, NULL },
-       { "lpfx", roff_unsupp, NULL, NULL, 0, NULL },
-       { "ls", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "lsm", roff_unsupp, NULL, NULL, 0, NULL },
-       { "lt", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "mc", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "mediasize", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "minss", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "mk", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "mso", roff_insec, NULL, NULL, 0, NULL },
-       { "na", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "ne", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "nh", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "nhychar", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "nm", roff_unsupp, NULL, NULL, 0, NULL },
-       { "nn", roff_unsupp, NULL, NULL, 0, NULL },
-       { "nop", roff_unsupp, NULL, NULL, 0, NULL },
-       { "nr", roff_nr, NULL, NULL, 0, NULL },
-       { "nrf", roff_unsupp, NULL, NULL, 0, NULL },
-       { "nroff", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "ns", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "nx", roff_insec, NULL, NULL, 0, NULL },
-       { "open", roff_insec, NULL, NULL, 0, NULL },
-       { "opena", roff_insec, NULL, NULL, 0, NULL },
-       { "os", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "output", roff_unsupp, NULL, NULL, 0, NULL },
-       { "padj", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "papersize", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "pc", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "pev", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "pi", roff_insec, NULL, NULL, 0, NULL },
-       { "PI", roff_unsupp, NULL, NULL, 0, NULL },
-       { "pl", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "pm", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "pn", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "pnr", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "po", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "ps", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "psbb", roff_unsupp, NULL, NULL, 0, NULL },
-       { "pshape", roff_unsupp, NULL, NULL, 0, NULL },
-       { "pso", roff_insec, NULL, NULL, 0, NULL },
-       { "ptr", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "pvs", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "rchar", roff_unsupp, NULL, NULL, 0, NULL },
-       { "rd", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "recursionlimit", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "return", roff_unsupp, NULL, NULL, 0, NULL },
-       { "rfschar", roff_unsupp, NULL, NULL, 0, NULL },
-       { "rhang", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "rj", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "rm", roff_rm, NULL, NULL, 0, NULL },
-       { "rn", roff_unsupp, NULL, NULL, 0, NULL },
-       { "rnn", roff_unsupp, NULL, NULL, 0, NULL },
-       { "rr", roff_rr, NULL, NULL, 0, NULL },
-       { "rs", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "rt", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "schar", roff_unsupp, NULL, NULL, 0, NULL },
-       { "sentchar", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "shc", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "shift", roff_unsupp, NULL, NULL, 0, NULL },
-       { "sizes", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "so", roff_so, NULL, NULL, 0, NULL },
-       { "spacewidth", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "special", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "spreadwarn", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "ss", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "sty", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "substring", roff_unsupp, NULL, NULL, 0, NULL },
-       { "sv", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "sy", roff_insec, NULL, NULL, 0, NULL },
-       { "T&", roff_T_, NULL, NULL, 0, NULL },
-       { "ta", roff_unsupp, NULL, NULL, 0, NULL },
-       { "tc", roff_unsupp, NULL, NULL, 0, NULL },
-       { "TE", roff_TE, NULL, NULL, 0, NULL },
-       { "TH", roff_TH, NULL, NULL, 0, NULL },
-       { "ti", roff_unsupp, NULL, NULL, 0, NULL },
-       { "tkf", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "tl", roff_unsupp, NULL, NULL, 0, NULL },
-       { "tm", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "tm1", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "tmc", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "tr", roff_tr, NULL, NULL, 0, NULL },
-       { "track", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "transchar", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "trf", roff_insec, NULL, NULL, 0, NULL },
-       { "trimat", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "trin", roff_unsupp, NULL, NULL, 0, NULL },
-       { "trnt", roff_unsupp, NULL, NULL, 0, NULL },
-       { "troff", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "TS", roff_TS, NULL, NULL, 0, NULL },
-       { "uf", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "ul", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "unformat", roff_unsupp, NULL, NULL, 0, NULL },
-       { "unwatch", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "unwatchn", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "vpt", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "vs", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "warn", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "warnscale", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "watch", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "watchlength", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "watchn", roff_line_ignore, NULL, NULL, 0, NULL },
-       { "wh", roff_unsupp, NULL, NULL, 0, NULL },
-       { "while", roff_unsupp, NULL, NULL, 0, NULL },
-       { "write", roff_insec, NULL, NULL, 0, NULL },
-       { "writec", roff_insec, NULL, NULL, 0, NULL },
-       { "writem", roff_insec, NULL, NULL, 0, NULL },
-       { "xflag", roff_line_ignore, NULL, NULL, 0, NULL },
-       { ".", roff_cblock, NULL, NULL, 0, NULL },
-       { NULL, roff_userdef, NULL, NULL, 0, NULL },
+       { roff_unsupp, NULL, NULL, 0 },  /* ab */
+       { roff_line_ignore, NULL, NULL, 0 },  /* ad */
+       { roff_line_ignore, NULL, NULL, 0 },  /* af */
+       { roff_unsupp, NULL, NULL, 0 },  /* aln */
+       { roff_unsupp, NULL, NULL, 0 },  /* als */
+       { roff_block, roff_block_text, roff_block_sub, 0 },  /* am */
+       { roff_block, roff_block_text, roff_block_sub, 0 },  /* am1 */
+       { roff_block, roff_block_text, roff_block_sub, 0 },  /* ami */
+       { roff_block, roff_block_text, roff_block_sub, 0 },  /* ami1 */
+       { roff_ds, NULL, NULL, 0 },  /* as */
+       { roff_ds, NULL, NULL, 0 },  /* as1 */
+       { roff_unsupp, NULL, NULL, 0 },  /* asciify */
+       { roff_line_ignore, NULL, NULL, 0 },  /* backtrace */
+       { roff_line_ignore, NULL, NULL, 0 },  /* bd */
+       { roff_line_ignore, NULL, NULL, 0 },  /* bleedat */
+       { roff_unsupp, NULL, NULL, 0 },  /* blm */
+       { roff_unsupp, NULL, NULL, 0 },  /* box */
+       { roff_unsupp, NULL, NULL, 0 },  /* boxa */
+       { roff_line_ignore, NULL, NULL, 0 },  /* bp */
+       { roff_unsupp, NULL, NULL, 0 },  /* BP */
+       { roff_unsupp, NULL, NULL, 0 },  /* break */
+       { roff_line_ignore, NULL, NULL, 0 },  /* breakchar */
+       { roff_line_ignore, NULL, NULL, 0 },  /* brnl */
+       { roff_brp, NULL, NULL, 0 },  /* brp */
+       { roff_line_ignore, NULL, NULL, 0 },  /* brpnl */
+       { roff_unsupp, NULL, NULL, 0 },  /* c2 */
+       { roff_cc, NULL, NULL, 0 },  /* cc */
+       { roff_line_ignore, NULL, NULL, 0 },  /* ce */
+       { roff_insec, NULL, NULL, 0 },  /* cf */
+       { roff_line_ignore, NULL, NULL, 0 },  /* cflags */
+       { roff_line_ignore, NULL, NULL, 0 },  /* ch */
+       { roff_unsupp, NULL, NULL, 0 },  /* char */
+       { roff_unsupp, NULL, NULL, 0 },  /* chop */
+       { roff_line_ignore, NULL, NULL, 0 },  /* class */
+       { roff_insec, NULL, NULL, 0 },  /* close */
+       { roff_unsupp, NULL, NULL, 0 },  /* CL */
+       { roff_line_ignore, NULL, NULL, 0 },  /* color */
+       { roff_unsupp, NULL, NULL, 0 },  /* composite */
+       { roff_unsupp, NULL, NULL, 0 },  /* continue */
+       { roff_line_ignore, NULL, NULL, 0 },  /* cp */
+       { roff_line_ignore, NULL, NULL, 0 },  /* cropat */
+       { roff_line_ignore, NULL, NULL, 0 },  /* cs */
+       { roff_line_ignore, NULL, NULL, 0 },  /* cu */
+       { roff_unsupp, NULL, NULL, 0 },  /* da */
+       { roff_unsupp, NULL, NULL, 0 },  /* dch */
+       { roff_Dd, NULL, NULL, 0 },  /* Dd */
+       { roff_block, roff_block_text, roff_block_sub, 0 },  /* de */
+       { roff_block, roff_block_text, roff_block_sub, 0 },  /* de1 */
+       { roff_line_ignore, NULL, NULL, 0 },  /* defcolor */
+       { roff_block, roff_block_text, roff_block_sub, 0 },  /* dei */
+       { roff_block, roff_block_text, roff_block_sub, 0 },  /* dei1 */
+       { roff_unsupp, NULL, NULL, 0 },  /* device */
+       { roff_unsupp, NULL, NULL, 0 },  /* devicem */
+       { roff_unsupp, NULL, NULL, 0 },  /* di */
+       { roff_unsupp, NULL, NULL, 0 },  /* do */
+       { roff_ds, NULL, NULL, 0 },  /* ds */
+       { roff_ds, NULL, NULL, 0 },  /* ds1 */
+       { roff_unsupp, NULL, NULL, 0 },  /* dwh */
+       { roff_unsupp, NULL, NULL, 0 },  /* dt */
+       { roff_unsupp, NULL, NULL, 0 },  /* ec */
+       { roff_unsupp, NULL, NULL, 0 },  /* ecr */
+       { roff_unsupp, NULL, NULL, 0 },  /* ecs */
+       { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },  /* el */
+       { roff_unsupp, NULL, NULL, 0 },  /* em */
+       { roff_EN, NULL, NULL, 0 },  /* EN */
+       { roff_unsupp, NULL, NULL, 0 },  /* eo */
+       { roff_unsupp, NULL, NULL, 0 },  /* EP */
+       { roff_EQ, NULL, NULL, 0 },  /* EQ */
+       { roff_line_ignore, NULL, NULL, 0 },  /* errprint */
+       { roff_unsupp, NULL, NULL, 0 },  /* ev */
+       { roff_unsupp, NULL, NULL, 0 },  /* evc */
+       { roff_unsupp, NULL, NULL, 0 },  /* ex */
+       { roff_line_ignore, NULL, NULL, 0 },  /* fallback */
+       { roff_line_ignore, NULL, NULL, 0 },  /* fam */
+       { roff_unsupp, NULL, NULL, 0 },  /* fc */
+       { roff_unsupp, NULL, NULL, 0 },  /* fchar */
+       { roff_line_ignore, NULL, NULL, 0 },  /* fcolor */
+       { roff_line_ignore, NULL, NULL, 0 },  /* fdeferlig */
+       { roff_line_ignore, NULL, NULL, 0 },  /* feature */
+       { roff_line_ignore, NULL, NULL, 0 },  /* fkern */
+       { roff_line_ignore, NULL, NULL, 0 },  /* fl */
+       { roff_line_ignore, NULL, NULL, 0 },  /* flig */
+       { roff_line_ignore, NULL, NULL, 0 },  /* fp */
+       { roff_line_ignore, NULL, NULL, 0 },  /* fps */
+       { roff_unsupp, NULL, NULL, 0 },  /* fschar */
+       { roff_line_ignore, NULL, NULL, 0 },  /* fspacewidth */
+       { roff_line_ignore, NULL, NULL, 0 },  /* fspecial */
+       { roff_line_ignore, NULL, NULL, 0 },  /* ftr */
+       { roff_line_ignore, NULL, NULL, 0 },  /* fzoom */
+       { roff_line_ignore, NULL, NULL, 0 },  /* gcolor */
+       { roff_line_ignore, NULL, NULL, 0 },  /* hc */
+       { roff_line_ignore, NULL, NULL, 0 },  /* hcode */
+       { roff_line_ignore, NULL, NULL, 0 },  /* hidechar */
+       { roff_line_ignore, NULL, NULL, 0 },  /* hla */
+       { roff_line_ignore, NULL, NULL, 0 },  /* hlm */
+       { roff_line_ignore, NULL, NULL, 0 },  /* hpf */
+       { roff_line_ignore, NULL, NULL, 0 },  /* hpfa */
+       { roff_line_ignore, NULL, NULL, 0 },  /* hpfcode */
+       { roff_line_ignore, NULL, NULL, 0 },  /* hw */
+       { roff_line_ignore, NULL, NULL, 0 },  /* hy */
+       { roff_line_ignore, NULL, NULL, 0 },  /* hylang */
+       { roff_line_ignore, NULL, NULL, 0 },  /* hylen */
+       { roff_line_ignore, NULL, NULL, 0 },  /* hym */
+       { roff_line_ignore, NULL, NULL, 0 },  /* hypp */
+       { roff_line_ignore, NULL, NULL, 0 },  /* hys */
+       { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },  /* ie */
+       { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },  /* if */
+       { roff_block, roff_block_text, roff_block_sub, 0 },  /* ig */
+       { roff_unsupp, NULL, NULL, 0 },  /* index */
+       { roff_it, NULL, NULL, 0 },  /* it */
+       { roff_unsupp, NULL, NULL, 0 },  /* itc */
+       { roff_line_ignore, NULL, NULL, 0 },  /* IX */
+       { roff_line_ignore, NULL, NULL, 0 },  /* kern */
+       { roff_line_ignore, NULL, NULL, 0 },  /* kernafter */
+       { roff_line_ignore, NULL, NULL, 0 },  /* kernbefore */
+       { roff_line_ignore, NULL, NULL, 0 },  /* kernpair */
+       { roff_unsupp, NULL, NULL, 0 },  /* lc */
+       { roff_unsupp, NULL, NULL, 0 },  /* lc_ctype */
+       { roff_unsupp, NULL, NULL, 0 },  /* lds */
+       { roff_unsupp, NULL, NULL, 0 },  /* length */
+       { roff_line_ignore, NULL, NULL, 0 },  /* letadj */
+       { roff_insec, NULL, NULL, 0 },  /* lf */
+       { roff_line_ignore, NULL, NULL, 0 },  /* lg */
+       { roff_line_ignore, NULL, NULL, 0 },  /* lhang */
+       { roff_unsupp, NULL, NULL, 0 },  /* linetabs */
+       { roff_unsupp, NULL, NULL, 0 },  /* lnr */
+       { roff_unsupp, NULL, NULL, 0 },  /* lnrf */
+       { roff_unsupp, NULL, NULL, 0 },  /* lpfx */
+       { roff_line_ignore, NULL, NULL, 0 },  /* ls */
+       { roff_unsupp, NULL, NULL, 0 },  /* lsm */
+       { roff_line_ignore, NULL, NULL, 0 },  /* lt */
+       { roff_line_ignore, NULL, NULL, 0 },  /* mc */
+       { roff_line_ignore, NULL, NULL, 0 },  /* mediasize */
+       { roff_line_ignore, NULL, NULL, 0 },  /* minss */
+       { roff_line_ignore, NULL, NULL, 0 },  /* mk */
+       { roff_insec, NULL, NULL, 0 },  /* mso */
+       { roff_line_ignore, NULL, NULL, 0 },  /* na */
+       { roff_line_ignore, NULL, NULL, 0 },  /* ne */
+       { roff_line_ignore, NULL, NULL, 0 },  /* nh */
+       { roff_line_ignore, NULL, NULL, 0 },  /* nhychar */
+       { roff_unsupp, NULL, NULL, 0 },  /* nm */
+       { roff_unsupp, NULL, NULL, 0 },  /* nn */
+       { roff_unsupp, NULL, NULL, 0 },  /* nop */
+       { roff_nr, NULL, NULL, 0 },  /* nr */
+       { roff_unsupp, NULL, NULL, 0 },  /* nrf */
+       { roff_line_ignore, NULL, NULL, 0 },  /* nroff */
+       { roff_line_ignore, NULL, NULL, 0 },  /* ns */
+       { roff_insec, NULL, NULL, 0 },  /* nx */
+       { roff_insec, NULL, NULL, 0 },  /* open */
+       { roff_insec, NULL, NULL, 0 },  /* opena */
+       { roff_line_ignore, NULL, NULL, 0 },  /* os */
+       { roff_unsupp, NULL, NULL, 0 },  /* output */
+       { roff_line_ignore, NULL, NULL, 0 },  /* padj */
+       { roff_line_ignore, NULL, NULL, 0 },  /* papersize */
+       { roff_line_ignore, NULL, NULL, 0 },  /* pc */
+       { roff_line_ignore, NULL, NULL, 0 },  /* pev */
+       { roff_insec, NULL, NULL, 0 },  /* pi */
+       { roff_unsupp, NULL, NULL, 0 },  /* PI */
+       { roff_line_ignore, NULL, NULL, 0 },  /* pl */
+       { roff_line_ignore, NULL, NULL, 0 },  /* pm */
+       { roff_line_ignore, NULL, NULL, 0 },  /* pn */
+       { roff_line_ignore, NULL, NULL, 0 },  /* pnr */
+       { roff_line_ignore, NULL, NULL, 0 },  /* po */
+       { roff_line_ignore, NULL, NULL, 0 },  /* ps */
+       { roff_unsupp, NULL, NULL, 0 },  /* psbb */
+       { roff_unsupp, NULL, NULL, 0 },  /* pshape */
+       { roff_insec, NULL, NULL, 0 },  /* pso */
+       { roff_line_ignore, NULL, NULL, 0 },  /* ptr */
+       { roff_line_ignore, NULL, NULL, 0 },  /* pvs */
+       { roff_unsupp, NULL, NULL, 0 },  /* rchar */
+       { roff_line_ignore, NULL, NULL, 0 },  /* rd */
+       { roff_line_ignore, NULL, NULL, 0 },  /* recursionlimit */
+       { roff_unsupp, NULL, NULL, 0 },  /* return */
+       { roff_unsupp, NULL, NULL, 0 },  /* rfschar */
+       { roff_line_ignore, NULL, NULL, 0 },  /* rhang */
+       { roff_line_ignore, NULL, NULL, 0 },  /* rj */
+       { roff_rm, NULL, NULL, 0 },  /* rm */
+       { roff_unsupp, NULL, NULL, 0 },  /* rn */
+       { roff_unsupp, NULL, NULL, 0 },  /* rnn */
+       { roff_rr, NULL, NULL, 0 },  /* rr */
+       { roff_line_ignore, NULL, NULL, 0 },  /* rs */
+       { roff_line_ignore, NULL, NULL, 0 },  /* rt */
+       { roff_unsupp, NULL, NULL, 0 },  /* schar */
+       { roff_line_ignore, NULL, NULL, 0 },  /* sentchar */
+       { roff_line_ignore, NULL, NULL, 0 },  /* shc */
+       { roff_unsupp, NULL, NULL, 0 },  /* shift */
+       { roff_line_ignore, NULL, NULL, 0 },  /* sizes */
+       { roff_so, NULL, NULL, 0 },  /* so */
+       { roff_line_ignore, NULL, NULL, 0 },  /* spacewidth */
+       { roff_line_ignore, NULL, NULL, 0 },  /* special */
+       { roff_line_ignore, NULL, NULL, 0 },  /* spreadwarn */
+       { roff_line_ignore, NULL, NULL, 0 },  /* ss */
+       { roff_line_ignore, NULL, NULL, 0 },  /* sty */
+       { roff_unsupp, NULL, NULL, 0 },  /* substring */
+       { roff_line_ignore, NULL, NULL, 0 },  /* sv */
+       { roff_insec, NULL, NULL, 0 },  /* sy */
+       { roff_T_, NULL, NULL, 0 },  /* T& */
+       { roff_unsupp, NULL, NULL, 0 },  /* ta */
+       { roff_unsupp, NULL, NULL, 0 },  /* tc */
+       { roff_TE, NULL, NULL, 0 },  /* TE */
+       { roff_TH, NULL, NULL, 0 },  /* TH */
+       { roff_unsupp, NULL, NULL, 0 },  /* ti */
+       { roff_line_ignore, NULL, NULL, 0 },  /* tkf */
+       { roff_unsupp, NULL, NULL, 0 },  /* tl */
+       { roff_line_ignore, NULL, NULL, 0 },  /* tm */
+       { roff_line_ignore, NULL, NULL, 0 },  /* tm1 */
+       { roff_line_ignore, NULL, NULL, 0 },  /* tmc */
+       { roff_tr, NULL, NULL, 0 },  /* tr */
+       { roff_line_ignore, NULL, NULL, 0 },  /* track */
+       { roff_line_ignore, NULL, NULL, 0 },  /* transchar */
+       { roff_insec, NULL, NULL, 0 },  /* trf */
+       { roff_line_ignore, NULL, NULL, 0 },  /* trimat */
+       { roff_unsupp, NULL, NULL, 0 },  /* trin */
+       { roff_unsupp, NULL, NULL, 0 },  /* trnt */
+       { roff_line_ignore, NULL, NULL, 0 },  /* troff */
+       { roff_TS, NULL, NULL, 0 },  /* TS */
+       { roff_line_ignore, NULL, NULL, 0 },  /* uf */
+       { roff_line_ignore, NULL, NULL, 0 },  /* ul */
+       { roff_unsupp, NULL, NULL, 0 },  /* unformat */
+       { roff_line_ignore, NULL, NULL, 0 },  /* unwatch */
+       { roff_line_ignore, NULL, NULL, 0 },  /* unwatchn */
+       { roff_line_ignore, NULL, NULL, 0 },  /* vpt */
+       { roff_line_ignore, NULL, NULL, 0 },  /* vs */
+       { roff_line_ignore, NULL, NULL, 0 },  /* warn */
+       { roff_line_ignore, NULL, NULL, 0 },  /* warnscale */
+       { roff_line_ignore, NULL, NULL, 0 },  /* watch */
+       { roff_line_ignore, NULL, NULL, 0 },  /* watchlength */
+       { roff_line_ignore, NULL, NULL, 0 },  /* watchn */
+       { roff_unsupp, NULL, NULL, 0 },  /* wh */
+       { roff_unsupp, NULL, NULL, 0 },  /* while */
+       { roff_insec, NULL, NULL, 0 },  /* write */
+       { roff_insec, NULL, NULL, 0 },  /* writec */
+       { roff_insec, NULL, NULL, 0 },  /* writem */
+       { roff_line_ignore, NULL, NULL, 0 },  /* xflag */
+       { roff_cblock, NULL, NULL, 0 },  /* . */
+       { roff_userdef, NULL, NULL, 0 }
 };
 
 /* not currently implemented: Ds em Eq LP Me PP pp Or Rd Sf SH */
@@ -589,65 +589,62 @@ static    const struct predef predefs[PREDEFS_MAX] = {
 #include "predefs.in"
 };
 
-/* See roffhash_find() */
-#define        ROFF_HASH(p)    (p[0] - ASCII_LO)
-
 static int      roffit_lines;  /* number of lines to delay */
 static char    *roffit_macro;  /* nil-terminated macro line */
 
 
 /* --- request table ------------------------------------------------------ */
 
-static void
-roffhash_init(void)
+struct ohash *
+roffhash_alloc(enum roff_tok mintok, enum roff_tok maxtok)
 {
-       struct roffmac   *n;
-       int               buc, i;
-
-       for (i = 0; i < (int)ROFF_USERDEF; i++) {
-               assert(roffs[i].name[0] >= ASCII_LO);
-               assert(roffs[i].name[0] <= ASCII_HI);
+       struct ohash    *htab;
+       struct roffreq  *req;
+       enum roff_tok    tok;
+       size_t           sz;
+       unsigned int     slot;
 
-               buc = ROFF_HASH(roffs[i].name);
+       htab = mandoc_malloc(sizeof(*htab));
+       mandoc_ohash_init(htab, 8, offsetof(struct roffreq, name));
 
-               if (NULL != (n = hash[buc])) {
-                       for ( ; n->next; n = n->next)
-                               /* Do nothing. */ ;
-                       n->next = &roffs[i];
-               } else
-                       hash[buc] = &roffs[i];
+       for (tok = mintok; tok < maxtok; tok++) {
+               sz = strlen(roff_name[tok]);
+               req = mandoc_malloc(sizeof(*req) + sz + 1);
+               req->tok = tok;
+               memcpy(req->name, roff_name[tok], sz + 1);
+               slot = ohash_qlookup(htab, req->name);
+               ohash_insert(htab, slot, req);
        }
+       return htab;
 }
 
-/*
- * Look up a roff token by its name.  Returns TOKEN_NONE if no macro by
- * the nil-terminated string name could be found.
- */
-static enum roff_tok
-roffhash_find(const char *p, size_t s)
+void
+roffhash_free(struct ohash *htab)
 {
-       int              buc;
-       struct roffmac  *n;
-
-       /*
-        * 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.
-        */
+       struct roffreq  *req;
+       unsigned int     slot;
 
-       if (p[0] < ASCII_LO || p[0] > ASCII_HI)
-               return TOKEN_NONE;
-
-       buc = ROFF_HASH(p);
+       if (htab == NULL)
+               return;
+       for (req = ohash_first(htab, &slot); req != NULL;
+            req = ohash_next(htab, &slot))
+               free(req);
+       ohash_delete(htab);
+       free(htab);
+}
 
-       if (NULL == (n = hash[buc]))
-               return TOKEN_NONE;
-       for ( ; n; n = n->next)
-               if (0 == strncmp(n->name, p, s) && '\0' == n->name[(int)s])
-                       return (enum roff_tok)(n - roffs);
+enum roff_tok
+roffhash_find(struct ohash *htab, const char *name, size_t sz)
+{
+       struct roffreq  *req;
+       const char      *end;
 
-       return TOKEN_NONE;
+       if (sz) {
+               end = name + sz;
+               req = ohash_find(htab, ohash_qlookupi(htab, name, &end));
+       } else
+               req = ohash_find(htab, ohash_qlookup(htab, name));
+       return req == NULL ? TOKEN_NONE : req->tok;
 }
 
 /* --- stack of request blocks -------------------------------------------- */
@@ -738,7 +735,6 @@ roff_free1(struct roff *r)
 void
 roff_reset(struct roff *r)
 {
-
        roff_free1(r);
        r->format = r->options & (MPARSE_MDOC | MPARSE_MAN);
        r->control = 0;
@@ -747,8 +743,8 @@ roff_reset(struct roff *r)
 void
 roff_free(struct roff *r)
 {
-
        roff_free1(r);
+       roffhash_free(r->reqtab);
        free(r);
 }
 
@@ -759,12 +755,10 @@ roff_alloc(struct mparse *parse, int options)
 
        r = mandoc_calloc(1, sizeof(struct roff));
        r->parse = parse;
+       r->reqtab = roffhash_alloc(0, ROFF_USERDEF);
        r->options = options;
        r->format = options & (MPARSE_MDOC | MPARSE_MAN);
        r->rstackpos = -1;
-
-       roffhash_init();
-
        return r;
 }
 
@@ -1487,7 +1481,7 @@ roff_endparse(struct roff *r)
        if (r->last)
                mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
                    r->last->line, r->last->col,
-                   roffs[r->last->tok].name);
+                   roff_name[r->last->tok]);
 
        if (r->eqn) {
                mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
@@ -1523,7 +1517,7 @@ roff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos)
        maclen = roff_getname(r, &cp, ln, ppos);
 
        t = (r->current_string = roff_getstrn(r, mac, maclen))
-           ? ROFF_USERDEF : roffhash_find(mac, maclen);
+           ? ROFF_USERDEF : roffhash_find(r->reqtab, mac, maclen);
 
        if (t != TOKEN_NONE)
                *pos = cp - buf;
@@ -1661,7 +1655,7 @@ roff_block(ROFF_ARGS)
 
        if (namesz == 0 && tok != ROFF_ig) {
                mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse,
-                   ln, ppos, roffs[tok].name);
+                   ln, ppos, roff_name[tok]);
                return ROFF_IGN;
        }
 
@@ -1702,7 +1696,7 @@ roff_block(ROFF_ARGS)
 
        if (*cp != '\0')
                mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse,
-                   ln, pos, ".%s ... %s", roffs[tok].name, cp);
+                   ln, pos, ".%s ... %s", roff_name[tok], cp);
 
        return ROFF_IGN;
 }
@@ -2011,7 +2005,7 @@ roff_insec(ROFF_ARGS)
 {
 
        mandoc_msg(MANDOCERR_REQ_INSEC, r->parse,
-           ln, ppos, roffs[tok].name);
+           ln, ppos, roff_name[tok]);
        return ROFF_IGN;
 }
 
@@ -2020,7 +2014,7 @@ roff_unsupp(ROFF_ARGS)
 {
 
        mandoc_msg(MANDOCERR_REQ_UNSUPP, r->parse,
-           ln, ppos, roffs[tok].name);
+           ln, ppos, roff_name[tok]);
        return ROFF_IGN;
 }
 
@@ -2093,7 +2087,7 @@ roff_cond(ROFF_ARGS)
 
        if (buf->buf[pos] == '\0')
                mandoc_msg(MANDOCERR_COND_EMPTY, r->parse,
-                   ln, ppos, roffs[tok].name);
+                   ln, ppos, roff_name[tok]);
 
        r->last->endspan = 1;
 
index 69c2866..5d560e0 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: roff.h,v 1.23 2017/04/24 23:06:09 schwarze Exp $      */
+/*     $OpenBSD: roff.h,v 1.24 2017/04/29 12:43:55 schwarze Exp $      */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2013, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
@@ -16,6 +16,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+struct ohash;
 struct mdoc_arg;
 union  mdoc_data;
 
@@ -538,6 +539,8 @@ struct      roff_man {
        struct roff_meta  meta;    /* Document meta-data. */
        struct mparse    *parse;   /* Parse pointer. */
        struct roff      *roff;    /* Roff parser state data. */
+       struct ohash     *mdocmac; /* Mdoc macro lookup table. */
+       struct ohash     *manmac;  /* Man macro lookup table. */
        const char       *defos;   /* Default operating system. */
        struct roff_node *first;   /* The first node parsed. */
        struct roff_node *last;    /* The last node parsed. */
@@ -569,4 +572,8 @@ struct      roff_man {
 
 extern const char *const *roff_name;
 
+
 void            deroff(char **, const struct roff_node *);
+struct ohash   *roffhash_alloc(enum roff_tok, enum roff_tok);
+enum roff_tok   roffhash_find(struct ohash *, const char *, size_t);
+void            roffhash_free(struct ohash *);