]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/mdocml/roff.c
Upgrade our copy of clang and llvm to 3.5.1 release. This is a bugfix
[FreeBSD/FreeBSD.git] / contrib / mdocml / roff.c
1 /*      $Id: roff.c,v 1.239 2014/11/19 01:20:25 schwarze Exp $ */
2 /*
3  * Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
4  * Copyright (c) 2010-2014 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 <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "mandoc.h"
29 #include "mandoc_aux.h"
30 #include "libmandoc.h"
31 #include "libroff.h"
32
33 /* Maximum number of nested if-else conditionals. */
34 #define RSTACK_MAX      128
35
36 /* Maximum number of string expansions per line, to break infinite loops. */
37 #define EXPAND_LIMIT    1000
38
39 enum    rofft {
40         ROFF_ad,
41         ROFF_am,
42         ROFF_ami,
43         ROFF_am1,
44         ROFF_as,
45         ROFF_cc,
46         ROFF_ce,
47         ROFF_de,
48         ROFF_dei,
49         ROFF_de1,
50         ROFF_ds,
51         ROFF_el,
52         ROFF_fam,
53         ROFF_hw,
54         ROFF_hy,
55         ROFF_ie,
56         ROFF_if,
57         ROFF_ig,
58         ROFF_it,
59         ROFF_ne,
60         ROFF_nh,
61         ROFF_nr,
62         ROFF_ns,
63         ROFF_pl,
64         ROFF_ps,
65         ROFF_rm,
66         ROFF_rr,
67         ROFF_so,
68         ROFF_ta,
69         ROFF_tr,
70         ROFF_Dd,
71         ROFF_TH,
72         ROFF_TS,
73         ROFF_TE,
74         ROFF_T_,
75         ROFF_EQ,
76         ROFF_EN,
77         ROFF_cblock,
78         ROFF_USERDEF,
79         ROFF_MAX
80 };
81
82 /*
83  * An incredibly-simple string buffer.
84  */
85 struct  roffstr {
86         char            *p; /* nil-terminated buffer */
87         size_t           sz; /* saved strlen(p) */
88 };
89
90 /*
91  * A key-value roffstr pair as part of a singly-linked list.
92  */
93 struct  roffkv {
94         struct roffstr   key;
95         struct roffstr   val;
96         struct roffkv   *next; /* next in list */
97 };
98
99 /*
100  * A single number register as part of a singly-linked list.
101  */
102 struct  roffreg {
103         struct roffstr   key;
104         int              val;
105         struct roffreg  *next;
106 };
107
108 struct  roff {
109         struct mparse   *parse; /* parse point */
110         const struct mchars *mchars; /* character table */
111         struct roffnode *last; /* leaf of stack */
112         int             *rstack; /* stack of inverted `ie' values */
113         struct roffreg  *regtab; /* number registers */
114         struct roffkv   *strtab; /* user-defined strings & macros */
115         struct roffkv   *xmbtab; /* multi-byte trans table (`tr') */
116         struct roffstr  *xtab; /* single-byte trans table (`tr') */
117         const char      *current_string; /* value of last called user macro */
118         struct tbl_node *first_tbl; /* first table parsed */
119         struct tbl_node *last_tbl; /* last table parsed */
120         struct tbl_node *tbl; /* current table being parsed */
121         struct eqn_node *last_eqn; /* last equation parsed */
122         struct eqn_node *first_eqn; /* first equation parsed */
123         struct eqn_node *eqn; /* current equation being parsed */
124         int              eqn_inline; /* current equation is inline */
125         int              options; /* parse options */
126         int              rstacksz; /* current size limit of rstack */
127         int              rstackpos; /* position in rstack */
128         int              format; /* current file in mdoc or man format */
129         char             control; /* control character */
130 };
131
132 struct  roffnode {
133         enum rofft       tok; /* type of node */
134         struct roffnode *parent; /* up one in stack */
135         int              line; /* parse line */
136         int              col; /* parse col */
137         char            *name; /* node name, e.g. macro name */
138         char            *end; /* end-rules: custom token */
139         int              endspan; /* end-rules: next-line or infty */
140         int              rule; /* current evaluation rule */
141 };
142
143 #define ROFF_ARGS        struct roff *r, /* parse ctx */ \
144                          enum rofft tok, /* tok of macro */ \
145                          struct buf *buf, /* input buffer */ \
146                          int ln, /* parse line */ \
147                          int ppos, /* original pos in buffer */ \
148                          int pos, /* current pos in buffer */ \
149                          int *offs /* reset offset of buffer data */
150
151 typedef enum rofferr (*roffproc)(ROFF_ARGS);
152
153 struct  roffmac {
154         const char      *name; /* macro name */
155         roffproc         proc; /* process new macro */
156         roffproc         text; /* process as child text of macro */
157         roffproc         sub; /* process as child of macro */
158         int              flags;
159 #define ROFFMAC_STRUCT  (1 << 0) /* always interpret */
160         struct roffmac  *next;
161 };
162
163 struct  predef {
164         const char      *name; /* predefined input name */
165         const char      *str; /* replacement symbol */
166 };
167
168 #define PREDEF(__name, __str) \
169         { (__name), (__str) },
170
171 static  enum rofft       roffhash_find(const char *, size_t);
172 static  void             roffhash_init(void);
173 static  void             roffnode_cleanscope(struct roff *);
174 static  void             roffnode_pop(struct roff *);
175 static  void             roffnode_push(struct roff *, enum rofft,
176                                 const char *, int, int);
177 static  enum rofferr     roff_block(ROFF_ARGS);
178 static  enum rofferr     roff_block_text(ROFF_ARGS);
179 static  enum rofferr     roff_block_sub(ROFF_ARGS);
180 static  enum rofferr     roff_cblock(ROFF_ARGS);
181 static  enum rofferr     roff_cc(ROFF_ARGS);
182 static  void             roff_ccond(struct roff *, int, int);
183 static  enum rofferr     roff_cond(ROFF_ARGS);
184 static  enum rofferr     roff_cond_text(ROFF_ARGS);
185 static  enum rofferr     roff_cond_sub(ROFF_ARGS);
186 static  enum rofferr     roff_ds(ROFF_ARGS);
187 static  enum rofferr     roff_eqndelim(struct roff *, struct buf *, int);
188 static  int              roff_evalcond(struct roff *r, int,
189                                 const char *, int *);
190 static  int              roff_evalnum(struct roff *, int,
191                                 const char *, int *, int *, int);
192 static  int              roff_evalpar(struct roff *, int,
193                                 const char *, int *, int *);
194 static  int              roff_evalstrcond(const char *, int *);
195 static  void             roff_free1(struct roff *);
196 static  void             roff_freereg(struct roffreg *);
197 static  void             roff_freestr(struct roffkv *);
198 static  size_t           roff_getname(struct roff *, char **, int, int);
199 static  int              roff_getnum(const char *, int *, int *);
200 static  int              roff_getop(const char *, int *, char *);
201 static  int              roff_getregn(const struct roff *,
202                                 const char *, size_t);
203 static  int              roff_getregro(const char *name);
204 static  const char      *roff_getstrn(const struct roff *,
205                                 const char *, size_t);
206 static  enum rofferr     roff_it(ROFF_ARGS);
207 static  enum rofferr     roff_line_ignore(ROFF_ARGS);
208 static  enum rofferr     roff_nr(ROFF_ARGS);
209 static  enum rofft       roff_parse(struct roff *, char *, int *,
210                                 int, int);
211 static  enum rofferr     roff_parsetext(struct buf *, int, int *);
212 static  enum rofferr     roff_res(struct roff *, struct buf *, int, int);
213 static  enum rofferr     roff_rm(ROFF_ARGS);
214 static  enum rofferr     roff_rr(ROFF_ARGS);
215 static  void             roff_setstr(struct roff *,
216                                 const char *, const char *, int);
217 static  void             roff_setstrn(struct roffkv **, const char *,
218                                 size_t, const char *, size_t, int);
219 static  enum rofferr     roff_so(ROFF_ARGS);
220 static  enum rofferr     roff_tr(ROFF_ARGS);
221 static  enum rofferr     roff_Dd(ROFF_ARGS);
222 static  enum rofferr     roff_TH(ROFF_ARGS);
223 static  enum rofferr     roff_TE(ROFF_ARGS);
224 static  enum rofferr     roff_TS(ROFF_ARGS);
225 static  enum rofferr     roff_EQ(ROFF_ARGS);
226 static  enum rofferr     roff_EN(ROFF_ARGS);
227 static  enum rofferr     roff_T_(ROFF_ARGS);
228 static  enum rofferr     roff_userdef(ROFF_ARGS);
229
230 /* See roffhash_find() */
231
232 #define ASCII_HI         126
233 #define ASCII_LO         33
234 #define HASHWIDTH       (ASCII_HI - ASCII_LO + 1)
235
236 static  struct roffmac  *hash[HASHWIDTH];
237
238 static  struct roffmac   roffs[ROFF_MAX] = {
239         { "ad", roff_line_ignore, NULL, NULL, 0, NULL },
240         { "am", roff_block, roff_block_text, roff_block_sub, 0, NULL },
241         { "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL },
242         { "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
243         { "as", roff_ds, NULL, NULL, 0, NULL },
244         { "cc", roff_cc, NULL, NULL, 0, NULL },
245         { "ce", roff_line_ignore, NULL, NULL, 0, NULL },
246         { "de", roff_block, roff_block_text, roff_block_sub, 0, NULL },
247         { "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL },
248         { "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
249         { "ds", roff_ds, NULL, NULL, 0, NULL },
250         { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
251         { "fam", roff_line_ignore, NULL, NULL, 0, NULL },
252         { "hw", roff_line_ignore, NULL, NULL, 0, NULL },
253         { "hy", roff_line_ignore, NULL, NULL, 0, NULL },
254         { "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
255         { "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
256         { "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL },
257         { "it", roff_it, NULL, NULL, 0, NULL },
258         { "ne", roff_line_ignore, NULL, NULL, 0, NULL },
259         { "nh", roff_line_ignore, NULL, NULL, 0, NULL },
260         { "nr", roff_nr, NULL, NULL, 0, NULL },
261         { "ns", roff_line_ignore, NULL, NULL, 0, NULL },
262         { "pl", roff_line_ignore, NULL, NULL, 0, NULL },
263         { "ps", roff_line_ignore, NULL, NULL, 0, NULL },
264         { "rm", roff_rm, NULL, NULL, 0, NULL },
265         { "rr", roff_rr, NULL, NULL, 0, NULL },
266         { "so", roff_so, NULL, NULL, 0, NULL },
267         { "ta", roff_line_ignore, NULL, NULL, 0, NULL },
268         { "tr", roff_tr, NULL, NULL, 0, NULL },
269         { "Dd", roff_Dd, NULL, NULL, 0, NULL },
270         { "TH", roff_TH, NULL, NULL, 0, NULL },
271         { "TS", roff_TS, NULL, NULL, 0, NULL },
272         { "TE", roff_TE, NULL, NULL, 0, NULL },
273         { "T&", roff_T_, NULL, NULL, 0, NULL },
274         { "EQ", roff_EQ, NULL, NULL, 0, NULL },
275         { "EN", roff_EN, NULL, NULL, 0, NULL },
276         { ".", roff_cblock, NULL, NULL, 0, NULL },
277         { NULL, roff_userdef, NULL, NULL, 0, NULL },
278 };
279
280 /* not currently implemented: Ds em Eq LP Me PP pp Or Rd Sf SH */
281 const   char *const __mdoc_reserved[] = {
282         "Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At",
283         "Bc", "Bd", "Bf", "Bk", "Bl", "Bo", "Bq",
284         "Brc", "Bro", "Brq", "Bsx", "Bt", "Bx",
285         "Cd", "Cm", "Db", "Dc", "Dd", "Dl", "Do", "Dq",
286         "Dt", "Dv", "Dx", "D1",
287         "Ec", "Ed", "Ef", "Ek", "El", "Em",
288         "En", "Eo", "Er", "Es", "Ev", "Ex",
289         "Fa", "Fc", "Fd", "Fl", "Fn", "Fo", "Fr", "Ft", "Fx",
290         "Hf", "Ic", "In", "It", "Lb", "Li", "Lk", "Lp",
291         "Ms", "Mt", "Nd", "Nm", "No", "Ns", "Nx",
292         "Oc", "Oo", "Op", "Os", "Ot", "Ox",
293         "Pa", "Pc", "Pf", "Po", "Pp", "Pq",
294         "Qc", "Ql", "Qo", "Qq", "Re", "Rs", "Rv",
295         "Sc", "Sh", "Sm", "So", "Sq",
296         "Ss", "St", "Sx", "Sy",
297         "Ta", "Tn", "Ud", "Ux", "Va", "Vt", "Xc", "Xo", "Xr",
298         "%A", "%B", "%C", "%D", "%I", "%J", "%N", "%O",
299         "%P", "%Q", "%R", "%T", "%U", "%V",
300         NULL
301 };
302
303 /* not currently implemented: BT DE DS ME MT PT SY TQ YS */
304 const   char *const __man_reserved[] = {
305         "AT", "B", "BI", "BR", "DT",
306         "EE", "EN", "EQ", "EX", "HP", "I", "IB", "IP", "IR",
307         "LP", "OP", "P", "PD", "PP",
308         "R", "RB", "RE", "RI", "RS", "SB", "SH", "SM", "SS",
309         "TE", "TH", "TP", "TS", "T&", "UC", "UE", "UR",
310         NULL
311 };
312
313 /* Array of injected predefined strings. */
314 #define PREDEFS_MAX      38
315 static  const struct predef predefs[PREDEFS_MAX] = {
316 #include "predefs.in"
317 };
318
319 /* See roffhash_find() */
320 #define ROFF_HASH(p)    (p[0] - ASCII_LO)
321
322 static  int      roffit_lines;  /* number of lines to delay */
323 static  char    *roffit_macro;  /* nil-terminated macro line */
324
325
326 static void
327 roffhash_init(void)
328 {
329         struct roffmac   *n;
330         int               buc, i;
331
332         for (i = 0; i < (int)ROFF_USERDEF; i++) {
333                 assert(roffs[i].name[0] >= ASCII_LO);
334                 assert(roffs[i].name[0] <= ASCII_HI);
335
336                 buc = ROFF_HASH(roffs[i].name);
337
338                 if (NULL != (n = hash[buc])) {
339                         for ( ; n->next; n = n->next)
340                                 /* Do nothing. */ ;
341                         n->next = &roffs[i];
342                 } else
343                         hash[buc] = &roffs[i];
344         }
345 }
346
347 /*
348  * Look up a roff token by its name.  Returns ROFF_MAX if no macro by
349  * the nil-terminated string name could be found.
350  */
351 static enum rofft
352 roffhash_find(const char *p, size_t s)
353 {
354         int              buc;
355         struct roffmac  *n;
356
357         /*
358          * libroff has an extremely simple hashtable, for the time
359          * being, which simply keys on the first character, which must
360          * be printable, then walks a chain.  It works well enough until
361          * optimised.
362          */
363
364         if (p[0] < ASCII_LO || p[0] > ASCII_HI)
365                 return(ROFF_MAX);
366
367         buc = ROFF_HASH(p);
368
369         if (NULL == (n = hash[buc]))
370                 return(ROFF_MAX);
371         for ( ; n; n = n->next)
372                 if (0 == strncmp(n->name, p, s) && '\0' == n->name[(int)s])
373                         return((enum rofft)(n - roffs));
374
375         return(ROFF_MAX);
376 }
377
378 /*
379  * Pop the current node off of the stack of roff instructions currently
380  * pending.
381  */
382 static void
383 roffnode_pop(struct roff *r)
384 {
385         struct roffnode *p;
386
387         assert(r->last);
388         p = r->last;
389
390         r->last = r->last->parent;
391         free(p->name);
392         free(p->end);
393         free(p);
394 }
395
396 /*
397  * Push a roff node onto the instruction stack.  This must later be
398  * removed with roffnode_pop().
399  */
400 static void
401 roffnode_push(struct roff *r, enum rofft tok, const char *name,
402                 int line, int col)
403 {
404         struct roffnode *p;
405
406         p = mandoc_calloc(1, sizeof(struct roffnode));
407         p->tok = tok;
408         if (name)
409                 p->name = mandoc_strdup(name);
410         p->parent = r->last;
411         p->line = line;
412         p->col = col;
413         p->rule = p->parent ? p->parent->rule : 0;
414
415         r->last = p;
416 }
417
418 static void
419 roff_free1(struct roff *r)
420 {
421         struct tbl_node *tbl;
422         struct eqn_node *e;
423         int              i;
424
425         while (NULL != (tbl = r->first_tbl)) {
426                 r->first_tbl = tbl->next;
427                 tbl_free(tbl);
428         }
429         r->first_tbl = r->last_tbl = r->tbl = NULL;
430
431         while (NULL != (e = r->first_eqn)) {
432                 r->first_eqn = e->next;
433                 eqn_free(e);
434         }
435         r->first_eqn = r->last_eqn = r->eqn = NULL;
436
437         while (r->last)
438                 roffnode_pop(r);
439
440         free (r->rstack);
441         r->rstack = NULL;
442         r->rstacksz = 0;
443         r->rstackpos = -1;
444
445         roff_freereg(r->regtab);
446         r->regtab = NULL;
447
448         roff_freestr(r->strtab);
449         roff_freestr(r->xmbtab);
450         r->strtab = r->xmbtab = NULL;
451
452         if (r->xtab)
453                 for (i = 0; i < 128; i++)
454                         free(r->xtab[i].p);
455         free(r->xtab);
456         r->xtab = NULL;
457 }
458
459 void
460 roff_reset(struct roff *r)
461 {
462
463         roff_free1(r);
464         r->format = r->options & (MPARSE_MDOC | MPARSE_MAN);
465         r->control = 0;
466 }
467
468 void
469 roff_free(struct roff *r)
470 {
471
472         roff_free1(r);
473         free(r);
474 }
475
476 struct roff *
477 roff_alloc(struct mparse *parse, const struct mchars *mchars, int options)
478 {
479         struct roff     *r;
480
481         r = mandoc_calloc(1, sizeof(struct roff));
482         r->parse = parse;
483         r->mchars = mchars;
484         r->options = options;
485         r->format = options & (MPARSE_MDOC | MPARSE_MAN);
486         r->rstackpos = -1;
487
488         roffhash_init();
489
490         return(r);
491 }
492
493 /*
494  * In the current line, expand escape sequences that tend to get
495  * used in numerical expressions and conditional requests.
496  * Also check the syntax of the remaining escape sequences.
497  */
498 static enum rofferr
499 roff_res(struct roff *r, struct buf *buf, int ln, int pos)
500 {
501         char             ubuf[24]; /* buffer to print the number */
502         const char      *start; /* start of the string to process */
503         char            *stesc; /* start of an escape sequence ('\\') */
504         const char      *stnam; /* start of the name, after "[(*" */
505         const char      *cp;    /* end of the name, e.g. before ']' */
506         const char      *res;   /* the string to be substituted */
507         char            *nbuf;  /* new buffer to copy buf->buf to */
508         size_t           maxl;  /* expected length of the escape name */
509         size_t           naml;  /* actual length of the escape name */
510         enum mandoc_esc  esc;   /* type of the escape sequence */
511         int              inaml; /* length returned from mandoc_escape() */
512         int              expand_count;  /* to avoid infinite loops */
513         int              npos;  /* position in numeric expression */
514         int              arg_complete; /* argument not interrupted by eol */
515         char             term;  /* character terminating the escape */
516
517         expand_count = 0;
518         start = buf->buf + pos;
519         stesc = strchr(start, '\0') - 1;
520         while (stesc-- > start) {
521
522                 /* Search backwards for the next backslash. */
523
524                 if (*stesc != '\\')
525                         continue;
526
527                 /* If it is escaped, skip it. */
528
529                 for (cp = stesc - 1; cp >= start; cp--)
530                         if (*cp != '\\')
531                                 break;
532
533                 if ((stesc - cp) % 2 == 0) {
534                         stesc = (char *)cp;
535                         continue;
536                 }
537
538                 /* Decide whether to expand or to check only. */
539
540                 term = '\0';
541                 cp = stesc + 1;
542                 switch (*cp) {
543                 case '*':
544                         res = NULL;
545                         break;
546                 case 'B':
547                         /* FALLTHROUGH */
548                 case 'w':
549                         term = cp[1];
550                         /* FALLTHROUGH */
551                 case 'n':
552                         res = ubuf;
553                         break;
554                 default:
555                         esc = mandoc_escape(&cp, &stnam, &inaml);
556                         if (esc == ESCAPE_ERROR ||
557                             (esc == ESCAPE_SPECIAL &&
558                              mchars_spec2cp(r->mchars, stnam, inaml) < 0))
559                                 mandoc_vmsg(MANDOCERR_ESC_BAD,
560                                     r->parse, ln, (int)(stesc - buf->buf),
561                                     "%.*s", (int)(cp - stesc), stesc);
562                         continue;
563                 }
564
565                 if (EXPAND_LIMIT < ++expand_count) {
566                         mandoc_msg(MANDOCERR_ROFFLOOP, r->parse,
567                             ln, (int)(stesc - buf->buf), NULL);
568                         return(ROFF_IGN);
569                 }
570
571                 /*
572                  * The third character decides the length
573                  * of the name of the string or register.
574                  * Save a pointer to the name.
575                  */
576
577                 if (term == '\0') {
578                         switch (*++cp) {
579                         case '\0':
580                                 maxl = 0;
581                                 break;
582                         case '(':
583                                 cp++;
584                                 maxl = 2;
585                                 break;
586                         case '[':
587                                 cp++;
588                                 term = ']';
589                                 maxl = 0;
590                                 break;
591                         default:
592                                 maxl = 1;
593                                 break;
594                         }
595                 } else {
596                         cp += 2;
597                         maxl = 0;
598                 }
599                 stnam = cp;
600
601                 /* Advance to the end of the name. */
602
603                 arg_complete = 1;
604                 for (naml = 0; maxl == 0 || naml < maxl; naml++, cp++) {
605                         if (*cp == '\0') {
606                                 mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
607                                     ln, (int)(stesc - buf->buf), stesc);
608                                 arg_complete = 0;
609                                 break;
610                         }
611                         if (maxl == 0 && *cp == term) {
612                                 cp++;
613                                 break;
614                         }
615                 }
616
617                 /*
618                  * Retrieve the replacement string; if it is
619                  * undefined, resume searching for escapes.
620                  */
621
622                 switch (stesc[1]) {
623                 case '*':
624                         if (arg_complete)
625                                 res = roff_getstrn(r, stnam, naml);
626                         break;
627                 case 'B':
628                         npos = 0;
629                         ubuf[0] = arg_complete &&
630                             roff_evalnum(r, ln, stnam, &npos, NULL, 0) &&
631                             stnam + npos + 1 == cp ? '1' : '0';
632                         ubuf[1] = '\0';
633                         break;
634                 case 'n':
635                         if (arg_complete)
636                                 (void)snprintf(ubuf, sizeof(ubuf), "%d",
637                                     roff_getregn(r, stnam, naml));
638                         else
639                                 ubuf[0] = '\0';
640                         break;
641                 case 'w':
642                         /* use even incomplete args */
643                         (void)snprintf(ubuf, sizeof(ubuf), "%d",
644                             24 * (int)naml);
645                         break;
646                 }
647
648                 if (res == NULL) {
649                         mandoc_vmsg(MANDOCERR_STR_UNDEF,
650                             r->parse, ln, (int)(stesc - buf->buf),
651                             "%.*s", (int)naml, stnam);
652                         res = "";
653                 }
654
655                 /* Replace the escape sequence by the string. */
656
657                 *stesc = '\0';
658                 buf->sz = mandoc_asprintf(&nbuf, "%s%s%s",
659                     buf->buf, res, cp) + 1;
660
661                 /* Prepare for the next replacement. */
662
663                 start = nbuf + pos;
664                 stesc = nbuf + (stesc - buf->buf) + strlen(res);
665                 free(buf->buf);
666                 buf->buf = nbuf;
667         }
668         return(ROFF_CONT);
669 }
670
671 /*
672  * Process text streams:
673  * Convert all breakable hyphens into ASCII_HYPH.
674  * Decrement and spring input line trap.
675  */
676 static enum rofferr
677 roff_parsetext(struct buf *buf, int pos, int *offs)
678 {
679         size_t           sz;
680         const char      *start;
681         char            *p;
682         int              isz;
683         enum mandoc_esc  esc;
684
685         start = p = buf->buf + pos;
686
687         while (*p != '\0') {
688                 sz = strcspn(p, "-\\");
689                 p += sz;
690
691                 if (*p == '\0')
692                         break;
693
694                 if (*p == '\\') {
695                         /* Skip over escapes. */
696                         p++;
697                         esc = mandoc_escape((const char **)&p, NULL, NULL);
698                         if (esc == ESCAPE_ERROR)
699                                 break;
700                         continue;
701                 } else if (p == start) {
702                         p++;
703                         continue;
704                 }
705
706                 if (isalpha((unsigned char)p[-1]) &&
707                     isalpha((unsigned char)p[1]))
708                         *p = ASCII_HYPH;
709                 p++;
710         }
711
712         /* Spring the input line trap. */
713         if (roffit_lines == 1) {
714                 isz = mandoc_asprintf(&p, "%s\n.%s", buf->buf, roffit_macro);
715                 free(buf->buf);
716                 buf->buf = p;
717                 buf->sz = isz + 1;
718                 *offs = 0;
719                 free(roffit_macro);
720                 roffit_lines = 0;
721                 return(ROFF_REPARSE);
722         } else if (roffit_lines > 1)
723                 --roffit_lines;
724         return(ROFF_CONT);
725 }
726
727 enum rofferr
728 roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs)
729 {
730         enum rofft       t;
731         enum rofferr     e;
732         int              pos;   /* parse point */
733         int              ppos;  /* original offset in buf->buf */
734         int              ctl;   /* macro line (boolean) */
735
736         ppos = pos = *offs;
737
738         /* Handle in-line equation delimiters. */
739
740         if (r->tbl == NULL &&
741             r->last_eqn != NULL && r->last_eqn->delim &&
742             (r->eqn == NULL || r->eqn_inline)) {
743                 e = roff_eqndelim(r, buf, pos);
744                 if (e == ROFF_REPARSE)
745                         return(e);
746                 assert(e == ROFF_CONT);
747         }
748
749         /* Expand some escape sequences. */
750
751         e = roff_res(r, buf, ln, pos);
752         if (e == ROFF_IGN)
753                 return(e);
754         assert(e == ROFF_CONT);
755
756         ctl = roff_getcontrol(r, buf->buf, &pos);
757
758         /*
759          * First, if a scope is open and we're not a macro, pass the
760          * text through the macro's filter.  If a scope isn't open and
761          * we're not a macro, just let it through.
762          * Finally, if there's an equation scope open, divert it into it
763          * no matter our state.
764          */
765
766         if (r->last && ! ctl) {
767                 t = r->last->tok;
768                 assert(roffs[t].text);
769                 e = (*roffs[t].text)(r, t, buf, ln, pos, pos, offs);
770                 assert(e == ROFF_IGN || e == ROFF_CONT);
771                 if (e != ROFF_CONT)
772                         return(e);
773         }
774         if (r->eqn)
775                 return(eqn_read(&r->eqn, ln, buf->buf, ppos, offs));
776         if ( ! ctl) {
777                 if (r->tbl)
778                         return(tbl_read(r->tbl, ln, buf->buf, pos));
779                 return(roff_parsetext(buf, pos, offs));
780         }
781
782         /* Skip empty request lines. */
783
784         if (buf->buf[pos] == '"') {
785                 mandoc_msg(MANDOCERR_COMMENT_BAD, r->parse,
786                     ln, pos, NULL);
787                 return(ROFF_IGN);
788         } else if (buf->buf[pos] == '\0')
789                 return(ROFF_IGN);
790
791         /*
792          * If a scope is open, go to the child handler for that macro,
793          * as it may want to preprocess before doing anything with it.
794          * Don't do so if an equation is open.
795          */
796
797         if (r->last) {
798                 t = r->last->tok;
799                 assert(roffs[t].sub);
800                 return((*roffs[t].sub)(r, t, buf, ln, ppos, pos, offs));
801         }
802
803         /*
804          * Lastly, as we've no scope open, try to look up and execute
805          * the new macro.  If no macro is found, simply return and let
806          * the compilers handle it.
807          */
808
809         if ((t = roff_parse(r, buf->buf, &pos, ln, ppos)) == ROFF_MAX)
810                 return(ROFF_CONT);
811
812         assert(roffs[t].proc);
813         return((*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs));
814 }
815
816 void
817 roff_endparse(struct roff *r)
818 {
819
820         if (r->last)
821                 mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
822                     r->last->line, r->last->col,
823                     roffs[r->last->tok].name);
824
825         if (r->eqn) {
826                 mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
827                     r->eqn->eqn.ln, r->eqn->eqn.pos, "EQ");
828                 eqn_end(&r->eqn);
829         }
830
831         if (r->tbl) {
832                 mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
833                     r->tbl->line, r->tbl->pos, "TS");
834                 tbl_end(&r->tbl);
835         }
836 }
837
838 /*
839  * Parse a roff node's type from the input buffer.  This must be in the
840  * form of ".foo xxx" in the usual way.
841  */
842 static enum rofft
843 roff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos)
844 {
845         char            *cp;
846         const char      *mac;
847         size_t           maclen;
848         enum rofft       t;
849
850         cp = buf + *pos;
851
852         if ('\0' == *cp || '"' == *cp || '\t' == *cp || ' ' == *cp)
853                 return(ROFF_MAX);
854
855         mac = cp;
856         maclen = roff_getname(r, &cp, ln, ppos);
857
858         t = (r->current_string = roff_getstrn(r, mac, maclen))
859             ? ROFF_USERDEF : roffhash_find(mac, maclen);
860
861         if (ROFF_MAX != t)
862                 *pos = cp - buf;
863
864         return(t);
865 }
866
867 static enum rofferr
868 roff_cblock(ROFF_ARGS)
869 {
870
871         /*
872          * A block-close `..' should only be invoked as a child of an
873          * ignore macro, otherwise raise a warning and just ignore it.
874          */
875
876         if (r->last == NULL) {
877                 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
878                     ln, ppos, "..");
879                 return(ROFF_IGN);
880         }
881
882         switch (r->last->tok) {
883         case ROFF_am:
884                 /* ROFF_am1 is remapped to ROFF_am in roff_block(). */
885                 /* FALLTHROUGH */
886         case ROFF_ami:
887                 /* FALLTHROUGH */
888         case ROFF_de:
889                 /* ROFF_de1 is remapped to ROFF_de in roff_block(). */
890                 /* FALLTHROUGH */
891         case ROFF_dei:
892                 /* FALLTHROUGH */
893         case ROFF_ig:
894                 break;
895         default:
896                 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
897                     ln, ppos, "..");
898                 return(ROFF_IGN);
899         }
900
901         if (buf->buf[pos] != '\0')
902                 mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos,
903                     ".. %s", buf->buf + pos);
904
905         roffnode_pop(r);
906         roffnode_cleanscope(r);
907         return(ROFF_IGN);
908
909 }
910
911 static void
912 roffnode_cleanscope(struct roff *r)
913 {
914
915         while (r->last) {
916                 if (--r->last->endspan != 0)
917                         break;
918                 roffnode_pop(r);
919         }
920 }
921
922 static void
923 roff_ccond(struct roff *r, int ln, int ppos)
924 {
925
926         if (NULL == r->last) {
927                 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
928                     ln, ppos, "\\}");
929                 return;
930         }
931
932         switch (r->last->tok) {
933         case ROFF_el:
934                 /* FALLTHROUGH */
935         case ROFF_ie:
936                 /* FALLTHROUGH */
937         case ROFF_if:
938                 break;
939         default:
940                 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
941                     ln, ppos, "\\}");
942                 return;
943         }
944
945         if (r->last->endspan > -1) {
946                 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
947                     ln, ppos, "\\}");
948                 return;
949         }
950
951         roffnode_pop(r);
952         roffnode_cleanscope(r);
953         return;
954 }
955
956 static enum rofferr
957 roff_block(ROFF_ARGS)
958 {
959         const char      *name;
960         char            *iname, *cp;
961         size_t           namesz;
962
963         /* Ignore groff compatibility mode for now. */
964
965         if (tok == ROFF_de1)
966                 tok = ROFF_de;
967         else if (tok == ROFF_am1)
968                 tok = ROFF_am;
969
970         /* Parse the macro name argument. */
971
972         cp = buf->buf + pos;
973         if (tok == ROFF_ig) {
974                 iname = NULL;
975                 namesz = 0;
976         } else {
977                 iname = cp;
978                 namesz = roff_getname(r, &cp, ln, ppos);
979                 iname[namesz] = '\0';
980         }
981
982         /* Resolve the macro name argument if it is indirect. */
983
984         if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
985                 if ((name = roff_getstrn(r, iname, namesz)) == NULL) {
986                         mandoc_vmsg(MANDOCERR_STR_UNDEF,
987                             r->parse, ln, (int)(iname - buf->buf),
988                             "%.*s", (int)namesz, iname);
989                         namesz = 0;
990                 } else
991                         namesz = strlen(name);
992         } else
993                 name = iname;
994
995         if (namesz == 0 && tok != ROFF_ig) {
996                 mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse,
997                     ln, ppos, roffs[tok].name);
998                 return(ROFF_IGN);
999         }
1000
1001         roffnode_push(r, tok, name, ln, ppos);
1002
1003         /*
1004          * At the beginning of a `de' macro, clear the existing string
1005          * with the same name, if there is one.  New content will be
1006          * appended from roff_block_text() in multiline mode.
1007          */
1008
1009         if (tok == ROFF_de || tok == ROFF_dei)
1010                 roff_setstrn(&r->strtab, name, namesz, "", 0, 0);
1011
1012         if (*cp == '\0')
1013                 return(ROFF_IGN);
1014
1015         /* Get the custom end marker. */
1016
1017         iname = cp;
1018         namesz = roff_getname(r, &cp, ln, ppos);
1019
1020         /* Resolve the end marker if it is indirect. */
1021
1022         if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
1023                 if ((name = roff_getstrn(r, iname, namesz)) == NULL) {
1024                         mandoc_vmsg(MANDOCERR_STR_UNDEF,
1025                             r->parse, ln, (int)(iname - buf->buf),
1026                             "%.*s", (int)namesz, iname);
1027                         namesz = 0;
1028                 } else
1029                         namesz = strlen(name);
1030         } else
1031                 name = iname;
1032
1033         if (namesz)
1034                 r->last->end = mandoc_strndup(name, namesz);
1035
1036         if (*cp != '\0')
1037                 mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse,
1038                     ln, pos, ".%s ... %s", roffs[tok].name, cp);
1039
1040         return(ROFF_IGN);
1041 }
1042
1043 static enum rofferr
1044 roff_block_sub(ROFF_ARGS)
1045 {
1046         enum rofft      t;
1047         int             i, j;
1048
1049         /*
1050          * First check whether a custom macro exists at this level.  If
1051          * it does, then check against it.  This is some of groff's
1052          * stranger behaviours.  If we encountered a custom end-scope
1053          * tag and that tag also happens to be a "real" macro, then we
1054          * need to try interpreting it again as a real macro.  If it's
1055          * not, then return ignore.  Else continue.
1056          */
1057
1058         if (r->last->end) {
1059                 for (i = pos, j = 0; r->last->end[j]; j++, i++)
1060                         if (buf->buf[i] != r->last->end[j])
1061                                 break;
1062
1063                 if (r->last->end[j] == '\0' &&
1064                     (buf->buf[i] == '\0' ||
1065                      buf->buf[i] == ' ' ||
1066                      buf->buf[i] == '\t')) {
1067                         roffnode_pop(r);
1068                         roffnode_cleanscope(r);
1069
1070                         while (buf->buf[i] == ' ' || buf->buf[i] == '\t')
1071                                 i++;
1072
1073                         pos = i;
1074                         if (roff_parse(r, buf->buf, &pos, ln, ppos) !=
1075                             ROFF_MAX)
1076                                 return(ROFF_RERUN);
1077                         return(ROFF_IGN);
1078                 }
1079         }
1080
1081         /*
1082          * If we have no custom end-query or lookup failed, then try
1083          * pulling it out of the hashtable.
1084          */
1085
1086         t = roff_parse(r, buf->buf, &pos, ln, ppos);
1087
1088         if (t != ROFF_cblock) {
1089                 if (tok != ROFF_ig)
1090                         roff_setstr(r, r->last->name, buf->buf + ppos, 2);
1091                 return(ROFF_IGN);
1092         }
1093
1094         assert(roffs[t].proc);
1095         return((*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs));
1096 }
1097
1098 static enum rofferr
1099 roff_block_text(ROFF_ARGS)
1100 {
1101
1102         if (tok != ROFF_ig)
1103                 roff_setstr(r, r->last->name, buf->buf + pos, 2);
1104
1105         return(ROFF_IGN);
1106 }
1107
1108 static enum rofferr
1109 roff_cond_sub(ROFF_ARGS)
1110 {
1111         enum rofft       t;
1112         char            *ep;
1113         int              rr;
1114
1115         rr = r->last->rule;
1116         roffnode_cleanscope(r);
1117         t = roff_parse(r, buf->buf, &pos, ln, ppos);
1118
1119         /*
1120          * Fully handle known macros when they are structurally
1121          * required or when the conditional evaluated to true.
1122          */
1123
1124         if ((t != ROFF_MAX) &&
1125             (rr || roffs[t].flags & ROFFMAC_STRUCT)) {
1126                 assert(roffs[t].proc);
1127                 return((*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs));
1128         }
1129
1130         /*
1131          * If `\}' occurs on a macro line without a preceding macro,
1132          * drop the line completely.
1133          */
1134
1135         ep = buf->buf + pos;
1136         if (ep[0] == '\\' && ep[1] == '}')
1137                 rr = 0;
1138
1139         /* Always check for the closing delimiter `\}'. */
1140
1141         while ((ep = strchr(ep, '\\')) != NULL) {
1142                 if (*(++ep) == '}') {
1143                         *ep = '&';
1144                         roff_ccond(r, ln, ep - buf->buf - 1);
1145                 }
1146                 ++ep;
1147         }
1148         return(rr ? ROFF_CONT : ROFF_IGN);
1149 }
1150
1151 static enum rofferr
1152 roff_cond_text(ROFF_ARGS)
1153 {
1154         char            *ep;
1155         int              rr;
1156
1157         rr = r->last->rule;
1158         roffnode_cleanscope(r);
1159
1160         ep = buf->buf + pos;
1161         while ((ep = strchr(ep, '\\')) != NULL) {
1162                 if (*(++ep) == '}') {
1163                         *ep = '&';
1164                         roff_ccond(r, ln, ep - buf->buf - 1);
1165                 }
1166                 ++ep;
1167         }
1168         return(rr ? ROFF_CONT : ROFF_IGN);
1169 }
1170
1171 /*
1172  * Parse a single signed integer number.  Stop at the first non-digit.
1173  * If there is at least one digit, return success and advance the
1174  * parse point, else return failure and let the parse point unchanged.
1175  * Ignore overflows, treat them just like the C language.
1176  */
1177 static int
1178 roff_getnum(const char *v, int *pos, int *res)
1179 {
1180         int      myres, n, p;
1181
1182         if (NULL == res)
1183                 res = &myres;
1184
1185         p = *pos;
1186         n = v[p] == '-';
1187         if (n)
1188                 p++;
1189
1190         for (*res = 0; isdigit((unsigned char)v[p]); p++)
1191                 *res = 10 * *res + v[p] - '0';
1192         if (p == *pos + n)
1193                 return 0;
1194
1195         if (n)
1196                 *res = -*res;
1197
1198         *pos = p;
1199         return 1;
1200 }
1201
1202 /*
1203  * Evaluate a string comparison condition.
1204  * The first character is the delimiter.
1205  * Succeed if the string up to its second occurrence
1206  * matches the string up to its third occurence.
1207  * Advance the cursor after the third occurrence
1208  * or lacking that, to the end of the line.
1209  */
1210 static int
1211 roff_evalstrcond(const char *v, int *pos)
1212 {
1213         const char      *s1, *s2, *s3;
1214         int              match;
1215
1216         match = 0;
1217         s1 = v + *pos;          /* initial delimiter */
1218         s2 = s1 + 1;            /* for scanning the first string */
1219         s3 = strchr(s2, *s1);   /* for scanning the second string */
1220
1221         if (NULL == s3)         /* found no middle delimiter */
1222                 goto out;
1223
1224         while ('\0' != *++s3) {
1225                 if (*s2 != *s3) {  /* mismatch */
1226                         s3 = strchr(s3, *s1);
1227                         break;
1228                 }
1229                 if (*s3 == *s1) {  /* found the final delimiter */
1230                         match = 1;
1231                         break;
1232                 }
1233                 s2++;
1234         }
1235
1236 out:
1237         if (NULL == s3)
1238                 s3 = strchr(s2, '\0');
1239         else
1240                 s3++;
1241         *pos = s3 - v;
1242         return(match);
1243 }
1244
1245 /*
1246  * Evaluate an optionally negated single character, numerical,
1247  * or string condition.
1248  */
1249 static int
1250 roff_evalcond(struct roff *r, int ln, const char *v, int *pos)
1251 {
1252         int      wanttrue, number;
1253
1254         if ('!' == v[*pos]) {
1255                 wanttrue = 0;
1256                 (*pos)++;
1257         } else
1258                 wanttrue = 1;
1259
1260         switch (v[*pos]) {
1261         case 'n':
1262                 /* FALLTHROUGH */
1263         case 'o':
1264                 (*pos)++;
1265                 return(wanttrue);
1266         case 'c':
1267                 /* FALLTHROUGH */
1268         case 'd':
1269                 /* FALLTHROUGH */
1270         case 'e':
1271                 /* FALLTHROUGH */
1272         case 'r':
1273                 /* FALLTHROUGH */
1274         case 't':
1275                 /* FALLTHROUGH */
1276         case 'v':
1277                 (*pos)++;
1278                 return(!wanttrue);
1279         default:
1280                 break;
1281         }
1282
1283         if (roff_evalnum(r, ln, v, pos, &number, 0))
1284                 return((number > 0) == wanttrue);
1285         else
1286                 return(roff_evalstrcond(v, pos) == wanttrue);
1287 }
1288
1289 static enum rofferr
1290 roff_line_ignore(ROFF_ARGS)
1291 {
1292
1293         return(ROFF_IGN);
1294 }
1295
1296 static enum rofferr
1297 roff_cond(ROFF_ARGS)
1298 {
1299
1300         roffnode_push(r, tok, NULL, ln, ppos);
1301
1302         /*
1303          * An `.el' has no conditional body: it will consume the value
1304          * of the current rstack entry set in prior `ie' calls or
1305          * defaults to DENY.
1306          *
1307          * If we're not an `el', however, then evaluate the conditional.
1308          */
1309
1310         r->last->rule = tok == ROFF_el ?
1311             (r->rstackpos < 0 ? 0 : r->rstack[r->rstackpos--]) :
1312             roff_evalcond(r, ln, buf->buf, &pos);
1313
1314         /*
1315          * An if-else will put the NEGATION of the current evaluated
1316          * conditional into the stack of rules.
1317          */
1318
1319         if (tok == ROFF_ie) {
1320                 if (r->rstackpos + 1 == r->rstacksz) {
1321                         r->rstacksz += 16;
1322                         r->rstack = mandoc_reallocarray(r->rstack,
1323                             r->rstacksz, sizeof(int));
1324                 }
1325                 r->rstack[++r->rstackpos] = !r->last->rule;
1326         }
1327
1328         /* If the parent has false as its rule, then so do we. */
1329
1330         if (r->last->parent && !r->last->parent->rule)
1331                 r->last->rule = 0;
1332
1333         /*
1334          * Determine scope.
1335          * If there is nothing on the line after the conditional,
1336          * not even whitespace, use next-line scope.
1337          */
1338
1339         if (buf->buf[pos] == '\0') {
1340                 r->last->endspan = 2;
1341                 goto out;
1342         }
1343
1344         while (buf->buf[pos] == ' ')
1345                 pos++;
1346
1347         /* An opening brace requests multiline scope. */
1348
1349         if (buf->buf[pos] == '\\' && buf->buf[pos + 1] == '{') {
1350                 r->last->endspan = -1;
1351                 pos += 2;
1352                 goto out;
1353         }
1354
1355         /*
1356          * Anything else following the conditional causes
1357          * single-line scope.  Warn if the scope contains
1358          * nothing but trailing whitespace.
1359          */
1360
1361         if (buf->buf[pos] == '\0')
1362                 mandoc_msg(MANDOCERR_COND_EMPTY, r->parse,
1363                     ln, ppos, roffs[tok].name);
1364
1365         r->last->endspan = 1;
1366
1367 out:
1368         *offs = pos;
1369         return(ROFF_RERUN);
1370 }
1371
1372 static enum rofferr
1373 roff_ds(ROFF_ARGS)
1374 {
1375         char            *string;
1376         const char      *name;
1377         size_t           namesz;
1378
1379         /*
1380          * The first word is the name of the string.
1381          * If it is empty or terminated by an escape sequence,
1382          * abort the `ds' request without defining anything.
1383          */
1384
1385         name = string = buf->buf + pos;
1386         if (*name == '\0')
1387                 return(ROFF_IGN);
1388
1389         namesz = roff_getname(r, &string, ln, pos);
1390         if (name[namesz] == '\\')
1391                 return(ROFF_IGN);
1392
1393         /* Read past the initial double-quote, if any. */
1394         if (*string == '"')
1395                 string++;
1396
1397         /* The rest is the value. */
1398         roff_setstrn(&r->strtab, name, namesz, string, strlen(string),
1399             ROFF_as == tok);
1400         return(ROFF_IGN);
1401 }
1402
1403 /*
1404  * Parse a single operator, one or two characters long.
1405  * If the operator is recognized, return success and advance the
1406  * parse point, else return failure and let the parse point unchanged.
1407  */
1408 static int
1409 roff_getop(const char *v, int *pos, char *res)
1410 {
1411
1412         *res = v[*pos];
1413
1414         switch (*res) {
1415         case '+':
1416                 /* FALLTHROUGH */
1417         case '-':
1418                 /* FALLTHROUGH */
1419         case '*':
1420                 /* FALLTHROUGH */
1421         case '/':
1422                 /* FALLTHROUGH */
1423         case '%':
1424                 /* FALLTHROUGH */
1425         case '&':
1426                 /* FALLTHROUGH */
1427         case ':':
1428                 break;
1429         case '<':
1430                 switch (v[*pos + 1]) {
1431                 case '=':
1432                         *res = 'l';
1433                         (*pos)++;
1434                         break;
1435                 case '>':
1436                         *res = '!';
1437                         (*pos)++;
1438                         break;
1439                 case '?':
1440                         *res = 'i';
1441                         (*pos)++;
1442                         break;
1443                 default:
1444                         break;
1445                 }
1446                 break;
1447         case '>':
1448                 switch (v[*pos + 1]) {
1449                 case '=':
1450                         *res = 'g';
1451                         (*pos)++;
1452                         break;
1453                 case '?':
1454                         *res = 'a';
1455                         (*pos)++;
1456                         break;
1457                 default:
1458                         break;
1459                 }
1460                 break;
1461         case '=':
1462                 if ('=' == v[*pos + 1])
1463                         (*pos)++;
1464                 break;
1465         default:
1466                 return(0);
1467         }
1468         (*pos)++;
1469
1470         return(*res);
1471 }
1472
1473 /*
1474  * Evaluate either a parenthesized numeric expression
1475  * or a single signed integer number.
1476  */
1477 static int
1478 roff_evalpar(struct roff *r, int ln,
1479         const char *v, int *pos, int *res)
1480 {
1481
1482         if ('(' != v[*pos])
1483                 return(roff_getnum(v, pos, res));
1484
1485         (*pos)++;
1486         if ( ! roff_evalnum(r, ln, v, pos, res, 1))
1487                 return(0);
1488
1489         /*
1490          * Omission of the closing parenthesis
1491          * is an error in validation mode,
1492          * but ignored in evaluation mode.
1493          */
1494
1495         if (')' == v[*pos])
1496                 (*pos)++;
1497         else if (NULL == res)
1498                 return(0);
1499
1500         return(1);
1501 }
1502
1503 /*
1504  * Evaluate a complete numeric expression.
1505  * Proceed left to right, there is no concept of precedence.
1506  */
1507 static int
1508 roff_evalnum(struct roff *r, int ln, const char *v,
1509         int *pos, int *res, int skipwhite)
1510 {
1511         int              mypos, operand2;
1512         char             operator;
1513
1514         if (NULL == pos) {
1515                 mypos = 0;
1516                 pos = &mypos;
1517         }
1518
1519         if (skipwhite)
1520                 while (isspace((unsigned char)v[*pos]))
1521                         (*pos)++;
1522
1523         if ( ! roff_evalpar(r, ln, v, pos, res))
1524                 return(0);
1525
1526         while (1) {
1527                 if (skipwhite)
1528                         while (isspace((unsigned char)v[*pos]))
1529                                 (*pos)++;
1530
1531                 if ( ! roff_getop(v, pos, &operator))
1532                         break;
1533
1534                 if (skipwhite)
1535                         while (isspace((unsigned char)v[*pos]))
1536                                 (*pos)++;
1537
1538                 if ( ! roff_evalpar(r, ln, v, pos, &operand2))
1539                         return(0);
1540
1541                 if (skipwhite)
1542                         while (isspace((unsigned char)v[*pos]))
1543                                 (*pos)++;
1544
1545                 if (NULL == res)
1546                         continue;
1547
1548                 switch (operator) {
1549                 case '+':
1550                         *res += operand2;
1551                         break;
1552                 case '-':
1553                         *res -= operand2;
1554                         break;
1555                 case '*':
1556                         *res *= operand2;
1557                         break;
1558                 case '/':
1559                         if (0 == operand2) {
1560                                 mandoc_msg(MANDOCERR_DIVZERO,
1561                                         r->parse, ln, *pos, v);
1562                                 *res = 0;
1563                                 break;
1564                         }
1565                         *res /= operand2;
1566                         break;
1567                 case '%':
1568                         *res %= operand2;
1569                         break;
1570                 case '<':
1571                         *res = *res < operand2;
1572                         break;
1573                 case '>':
1574                         *res = *res > operand2;
1575                         break;
1576                 case 'l':
1577                         *res = *res <= operand2;
1578                         break;
1579                 case 'g':
1580                         *res = *res >= operand2;
1581                         break;
1582                 case '=':
1583                         *res = *res == operand2;
1584                         break;
1585                 case '!':
1586                         *res = *res != operand2;
1587                         break;
1588                 case '&':
1589                         *res = *res && operand2;
1590                         break;
1591                 case ':':
1592                         *res = *res || operand2;
1593                         break;
1594                 case 'i':
1595                         if (operand2 < *res)
1596                                 *res = operand2;
1597                         break;
1598                 case 'a':
1599                         if (operand2 > *res)
1600                                 *res = operand2;
1601                         break;
1602                 default:
1603                         abort();
1604                 }
1605         }
1606         return(1);
1607 }
1608
1609 void
1610 roff_setreg(struct roff *r, const char *name, int val, char sign)
1611 {
1612         struct roffreg  *reg;
1613
1614         /* Search for an existing register with the same name. */
1615         reg = r->regtab;
1616
1617         while (reg && strcmp(name, reg->key.p))
1618                 reg = reg->next;
1619
1620         if (NULL == reg) {
1621                 /* Create a new register. */
1622                 reg = mandoc_malloc(sizeof(struct roffreg));
1623                 reg->key.p = mandoc_strdup(name);
1624                 reg->key.sz = strlen(name);
1625                 reg->val = 0;
1626                 reg->next = r->regtab;
1627                 r->regtab = reg;
1628         }
1629
1630         if ('+' == sign)
1631                 reg->val += val;
1632         else if ('-' == sign)
1633                 reg->val -= val;
1634         else
1635                 reg->val = val;
1636 }
1637
1638 /*
1639  * Handle some predefined read-only number registers.
1640  * For now, return -1 if the requested register is not predefined;
1641  * in case a predefined read-only register having the value -1
1642  * were to turn up, another special value would have to be chosen.
1643  */
1644 static int
1645 roff_getregro(const char *name)
1646 {
1647
1648         switch (*name) {
1649         case 'A':  /* ASCII approximation mode is always off. */
1650                 return(0);
1651         case 'g':  /* Groff compatibility mode is always on. */
1652                 return(1);
1653         case 'H':  /* Fixed horizontal resolution. */
1654                 return (24);
1655         case 'j':  /* Always adjust left margin only. */
1656                 return(0);
1657         case 'T':  /* Some output device is always defined. */
1658                 return(1);
1659         case 'V':  /* Fixed vertical resolution. */
1660                 return (40);
1661         default:
1662                 return (-1);
1663         }
1664 }
1665
1666 int
1667 roff_getreg(const struct roff *r, const char *name)
1668 {
1669         struct roffreg  *reg;
1670         int              val;
1671
1672         if ('.' == name[0] && '\0' != name[1] && '\0' == name[2]) {
1673                 val = roff_getregro(name + 1);
1674                 if (-1 != val)
1675                         return (val);
1676         }
1677
1678         for (reg = r->regtab; reg; reg = reg->next)
1679                 if (0 == strcmp(name, reg->key.p))
1680                         return(reg->val);
1681
1682         return(0);
1683 }
1684
1685 static int
1686 roff_getregn(const struct roff *r, const char *name, size_t len)
1687 {
1688         struct roffreg  *reg;
1689         int              val;
1690
1691         if ('.' == name[0] && 2 == len) {
1692                 val = roff_getregro(name + 1);
1693                 if (-1 != val)
1694                         return (val);
1695         }
1696
1697         for (reg = r->regtab; reg; reg = reg->next)
1698                 if (len == reg->key.sz &&
1699                     0 == strncmp(name, reg->key.p, len))
1700                         return(reg->val);
1701
1702         return(0);
1703 }
1704
1705 static void
1706 roff_freereg(struct roffreg *reg)
1707 {
1708         struct roffreg  *old_reg;
1709
1710         while (NULL != reg) {
1711                 free(reg->key.p);
1712                 old_reg = reg;
1713                 reg = reg->next;
1714                 free(old_reg);
1715         }
1716 }
1717
1718 static enum rofferr
1719 roff_nr(ROFF_ARGS)
1720 {
1721         char            *key, *val;
1722         size_t           keysz;
1723         int              iv;
1724         char             sign;
1725
1726         key = val = buf->buf + pos;
1727         if (*key == '\0')
1728                 return(ROFF_IGN);
1729
1730         keysz = roff_getname(r, &val, ln, pos);
1731         if (key[keysz] == '\\')
1732                 return(ROFF_IGN);
1733         key[keysz] = '\0';
1734
1735         sign = *val;
1736         if (sign == '+' || sign == '-')
1737                 val++;
1738
1739         if (roff_evalnum(r, ln, val, NULL, &iv, 0))
1740                 roff_setreg(r, key, iv, sign);
1741
1742         return(ROFF_IGN);
1743 }
1744
1745 static enum rofferr
1746 roff_rr(ROFF_ARGS)
1747 {
1748         struct roffreg  *reg, **prev;
1749         char            *name, *cp;
1750         size_t           namesz;
1751
1752         name = cp = buf->buf + pos;
1753         if (*name == '\0')
1754                 return(ROFF_IGN);
1755         namesz = roff_getname(r, &cp, ln, pos);
1756         name[namesz] = '\0';
1757
1758         prev = &r->regtab;
1759         while (1) {
1760                 reg = *prev;
1761                 if (reg == NULL || !strcmp(name, reg->key.p))
1762                         break;
1763                 prev = &reg->next;
1764         }
1765         if (reg != NULL) {
1766                 *prev = reg->next;
1767                 free(reg->key.p);
1768                 free(reg);
1769         }
1770         return(ROFF_IGN);
1771 }
1772
1773 static enum rofferr
1774 roff_rm(ROFF_ARGS)
1775 {
1776         const char       *name;
1777         char             *cp;
1778         size_t            namesz;
1779
1780         cp = buf->buf + pos;
1781         while (*cp != '\0') {
1782                 name = cp;
1783                 namesz = roff_getname(r, &cp, ln, (int)(cp - buf->buf));
1784                 roff_setstrn(&r->strtab, name, namesz, NULL, 0, 0);
1785                 if (name[namesz] == '\\')
1786                         break;
1787         }
1788         return(ROFF_IGN);
1789 }
1790
1791 static enum rofferr
1792 roff_it(ROFF_ARGS)
1793 {
1794         char            *cp;
1795         size_t           len;
1796         int              iv;
1797
1798         /* Parse the number of lines. */
1799         cp = buf->buf + pos;
1800         len = strcspn(cp, " \t");
1801         cp[len] = '\0';
1802         if ((iv = mandoc_strntoi(cp, len, 10)) <= 0) {
1803                 mandoc_msg(MANDOCERR_IT_NONUM, r->parse,
1804                     ln, ppos, buf->buf + 1);
1805                 return(ROFF_IGN);
1806         }
1807         cp += len + 1;
1808
1809         /* Arm the input line trap. */
1810         roffit_lines = iv;
1811         roffit_macro = mandoc_strdup(cp);
1812         return(ROFF_IGN);
1813 }
1814
1815 static enum rofferr
1816 roff_Dd(ROFF_ARGS)
1817 {
1818         const char *const       *cp;
1819
1820         if ((r->options & (MPARSE_MDOC | MPARSE_QUICK)) == 0)
1821                 for (cp = __mdoc_reserved; *cp; cp++)
1822                         roff_setstr(r, *cp, NULL, 0);
1823
1824         if (r->format == 0)
1825                 r->format = MPARSE_MDOC;
1826
1827         return(ROFF_CONT);
1828 }
1829
1830 static enum rofferr
1831 roff_TH(ROFF_ARGS)
1832 {
1833         const char *const       *cp;
1834
1835         if ((r->options & MPARSE_QUICK) == 0)
1836                 for (cp = __man_reserved; *cp; cp++)
1837                         roff_setstr(r, *cp, NULL, 0);
1838
1839         if (r->format == 0)
1840                 r->format = MPARSE_MAN;
1841
1842         return(ROFF_CONT);
1843 }
1844
1845 static enum rofferr
1846 roff_TE(ROFF_ARGS)
1847 {
1848
1849         if (NULL == r->tbl)
1850                 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1851                     ln, ppos, "TE");
1852         else
1853                 tbl_end(&r->tbl);
1854
1855         return(ROFF_IGN);
1856 }
1857
1858 static enum rofferr
1859 roff_T_(ROFF_ARGS)
1860 {
1861
1862         if (NULL == r->tbl)
1863                 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1864                     ln, ppos, "T&");
1865         else
1866                 tbl_restart(ppos, ln, r->tbl);
1867
1868         return(ROFF_IGN);
1869 }
1870
1871 /*
1872  * Handle in-line equation delimiters.
1873  */
1874 static enum rofferr
1875 roff_eqndelim(struct roff *r, struct buf *buf, int pos)
1876 {
1877         char            *cp1, *cp2;
1878         const char      *bef_pr, *bef_nl, *mac, *aft_nl, *aft_pr;
1879
1880         /*
1881          * Outside equations, look for an opening delimiter.
1882          * If we are inside an equation, we already know it is
1883          * in-line, or this function wouldn't have been called;
1884          * so look for a closing delimiter.
1885          */
1886
1887         cp1 = buf->buf + pos;
1888         cp2 = strchr(cp1, r->eqn == NULL ?
1889             r->last_eqn->odelim : r->last_eqn->cdelim);
1890         if (cp2 == NULL)
1891                 return(ROFF_CONT);
1892
1893         *cp2++ = '\0';
1894         bef_pr = bef_nl = aft_nl = aft_pr = "";
1895
1896         /* Handle preceding text, protecting whitespace. */
1897
1898         if (*buf->buf != '\0') {
1899                 if (r->eqn == NULL)
1900                         bef_pr = "\\&";
1901                 bef_nl = "\n";
1902         }
1903
1904         /*
1905          * Prepare replacing the delimiter with an equation macro
1906          * and drop leading white space from the equation.
1907          */
1908
1909         if (r->eqn == NULL) {
1910                 while (*cp2 == ' ')
1911                         cp2++;
1912                 mac = ".EQ";
1913         } else
1914                 mac = ".EN";
1915
1916         /* Handle following text, protecting whitespace. */
1917
1918         if (*cp2 != '\0') {
1919                 aft_nl = "\n";
1920                 if (r->eqn != NULL)
1921                         aft_pr = "\\&";
1922         }
1923
1924         /* Do the actual replacement. */
1925
1926         buf->sz = mandoc_asprintf(&cp1, "%s%s%s%s%s%s%s", buf->buf,
1927             bef_pr, bef_nl, mac, aft_nl, aft_pr, cp2) + 1;
1928         free(buf->buf);
1929         buf->buf = cp1;
1930
1931         /* Toggle the in-line state of the eqn subsystem. */
1932
1933         r->eqn_inline = r->eqn == NULL;
1934         return(ROFF_REPARSE);
1935 }
1936
1937 static enum rofferr
1938 roff_EQ(ROFF_ARGS)
1939 {
1940         struct eqn_node *e;
1941
1942         assert(r->eqn == NULL);
1943         e = eqn_alloc(ppos, ln, r->parse);
1944
1945         if (r->last_eqn) {
1946                 r->last_eqn->next = e;
1947                 e->delim = r->last_eqn->delim;
1948                 e->odelim = r->last_eqn->odelim;
1949                 e->cdelim = r->last_eqn->cdelim;
1950         } else
1951                 r->first_eqn = r->last_eqn = e;
1952
1953         r->eqn = r->last_eqn = e;
1954
1955         if (buf->buf[pos] != '\0')
1956                 mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos,
1957                     ".EQ %s", buf->buf + pos);
1958
1959         return(ROFF_IGN);
1960 }
1961
1962 static enum rofferr
1963 roff_EN(ROFF_ARGS)
1964 {
1965
1966         mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, ln, ppos, "EN");
1967         return(ROFF_IGN);
1968 }
1969
1970 static enum rofferr
1971 roff_TS(ROFF_ARGS)
1972 {
1973         struct tbl_node *tbl;
1974
1975         if (r->tbl) {
1976                 mandoc_msg(MANDOCERR_BLK_BROKEN, r->parse,
1977                     ln, ppos, "TS breaks TS");
1978                 tbl_end(&r->tbl);
1979         }
1980
1981         tbl = tbl_alloc(ppos, ln, r->parse);
1982
1983         if (r->last_tbl)
1984                 r->last_tbl->next = tbl;
1985         else
1986                 r->first_tbl = r->last_tbl = tbl;
1987
1988         r->tbl = r->last_tbl = tbl;
1989         return(ROFF_IGN);
1990 }
1991
1992 static enum rofferr
1993 roff_cc(ROFF_ARGS)
1994 {
1995         const char      *p;
1996
1997         p = buf->buf + pos;
1998
1999         if (*p == '\0' || (r->control = *p++) == '.')
2000                 r->control = 0;
2001
2002         if (*p != '\0')
2003                 mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL);
2004
2005         return(ROFF_IGN);
2006 }
2007
2008 static enum rofferr
2009 roff_tr(ROFF_ARGS)
2010 {
2011         const char      *p, *first, *second;
2012         size_t           fsz, ssz;
2013         enum mandoc_esc  esc;
2014
2015         p = buf->buf + pos;
2016
2017         if (*p == '\0') {
2018                 mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL);
2019                 return(ROFF_IGN);
2020         }
2021
2022         while (*p != '\0') {
2023                 fsz = ssz = 1;
2024
2025                 first = p++;
2026                 if (*first == '\\') {
2027                         esc = mandoc_escape(&p, NULL, NULL);
2028                         if (esc == ESCAPE_ERROR) {
2029                                 mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
2030                                     ln, (int)(p - buf->buf), first);
2031                                 return(ROFF_IGN);
2032                         }
2033                         fsz = (size_t)(p - first);
2034                 }
2035
2036                 second = p++;
2037                 if (*second == '\\') {
2038                         esc = mandoc_escape(&p, NULL, NULL);
2039                         if (esc == ESCAPE_ERROR) {
2040                                 mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
2041                                     ln, (int)(p - buf->buf), second);
2042                                 return(ROFF_IGN);
2043                         }
2044                         ssz = (size_t)(p - second);
2045                 } else if (*second == '\0') {
2046                         mandoc_msg(MANDOCERR_ARGCOUNT, r->parse,
2047                             ln, (int)(p - buf->buf), NULL);
2048                         second = " ";
2049                         p--;
2050                 }
2051
2052                 if (fsz > 1) {
2053                         roff_setstrn(&r->xmbtab, first, fsz,
2054                             second, ssz, 0);
2055                         continue;
2056                 }
2057
2058                 if (r->xtab == NULL)
2059                         r->xtab = mandoc_calloc(128,
2060                             sizeof(struct roffstr));
2061
2062                 free(r->xtab[(int)*first].p);
2063                 r->xtab[(int)*first].p = mandoc_strndup(second, ssz);
2064                 r->xtab[(int)*first].sz = ssz;
2065         }
2066
2067         return(ROFF_IGN);
2068 }
2069
2070 static enum rofferr
2071 roff_so(ROFF_ARGS)
2072 {
2073         char *name;
2074
2075         name = buf->buf + pos;
2076         mandoc_vmsg(MANDOCERR_SO, r->parse, ln, ppos, "so %s", name);
2077
2078         /*
2079          * Handle `so'.  Be EXTREMELY careful, as we shouldn't be
2080          * opening anything that's not in our cwd or anything beneath
2081          * it.  Thus, explicitly disallow traversing up the file-system
2082          * or using absolute paths.
2083          */
2084
2085         if (*name == '/' || strstr(name, "../") || strstr(name, "/..")) {
2086                 mandoc_vmsg(MANDOCERR_SO_PATH, r->parse, ln, ppos,
2087                     ".so %s", name);
2088                 return(ROFF_ERR);
2089         }
2090
2091         *offs = pos;
2092         return(ROFF_SO);
2093 }
2094
2095 static enum rofferr
2096 roff_userdef(ROFF_ARGS)
2097 {
2098         const char       *arg[9];
2099         char             *cp, *n1, *n2;
2100         int               i;
2101
2102         /*
2103          * Collect pointers to macro argument strings
2104          * and NUL-terminate them.
2105          */
2106         cp = buf->buf + pos;
2107         for (i = 0; i < 9; i++)
2108                 arg[i] = *cp == '\0' ? "" :
2109                     mandoc_getarg(r->parse, &cp, ln, &pos);
2110
2111         /*
2112          * Expand macro arguments.
2113          */
2114         buf->sz = 0;
2115         n1 = cp = mandoc_strdup(r->current_string);
2116         while ((cp = strstr(cp, "\\$")) != NULL) {
2117                 i = cp[2] - '1';
2118                 if (0 > i || 8 < i) {
2119                         /* Not an argument invocation. */
2120                         cp += 2;
2121                         continue;
2122                 }
2123                 *cp = '\0';
2124                 buf->sz = mandoc_asprintf(&n2, "%s%s%s",
2125                     n1, arg[i], cp + 3) + 1;
2126                 cp = n2 + (cp - n1);
2127                 free(n1);
2128                 n1 = n2;
2129         }
2130
2131         /*
2132          * Replace the macro invocation
2133          * by the expanded macro.
2134          */
2135         free(buf->buf);
2136         buf->buf = n1;
2137         if (buf->sz == 0)
2138                 buf->sz = strlen(buf->buf) + 1;
2139
2140         return(buf->sz > 1 && buf->buf[buf->sz - 2] == '\n' ?
2141            ROFF_REPARSE : ROFF_APPEND);
2142 }
2143
2144 static size_t
2145 roff_getname(struct roff *r, char **cpp, int ln, int pos)
2146 {
2147         char     *name, *cp;
2148         size_t    namesz;
2149
2150         name = *cpp;
2151         if ('\0' == *name)
2152                 return(0);
2153
2154         /* Read until end of name and terminate it with NUL. */
2155         for (cp = name; 1; cp++) {
2156                 if ('\0' == *cp || ' ' == *cp) {
2157                         namesz = cp - name;
2158                         break;
2159                 }
2160                 if ('\\' != *cp)
2161                         continue;
2162                 namesz = cp - name;
2163                 if ('{' == cp[1] || '}' == cp[1])
2164                         break;
2165                 cp++;
2166                 if ('\\' == *cp)
2167                         continue;
2168                 mandoc_vmsg(MANDOCERR_NAMESC, r->parse, ln, pos,
2169                     "%.*s", (int)(cp - name + 1), name);
2170                 mandoc_escape((const char **)&cp, NULL, NULL);
2171                 break;
2172         }
2173
2174         /* Read past spaces. */
2175         while (' ' == *cp)
2176                 cp++;
2177
2178         *cpp = cp;
2179         return(namesz);
2180 }
2181
2182 /*
2183  * Store *string into the user-defined string called *name.
2184  * To clear an existing entry, call with (*r, *name, NULL, 0).
2185  * append == 0: replace mode
2186  * append == 1: single-line append mode
2187  * append == 2: multiline append mode, append '\n' after each call
2188  */
2189 static void
2190 roff_setstr(struct roff *r, const char *name, const char *string,
2191         int append)
2192 {
2193
2194         roff_setstrn(&r->strtab, name, strlen(name), string,
2195             string ? strlen(string) : 0, append);
2196 }
2197
2198 static void
2199 roff_setstrn(struct roffkv **r, const char *name, size_t namesz,
2200                 const char *string, size_t stringsz, int append)
2201 {
2202         struct roffkv   *n;
2203         char            *c;
2204         int              i;
2205         size_t           oldch, newch;
2206
2207         /* Search for an existing string with the same name. */
2208         n = *r;
2209
2210         while (n && (namesz != n->key.sz ||
2211                         strncmp(n->key.p, name, namesz)))
2212                 n = n->next;
2213
2214         if (NULL == n) {
2215                 /* Create a new string table entry. */
2216                 n = mandoc_malloc(sizeof(struct roffkv));
2217                 n->key.p = mandoc_strndup(name, namesz);
2218                 n->key.sz = namesz;
2219                 n->val.p = NULL;
2220                 n->val.sz = 0;
2221                 n->next = *r;
2222                 *r = n;
2223         } else if (0 == append) {
2224                 free(n->val.p);
2225                 n->val.p = NULL;
2226                 n->val.sz = 0;
2227         }
2228
2229         if (NULL == string)
2230                 return;
2231
2232         /*
2233          * One additional byte for the '\n' in multiline mode,
2234          * and one for the terminating '\0'.
2235          */
2236         newch = stringsz + (1 < append ? 2u : 1u);
2237
2238         if (NULL == n->val.p) {
2239                 n->val.p = mandoc_malloc(newch);
2240                 *n->val.p = '\0';
2241                 oldch = 0;
2242         } else {
2243                 oldch = n->val.sz;
2244                 n->val.p = mandoc_realloc(n->val.p, oldch + newch);
2245         }
2246
2247         /* Skip existing content in the destination buffer. */
2248         c = n->val.p + (int)oldch;
2249
2250         /* Append new content to the destination buffer. */
2251         i = 0;
2252         while (i < (int)stringsz) {
2253                 /*
2254                  * Rudimentary roff copy mode:
2255                  * Handle escaped backslashes.
2256                  */
2257                 if ('\\' == string[i] && '\\' == string[i + 1])
2258                         i++;
2259                 *c++ = string[i++];
2260         }
2261
2262         /* Append terminating bytes. */
2263         if (1 < append)
2264                 *c++ = '\n';
2265
2266         *c = '\0';
2267         n->val.sz = (int)(c - n->val.p);
2268 }
2269
2270 static const char *
2271 roff_getstrn(const struct roff *r, const char *name, size_t len)
2272 {
2273         const struct roffkv *n;
2274         int i;
2275
2276         for (n = r->strtab; n; n = n->next)
2277                 if (0 == strncmp(name, n->key.p, len) &&
2278                     '\0' == n->key.p[(int)len])
2279                         return(n->val.p);
2280
2281         for (i = 0; i < PREDEFS_MAX; i++)
2282                 if (0 == strncmp(name, predefs[i].name, len) &&
2283                                 '\0' == predefs[i].name[(int)len])
2284                         return(predefs[i].str);
2285
2286         return(NULL);
2287 }
2288
2289 static void
2290 roff_freestr(struct roffkv *r)
2291 {
2292         struct roffkv    *n, *nn;
2293
2294         for (n = r; n; n = nn) {
2295                 free(n->key.p);
2296                 free(n->val.p);
2297                 nn = n->next;
2298                 free(n);
2299         }
2300 }
2301
2302 const struct tbl_span *
2303 roff_span(const struct roff *r)
2304 {
2305
2306         return(r->tbl ? tbl_span(r->tbl) : NULL);
2307 }
2308
2309 const struct eqn *
2310 roff_eqn(const struct roff *r)
2311 {
2312
2313         return(r->last_eqn ? &r->last_eqn->eqn : NULL);
2314 }
2315
2316 /*
2317  * Duplicate an input string, making the appropriate character
2318  * conversations (as stipulated by `tr') along the way.
2319  * Returns a heap-allocated string with all the replacements made.
2320  */
2321 char *
2322 roff_strdup(const struct roff *r, const char *p)
2323 {
2324         const struct roffkv *cp;
2325         char            *res;
2326         const char      *pp;
2327         size_t           ssz, sz;
2328         enum mandoc_esc  esc;
2329
2330         if (NULL == r->xmbtab && NULL == r->xtab)
2331                 return(mandoc_strdup(p));
2332         else if ('\0' == *p)
2333                 return(mandoc_strdup(""));
2334
2335         /*
2336          * Step through each character looking for term matches
2337          * (remember that a `tr' can be invoked with an escape, which is
2338          * a glyph but the escape is multi-character).
2339          * We only do this if the character hash has been initialised
2340          * and the string is >0 length.
2341          */
2342
2343         res = NULL;
2344         ssz = 0;
2345
2346         while ('\0' != *p) {
2347                 if ('\\' != *p && r->xtab && r->xtab[(int)*p].p) {
2348                         sz = r->xtab[(int)*p].sz;
2349                         res = mandoc_realloc(res, ssz + sz + 1);
2350                         memcpy(res + ssz, r->xtab[(int)*p].p, sz);
2351                         ssz += sz;
2352                         p++;
2353                         continue;
2354                 } else if ('\\' != *p) {
2355                         res = mandoc_realloc(res, ssz + 2);
2356                         res[ssz++] = *p++;
2357                         continue;
2358                 }
2359
2360                 /* Search for term matches. */
2361                 for (cp = r->xmbtab; cp; cp = cp->next)
2362                         if (0 == strncmp(p, cp->key.p, cp->key.sz))
2363                                 break;
2364
2365                 if (NULL != cp) {
2366                         /*
2367                          * A match has been found.
2368                          * Append the match to the array and move
2369                          * forward by its keysize.
2370                          */
2371                         res = mandoc_realloc(res,
2372                             ssz + cp->val.sz + 1);
2373                         memcpy(res + ssz, cp->val.p, cp->val.sz);
2374                         ssz += cp->val.sz;
2375                         p += (int)cp->key.sz;
2376                         continue;
2377                 }
2378
2379                 /*
2380                  * Handle escapes carefully: we need to copy
2381                  * over just the escape itself, or else we might
2382                  * do replacements within the escape itself.
2383                  * Make sure to pass along the bogus string.
2384                  */
2385                 pp = p++;
2386                 esc = mandoc_escape(&p, NULL, NULL);
2387                 if (ESCAPE_ERROR == esc) {
2388                         sz = strlen(pp);
2389                         res = mandoc_realloc(res, ssz + sz + 1);
2390                         memcpy(res + ssz, pp, sz);
2391                         break;
2392                 }
2393                 /*
2394                  * We bail out on bad escapes.
2395                  * No need to warn: we already did so when
2396                  * roff_res() was called.
2397                  */
2398                 sz = (int)(p - pp);
2399                 res = mandoc_realloc(res, ssz + sz + 1);
2400                 memcpy(res + ssz, pp, sz);
2401                 ssz += sz;
2402         }
2403
2404         res[(int)ssz] = '\0';
2405         return(res);
2406 }
2407
2408 int
2409 roff_getformat(const struct roff *r)
2410 {
2411
2412         return(r->format);
2413 }
2414
2415 /*
2416  * Find out whether a line is a macro line or not.
2417  * If it is, adjust the current position and return one; if it isn't,
2418  * return zero and don't change the current position.
2419  * If the control character has been set with `.cc', then let that grain
2420  * precedence.
2421  * This is slighly contrary to groff, where using the non-breaking
2422  * control character when `cc' has been invoked will cause the
2423  * non-breaking macro contents to be printed verbatim.
2424  */
2425 int
2426 roff_getcontrol(const struct roff *r, const char *cp, int *ppos)
2427 {
2428         int             pos;
2429
2430         pos = *ppos;
2431
2432         if (0 != r->control && cp[pos] == r->control)
2433                 pos++;
2434         else if (0 != r->control)
2435                 return(0);
2436         else if ('\\' == cp[pos] && '.' == cp[pos + 1])
2437                 pos += 2;
2438         else if ('.' == cp[pos] || '\'' == cp[pos])
2439                 pos++;
2440         else
2441                 return(0);
2442
2443         while (' ' == cp[pos] || '\t' == cp[pos])
2444                 pos++;
2445
2446         *ppos = pos;
2447         return(1);
2448 }