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