+ if ( ! roff_evalpar(v, pos, res))
+ return(0);
+
+ while (1) {
+ if (skipwhite)
+ while (isspace((unsigned char)v[*pos]))
+ (*pos)++;
+
+ if ( ! roff_getop(v, pos, &operator))
+ break;
+
+ if (skipwhite)
+ while (isspace((unsigned char)v[*pos]))
+ (*pos)++;
+
+ if ( ! roff_evalpar(v, pos, &operand2))
+ return(0);
+
+ if (skipwhite)
+ while (isspace((unsigned char)v[*pos]))
+ (*pos)++;
+
+ if (NULL == res)
+ continue;
+
+ switch (operator) {
+ case '+':
+ *res += operand2;
+ break;
+ case '-':
+ *res -= operand2;
+ break;
+ case '*':
+ *res *= operand2;
+ break;
+ case '/':
+ *res /= operand2;
+ break;
+ case '%':
+ *res %= operand2;
+ break;
+ case '<':
+ *res = *res < operand2;
+ break;
+ case '>':
+ *res = *res > operand2;
+ break;
+ case 'l':
+ *res = *res <= operand2;
+ break;
+ case 'g':
+ *res = *res >= operand2;
+ break;
+ case '=':
+ *res = *res == operand2;
+ break;
+ case '!':
+ *res = *res != operand2;
+ break;
+ case '&':
+ *res = *res && operand2;
+ break;
+ case ':':
+ *res = *res || operand2;
+ break;
+ case 'i':
+ if (operand2 < *res)
+ *res = operand2;
+ break;
+ case 'a':
+ if (operand2 > *res)
+ *res = operand2;
+ break;
+ default:
+ abort();
+ }
+ }
+ return(1);
+}
+
+void
+roff_setreg(struct roff *r, const char *name, int val, char sign)
+{
+ struct roffreg *reg;
+
+ /* Search for an existing register with the same name. */
+ reg = r->regtab;
+
+ while (reg && strcmp(name, reg->key.p))
+ reg = reg->next;
+
+ if (NULL == reg) {
+ /* Create a new register. */
+ reg = mandoc_malloc(sizeof(struct roffreg));
+ reg->key.p = mandoc_strdup(name);
+ reg->key.sz = strlen(name);
+ reg->val = 0;
+ reg->next = r->regtab;
+ r->regtab = reg;
+ }
+
+ if ('+' == sign)
+ reg->val += val;
+ else if ('-' == sign)
+ reg->val -= val;
+ else
+ reg->val = val;
+}
+
+/*
+ * Handle some predefined read-only number registers.
+ * For now, return -1 if the requested register is not predefined;
+ * in case a predefined read-only register having the value -1
+ * were to turn up, another special value would have to be chosen.
+ */
+static int
+roff_getregro(const char *name)
+{
+
+ switch (*name) {
+ case 'A': /* ASCII approximation mode is always off. */
+ return(0);
+ case 'g': /* Groff compatibility mode is always on. */
+ return(1);
+ case 'H': /* Fixed horizontal resolution. */
+ return (24);
+ case 'j': /* Always adjust left margin only. */
+ return(0);
+ case 'T': /* Some output device is always defined. */
+ return(1);
+ case 'V': /* Fixed vertical resolution. */
+ return (40);
+ default:
+ return (-1);
+ }
+}
+
+int
+roff_getreg(const struct roff *r, const char *name)
+{
+ struct roffreg *reg;
+ int val;
+
+ if ('.' == name[0] && '\0' != name[1] && '\0' == name[2]) {
+ val = roff_getregro(name + 1);
+ if (-1 != val)
+ return (val);
+ }
+
+ for (reg = r->regtab; reg; reg = reg->next)
+ if (0 == strcmp(name, reg->key.p))
+ return(reg->val);
+
+ return(0);
+}
+
+static int
+roff_getregn(const struct roff *r, const char *name, size_t len)
+{
+ struct roffreg *reg;
+ int val;
+
+ if ('.' == name[0] && 2 == len) {
+ val = roff_getregro(name + 1);
+ if (-1 != val)
+ return (val);
+ }
+
+ for (reg = r->regtab; reg; reg = reg->next)
+ if (len == reg->key.sz &&
+ 0 == strncmp(name, reg->key.p, len))
+ return(reg->val);
+
+ return(0);
+}
+
+static void
+roff_freereg(struct roffreg *reg)
+{
+ struct roffreg *old_reg;
+
+ while (NULL != reg) {
+ free(reg->key.p);
+ old_reg = reg;
+ reg = reg->next;
+ free(old_reg);
+ }
+}
+
+static enum rofferr
+roff_nr(ROFF_ARGS)
+{
+ char *key, *val;
+ size_t keysz;
+ int iv;
+ char sign;
+
+ key = val = *bufp + pos;
+ if ('\0' == *key)
+ return(ROFF_IGN);
+
+ keysz = roff_getname(r, &val, ln, pos);
+ if ('\\' == key[keysz])
+ return(ROFF_IGN);
+ key[keysz] = '\0';
+
+ sign = *val;
+ if ('+' == sign || '-' == sign)
+ val++;
+
+ if (roff_evalnum(val, NULL, &iv, 0))
+ roff_setreg(r, key, iv, sign);
+
+ return(ROFF_IGN);
+}
+
+static enum rofferr
+roff_rr(ROFF_ARGS)
+{
+ struct roffreg *reg, **prev;
+ char *name, *cp;
+ size_t namesz;
+
+ name = cp = *bufp + pos;
+ if ('\0' == *name)
+ return(ROFF_IGN);
+ namesz = roff_getname(r, &cp, ln, pos);
+ name[namesz] = '\0';
+
+ prev = &r->regtab;
+ while (1) {
+ reg = *prev;
+ if (NULL == reg || !strcmp(name, reg->key.p))
+ break;
+ prev = ®->next;
+ }
+ if (NULL != reg) {
+ *prev = reg->next;
+ free(reg->key.p);
+ free(reg);
+ }
+ return(ROFF_IGN);
+}
+
+static enum rofferr
+roff_rm(ROFF_ARGS)
+{
+ const char *name;
+ char *cp;
+ size_t namesz;
+
+ cp = *bufp + pos;
+ while ('\0' != *cp) {
+ name = cp;
+ namesz = roff_getname(r, &cp, ln, (int)(cp - *bufp));
+ roff_setstrn(&r->strtab, name, namesz, NULL, 0, 0);
+ if ('\\' == name[namesz])
+ break;
+ }
+ return(ROFF_IGN);
+}
+
+static enum rofferr
+roff_it(ROFF_ARGS)
+{
+ char *cp;
+ size_t len;
+ int iv;
+
+ /* Parse the number of lines. */
+ cp = *bufp + pos;
+ len = strcspn(cp, " \t");
+ cp[len] = '\0';
+ if ((iv = mandoc_strntoi(cp, len, 10)) <= 0) {
+ mandoc_msg(MANDOCERR_NUMERIC, r->parse,
+ ln, ppos, *bufp + 1);
+ return(ROFF_IGN);
+ }
+ cp += len + 1;
+
+ /* Arm the input line trap. */
+ roffit_lines = iv;
+ roffit_macro = mandoc_strdup(cp);
+ return(ROFF_IGN);
+}
+
+static enum rofferr
+roff_Dd(ROFF_ARGS)
+{
+ const char *const *cp;
+
+ if (0 == ((MPARSE_MDOC | MPARSE_QUICK) & r->options))
+ for (cp = __mdoc_reserved; *cp; cp++)
+ roff_setstr(r, *cp, NULL, 0);
+
+ return(ROFF_CONT);
+}
+
+static enum rofferr
+roff_TH(ROFF_ARGS)
+{
+ const char *const *cp;
+
+ if (0 == (MPARSE_QUICK & r->options))
+ for (cp = __man_reserved; *cp; cp++)
+ roff_setstr(r, *cp, NULL, 0);
+
+ return(ROFF_CONT);
+}
+
+static enum rofferr
+roff_TE(ROFF_ARGS)
+{
+
+ if (NULL == r->tbl)
+ mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
+ else
+ tbl_end(&r->tbl);
+
+ return(ROFF_IGN);
+}
+
+static enum rofferr
+roff_T_(ROFF_ARGS)
+{
+
+ if (NULL == r->tbl)
+ mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
+ else
+ tbl_restart(ppos, ln, r->tbl);
+
+ return(ROFF_IGN);
+}
+
+#if 0
+static int
+roff_closeeqn(struct roff *r)
+{
+
+ return(r->eqn && ROFF_EQN == eqn_end(&r->eqn) ? 1 : 0);
+}
+#endif
+
+static void
+roff_openeqn(struct roff *r, const char *name, int line,
+ int offs, const char *buf)
+{
+ struct eqn_node *e;
+ int poff;
+
+ assert(NULL == r->eqn);
+ e = eqn_alloc(name, offs, line, r->parse);
+
+ if (r->last_eqn)
+ r->last_eqn->next = e;
+ else
+ r->first_eqn = r->last_eqn = e;
+
+ r->eqn = r->last_eqn = e;
+
+ if (buf) {
+ poff = 0;
+ eqn_read(&r->eqn, line, buf, offs, &poff);
+ }
+}
+
+static enum rofferr
+roff_EQ(ROFF_ARGS)
+{
+
+ roff_openeqn(r, *bufp + pos, ln, ppos, NULL);
+ return(ROFF_IGN);
+}
+
+static enum rofferr
+roff_EN(ROFF_ARGS)
+{
+
+ mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
+ return(ROFF_IGN);
+}
+
+static enum rofferr
+roff_TS(ROFF_ARGS)
+{
+ struct tbl_node *tbl;
+
+ if (r->tbl) {
+ mandoc_msg(MANDOCERR_SCOPEBROKEN, r->parse, ln, ppos, NULL);
+ tbl_end(&r->tbl);
+ }
+
+ tbl = tbl_alloc(ppos, ln, r->parse);
+
+ if (r->last_tbl)
+ r->last_tbl->next = tbl;
+ else
+ r->first_tbl = r->last_tbl = tbl;
+
+ r->tbl = r->last_tbl = tbl;
+ return(ROFF_IGN);
+}
+
+static enum rofferr
+roff_cc(ROFF_ARGS)
+{
+ const char *p;
+
+ p = *bufp + pos;
+
+ if ('\0' == *p || '.' == (r->control = *p++))
+ r->control = 0;
+
+ if ('\0' != *p)
+ mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL);
+
+ return(ROFF_IGN);
+}
+
+static enum rofferr
+roff_tr(ROFF_ARGS)
+{
+ const char *p, *first, *second;
+ size_t fsz, ssz;
+ enum mandoc_esc esc;
+
+ p = *bufp + pos;
+
+ if ('\0' == *p) {
+ mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL);
+ return(ROFF_IGN);
+ }
+
+ while ('\0' != *p) {
+ fsz = ssz = 1;
+
+ first = p++;
+ if ('\\' == *first) {
+ esc = mandoc_escape(&p, NULL, NULL);
+ if (ESCAPE_ERROR == esc) {
+ mandoc_msg(MANDOCERR_BADESCAPE,
+ r->parse, ln,
+ (int)(p - *bufp), NULL);
+ return(ROFF_IGN);
+ }
+ fsz = (size_t)(p - first);
+ }
+
+ second = p++;
+ if ('\\' == *second) {
+ esc = mandoc_escape(&p, NULL, NULL);
+ if (ESCAPE_ERROR == esc) {
+ mandoc_msg(MANDOCERR_BADESCAPE,
+ r->parse, ln,
+ (int)(p - *bufp), NULL);
+ return(ROFF_IGN);
+ }
+ ssz = (size_t)(p - second);
+ } else if ('\0' == *second) {
+ mandoc_msg(MANDOCERR_ARGCOUNT, r->parse,
+ ln, (int)(p - *bufp), NULL);
+ second = " ";
+ p--;
+ }
+
+ if (fsz > 1) {
+ roff_setstrn(&r->xmbtab, first, fsz,
+ second, ssz, 0);
+ continue;
+ }
+
+ if (NULL == r->xtab)
+ r->xtab = mandoc_calloc(128,
+ sizeof(struct roffstr));
+
+ free(r->xtab[(int)*first].p);
+ r->xtab[(int)*first].p = mandoc_strndup(second, ssz);
+ r->xtab[(int)*first].sz = ssz;
+ }