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