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