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