1 /*      $Id: roff.c,v 1.329 2018/08/01 15:40:17 schwarze Exp $ */
   2 /*
   3  * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
   4  * Copyright (c) 2010-2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
   5  *
   6  * Permission to use, copy, modify, and distribute this software for any
   7  * purpose with or without fee is hereby granted, provided that the above
   8  * copyright notice and this permission notice appear in all copies.
   9  *
  10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
  11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
  13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17  */
  18 #include "config.h"
  19 
  20 #include <sys/types.h>
  21 
  22 #include <assert.h>
  23 #include <ctype.h>
  24 #include <limits.h>
  25 #include <stddef.h>
  26 #include <stdint.h>
  27 #include <stdio.h>
  28 #include <stdlib.h>
  29 #include <string.h>
  30 
  31 #include "mandoc.h"
  32 #include "mandoc_aux.h"
  33 #include "mandoc_ohash.h"
  34 #include "roff.h"
  35 #include "libmandoc.h"
  36 #include "roff_int.h"
  37 #include "libroff.h"
  38 
  39 /* Maximum number of string expansions per line, to break infinite loops. */
  40 #define EXPAND_LIMIT    1000
  41 
  42 /* Types of definitions of macros and strings. */
  43 #define ROFFDEF_USER    (1 << 1)  /* User-defined. */
  44 #define ROFFDEF_PRE     (1 << 2)  /* Predefined. */
  45 #define ROFFDEF_REN     (1 << 3)  /* Renamed standard macro. */
  46 #define ROFFDEF_STD     (1 << 4)  /* mdoc(7) or man(7) macro. */
  47 #define ROFFDEF_ANY     (ROFFDEF_USER | ROFFDEF_PRE | \
  48                          ROFFDEF_REN | ROFFDEF_STD)
  49 #define ROFFDEF_UNDEF   (1 << 5)  /* Completely undefined. */
  50 
  51 /* --- data types --------------------------------------------------------- */
  52 
  53 /*
  54  * An incredibly-simple string buffer.
  55  */
  56 struct  roffstr {
  57         char            *p; /* nil-terminated buffer */
  58         size_t           sz; /* saved strlen(p) */
  59 };
  60 
  61 /*
  62  * A key-value roffstr pair as part of a singly-linked list.
  63  */
  64 struct  roffkv {
  65         struct roffstr   key;
  66         struct roffstr   val;
  67         struct roffkv   *next; /* next in list */
  68 };
  69 
  70 /*
  71  * A single number register as part of a singly-linked list.
  72  */
  73 struct  roffreg {
  74         struct roffstr   key;
  75         int              val;
  76         int              step;
  77         struct roffreg  *next;
  78 };
  79 
  80 /*
  81  * Association of request and macro names with token IDs.
  82  */
  83 struct  roffreq {
  84         enum roff_tok    tok;
  85         char             name[];
  86 };
  87 
  88 struct  roff {
  89         struct mparse   *parse; /* parse point */
  90         struct roff_man *man; /* mdoc or man parser */
  91         struct roffnode *last; /* leaf of stack */
  92         int             *rstack; /* stack of inverted `ie' values */
  93         struct ohash    *reqtab; /* request lookup table */
  94         struct roffreg  *regtab; /* number registers */
  95         struct roffkv   *strtab; /* user-defined strings & macros */
  96         struct roffkv   *rentab; /* renamed strings & macros */
  97         struct roffkv   *xmbtab; /* multi-byte trans table (`tr') */
  98         struct roffstr  *xtab; /* single-byte trans table (`tr') */
  99         const char      *current_string; /* value of last called user macro */
 100         struct tbl_node *first_tbl; /* first table parsed */
 101         struct tbl_node *last_tbl; /* last table parsed */
 102         struct tbl_node *tbl; /* current table being parsed */
 103         struct eqn_node *last_eqn; /* equation parser */
 104         struct eqn_node *eqn; /* active equation parser */
 105         int              eqn_inline; /* current equation is inline */
 106         int              options; /* parse options */
 107         int              rstacksz; /* current size limit of rstack */
 108         int              rstackpos; /* position in rstack */
 109         int              format; /* current file in mdoc or man format */
 110         int              argc; /* number of args of the last macro */
 111         char             control; /* control character */
 112         char             escape; /* escape character */
 113 };
 114 
 115 struct  roffnode {
 116         enum roff_tok    tok; /* type of node */
 117         struct roffnode *parent; /* up one in stack */
 118         int              line; /* parse line */
 119         int              col; /* parse col */
 120         char            *name; /* node name, e.g. macro name */
 121         char            *end; /* end-rules: custom token */
 122         int              endspan; /* end-rules: next-line or infty */
 123         int              rule; /* current evaluation rule */
 124 };
 125 
 126 #define ROFF_ARGS        struct roff *r, /* parse ctx */ \
 127                          enum roff_tok tok, /* tok of macro */ \
 128                          struct buf *buf, /* input buffer */ \
 129                          int ln, /* parse line */ \
 130                          int ppos, /* original pos in buffer */ \
 131                          int pos, /* current pos in buffer */ \
 132                          int *offs /* reset offset of buffer data */
 133 
 134 typedef enum rofferr (*roffproc)(ROFF_ARGS);
 135 
 136 struct  roffmac {
 137         roffproc         proc; /* process new macro */
 138         roffproc         text; /* process as child text of macro */
 139         roffproc         sub; /* process as child of macro */
 140         int              flags;
 141 #define ROFFMAC_STRUCT  (1 << 0) /* always interpret */
 142 };
 143 
 144 struct  predef {
 145         const char      *name; /* predefined input name */
 146         const char      *str; /* replacement symbol */
 147 };
 148 
 149 #define PREDEF(__name, __str) \
 150         { (__name), (__str) },
 151 
 152 /* --- function prototypes ------------------------------------------------ */
 153 
 154 static  void             roffnode_cleanscope(struct roff *);
 155 static  void             roffnode_pop(struct roff *);
 156 static  void             roffnode_push(struct roff *, enum roff_tok,
 157                                 const char *, int, int);
 158 static  void             roff_addtbl(struct roff_man *, struct tbl_node *);
 159 static  enum rofferr     roff_als(ROFF_ARGS);
 160 static  enum rofferr     roff_block(ROFF_ARGS);
 161 static  enum rofferr     roff_block_text(ROFF_ARGS);
 162 static  enum rofferr     roff_block_sub(ROFF_ARGS);
 163 static  enum rofferr     roff_br(ROFF_ARGS);
 164 static  enum rofferr     roff_cblock(ROFF_ARGS);
 165 static  enum rofferr     roff_cc(ROFF_ARGS);
 166 static  void             roff_ccond(struct roff *, int, int);
 167 static  enum rofferr     roff_cond(ROFF_ARGS);
 168 static  enum rofferr     roff_cond_text(ROFF_ARGS);
 169 static  enum rofferr     roff_cond_sub(ROFF_ARGS);
 170 static  enum rofferr     roff_ds(ROFF_ARGS);
 171 static  enum rofferr     roff_ec(ROFF_ARGS);
 172 static  enum rofferr     roff_eo(ROFF_ARGS);
 173 static  enum rofferr     roff_eqndelim(struct roff *, struct buf *, int);
 174 static  int              roff_evalcond(struct roff *r, int, char *, int *);
 175 static  int              roff_evalnum(struct roff *, int,
 176                                 const char *, int *, int *, int);
 177 static  int              roff_evalpar(struct roff *, int,
 178                                 const char *, int *, int *, int);
 179 static  int              roff_evalstrcond(const char *, int *);
 180 static  void             roff_free1(struct roff *);
 181 static  void             roff_freereg(struct roffreg *);
 182 static  void             roff_freestr(struct roffkv *);
 183 static  size_t           roff_getname(struct roff *, char **, int, int);
 184 static  int              roff_getnum(const char *, int *, int *, int);
 185 static  int              roff_getop(const char *, int *, char *);
 186 static  int              roff_getregn(struct roff *,
 187                                 const char *, size_t, char);
 188 static  int              roff_getregro(const struct roff *,
 189                                 const char *name);
 190 static  const char      *roff_getstrn(struct roff *,
 191                                 const char *, size_t, int *);
 192 static  int              roff_hasregn(const struct roff *,
 193                                 const char *, size_t);
 194 static  enum rofferr     roff_insec(ROFF_ARGS);
 195 static  enum rofferr     roff_it(ROFF_ARGS);
 196 static  enum rofferr     roff_line_ignore(ROFF_ARGS);
 197 static  void             roff_man_alloc1(struct roff_man *);
 198 static  void             roff_man_free1(struct roff_man *);
 199 static  enum rofferr     roff_manyarg(ROFF_ARGS);
 200 static  enum rofferr     roff_nr(ROFF_ARGS);
 201 static  enum rofferr     roff_onearg(ROFF_ARGS);
 202 static  enum roff_tok    roff_parse(struct roff *, char *, int *,
 203                                 int, int);
 204 static  enum rofferr     roff_parsetext(struct roff *, struct buf *,
 205                                 int, int *);
 206 static  enum rofferr     roff_renamed(ROFF_ARGS);
 207 static  enum rofferr     roff_res(struct roff *, struct buf *, int, int);
 208 static  enum rofferr     roff_rm(ROFF_ARGS);
 209 static  enum rofferr     roff_rn(ROFF_ARGS);
 210 static  enum rofferr     roff_rr(ROFF_ARGS);
 211 static  void             roff_setregn(struct roff *, const char *,
 212                                 size_t, int, char, int);
 213 static  void             roff_setstr(struct roff *,
 214                                 const char *, const char *, int);
 215 static  void             roff_setstrn(struct roffkv **, const char *,
 216                                 size_t, const char *, size_t, int);
 217 static  enum rofferr     roff_so(ROFF_ARGS);
 218 static  enum rofferr     roff_tr(ROFF_ARGS);
 219 static  enum rofferr     roff_Dd(ROFF_ARGS);
 220 static  enum rofferr     roff_TE(ROFF_ARGS);
 221 static  enum rofferr     roff_TS(ROFF_ARGS);
 222 static  enum rofferr     roff_EQ(ROFF_ARGS);
 223 static  enum rofferr     roff_EN(ROFF_ARGS);
 224 static  enum rofferr     roff_T_(ROFF_ARGS);
 225 static  enum rofferr     roff_unsupp(ROFF_ARGS);
 226 static  enum rofferr     roff_userdef(ROFF_ARGS);
 227 
 228 /* --- constant data ------------------------------------------------------ */
 229 
 230 #define ROFFNUM_SCALE   (1 << 0)  /* Honour scaling in roff_getnum(). */
 231 #define ROFFNUM_WHITE   (1 << 1)  /* Skip whitespace in roff_evalnum(). */
 232 
 233 const char *__roff_name[MAN_MAX + 1] = {
 234         "br",           "ce",           "ft",           "ll",
 235         "mc",           "po",           "rj",           "sp",
 236         "ta",           "ti",           NULL,
 237         "ab",           "ad",           "af",           "aln",
 238         "als",          "am",           "am1",          "ami",
 239         "ami1",         "as",           "as1",          "asciify",
 240         "backtrace",    "bd",           "bleedat",      "blm",
 241         "box",          "boxa",         "bp",           "BP",
 242         "break",        "breakchar",    "brnl",         "brp",
 243         "brpnl",        "c2",           "cc",
 244         "cf",           "cflags",       "ch",           "char",
 245         "chop",         "class",        "close",        "CL",
 246         "color",        "composite",    "continue",     "cp",
 247         "cropat",       "cs",           "cu",           "da",
 248         "dch",          "Dd",           "de",           "de1",
 249         "defcolor",     "dei",          "dei1",         "device",
 250         "devicem",      "di",           "do",           "ds",
 251         "ds1",          "dwh",          "dt",           "ec",
 252         "ecr",          "ecs",          "el",           "em",
 253         "EN",           "eo",           "EP",           "EQ",
 254         "errprint",     "ev",           "evc",          "ex",
 255         "fallback",     "fam",          "fc",           "fchar",
 256         "fcolor",       "fdeferlig",    "feature",      "fkern",
 257         "fl",           "flig",         "fp",           "fps",
 258         "fschar",       "fspacewidth",  "fspecial",     "ftr",
 259         "fzoom",        "gcolor",       "hc",           "hcode",
 260         "hidechar",     "hla",          "hlm",          "hpf",
 261         "hpfa",         "hpfcode",      "hw",           "hy",
 262         "hylang",       "hylen",        "hym",          "hypp",
 263         "hys",          "ie",           "if",           "ig",
 264         "index",        "it",           "itc",          "IX",
 265         "kern",         "kernafter",    "kernbefore",   "kernpair",
 266         "lc",           "lc_ctype",     "lds",          "length",
 267         "letadj",       "lf",           "lg",           "lhang",
 268         "linetabs",     "lnr",          "lnrf",         "lpfx",
 269         "ls",           "lsm",          "lt",
 270         "mediasize",    "minss",        "mk",           "mso",
 271         "na",           "ne",           "nh",           "nhychar",
 272         "nm",           "nn",           "nop",          "nr",
 273         "nrf",          "nroff",        "ns",           "nx",
 274         "open",         "opena",        "os",           "output",
 275         "padj",         "papersize",    "pc",           "pev",
 276         "pi",           "PI",           "pl",           "pm",
 277         "pn",           "pnr",          "ps",
 278         "psbb",         "pshape",       "pso",          "ptr",
 279         "pvs",          "rchar",        "rd",           "recursionlimit",
 280         "return",       "rfschar",      "rhang",
 281         "rm",           "rn",           "rnn",          "rr",
 282         "rs",           "rt",           "schar",        "sentchar",
 283         "shc",          "shift",        "sizes",        "so",
 284         "spacewidth",   "special",      "spreadwarn",   "ss",
 285         "sty",          "substring",    "sv",           "sy",
 286         "T&",               "tc",           "TE",
 287         "TH",           "tkf",          "tl",
 288         "tm",           "tm1",          "tmc",          "tr",
 289         "track",        "transchar",    "trf",          "trimat",
 290         "trin",         "trnt",         "troff",        "TS",
 291         "uf",           "ul",           "unformat",     "unwatch",
 292         "unwatchn",     "vpt",          "vs",           "warn",
 293         "warnscale",    "watch",        "watchlength",  "watchn",
 294         "wh",           "while",        "write",        "writec",
 295         "writem",       "xflag",        ".",            NULL,
 296         NULL,           "text",
 297         "Dd",           "Dt",           "Os",           "Sh",
 298         "Ss",           "Pp",           "D1",           "Dl",
 299         "Bd",           "Ed",           "Bl",           "El",
 300         "It",           "Ad",           "An",           "Ap",
 301         "Ar",           "Cd",           "Cm",           "Dv",
 302         "Er",           "Ev",           "Ex",           "Fa",
 303         "Fd",           "Fl",           "Fn",           "Ft",
 304         "Ic",           "In",           "Li",           "Nd",
 305         "Nm",           "Op",           "Ot",           "Pa",
 306         "Rv",           "St",           "Va",           "Vt",
 307         "Xr",           "%A",           "%B",           "%D",
 308         "%I",           "%J",           "%N",           "%O",
 309         "%P",           "%R",           "%T",           "%V",
 310         "Ac",           "Ao",           "Aq",           "At",
 311         "Bc",           "Bf",           "Bo",           "Bq",
 312         "Bsx",          "Bx",           "Db",           "Dc",
 313         "Do",           "Dq",           "Ec",           "Ef",
 314         "Em",           "Eo",           "Fx",           "Ms",
 315         "No",           "Ns",           "Nx",           "Ox",
 316         "Pc",           "Pf",           "Po",           "Pq",
 317         "Qc",           "Ql",           "Qo",           "Qq",
 318         "Re",           "Rs",           "Sc",           "So",
 319         "Sq",           "Sm",           "Sx",           "Sy",
 320         "Tn",           "Ux",           "Xc",           "Xo",
 321         "Fo",           "Fc",           "Oo",           "Oc",
 322         "Bk",           "Ek",           "Bt",           "Hf",
 323         "Fr",           "Ud",           "Lb",           "Lp",
 324         "Lk",           "Mt",           "Brq",          "Bro",
 325         "Brc",          "%C",           "Es",           "En",
 326         "Dx",           "%Q",           "%U",           "Ta",
 327         NULL,
 328         "TH",           "SH",           "SS",           "TP",
 329         "LP",           "PP",           "P",            "IP",
 330         "HP",           "SM",           "SB",           "BI",
 331         "IB",           "BR",           "RB",           "R",
 332         "B",            "I",            "IR",           "RI",
 333         "nf",           "fi",
 334         "RE",           "RS",           "DT",           "UC",
 335         "PD",           "AT",           "in",
 336         "OP",           "EX",           "EE",           "UR",
 337         "UE",           "MT",           "ME",           NULL
 338 };
 339 const   char *const *roff_name = __roff_name;
 340 
 341 static  struct roffmac   roffs[TOKEN_NONE] = {
 342         { roff_br, NULL, NULL, 0 },  /* br */
 343         { roff_onearg, NULL, NULL, 0 },  /* ce */
 344         { roff_onearg, NULL, NULL, 0 },  /* ft */
 345         { roff_onearg, NULL, NULL, 0 },  /* ll */
 346         { roff_onearg, NULL, NULL, 0 },  /* mc */
 347         { roff_onearg, NULL, NULL, 0 },  /* po */
 348         { roff_onearg, NULL, NULL, 0 },  /* rj */
 349         { roff_onearg, NULL, NULL, 0 },  /* sp */
 350         { roff_manyarg, NULL, NULL, 0 },  /* ta */
 351         { roff_onearg, NULL, NULL, 0 },  /* ti */
 352         { NULL, NULL, NULL, 0 },  /* ROFF_MAX */
 353         { roff_unsupp, NULL, NULL, 0 },  /* ab */
 354         { roff_line_ignore, NULL, NULL, 0 },  /* ad */
 355         { roff_line_ignore, NULL, NULL, 0 },  /* af */
 356         { roff_unsupp, NULL, NULL, 0 },  /* aln */
 357         { roff_als, NULL, NULL, 0 },  /* als */
 358         { roff_block, roff_block_text, roff_block_sub, 0 },  /* am */
 359         { roff_block, roff_block_text, roff_block_sub, 0 },  /* am1 */
 360         { roff_block, roff_block_text, roff_block_sub, 0 },  /* ami */
 361         { roff_block, roff_block_text, roff_block_sub, 0 },  /* ami1 */
 362         { roff_ds, NULL, NULL, 0 },  /* as */
 363         { roff_ds, NULL, NULL, 0 },  /* as1 */
 364         { roff_unsupp, NULL, NULL, 0 },  /* asciify */
 365         { roff_line_ignore, NULL, NULL, 0 },  /* backtrace */
 366         { roff_line_ignore, NULL, NULL, 0 },  /* bd */
 367         { roff_line_ignore, NULL, NULL, 0 },  /* bleedat */
 368         { roff_unsupp, NULL, NULL, 0 },  /* blm */
 369         { roff_unsupp, NULL, NULL, 0 },  /* box */
 370         { roff_unsupp, NULL, NULL, 0 },  /* boxa */
 371         { roff_line_ignore, NULL, NULL, 0 },  /* bp */
 372         { roff_unsupp, NULL, NULL, 0 },  /* BP */
 373         { roff_unsupp, NULL, NULL, 0 },  /* break */
 374         { roff_line_ignore, NULL, NULL, 0 },  /* breakchar */
 375         { roff_line_ignore, NULL, NULL, 0 },  /* brnl */
 376         { roff_br, NULL, NULL, 0 },  /* brp */
 377         { roff_line_ignore, NULL, NULL, 0 },  /* brpnl */
 378         { roff_unsupp, NULL, NULL, 0 },  /* c2 */
 379         { roff_cc, NULL, NULL, 0 },  /* cc */
 380         { roff_insec, NULL, NULL, 0 },  /* cf */
 381         { roff_line_ignore, NULL, NULL, 0 },  /* cflags */
 382         { roff_line_ignore, NULL, NULL, 0 },  /* ch */
 383         { roff_unsupp, NULL, NULL, 0 },  /* char */
 384         { roff_unsupp, NULL, NULL, 0 },  /* chop */
 385         { roff_line_ignore, NULL, NULL, 0 },  /* class */
 386         { roff_insec, NULL, NULL, 0 },  /* close */
 387         { roff_unsupp, NULL, NULL, 0 },  /* CL */
 388         { roff_line_ignore, NULL, NULL, 0 },  /* color */
 389         { roff_unsupp, NULL, NULL, 0 },  /* composite */
 390         { roff_unsupp, NULL, NULL, 0 },  /* continue */
 391         { roff_line_ignore, NULL, NULL, 0 },  /* cp */
 392         { roff_line_ignore, NULL, NULL, 0 },  /* cropat */
 393         { roff_line_ignore, NULL, NULL, 0 },  /* cs */
 394         { roff_line_ignore, NULL, NULL, 0 },  /* cu */
 395         { roff_unsupp, NULL, NULL, 0 },  /* da */
 396         { roff_unsupp, NULL, NULL, 0 },  /* dch */
 397         { roff_Dd, NULL, NULL, 0 },  /* Dd */
 398         { roff_block, roff_block_text, roff_block_sub, 0 },  /* de */
 399         { roff_block, roff_block_text, roff_block_sub, 0 },  /* de1 */
 400         { roff_line_ignore, NULL, NULL, 0 },  /* defcolor */
 401         { roff_block, roff_block_text, roff_block_sub, 0 },  /* dei */
 402         { roff_block, roff_block_text, roff_block_sub, 0 },  /* dei1 */
 403         { roff_unsupp, NULL, NULL, 0 },  /* device */
 404         { roff_unsupp, NULL, NULL, 0 },  /* devicem */
 405         { roff_unsupp, NULL, NULL, 0 },  /* di */
 406         { roff_unsupp, NULL, NULL, 0 },  /* do */
 407         { roff_ds, NULL, NULL, 0 },  /* ds */
 408         { roff_ds, NULL, NULL, 0 },  /* ds1 */
 409         { roff_unsupp, NULL, NULL, 0 },  /* dwh */
 410         { roff_unsupp, NULL, NULL, 0 },  /* dt */
 411         { roff_ec, NULL, NULL, 0 },  /* ec */
 412         { roff_unsupp, NULL, NULL, 0 },  /* ecr */
 413         { roff_unsupp, NULL, NULL, 0 },  /* ecs */
 414         { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },  /* el */
 415         { roff_unsupp, NULL, NULL, 0 },  /* em */
 416         { roff_EN, NULL, NULL, 0 },  /* EN */
 417         { roff_eo, NULL, NULL, 0 },  /* eo */
 418         { roff_unsupp, NULL, NULL, 0 },  /* EP */
 419         { roff_EQ, NULL, NULL, 0 },  /* EQ */
 420         { roff_line_ignore, NULL, NULL, 0 },  /* errprint */
 421         { roff_unsupp, NULL, NULL, 0 },  /* ev */
 422         { roff_unsupp, NULL, NULL, 0 },  /* evc */
 423         { roff_unsupp, NULL, NULL, 0 },  /* ex */
 424         { roff_line_ignore, NULL, NULL, 0 },  /* fallback */
 425         { roff_line_ignore, NULL, NULL, 0 },  /* fam */
 426         { roff_unsupp, NULL, NULL, 0 },  /* fc */
 427         { roff_unsupp, NULL, NULL, 0 },  /* fchar */
 428         { roff_line_ignore, NULL, NULL, 0 },  /* fcolor */
 429         { roff_line_ignore, NULL, NULL, 0 },  /* fdeferlig */
 430         { roff_line_ignore, NULL, NULL, 0 },  /* feature */
 431         { roff_line_ignore, NULL, NULL, 0 },  /* fkern */
 432         { roff_line_ignore, NULL, NULL, 0 },  /* fl */
 433         { roff_line_ignore, NULL, NULL, 0 },  /* flig */
 434         { roff_line_ignore, NULL, NULL, 0 },  /* fp */
 435         { roff_line_ignore, NULL, NULL, 0 },  /* fps */
 436         { roff_unsupp, NULL, NULL, 0 },  /* fschar */
 437         { roff_line_ignore, NULL, NULL, 0 },  /* fspacewidth */
 438         { roff_line_ignore, NULL, NULL, 0 },  /* fspecial */
 439         { roff_line_ignore, NULL, NULL, 0 },  /* ftr */
 440         { roff_line_ignore, NULL, NULL, 0 },  /* fzoom */
 441         { roff_line_ignore, NULL, NULL, 0 },  /* gcolor */
 442         { roff_line_ignore, NULL, NULL, 0 },  /* hc */
 443         { roff_line_ignore, NULL, NULL, 0 },  /* hcode */
 444         { roff_line_ignore, NULL, NULL, 0 },  /* hidechar */
 445         { roff_line_ignore, NULL, NULL, 0 },  /* hla */
 446         { roff_line_ignore, NULL, NULL, 0 },  /* hlm */
 447         { roff_line_ignore, NULL, NULL, 0 },  /* hpf */
 448         { roff_line_ignore, NULL, NULL, 0 },  /* hpfa */
 449         { roff_line_ignore, NULL, NULL, 0 },  /* hpfcode */
 450         { roff_line_ignore, NULL, NULL, 0 },  /* hw */
 451         { roff_line_ignore, NULL, NULL, 0 },  /* hy */
 452         { roff_line_ignore, NULL, NULL, 0 },  /* hylang */
 453         { roff_line_ignore, NULL, NULL, 0 },  /* hylen */
 454         { roff_line_ignore, NULL, NULL, 0 },  /* hym */
 455         { roff_line_ignore, NULL, NULL, 0 },  /* hypp */
 456         { roff_line_ignore, NULL, NULL, 0 },  /* hys */
 457         { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },  /* ie */
 458         { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },  /* if */
 459         { roff_block, roff_block_text, roff_block_sub, 0 },  /* ig */
 460         { roff_unsupp, NULL, NULL, 0 },  /* index */
 461         { roff_it, NULL, NULL, 0 },  /* it */
 462         { roff_unsupp, NULL, NULL, 0 },  /* itc */
 463         { roff_line_ignore, NULL, NULL, 0 },  /* IX */
 464         { roff_line_ignore, NULL, NULL, 0 },  /* kern */
 465         { roff_line_ignore, NULL, NULL, 0 },  /* kernafter */
 466         { roff_line_ignore, NULL, NULL, 0 },  /* kernbefore */
 467         { roff_line_ignore, NULL, NULL, 0 },  /* kernpair */
 468         { roff_unsupp, NULL, NULL, 0 },  /* lc */
 469         { roff_unsupp, NULL, NULL, 0 },  /* lc_ctype */
 470         { roff_unsupp, NULL, NULL, 0 },  /* lds */
 471         { roff_unsupp, NULL, NULL, 0 },  /* length */
 472         { roff_line_ignore, NULL, NULL, 0 },  /* letadj */
 473         { roff_insec, NULL, NULL, 0 },  /* lf */
 474         { roff_line_ignore, NULL, NULL, 0 },  /* lg */
 475         { roff_line_ignore, NULL, NULL, 0 },  /* lhang */
 476         { roff_unsupp, NULL, NULL, 0 },  /* linetabs */
 477         { roff_unsupp, NULL, NULL, 0 },  /* lnr */
 478         { roff_unsupp, NULL, NULL, 0 },  /* lnrf */
 479         { roff_unsupp, NULL, NULL, 0 },  /* lpfx */
 480         { roff_line_ignore, NULL, NULL, 0 },  /* ls */
 481         { roff_unsupp, NULL, NULL, 0 },  /* lsm */
 482         { roff_line_ignore, NULL, NULL, 0 },  /* lt */
 483         { roff_line_ignore, NULL, NULL, 0 },  /* mediasize */
 484         { roff_line_ignore, NULL, NULL, 0 },  /* minss */
 485         { roff_line_ignore, NULL, NULL, 0 },  /* mk */
 486         { roff_insec, NULL, NULL, 0 },  /* mso */
 487         { roff_line_ignore, NULL, NULL, 0 },  /* na */
 488         { roff_line_ignore, NULL, NULL, 0 },  /* ne */
 489         { roff_line_ignore, NULL, NULL, 0 },  /* nh */
 490         { roff_line_ignore, NULL, NULL, 0 },  /* nhychar */
 491         { roff_unsupp, NULL, NULL, 0 },  /* nm */
 492         { roff_unsupp, NULL, NULL, 0 },  /* nn */
 493         { roff_unsupp, NULL, NULL, 0 },  /* nop */
 494         { roff_nr, NULL, NULL, 0 },  /* nr */
 495         { roff_unsupp, NULL, NULL, 0 },  /* nrf */
 496         { roff_line_ignore, NULL, NULL, 0 },  /* nroff */
 497         { roff_line_ignore, NULL, NULL, 0 },  /* ns */
 498         { roff_insec, NULL, NULL, 0 },  /* nx */
 499         { roff_insec, NULL, NULL, 0 },  /* open */
 500         { roff_insec, NULL, NULL, 0 },  /* opena */
 501         { roff_line_ignore, NULL, NULL, 0 },  /* os */
 502         { roff_unsupp, NULL, NULL, 0 },  /* output */
 503         { roff_line_ignore, NULL, NULL, 0 },  /* padj */
 504         { roff_line_ignore, NULL, NULL, 0 },  /* papersize */
 505         { roff_line_ignore, NULL, NULL, 0 },  /* pc */
 506         { roff_line_ignore, NULL, NULL, 0 },  /* pev */
 507         { roff_insec, NULL, NULL, 0 },  /* pi */
 508         { roff_unsupp, NULL, NULL, 0 },  /* PI */
 509         { roff_line_ignore, NULL, NULL, 0 },  /* pl */
 510         { roff_line_ignore, NULL, NULL, 0 },  /* pm */
 511         { roff_line_ignore, NULL, NULL, 0 },  /* pn */
 512         { roff_line_ignore, NULL, NULL, 0 },  /* pnr */
 513         { roff_line_ignore, NULL, NULL, 0 },  /* ps */
 514         { roff_unsupp, NULL, NULL, 0 },  /* psbb */
 515         { roff_unsupp, NULL, NULL, 0 },  /* pshape */
 516         { roff_insec, NULL, NULL, 0 },  /* pso */
 517         { roff_line_ignore, NULL, NULL, 0 },  /* ptr */
 518         { roff_line_ignore, NULL, NULL, 0 },  /* pvs */
 519         { roff_unsupp, NULL, NULL, 0 },  /* rchar */
 520         { roff_line_ignore, NULL, NULL, 0 },  /* rd */
 521         { roff_line_ignore, NULL, NULL, 0 },  /* recursionlimit */
 522         { roff_unsupp, NULL, NULL, 0 },  /* return */
 523         { roff_unsupp, NULL, NULL, 0 },  /* rfschar */
 524         { roff_line_ignore, NULL, NULL, 0 },  /* rhang */
 525         { roff_rm, NULL, NULL, 0 },  /* rm */
 526         { roff_rn, NULL, NULL, 0 },  /* rn */
 527         { roff_unsupp, NULL, NULL, 0 },  /* rnn */
 528         { roff_rr, NULL, NULL, 0 },  /* rr */
 529         { roff_line_ignore, NULL, NULL, 0 },  /* rs */
 530         { roff_line_ignore, NULL, NULL, 0 },  /* rt */
 531         { roff_unsupp, NULL, NULL, 0 },  /* schar */
 532         { roff_line_ignore, NULL, NULL, 0 },  /* sentchar */
 533         { roff_line_ignore, NULL, NULL, 0 },  /* shc */
 534         { roff_unsupp, NULL, NULL, 0 },  /* shift */
 535         { roff_line_ignore, NULL, NULL, 0 },  /* sizes */
 536         { roff_so, NULL, NULL, 0 },  /* so */
 537         { roff_line_ignore, NULL, NULL, 0 },  /* spacewidth */
 538         { roff_line_ignore, NULL, NULL, 0 },  /* special */
 539         { roff_line_ignore, NULL, NULL, 0 },  /* spreadwarn */
 540         { roff_line_ignore, NULL, NULL, 0 },  /* ss */
 541         { roff_line_ignore, NULL, NULL, 0 },  /* sty */
 542         { roff_unsupp, NULL, NULL, 0 },  /* substring */
 543         { roff_line_ignore, NULL, NULL, 0 },  /* sv */
 544         { roff_insec, NULL, NULL, 0 },  /* sy */
 545         { roff_T_, NULL, NULL, 0 },  /* T& */
 546         { roff_unsupp, NULL, NULL, 0 },  /* tc */
 547         { roff_TE, NULL, NULL, 0 },  /* TE */
 548         { roff_Dd, NULL, NULL, 0 },  /* TH */
 549         { roff_line_ignore, NULL, NULL, 0 },  /* tkf */
 550         { roff_unsupp, NULL, NULL, 0 },  /* tl */
 551         { roff_line_ignore, NULL, NULL, 0 },  /* tm */
 552         { roff_line_ignore, NULL, NULL, 0 },  /* tm1 */
 553         { roff_line_ignore, NULL, NULL, 0 },  /* tmc */
 554         { roff_tr, NULL, NULL, 0 },  /* tr */
 555         { roff_line_ignore, NULL, NULL, 0 },  /* track */
 556         { roff_line_ignore, NULL, NULL, 0 },  /* transchar */
 557         { roff_insec, NULL, NULL, 0 },  /* trf */
 558         { roff_line_ignore, NULL, NULL, 0 },  /* trimat */
 559         { roff_unsupp, NULL, NULL, 0 },  /* trin */
 560         { roff_unsupp, NULL, NULL, 0 },  /* trnt */
 561         { roff_line_ignore, NULL, NULL, 0 },  /* troff */
 562         { roff_TS, NULL, NULL, 0 },  /* TS */
 563         { roff_line_ignore, NULL, NULL, 0 },  /* uf */
 564         { roff_line_ignore, NULL, NULL, 0 },  /* ul */
 565         { roff_unsupp, NULL, NULL, 0 },  /* unformat */
 566         { roff_line_ignore, NULL, NULL, 0 },  /* unwatch */
 567         { roff_line_ignore, NULL, NULL, 0 },  /* unwatchn */
 568         { roff_line_ignore, NULL, NULL, 0 },  /* vpt */
 569         { roff_line_ignore, NULL, NULL, 0 },  /* vs */
 570         { roff_line_ignore, NULL, NULL, 0 },  /* warn */
 571         { roff_line_ignore, NULL, NULL, 0 },  /* warnscale */
 572         { roff_line_ignore, NULL, NULL, 0 },  /* watch */
 573         { roff_line_ignore, NULL, NULL, 0 },  /* watchlength */
 574         { roff_line_ignore, NULL, NULL, 0 },  /* watchn */
 575         { roff_unsupp, NULL, NULL, 0 },  /* wh */
 576         { roff_unsupp, NULL, NULL, 0 },  /* while */
 577         { roff_insec, NULL, NULL, 0 },  /* write */
 578         { roff_insec, NULL, NULL, 0 },  /* writec */
 579         { roff_insec, NULL, NULL, 0 },  /* writem */
 580         { roff_line_ignore, NULL, NULL, 0 },  /* xflag */
 581         { roff_cblock, NULL, NULL, 0 },  /* . */
 582         { roff_renamed, NULL, NULL, 0 },
 583         { roff_userdef, NULL, NULL, 0 }
 584 };
 585 
 586 /* Array of injected predefined strings. */
 587 #define PREDEFS_MAX      38
 588 static  const struct predef predefs[PREDEFS_MAX] = {
 589 #include "predefs.in"
 590 };
 591 
 592 static  int      roffce_lines;  /* number of input lines to center */
 593 static  struct roff_node *roffce_node;  /* active request */
 594 static  int      roffit_lines;  /* number of lines to delay */
 595 static  char    *roffit_macro;  /* nil-terminated macro line */
 596 
 597 
 598 /* --- request table ------------------------------------------------------ */
 599 
 600 struct ohash *
 601 roffhash_alloc(enum roff_tok mintok, enum roff_tok maxtok)
 602 {
 603         struct ohash    *htab;
 604         struct roffreq  *req;
 605         enum roff_tok    tok;
 606         size_t           sz;
 607         unsigned int     slot;
 608 
 609         htab = mandoc_malloc(sizeof(*htab));
 610         mandoc_ohash_init(htab, 8, offsetof(struct roffreq, name));
 611 
 612         for (tok = mintok; tok < maxtok; tok++) {
 613                 if (roff_name[tok] == NULL)
 614                         continue;
 615                 sz = strlen(roff_name[tok]);
 616                 req = mandoc_malloc(sizeof(*req) + sz + 1);
 617                 req->tok = tok;
 618                 memcpy(req->name, roff_name[tok], sz + 1);
 619                 slot = ohash_qlookup(htab, req->name);
 620                 ohash_insert(htab, slot, req);
 621         }
 622         return htab;
 623 }
 624 
 625 void
 626 roffhash_free(struct ohash *htab)
 627 {
 628         struct roffreq  *req;
 629         unsigned int     slot;
 630 
 631         if (htab == NULL)
 632                 return;
 633         for (req = ohash_first(htab, &slot); req != NULL;
 634              req = ohash_next(htab, &slot))
 635                 free(req);
 636         ohash_delete(htab);
 637         free(htab);
 638 }
 639 
 640 enum roff_tok
 641 roffhash_find(struct ohash *htab, const char *name, size_t sz)
 642 {
 643         struct roffreq  *req;
 644         const char      *end;
 645 
 646         if (sz) {
 647                 end = name + sz;
 648                 req = ohash_find(htab, ohash_qlookupi(htab, name, &end));
 649         } else
 650                 req = ohash_find(htab, ohash_qlookup(htab, name));
 651         return req == NULL ? TOKEN_NONE : req->tok;
 652 }
 653 
 654 /* --- stack of request blocks -------------------------------------------- */
 655 
 656 /*
 657  * Pop the current node off of the stack of roff instructions currently
 658  * pending.
 659  */
 660 static void
 661 roffnode_pop(struct roff *r)
 662 {
 663         struct roffnode *p;
 664 
 665         assert(r->last);
 666         p = r->last;
 667 
 668         r->last = r->last->parent;
 669         free(p->name);
 670         free(p->end);
 671         free(p);
 672 }
 673 
 674 /*
 675  * Push a roff node onto the instruction stack.  This must later be
 676  * removed with roffnode_pop().
 677  */
 678 static void
 679 roffnode_push(struct roff *r, enum roff_tok tok, const char *name,
 680                 int line, int col)
 681 {
 682         struct roffnode *p;
 683 
 684         p = mandoc_calloc(1, sizeof(struct roffnode));
 685         p->tok = tok;
 686         if (name)
 687                 p->name = mandoc_strdup(name);
 688         p->parent = r->last;
 689         p->line = line;
 690         p->col = col;
 691         p->rule = p->parent ? p->parent->rule : 0;
 692 
 693         r->last = p;
 694 }
 695 
 696 /* --- roff parser state data management ---------------------------------- */
 697 
 698 static void
 699 roff_free1(struct roff *r)
 700 {
 701         struct tbl_node *tbl;
 702         int              i;
 703 
 704         while (NULL != (tbl = r->first_tbl)) {
 705                 r->first_tbl = tbl->next;
 706                 tbl_free(tbl);
 707         }
 708         r->first_tbl = r->last_tbl = r->tbl = NULL;
 709 
 710         if (r->last_eqn != NULL)
 711                 eqn_free(r->last_eqn);
 712         r->last_eqn = r->eqn = NULL;
 713 
 714         while (r->last)
 715                 roffnode_pop(r);
 716 
 717         free (r->rstack);
 718         r->rstack = NULL;
 719         r->rstacksz = 0;
 720         r->rstackpos = -1;
 721 
 722         roff_freereg(r->regtab);
 723         r->regtab = NULL;
 724 
 725         roff_freestr(r->strtab);
 726         roff_freestr(r->rentab);
 727         roff_freestr(r->xmbtab);
 728         r->strtab = r->rentab = r->xmbtab = NULL;
 729 
 730         if (r->xtab)
 731                 for (i = 0; i < 128; i++)
 732                         free(r->xtab[i].p);
 733         free(r->xtab);
 734         r->xtab = NULL;
 735 }
 736 
 737 void
 738 roff_reset(struct roff *r)
 739 {
 740         roff_free1(r);
 741         r->format = r->options & (MPARSE_MDOC | MPARSE_MAN);
 742         r->control = '\0';
 743         r->escape = '\\';
 744         roffce_lines = 0;
 745         roffce_node = NULL;
 746         roffit_lines = 0;
 747         roffit_macro = NULL;
 748 }
 749 
 750 void
 751 roff_free(struct roff *r)
 752 {
 753         roff_free1(r);
 754         roffhash_free(r->reqtab);
 755         free(r);
 756 }
 757 
 758 struct roff *
 759 roff_alloc(struct mparse *parse, int options)
 760 {
 761         struct roff     *r;
 762 
 763         r = mandoc_calloc(1, sizeof(struct roff));
 764         r->parse = parse;
 765         r->reqtab = roffhash_alloc(0, ROFF_RENAMED);
 766         r->options = options;
 767         r->format = options & (MPARSE_MDOC | MPARSE_MAN);
 768         r->rstackpos = -1;
 769         r->escape = '\\';
 770         return r;
 771 }
 772 
 773 /* --- syntax tree state data management ---------------------------------- */
 774 
 775 static void
 776 roff_man_free1(struct roff_man *man)
 777 {
 778 
 779         if (man->first != NULL)
 780                 roff_node_delete(man, man->first);
 781         free(man->meta.msec);
 782         free(man->meta.vol);
 783         free(man->meta.os);
 784         free(man->meta.arch);
 785         free(man->meta.title);
 786         free(man->meta.name);
 787         free(man->meta.date);
 788 }
 789 
 790 static void
 791 roff_man_alloc1(struct roff_man *man)
 792 {
 793 
 794         memset(&man->meta, 0, sizeof(man->meta));
 795         man->first = mandoc_calloc(1, sizeof(*man->first));
 796         man->first->type = ROFFT_ROOT;
 797         man->last = man->first;
 798         man->last_es = NULL;
 799         man->flags = 0;
 800         man->macroset = MACROSET_NONE;
 801         man->lastsec = man->lastnamed = SEC_NONE;
 802         man->next = ROFF_NEXT_CHILD;
 803 }
 804 
 805 void
 806 roff_man_reset(struct roff_man *man)
 807 {
 808 
 809         roff_man_free1(man);
 810         roff_man_alloc1(man);
 811 }
 812 
 813 void
 814 roff_man_free(struct roff_man *man)
 815 {
 816 
 817         roff_man_free1(man);
 818         free(man);
 819 }
 820 
 821 struct roff_man *
 822 roff_man_alloc(struct roff *roff, struct mparse *parse,
 823         const char *os_s, int quick)
 824 {
 825         struct roff_man *man;
 826 
 827         man = mandoc_calloc(1, sizeof(*man));
 828         man->parse = parse;
 829         man->roff = roff;
 830         man->os_s = os_s;
 831         man->quick = quick;
 832         roff_man_alloc1(man);
 833         roff->man = man;
 834         return man;
 835 }
 836 
 837 /* --- syntax tree handling ----------------------------------------------- */
 838 
 839 struct roff_node *
 840 roff_node_alloc(struct roff_man *man, int line, int pos,
 841         enum roff_type type, int tok)
 842 {
 843         struct roff_node        *n;
 844 
 845         n = mandoc_calloc(1, sizeof(*n));
 846         n->line = line;
 847         n->pos = pos;
 848         n->tok = tok;
 849         n->type = type;
 850         n->sec = man->lastsec;
 851 
 852         if (man->flags & MDOC_SYNOPSIS)
 853                 n->flags |= NODE_SYNPRETTY;
 854         else
 855                 n->flags &= ~NODE_SYNPRETTY;
 856         if (man->flags & MDOC_NEWLINE)
 857                 n->flags |= NODE_LINE;
 858         man->flags &= ~MDOC_NEWLINE;
 859 
 860         return n;
 861 }
 862 
 863 void
 864 roff_node_append(struct roff_man *man, struct roff_node *n)
 865 {
 866 
 867         switch (man->next) {
 868         case ROFF_NEXT_SIBLING:
 869                 if (man->last->next != NULL) {
 870                         n->next = man->last->next;
 871                         man->last->next->prev = n;
 872                 } else
 873                         man->last->parent->last = n;
 874                 man->last->next = n;
 875                 n->prev = man->last;
 876                 n->parent = man->last->parent;
 877                 break;
 878         case ROFF_NEXT_CHILD:
 879                 if (man->last->child != NULL) {
 880                         n->next = man->last->child;
 881                         man->last->child->prev = n;
 882                 } else
 883                         man->last->last = n;
 884                 man->last->child = n;
 885                 n->parent = man->last;
 886                 break;
 887         default:
 888                 abort();
 889         }
 890         man->last = n;
 891 
 892         switch (n->type) {
 893         case ROFFT_HEAD:
 894                 n->parent->head = n;
 895                 break;
 896         case ROFFT_BODY:
 897                 if (n->end != ENDBODY_NOT)
 898                         return;
 899                 n->parent->body = n;
 900                 break;
 901         case ROFFT_TAIL:
 902                 n->parent->tail = n;
 903                 break;
 904         default:
 905                 return;
 906         }
 907 
 908         /*
 909          * Copy over the normalised-data pointer of our parent.  Not
 910          * everybody has one, but copying a null pointer is fine.
 911          */
 912 
 913         n->norm = n->parent->norm;
 914         assert(n->parent->type == ROFFT_BLOCK);
 915 }
 916 
 917 void
 918 roff_word_alloc(struct roff_man *man, int line, int pos, const char *word)
 919 {
 920         struct roff_node        *n;
 921 
 922         n = roff_node_alloc(man, line, pos, ROFFT_TEXT, TOKEN_NONE);
 923         n->string = roff_strdup(man->roff, word);
 924         roff_node_append(man, n);
 925         n->flags |= NODE_VALID | NODE_ENDED;
 926         man->next = ROFF_NEXT_SIBLING;
 927 }
 928 
 929 void
 930 roff_word_append(struct roff_man *man, const char *word)
 931 {
 932         struct roff_node        *n;
 933         char                    *addstr, *newstr;
 934 
 935         n = man->last;
 936         addstr = roff_strdup(man->roff, word);
 937         mandoc_asprintf(&newstr, "%s %s", n->string, addstr);
 938         free(addstr);
 939         free(n->string);
 940         n->string = newstr;
 941         man->next = ROFF_NEXT_SIBLING;
 942 }
 943 
 944 void
 945 roff_elem_alloc(struct roff_man *man, int line, int pos, int tok)
 946 {
 947         struct roff_node        *n;
 948 
 949         n = roff_node_alloc(man, line, pos, ROFFT_ELEM, tok);
 950         roff_node_append(man, n);
 951         man->next = ROFF_NEXT_CHILD;
 952 }
 953 
 954 struct roff_node *
 955 roff_block_alloc(struct roff_man *man, int line, int pos, int tok)
 956 {
 957         struct roff_node        *n;
 958 
 959         n = roff_node_alloc(man, line, pos, ROFFT_BLOCK, tok);
 960         roff_node_append(man, n);
 961         man->next = ROFF_NEXT_CHILD;
 962         return n;
 963 }
 964 
 965 struct roff_node *
 966 roff_head_alloc(struct roff_man *man, int line, int pos, int tok)
 967 {
 968         struct roff_node        *n;
 969 
 970         n = roff_node_alloc(man, line, pos, ROFFT_HEAD, tok);
 971         roff_node_append(man, n);
 972         man->next = ROFF_NEXT_CHILD;
 973         return n;
 974 }
 975 
 976 struct roff_node *
 977 roff_body_alloc(struct roff_man *man, int line, int pos, int tok)
 978 {
 979         struct roff_node        *n;
 980 
 981         n = roff_node_alloc(man, line, pos, ROFFT_BODY, tok);
 982         roff_node_append(man, n);
 983         man->next = ROFF_NEXT_CHILD;
 984         return n;
 985 }
 986 
 987 static void
 988 roff_addtbl(struct roff_man *man, struct tbl_node *tbl)
 989 {
 990         struct roff_node        *n;
 991         const struct tbl_span   *span;
 992 
 993         if (man->macroset == MACROSET_MAN)
 994                 man_breakscope(man, ROFF_TS);
 995         while ((span = tbl_span(tbl)) != NULL) {
 996                 n = roff_node_alloc(man, tbl->line, 0, ROFFT_TBL, TOKEN_NONE);
 997                 n->span = span;
 998                 roff_node_append(man, n);
 999                 n->flags |= NODE_VALID | NODE_ENDED;
1000                 man->next = ROFF_NEXT_SIBLING;
1001         }
1002 }
1003 
1004 void
1005 roff_node_unlink(struct roff_man *man, struct roff_node *n)
1006 {
1007 
1008         /* Adjust siblings. */
1009 
1010         if (n->prev)
1011                 n->prev->next = n->next;
1012         if (n->next)
1013                 n->next->prev = n->prev;
1014 
1015         /* Adjust parent. */
1016 
1017         if (n->parent != NULL) {
1018                 if (n->parent->child == n)
1019                         n->parent->child = n->next;
1020                 if (n->parent->last == n)
1021                         n->parent->last = n->prev;
1022         }
1023 
1024         /* Adjust parse point. */
1025 
1026         if (man == NULL)
1027                 return;
1028         if (man->last == n) {
1029                 if (n->prev == NULL) {
1030                         man->last = n->parent;
1031                         man->next = ROFF_NEXT_CHILD;
1032                 } else {
1033                         man->last = n->prev;
1034                         man->next = ROFF_NEXT_SIBLING;
1035                 }
1036         }
1037         if (man->first == n)
1038                 man->first = NULL;
1039 }
1040 
1041 void
1042 roff_node_free(struct roff_node *n)
1043 {
1044 
1045         if (n->args != NULL)
1046                 mdoc_argv_free(n->args);
1047         if (n->type == ROFFT_BLOCK || n->type == ROFFT_ELEM)
1048                 free(n->norm);
1049         if (n->eqn != NULL)
1050                 eqn_box_free(n->eqn);
1051         free(n->string);
1052         free(n);
1053 }
1054 
1055 void
1056 roff_node_delete(struct roff_man *man, struct roff_node *n)
1057 {
1058 
1059         while (n->child != NULL)
1060                 roff_node_delete(man, n->child);
1061         roff_node_unlink(man, n);
1062         roff_node_free(n);
1063 }
1064 
1065 void
1066 deroff(char **dest, const struct roff_node *n)
1067 {
1068         char    *cp;
1069         size_t   sz;
1070 
1071         if (n->type != ROFFT_TEXT) {
1072                 for (n = n->child; n != NULL; n = n->next)
1073                         deroff(dest, n);
1074                 return;
1075         }
1076 
1077         /* Skip leading whitespace. */
1078 
1079         for (cp = n->string; *cp != '\0'; cp++) {
1080                 if (cp[0] == '\\' && cp[1] != '\0' &&
1081                     strchr(" %&0^|~", cp[1]) != NULL)
1082                         cp++;
1083                 else if ( ! isspace((unsigned char)*cp))
1084                         break;
1085         }
1086 
1087         /* Skip trailing backslash. */
1088 
1089         sz = strlen(cp);
1090         if (sz > 0 && cp[sz - 1] == '\\')
1091                 sz--;
1092 
1093         /* Skip trailing whitespace. */
1094 
1095         for (; sz; sz--)
1096                 if ( ! isspace((unsigned char)cp[sz-1]))
1097                         break;
1098 
1099         /* Skip empty strings. */
1100 
1101         if (sz == 0)
1102                 return;
1103 
1104         if (*dest == NULL) {
1105                 *dest = mandoc_strndup(cp, sz);
1106                 return;
1107         }
1108 
1109         mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp);
1110         free(*dest);
1111         *dest = cp;
1112 }
1113 
1114 /* --- main functions of the roff parser ---------------------------------- */
1115 
1116 /*
1117  * In the current line, expand escape sequences that tend to get
1118  * used in numerical expressions and conditional requests.
1119  * Also check the syntax of the remaining escape sequences.
1120  */
1121 static enum rofferr
1122 roff_res(struct roff *r, struct buf *buf, int ln, int pos)
1123 {
1124         char             ubuf[24]; /* buffer to print the number */
1125         struct roff_node *n;    /* used for header comments */
1126         const char      *start; /* start of the string to process */
1127         char            *stesc; /* start of an escape sequence ('\\') */
1128         char            *ep;    /* end of comment string */
1129         const char      *stnam; /* start of the name, after "[(*" */
1130         const char      *cp;    /* end of the name, e.g. before ']' */
1131         const char      *res;   /* the string to be substituted */
1132         char            *nbuf;  /* new buffer to copy buf->buf to */
1133         size_t           maxl;  /* expected length of the escape name */
1134         size_t           naml;  /* actual length of the escape name */
1135         enum mandoc_esc  esc;   /* type of the escape sequence */
1136         int              inaml; /* length returned from mandoc_escape() */
1137         int              expand_count;  /* to avoid infinite loops */
1138         int              npos;  /* position in numeric expression */
1139         int              arg_complete; /* argument not interrupted by eol */
1140         int              done;  /* no more input available */
1141         int              deftype; /* type of definition to paste */
1142         int              rcsid; /* kind of RCS id seen */
1143         char             sign;  /* increment number register */
1144         char             term;  /* character terminating the escape */
1145 
1146         /* Search forward for comments. */
1147 
1148         done = 0;
1149         start = buf->buf + pos;
1150         for (stesc = buf->buf + pos; *stesc != '\0'; stesc++) {
1151                 if (stesc[0] != r->escape || stesc[1] == '\0')
1152                         continue;
1153                 stesc++;
1154                 if (*stesc != '"' && *stesc != '#')
1155                         continue;
1156 
1157                 /* Comment found, look for RCS id. */
1158 
1159                 rcsid = 0;
1160                 if ((cp = strstr(stesc, "$" "OpenBSD")) != NULL) {
1161                         rcsid = 1 << MANDOC_OS_OPENBSD;
1162                         cp += 8;
1163                 } else if ((cp = strstr(stesc, "$" "NetBSD")) != NULL) {
1164                         rcsid = 1 << MANDOC_OS_NETBSD;
1165                         cp += 7;
1166                 }
1167                 if (cp != NULL &&
1168                     isalnum((unsigned char)*cp) == 0 &&
1169                     strchr(cp, '$') != NULL) {
1170                         if (r->man->meta.rcsids & rcsid)
1171                                 mandoc_msg(MANDOCERR_RCS_REP, r->parse,
1172                                     ln, stesc + 1 - buf->buf, stesc + 1);
1173                         r->man->meta.rcsids |= rcsid;
1174                 }
1175 
1176                 /* Handle trailing whitespace. */
1177 
1178                 ep = strchr(stesc--, '\0') - 1;
1179                 if (*ep == '\n') {
1180                         done = 1;
1181                         ep--;
1182                 }
1183                 if (*ep == ' ' || *ep == '\t')
1184                         mandoc_msg(MANDOCERR_SPACE_EOL, r->parse,
1185                             ln, ep - buf->buf, NULL);
1186 
1187                 /*
1188                  * Save comments preceding the title macro
1189                  * in the syntax tree.
1190                  */
1191 
1192                 if (r->format == 0) {
1193                         while (*ep == ' ' || *ep == '\t')
1194                                 ep--;
1195                         ep[1] = '\0';
1196                         n = roff_node_alloc(r->man,
1197                             ln, stesc + 1 - buf->buf,
1198                             ROFFT_COMMENT, TOKEN_NONE);
1199                         n->string = mandoc_strdup(stesc + 2);
1200                         roff_node_append(r->man, n);
1201                         n->flags |= NODE_VALID | NODE_ENDED;
1202                         r->man->next = ROFF_NEXT_SIBLING;
1203                 }
1204 
1205                 /* Discard comments. */
1206 
1207                 while (stesc > start && stesc[-1] == ' ')
1208                         stesc--;
1209                 *stesc = '\0';
1210                 break;
1211         }
1212         if (stesc == start)
1213                 return ROFF_CONT;
1214         stesc--;
1215 
1216         /* Notice the end of the input. */
1217 
1218         if (*stesc == '\n') {
1219                 *stesc-- = '\0';
1220                 done = 1;
1221         }
1222 
1223         expand_count = 0;
1224         while (stesc >= start) {
1225 
1226                 /* Search backwards for the next backslash. */
1227 
1228                 if (*stesc != r->escape) {
1229                         if (*stesc == '\\') {
1230                                 *stesc = '\0';
1231                                 buf->sz = mandoc_asprintf(&nbuf, "%s\\e%s",
1232                                     buf->buf, stesc + 1) + 1;
1233                                 start = nbuf + pos;
1234                                 stesc = nbuf + (stesc - buf->buf);
1235                                 free(buf->buf);
1236                                 buf->buf = nbuf;
1237                         }
1238                         stesc--;
1239                         continue;
1240                 }
1241 
1242                 /* If it is escaped, skip it. */
1243 
1244                 for (cp = stesc - 1; cp >= start; cp--)
1245                         if (*cp != r->escape)
1246                                 break;
1247 
1248                 if ((stesc - cp) % 2 == 0) {
1249                         while (stesc > cp)
1250                                 *stesc-- = '\\';
1251                         continue;
1252                 } else if (stesc[1] != '\0') {
1253                         *stesc = '\\';
1254                 } else {
1255                         *stesc-- = '\0';
1256                         if (done)
1257                                 continue;
1258                         else
1259                                 return ROFF_APPEND;
1260                 }
1261 
1262                 /* Decide whether to expand or to check only. */
1263 
1264                 term = '\0';
1265                 cp = stesc + 1;
1266                 switch (*cp) {
1267                 case '*':
1268                         res = NULL;
1269                         break;
1270                 case 'B':
1271                 case 'w':
1272                         term = cp[1];
1273                         /* FALLTHROUGH */
1274                 case 'n':
1275                         sign = cp[1];
1276                         if (sign == '+' || sign == '-')
1277                                 cp++;
1278                         res = ubuf;
1279                         break;
1280                 default:
1281                         esc = mandoc_escape(&cp, &stnam, &inaml);
1282                         if (esc == ESCAPE_ERROR ||
1283                             (esc == ESCAPE_SPECIAL &&
1284                              mchars_spec2cp(stnam, inaml) < 0))
1285                                 mandoc_vmsg(MANDOCERR_ESC_BAD,
1286                                     r->parse, ln, (int)(stesc - buf->buf),
1287                                     "%.*s", (int)(cp - stesc), stesc);
1288                         stesc--;
1289                         continue;
1290                 }
1291 
1292                 if (EXPAND_LIMIT < ++expand_count) {
1293                         mandoc_msg(MANDOCERR_ROFFLOOP, r->parse,
1294                             ln, (int)(stesc - buf->buf), NULL);
1295                         return ROFF_IGN;
1296                 }
1297 
1298                 /*
1299                  * The third character decides the length
1300                  * of the name of the string or register.
1301                  * Save a pointer to the name.
1302                  */
1303 
1304                 if (term == '\0') {
1305                         switch (*++cp) {
1306                         case '\0':
1307                                 maxl = 0;
1308                                 break;
1309                         case '(':
1310                                 cp++;
1311                                 maxl = 2;
1312                                 break;
1313                         case '[':
1314                                 cp++;
1315                                 term = ']';
1316                                 maxl = 0;
1317                                 break;
1318                         default:
1319                                 maxl = 1;
1320                                 break;
1321                         }
1322                 } else {
1323                         cp += 2;
1324                         maxl = 0;
1325                 }
1326                 stnam = cp;
1327 
1328                 /* Advance to the end of the name. */
1329 
1330                 naml = 0;
1331                 arg_complete = 1;
1332                 while (maxl == 0 || naml < maxl) {
1333                         if (*cp == '\0') {
1334                                 mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
1335                                     ln, (int)(stesc - buf->buf), stesc);
1336                                 arg_complete = 0;
1337                                 break;
1338                         }
1339                         if (maxl == 0 && *cp == term) {
1340                                 cp++;
1341                                 break;
1342                         }
1343                         if (*cp++ != '\\' || stesc[1] != 'w') {
1344                                 naml++;
1345                                 continue;
1346                         }
1347                         switch (mandoc_escape(&cp, NULL, NULL)) {
1348                         case ESCAPE_SPECIAL:
1349                         case ESCAPE_UNICODE:
1350                         case ESCAPE_NUMBERED:
1351                         case ESCAPE_OVERSTRIKE:
1352                                 naml++;
1353                                 break;
1354                         default:
1355                                 break;
1356                         }
1357                 }
1358 
1359                 /*
1360                  * Retrieve the replacement string; if it is
1361                  * undefined, resume searching for escapes.
1362                  */
1363 
1364                 switch (stesc[1]) {
1365                 case '*':
1366                         if (arg_complete) {
1367                                 deftype = ROFFDEF_USER | ROFFDEF_PRE;
1368                                 res = roff_getstrn(r, stnam, naml, &deftype);
1369                         }
1370                         break;
1371                 case 'B':
1372                         npos = 0;
1373                         ubuf[0] = arg_complete &&
1374                             roff_evalnum(r, ln, stnam, &npos,
1375                               NULL, ROFFNUM_SCALE) &&
1376                             stnam + npos + 1 == cp ? '1' : '0';
1377                         ubuf[1] = '\0';
1378                         break;
1379                 case 'n':
1380                         if (arg_complete)
1381                                 (void)snprintf(ubuf, sizeof(ubuf), "%d",
1382                                     roff_getregn(r, stnam, naml, sign));
1383                         else
1384                                 ubuf[0] = '\0';
1385                         break;
1386                 case 'w':
1387                         /* use even incomplete args */
1388                         (void)snprintf(ubuf, sizeof(ubuf), "%d",
1389                             24 * (int)naml);
1390                         break;
1391                 }
1392 
1393                 if (res == NULL) {
1394                         mandoc_vmsg(MANDOCERR_STR_UNDEF,
1395                             r->parse, ln, (int)(stesc - buf->buf),
1396                             "%.*s", (int)naml, stnam);
1397                         res = "";
1398                 } else if (buf->sz + strlen(res) > SHRT_MAX) {
1399                         mandoc_msg(MANDOCERR_ROFFLOOP, r->parse,
1400                             ln, (int)(stesc - buf->buf), NULL);
1401                         return ROFF_IGN;
1402                 }
1403 
1404                 /* Replace the escape sequence by the string. */
1405 
1406                 *stesc = '\0';
1407                 buf->sz = mandoc_asprintf(&nbuf, "%s%s%s",
1408                     buf->buf, res, cp) + 1;
1409 
1410                 /* Prepare for the next replacement. */
1411 
1412                 start = nbuf + pos;
1413                 stesc = nbuf + (stesc - buf->buf) + strlen(res);
1414                 free(buf->buf);
1415                 buf->buf = nbuf;
1416         }
1417         return ROFF_CONT;
1418 }
1419 
1420 /*
1421  * Process text streams.
1422  */
1423 static enum rofferr
1424 roff_parsetext(struct roff *r, struct buf *buf, int pos, int *offs)
1425 {
1426         size_t           sz;
1427         const char      *start;
1428         char            *p;
1429         int              isz;
1430         enum mandoc_esc  esc;
1431 
1432         /* Spring the input line trap. */
1433 
1434         if (roffit_lines == 1) {
1435                 isz = mandoc_asprintf(&p, "%s\n.%s", buf->buf, roffit_macro);
1436                 free(buf->buf);
1437                 buf->buf = p;
1438                 buf->sz = isz + 1;
1439                 *offs = 0;
1440                 free(roffit_macro);
1441                 roffit_lines = 0;
1442                 return ROFF_REPARSE;
1443         } else if (roffit_lines > 1)
1444                 --roffit_lines;
1445 
1446         if (roffce_node != NULL && buf->buf[pos] != '\0') {
1447                 if (roffce_lines < 1) {
1448                         r->man->last = roffce_node;
1449                         r->man->next = ROFF_NEXT_SIBLING;
1450                         roffce_lines = 0;
1451                         roffce_node = NULL;
1452                 } else
1453                         roffce_lines--;
1454         }
1455 
1456         /* Convert all breakable hyphens into ASCII_HYPH. */
1457 
1458         start = p = buf->buf + pos;
1459 
1460         while (*p != '\0') {
1461                 sz = strcspn(p, "-\\");
1462                 p += sz;
1463 
1464                 if (*p == '\0')
1465                         break;
1466 
1467                 if (*p == '\\') {
1468                         /* Skip over escapes. */
1469                         p++;
1470                         esc = mandoc_escape((const char **)&p, NULL, NULL);
1471                         if (esc == ESCAPE_ERROR)
1472                                 break;
1473                         while (*p == '-')
1474                                 p++;
1475                         continue;
1476                 } else if (p == start) {
1477                         p++;
1478                         continue;
1479                 }
1480 
1481                 if (isalpha((unsigned char)p[-1]) &&
1482                     isalpha((unsigned char)p[1]))
1483                         *p = ASCII_HYPH;
1484                 p++;
1485         }
1486         return ROFF_CONT;
1487 }
1488 
1489 enum rofferr
1490 roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs)
1491 {
1492         enum roff_tok    t;
1493         enum rofferr     e;
1494         int              pos;   /* parse point */
1495         int              spos;  /* saved parse point for messages */
1496         int              ppos;  /* original offset in buf->buf */
1497         int              ctl;   /* macro line (boolean) */
1498 
1499         ppos = pos = *offs;
1500 
1501         /* Handle in-line equation delimiters. */
1502 
1503         if (r->tbl == NULL &&
1504             r->last_eqn != NULL && r->last_eqn->delim &&
1505             (r->eqn == NULL || r->eqn_inline)) {
1506                 e = roff_eqndelim(r, buf, pos);
1507                 if (e == ROFF_REPARSE)
1508                         return e;
1509                 assert(e == ROFF_CONT);
1510         }
1511 
1512         /* Expand some escape sequences. */
1513 
1514         e = roff_res(r, buf, ln, pos);
1515         if (e == ROFF_IGN || e == ROFF_APPEND)
1516                 return e;
1517         assert(e == ROFF_CONT);
1518 
1519         ctl = roff_getcontrol(r, buf->buf, &pos);
1520 
1521         /*
1522          * First, if a scope is open and we're not a macro, pass the
1523          * text through the macro's filter.
1524          * Equations process all content themselves.
1525          * Tables process almost all content themselves, but we want
1526          * to warn about macros before passing it there.
1527          */
1528 
1529         if (r->last != NULL && ! ctl) {
1530                 t = r->last->tok;
1531                 e = (*roffs[t].text)(r, t, buf, ln, pos, pos, offs);
1532                 if (e == ROFF_IGN)
1533                         return e;
1534                 assert(e == ROFF_CONT);
1535         }
1536         if (r->eqn != NULL && strncmp(buf->buf + ppos, ".EN", 3)) {
1537                 eqn_read(r->eqn, buf->buf + ppos);
1538                 return ROFF_IGN;
1539         }
1540         if (r->tbl != NULL && (ctl == 0 || buf->buf[pos] == '\0')) {
1541                 tbl_read(r->tbl, ln, buf->buf, ppos);
1542                 roff_addtbl(r->man, r->tbl);
1543                 return ROFF_IGN;
1544         }
1545         if ( ! ctl)
1546                 return roff_parsetext(r, buf, pos, offs);
1547 
1548         /* Skip empty request lines. */
1549 
1550         if (buf->buf[pos] == '"') {
1551                 mandoc_msg(MANDOCERR_COMMENT_BAD, r->parse,
1552                     ln, pos, NULL);
1553                 return ROFF_IGN;
1554         } else if (buf->buf[pos] == '\0')
1555                 return ROFF_IGN;
1556 
1557         /*
1558          * If a scope is open, go to the child handler for that macro,
1559          * as it may want to preprocess before doing anything with it.
1560          * Don't do so if an equation is open.
1561          */
1562 
1563         if (r->last) {
1564                 t = r->last->tok;
1565                 return (*roffs[t].sub)(r, t, buf, ln, ppos, pos, offs);
1566         }
1567 
1568         /* No scope is open.  This is a new request or macro. */
1569 
1570         spos = pos;
1571         t = roff_parse(r, buf->buf, &pos, ln, ppos);
1572 
1573         /* Tables ignore most macros. */
1574 
1575         if (r->tbl != NULL && (t == TOKEN_NONE || t == ROFF_TS ||
1576             t == ROFF_br || t == ROFF_ce || t == ROFF_rj || t == ROFF_sp)) {
1577                 mandoc_msg(MANDOCERR_TBLMACRO, r->parse,
1578                     ln, pos, buf->buf + spos);
1579                 if (t != TOKEN_NONE)
1580                         return ROFF_IGN;
1581                 while (buf->buf[pos] != '\0' && buf->buf[pos] != ' ')
1582                         pos++;
1583                 while (buf->buf[pos] == ' ')
1584                         pos++;
1585                 tbl_read(r->tbl, ln, buf->buf, pos);
1586                 roff_addtbl(r->man, r->tbl);
1587                 return ROFF_IGN;
1588         }
1589 
1590         /* For now, let high level macros abort .ce mode. */
1591 
1592         if (ctl && roffce_node != NULL &&
1593             (t == TOKEN_NONE || t == ROFF_Dd || t == ROFF_EQ ||
1594              t == ROFF_TH || t == ROFF_TS)) {
1595                 r->man->last = roffce_node;
1596                 r->man->next = ROFF_NEXT_SIBLING;
1597                 roffce_lines = 0;
1598                 roffce_node = NULL;
1599         }
1600 
1601         /*
1602          * This is neither a roff request nor a user-defined macro.
1603          * Let the standard macro set parsers handle it.
1604          */
1605 
1606         if (t == TOKEN_NONE)
1607                 return ROFF_CONT;
1608 
1609         /* Execute a roff request or a user defined macro. */
1610 
1611         return (*roffs[t].proc)(r, t, buf, ln, spos, pos, offs);
1612 }
1613 
1614 void
1615 roff_endparse(struct roff *r)
1616 {
1617         if (r->last != NULL)
1618                 mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
1619                     r->last->line, r->last->col,
1620                     roff_name[r->last->tok]);
1621 
1622         if (r->eqn != NULL) {
1623                 mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
1624                     r->eqn->node->line, r->eqn->node->pos, "EQ");
1625                 eqn_parse(r->eqn);
1626                 r->eqn = NULL;
1627         }
1628 
1629         if (r->tbl != NULL) {
1630                 mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
1631                     r->tbl->line, r->tbl->pos, "TS");
1632                 tbl_end(r->tbl);
1633                 r->tbl = NULL;
1634         }
1635 }
1636 
1637 /*
1638  * Parse a roff node's type from the input buffer.  This must be in the
1639  * form of ".foo xxx" in the usual way.
1640  */
1641 static enum roff_tok
1642 roff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos)
1643 {
1644         char            *cp;
1645         const char      *mac;
1646         size_t           maclen;
1647         int              deftype;
1648         enum roff_tok    t;
1649 
1650         cp = buf + *pos;
1651 
1652         if ('\0' == *cp || '"' == *cp || '\t' == *cp || ' ' == *cp)
1653                 return TOKEN_NONE;
1654 
1655         mac = cp;
1656         maclen = roff_getname(r, &cp, ln, ppos);
1657 
1658         deftype = ROFFDEF_USER | ROFFDEF_REN;
1659         r->current_string = roff_getstrn(r, mac, maclen, &deftype);
1660         switch (deftype) {
1661         case ROFFDEF_USER:
1662                 t = ROFF_USERDEF;
1663                 break;
1664         case ROFFDEF_REN:
1665                 t = ROFF_RENAMED;
1666                 break;
1667         default:
1668                 t = roffhash_find(r->reqtab, mac, maclen);
1669                 break;
1670         }
1671         if (t != TOKEN_NONE)
1672                 *pos = cp - buf;
1673         else if (deftype == ROFFDEF_UNDEF) {
1674                 /* Using an undefined macro defines it to be empty. */
1675                 roff_setstrn(&r->strtab, mac, maclen, "", 0, 0);
1676                 roff_setstrn(&r->rentab, mac, maclen, NULL, 0, 0);
1677         }
1678         return t;
1679 }
1680 
1681 /* --- handling of request blocks ----------------------------------------- */
1682 
1683 static enum rofferr
1684 roff_cblock(ROFF_ARGS)
1685 {
1686 
1687         /*
1688          * A block-close `..' should only be invoked as a child of an
1689          * ignore macro, otherwise raise a warning and just ignore it.
1690          */
1691 
1692         if (r->last == NULL) {
1693                 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1694                     ln, ppos, "..");
1695                 return ROFF_IGN;
1696         }
1697 
1698         switch (r->last->tok) {
1699         case ROFF_am:
1700                 /* ROFF_am1 is remapped to ROFF_am in roff_block(). */
1701         case ROFF_ami:
1702         case ROFF_de:
1703                 /* ROFF_de1 is remapped to ROFF_de in roff_block(). */
1704         case ROFF_dei:
1705         case ROFF_ig:
1706                 break;
1707         default:
1708                 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1709                     ln, ppos, "..");
1710                 return ROFF_IGN;
1711         }
1712 
1713         if (buf->buf[pos] != '\0')
1714                 mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos,
1715                     ".. %s", buf->buf + pos);
1716 
1717         roffnode_pop(r);
1718         roffnode_cleanscope(r);
1719         return ROFF_IGN;
1720 
1721 }
1722 
1723 static void
1724 roffnode_cleanscope(struct roff *r)
1725 {
1726 
1727         while (r->last) {
1728                 if (--r->last->endspan != 0)
1729                         break;
1730                 roffnode_pop(r);
1731         }
1732 }
1733 
1734 static void
1735 roff_ccond(struct roff *r, int ln, int ppos)
1736 {
1737 
1738         if (NULL == r->last) {
1739                 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1740                     ln, ppos, "\\}");
1741                 return;
1742         }
1743 
1744         switch (r->last->tok) {
1745         case ROFF_el:
1746         case ROFF_ie:
1747         case ROFF_if:
1748                 break;
1749         default:
1750                 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1751                     ln, ppos, "\\}");
1752                 return;
1753         }
1754 
1755         if (r->last->endspan > -1) {
1756                 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1757                     ln, ppos, "\\}");
1758                 return;
1759         }
1760 
1761         roffnode_pop(r);
1762         roffnode_cleanscope(r);
1763         return;
1764 }
1765 
1766 static enum rofferr
1767 roff_block(ROFF_ARGS)
1768 {
1769         const char      *name, *value;
1770         char            *call, *cp, *iname, *rname;
1771         size_t           csz, namesz, rsz;
1772         int              deftype;
1773 
1774         /* Ignore groff compatibility mode for now. */
1775 
1776         if (tok == ROFF_de1)
1777                 tok = ROFF_de;
1778         else if (tok == ROFF_dei1)
1779                 tok = ROFF_dei;
1780         else if (tok == ROFF_am1)
1781                 tok = ROFF_am;
1782         else if (tok == ROFF_ami1)
1783                 tok = ROFF_ami;
1784 
1785         /* Parse the macro name argument. */
1786 
1787         cp = buf->buf + pos;
1788         if (tok == ROFF_ig) {
1789                 iname = NULL;
1790                 namesz = 0;
1791         } else {
1792                 iname = cp;
1793                 namesz = roff_getname(r, &cp, ln, ppos);
1794                 iname[namesz] = '\0';
1795         }
1796 
1797         /* Resolve the macro name argument if it is indirect. */
1798 
1799         if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
1800                 deftype = ROFFDEF_USER;
1801                 name = roff_getstrn(r, iname, namesz, &deftype);
1802                 if (name == NULL) {
1803                         mandoc_vmsg(MANDOCERR_STR_UNDEF,
1804                             r->parse, ln, (int)(iname - buf->buf),
1805                             "%.*s", (int)namesz, iname);
1806                         namesz = 0;
1807                 } else
1808                         namesz = strlen(name);
1809         } else
1810                 name = iname;
1811 
1812         if (namesz == 0 && tok != ROFF_ig) {
1813                 mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse,
1814                     ln, ppos, roff_name[tok]);
1815                 return ROFF_IGN;
1816         }
1817 
1818         roffnode_push(r, tok, name, ln, ppos);
1819 
1820         /*
1821          * At the beginning of a `de' macro, clear the existing string
1822          * with the same name, if there is one.  New content will be
1823          * appended from roff_block_text() in multiline mode.
1824          */
1825 
1826         if (tok == ROFF_de || tok == ROFF_dei) {
1827                 roff_setstrn(&r->strtab, name, namesz, "", 0, 0);
1828                 roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
1829         } else if (tok == ROFF_am || tok == ROFF_ami) {
1830                 deftype = ROFFDEF_ANY;
1831                 value = roff_getstrn(r, iname, namesz, &deftype);
1832                 switch (deftype) {  /* Before appending, ... */
1833                 case ROFFDEF_PRE: /* copy predefined to user-defined. */
1834                         roff_setstrn(&r->strtab, name, namesz,
1835                             value, strlen(value), 0);
1836                         break;
1837                 case ROFFDEF_REN: /* call original standard macro. */
1838                         csz = mandoc_asprintf(&call, ".%.*s \\$* \\\"\n",
1839                             (int)strlen(value), value);
1840                         roff_setstrn(&r->strtab, name, namesz, call, csz, 0);
1841                         roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
1842                         free(call);
1843                         break;
1844                 case ROFFDEF_STD:  /* rename and call standard macro. */
1845                         rsz = mandoc_asprintf(&rname, "__%s_renamed", name);
1846                         roff_setstrn(&r->rentab, rname, rsz, name, namesz, 0);
1847                         csz = mandoc_asprintf(&call, ".%.*s \\$* \\\"\n",
1848                             (int)rsz, rname);
1849                         roff_setstrn(&r->strtab, name, namesz, call, csz, 0);
1850                         free(call);
1851                         free(rname);
1852                         break;
1853                 default:
1854                         break;
1855                 }
1856         }
1857 
1858         if (*cp == '\0')
1859                 return ROFF_IGN;
1860 
1861         /* Get the custom end marker. */
1862 
1863         iname = cp;
1864         namesz = roff_getname(r, &cp, ln, ppos);
1865 
1866         /* Resolve the end marker if it is indirect. */
1867 
1868         if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
1869                 deftype = ROFFDEF_USER;
1870                 name = roff_getstrn(r, iname, namesz, &deftype);
1871                 if (name == NULL) {
1872                         mandoc_vmsg(MANDOCERR_STR_UNDEF,
1873                             r->parse, ln, (int)(iname - buf->buf),
1874                             "%.*s", (int)namesz, iname);
1875                         namesz = 0;
1876                 } else
1877                         namesz = strlen(name);
1878         } else
1879                 name = iname;
1880 
1881         if (namesz)
1882                 r->last->end = mandoc_strndup(name, namesz);
1883 
1884         if (*cp != '\0')
1885                 mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse,
1886                     ln, pos, ".%s ... %s", roff_name[tok], cp);
1887 
1888         return ROFF_IGN;
1889 }
1890 
1891 static enum rofferr
1892 roff_block_sub(ROFF_ARGS)
1893 {
1894         enum roff_tok   t;
1895         int             i, j;
1896 
1897         /*
1898          * First check whether a custom macro exists at this level.  If
1899          * it does, then check against it.  This is some of groff's
1900          * stranger behaviours.  If we encountered a custom end-scope
1901          * tag and that tag also happens to be a "real" macro, then we
1902          * need to try interpreting it again as a real macro.  If it's
1903          * not, then return ignore.  Else continue.
1904          */
1905 
1906         if (r->last->end) {
1907                 for (i = pos, j = 0; r->last->end[j]; j++, i++)
1908                         if (buf->buf[i] != r->last->end[j])
1909                                 break;
1910 
1911                 if (r->last->end[j] == '\0' &&
1912                     (buf->buf[i] == '\0' ||
1913                      buf->buf[i] == ' ' ||
1914                      buf->buf[i] == '\t')) {
1915                         roffnode_pop(r);
1916                         roffnode_cleanscope(r);
1917 
1918                         while (buf->buf[i] == ' ' || buf->buf[i] == '\t')
1919                                 i++;
1920 
1921                         pos = i;
1922                         if (roff_parse(r, buf->buf, &pos, ln, ppos) !=
1923                             TOKEN_NONE)
1924                                 return ROFF_RERUN;
1925                         return ROFF_IGN;
1926                 }
1927         }
1928 
1929         /*
1930          * If we have no custom end-query or lookup failed, then try
1931          * pulling it out of the hashtable.
1932          */
1933 
1934         t = roff_parse(r, buf->buf, &pos, ln, ppos);
1935 
1936         if (t != ROFF_cblock) {
1937                 if (tok != ROFF_ig)
1938                         roff_setstr(r, r->last->name, buf->buf + ppos, 2);
1939                 return ROFF_IGN;
1940         }
1941 
1942         return (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs);
1943 }
1944 
1945 static enum rofferr
1946 roff_block_text(ROFF_ARGS)
1947 {
1948 
1949         if (tok != ROFF_ig)
1950                 roff_setstr(r, r->last->name, buf->buf + pos, 2);
1951 
1952         return ROFF_IGN;
1953 }
1954 
1955 static enum rofferr
1956 roff_cond_sub(ROFF_ARGS)
1957 {
1958         enum roff_tok    t;
1959         char            *ep;
1960         int              rr;
1961 
1962         rr = r->last->rule;
1963         roffnode_cleanscope(r);
1964 
1965         /*
1966          * If `\}' occurs on a macro line without a preceding macro,
1967          * drop the line completely.
1968          */
1969 
1970         ep = buf->buf + pos;
1971         if (ep[0] == '\\' && ep[1] == '}')
1972                 rr = 0;
1973 
1974         /* Always check for the closing delimiter `\}'. */
1975 
1976         while ((ep = strchr(ep, '\\')) != NULL) {
1977                 switch (ep[1]) {
1978                 case '}':
1979                         memmove(ep, ep + 2, strlen(ep + 2) + 1);
1980                         roff_ccond(r, ln, ep - buf->buf);
1981                         break;
1982                 case '\0':
1983                         ++ep;
1984                         break;
1985                 default:
1986                         ep += 2;
1987                         break;
1988                 }
1989         }
1990 
1991         /*
1992          * Fully handle known macros when they are structurally
1993          * required or when the conditional evaluated to true.
1994          */
1995 
1996         t = roff_parse(r, buf->buf, &pos, ln, ppos);
1997         return t != TOKEN_NONE && (rr || roffs[t].flags & ROFFMAC_STRUCT)
1998             ? (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs) : rr
1999             ? ROFF_CONT : ROFF_IGN;
2000 }
2001 
2002 static enum rofferr
2003 roff_cond_text(ROFF_ARGS)
2004 {
2005         char            *ep;
2006         int              rr;
2007 
2008         rr = r->last->rule;
2009         roffnode_cleanscope(r);
2010 
2011         ep = buf->buf + pos;
2012         while ((ep = strchr(ep, '\\')) != NULL) {
2013                 if (*(++ep) == '}') {
2014                         *ep = '&';
2015                         roff_ccond(r, ln, ep - buf->buf - 1);
2016                 }
2017                 if (*ep != '\0')
2018                         ++ep;
2019         }
2020         return rr ? ROFF_CONT : ROFF_IGN;
2021 }
2022 
2023 /* --- handling of numeric and conditional expressions -------------------- */
2024 
2025 /*
2026  * Parse a single signed integer number.  Stop at the first non-digit.
2027  * If there is at least one digit, return success and advance the
2028  * parse point, else return failure and let the parse point unchanged.
2029  * Ignore overflows, treat them just like the C language.
2030  */
2031 static int
2032 roff_getnum(const char *v, int *pos, int *res, int flags)
2033 {
2034         int      myres, scaled, n, p;
2035 
2036         if (NULL == res)
2037                 res = &myres;
2038 
2039         p = *pos;
2040         n = v[p] == '-';
2041         if (n || v[p] == '+')
2042                 p++;
2043 
2044         if (flags & ROFFNUM_WHITE)
2045                 while (isspace((unsigned char)v[p]))
2046                         p++;
2047 
2048         for (*res = 0; isdigit((unsigned char)v[p]); p++)
2049                 *res = 10 * *res + v[p] - '0';
2050         if (p == *pos + n)
2051                 return 0;
2052 
2053         if (n)
2054                 *res = -*res;
2055 
2056         /* Each number may be followed by one optional scaling unit. */
2057 
2058         switch (v[p]) {
2059         case 'f':
2060                 scaled = *res * 65536;
2061                 break;
2062         case 'i':
2063                 scaled = *res * 240;
2064                 break;
2065         case 'c':
2066                 scaled = *res * 240 / 2.54;
2067                 break;
2068         case 'v':
2069         case 'P':
2070                 scaled = *res * 40;
2071                 break;
2072         case 'm':
2073         case 'n':
2074                 scaled = *res * 24;
2075                 break;
2076         case 'p':
2077                 scaled = *res * 10 / 3;
2078                 break;
2079         case 'u':
2080                 scaled = *res;
2081                 break;
2082         case 'M':
2083                 scaled = *res * 6 / 25;
2084                 break;
2085         default:
2086                 scaled = *res;
2087                 p--;
2088                 break;
2089         }
2090         if (flags & ROFFNUM_SCALE)
2091                 *res = scaled;
2092 
2093         *pos = p + 1;
2094         return 1;
2095 }
2096 
2097 /*
2098  * Evaluate a string comparison condition.
2099  * The first character is the delimiter.
2100  * Succeed if the string up to its second occurrence
2101  * matches the string up to its third occurence.
2102  * Advance the cursor after the third occurrence
2103  * or lacking that, to the end of the line.
2104  */
2105 static int
2106 roff_evalstrcond(const char *v, int *pos)
2107 {
2108         const char      *s1, *s2, *s3;
2109         int              match;
2110 
2111         match = 0;
2112         s1 = v + *pos;          /* initial delimiter */
2113         s2 = s1 + 1;            /* for scanning the first string */
2114         s3 = strchr(s2, *s1);   /* for scanning the second string */
2115 
2116         if (NULL == s3)         /* found no middle delimiter */
2117                 goto out;
2118 
2119         while ('\0' != *++s3) {
2120                 if (*s2 != *s3) {  /* mismatch */
2121                         s3 = strchr(s3, *s1);
2122                         break;
2123                 }
2124                 if (*s3 == *s1) {  /* found the final delimiter */
2125                         match = 1;
2126                         break;
2127                 }
2128                 s2++;
2129         }
2130 
2131 out:
2132         if (NULL == s3)
2133                 s3 = strchr(s2, '\0');
2134         else if (*s3 != '\0')
2135                 s3++;
2136         *pos = s3 - v;
2137         return match;
2138 }
2139 
2140 /*
2141  * Evaluate an optionally negated single character, numerical,
2142  * or string condition.
2143  */
2144 static int
2145 roff_evalcond(struct roff *r, int ln, char *v, int *pos)
2146 {
2147         char    *cp, *name;
2148         size_t   sz;
2149         int      deftype, number, savepos, istrue, wanttrue;
2150 
2151         if ('!' == v[*pos]) {
2152                 wanttrue = 0;
2153                 (*pos)++;
2154         } else
2155                 wanttrue = 1;
2156 
2157         switch (v[*pos]) {
2158         case '\0':
2159                 return 0;
2160         case 'n':
2161         case 'o':
2162                 (*pos)++;
2163                 return wanttrue;
2164         case 'c':
2165         case 'e':
2166         case 't':
2167         case 'v':
2168                 (*pos)++;
2169                 return !wanttrue;
2170         case 'd':
2171         case 'r':
2172                 cp = v + *pos + 1;
2173                 while (*cp == ' ')
2174                         cp++;
2175                 name = cp;
2176                 sz = roff_getname(r, &cp, ln, cp - v);
2177                 if (sz == 0)
2178                         istrue = 0;
2179                 else if (v[*pos] == 'r')
2180                         istrue = roff_hasregn(r, name, sz);
2181                 else {
2182                         deftype = ROFFDEF_ANY;
2183                         roff_getstrn(r, name, sz, &deftype);
2184                         istrue = !!deftype;
2185                 }
2186                 *pos = cp - v;
2187                 return istrue == wanttrue;
2188         default:
2189                 break;
2190         }
2191 
2192         savepos = *pos;
2193         if (roff_evalnum(r, ln, v, pos, &number, ROFFNUM_SCALE))
2194                 return (number > 0) == wanttrue;
2195         else if (*pos == savepos)
2196                 return roff_evalstrcond(v, pos) == wanttrue;
2197         else
2198                 return 0;
2199 }
2200 
2201 static enum rofferr
2202 roff_line_ignore(ROFF_ARGS)
2203 {
2204 
2205         return ROFF_IGN;
2206 }
2207 
2208 static enum rofferr
2209 roff_insec(ROFF_ARGS)
2210 {
2211 
2212         mandoc_msg(MANDOCERR_REQ_INSEC, r->parse,
2213             ln, ppos, roff_name[tok]);
2214         return ROFF_IGN;
2215 }
2216 
2217 static enum rofferr
2218 roff_unsupp(ROFF_ARGS)
2219 {
2220 
2221         mandoc_msg(MANDOCERR_REQ_UNSUPP, r->parse,
2222             ln, ppos, roff_name[tok]);
2223         return ROFF_IGN;
2224 }
2225 
2226 static enum rofferr
2227 roff_cond(ROFF_ARGS)
2228 {
2229 
2230         roffnode_push(r, tok, NULL, ln, ppos);
2231 
2232         /*
2233          * An `.el' has no conditional body: it will consume the value
2234          * of the current rstack entry set in prior `ie' calls or
2235          * defaults to DENY.
2236          *
2237          * If we're not an `el', however, then evaluate the conditional.
2238          */
2239 
2240         r->last->rule = tok == ROFF_el ?
2241             (r->rstackpos < 0 ? 0 : r->rstack[r->rstackpos--]) :
2242             roff_evalcond(r, ln, buf->buf, &pos);
2243 
2244         /*
2245          * An if-else will put the NEGATION of the current evaluated
2246          * conditional into the stack of rules.
2247          */
2248 
2249         if (tok == ROFF_ie) {
2250                 if (r->rstackpos + 1 == r->rstacksz) {
2251                         r->rstacksz += 16;
2252                         r->rstack = mandoc_reallocarray(r->rstack,
2253                             r->rstacksz, sizeof(int));
2254                 }
2255                 r->rstack[++r->rstackpos] = !r->last->rule;
2256         }
2257 
2258         /* If the parent has false as its rule, then so do we. */
2259 
2260         if (r->last->parent && !r->last->parent->rule)
2261                 r->last->rule = 0;
2262 
2263         /*
2264          * Determine scope.
2265          * If there is nothing on the line after the conditional,
2266          * not even whitespace, use next-line scope.
2267          */
2268 
2269         if (buf->buf[pos] == '\0') {
2270                 r->last->endspan = 2;
2271                 goto out;
2272         }
2273 
2274         while (buf->buf[pos] == ' ')
2275                 pos++;
2276 
2277         /* An opening brace requests multiline scope. */
2278 
2279         if (buf->buf[pos] == '\\' && buf->buf[pos + 1] == '{') {
2280                 r->last->endspan = -1;
2281                 pos += 2;
2282                 while (buf->buf[pos] == ' ')
2283                         pos++;
2284                 goto out;
2285         }
2286 
2287         /*
2288          * Anything else following the conditional causes
2289          * single-line scope.  Warn if the scope contains
2290          * nothing but trailing whitespace.
2291          */
2292 
2293         if (buf->buf[pos] == '\0')
2294                 mandoc_msg(MANDOCERR_COND_EMPTY, r->parse,
2295                     ln, ppos, roff_name[tok]);
2296 
2297         r->last->endspan = 1;
2298 
2299 out:
2300         *offs = pos;
2301         return ROFF_RERUN;
2302 }
2303 
2304 static enum rofferr
2305 roff_ds(ROFF_ARGS)
2306 {
2307         char            *string;
2308         const char      *name;
2309         size_t           namesz;
2310 
2311         /* Ignore groff compatibility mode for now. */
2312 
2313         if (tok == ROFF_ds1)
2314                 tok = ROFF_ds;
2315         else if (tok == ROFF_as1)
2316                 tok = ROFF_as;
2317 
2318         /*
2319          * The first word is the name of the string.
2320          * If it is empty or terminated by an escape sequence,
2321          * abort the `ds' request without defining anything.
2322          */
2323 
2324         name = string = buf->buf + pos;
2325         if (*name == '\0')
2326                 return ROFF_IGN;
2327 
2328         namesz = roff_getname(r, &string, ln, pos);
2329         if (name[namesz] == '\\')
2330                 return ROFF_IGN;
2331 
2332         /* Read past the initial double-quote, if any. */
2333         if (*string == '"')
2334                 string++;
2335 
2336         /* The rest is the value. */
2337         roff_setstrn(&r->strtab, name, namesz, string, strlen(string),
2338             ROFF_as == tok);
2339         roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
2340         return ROFF_IGN;
2341 }
2342 
2343 /*
2344  * Parse a single operator, one or two characters long.
2345  * If the operator is recognized, return success and advance the
2346  * parse point, else return failure and let the parse point unchanged.
2347  */
2348 static int
2349 roff_getop(const char *v, int *pos, char *res)
2350 {
2351 
2352         *res = v[*pos];
2353 
2354         switch (*res) {
2355         case '+':
2356         case '-':
2357         case '*':
2358         case '/':
2359         case '%':
2360         case '&':
2361         case ':':
2362                 break;
2363         case '<':
2364                 switch (v[*pos + 1]) {
2365                 case '=':
2366                         *res = 'l';
2367                         (*pos)++;
2368                         break;
2369                 case '>':
2370                         *res = '!';
2371                         (*pos)++;
2372                         break;
2373                 case '?':
2374                         *res = 'i';
2375                         (*pos)++;
2376                         break;
2377                 default:
2378                         break;
2379                 }
2380                 break;
2381         case '>':
2382                 switch (v[*pos + 1]) {
2383                 case '=':
2384                         *res = 'g';
2385                         (*pos)++;
2386                         break;
2387                 case '?':
2388                         *res = 'a';
2389                         (*pos)++;
2390                         break;
2391                 default:
2392                         break;
2393                 }
2394                 break;
2395         case '=':
2396                 if ('=' == v[*pos + 1])
2397                         (*pos)++;
2398                 break;
2399         default:
2400                 return 0;
2401         }
2402         (*pos)++;
2403 
2404         return *res;
2405 }
2406 
2407 /*
2408  * Evaluate either a parenthesized numeric expression
2409  * or a single signed integer number.
2410  */
2411 static int
2412 roff_evalpar(struct roff *r, int ln,
2413         const char *v, int *pos, int *res, int flags)
2414 {
2415 
2416         if ('(' != v[*pos])
2417                 return roff_getnum(v, pos, res, flags);
2418 
2419         (*pos)++;
2420         if ( ! roff_evalnum(r, ln, v, pos, res, flags | ROFFNUM_WHITE))
2421                 return 0;
2422 
2423         /*
2424          * Omission of the closing parenthesis
2425          * is an error in validation mode,
2426          * but ignored in evaluation mode.
2427          */
2428 
2429         if (')' == v[*pos])
2430                 (*pos)++;
2431         else if (NULL == res)
2432                 return 0;
2433 
2434         return 1;
2435 }
2436 
2437 /*
2438  * Evaluate a complete numeric expression.
2439  * Proceed left to right, there is no concept of precedence.
2440  */
2441 static int
2442 roff_evalnum(struct roff *r, int ln, const char *v,
2443         int *pos, int *res, int flags)
2444 {
2445         int              mypos, operand2;
2446         char             operator;
2447 
2448         if (NULL == pos) {
2449                 mypos = 0;
2450                 pos = &mypos;
2451         }
2452 
2453         if (flags & ROFFNUM_WHITE)
2454                 while (isspace((unsigned char)v[*pos]))
2455                         (*pos)++;
2456 
2457         if ( ! roff_evalpar(r, ln, v, pos, res, flags))
2458                 return 0;
2459 
2460         while (1) {
2461                 if (flags & ROFFNUM_WHITE)
2462                         while (isspace((unsigned char)v[*pos]))
2463                                 (*pos)++;
2464 
2465                 if ( ! roff_getop(v, pos, &operator))
2466                         break;
2467 
2468                 if (flags & ROFFNUM_WHITE)
2469                         while (isspace((unsigned char)v[*pos]))
2470                                 (*pos)++;
2471 
2472                 if ( ! roff_evalpar(r, ln, v, pos, &operand2, flags))
2473                         return 0;
2474 
2475                 if (flags & ROFFNUM_WHITE)
2476                         while (isspace((unsigned char)v[*pos]))
2477                                 (*pos)++;
2478 
2479                 if (NULL == res)
2480                         continue;
2481 
2482                 switch (operator) {
2483                 case '+':
2484                         *res += operand2;
2485                         break;
2486                 case '-':
2487                         *res -= operand2;
2488                         break;
2489                 case '*':
2490                         *res *= operand2;
2491                         break;
2492                 case '/':
2493                         if (operand2 == 0) {
2494                                 mandoc_msg(MANDOCERR_DIVZERO,
2495                                         r->parse, ln, *pos, v);
2496                                 *res = 0;
2497                                 break;
2498                         }
2499                         *res /= operand2;
2500                         break;
2501                 case '%':
2502                         if (operand2 == 0) {
2503                                 mandoc_msg(MANDOCERR_DIVZERO,
2504                                         r->parse, ln, *pos, v);
2505                                 *res = 0;
2506                                 break;
2507                         }
2508                         *res %= operand2;
2509                         break;
2510                 case '<':
2511                         *res = *res < operand2;
2512                         break;
2513                 case '>':
2514                         *res = *res > operand2;
2515                         break;
2516                 case 'l':
2517                         *res = *res <= operand2;
2518                         break;
2519                 case 'g':
2520                         *res = *res >= operand2;
2521                         break;
2522                 case '=':
2523                         *res = *res == operand2;
2524                         break;
2525                 case '!':
2526                         *res = *res != operand2;
2527                         break;
2528                 case '&':
2529                         *res = *res && operand2;
2530                         break;
2531                 case ':':
2532                         *res = *res || operand2;
2533                         break;
2534                 case 'i':
2535                         if (operand2 < *res)
2536                                 *res = operand2;
2537                         break;
2538                 case 'a':
2539                         if (operand2 > *res)
2540                                 *res = operand2;
2541                         break;
2542                 default:
2543                         abort();
2544                 }
2545         }
2546         return 1;
2547 }
2548 
2549 /* --- register management ------------------------------------------------ */
2550 
2551 void
2552 roff_setreg(struct roff *r, const char *name, int val, char sign)
2553 {
2554         roff_setregn(r, name, strlen(name), val, sign, INT_MIN);
2555 }
2556 
2557 static void
2558 roff_setregn(struct roff *r, const char *name, size_t len,
2559     int val, char sign, int step)
2560 {
2561         struct roffreg  *reg;
2562 
2563         /* Search for an existing register with the same name. */
2564         reg = r->regtab;
2565 
2566         while (reg != NULL && (reg->key.sz != len ||
2567             strncmp(reg->key.p, name, len) != 0))
2568                 reg = reg->next;
2569 
2570         if (NULL == reg) {
2571                 /* Create a new register. */
2572                 reg = mandoc_malloc(sizeof(struct roffreg));
2573                 reg->key.p = mandoc_strndup(name, len);
2574                 reg->key.sz = len;
2575                 reg->val = 0;
2576                 reg->step = 0;
2577                 reg->next = r->regtab;
2578                 r->regtab = reg;
2579         }
2580 
2581         if ('+' == sign)
2582                 reg->val += val;
2583         else if ('-' == sign)
2584                 reg->val -= val;
2585         else
2586                 reg->val = val;
2587         if (step != INT_MIN)
2588                 reg->step = step;
2589 }
2590 
2591 /*
2592  * Handle some predefined read-only number registers.
2593  * For now, return -1 if the requested register is not predefined;
2594  * in case a predefined read-only register having the value -1
2595  * were to turn up, another special value would have to be chosen.
2596  */
2597 static int
2598 roff_getregro(const struct roff *r, const char *name)
2599 {
2600 
2601         switch (*name) {
2602         case '$':  /* Number of arguments of the last macro evaluated. */
2603                 return r->argc;
2604         case 'A':  /* ASCII approximation mode is always off. */
2605                 return 0;
2606         case 'g':  /* Groff compatibility mode is always on. */
2607                 return 1;
2608         case 'H':  /* Fixed horizontal resolution. */
2609                 return 24;
2610         case 'j':  /* Always adjust left margin only. */
2611                 return 0;
2612         case 'T':  /* Some output device is always defined. */
2613                 return 1;
2614         case 'V':  /* Fixed vertical resolution. */
2615                 return 40;
2616         default:
2617                 return -1;
2618         }
2619 }
2620 
2621 int
2622 roff_getreg(struct roff *r, const char *name)
2623 {
2624         return roff_getregn(r, name, strlen(name), '\0');
2625 }
2626 
2627 static int
2628 roff_getregn(struct roff *r, const char *name, size_t len, char sign)
2629 {
2630         struct roffreg  *reg;
2631         int              val;
2632 
2633         if ('.' == name[0] && 2 == len) {
2634                 val = roff_getregro(r, name + 1);
2635                 if (-1 != val)
2636                         return val;
2637         }
2638 
2639         for (reg = r->regtab; reg; reg = reg->next) {
2640                 if (len == reg->key.sz &&
2641                     0 == strncmp(name, reg->key.p, len)) {
2642                         switch (sign) {
2643                         case '+':
2644                                 reg->val += reg->step;
2645                                 break;
2646                         case '-':
2647                                 reg->val -= reg->step;
2648                                 break;
2649                         default:
2650                                 break;
2651                         }
2652                         return reg->val;
2653                 }
2654         }
2655 
2656         roff_setregn(r, name, len, 0, '\0', INT_MIN);
2657         return 0;
2658 }
2659 
2660 static int
2661 roff_hasregn(const struct roff *r, const char *name, size_t len)
2662 {
2663         struct roffreg  *reg;
2664         int              val;
2665 
2666         if ('.' == name[0] && 2 == len) {
2667                 val = roff_getregro(r, name + 1);
2668                 if (-1 != val)
2669                         return 1;
2670         }
2671 
2672         for (reg = r->regtab; reg; reg = reg->next)
2673                 if (len == reg->key.sz &&
2674                     0 == strncmp(name, reg->key.p, len))
2675                         return 1;
2676 
2677         return 0;
2678 }
2679 
2680 static void
2681 roff_freereg(struct roffreg *reg)
2682 {
2683         struct roffreg  *old_reg;
2684 
2685         while (NULL != reg) {
2686                 free(reg->key.p);
2687                 old_reg = reg;
2688                 reg = reg->next;
2689                 free(old_reg);
2690         }
2691 }
2692 
2693 static enum rofferr
2694 roff_nr(ROFF_ARGS)
2695 {
2696         char            *key, *val, *step;
2697         size_t           keysz;
2698         int              iv, is, len;
2699         char             sign;
2700 
2701         key = val = buf->buf + pos;
2702         if (*key == '\0')
2703                 return ROFF_IGN;
2704 
2705         keysz = roff_getname(r, &val, ln, pos);
2706         if (key[keysz] == '\\')
2707                 return ROFF_IGN;
2708 
2709         sign = *val;
2710         if (sign == '+' || sign == '-')
2711                 val++;
2712 
2713         len = 0;
2714         if (roff_evalnum(r, ln, val, &len, &iv, ROFFNUM_SCALE) == 0)
2715                 return ROFF_IGN;
2716 
2717         step = val + len;
2718         while (isspace((unsigned char)*step))
2719                 step++;
2720         if (roff_evalnum(r, ln, step, NULL, &is, 0) == 0)
2721                 is = INT_MIN;
2722 
2723         roff_setregn(r, key, keysz, iv, sign, is);
2724         return ROFF_IGN;
2725 }
2726 
2727 static enum rofferr
2728 roff_rr(ROFF_ARGS)
2729 {
2730         struct roffreg  *reg, **prev;
2731         char            *name, *cp;
2732         size_t           namesz;
2733 
2734         name = cp = buf->buf + pos;
2735         if (*name == '\0')
2736                 return ROFF_IGN;
2737         namesz = roff_getname(r, &cp, ln, pos);
2738         name[namesz] = '\0';
2739 
2740         prev = &r->regtab;
2741         while (1) {
2742                 reg = *prev;
2743                 if (reg == NULL || !strcmp(name, reg->key.p))
2744                         break;
2745                 prev = &reg->next;
2746         }
2747         if (reg != NULL) {
2748                 *prev = reg->next;
2749                 free(reg->key.p);
2750                 free(reg);
2751         }
2752         return ROFF_IGN;
2753 }
2754 
2755 /* --- handler functions for roff requests -------------------------------- */
2756 
2757 static enum rofferr
2758 roff_rm(ROFF_ARGS)
2759 {
2760         const char       *name;
2761         char             *cp;
2762         size_t            namesz;
2763 
2764         cp = buf->buf + pos;
2765         while (*cp != '\0') {
2766                 name = cp;
2767                 namesz = roff_getname(r, &cp, ln, (int)(cp - buf->buf));
2768                 roff_setstrn(&r->strtab, name, namesz, NULL, 0, 0);
2769                 roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
2770                 if (name[namesz] == '\\')
2771                         break;
2772         }
2773         return ROFF_IGN;
2774 }
2775 
2776 static enum rofferr
2777 roff_it(ROFF_ARGS)
2778 {
2779         int              iv;
2780 
2781         /* Parse the number of lines. */
2782 
2783         if ( ! roff_evalnum(r, ln, buf->buf, &pos, &iv, 0)) {
2784                 mandoc_msg(MANDOCERR_IT_NONUM, r->parse,
2785                     ln, ppos, buf->buf + 1);
2786                 return ROFF_IGN;
2787         }
2788 
2789         while (isspace((unsigned char)buf->buf[pos]))
2790                 pos++;
2791 
2792         /*
2793          * Arm the input line trap.
2794          * Special-casing "an-trap" is an ugly workaround to cope
2795          * with DocBook stupidly fiddling with man(7) internals.
2796          */
2797 
2798         roffit_lines = iv;
2799         roffit_macro = mandoc_strdup(iv != 1 ||
2800             strcmp(buf->buf + pos, "an-trap") ?
2801             buf->buf + pos : "br");
2802         return ROFF_IGN;
2803 }
2804 
2805 static enum rofferr
2806 roff_Dd(ROFF_ARGS)
2807 {
2808         int              mask;
2809         enum roff_tok    t, te;
2810 
2811         switch (tok) {
2812         case ROFF_Dd:
2813                 tok = MDOC_Dd;
2814                 te = MDOC_MAX;
2815                 if (r->format == 0)
2816                         r->format = MPARSE_MDOC;
2817                 mask = MPARSE_MDOC | MPARSE_QUICK;
2818                 break;
2819         case ROFF_TH:
2820                 tok = MAN_TH;
2821                 te = MAN_MAX;
2822                 if (r->format == 0)
2823                         r->format = MPARSE_MAN;
2824                 mask = MPARSE_QUICK;
2825                 break;
2826         default:
2827                 abort();
2828         }
2829         if ((r->options & mask) == 0)
2830                 for (t = tok; t < te; t++)
2831                         roff_setstr(r, roff_name[t], NULL, 0);
2832         return ROFF_CONT;
2833 }
2834 
2835 static enum rofferr
2836 roff_TE(ROFF_ARGS)
2837 {
2838         if (r->tbl == NULL) {
2839                 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
2840                     ln, ppos, "TE");
2841                 return ROFF_IGN;
2842         }
2843         if (tbl_end(r->tbl) == 0) {
2844                 r->tbl = NULL;
2845                 free(buf->buf);
2846                 buf->buf = mandoc_strdup(".sp");
2847                 buf->sz = 4;
2848                 *offs = 0;
2849                 return ROFF_REPARSE;
2850         }
2851         r->tbl = NULL;
2852         return ROFF_IGN;
2853 }
2854 
2855 static enum rofferr
2856 roff_T_(ROFF_ARGS)
2857 {
2858 
2859         if (NULL == r->tbl)
2860                 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
2861                     ln, ppos, "T&");
2862         else
2863                 tbl_restart(ln, ppos, r->tbl);
2864 
2865         return ROFF_IGN;
2866 }
2867 
2868 /*
2869  * Handle in-line equation delimiters.
2870  */
2871 static enum rofferr
2872 roff_eqndelim(struct roff *r, struct buf *buf, int pos)
2873 {
2874         char            *cp1, *cp2;
2875         const char      *bef_pr, *bef_nl, *mac, *aft_nl, *aft_pr;
2876 
2877         /*
2878          * Outside equations, look for an opening delimiter.
2879          * If we are inside an equation, we already know it is
2880          * in-line, or this function wouldn't have been called;
2881          * so look for a closing delimiter.
2882          */
2883 
2884         cp1 = buf->buf + pos;
2885         cp2 = strchr(cp1, r->eqn == NULL ?
2886             r->last_eqn->odelim : r->last_eqn->cdelim);
2887         if (cp2 == NULL)
2888                 return ROFF_CONT;
2889 
2890         *cp2++ = '\0';
2891         bef_pr = bef_nl = aft_nl = aft_pr = "";
2892 
2893         /* Handle preceding text, protecting whitespace. */
2894 
2895         if (*buf->buf != '\0') {
2896                 if (r->eqn == NULL)
2897                         bef_pr = "\\&";
2898                 bef_nl = "\n";
2899         }
2900 
2901         /*
2902          * Prepare replacing the delimiter with an equation macro
2903          * and drop leading white space from the equation.
2904          */
2905 
2906         if (r->eqn == NULL) {
2907                 while (*cp2 == ' ')
2908                         cp2++;
2909                 mac = ".EQ";
2910         } else
2911                 mac = ".EN";
2912 
2913         /* Handle following text, protecting whitespace. */
2914 
2915         if (*cp2 != '\0') {
2916                 aft_nl = "\n";
2917                 if (r->eqn != NULL)
2918                         aft_pr = "\\&";
2919         }
2920 
2921         /* Do the actual replacement. */
2922 
2923         buf->sz = mandoc_asprintf(&cp1, "%s%s%s%s%s%s%s", buf->buf,
2924             bef_pr, bef_nl, mac, aft_nl, aft_pr, cp2) + 1;
2925         free(buf->buf);
2926         buf->buf = cp1;
2927 
2928         /* Toggle the in-line state of the eqn subsystem. */
2929 
2930         r->eqn_inline = r->eqn == NULL;
2931         return ROFF_REPARSE;
2932 }
2933 
2934 static enum rofferr
2935 roff_EQ(ROFF_ARGS)
2936 {
2937         struct roff_node        *n;
2938 
2939         if (r->man->macroset == MACROSET_MAN)
2940                 man_breakscope(r->man, ROFF_EQ);
2941         n = roff_node_alloc(r->man, ln, ppos, ROFFT_EQN, TOKEN_NONE);
2942         if (ln > r->man->last->line)
2943                 n->flags |= NODE_LINE;
2944         n->eqn = mandoc_calloc(1, sizeof(*n->eqn));
2945         n->eqn->expectargs = UINT_MAX;
2946         roff_node_append(r->man, n);
2947         r->man->next = ROFF_NEXT_SIBLING;
2948 
2949         assert(r->eqn == NULL);
2950         if (r->last_eqn == NULL)
2951                 r->last_eqn = eqn_alloc(r->parse);
2952         else
2953                 eqn_reset(r->last_eqn);
2954         r->eqn = r->last_eqn;
2955         r->eqn->node = n;
2956 
2957         if (buf->buf[pos] != '\0')
2958                 mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos,
2959                     ".EQ %s", buf->buf + pos);
2960 
2961         return ROFF_IGN;
2962 }
2963 
2964 static enum rofferr
2965 roff_EN(ROFF_ARGS)
2966 {
2967         if (r->eqn != NULL) {
2968                 eqn_parse(r->eqn);
2969                 r->eqn = NULL;
2970         } else
2971                 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, ln, ppos, "EN");
2972         if (buf->buf[pos] != '\0')
2973                 mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos,
2974                     "EN %s", buf->buf + pos);
2975         return ROFF_IGN;
2976 }
2977 
2978 static enum rofferr
2979 roff_TS(ROFF_ARGS)
2980 {
2981         if (r->tbl != NULL) {
2982                 mandoc_msg(MANDOCERR_BLK_BROKEN, r->parse,
2983                     ln, ppos, "TS breaks TS");
2984                 tbl_end(r->tbl);
2985         }
2986         r->tbl = tbl_alloc(ppos, ln, r->parse);
2987         if (r->last_tbl)
2988                 r->last_tbl->next = r->tbl;
2989         else
2990                 r->first_tbl = r->tbl;
2991         r->last_tbl = r->tbl;
2992         return ROFF_IGN;
2993 }
2994 
2995 static enum rofferr
2996 roff_onearg(ROFF_ARGS)
2997 {
2998         struct roff_node        *n;
2999         char                    *cp;
3000         int                      npos;
3001 
3002         if (r->man->flags & (MAN_BLINE | MAN_ELINE) &&
3003             (tok == ROFF_ce || tok == ROFF_rj || tok == ROFF_sp ||
3004              tok == ROFF_ti))
3005                 man_breakscope(r->man, tok);
3006 
3007         if (roffce_node != NULL && (tok == ROFF_ce || tok == ROFF_rj)) {
3008                 r->man->last = roffce_node;
3009                 r->man->next = ROFF_NEXT_SIBLING;
3010         }
3011 
3012         roff_elem_alloc(r->man, ln, ppos, tok);
3013         n = r->man->last;
3014 
3015         cp = buf->buf + pos;
3016         if (*cp != '\0') {
3017                 while (*cp != '\0' && *cp != ' ')
3018                         cp++;
3019                 while (*cp == ' ')
3020                         *cp++ = '\0';
3021                 if (*cp != '\0')
3022                         mandoc_vmsg(MANDOCERR_ARG_EXCESS,
3023                             r->parse, ln, cp - buf->buf,
3024                             "%s ... %s", roff_name[tok], cp);
3025                 roff_word_alloc(r->man, ln, pos, buf->buf + pos);
3026         }
3027 
3028         if (tok == ROFF_ce || tok == ROFF_rj) {
3029                 if (r->man->last->type == ROFFT_ELEM) {
3030                         roff_word_alloc(r->man, ln, pos, "1");
3031                         r->man->last->flags |= NODE_NOSRC;
3032                 }
3033                 npos = 0;
3034                 if (roff_evalnum(r, ln, r->man->last->string, &npos,
3035                     &roffce_lines, 0) == 0) {
3036                         mandoc_vmsg(MANDOCERR_CE_NONUM,
3037                             r->parse, ln, pos, "ce %s", buf->buf + pos);
3038                         roffce_lines = 1;
3039                 }
3040                 if (roffce_lines < 1) {
3041                         r->man->last = r->man->last->parent;
3042                         roffce_node = NULL;
3043                         roffce_lines = 0;
3044                 } else
3045                         roffce_node = r->man->last->parent;
3046         } else {
3047                 n->flags |= NODE_VALID | NODE_ENDED;
3048                 r->man->last = n;
3049         }
3050         n->flags |= NODE_LINE;
3051         r->man->next = ROFF_NEXT_SIBLING;
3052         return ROFF_IGN;
3053 }
3054 
3055 static enum rofferr
3056 roff_manyarg(ROFF_ARGS)
3057 {
3058         struct roff_node        *n;
3059         char                    *sp, *ep;
3060 
3061         roff_elem_alloc(r->man, ln, ppos, tok);
3062         n = r->man->last;
3063 
3064         for (sp = ep = buf->buf + pos; *sp != '\0'; sp = ep) {
3065                 while (*ep != '\0' && *ep != ' ')
3066                         ep++;
3067                 while (*ep == ' ')
3068                         *ep++ = '\0';
3069                 roff_word_alloc(r->man, ln, sp - buf->buf, sp);
3070         }
3071 
3072         n->flags |= NODE_LINE | NODE_VALID | NODE_ENDED;
3073         r->man->last = n;
3074         r->man->next = ROFF_NEXT_SIBLING;
3075         return ROFF_IGN;
3076 }
3077 
3078 static enum rofferr
3079 roff_als(ROFF_ARGS)
3080 {
3081         char            *oldn, *newn, *end, *value;
3082         size_t           oldsz, newsz, valsz;
3083 
3084         newn = oldn = buf->buf + pos;
3085         if (*newn == '\0')
3086                 return ROFF_IGN;
3087 
3088         newsz = roff_getname(r, &oldn, ln, pos);
3089         if (newn[newsz] == '\\' || *oldn == '\0')
3090                 return ROFF_IGN;
3091 
3092         end = oldn;
3093         oldsz = roff_getname(r, &end, ln, oldn - buf->buf);
3094         if (oldsz == 0)
3095                 return ROFF_IGN;
3096 
3097         valsz = mandoc_asprintf(&value, ".%.*s \\$*\\\"\n",
3098             (int)oldsz, oldn);
3099         roff_setstrn(&r->strtab, newn, newsz, value, valsz, 0);
3100         roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
3101         free(value);
3102         return ROFF_IGN;
3103 }
3104 
3105 static enum rofferr
3106 roff_br(ROFF_ARGS)
3107 {
3108         if (r->man->flags & (MAN_BLINE | MAN_ELINE))
3109                 man_breakscope(r->man, ROFF_br);
3110         roff_elem_alloc(r->man, ln, ppos, ROFF_br);
3111         if (buf->buf[pos] != '\0')
3112                 mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos,
3113                     "%s %s", roff_name[tok], buf->buf + pos);
3114         r->man->last->flags |= NODE_LINE | NODE_VALID | NODE_ENDED;
3115         r->man->next = ROFF_NEXT_SIBLING;
3116         return ROFF_IGN;
3117 }
3118 
3119 static enum rofferr
3120 roff_cc(ROFF_ARGS)
3121 {
3122         const char      *p;
3123 
3124         p = buf->buf + pos;
3125 
3126         if (*p == '\0' || (r->control = *p++) == '.')
3127                 r->control = '\0';
3128 
3129         if (*p != '\0')
3130                 mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse,
3131                     ln, p - buf->buf, "cc ... %s", p);
3132 
3133         return ROFF_IGN;
3134 }
3135 
3136 static enum rofferr
3137 roff_ec(ROFF_ARGS)
3138 {
3139         const char      *p;
3140 
3141         p = buf->buf + pos;
3142         if (*p == '\0')
3143                 r->escape = '\\';
3144         else {
3145                 r->escape = *p;
3146                 if (*++p != '\0')
3147                         mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse,
3148                             ln, p - buf->buf, "ec ... %s", p);
3149         }
3150         return ROFF_IGN;
3151 }
3152 
3153 static enum rofferr
3154 roff_eo(ROFF_ARGS)
3155 {
3156         r->escape = '\0';
3157         if (buf->buf[pos] != '\0')
3158                 mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse,
3159                     ln, pos, "eo %s", buf->buf + pos);
3160         return ROFF_IGN;
3161 }
3162 
3163 static enum rofferr
3164 roff_tr(ROFF_ARGS)
3165 {
3166         const char      *p, *first, *second;
3167         size_t           fsz, ssz;
3168         enum mandoc_esc  esc;
3169 
3170         p = buf->buf + pos;
3171 
3172         if (*p == '\0') {
3173                 mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse, ln, ppos, "tr");
3174                 return ROFF_IGN;
3175         }
3176 
3177         while (*p != '\0') {
3178                 fsz = ssz = 1;
3179 
3180                 first = p++;
3181                 if (*first == '\\') {
3182                         esc = mandoc_escape(&p, NULL, NULL);
3183                         if (esc == ESCAPE_ERROR) {
3184                                 mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
3185                                     ln, (int)(p - buf->buf), first);
3186                                 return ROFF_IGN;
3187                         }
3188                         fsz = (size_t)(p - first);
3189                 }
3190 
3191                 second = p++;
3192                 if (*second == '\\') {
3193                         esc = mandoc_escape(&p, NULL, NULL);
3194                         if (esc == ESCAPE_ERROR) {
3195                                 mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
3196                                     ln, (int)(p - buf->buf), second);
3197                                 return ROFF_IGN;
3198                         }
3199                         ssz = (size_t)(p - second);
3200                 } else if (*second == '\0') {
3201                         mandoc_vmsg(MANDOCERR_TR_ODD, r->parse,
3202                             ln, first - buf->buf, "tr %s", first);
3203                         second = " ";
3204                         p--;
3205                 }
3206 
3207                 if (fsz > 1) {
3208                         roff_setstrn(&r->xmbtab, first, fsz,
3209                             second, ssz, 0);
3210                         continue;
3211                 }
3212 
3213                 if (r->xtab == NULL)
3214                         r->xtab = mandoc_calloc(128,
3215                             sizeof(struct roffstr));
3216 
3217                 free(r->xtab[(int)*first].p);
3218                 r->xtab[(int)*first].p = mandoc_strndup(second, ssz);
3219                 r->xtab[(int)*first].sz = ssz;
3220         }
3221 
3222         return ROFF_IGN;
3223 }
3224 
3225 static enum rofferr
3226 roff_rn(ROFF_ARGS)
3227 {
3228         const char      *value;
3229         char            *oldn, *newn, *end;
3230         size_t           oldsz, newsz;
3231         int              deftype;
3232 
3233         oldn = newn = buf->buf + pos;
3234         if (*oldn == '\0')
3235                 return ROFF_IGN;
3236 
3237         oldsz = roff_getname(r, &newn, ln, pos);
3238         if (oldn[oldsz] == '\\' || *newn == '\0')
3239                 return ROFF_IGN;
3240 
3241         end = newn;
3242         newsz = roff_getname(r, &end, ln, newn - buf->buf);
3243         if (newsz == 0)
3244                 return ROFF_IGN;
3245 
3246         deftype = ROFFDEF_ANY;
3247         value = roff_getstrn(r, oldn, oldsz, &deftype);
3248         switch (deftype) {
3249         case ROFFDEF_USER:
3250                 roff_setstrn(&r->strtab, newn, newsz, value, strlen(value), 0);
3251                 roff_setstrn(&r->strtab, oldn, oldsz, NULL, 0, 0);
3252                 roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
3253                 break;
3254         case ROFFDEF_PRE:
3255                 roff_setstrn(&r->strtab, newn, newsz, value, strlen(value), 0);
3256                 roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
3257                 break;
3258         case ROFFDEF_REN:
3259                 roff_setstrn(&r->rentab, newn, newsz, value, strlen(value), 0);
3260                 roff_setstrn(&r->rentab, oldn, oldsz, NULL, 0, 0);
3261                 roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0);
3262                 break;
3263         case ROFFDEF_STD:
3264                 roff_setstrn(&r->rentab, newn, newsz, oldn, oldsz, 0);
3265                 roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0);
3266                 break;
3267         default:
3268                 roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0);
3269                 roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
3270                 break;
3271         }
3272         return ROFF_IGN;
3273 }
3274 
3275 static enum rofferr
3276 roff_so(ROFF_ARGS)
3277 {
3278         char *name, *cp;
3279 
3280         name = buf->buf + pos;
3281         mandoc_vmsg(MANDOCERR_SO, r->parse, ln, ppos, "so %s", name);
3282 
3283         /*
3284          * Handle `so'.  Be EXTREMELY careful, as we shouldn't be
3285          * opening anything that's not in our cwd or anything beneath
3286          * it.  Thus, explicitly disallow traversing up the file-system
3287          * or using absolute paths.
3288          */
3289 
3290         if (*name == '/' || strstr(name, "../") || strstr(name, "/..")) {
3291                 mandoc_vmsg(MANDOCERR_SO_PATH, r->parse, ln, ppos,
3292                     ".so %s", name);
3293                 buf->sz = mandoc_asprintf(&cp,
3294                     ".sp\nSee the file %s.\n.sp", name) + 1;
3295                 free(buf->buf);
3296                 buf->buf = cp;
3297                 *offs = 0;
3298                 return ROFF_REPARSE;
3299         }
3300 
3301         *offs = pos;
3302         return ROFF_SO;
3303 }
3304 
3305 /* --- user defined strings and macros ------------------------------------ */
3306 
3307 static enum rofferr
3308 roff_userdef(ROFF_ARGS)
3309 {
3310         const char       *arg[16], *ap;
3311         char             *cp, *n1, *n2;
3312         int               expand_count, i, ib, ie;
3313         size_t            asz, rsz;
3314 
3315         /*
3316          * Collect pointers to macro argument strings
3317          * and NUL-terminate them.
3318          */
3319 
3320         r->argc = 0;
3321         cp = buf->buf + pos;
3322         for (i = 0; i < 16; i++) {
3323                 if (*cp == '\0')
3324                         arg[i] = "";
3325                 else {
3326                         arg[i] = mandoc_getarg(r->parse, &cp, ln, &pos);
3327                         r->argc = i + 1;
3328                 }
3329         }
3330 
3331         /*
3332          * Expand macro arguments.
3333          */
3334 
3335         buf->sz = strlen(r->current_string) + 1;
3336         n1 = n2 = cp = mandoc_malloc(buf->sz);
3337         memcpy(n1, r->current_string, buf->sz);
3338         expand_count = 0;
3339         while (*cp != '\0') {
3340 
3341                 /* Scan ahead for the next argument invocation. */
3342 
3343                 if (*cp++ != '\\')
3344                         continue;
3345                 if (*cp++ != '$')
3346                         continue;
3347                 if (*cp == '*') {  /* \\$* inserts all arguments */
3348                         ib = 0;
3349                         ie = r->argc - 1;
3350                 } else {  /* \\$1 .. \\$9 insert one argument */
3351                         ib = ie = *cp - '1';
3352                         if (ib < 0 || ib > 8)
3353                                 continue;
3354                 }
3355                 cp -= 2;
3356 
3357                 /*
3358                  * Prevent infinite recursion.
3359                  */
3360 
3361                 if (cp >= n2)
3362                         expand_count = 1;
3363                 else if (++expand_count > EXPAND_LIMIT) {
3364                         mandoc_msg(MANDOCERR_ROFFLOOP, r->parse,
3365                             ln, (int)(cp - n1), NULL);
3366                         free(buf->buf);
3367                         buf->buf = n1;
3368                         *offs = 0;
3369                         return ROFF_IGN;
3370                 }
3371 
3372                 /*
3373                  * Determine the size of the expanded argument,
3374                  * taking escaping of quotes into account.
3375                  */
3376 
3377                 asz = ie > ib ? ie - ib : 0;  /* for blanks */
3378                 for (i = ib; i <= ie; i++) {
3379                         for (ap = arg[i]; *ap != '\0'; ap++) {
3380                                 asz++;
3381                                 if (*ap == '"')
3382                                         asz += 3;
3383                         }
3384                 }
3385                 if (asz != 3) {
3386 
3387                         /*
3388                          * Determine the size of the rest of the
3389                          * unexpanded macro, including the NUL.
3390                          */
3391 
3392                         rsz = buf->sz - (cp - n1) - 3;
3393 
3394                         /*
3395                          * When shrinking, move before
3396                          * releasing the storage.
3397                          */
3398 
3399                         if (asz < 3)
3400                                 memmove(cp + asz, cp + 3, rsz);
3401 
3402                         /*
3403                          * Resize the storage for the macro
3404                          * and readjust the parse pointer.
3405                          */
3406 
3407                         buf->sz += asz - 3;
3408                         n2 = mandoc_realloc(n1, buf->sz);
3409                         cp = n2 + (cp - n1);
3410                         n1 = n2;
3411 
3412                         /*
3413                          * When growing, make room
3414                          * for the expanded argument.
3415                          */
3416 
3417                         if (asz > 3)
3418                                 memmove(cp + asz, cp + 3, rsz);
3419                 }
3420 
3421                 /* Copy the expanded argument, escaping quotes. */
3422 
3423                 n2 = cp;
3424                 for (i = ib; i <= ie; i++) {
3425                         for (ap = arg[i]; *ap != '\0'; ap++) {
3426                                 if (*ap == '"') {
3427                                         memcpy(n2, "\\(dq", 4);
3428                                         n2 += 4;
3429                                 } else
3430                                         *n2++ = *ap;
3431                         }
3432                         if (i < ie)
3433                                 *n2++ = ' ';
3434                 }
3435         }
3436 
3437         /*
3438          * Replace the macro invocation
3439          * by the expanded macro.
3440          */
3441 
3442         free(buf->buf);
3443         buf->buf = n1;
3444         *offs = 0;
3445 
3446         return buf->sz > 1 && buf->buf[buf->sz - 2] == '\n' ?
3447            ROFF_REPARSE : ROFF_APPEND;
3448 }
3449 
3450 /*
3451  * Calling a high-level macro that was renamed with .rn.
3452  * r->current_string has already been set up by roff_parse().
3453  */
3454 static enum rofferr
3455 roff_renamed(ROFF_ARGS)
3456 {
3457         char    *nbuf;
3458 
3459         buf->sz = mandoc_asprintf(&nbuf, ".%s%s%s", r->current_string,
3460             buf->buf[pos] == '\0' ? "" : " ", buf->buf + pos) + 1;
3461         free(buf->buf);
3462         buf->buf = nbuf;
3463         *offs = 0;
3464         return ROFF_CONT;
3465 }
3466 
3467 static size_t
3468 roff_getname(struct roff *r, char **cpp, int ln, int pos)
3469 {
3470         char     *name, *cp;
3471         size_t    namesz;
3472 
3473         name = *cpp;
3474         if ('\0' == *name)
3475                 return 0;
3476 
3477         /* Read until end of name and terminate it with NUL. */
3478         for (cp = name; 1; cp++) {
3479                 if ('\0' == *cp || ' ' == *cp) {
3480                         namesz = cp - name;
3481                         break;
3482                 }
3483                 if ('\\' != *cp)
3484                         continue;
3485                 namesz = cp - name;
3486                 if ('{' == cp[1] || '}' == cp[1])
3487                         break;
3488                 cp++;
3489                 if ('\\' == *cp)
3490                         continue;
3491                 mandoc_vmsg(MANDOCERR_NAMESC, r->parse, ln, pos,
3492                     "%.*s", (int)(cp - name + 1), name);
3493                 mandoc_escape((const char **)&cp, NULL, NULL);
3494                 break;
3495         }
3496 
3497         /* Read past spaces. */
3498         while (' ' == *cp)
3499                 cp++;
3500 
3501         *cpp = cp;
3502         return namesz;
3503 }
3504 
3505 /*
3506  * Store *string into the user-defined string called *name.
3507  * To clear an existing entry, call with (*r, *name, NULL, 0).
3508  * append == 0: replace mode
3509  * append == 1: single-line append mode
3510  * append == 2: multiline append mode, append '\n' after each call
3511  */
3512 static void
3513 roff_setstr(struct roff *r, const char *name, const char *string,
3514         int append)
3515 {
3516         size_t   namesz;
3517 
3518         namesz = strlen(name);
3519         roff_setstrn(&r->strtab, name, namesz, string,
3520             string ? strlen(string) : 0, append);
3521         roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
3522 }
3523 
3524 static void
3525 roff_setstrn(struct roffkv **r, const char *name, size_t namesz,
3526                 const char *string, size_t stringsz, int append)
3527 {
3528         struct roffkv   *n;
3529         char            *c;
3530         int              i;
3531         size_t           oldch, newch;
3532 
3533         /* Search for an existing string with the same name. */
3534         n = *r;
3535 
3536         while (n && (namesz != n->key.sz ||
3537                         strncmp(n->key.p, name, namesz)))
3538                 n = n->next;
3539 
3540         if (NULL == n) {
3541                 /* Create a new string table entry. */
3542                 n = mandoc_malloc(sizeof(struct roffkv));
3543                 n->key.p = mandoc_strndup(name, namesz);
3544                 n->key.sz = namesz;
3545                 n->val.p = NULL;
3546                 n->val.sz = 0;
3547                 n->next = *r;
3548                 *r = n;
3549         } else if (0 == append) {
3550                 free(n->val.p);
3551                 n->val.p = NULL;
3552                 n->val.sz = 0;
3553         }
3554 
3555         if (NULL == string)
3556                 return;
3557 
3558         /*
3559          * One additional byte for the '\n' in multiline mode,
3560          * and one for the terminating '\0'.
3561          */
3562         newch = stringsz + (1 < append ? 2u : 1u);
3563 
3564         if (NULL == n->val.p) {
3565                 n->val.p = mandoc_malloc(newch);
3566                 *n->val.p = '\0';
3567                 oldch = 0;
3568         } else {
3569                 oldch = n->val.sz;
3570                 n->val.p = mandoc_realloc(n->val.p, oldch + newch);
3571         }
3572 
3573         /* Skip existing content in the destination buffer. */
3574         c = n->val.p + (int)oldch;
3575 
3576         /* Append new content to the destination buffer. */
3577         i = 0;
3578         while (i < (int)stringsz) {
3579                 /*
3580                  * Rudimentary roff copy mode:
3581                  * Handle escaped backslashes.
3582                  */
3583                 if ('\\' == string[i] && '\\' == string[i + 1])
3584                         i++;
3585                 *c++ = string[i++];
3586         }
3587 
3588         /* Append terminating bytes. */
3589         if (1 < append)
3590                 *c++ = '\n';
3591 
3592         *c = '\0';
3593         n->val.sz = (int)(c - n->val.p);
3594 }
3595 
3596 static const char *
3597 roff_getstrn(struct roff *r, const char *name, size_t len,
3598     int *deftype)
3599 {
3600         const struct roffkv     *n;
3601         int                      found, i;
3602         enum roff_tok            tok;
3603 
3604         found = 0;
3605         for (n = r->strtab; n != NULL; n = n->next) {
3606                 if (strncmp(name, n->key.p, len) != 0 ||
3607                     n->key.p[len] != '\0' || n->val.p == NULL)
3608                         continue;
3609                 if (*deftype & ROFFDEF_USER) {
3610                         *deftype = ROFFDEF_USER;
3611                         return n->val.p;
3612                 } else {
3613                         found = 1;
3614                         break;
3615                 }
3616         }
3617         for (n = r->rentab; n != NULL; n = n->next) {
3618                 if (strncmp(name, n->key.p, len) != 0 ||
3619                     n->key.p[len] != '\0' || n->val.p == NULL)
3620                         continue;
3621                 if (*deftype & ROFFDEF_REN) {
3622                         *deftype = ROFFDEF_REN;
3623                         return n->val.p;
3624                 } else {
3625                         found = 1;
3626                         break;
3627                 }
3628         }
3629         for (i = 0; i < PREDEFS_MAX; i++) {
3630                 if (strncmp(name, predefs[i].name, len) != 0 ||
3631                     predefs[i].name[len] != '\0')
3632                         continue;
3633                 if (*deftype & ROFFDEF_PRE) {
3634                         *deftype = ROFFDEF_PRE;
3635                         return predefs[i].str;
3636                 } else {
3637                         found = 1;
3638                         break;
3639                 }
3640         }
3641         if (r->man->macroset != MACROSET_MAN) {
3642                 for (tok = MDOC_Dd; tok < MDOC_MAX; tok++) {
3643                         if (strncmp(name, roff_name[tok], len) != 0 ||
3644                             roff_name[tok][len] != '\0')
3645                                 continue;
3646                         if (*deftype & ROFFDEF_STD) {
3647                                 *deftype = ROFFDEF_STD;
3648                                 return NULL;
3649                         } else {
3650                                 found = 1;
3651                                 break;
3652                         }
3653                 }
3654         }
3655         if (r->man->macroset != MACROSET_MDOC) {
3656                 for (tok = MAN_TH; tok < MAN_MAX; tok++) {
3657                         if (strncmp(name, roff_name[tok], len) != 0 ||
3658                             roff_name[tok][len] != '\0')
3659                                 continue;
3660                         if (*deftype & ROFFDEF_STD) {
3661                                 *deftype = ROFFDEF_STD;
3662                                 return NULL;
3663                         } else {
3664                                 found = 1;
3665                                 break;
3666                         }
3667                 }
3668         }
3669 
3670         if (found == 0 && *deftype != ROFFDEF_ANY) {
3671                 if (*deftype & ROFFDEF_REN) {
3672                         /*
3673                          * This might still be a request,
3674                          * so do not treat it as undefined yet.
3675                          */
3676                         *deftype = ROFFDEF_UNDEF;
3677                         return NULL;
3678                 }
3679 
3680                 /* Using an undefined string defines it to be empty. */
3681 
3682                 roff_setstrn(&r->strtab, name, len, "", 0, 0);
3683                 roff_setstrn(&r->rentab, name, len, NULL, 0, 0);
3684         }
3685 
3686         *deftype = 0;
3687         return NULL;
3688 }
3689 
3690 static void
3691 roff_freestr(struct roffkv *r)
3692 {
3693         struct roffkv    *n, *nn;
3694 
3695         for (n = r; n; n = nn) {
3696                 free(n->key.p);
3697                 free(n->val.p);
3698                 nn = n->next;
3699                 free(n);
3700         }
3701 }
3702 
3703 /* --- accessors and utility functions ------------------------------------ */
3704 
3705 /*
3706  * Duplicate an input string, making the appropriate character
3707  * conversations (as stipulated by `tr') along the way.
3708  * Returns a heap-allocated string with all the replacements made.
3709  */
3710 char *
3711 roff_strdup(const struct roff *r, const char *p)
3712 {
3713         const struct roffkv *cp;
3714         char            *res;
3715         const char      *pp;
3716         size_t           ssz, sz;
3717         enum mandoc_esc  esc;
3718 
3719         if (NULL == r->xmbtab && NULL == r->xtab)
3720                 return mandoc_strdup(p);
3721         else if ('\0' == *p)
3722                 return mandoc_strdup("");
3723 
3724         /*
3725          * Step through each character looking for term matches
3726          * (remember that a `tr' can be invoked with an escape, which is
3727          * a glyph but the escape is multi-character).
3728          * We only do this if the character hash has been initialised
3729          * and the string is >0 length.
3730          */
3731 
3732         res = NULL;
3733         ssz = 0;
3734 
3735         while ('\0' != *p) {
3736                 assert((unsigned int)*p < 128);
3737                 if ('\\' != *p && r->xtab && r->xtab[(unsigned int)*p].p) {
3738                         sz = r->xtab[(int)*p].sz;
3739                         res = mandoc_realloc(res, ssz + sz + 1);
3740                         memcpy(res + ssz, r->xtab[(int)*p].p, sz);
3741                         ssz += sz;
3742                         p++;
3743                         continue;
3744                 } else if ('\\' != *p) {
3745                         res = mandoc_realloc(res, ssz + 2);
3746                         res[ssz++] = *p++;
3747                         continue;
3748                 }
3749 
3750                 /* Search for term matches. */
3751                 for (cp = r->xmbtab; cp; cp = cp->next)
3752                         if (0 == strncmp(p, cp->key.p, cp->key.sz))
3753                                 break;
3754 
3755                 if (NULL != cp) {
3756                         /*
3757                          * A match has been found.
3758                          * Append the match to the array and move
3759                          * forward by its keysize.
3760                          */
3761                         res = mandoc_realloc(res,
3762                             ssz + cp->val.sz + 1);
3763                         memcpy(res + ssz, cp->val.p, cp->val.sz);
3764                         ssz += cp->val.sz;
3765                         p += (int)cp->key.sz;
3766                         continue;
3767                 }
3768 
3769                 /*
3770                  * Handle escapes carefully: we need to copy
3771                  * over just the escape itself, or else we might
3772                  * do replacements within the escape itself.
3773                  * Make sure to pass along the bogus string.
3774                  */
3775                 pp = p++;
3776                 esc = mandoc_escape(&p, NULL, NULL);
3777                 if (ESCAPE_ERROR == esc) {
3778                         sz = strlen(pp);
3779                         res = mandoc_realloc(res, ssz + sz + 1);
3780                         memcpy(res + ssz, pp, sz);
3781                         break;
3782                 }
3783                 /*
3784                  * We bail out on bad escapes.
3785                  * No need to warn: we already did so when
3786                  * roff_res() was called.
3787                  */
3788                 sz = (int)(p - pp);
3789                 res = mandoc_realloc(res, ssz + sz + 1);
3790                 memcpy(res + ssz, pp, sz);
3791                 ssz += sz;
3792         }
3793 
3794         res[(int)ssz] = '\0';
3795         return res;
3796 }
3797 
3798 int
3799 roff_getformat(const struct roff *r)
3800 {
3801 
3802         return r->format;
3803 }
3804 
3805 /*
3806  * Find out whether a line is a macro line or not.
3807  * If it is, adjust the current position and return one; if it isn't,
3808  * return zero and don't change the current position.
3809  * If the control character has been set with `.cc', then let that grain
3810  * precedence.
3811  * This is slighly contrary to groff, where using the non-breaking
3812  * control character when `cc' has been invoked will cause the
3813  * non-breaking macro contents to be printed verbatim.
3814  */
3815 int
3816 roff_getcontrol(const struct roff *r, const char *cp, int *ppos)
3817 {
3818         int             pos;
3819 
3820         pos = *ppos;
3821 
3822         if (r->control != '\0' && cp[pos] == r->control)
3823                 pos++;
3824         else if (r->control != '\0')
3825                 return 0;
3826         else if ('\\' == cp[pos] && '.' == cp[pos + 1])
3827                 pos += 2;
3828         else if ('.' == cp[pos] || '\'' == cp[pos])
3829                 pos++;
3830         else
3831                 return 0;
3832 
3833         while (' ' == cp[pos] || '\t' == cp[pos])
3834                 pos++;
3835 
3836         *ppos = pos;
3837         return 1;
3838 }