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