xref: /titanic_51/usr/src/cmd/mandoc/roff.c (revision 260e9a87725c090ba5835b1f9f0b62fa2f96036f)
1*260e9a87SYuri Pankov /*	$Id: roff.c,v 1.263 2015/02/21 14:46:58 schwarze Exp $ */
295c635efSGarrett D'Amore /*
3*260e9a87SYuri Pankov  * Copyright (c) 2010, 2011, 2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4*260e9a87SYuri Pankov  * Copyright (c) 2010-2015 Ingo Schwarze <schwarze@openbsd.org>
595c635efSGarrett D'Amore  *
695c635efSGarrett D'Amore  * Permission to use, copy, modify, and distribute this software for any
795c635efSGarrett D'Amore  * purpose with or without fee is hereby granted, provided that the above
895c635efSGarrett D'Amore  * copyright notice and this permission notice appear in all copies.
995c635efSGarrett D'Amore  *
1095c635efSGarrett D'Amore  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1195c635efSGarrett D'Amore  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1295c635efSGarrett D'Amore  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1395c635efSGarrett D'Amore  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1495c635efSGarrett D'Amore  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1595c635efSGarrett D'Amore  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1695c635efSGarrett D'Amore  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1795c635efSGarrett D'Amore  */
1895c635efSGarrett D'Amore #include "config.h"
19*260e9a87SYuri Pankov 
20*260e9a87SYuri Pankov #include <sys/types.h>
2195c635efSGarrett D'Amore 
2295c635efSGarrett D'Amore #include <assert.h>
2395c635efSGarrett D'Amore #include <ctype.h>
24*260e9a87SYuri Pankov #include <limits.h>
25698f87a4SGarrett D'Amore #include <stdio.h>
2695c635efSGarrett D'Amore #include <stdlib.h>
2795c635efSGarrett D'Amore #include <string.h>
2895c635efSGarrett D'Amore 
2995c635efSGarrett D'Amore #include "mandoc.h"
30*260e9a87SYuri Pankov #include "mandoc_aux.h"
3195c635efSGarrett D'Amore #include "libmandoc.h"
32*260e9a87SYuri Pankov #include "libroff.h"
3395c635efSGarrett D'Amore 
3495c635efSGarrett D'Amore /* Maximum number of nested if-else conditionals. */
3595c635efSGarrett D'Amore #define	RSTACK_MAX	128
3695c635efSGarrett D'Amore 
3795c635efSGarrett D'Amore /* Maximum number of string expansions per line, to break infinite loops. */
3895c635efSGarrett D'Amore #define	EXPAND_LIMIT	1000
3995c635efSGarrett D'Amore 
4095c635efSGarrett D'Amore enum	rofft {
41*260e9a87SYuri Pankov 	ROFF_ab,
4295c635efSGarrett D'Amore 	ROFF_ad,
43*260e9a87SYuri Pankov 	ROFF_af,
44*260e9a87SYuri Pankov 	ROFF_aln,
45*260e9a87SYuri Pankov 	ROFF_als,
4695c635efSGarrett D'Amore 	ROFF_am,
4795c635efSGarrett D'Amore 	ROFF_am1,
48*260e9a87SYuri Pankov 	ROFF_ami,
49*260e9a87SYuri Pankov 	ROFF_ami1,
50*260e9a87SYuri Pankov 	ROFF_as,
51*260e9a87SYuri Pankov 	ROFF_as1,
52*260e9a87SYuri Pankov 	ROFF_asciify,
53*260e9a87SYuri Pankov 	ROFF_backtrace,
54*260e9a87SYuri Pankov 	ROFF_bd,
55*260e9a87SYuri Pankov 	ROFF_bleedat,
56*260e9a87SYuri Pankov 	ROFF_blm,
57*260e9a87SYuri Pankov 	ROFF_box,
58*260e9a87SYuri Pankov 	ROFF_boxa,
59*260e9a87SYuri Pankov 	ROFF_bp,
60*260e9a87SYuri Pankov 	ROFF_BP,
61*260e9a87SYuri Pankov 	/* MAN_br, MDOC_br */
62*260e9a87SYuri Pankov 	ROFF_break,
63*260e9a87SYuri Pankov 	ROFF_breakchar,
64*260e9a87SYuri Pankov 	ROFF_brnl,
65*260e9a87SYuri Pankov 	ROFF_brp,
66*260e9a87SYuri Pankov 	ROFF_brpnl,
67*260e9a87SYuri Pankov 	ROFF_c2,
68698f87a4SGarrett D'Amore 	ROFF_cc,
69*260e9a87SYuri Pankov 	ROFF_ce,
70*260e9a87SYuri Pankov 	ROFF_cf,
71*260e9a87SYuri Pankov 	ROFF_cflags,
72*260e9a87SYuri Pankov 	ROFF_ch,
73*260e9a87SYuri Pankov 	ROFF_char,
74*260e9a87SYuri Pankov 	ROFF_chop,
75*260e9a87SYuri Pankov 	ROFF_class,
76*260e9a87SYuri Pankov 	ROFF_close,
77*260e9a87SYuri Pankov 	ROFF_CL,
78*260e9a87SYuri Pankov 	ROFF_color,
79*260e9a87SYuri Pankov 	ROFF_composite,
80*260e9a87SYuri Pankov 	ROFF_continue,
81*260e9a87SYuri Pankov 	ROFF_cp,
82*260e9a87SYuri Pankov 	ROFF_cropat,
83*260e9a87SYuri Pankov 	ROFF_cs,
84*260e9a87SYuri Pankov 	ROFF_cu,
85*260e9a87SYuri Pankov 	ROFF_da,
86*260e9a87SYuri Pankov 	ROFF_dch,
87*260e9a87SYuri Pankov 	ROFF_Dd,
8895c635efSGarrett D'Amore 	ROFF_de,
8995c635efSGarrett D'Amore 	ROFF_de1,
90*260e9a87SYuri Pankov 	ROFF_defcolor,
91*260e9a87SYuri Pankov 	ROFF_dei,
92*260e9a87SYuri Pankov 	ROFF_dei1,
93*260e9a87SYuri Pankov 	ROFF_device,
94*260e9a87SYuri Pankov 	ROFF_devicem,
95*260e9a87SYuri Pankov 	ROFF_di,
96*260e9a87SYuri Pankov 	ROFF_do,
9795c635efSGarrett D'Amore 	ROFF_ds,
98*260e9a87SYuri Pankov 	ROFF_ds1,
99*260e9a87SYuri Pankov 	ROFF_dwh,
100*260e9a87SYuri Pankov 	ROFF_dt,
101*260e9a87SYuri Pankov 	ROFF_ec,
102*260e9a87SYuri Pankov 	ROFF_ecr,
103*260e9a87SYuri Pankov 	ROFF_ecs,
10495c635efSGarrett D'Amore 	ROFF_el,
105*260e9a87SYuri Pankov 	ROFF_em,
106*260e9a87SYuri Pankov 	ROFF_EN,
107*260e9a87SYuri Pankov 	ROFF_eo,
108*260e9a87SYuri Pankov 	ROFF_EP,
109*260e9a87SYuri Pankov 	ROFF_EQ,
110*260e9a87SYuri Pankov 	ROFF_errprint,
111*260e9a87SYuri Pankov 	ROFF_ev,
112*260e9a87SYuri Pankov 	ROFF_evc,
113*260e9a87SYuri Pankov 	ROFF_ex,
114*260e9a87SYuri Pankov 	ROFF_fallback,
115698f87a4SGarrett D'Amore 	ROFF_fam,
116*260e9a87SYuri Pankov 	ROFF_fc,
117*260e9a87SYuri Pankov 	ROFF_fchar,
118*260e9a87SYuri Pankov 	ROFF_fcolor,
119*260e9a87SYuri Pankov 	ROFF_fdeferlig,
120*260e9a87SYuri Pankov 	ROFF_feature,
121*260e9a87SYuri Pankov 	/* MAN_fi; ignored in mdoc(7) */
122*260e9a87SYuri Pankov 	ROFF_fkern,
123*260e9a87SYuri Pankov 	ROFF_fl,
124*260e9a87SYuri Pankov 	ROFF_flig,
125*260e9a87SYuri Pankov 	ROFF_fp,
126*260e9a87SYuri Pankov 	ROFF_fps,
127*260e9a87SYuri Pankov 	ROFF_fschar,
128*260e9a87SYuri Pankov 	ROFF_fspacewidth,
129*260e9a87SYuri Pankov 	ROFF_fspecial,
130*260e9a87SYuri Pankov 	/* MAN_ft; ignored in mdoc(7) */
131*260e9a87SYuri Pankov 	ROFF_ftr,
132*260e9a87SYuri Pankov 	ROFF_fzoom,
133*260e9a87SYuri Pankov 	ROFF_gcolor,
134*260e9a87SYuri Pankov 	ROFF_hc,
135*260e9a87SYuri Pankov 	ROFF_hcode,
136*260e9a87SYuri Pankov 	ROFF_hidechar,
137*260e9a87SYuri Pankov 	ROFF_hla,
138*260e9a87SYuri Pankov 	ROFF_hlm,
139*260e9a87SYuri Pankov 	ROFF_hpf,
140*260e9a87SYuri Pankov 	ROFF_hpfa,
141*260e9a87SYuri Pankov 	ROFF_hpfcode,
142698f87a4SGarrett D'Amore 	ROFF_hw,
14395c635efSGarrett D'Amore 	ROFF_hy,
144*260e9a87SYuri Pankov 	ROFF_hylang,
145*260e9a87SYuri Pankov 	ROFF_hylen,
146*260e9a87SYuri Pankov 	ROFF_hym,
147*260e9a87SYuri Pankov 	ROFF_hypp,
148*260e9a87SYuri Pankov 	ROFF_hys,
14995c635efSGarrett D'Amore 	ROFF_ie,
15095c635efSGarrett D'Amore 	ROFF_if,
15195c635efSGarrett D'Amore 	ROFF_ig,
152*260e9a87SYuri Pankov 	/* MAN_in; ignored in mdoc(7) */
153*260e9a87SYuri Pankov 	ROFF_index,
15495c635efSGarrett D'Amore 	ROFF_it,
155*260e9a87SYuri Pankov 	ROFF_itc,
156*260e9a87SYuri Pankov 	ROFF_IX,
157*260e9a87SYuri Pankov 	ROFF_kern,
158*260e9a87SYuri Pankov 	ROFF_kernafter,
159*260e9a87SYuri Pankov 	ROFF_kernbefore,
160*260e9a87SYuri Pankov 	ROFF_kernpair,
161*260e9a87SYuri Pankov 	ROFF_lc,
162*260e9a87SYuri Pankov 	ROFF_lc_ctype,
163*260e9a87SYuri Pankov 	ROFF_lds,
164*260e9a87SYuri Pankov 	ROFF_length,
165*260e9a87SYuri Pankov 	ROFF_letadj,
166*260e9a87SYuri Pankov 	ROFF_lf,
167*260e9a87SYuri Pankov 	ROFF_lg,
168*260e9a87SYuri Pankov 	ROFF_lhang,
169*260e9a87SYuri Pankov 	ROFF_linetabs,
170*260e9a87SYuri Pankov 	/* MAN_ll, MDOC_ll */
171*260e9a87SYuri Pankov 	ROFF_lnr,
172*260e9a87SYuri Pankov 	ROFF_lnrf,
173*260e9a87SYuri Pankov 	ROFF_lpfx,
174*260e9a87SYuri Pankov 	ROFF_ls,
175*260e9a87SYuri Pankov 	ROFF_lsm,
176*260e9a87SYuri Pankov 	ROFF_lt,
177*260e9a87SYuri Pankov 	ROFF_mc,
178*260e9a87SYuri Pankov 	ROFF_mediasize,
179*260e9a87SYuri Pankov 	ROFF_minss,
180*260e9a87SYuri Pankov 	ROFF_mk,
181*260e9a87SYuri Pankov 	ROFF_mso,
182*260e9a87SYuri Pankov 	ROFF_na,
18395c635efSGarrett D'Amore 	ROFF_ne,
184*260e9a87SYuri Pankov 	/* MAN_nf; ignored in mdoc(7) */
18595c635efSGarrett D'Amore 	ROFF_nh,
186*260e9a87SYuri Pankov 	ROFF_nhychar,
187*260e9a87SYuri Pankov 	ROFF_nm,
188*260e9a87SYuri Pankov 	ROFF_nn,
189*260e9a87SYuri Pankov 	ROFF_nop,
19095c635efSGarrett D'Amore 	ROFF_nr,
191*260e9a87SYuri Pankov 	ROFF_nrf,
192*260e9a87SYuri Pankov 	ROFF_nroff,
19395c635efSGarrett D'Amore 	ROFF_ns,
194*260e9a87SYuri Pankov 	ROFF_nx,
195*260e9a87SYuri Pankov 	ROFF_open,
196*260e9a87SYuri Pankov 	ROFF_opena,
197*260e9a87SYuri Pankov 	ROFF_os,
198*260e9a87SYuri Pankov 	ROFF_output,
199*260e9a87SYuri Pankov 	ROFF_padj,
200*260e9a87SYuri Pankov 	ROFF_papersize,
201*260e9a87SYuri Pankov 	ROFF_pc,
202*260e9a87SYuri Pankov 	ROFF_pev,
203*260e9a87SYuri Pankov 	ROFF_pi,
204*260e9a87SYuri Pankov 	ROFF_PI,
205*260e9a87SYuri Pankov 	ROFF_pl,
206*260e9a87SYuri Pankov 	ROFF_pm,
207*260e9a87SYuri Pankov 	ROFF_pn,
208*260e9a87SYuri Pankov 	ROFF_pnr,
209*260e9a87SYuri Pankov 	ROFF_po,
21095c635efSGarrett D'Amore 	ROFF_ps,
211*260e9a87SYuri Pankov 	ROFF_psbb,
212*260e9a87SYuri Pankov 	ROFF_pshape,
213*260e9a87SYuri Pankov 	ROFF_pso,
214*260e9a87SYuri Pankov 	ROFF_ptr,
215*260e9a87SYuri Pankov 	ROFF_pvs,
216*260e9a87SYuri Pankov 	ROFF_rchar,
217*260e9a87SYuri Pankov 	ROFF_rd,
218*260e9a87SYuri Pankov 	ROFF_recursionlimit,
219*260e9a87SYuri Pankov 	ROFF_return,
220*260e9a87SYuri Pankov 	ROFF_rfschar,
221*260e9a87SYuri Pankov 	ROFF_rhang,
222*260e9a87SYuri Pankov 	ROFF_rj,
22395c635efSGarrett D'Amore 	ROFF_rm,
224*260e9a87SYuri Pankov 	ROFF_rn,
225*260e9a87SYuri Pankov 	ROFF_rnn,
226*260e9a87SYuri Pankov 	ROFF_rr,
227*260e9a87SYuri Pankov 	ROFF_rs,
228*260e9a87SYuri Pankov 	ROFF_rt,
229*260e9a87SYuri Pankov 	ROFF_schar,
230*260e9a87SYuri Pankov 	ROFF_sentchar,
231*260e9a87SYuri Pankov 	ROFF_shc,
232*260e9a87SYuri Pankov 	ROFF_shift,
233*260e9a87SYuri Pankov 	ROFF_sizes,
23495c635efSGarrett D'Amore 	ROFF_so,
235*260e9a87SYuri Pankov 	/* MAN_sp, MDOC_sp */
236*260e9a87SYuri Pankov 	ROFF_spacewidth,
237*260e9a87SYuri Pankov 	ROFF_special,
238*260e9a87SYuri Pankov 	ROFF_spreadwarn,
239*260e9a87SYuri Pankov 	ROFF_ss,
240*260e9a87SYuri Pankov 	ROFF_sty,
241*260e9a87SYuri Pankov 	ROFF_substring,
242*260e9a87SYuri Pankov 	ROFF_sv,
243*260e9a87SYuri Pankov 	ROFF_sy,
24495c635efSGarrett D'Amore 	ROFF_T_,
245*260e9a87SYuri Pankov 	ROFF_ta,
246*260e9a87SYuri Pankov 	ROFF_tc,
247*260e9a87SYuri Pankov 	ROFF_TE,
248*260e9a87SYuri Pankov 	ROFF_TH,
249*260e9a87SYuri Pankov 	ROFF_ti,
250*260e9a87SYuri Pankov 	ROFF_tkf,
251*260e9a87SYuri Pankov 	ROFF_tl,
252*260e9a87SYuri Pankov 	ROFF_tm,
253*260e9a87SYuri Pankov 	ROFF_tm1,
254*260e9a87SYuri Pankov 	ROFF_tmc,
255*260e9a87SYuri Pankov 	ROFF_tr,
256*260e9a87SYuri Pankov 	ROFF_track,
257*260e9a87SYuri Pankov 	ROFF_transchar,
258*260e9a87SYuri Pankov 	ROFF_trf,
259*260e9a87SYuri Pankov 	ROFF_trimat,
260*260e9a87SYuri Pankov 	ROFF_trin,
261*260e9a87SYuri Pankov 	ROFF_trnt,
262*260e9a87SYuri Pankov 	ROFF_troff,
263*260e9a87SYuri Pankov 	ROFF_TS,
264*260e9a87SYuri Pankov 	ROFF_uf,
265*260e9a87SYuri Pankov 	ROFF_ul,
266*260e9a87SYuri Pankov 	ROFF_unformat,
267*260e9a87SYuri Pankov 	ROFF_unwatch,
268*260e9a87SYuri Pankov 	ROFF_unwatchn,
269*260e9a87SYuri Pankov 	ROFF_vpt,
270*260e9a87SYuri Pankov 	ROFF_vs,
271*260e9a87SYuri Pankov 	ROFF_warn,
272*260e9a87SYuri Pankov 	ROFF_warnscale,
273*260e9a87SYuri Pankov 	ROFF_watch,
274*260e9a87SYuri Pankov 	ROFF_watchlength,
275*260e9a87SYuri Pankov 	ROFF_watchn,
276*260e9a87SYuri Pankov 	ROFF_wh,
277*260e9a87SYuri Pankov 	ROFF_while,
278*260e9a87SYuri Pankov 	ROFF_write,
279*260e9a87SYuri Pankov 	ROFF_writec,
280*260e9a87SYuri Pankov 	ROFF_writem,
281*260e9a87SYuri Pankov 	ROFF_xflag,
28295c635efSGarrett D'Amore 	ROFF_cblock,
28395c635efSGarrett D'Amore 	ROFF_USERDEF,
28495c635efSGarrett D'Amore 	ROFF_MAX
28595c635efSGarrett D'Amore };
28695c635efSGarrett D'Amore 
28795c635efSGarrett D'Amore /*
28895c635efSGarrett D'Amore  * An incredibly-simple string buffer.
28995c635efSGarrett D'Amore  */
29095c635efSGarrett D'Amore struct	roffstr {
29195c635efSGarrett D'Amore 	char		*p; /* nil-terminated buffer */
29295c635efSGarrett D'Amore 	size_t		 sz; /* saved strlen(p) */
29395c635efSGarrett D'Amore };
29495c635efSGarrett D'Amore 
29595c635efSGarrett D'Amore /*
29695c635efSGarrett D'Amore  * A key-value roffstr pair as part of a singly-linked list.
29795c635efSGarrett D'Amore  */
29895c635efSGarrett D'Amore struct	roffkv {
29995c635efSGarrett D'Amore 	struct roffstr	 key;
30095c635efSGarrett D'Amore 	struct roffstr	 val;
30195c635efSGarrett D'Amore 	struct roffkv	*next; /* next in list */
30295c635efSGarrett D'Amore };
30395c635efSGarrett D'Amore 
304698f87a4SGarrett D'Amore /*
305698f87a4SGarrett D'Amore  * A single number register as part of a singly-linked list.
306698f87a4SGarrett D'Amore  */
307698f87a4SGarrett D'Amore struct	roffreg {
308698f87a4SGarrett D'Amore 	struct roffstr	 key;
309698f87a4SGarrett D'Amore 	int		 val;
310698f87a4SGarrett D'Amore 	struct roffreg	*next;
311698f87a4SGarrett D'Amore };
312698f87a4SGarrett D'Amore 
31395c635efSGarrett D'Amore struct	roff {
31495c635efSGarrett D'Amore 	struct mparse	*parse; /* parse point */
315*260e9a87SYuri Pankov 	const struct mchars *mchars; /* character table */
31695c635efSGarrett D'Amore 	struct roffnode	*last; /* leaf of stack */
317*260e9a87SYuri Pankov 	int		*rstack; /* stack of inverted `ie' values */
318698f87a4SGarrett D'Amore 	struct roffreg	*regtab; /* number registers */
31995c635efSGarrett D'Amore 	struct roffkv	*strtab; /* user-defined strings & macros */
32095c635efSGarrett D'Amore 	struct roffkv	*xmbtab; /* multi-byte trans table (`tr') */
32195c635efSGarrett D'Amore 	struct roffstr	*xtab; /* single-byte trans table (`tr') */
32295c635efSGarrett D'Amore 	const char	*current_string; /* value of last called user macro */
32395c635efSGarrett D'Amore 	struct tbl_node	*first_tbl; /* first table parsed */
32495c635efSGarrett D'Amore 	struct tbl_node	*last_tbl; /* last table parsed */
32595c635efSGarrett D'Amore 	struct tbl_node	*tbl; /* current table being parsed */
32695c635efSGarrett D'Amore 	struct eqn_node	*last_eqn; /* last equation parsed */
32795c635efSGarrett D'Amore 	struct eqn_node	*first_eqn; /* first equation parsed */
32895c635efSGarrett D'Amore 	struct eqn_node	*eqn; /* current equation being parsed */
329*260e9a87SYuri Pankov 	int		 eqn_inline; /* current equation is inline */
330*260e9a87SYuri Pankov 	int		 options; /* parse options */
331*260e9a87SYuri Pankov 	int		 rstacksz; /* current size limit of rstack */
332*260e9a87SYuri Pankov 	int		 rstackpos; /* position in rstack */
333*260e9a87SYuri Pankov 	int		 format; /* current file in mdoc or man format */
334*260e9a87SYuri Pankov 	char		 control; /* control character */
33595c635efSGarrett D'Amore };
33695c635efSGarrett D'Amore 
33795c635efSGarrett D'Amore struct	roffnode {
33895c635efSGarrett D'Amore 	enum rofft	 tok; /* type of node */
33995c635efSGarrett D'Amore 	struct roffnode	*parent; /* up one in stack */
34095c635efSGarrett D'Amore 	int		 line; /* parse line */
34195c635efSGarrett D'Amore 	int		 col; /* parse col */
34295c635efSGarrett D'Amore 	char		*name; /* node name, e.g. macro name */
34395c635efSGarrett D'Amore 	char		*end; /* end-rules: custom token */
34495c635efSGarrett D'Amore 	int		 endspan; /* end-rules: next-line or infty */
345*260e9a87SYuri Pankov 	int		 rule; /* current evaluation rule */
34695c635efSGarrett D'Amore };
34795c635efSGarrett D'Amore 
34895c635efSGarrett D'Amore #define	ROFF_ARGS	 struct roff *r, /* parse ctx */ \
34995c635efSGarrett D'Amore 			 enum rofft tok, /* tok of macro */ \
350*260e9a87SYuri Pankov 			 struct buf *buf, /* input buffer */ \
35195c635efSGarrett D'Amore 			 int ln, /* parse line */ \
35295c635efSGarrett D'Amore 			 int ppos, /* original pos in buffer */ \
35395c635efSGarrett D'Amore 			 int pos, /* current pos in buffer */ \
35495c635efSGarrett D'Amore 			 int *offs /* reset offset of buffer data */
35595c635efSGarrett D'Amore 
35695c635efSGarrett D'Amore typedef	enum rofferr (*roffproc)(ROFF_ARGS);
35795c635efSGarrett D'Amore 
35895c635efSGarrett D'Amore struct	roffmac {
35995c635efSGarrett D'Amore 	const char	*name; /* macro name */
36095c635efSGarrett D'Amore 	roffproc	 proc; /* process new macro */
36195c635efSGarrett D'Amore 	roffproc	 text; /* process as child text of macro */
36295c635efSGarrett D'Amore 	roffproc	 sub; /* process as child of macro */
36395c635efSGarrett D'Amore 	int		 flags;
36495c635efSGarrett D'Amore #define	ROFFMAC_STRUCT	(1 << 0) /* always interpret */
36595c635efSGarrett D'Amore 	struct roffmac	*next;
36695c635efSGarrett D'Amore };
36795c635efSGarrett D'Amore 
36895c635efSGarrett D'Amore struct	predef {
36995c635efSGarrett D'Amore 	const char	*name; /* predefined input name */
37095c635efSGarrett D'Amore 	const char	*str; /* replacement symbol */
37195c635efSGarrett D'Amore };
37295c635efSGarrett D'Amore 
37395c635efSGarrett D'Amore #define	PREDEF(__name, __str) \
37495c635efSGarrett D'Amore 	{ (__name), (__str) },
37595c635efSGarrett D'Amore 
37695c635efSGarrett D'Amore static	enum rofft	 roffhash_find(const char *, size_t);
37795c635efSGarrett D'Amore static	void		 roffhash_init(void);
37895c635efSGarrett D'Amore static	void		 roffnode_cleanscope(struct roff *);
37995c635efSGarrett D'Amore static	void		 roffnode_pop(struct roff *);
38095c635efSGarrett D'Amore static	void		 roffnode_push(struct roff *, enum rofft,
38195c635efSGarrett D'Amore 				const char *, int, int);
38295c635efSGarrett D'Amore static	enum rofferr	 roff_block(ROFF_ARGS);
38395c635efSGarrett D'Amore static	enum rofferr	 roff_block_text(ROFF_ARGS);
38495c635efSGarrett D'Amore static	enum rofferr	 roff_block_sub(ROFF_ARGS);
385*260e9a87SYuri Pankov static	enum rofferr	 roff_brp(ROFF_ARGS);
38695c635efSGarrett D'Amore static	enum rofferr	 roff_cblock(ROFF_ARGS);
387698f87a4SGarrett D'Amore static	enum rofferr	 roff_cc(ROFF_ARGS);
388*260e9a87SYuri Pankov static	void		 roff_ccond(struct roff *, int, int);
38995c635efSGarrett D'Amore static	enum rofferr	 roff_cond(ROFF_ARGS);
39095c635efSGarrett D'Amore static	enum rofferr	 roff_cond_text(ROFF_ARGS);
39195c635efSGarrett D'Amore static	enum rofferr	 roff_cond_sub(ROFF_ARGS);
39295c635efSGarrett D'Amore static	enum rofferr	 roff_ds(ROFF_ARGS);
393*260e9a87SYuri Pankov static	enum rofferr	 roff_eqndelim(struct roff *, struct buf *, int);
394*260e9a87SYuri Pankov static	int		 roff_evalcond(struct roff *r, int,
395*260e9a87SYuri Pankov 				const char *, int *);
396*260e9a87SYuri Pankov static	int		 roff_evalnum(struct roff *, int,
397*260e9a87SYuri Pankov 				const char *, int *, int *, int);
398*260e9a87SYuri Pankov static	int		 roff_evalpar(struct roff *, int,
399*260e9a87SYuri Pankov 				const char *, int *, int *, int);
400*260e9a87SYuri Pankov static	int		 roff_evalstrcond(const char *, int *);
40195c635efSGarrett D'Amore static	void		 roff_free1(struct roff *);
402698f87a4SGarrett D'Amore static	void		 roff_freereg(struct roffreg *);
40395c635efSGarrett D'Amore static	void		 roff_freestr(struct roffkv *);
404*260e9a87SYuri Pankov static	size_t		 roff_getname(struct roff *, char **, int, int);
405*260e9a87SYuri Pankov static	int		 roff_getnum(const char *, int *, int *, int);
406698f87a4SGarrett D'Amore static	int		 roff_getop(const char *, int *, char *);
407698f87a4SGarrett D'Amore static	int		 roff_getregn(const struct roff *,
408698f87a4SGarrett D'Amore 				const char *, size_t);
409*260e9a87SYuri Pankov static	int		 roff_getregro(const char *name);
41095c635efSGarrett D'Amore static	const char	*roff_getstrn(const struct roff *,
41195c635efSGarrett D'Amore 				const char *, size_t);
412*260e9a87SYuri Pankov static	enum rofferr	 roff_insec(ROFF_ARGS);
413698f87a4SGarrett D'Amore static	enum rofferr	 roff_it(ROFF_ARGS);
41495c635efSGarrett D'Amore static	enum rofferr	 roff_line_ignore(ROFF_ARGS);
41595c635efSGarrett D'Amore static	enum rofferr	 roff_nr(ROFF_ARGS);
416*260e9a87SYuri Pankov static	enum rofft	 roff_parse(struct roff *, char *, int *,
417*260e9a87SYuri Pankov 				int, int);
418*260e9a87SYuri Pankov static	enum rofferr	 roff_parsetext(struct buf *, int, int *);
419*260e9a87SYuri Pankov static	enum rofferr	 roff_res(struct roff *, struct buf *, int, int);
42095c635efSGarrett D'Amore static	enum rofferr	 roff_rm(ROFF_ARGS);
421*260e9a87SYuri Pankov static	enum rofferr	 roff_rr(ROFF_ARGS);
42295c635efSGarrett D'Amore static	void		 roff_setstr(struct roff *,
42395c635efSGarrett D'Amore 				const char *, const char *, int);
42495c635efSGarrett D'Amore static	void		 roff_setstrn(struct roffkv **, const char *,
42595c635efSGarrett D'Amore 				size_t, const char *, size_t, int);
42695c635efSGarrett D'Amore static	enum rofferr	 roff_so(ROFF_ARGS);
42795c635efSGarrett D'Amore static	enum rofferr	 roff_tr(ROFF_ARGS);
428698f87a4SGarrett D'Amore static	enum rofferr	 roff_Dd(ROFF_ARGS);
429698f87a4SGarrett D'Amore static	enum rofferr	 roff_TH(ROFF_ARGS);
43095c635efSGarrett D'Amore static	enum rofferr	 roff_TE(ROFF_ARGS);
43195c635efSGarrett D'Amore static	enum rofferr	 roff_TS(ROFF_ARGS);
43295c635efSGarrett D'Amore static	enum rofferr	 roff_EQ(ROFF_ARGS);
43395c635efSGarrett D'Amore static	enum rofferr	 roff_EN(ROFF_ARGS);
43495c635efSGarrett D'Amore static	enum rofferr	 roff_T_(ROFF_ARGS);
435*260e9a87SYuri Pankov static	enum rofferr	 roff_unsupp(ROFF_ARGS);
43695c635efSGarrett D'Amore static	enum rofferr	 roff_userdef(ROFF_ARGS);
43795c635efSGarrett D'Amore 
43895c635efSGarrett D'Amore /* See roffhash_find() */
43995c635efSGarrett D'Amore 
44095c635efSGarrett D'Amore #define	ASCII_HI	 126
44195c635efSGarrett D'Amore #define	ASCII_LO	 33
44295c635efSGarrett D'Amore #define	HASHWIDTH	(ASCII_HI - ASCII_LO + 1)
44395c635efSGarrett D'Amore 
444*260e9a87SYuri Pankov #define	ROFFNUM_SCALE	(1 << 0)  /* Honour scaling in roff_getnum(). */
445*260e9a87SYuri Pankov #define	ROFFNUM_WHITE	(1 << 1)  /* Skip whitespace in roff_evalnum(). */
446*260e9a87SYuri Pankov 
44795c635efSGarrett D'Amore static	struct roffmac	*hash[HASHWIDTH];
44895c635efSGarrett D'Amore 
44995c635efSGarrett D'Amore static	struct roffmac	 roffs[ROFF_MAX] = {
450*260e9a87SYuri Pankov 	{ "ab", roff_unsupp, NULL, NULL, 0, NULL },
45195c635efSGarrett D'Amore 	{ "ad", roff_line_ignore, NULL, NULL, 0, NULL },
452*260e9a87SYuri Pankov 	{ "af", roff_line_ignore, NULL, NULL, 0, NULL },
453*260e9a87SYuri Pankov 	{ "aln", roff_unsupp, NULL, NULL, 0, NULL },
454*260e9a87SYuri Pankov 	{ "als", roff_unsupp, NULL, NULL, 0, NULL },
45595c635efSGarrett D'Amore 	{ "am", roff_block, roff_block_text, roff_block_sub, 0, NULL },
45695c635efSGarrett D'Amore 	{ "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
457*260e9a87SYuri Pankov 	{ "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL },
458*260e9a87SYuri Pankov 	{ "ami1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
459*260e9a87SYuri Pankov 	{ "as", roff_ds, NULL, NULL, 0, NULL },
460*260e9a87SYuri Pankov 	{ "as1", roff_ds, NULL, NULL, 0, NULL },
461*260e9a87SYuri Pankov 	{ "asciify", roff_unsupp, NULL, NULL, 0, NULL },
462*260e9a87SYuri Pankov 	{ "backtrace", roff_line_ignore, NULL, NULL, 0, NULL },
463*260e9a87SYuri Pankov 	{ "bd", roff_line_ignore, NULL, NULL, 0, NULL },
464*260e9a87SYuri Pankov 	{ "bleedat", roff_line_ignore, NULL, NULL, 0, NULL },
465*260e9a87SYuri Pankov 	{ "blm", roff_unsupp, NULL, NULL, 0, NULL },
466*260e9a87SYuri Pankov 	{ "box", roff_unsupp, NULL, NULL, 0, NULL },
467*260e9a87SYuri Pankov 	{ "boxa", roff_unsupp, NULL, NULL, 0, NULL },
468*260e9a87SYuri Pankov 	{ "bp", roff_line_ignore, NULL, NULL, 0, NULL },
469*260e9a87SYuri Pankov 	{ "BP", roff_unsupp, NULL, NULL, 0, NULL },
470*260e9a87SYuri Pankov 	{ "break", roff_unsupp, NULL, NULL, 0, NULL },
471*260e9a87SYuri Pankov 	{ "breakchar", roff_line_ignore, NULL, NULL, 0, NULL },
472*260e9a87SYuri Pankov 	{ "brnl", roff_line_ignore, NULL, NULL, 0, NULL },
473*260e9a87SYuri Pankov 	{ "brp", roff_brp, NULL, NULL, 0, NULL },
474*260e9a87SYuri Pankov 	{ "brpnl", roff_line_ignore, NULL, NULL, 0, NULL },
475*260e9a87SYuri Pankov 	{ "c2", roff_unsupp, NULL, NULL, 0, NULL },
476698f87a4SGarrett D'Amore 	{ "cc", roff_cc, NULL, NULL, 0, NULL },
477*260e9a87SYuri Pankov 	{ "ce", roff_line_ignore, NULL, NULL, 0, NULL },
478*260e9a87SYuri Pankov 	{ "cf", roff_insec, NULL, NULL, 0, NULL },
479*260e9a87SYuri Pankov 	{ "cflags", roff_line_ignore, NULL, NULL, 0, NULL },
480*260e9a87SYuri Pankov 	{ "ch", roff_line_ignore, NULL, NULL, 0, NULL },
481*260e9a87SYuri Pankov 	{ "char", roff_unsupp, NULL, NULL, 0, NULL },
482*260e9a87SYuri Pankov 	{ "chop", roff_unsupp, NULL, NULL, 0, NULL },
483*260e9a87SYuri Pankov 	{ "class", roff_line_ignore, NULL, NULL, 0, NULL },
484*260e9a87SYuri Pankov 	{ "close", roff_insec, NULL, NULL, 0, NULL },
485*260e9a87SYuri Pankov 	{ "CL", roff_unsupp, NULL, NULL, 0, NULL },
486*260e9a87SYuri Pankov 	{ "color", roff_line_ignore, NULL, NULL, 0, NULL },
487*260e9a87SYuri Pankov 	{ "composite", roff_unsupp, NULL, NULL, 0, NULL },
488*260e9a87SYuri Pankov 	{ "continue", roff_unsupp, NULL, NULL, 0, NULL },
489*260e9a87SYuri Pankov 	{ "cp", roff_line_ignore, NULL, NULL, 0, NULL },
490*260e9a87SYuri Pankov 	{ "cropat", roff_line_ignore, NULL, NULL, 0, NULL },
491*260e9a87SYuri Pankov 	{ "cs", roff_line_ignore, NULL, NULL, 0, NULL },
492*260e9a87SYuri Pankov 	{ "cu", roff_line_ignore, NULL, NULL, 0, NULL },
493*260e9a87SYuri Pankov 	{ "da", roff_unsupp, NULL, NULL, 0, NULL },
494*260e9a87SYuri Pankov 	{ "dch", roff_unsupp, NULL, NULL, 0, NULL },
495*260e9a87SYuri Pankov 	{ "Dd", roff_Dd, NULL, NULL, 0, NULL },
49695c635efSGarrett D'Amore 	{ "de", roff_block, roff_block_text, roff_block_sub, 0, NULL },
49795c635efSGarrett D'Amore 	{ "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
498*260e9a87SYuri Pankov 	{ "defcolor", roff_line_ignore, NULL, NULL, 0, NULL },
499*260e9a87SYuri Pankov 	{ "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL },
500*260e9a87SYuri Pankov 	{ "dei1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
501*260e9a87SYuri Pankov 	{ "device", roff_unsupp, NULL, NULL, 0, NULL },
502*260e9a87SYuri Pankov 	{ "devicem", roff_unsupp, NULL, NULL, 0, NULL },
503*260e9a87SYuri Pankov 	{ "di", roff_unsupp, NULL, NULL, 0, NULL },
504*260e9a87SYuri Pankov 	{ "do", roff_unsupp, NULL, NULL, 0, NULL },
50595c635efSGarrett D'Amore 	{ "ds", roff_ds, NULL, NULL, 0, NULL },
506*260e9a87SYuri Pankov 	{ "ds1", roff_ds, NULL, NULL, 0, NULL },
507*260e9a87SYuri Pankov 	{ "dwh", roff_unsupp, NULL, NULL, 0, NULL },
508*260e9a87SYuri Pankov 	{ "dt", roff_unsupp, NULL, NULL, 0, NULL },
509*260e9a87SYuri Pankov 	{ "ec", roff_unsupp, NULL, NULL, 0, NULL },
510*260e9a87SYuri Pankov 	{ "ecr", roff_unsupp, NULL, NULL, 0, NULL },
511*260e9a87SYuri Pankov 	{ "ecs", roff_unsupp, NULL, NULL, 0, NULL },
51295c635efSGarrett D'Amore 	{ "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
513*260e9a87SYuri Pankov 	{ "em", roff_unsupp, NULL, NULL, 0, NULL },
514*260e9a87SYuri Pankov 	{ "EN", roff_EN, NULL, NULL, 0, NULL },
515*260e9a87SYuri Pankov 	{ "eo", roff_unsupp, NULL, NULL, 0, NULL },
516*260e9a87SYuri Pankov 	{ "EP", roff_unsupp, NULL, NULL, 0, NULL },
517*260e9a87SYuri Pankov 	{ "EQ", roff_EQ, NULL, NULL, 0, NULL },
518*260e9a87SYuri Pankov 	{ "errprint", roff_line_ignore, NULL, NULL, 0, NULL },
519*260e9a87SYuri Pankov 	{ "ev", roff_unsupp, NULL, NULL, 0, NULL },
520*260e9a87SYuri Pankov 	{ "evc", roff_unsupp, NULL, NULL, 0, NULL },
521*260e9a87SYuri Pankov 	{ "ex", roff_unsupp, NULL, NULL, 0, NULL },
522*260e9a87SYuri Pankov 	{ "fallback", roff_line_ignore, NULL, NULL, 0, NULL },
523698f87a4SGarrett D'Amore 	{ "fam", roff_line_ignore, NULL, NULL, 0, NULL },
524*260e9a87SYuri Pankov 	{ "fc", roff_unsupp, NULL, NULL, 0, NULL },
525*260e9a87SYuri Pankov 	{ "fchar", roff_unsupp, NULL, NULL, 0, NULL },
526*260e9a87SYuri Pankov 	{ "fcolor", roff_line_ignore, NULL, NULL, 0, NULL },
527*260e9a87SYuri Pankov 	{ "fdeferlig", roff_line_ignore, NULL, NULL, 0, NULL },
528*260e9a87SYuri Pankov 	{ "feature", roff_line_ignore, NULL, NULL, 0, NULL },
529*260e9a87SYuri Pankov 	{ "fkern", roff_line_ignore, NULL, NULL, 0, NULL },
530*260e9a87SYuri Pankov 	{ "fl", roff_line_ignore, NULL, NULL, 0, NULL },
531*260e9a87SYuri Pankov 	{ "flig", roff_line_ignore, NULL, NULL, 0, NULL },
532*260e9a87SYuri Pankov 	{ "fp", roff_line_ignore, NULL, NULL, 0, NULL },
533*260e9a87SYuri Pankov 	{ "fps", roff_line_ignore, NULL, NULL, 0, NULL },
534*260e9a87SYuri Pankov 	{ "fschar", roff_unsupp, NULL, NULL, 0, NULL },
535*260e9a87SYuri Pankov 	{ "fspacewidth", roff_line_ignore, NULL, NULL, 0, NULL },
536*260e9a87SYuri Pankov 	{ "fspecial", roff_line_ignore, NULL, NULL, 0, NULL },
537*260e9a87SYuri Pankov 	{ "ftr", roff_line_ignore, NULL, NULL, 0, NULL },
538*260e9a87SYuri Pankov 	{ "fzoom", roff_line_ignore, NULL, NULL, 0, NULL },
539*260e9a87SYuri Pankov 	{ "gcolor", roff_line_ignore, NULL, NULL, 0, NULL },
540*260e9a87SYuri Pankov 	{ "hc", roff_line_ignore, NULL, NULL, 0, NULL },
541*260e9a87SYuri Pankov 	{ "hcode", roff_line_ignore, NULL, NULL, 0, NULL },
542*260e9a87SYuri Pankov 	{ "hidechar", roff_line_ignore, NULL, NULL, 0, NULL },
543*260e9a87SYuri Pankov 	{ "hla", roff_line_ignore, NULL, NULL, 0, NULL },
544*260e9a87SYuri Pankov 	{ "hlm", roff_line_ignore, NULL, NULL, 0, NULL },
545*260e9a87SYuri Pankov 	{ "hpf", roff_line_ignore, NULL, NULL, 0, NULL },
546*260e9a87SYuri Pankov 	{ "hpfa", roff_line_ignore, NULL, NULL, 0, NULL },
547*260e9a87SYuri Pankov 	{ "hpfcode", roff_line_ignore, NULL, NULL, 0, NULL },
548698f87a4SGarrett D'Amore 	{ "hw", roff_line_ignore, NULL, NULL, 0, NULL },
54995c635efSGarrett D'Amore 	{ "hy", roff_line_ignore, NULL, NULL, 0, NULL },
550*260e9a87SYuri Pankov 	{ "hylang", roff_line_ignore, NULL, NULL, 0, NULL },
551*260e9a87SYuri Pankov 	{ "hylen", roff_line_ignore, NULL, NULL, 0, NULL },
552*260e9a87SYuri Pankov 	{ "hym", roff_line_ignore, NULL, NULL, 0, NULL },
553*260e9a87SYuri Pankov 	{ "hypp", roff_line_ignore, NULL, NULL, 0, NULL },
554*260e9a87SYuri Pankov 	{ "hys", roff_line_ignore, NULL, NULL, 0, NULL },
55595c635efSGarrett D'Amore 	{ "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
55695c635efSGarrett D'Amore 	{ "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
55795c635efSGarrett D'Amore 	{ "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL },
558*260e9a87SYuri Pankov 	{ "index", roff_unsupp, NULL, NULL, 0, NULL },
559698f87a4SGarrett D'Amore 	{ "it", roff_it, NULL, NULL, 0, NULL },
560*260e9a87SYuri Pankov 	{ "itc", roff_unsupp, NULL, NULL, 0, NULL },
561*260e9a87SYuri Pankov 	{ "IX", roff_line_ignore, NULL, NULL, 0, NULL },
562*260e9a87SYuri Pankov 	{ "kern", roff_line_ignore, NULL, NULL, 0, NULL },
563*260e9a87SYuri Pankov 	{ "kernafter", roff_line_ignore, NULL, NULL, 0, NULL },
564*260e9a87SYuri Pankov 	{ "kernbefore", roff_line_ignore, NULL, NULL, 0, NULL },
565*260e9a87SYuri Pankov 	{ "kernpair", roff_line_ignore, NULL, NULL, 0, NULL },
566*260e9a87SYuri Pankov 	{ "lc", roff_unsupp, NULL, NULL, 0, NULL },
567*260e9a87SYuri Pankov 	{ "lc_ctype", roff_unsupp, NULL, NULL, 0, NULL },
568*260e9a87SYuri Pankov 	{ "lds", roff_unsupp, NULL, NULL, 0, NULL },
569*260e9a87SYuri Pankov 	{ "length", roff_unsupp, NULL, NULL, 0, NULL },
570*260e9a87SYuri Pankov 	{ "letadj", roff_line_ignore, NULL, NULL, 0, NULL },
571*260e9a87SYuri Pankov 	{ "lf", roff_insec, NULL, NULL, 0, NULL },
572*260e9a87SYuri Pankov 	{ "lg", roff_line_ignore, NULL, NULL, 0, NULL },
573*260e9a87SYuri Pankov 	{ "lhang", roff_line_ignore, NULL, NULL, 0, NULL },
574*260e9a87SYuri Pankov 	{ "linetabs", roff_unsupp, NULL, NULL, 0, NULL },
575*260e9a87SYuri Pankov 	{ "lnr", roff_unsupp, NULL, NULL, 0, NULL },
576*260e9a87SYuri Pankov 	{ "lnrf", roff_unsupp, NULL, NULL, 0, NULL },
577*260e9a87SYuri Pankov 	{ "lpfx", roff_unsupp, NULL, NULL, 0, NULL },
578*260e9a87SYuri Pankov 	{ "ls", roff_line_ignore, NULL, NULL, 0, NULL },
579*260e9a87SYuri Pankov 	{ "lsm", roff_unsupp, NULL, NULL, 0, NULL },
580*260e9a87SYuri Pankov 	{ "lt", roff_line_ignore, NULL, NULL, 0, NULL },
581*260e9a87SYuri Pankov 	{ "mc", roff_line_ignore, NULL, NULL, 0, NULL },
582*260e9a87SYuri Pankov 	{ "mediasize", roff_line_ignore, NULL, NULL, 0, NULL },
583*260e9a87SYuri Pankov 	{ "minss", roff_line_ignore, NULL, NULL, 0, NULL },
584*260e9a87SYuri Pankov 	{ "mk", roff_line_ignore, NULL, NULL, 0, NULL },
585*260e9a87SYuri Pankov 	{ "mso", roff_insec, NULL, NULL, 0, NULL },
586*260e9a87SYuri Pankov 	{ "na", roff_line_ignore, NULL, NULL, 0, NULL },
58795c635efSGarrett D'Amore 	{ "ne", roff_line_ignore, NULL, NULL, 0, NULL },
58895c635efSGarrett D'Amore 	{ "nh", roff_line_ignore, NULL, NULL, 0, NULL },
589*260e9a87SYuri Pankov 	{ "nhychar", roff_line_ignore, NULL, NULL, 0, NULL },
590*260e9a87SYuri Pankov 	{ "nm", roff_unsupp, NULL, NULL, 0, NULL },
591*260e9a87SYuri Pankov 	{ "nn", roff_unsupp, NULL, NULL, 0, NULL },
592*260e9a87SYuri Pankov 	{ "nop", roff_unsupp, NULL, NULL, 0, NULL },
59395c635efSGarrett D'Amore 	{ "nr", roff_nr, NULL, NULL, 0, NULL },
594*260e9a87SYuri Pankov 	{ "nrf", roff_unsupp, NULL, NULL, 0, NULL },
595*260e9a87SYuri Pankov 	{ "nroff", roff_line_ignore, NULL, NULL, 0, NULL },
59695c635efSGarrett D'Amore 	{ "ns", roff_line_ignore, NULL, NULL, 0, NULL },
597*260e9a87SYuri Pankov 	{ "nx", roff_insec, NULL, NULL, 0, NULL },
598*260e9a87SYuri Pankov 	{ "open", roff_insec, NULL, NULL, 0, NULL },
599*260e9a87SYuri Pankov 	{ "opena", roff_insec, NULL, NULL, 0, NULL },
600*260e9a87SYuri Pankov 	{ "os", roff_line_ignore, NULL, NULL, 0, NULL },
601*260e9a87SYuri Pankov 	{ "output", roff_unsupp, NULL, NULL, 0, NULL },
602*260e9a87SYuri Pankov 	{ "padj", roff_line_ignore, NULL, NULL, 0, NULL },
603*260e9a87SYuri Pankov 	{ "papersize", roff_line_ignore, NULL, NULL, 0, NULL },
604*260e9a87SYuri Pankov 	{ "pc", roff_line_ignore, NULL, NULL, 0, NULL },
605*260e9a87SYuri Pankov 	{ "pev", roff_line_ignore, NULL, NULL, 0, NULL },
606*260e9a87SYuri Pankov 	{ "pi", roff_insec, NULL, NULL, 0, NULL },
607*260e9a87SYuri Pankov 	{ "PI", roff_unsupp, NULL, NULL, 0, NULL },
608*260e9a87SYuri Pankov 	{ "pl", roff_line_ignore, NULL, NULL, 0, NULL },
609*260e9a87SYuri Pankov 	{ "pm", roff_line_ignore, NULL, NULL, 0, NULL },
610*260e9a87SYuri Pankov 	{ "pn", roff_line_ignore, NULL, NULL, 0, NULL },
611*260e9a87SYuri Pankov 	{ "pnr", roff_line_ignore, NULL, NULL, 0, NULL },
612*260e9a87SYuri Pankov 	{ "po", roff_line_ignore, NULL, NULL, 0, NULL },
61395c635efSGarrett D'Amore 	{ "ps", roff_line_ignore, NULL, NULL, 0, NULL },
614*260e9a87SYuri Pankov 	{ "psbb", roff_unsupp, NULL, NULL, 0, NULL },
615*260e9a87SYuri Pankov 	{ "pshape", roff_unsupp, NULL, NULL, 0, NULL },
616*260e9a87SYuri Pankov 	{ "pso", roff_insec, NULL, NULL, 0, NULL },
617*260e9a87SYuri Pankov 	{ "ptr", roff_line_ignore, NULL, NULL, 0, NULL },
618*260e9a87SYuri Pankov 	{ "pvs", roff_line_ignore, NULL, NULL, 0, NULL },
619*260e9a87SYuri Pankov 	{ "rchar", roff_unsupp, NULL, NULL, 0, NULL },
620*260e9a87SYuri Pankov 	{ "rd", roff_line_ignore, NULL, NULL, 0, NULL },
621*260e9a87SYuri Pankov 	{ "recursionlimit", roff_line_ignore, NULL, NULL, 0, NULL },
622*260e9a87SYuri Pankov 	{ "return", roff_unsupp, NULL, NULL, 0, NULL },
623*260e9a87SYuri Pankov 	{ "rfschar", roff_unsupp, NULL, NULL, 0, NULL },
624*260e9a87SYuri Pankov 	{ "rhang", roff_line_ignore, NULL, NULL, 0, NULL },
625*260e9a87SYuri Pankov 	{ "rj", roff_line_ignore, NULL, NULL, 0, NULL },
62695c635efSGarrett D'Amore 	{ "rm", roff_rm, NULL, NULL, 0, NULL },
627*260e9a87SYuri Pankov 	{ "rn", roff_unsupp, NULL, NULL, 0, NULL },
628*260e9a87SYuri Pankov 	{ "rnn", roff_unsupp, NULL, NULL, 0, NULL },
629*260e9a87SYuri Pankov 	{ "rr", roff_rr, NULL, NULL, 0, NULL },
630*260e9a87SYuri Pankov 	{ "rs", roff_line_ignore, NULL, NULL, 0, NULL },
631*260e9a87SYuri Pankov 	{ "rt", roff_line_ignore, NULL, NULL, 0, NULL },
632*260e9a87SYuri Pankov 	{ "schar", roff_unsupp, NULL, NULL, 0, NULL },
633*260e9a87SYuri Pankov 	{ "sentchar", roff_line_ignore, NULL, NULL, 0, NULL },
634*260e9a87SYuri Pankov 	{ "shc", roff_line_ignore, NULL, NULL, 0, NULL },
635*260e9a87SYuri Pankov 	{ "shift", roff_unsupp, NULL, NULL, 0, NULL },
636*260e9a87SYuri Pankov 	{ "sizes", roff_line_ignore, NULL, NULL, 0, NULL },
63795c635efSGarrett D'Amore 	{ "so", roff_so, NULL, NULL, 0, NULL },
638*260e9a87SYuri Pankov 	{ "spacewidth", roff_line_ignore, NULL, NULL, 0, NULL },
639*260e9a87SYuri Pankov 	{ "special", roff_line_ignore, NULL, NULL, 0, NULL },
640*260e9a87SYuri Pankov 	{ "spreadwarn", roff_line_ignore, NULL, NULL, 0, NULL },
641*260e9a87SYuri Pankov 	{ "ss", roff_line_ignore, NULL, NULL, 0, NULL },
642*260e9a87SYuri Pankov 	{ "sty", roff_line_ignore, NULL, NULL, 0, NULL },
643*260e9a87SYuri Pankov 	{ "substring", roff_unsupp, NULL, NULL, 0, NULL },
644*260e9a87SYuri Pankov 	{ "sv", roff_line_ignore, NULL, NULL, 0, NULL },
645*260e9a87SYuri Pankov 	{ "sy", roff_insec, NULL, NULL, 0, NULL },
64695c635efSGarrett D'Amore 	{ "T&", roff_T_, NULL, NULL, 0, NULL },
647*260e9a87SYuri Pankov 	{ "ta", roff_unsupp, NULL, NULL, 0, NULL },
648*260e9a87SYuri Pankov 	{ "tc", roff_unsupp, NULL, NULL, 0, NULL },
649*260e9a87SYuri Pankov 	{ "TE", roff_TE, NULL, NULL, 0, NULL },
650*260e9a87SYuri Pankov 	{ "TH", roff_TH, NULL, NULL, 0, NULL },
651*260e9a87SYuri Pankov 	{ "ti", roff_unsupp, NULL, NULL, 0, NULL },
652*260e9a87SYuri Pankov 	{ "tkf", roff_line_ignore, NULL, NULL, 0, NULL },
653*260e9a87SYuri Pankov 	{ "tl", roff_unsupp, NULL, NULL, 0, NULL },
654*260e9a87SYuri Pankov 	{ "tm", roff_line_ignore, NULL, NULL, 0, NULL },
655*260e9a87SYuri Pankov 	{ "tm1", roff_line_ignore, NULL, NULL, 0, NULL },
656*260e9a87SYuri Pankov 	{ "tmc", roff_line_ignore, NULL, NULL, 0, NULL },
657*260e9a87SYuri Pankov 	{ "tr", roff_tr, NULL, NULL, 0, NULL },
658*260e9a87SYuri Pankov 	{ "track", roff_line_ignore, NULL, NULL, 0, NULL },
659*260e9a87SYuri Pankov 	{ "transchar", roff_line_ignore, NULL, NULL, 0, NULL },
660*260e9a87SYuri Pankov 	{ "trf", roff_insec, NULL, NULL, 0, NULL },
661*260e9a87SYuri Pankov 	{ "trimat", roff_line_ignore, NULL, NULL, 0, NULL },
662*260e9a87SYuri Pankov 	{ "trin", roff_unsupp, NULL, NULL, 0, NULL },
663*260e9a87SYuri Pankov 	{ "trnt", roff_unsupp, NULL, NULL, 0, NULL },
664*260e9a87SYuri Pankov 	{ "troff", roff_line_ignore, NULL, NULL, 0, NULL },
665*260e9a87SYuri Pankov 	{ "TS", roff_TS, NULL, NULL, 0, NULL },
666*260e9a87SYuri Pankov 	{ "uf", roff_line_ignore, NULL, NULL, 0, NULL },
667*260e9a87SYuri Pankov 	{ "ul", roff_line_ignore, NULL, NULL, 0, NULL },
668*260e9a87SYuri Pankov 	{ "unformat", roff_unsupp, NULL, NULL, 0, NULL },
669*260e9a87SYuri Pankov 	{ "unwatch", roff_line_ignore, NULL, NULL, 0, NULL },
670*260e9a87SYuri Pankov 	{ "unwatchn", roff_line_ignore, NULL, NULL, 0, NULL },
671*260e9a87SYuri Pankov 	{ "vpt", roff_line_ignore, NULL, NULL, 0, NULL },
672*260e9a87SYuri Pankov 	{ "vs", roff_line_ignore, NULL, NULL, 0, NULL },
673*260e9a87SYuri Pankov 	{ "warn", roff_line_ignore, NULL, NULL, 0, NULL },
674*260e9a87SYuri Pankov 	{ "warnscale", roff_line_ignore, NULL, NULL, 0, NULL },
675*260e9a87SYuri Pankov 	{ "watch", roff_line_ignore, NULL, NULL, 0, NULL },
676*260e9a87SYuri Pankov 	{ "watchlength", roff_line_ignore, NULL, NULL, 0, NULL },
677*260e9a87SYuri Pankov 	{ "watchn", roff_line_ignore, NULL, NULL, 0, NULL },
678*260e9a87SYuri Pankov 	{ "wh", roff_unsupp, NULL, NULL, 0, NULL },
679*260e9a87SYuri Pankov 	{ "while", roff_unsupp, NULL, NULL, 0, NULL },
680*260e9a87SYuri Pankov 	{ "write", roff_insec, NULL, NULL, 0, NULL },
681*260e9a87SYuri Pankov 	{ "writec", roff_insec, NULL, NULL, 0, NULL },
682*260e9a87SYuri Pankov 	{ "writem", roff_insec, NULL, NULL, 0, NULL },
683*260e9a87SYuri Pankov 	{ "xflag", roff_line_ignore, NULL, NULL, 0, NULL },
68495c635efSGarrett D'Amore 	{ ".", roff_cblock, NULL, NULL, 0, NULL },
68595c635efSGarrett D'Amore 	{ NULL, roff_userdef, NULL, NULL, 0, NULL },
68695c635efSGarrett D'Amore };
68795c635efSGarrett D'Amore 
688*260e9a87SYuri Pankov /* not currently implemented: Ds em Eq LP Me PP pp Or Rd Sf SH */
689698f87a4SGarrett D'Amore const	char *const __mdoc_reserved[] = {
690698f87a4SGarrett D'Amore 	"Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At",
691698f87a4SGarrett D'Amore 	"Bc", "Bd", "Bf", "Bk", "Bl", "Bo", "Bq",
692698f87a4SGarrett D'Amore 	"Brc", "Bro", "Brq", "Bsx", "Bt", "Bx",
693698f87a4SGarrett D'Amore 	"Cd", "Cm", "Db", "Dc", "Dd", "Dl", "Do", "Dq",
694*260e9a87SYuri Pankov 	"Dt", "Dv", "Dx", "D1",
695*260e9a87SYuri Pankov 	"Ec", "Ed", "Ef", "Ek", "El", "Em",
696*260e9a87SYuri Pankov 	"En", "Eo", "Er", "Es", "Ev", "Ex",
697698f87a4SGarrett D'Amore 	"Fa", "Fc", "Fd", "Fl", "Fn", "Fo", "Fr", "Ft", "Fx",
698*260e9a87SYuri Pankov 	"Hf", "Ic", "In", "It", "Lb", "Li", "Lk", "Lp",
699*260e9a87SYuri Pankov 	"Ms", "Mt", "Nd", "Nm", "No", "Ns", "Nx",
700698f87a4SGarrett D'Amore 	"Oc", "Oo", "Op", "Os", "Ot", "Ox",
701*260e9a87SYuri Pankov 	"Pa", "Pc", "Pf", "Po", "Pp", "Pq",
702*260e9a87SYuri Pankov 	"Qc", "Ql", "Qo", "Qq", "Re", "Rs", "Rv",
703*260e9a87SYuri Pankov 	"Sc", "Sh", "Sm", "So", "Sq",
704698f87a4SGarrett D'Amore 	"Ss", "St", "Sx", "Sy",
705698f87a4SGarrett D'Amore 	"Ta", "Tn", "Ud", "Ux", "Va", "Vt", "Xc", "Xo", "Xr",
706*260e9a87SYuri Pankov 	"%A", "%B", "%C", "%D", "%I", "%J", "%N", "%O",
707698f87a4SGarrett D'Amore 	"%P", "%Q", "%R", "%T", "%U", "%V",
708698f87a4SGarrett D'Amore 	NULL
709698f87a4SGarrett D'Amore };
710698f87a4SGarrett D'Amore 
711*260e9a87SYuri Pankov /* not currently implemented: BT DE DS ME MT PT SY TQ YS */
712698f87a4SGarrett D'Amore const	char *const __man_reserved[] = {
713*260e9a87SYuri Pankov 	"AT", "B", "BI", "BR", "DT",
714*260e9a87SYuri Pankov 	"EE", "EN", "EQ", "EX", "HP", "I", "IB", "IP", "IR",
715*260e9a87SYuri Pankov 	"LP", "OP", "P", "PD", "PP",
716*260e9a87SYuri Pankov 	"R", "RB", "RE", "RI", "RS", "SB", "SH", "SM", "SS",
717*260e9a87SYuri Pankov 	"TE", "TH", "TP", "TS", "T&", "UC", "UE", "UR",
718698f87a4SGarrett D'Amore 	NULL
719698f87a4SGarrett D'Amore };
720698f87a4SGarrett D'Amore 
72195c635efSGarrett D'Amore /* Array of injected predefined strings. */
72295c635efSGarrett D'Amore #define	PREDEFS_MAX	 38
72395c635efSGarrett D'Amore static	const struct predef predefs[PREDEFS_MAX] = {
72495c635efSGarrett D'Amore #include "predefs.in"
72595c635efSGarrett D'Amore };
72695c635efSGarrett D'Amore 
72795c635efSGarrett D'Amore /* See roffhash_find() */
72895c635efSGarrett D'Amore #define	ROFF_HASH(p)	(p[0] - ASCII_LO)
72995c635efSGarrett D'Amore 
730698f87a4SGarrett D'Amore static	int	 roffit_lines;  /* number of lines to delay */
731698f87a4SGarrett D'Amore static	char	*roffit_macro;  /* nil-terminated macro line */
732698f87a4SGarrett D'Amore 
733*260e9a87SYuri Pankov 
73495c635efSGarrett D'Amore static void
73595c635efSGarrett D'Amore roffhash_init(void)
73695c635efSGarrett D'Amore {
73795c635efSGarrett D'Amore 	struct roffmac	 *n;
73895c635efSGarrett D'Amore 	int		  buc, i;
73995c635efSGarrett D'Amore 
74095c635efSGarrett D'Amore 	for (i = 0; i < (int)ROFF_USERDEF; i++) {
74195c635efSGarrett D'Amore 		assert(roffs[i].name[0] >= ASCII_LO);
74295c635efSGarrett D'Amore 		assert(roffs[i].name[0] <= ASCII_HI);
74395c635efSGarrett D'Amore 
74495c635efSGarrett D'Amore 		buc = ROFF_HASH(roffs[i].name);
74595c635efSGarrett D'Amore 
74695c635efSGarrett D'Amore 		if (NULL != (n = hash[buc])) {
74795c635efSGarrett D'Amore 			for ( ; n->next; n = n->next)
74895c635efSGarrett D'Amore 				/* Do nothing. */ ;
74995c635efSGarrett D'Amore 			n->next = &roffs[i];
75095c635efSGarrett D'Amore 		} else
75195c635efSGarrett D'Amore 			hash[buc] = &roffs[i];
75295c635efSGarrett D'Amore 	}
75395c635efSGarrett D'Amore }
75495c635efSGarrett D'Amore 
75595c635efSGarrett D'Amore /*
75695c635efSGarrett D'Amore  * Look up a roff token by its name.  Returns ROFF_MAX if no macro by
75795c635efSGarrett D'Amore  * the nil-terminated string name could be found.
75895c635efSGarrett D'Amore  */
75995c635efSGarrett D'Amore static enum rofft
76095c635efSGarrett D'Amore roffhash_find(const char *p, size_t s)
76195c635efSGarrett D'Amore {
76295c635efSGarrett D'Amore 	int		 buc;
76395c635efSGarrett D'Amore 	struct roffmac	*n;
76495c635efSGarrett D'Amore 
76595c635efSGarrett D'Amore 	/*
76695c635efSGarrett D'Amore 	 * libroff has an extremely simple hashtable, for the time
76795c635efSGarrett D'Amore 	 * being, which simply keys on the first character, which must
76895c635efSGarrett D'Amore 	 * be printable, then walks a chain.  It works well enough until
76995c635efSGarrett D'Amore 	 * optimised.
77095c635efSGarrett D'Amore 	 */
77195c635efSGarrett D'Amore 
77295c635efSGarrett D'Amore 	if (p[0] < ASCII_LO || p[0] > ASCII_HI)
77395c635efSGarrett D'Amore 		return(ROFF_MAX);
77495c635efSGarrett D'Amore 
77595c635efSGarrett D'Amore 	buc = ROFF_HASH(p);
77695c635efSGarrett D'Amore 
77795c635efSGarrett D'Amore 	if (NULL == (n = hash[buc]))
77895c635efSGarrett D'Amore 		return(ROFF_MAX);
77995c635efSGarrett D'Amore 	for ( ; n; n = n->next)
78095c635efSGarrett D'Amore 		if (0 == strncmp(n->name, p, s) && '\0' == n->name[(int)s])
78195c635efSGarrett D'Amore 			return((enum rofft)(n - roffs));
78295c635efSGarrett D'Amore 
78395c635efSGarrett D'Amore 	return(ROFF_MAX);
78495c635efSGarrett D'Amore }
78595c635efSGarrett D'Amore 
78695c635efSGarrett D'Amore /*
78795c635efSGarrett D'Amore  * Pop the current node off of the stack of roff instructions currently
78895c635efSGarrett D'Amore  * pending.
78995c635efSGarrett D'Amore  */
79095c635efSGarrett D'Amore static void
79195c635efSGarrett D'Amore roffnode_pop(struct roff *r)
79295c635efSGarrett D'Amore {
79395c635efSGarrett D'Amore 	struct roffnode	*p;
79495c635efSGarrett D'Amore 
79595c635efSGarrett D'Amore 	assert(r->last);
79695c635efSGarrett D'Amore 	p = r->last;
79795c635efSGarrett D'Amore 
79895c635efSGarrett D'Amore 	r->last = r->last->parent;
79995c635efSGarrett D'Amore 	free(p->name);
80095c635efSGarrett D'Amore 	free(p->end);
80195c635efSGarrett D'Amore 	free(p);
80295c635efSGarrett D'Amore }
80395c635efSGarrett D'Amore 
80495c635efSGarrett D'Amore /*
80595c635efSGarrett D'Amore  * Push a roff node onto the instruction stack.  This must later be
80695c635efSGarrett D'Amore  * removed with roffnode_pop().
80795c635efSGarrett D'Amore  */
80895c635efSGarrett D'Amore static void
80995c635efSGarrett D'Amore roffnode_push(struct roff *r, enum rofft tok, const char *name,
81095c635efSGarrett D'Amore 		int line, int col)
81195c635efSGarrett D'Amore {
81295c635efSGarrett D'Amore 	struct roffnode	*p;
81395c635efSGarrett D'Amore 
81495c635efSGarrett D'Amore 	p = mandoc_calloc(1, sizeof(struct roffnode));
81595c635efSGarrett D'Amore 	p->tok = tok;
81695c635efSGarrett D'Amore 	if (name)
81795c635efSGarrett D'Amore 		p->name = mandoc_strdup(name);
81895c635efSGarrett D'Amore 	p->parent = r->last;
81995c635efSGarrett D'Amore 	p->line = line;
82095c635efSGarrett D'Amore 	p->col = col;
821*260e9a87SYuri Pankov 	p->rule = p->parent ? p->parent->rule : 0;
82295c635efSGarrett D'Amore 
82395c635efSGarrett D'Amore 	r->last = p;
82495c635efSGarrett D'Amore }
82595c635efSGarrett D'Amore 
82695c635efSGarrett D'Amore static void
82795c635efSGarrett D'Amore roff_free1(struct roff *r)
82895c635efSGarrett D'Amore {
829698f87a4SGarrett D'Amore 	struct tbl_node	*tbl;
83095c635efSGarrett D'Amore 	struct eqn_node	*e;
83195c635efSGarrett D'Amore 	int		 i;
83295c635efSGarrett D'Amore 
833698f87a4SGarrett D'Amore 	while (NULL != (tbl = r->first_tbl)) {
834698f87a4SGarrett D'Amore 		r->first_tbl = tbl->next;
835698f87a4SGarrett D'Amore 		tbl_free(tbl);
83695c635efSGarrett D'Amore 	}
83795c635efSGarrett D'Amore 	r->first_tbl = r->last_tbl = r->tbl = NULL;
83895c635efSGarrett D'Amore 
83995c635efSGarrett D'Amore 	while (NULL != (e = r->first_eqn)) {
84095c635efSGarrett D'Amore 		r->first_eqn = e->next;
84195c635efSGarrett D'Amore 		eqn_free(e);
84295c635efSGarrett D'Amore 	}
84395c635efSGarrett D'Amore 	r->first_eqn = r->last_eqn = r->eqn = NULL;
84495c635efSGarrett D'Amore 
84595c635efSGarrett D'Amore 	while (r->last)
84695c635efSGarrett D'Amore 		roffnode_pop(r);
84795c635efSGarrett D'Amore 
848*260e9a87SYuri Pankov 	free (r->rstack);
849*260e9a87SYuri Pankov 	r->rstack = NULL;
850*260e9a87SYuri Pankov 	r->rstacksz = 0;
851*260e9a87SYuri Pankov 	r->rstackpos = -1;
85295c635efSGarrett D'Amore 
853698f87a4SGarrett D'Amore 	roff_freereg(r->regtab);
854698f87a4SGarrett D'Amore 	r->regtab = NULL;
855698f87a4SGarrett D'Amore 
856*260e9a87SYuri Pankov 	roff_freestr(r->strtab);
857*260e9a87SYuri Pankov 	roff_freestr(r->xmbtab);
858*260e9a87SYuri Pankov 	r->strtab = r->xmbtab = NULL;
859*260e9a87SYuri Pankov 
86095c635efSGarrett D'Amore 	if (r->xtab)
86195c635efSGarrett D'Amore 		for (i = 0; i < 128; i++)
86295c635efSGarrett D'Amore 			free(r->xtab[i].p);
86395c635efSGarrett D'Amore 	free(r->xtab);
86495c635efSGarrett D'Amore 	r->xtab = NULL;
86595c635efSGarrett D'Amore }
86695c635efSGarrett D'Amore 
86795c635efSGarrett D'Amore void
86895c635efSGarrett D'Amore roff_reset(struct roff *r)
86995c635efSGarrett D'Amore {
87095c635efSGarrett D'Amore 
87195c635efSGarrett D'Amore 	roff_free1(r);
872*260e9a87SYuri Pankov 	r->format = r->options & (MPARSE_MDOC | MPARSE_MAN);
873698f87a4SGarrett D'Amore 	r->control = 0;
87495c635efSGarrett D'Amore }
87595c635efSGarrett D'Amore 
87695c635efSGarrett D'Amore void
87795c635efSGarrett D'Amore roff_free(struct roff *r)
87895c635efSGarrett D'Amore {
87995c635efSGarrett D'Amore 
88095c635efSGarrett D'Amore 	roff_free1(r);
88195c635efSGarrett D'Amore 	free(r);
88295c635efSGarrett D'Amore }
88395c635efSGarrett D'Amore 
88495c635efSGarrett D'Amore struct roff *
885*260e9a87SYuri Pankov roff_alloc(struct mparse *parse, const struct mchars *mchars, int options)
88695c635efSGarrett D'Amore {
88795c635efSGarrett D'Amore 	struct roff	*r;
88895c635efSGarrett D'Amore 
88995c635efSGarrett D'Amore 	r = mandoc_calloc(1, sizeof(struct roff));
89095c635efSGarrett D'Amore 	r->parse = parse;
891*260e9a87SYuri Pankov 	r->mchars = mchars;
892*260e9a87SYuri Pankov 	r->options = options;
893*260e9a87SYuri Pankov 	r->format = options & (MPARSE_MDOC | MPARSE_MAN);
89495c635efSGarrett D'Amore 	r->rstackpos = -1;
89595c635efSGarrett D'Amore 
89695c635efSGarrett D'Amore 	roffhash_init();
89795c635efSGarrett D'Amore 
89895c635efSGarrett D'Amore 	return(r);
89995c635efSGarrett D'Amore }
90095c635efSGarrett D'Amore 
90195c635efSGarrett D'Amore /*
902*260e9a87SYuri Pankov  * In the current line, expand escape sequences that tend to get
903*260e9a87SYuri Pankov  * used in numerical expressions and conditional requests.
904*260e9a87SYuri Pankov  * Also check the syntax of the remaining escape sequences.
90595c635efSGarrett D'Amore  */
90695c635efSGarrett D'Amore static enum rofferr
907*260e9a87SYuri Pankov roff_res(struct roff *r, struct buf *buf, int ln, int pos)
90895c635efSGarrett D'Amore {
909*260e9a87SYuri Pankov 	char		 ubuf[24]; /* buffer to print the number */
910*260e9a87SYuri Pankov 	const char	*start;	/* start of the string to process */
911*260e9a87SYuri Pankov 	char		*stesc;	/* start of an escape sequence ('\\') */
91295c635efSGarrett D'Amore 	const char	*stnam;	/* start of the name, after "[(*" */
91395c635efSGarrett D'Amore 	const char	*cp;	/* end of the name, e.g. before ']' */
91495c635efSGarrett D'Amore 	const char	*res;	/* the string to be substituted */
915*260e9a87SYuri Pankov 	char		*nbuf;	/* new buffer to copy buf->buf to */
916698f87a4SGarrett D'Amore 	size_t		 maxl;  /* expected length of the escape name */
917698f87a4SGarrett D'Amore 	size_t		 naml;	/* actual length of the escape name */
918*260e9a87SYuri Pankov 	enum mandoc_esc	 esc;	/* type of the escape sequence */
919*260e9a87SYuri Pankov 	int		 inaml;	/* length returned from mandoc_escape() */
920698f87a4SGarrett D'Amore 	int		 expand_count;	/* to avoid infinite loops */
921*260e9a87SYuri Pankov 	int		 npos;	/* position in numeric expression */
922*260e9a87SYuri Pankov 	int		 arg_complete; /* argument not interrupted by eol */
923*260e9a87SYuri Pankov 	char		 term;	/* character terminating the escape */
92495c635efSGarrett D'Amore 
92595c635efSGarrett D'Amore 	expand_count = 0;
926*260e9a87SYuri Pankov 	start = buf->buf + pos;
927*260e9a87SYuri Pankov 	stesc = strchr(start, '\0') - 1;
928*260e9a87SYuri Pankov 	while (stesc-- > start) {
92995c635efSGarrett D'Amore 
930*260e9a87SYuri Pankov 		/* Search backwards for the next backslash. */
93195c635efSGarrett D'Amore 
932*260e9a87SYuri Pankov 		if (*stesc != '\\')
933*260e9a87SYuri Pankov 			continue;
93495c635efSGarrett D'Amore 
935*260e9a87SYuri Pankov 		/* If it is escaped, skip it. */
93695c635efSGarrett D'Amore 
937*260e9a87SYuri Pankov 		for (cp = stesc - 1; cp >= start; cp--)
938*260e9a87SYuri Pankov 			if (*cp != '\\')
939*260e9a87SYuri Pankov 				break;
940*260e9a87SYuri Pankov 
941*260e9a87SYuri Pankov 		if ((stesc - cp) % 2 == 0) {
942*260e9a87SYuri Pankov 			stesc = (char *)cp;
943*260e9a87SYuri Pankov 			continue;
944*260e9a87SYuri Pankov 		}
945*260e9a87SYuri Pankov 
946*260e9a87SYuri Pankov 		/* Decide whether to expand or to check only. */
947*260e9a87SYuri Pankov 
948*260e9a87SYuri Pankov 		term = '\0';
949*260e9a87SYuri Pankov 		cp = stesc + 1;
950698f87a4SGarrett D'Amore 		switch (*cp) {
951*260e9a87SYuri Pankov 		case '*':
952698f87a4SGarrett D'Amore 			res = NULL;
953698f87a4SGarrett D'Amore 			break;
954*260e9a87SYuri Pankov 		case 'B':
955*260e9a87SYuri Pankov 			/* FALLTHROUGH */
956*260e9a87SYuri Pankov 		case 'w':
957*260e9a87SYuri Pankov 			term = cp[1];
958*260e9a87SYuri Pankov 			/* FALLTHROUGH */
959*260e9a87SYuri Pankov 		case 'n':
960698f87a4SGarrett D'Amore 			res = ubuf;
961698f87a4SGarrett D'Amore 			break;
962698f87a4SGarrett D'Amore 		default:
963*260e9a87SYuri Pankov 			esc = mandoc_escape(&cp, &stnam, &inaml);
964*260e9a87SYuri Pankov 			if (esc == ESCAPE_ERROR ||
965*260e9a87SYuri Pankov 			    (esc == ESCAPE_SPECIAL &&
966*260e9a87SYuri Pankov 			     mchars_spec2cp(r->mchars, stnam, inaml) < 0))
967*260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_ESC_BAD,
968*260e9a87SYuri Pankov 				    r->parse, ln, (int)(stesc - buf->buf),
969*260e9a87SYuri Pankov 				    "%.*s", (int)(cp - stesc), stesc);
97095c635efSGarrett D'Amore 			continue;
97195c635efSGarrett D'Amore 		}
97295c635efSGarrett D'Amore 
973*260e9a87SYuri Pankov 		if (EXPAND_LIMIT < ++expand_count) {
974*260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_ROFFLOOP, r->parse,
975*260e9a87SYuri Pankov 			    ln, (int)(stesc - buf->buf), NULL);
976*260e9a87SYuri Pankov 			return(ROFF_IGN);
977*260e9a87SYuri Pankov 		}
97895c635efSGarrett D'Amore 
97995c635efSGarrett D'Amore 		/*
98095c635efSGarrett D'Amore 		 * The third character decides the length
981698f87a4SGarrett D'Amore 		 * of the name of the string or register.
98295c635efSGarrett D'Amore 		 * Save a pointer to the name.
98395c635efSGarrett D'Amore 		 */
98495c635efSGarrett D'Amore 
985*260e9a87SYuri Pankov 		if (term == '\0') {
986*260e9a87SYuri Pankov 			switch (*++cp) {
987*260e9a87SYuri Pankov 			case '\0':
988*260e9a87SYuri Pankov 				maxl = 0;
989*260e9a87SYuri Pankov 				break;
990*260e9a87SYuri Pankov 			case '(':
99195c635efSGarrett D'Amore 				cp++;
99295c635efSGarrett D'Amore 				maxl = 2;
99395c635efSGarrett D'Amore 				break;
994*260e9a87SYuri Pankov 			case '[':
99595c635efSGarrett D'Amore 				cp++;
996*260e9a87SYuri Pankov 				term = ']';
99795c635efSGarrett D'Amore 				maxl = 0;
99895c635efSGarrett D'Amore 				break;
99995c635efSGarrett D'Amore 			default:
100095c635efSGarrett D'Amore 				maxl = 1;
100195c635efSGarrett D'Amore 				break;
100295c635efSGarrett D'Amore 			}
1003*260e9a87SYuri Pankov 		} else {
1004*260e9a87SYuri Pankov 			cp += 2;
1005*260e9a87SYuri Pankov 			maxl = 0;
1006*260e9a87SYuri Pankov 		}
100795c635efSGarrett D'Amore 		stnam = cp;
100895c635efSGarrett D'Amore 
100995c635efSGarrett D'Amore 		/* Advance to the end of the name. */
101095c635efSGarrett D'Amore 
1011*260e9a87SYuri Pankov 		naml = 0;
1012*260e9a87SYuri Pankov 		arg_complete = 1;
1013*260e9a87SYuri Pankov 		while (maxl == 0 || naml < maxl) {
1014*260e9a87SYuri Pankov 			if (*cp == '\0') {
1015*260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
1016*260e9a87SYuri Pankov 				    ln, (int)(stesc - buf->buf), stesc);
1017*260e9a87SYuri Pankov 				arg_complete = 0;
101895c635efSGarrett D'Amore 				break;
101995c635efSGarrett D'Amore 			}
1020*260e9a87SYuri Pankov 			if (maxl == 0 && *cp == term) {
1021*260e9a87SYuri Pankov 				cp++;
1022*260e9a87SYuri Pankov 				break;
1023*260e9a87SYuri Pankov 			}
1024*260e9a87SYuri Pankov 			if (*cp++ != '\\' || stesc[1] != 'w') {
1025*260e9a87SYuri Pankov 				naml++;
1026*260e9a87SYuri Pankov 				continue;
1027*260e9a87SYuri Pankov 			}
1028*260e9a87SYuri Pankov 			switch (mandoc_escape(&cp, NULL, NULL)) {
1029*260e9a87SYuri Pankov 			case ESCAPE_SPECIAL:
1030*260e9a87SYuri Pankov 				/* FALLTHROUGH */
1031*260e9a87SYuri Pankov 			case ESCAPE_UNICODE:
1032*260e9a87SYuri Pankov 				/* FALLTHROUGH */
1033*260e9a87SYuri Pankov 			case ESCAPE_NUMBERED:
1034*260e9a87SYuri Pankov 				/* FALLTHROUGH */
1035*260e9a87SYuri Pankov 			case ESCAPE_OVERSTRIKE:
1036*260e9a87SYuri Pankov 				naml++;
1037*260e9a87SYuri Pankov 				break;
1038*260e9a87SYuri Pankov 			default:
1039*260e9a87SYuri Pankov 				break;
1040*260e9a87SYuri Pankov 			}
1041*260e9a87SYuri Pankov 		}
104295c635efSGarrett D'Amore 
104395c635efSGarrett D'Amore 		/*
104495c635efSGarrett D'Amore 		 * Retrieve the replacement string; if it is
104595c635efSGarrett D'Amore 		 * undefined, resume searching for escapes.
104695c635efSGarrett D'Amore 		 */
104795c635efSGarrett D'Amore 
1048*260e9a87SYuri Pankov 		switch (stesc[1]) {
1049*260e9a87SYuri Pankov 		case '*':
1050*260e9a87SYuri Pankov 			if (arg_complete)
1051698f87a4SGarrett D'Amore 				res = roff_getstrn(r, stnam, naml);
1052*260e9a87SYuri Pankov 			break;
1053*260e9a87SYuri Pankov 		case 'B':
1054*260e9a87SYuri Pankov 			npos = 0;
1055*260e9a87SYuri Pankov 			ubuf[0] = arg_complete &&
1056*260e9a87SYuri Pankov 			    roff_evalnum(r, ln, stnam, &npos,
1057*260e9a87SYuri Pankov 			      NULL, ROFFNUM_SCALE) &&
1058*260e9a87SYuri Pankov 			    stnam + npos + 1 == cp ? '1' : '0';
1059*260e9a87SYuri Pankov 			ubuf[1] = '\0';
1060*260e9a87SYuri Pankov 			break;
1061*260e9a87SYuri Pankov 		case 'n':
1062*260e9a87SYuri Pankov 			if (arg_complete)
1063*260e9a87SYuri Pankov 				(void)snprintf(ubuf, sizeof(ubuf), "%d",
1064698f87a4SGarrett D'Amore 				    roff_getregn(r, stnam, naml));
1065*260e9a87SYuri Pankov 			else
1066*260e9a87SYuri Pankov 				ubuf[0] = '\0';
1067*260e9a87SYuri Pankov 			break;
1068*260e9a87SYuri Pankov 		case 'w':
1069*260e9a87SYuri Pankov 			/* use even incomplete args */
1070*260e9a87SYuri Pankov 			(void)snprintf(ubuf, sizeof(ubuf), "%d",
1071*260e9a87SYuri Pankov 			    24 * (int)naml);
1072*260e9a87SYuri Pankov 			break;
1073*260e9a87SYuri Pankov 		}
107495c635efSGarrett D'Amore 
1075*260e9a87SYuri Pankov 		if (res == NULL) {
1076*260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_STR_UNDEF,
1077*260e9a87SYuri Pankov 			    r->parse, ln, (int)(stesc - buf->buf),
1078*260e9a87SYuri Pankov 			    "%.*s", (int)naml, stnam);
107995c635efSGarrett D'Amore 			res = "";
1080*260e9a87SYuri Pankov 		} else if (buf->sz + strlen(res) > SHRT_MAX) {
1081*260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_ROFFLOOP, r->parse,
1082*260e9a87SYuri Pankov 			    ln, (int)(stesc - buf->buf), NULL);
1083*260e9a87SYuri Pankov 			return(ROFF_IGN);
108495c635efSGarrett D'Amore 		}
108595c635efSGarrett D'Amore 
108695c635efSGarrett D'Amore 		/* Replace the escape sequence by the string. */
108795c635efSGarrett D'Amore 
1088*260e9a87SYuri Pankov 		*stesc = '\0';
1089*260e9a87SYuri Pankov 		buf->sz = mandoc_asprintf(&nbuf, "%s%s%s",
1090*260e9a87SYuri Pankov 		    buf->buf, res, cp) + 1;
109195c635efSGarrett D'Amore 
1092*260e9a87SYuri Pankov 		/* Prepare for the next replacement. */
109395c635efSGarrett D'Amore 
1094*260e9a87SYuri Pankov 		start = nbuf + pos;
1095*260e9a87SYuri Pankov 		stesc = nbuf + (stesc - buf->buf) + strlen(res);
1096*260e9a87SYuri Pankov 		free(buf->buf);
1097*260e9a87SYuri Pankov 		buf->buf = nbuf;
109895c635efSGarrett D'Amore 	}
109995c635efSGarrett D'Amore 	return(ROFF_CONT);
110095c635efSGarrett D'Amore }
110195c635efSGarrett D'Amore 
110295c635efSGarrett D'Amore /*
1103698f87a4SGarrett D'Amore  * Process text streams:
1104698f87a4SGarrett D'Amore  * Convert all breakable hyphens into ASCII_HYPH.
1105698f87a4SGarrett D'Amore  * Decrement and spring input line trap.
110695c635efSGarrett D'Amore  */
110795c635efSGarrett D'Amore static enum rofferr
1108*260e9a87SYuri Pankov roff_parsetext(struct buf *buf, int pos, int *offs)
110995c635efSGarrett D'Amore {
111095c635efSGarrett D'Amore 	size_t		 sz;
111195c635efSGarrett D'Amore 	const char	*start;
1112698f87a4SGarrett D'Amore 	char		*p;
1113698f87a4SGarrett D'Amore 	int		 isz;
111495c635efSGarrett D'Amore 	enum mandoc_esc	 esc;
111595c635efSGarrett D'Amore 
1116*260e9a87SYuri Pankov 	start = p = buf->buf + pos;
111795c635efSGarrett D'Amore 
1118*260e9a87SYuri Pankov 	while (*p != '\0') {
111995c635efSGarrett D'Amore 		sz = strcspn(p, "-\\");
112095c635efSGarrett D'Amore 		p += sz;
112195c635efSGarrett D'Amore 
1122*260e9a87SYuri Pankov 		if (*p == '\0')
112395c635efSGarrett D'Amore 			break;
112495c635efSGarrett D'Amore 
1125*260e9a87SYuri Pankov 		if (*p == '\\') {
112695c635efSGarrett D'Amore 			/* Skip over escapes. */
112795c635efSGarrett D'Amore 			p++;
1128698f87a4SGarrett D'Amore 			esc = mandoc_escape((const char **)&p, NULL, NULL);
1129*260e9a87SYuri Pankov 			if (esc == ESCAPE_ERROR)
113095c635efSGarrett D'Amore 				break;
113195c635efSGarrett D'Amore 			continue;
113295c635efSGarrett D'Amore 		} else if (p == start) {
113395c635efSGarrett D'Amore 			p++;
113495c635efSGarrett D'Amore 			continue;
113595c635efSGarrett D'Amore 		}
113695c635efSGarrett D'Amore 
113795c635efSGarrett D'Amore 		if (isalpha((unsigned char)p[-1]) &&
113895c635efSGarrett D'Amore 		    isalpha((unsigned char)p[1]))
113995c635efSGarrett D'Amore 			*p = ASCII_HYPH;
114095c635efSGarrett D'Amore 		p++;
114195c635efSGarrett D'Amore 	}
114295c635efSGarrett D'Amore 
1143698f87a4SGarrett D'Amore 	/* Spring the input line trap. */
1144*260e9a87SYuri Pankov 	if (roffit_lines == 1) {
1145*260e9a87SYuri Pankov 		isz = mandoc_asprintf(&p, "%s\n.%s", buf->buf, roffit_macro);
1146*260e9a87SYuri Pankov 		free(buf->buf);
1147*260e9a87SYuri Pankov 		buf->buf = p;
1148*260e9a87SYuri Pankov 		buf->sz = isz + 1;
1149698f87a4SGarrett D'Amore 		*offs = 0;
1150698f87a4SGarrett D'Amore 		free(roffit_macro);
1151698f87a4SGarrett D'Amore 		roffit_lines = 0;
1152698f87a4SGarrett D'Amore 		return(ROFF_REPARSE);
1153*260e9a87SYuri Pankov 	} else if (roffit_lines > 1)
1154698f87a4SGarrett D'Amore 		--roffit_lines;
115595c635efSGarrett D'Amore 	return(ROFF_CONT);
115695c635efSGarrett D'Amore }
115795c635efSGarrett D'Amore 
115895c635efSGarrett D'Amore enum rofferr
1159*260e9a87SYuri Pankov roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs)
116095c635efSGarrett D'Amore {
116195c635efSGarrett D'Amore 	enum rofft	 t;
116295c635efSGarrett D'Amore 	enum rofferr	 e;
1163*260e9a87SYuri Pankov 	int		 pos;	/* parse point */
1164*260e9a87SYuri Pankov 	int		 spos;	/* saved parse point for messages */
1165*260e9a87SYuri Pankov 	int		 ppos;	/* original offset in buf->buf */
1166*260e9a87SYuri Pankov 	int		 ctl;	/* macro line (boolean) */
116795c635efSGarrett D'Amore 
1168*260e9a87SYuri Pankov 	ppos = pos = *offs;
116995c635efSGarrett D'Amore 
1170*260e9a87SYuri Pankov 	/* Handle in-line equation delimiters. */
1171*260e9a87SYuri Pankov 
1172*260e9a87SYuri Pankov 	if (r->tbl == NULL &&
1173*260e9a87SYuri Pankov 	    r->last_eqn != NULL && r->last_eqn->delim &&
1174*260e9a87SYuri Pankov 	    (r->eqn == NULL || r->eqn_inline)) {
1175*260e9a87SYuri Pankov 		e = roff_eqndelim(r, buf, pos);
1176*260e9a87SYuri Pankov 		if (e == ROFF_REPARSE)
117795c635efSGarrett D'Amore 			return(e);
1178*260e9a87SYuri Pankov 		assert(e == ROFF_CONT);
1179*260e9a87SYuri Pankov 	}
118095c635efSGarrett D'Amore 
1181*260e9a87SYuri Pankov 	/* Expand some escape sequences. */
1182*260e9a87SYuri Pankov 
1183*260e9a87SYuri Pankov 	e = roff_res(r, buf, ln, pos);
1184*260e9a87SYuri Pankov 	if (e == ROFF_IGN)
1185*260e9a87SYuri Pankov 		return(e);
1186*260e9a87SYuri Pankov 	assert(e == ROFF_CONT);
1187*260e9a87SYuri Pankov 
1188*260e9a87SYuri Pankov 	ctl = roff_getcontrol(r, buf->buf, &pos);
118995c635efSGarrett D'Amore 
119095c635efSGarrett D'Amore 	/*
119195c635efSGarrett D'Amore 	 * First, if a scope is open and we're not a macro, pass the
1192*260e9a87SYuri Pankov 	 * text through the macro's filter.
1193*260e9a87SYuri Pankov 	 * Equations process all content themselves.
1194*260e9a87SYuri Pankov 	 * Tables process almost all content themselves, but we want
1195*260e9a87SYuri Pankov 	 * to warn about macros before passing it there.
119695c635efSGarrett D'Amore 	 */
119795c635efSGarrett D'Amore 
1198*260e9a87SYuri Pankov 	if (r->last != NULL && ! ctl) {
119995c635efSGarrett D'Amore 		t = r->last->tok;
120095c635efSGarrett D'Amore 		assert(roffs[t].text);
1201*260e9a87SYuri Pankov 		e = (*roffs[t].text)(r, t, buf, ln, pos, pos, offs);
1202*260e9a87SYuri Pankov 		assert(e == ROFF_IGN || e == ROFF_CONT);
1203*260e9a87SYuri Pankov 		if (e != ROFF_CONT)
120495c635efSGarrett D'Amore 			return(e);
1205698f87a4SGarrett D'Amore 	}
1206*260e9a87SYuri Pankov 	if (r->eqn != NULL)
1207*260e9a87SYuri Pankov 		return(eqn_read(&r->eqn, ln, buf->buf, ppos, offs));
1208*260e9a87SYuri Pankov 	if (r->tbl != NULL && ( ! ctl || buf->buf[pos] == '\0'))
1209*260e9a87SYuri Pankov 		return(tbl_read(r->tbl, ln, buf->buf, ppos));
1210*260e9a87SYuri Pankov 	if ( ! ctl)
1211*260e9a87SYuri Pankov 		return(roff_parsetext(buf, pos, offs));
1212*260e9a87SYuri Pankov 
1213*260e9a87SYuri Pankov 	/* Skip empty request lines. */
1214*260e9a87SYuri Pankov 
1215*260e9a87SYuri Pankov 	if (buf->buf[pos] == '"') {
1216*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_COMMENT_BAD, r->parse,
1217*260e9a87SYuri Pankov 		    ln, pos, NULL);
1218*260e9a87SYuri Pankov 		return(ROFF_IGN);
1219*260e9a87SYuri Pankov 	} else if (buf->buf[pos] == '\0')
1220*260e9a87SYuri Pankov 		return(ROFF_IGN);
122195c635efSGarrett D'Amore 
122295c635efSGarrett D'Amore 	/*
122395c635efSGarrett D'Amore 	 * If a scope is open, go to the child handler for that macro,
122495c635efSGarrett D'Amore 	 * as it may want to preprocess before doing anything with it.
122595c635efSGarrett D'Amore 	 * Don't do so if an equation is open.
122695c635efSGarrett D'Amore 	 */
122795c635efSGarrett D'Amore 
122895c635efSGarrett D'Amore 	if (r->last) {
122995c635efSGarrett D'Amore 		t = r->last->tok;
123095c635efSGarrett D'Amore 		assert(roffs[t].sub);
1231*260e9a87SYuri Pankov 		return((*roffs[t].sub)(r, t, buf, ln, ppos, pos, offs));
1232*260e9a87SYuri Pankov 	}
1233*260e9a87SYuri Pankov 
1234*260e9a87SYuri Pankov 	/* No scope is open.  This is a new request or macro. */
1235*260e9a87SYuri Pankov 
1236*260e9a87SYuri Pankov 	spos = pos;
1237*260e9a87SYuri Pankov 	t = roff_parse(r, buf->buf, &pos, ln, ppos);
1238*260e9a87SYuri Pankov 
1239*260e9a87SYuri Pankov 	/* Tables ignore most macros. */
1240*260e9a87SYuri Pankov 
1241*260e9a87SYuri Pankov 	if (r->tbl != NULL && (t == ROFF_MAX || t == ROFF_TS)) {
1242*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_TBLMACRO, r->parse,
1243*260e9a87SYuri Pankov 		    ln, pos, buf->buf + spos);
1244*260e9a87SYuri Pankov 		if (t == ROFF_TS)
1245*260e9a87SYuri Pankov 			return(ROFF_IGN);
1246*260e9a87SYuri Pankov 		while (buf->buf[pos] != '\0' && buf->buf[pos] != ' ')
1247*260e9a87SYuri Pankov 			pos++;
1248*260e9a87SYuri Pankov 		while (buf->buf[pos] != '\0' && buf->buf[pos] == ' ')
1249*260e9a87SYuri Pankov 			pos++;
1250*260e9a87SYuri Pankov 		return(tbl_read(r->tbl, ln, buf->buf, pos));
125195c635efSGarrett D'Amore 	}
125295c635efSGarrett D'Amore 
125395c635efSGarrett D'Amore 	/*
1254*260e9a87SYuri Pankov 	 * This is neither a roff request nor a user-defined macro.
1255*260e9a87SYuri Pankov 	 * Let the standard macro set parsers handle it.
125695c635efSGarrett D'Amore 	 */
125795c635efSGarrett D'Amore 
1258*260e9a87SYuri Pankov 	if (t == ROFF_MAX)
125995c635efSGarrett D'Amore 		return(ROFF_CONT);
126095c635efSGarrett D'Amore 
1261*260e9a87SYuri Pankov 	/* Execute a roff request or a user defined macro. */
126295c635efSGarrett D'Amore 
1263*260e9a87SYuri Pankov 	assert(roffs[t].proc);
1264*260e9a87SYuri Pankov 	return((*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs));
1265*260e9a87SYuri Pankov }
126695c635efSGarrett D'Amore 
126795c635efSGarrett D'Amore void
126895c635efSGarrett D'Amore roff_endparse(struct roff *r)
126995c635efSGarrett D'Amore {
127095c635efSGarrett D'Amore 
127195c635efSGarrett D'Amore 	if (r->last)
1272*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
1273*260e9a87SYuri Pankov 		    r->last->line, r->last->col,
1274*260e9a87SYuri Pankov 		    roffs[r->last->tok].name);
127595c635efSGarrett D'Amore 
127695c635efSGarrett D'Amore 	if (r->eqn) {
1277*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
1278*260e9a87SYuri Pankov 		    r->eqn->eqn.ln, r->eqn->eqn.pos, "EQ");
127995c635efSGarrett D'Amore 		eqn_end(&r->eqn);
128095c635efSGarrett D'Amore 	}
128195c635efSGarrett D'Amore 
128295c635efSGarrett D'Amore 	if (r->tbl) {
1283*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
1284*260e9a87SYuri Pankov 		    r->tbl->line, r->tbl->pos, "TS");
128595c635efSGarrett D'Amore 		tbl_end(&r->tbl);
128695c635efSGarrett D'Amore 	}
128795c635efSGarrett D'Amore }
128895c635efSGarrett D'Amore 
128995c635efSGarrett D'Amore /*
129095c635efSGarrett D'Amore  * Parse a roff node's type from the input buffer.  This must be in the
129195c635efSGarrett D'Amore  * form of ".foo xxx" in the usual way.
129295c635efSGarrett D'Amore  */
129395c635efSGarrett D'Amore static enum rofft
1294*260e9a87SYuri Pankov roff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos)
129595c635efSGarrett D'Amore {
1296*260e9a87SYuri Pankov 	char		*cp;
129795c635efSGarrett D'Amore 	const char	*mac;
129895c635efSGarrett D'Amore 	size_t		 maclen;
129995c635efSGarrett D'Amore 	enum rofft	 t;
130095c635efSGarrett D'Amore 
1301*260e9a87SYuri Pankov 	cp = buf + *pos;
1302*260e9a87SYuri Pankov 
1303*260e9a87SYuri Pankov 	if ('\0' == *cp || '"' == *cp || '\t' == *cp || ' ' == *cp)
130495c635efSGarrett D'Amore 		return(ROFF_MAX);
130595c635efSGarrett D'Amore 
1306*260e9a87SYuri Pankov 	mac = cp;
1307*260e9a87SYuri Pankov 	maclen = roff_getname(r, &cp, ln, ppos);
130895c635efSGarrett D'Amore 
130995c635efSGarrett D'Amore 	t = (r->current_string = roff_getstrn(r, mac, maclen))
131095c635efSGarrett D'Amore 	    ? ROFF_USERDEF : roffhash_find(mac, maclen);
131195c635efSGarrett D'Amore 
1312*260e9a87SYuri Pankov 	if (ROFF_MAX != t)
1313*260e9a87SYuri Pankov 		*pos = cp - buf;
131495c635efSGarrett D'Amore 
131595c635efSGarrett D'Amore 	return(t);
131695c635efSGarrett D'Amore }
131795c635efSGarrett D'Amore 
131895c635efSGarrett D'Amore static enum rofferr
131995c635efSGarrett D'Amore roff_cblock(ROFF_ARGS)
132095c635efSGarrett D'Amore {
132195c635efSGarrett D'Amore 
132295c635efSGarrett D'Amore 	/*
132395c635efSGarrett D'Amore 	 * A block-close `..' should only be invoked as a child of an
132495c635efSGarrett D'Amore 	 * ignore macro, otherwise raise a warning and just ignore it.
132595c635efSGarrett D'Amore 	 */
132695c635efSGarrett D'Amore 
1327*260e9a87SYuri Pankov 	if (r->last == NULL) {
1328*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1329*260e9a87SYuri Pankov 		    ln, ppos, "..");
133095c635efSGarrett D'Amore 		return(ROFF_IGN);
133195c635efSGarrett D'Amore 	}
133295c635efSGarrett D'Amore 
133395c635efSGarrett D'Amore 	switch (r->last->tok) {
1334*260e9a87SYuri Pankov 	case ROFF_am:
1335*260e9a87SYuri Pankov 		/* ROFF_am1 is remapped to ROFF_am in roff_block(). */
133695c635efSGarrett D'Amore 		/* FALLTHROUGH */
1337*260e9a87SYuri Pankov 	case ROFF_ami:
133895c635efSGarrett D'Amore 		/* FALLTHROUGH */
1339*260e9a87SYuri Pankov 	case ROFF_de:
134095c635efSGarrett D'Amore 		/* ROFF_de1 is remapped to ROFF_de in roff_block(). */
134195c635efSGarrett D'Amore 		/* FALLTHROUGH */
1342*260e9a87SYuri Pankov 	case ROFF_dei:
134395c635efSGarrett D'Amore 		/* FALLTHROUGH */
1344*260e9a87SYuri Pankov 	case ROFF_ig:
134595c635efSGarrett D'Amore 		break;
134695c635efSGarrett D'Amore 	default:
1347*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1348*260e9a87SYuri Pankov 		    ln, ppos, "..");
134995c635efSGarrett D'Amore 		return(ROFF_IGN);
135095c635efSGarrett D'Amore 	}
135195c635efSGarrett D'Amore 
1352*260e9a87SYuri Pankov 	if (buf->buf[pos] != '\0')
1353*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos,
1354*260e9a87SYuri Pankov 		    ".. %s", buf->buf + pos);
135595c635efSGarrett D'Amore 
135695c635efSGarrett D'Amore 	roffnode_pop(r);
135795c635efSGarrett D'Amore 	roffnode_cleanscope(r);
135895c635efSGarrett D'Amore 	return(ROFF_IGN);
135995c635efSGarrett D'Amore 
136095c635efSGarrett D'Amore }
136195c635efSGarrett D'Amore 
136295c635efSGarrett D'Amore static void
136395c635efSGarrett D'Amore roffnode_cleanscope(struct roff *r)
136495c635efSGarrett D'Amore {
136595c635efSGarrett D'Amore 
136695c635efSGarrett D'Amore 	while (r->last) {
1367698f87a4SGarrett D'Amore 		if (--r->last->endspan != 0)
136895c635efSGarrett D'Amore 			break;
136995c635efSGarrett D'Amore 		roffnode_pop(r);
137095c635efSGarrett D'Amore 	}
137195c635efSGarrett D'Amore }
137295c635efSGarrett D'Amore 
1373*260e9a87SYuri Pankov static void
1374*260e9a87SYuri Pankov roff_ccond(struct roff *r, int ln, int ppos)
137595c635efSGarrett D'Amore {
137695c635efSGarrett D'Amore 
137795c635efSGarrett D'Amore 	if (NULL == r->last) {
1378*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1379*260e9a87SYuri Pankov 		    ln, ppos, "\\}");
1380*260e9a87SYuri Pankov 		return;
138195c635efSGarrett D'Amore 	}
138295c635efSGarrett D'Amore 
138395c635efSGarrett D'Amore 	switch (r->last->tok) {
1384*260e9a87SYuri Pankov 	case ROFF_el:
138595c635efSGarrett D'Amore 		/* FALLTHROUGH */
1386*260e9a87SYuri Pankov 	case ROFF_ie:
138795c635efSGarrett D'Amore 		/* FALLTHROUGH */
1388*260e9a87SYuri Pankov 	case ROFF_if:
138995c635efSGarrett D'Amore 		break;
139095c635efSGarrett D'Amore 	default:
1391*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1392*260e9a87SYuri Pankov 		    ln, ppos, "\\}");
1393*260e9a87SYuri Pankov 		return;
139495c635efSGarrett D'Amore 	}
139595c635efSGarrett D'Amore 
139695c635efSGarrett D'Amore 	if (r->last->endspan > -1) {
1397*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1398*260e9a87SYuri Pankov 		    ln, ppos, "\\}");
1399*260e9a87SYuri Pankov 		return;
140095c635efSGarrett D'Amore 	}
140195c635efSGarrett D'Amore 
140295c635efSGarrett D'Amore 	roffnode_pop(r);
140395c635efSGarrett D'Amore 	roffnode_cleanscope(r);
1404*260e9a87SYuri Pankov 	return;
140595c635efSGarrett D'Amore }
140695c635efSGarrett D'Amore 
140795c635efSGarrett D'Amore static enum rofferr
140895c635efSGarrett D'Amore roff_block(ROFF_ARGS)
140995c635efSGarrett D'Amore {
1410*260e9a87SYuri Pankov 	const char	*name;
1411*260e9a87SYuri Pankov 	char		*iname, *cp;
1412*260e9a87SYuri Pankov 	size_t		 namesz;
141395c635efSGarrett D'Amore 
1414*260e9a87SYuri Pankov 	/* Ignore groff compatibility mode for now. */
141595c635efSGarrett D'Amore 
1416*260e9a87SYuri Pankov 	if (tok == ROFF_de1)
1417*260e9a87SYuri Pankov 		tok = ROFF_de;
1418*260e9a87SYuri Pankov 	else if (tok == ROFF_dei1)
1419*260e9a87SYuri Pankov 		tok = ROFF_dei;
1420*260e9a87SYuri Pankov 	else if (tok == ROFF_am1)
1421*260e9a87SYuri Pankov 		tok = ROFF_am;
1422*260e9a87SYuri Pankov 	else if (tok == ROFF_ami1)
1423*260e9a87SYuri Pankov 		tok = ROFF_ami;
1424*260e9a87SYuri Pankov 
1425*260e9a87SYuri Pankov 	/* Parse the macro name argument. */
1426*260e9a87SYuri Pankov 
1427*260e9a87SYuri Pankov 	cp = buf->buf + pos;
1428*260e9a87SYuri Pankov 	if (tok == ROFF_ig) {
1429*260e9a87SYuri Pankov 		iname = NULL;
1430*260e9a87SYuri Pankov 		namesz = 0;
1431*260e9a87SYuri Pankov 	} else {
1432*260e9a87SYuri Pankov 		iname = cp;
1433*260e9a87SYuri Pankov 		namesz = roff_getname(r, &cp, ln, ppos);
1434*260e9a87SYuri Pankov 		iname[namesz] = '\0';
143595c635efSGarrett D'Amore 	}
143695c635efSGarrett D'Amore 
1437*260e9a87SYuri Pankov 	/* Resolve the macro name argument if it is indirect. */
143895c635efSGarrett D'Amore 
1439*260e9a87SYuri Pankov 	if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
1440*260e9a87SYuri Pankov 		if ((name = roff_getstrn(r, iname, namesz)) == NULL) {
1441*260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_STR_UNDEF,
1442*260e9a87SYuri Pankov 			    r->parse, ln, (int)(iname - buf->buf),
1443*260e9a87SYuri Pankov 			    "%.*s", (int)namesz, iname);
1444*260e9a87SYuri Pankov 			namesz = 0;
1445*260e9a87SYuri Pankov 		} else
1446*260e9a87SYuri Pankov 			namesz = strlen(name);
1447*260e9a87SYuri Pankov 	} else
1448*260e9a87SYuri Pankov 		name = iname;
144995c635efSGarrett D'Amore 
1450*260e9a87SYuri Pankov 	if (namesz == 0 && tok != ROFF_ig) {
1451*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse,
1452*260e9a87SYuri Pankov 		    ln, ppos, roffs[tok].name);
1453*260e9a87SYuri Pankov 		return(ROFF_IGN);
145495c635efSGarrett D'Amore 	}
145595c635efSGarrett D'Amore 
145695c635efSGarrett D'Amore 	roffnode_push(r, tok, name, ln, ppos);
145795c635efSGarrett D'Amore 
145895c635efSGarrett D'Amore 	/*
145995c635efSGarrett D'Amore 	 * At the beginning of a `de' macro, clear the existing string
146095c635efSGarrett D'Amore 	 * with the same name, if there is one.  New content will be
1461*260e9a87SYuri Pankov 	 * appended from roff_block_text() in multiline mode.
146295c635efSGarrett D'Amore 	 */
146395c635efSGarrett D'Amore 
1464*260e9a87SYuri Pankov 	if (tok == ROFF_de || tok == ROFF_dei)
1465*260e9a87SYuri Pankov 		roff_setstrn(&r->strtab, name, namesz, "", 0, 0);
146695c635efSGarrett D'Amore 
1467*260e9a87SYuri Pankov 	if (*cp == '\0')
146895c635efSGarrett D'Amore 		return(ROFF_IGN);
146995c635efSGarrett D'Amore 
1470*260e9a87SYuri Pankov 	/* Get the custom end marker. */
147195c635efSGarrett D'Amore 
1472*260e9a87SYuri Pankov 	iname = cp;
1473*260e9a87SYuri Pankov 	namesz = roff_getname(r, &cp, ln, ppos);
147495c635efSGarrett D'Amore 
1475*260e9a87SYuri Pankov 	/* Resolve the end marker if it is indirect. */
147695c635efSGarrett D'Amore 
1477*260e9a87SYuri Pankov 	if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
1478*260e9a87SYuri Pankov 		if ((name = roff_getstrn(r, iname, namesz)) == NULL) {
1479*260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_STR_UNDEF,
1480*260e9a87SYuri Pankov 			    r->parse, ln, (int)(iname - buf->buf),
1481*260e9a87SYuri Pankov 			    "%.*s", (int)namesz, iname);
1482*260e9a87SYuri Pankov 			namesz = 0;
1483*260e9a87SYuri Pankov 		} else
1484*260e9a87SYuri Pankov 			namesz = strlen(name);
1485*260e9a87SYuri Pankov 	} else
1486*260e9a87SYuri Pankov 		name = iname;
148795c635efSGarrett D'Amore 
1488*260e9a87SYuri Pankov 	if (namesz)
1489*260e9a87SYuri Pankov 		r->last->end = mandoc_strndup(name, namesz);
149095c635efSGarrett D'Amore 
1491*260e9a87SYuri Pankov 	if (*cp != '\0')
1492*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse,
1493*260e9a87SYuri Pankov 		    ln, pos, ".%s ... %s", roffs[tok].name, cp);
149495c635efSGarrett D'Amore 
149595c635efSGarrett D'Amore 	return(ROFF_IGN);
149695c635efSGarrett D'Amore }
149795c635efSGarrett D'Amore 
149895c635efSGarrett D'Amore static enum rofferr
149995c635efSGarrett D'Amore roff_block_sub(ROFF_ARGS)
150095c635efSGarrett D'Amore {
150195c635efSGarrett D'Amore 	enum rofft	t;
150295c635efSGarrett D'Amore 	int		i, j;
150395c635efSGarrett D'Amore 
150495c635efSGarrett D'Amore 	/*
150595c635efSGarrett D'Amore 	 * First check whether a custom macro exists at this level.  If
150695c635efSGarrett D'Amore 	 * it does, then check against it.  This is some of groff's
150795c635efSGarrett D'Amore 	 * stranger behaviours.  If we encountered a custom end-scope
150895c635efSGarrett D'Amore 	 * tag and that tag also happens to be a "real" macro, then we
150995c635efSGarrett D'Amore 	 * need to try interpreting it again as a real macro.  If it's
151095c635efSGarrett D'Amore 	 * not, then return ignore.  Else continue.
151195c635efSGarrett D'Amore 	 */
151295c635efSGarrett D'Amore 
151395c635efSGarrett D'Amore 	if (r->last->end) {
151495c635efSGarrett D'Amore 		for (i = pos, j = 0; r->last->end[j]; j++, i++)
1515*260e9a87SYuri Pankov 			if (buf->buf[i] != r->last->end[j])
151695c635efSGarrett D'Amore 				break;
151795c635efSGarrett D'Amore 
1518*260e9a87SYuri Pankov 		if (r->last->end[j] == '\0' &&
1519*260e9a87SYuri Pankov 		    (buf->buf[i] == '\0' ||
1520*260e9a87SYuri Pankov 		     buf->buf[i] == ' ' ||
1521*260e9a87SYuri Pankov 		     buf->buf[i] == '\t')) {
152295c635efSGarrett D'Amore 			roffnode_pop(r);
152395c635efSGarrett D'Amore 			roffnode_cleanscope(r);
152495c635efSGarrett D'Amore 
1525*260e9a87SYuri Pankov 			while (buf->buf[i] == ' ' || buf->buf[i] == '\t')
152695c635efSGarrett D'Amore 				i++;
152795c635efSGarrett D'Amore 
152895c635efSGarrett D'Amore 			pos = i;
1529*260e9a87SYuri Pankov 			if (roff_parse(r, buf->buf, &pos, ln, ppos) !=
1530*260e9a87SYuri Pankov 			    ROFF_MAX)
153195c635efSGarrett D'Amore 				return(ROFF_RERUN);
153295c635efSGarrett D'Amore 			return(ROFF_IGN);
153395c635efSGarrett D'Amore 		}
153495c635efSGarrett D'Amore 	}
153595c635efSGarrett D'Amore 
153695c635efSGarrett D'Amore 	/*
153795c635efSGarrett D'Amore 	 * If we have no custom end-query or lookup failed, then try
153895c635efSGarrett D'Amore 	 * pulling it out of the hashtable.
153995c635efSGarrett D'Amore 	 */
154095c635efSGarrett D'Amore 
1541*260e9a87SYuri Pankov 	t = roff_parse(r, buf->buf, &pos, ln, ppos);
154295c635efSGarrett D'Amore 
1543*260e9a87SYuri Pankov 	if (t != ROFF_cblock) {
1544*260e9a87SYuri Pankov 		if (tok != ROFF_ig)
1545*260e9a87SYuri Pankov 			roff_setstr(r, r->last->name, buf->buf + ppos, 2);
154695c635efSGarrett D'Amore 		return(ROFF_IGN);
154795c635efSGarrett D'Amore 	}
154895c635efSGarrett D'Amore 
154995c635efSGarrett D'Amore 	assert(roffs[t].proc);
1550*260e9a87SYuri Pankov 	return((*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs));
155195c635efSGarrett D'Amore }
155295c635efSGarrett D'Amore 
155395c635efSGarrett D'Amore static enum rofferr
155495c635efSGarrett D'Amore roff_block_text(ROFF_ARGS)
155595c635efSGarrett D'Amore {
155695c635efSGarrett D'Amore 
1557*260e9a87SYuri Pankov 	if (tok != ROFF_ig)
1558*260e9a87SYuri Pankov 		roff_setstr(r, r->last->name, buf->buf + pos, 2);
155995c635efSGarrett D'Amore 
156095c635efSGarrett D'Amore 	return(ROFF_IGN);
156195c635efSGarrett D'Amore }
156295c635efSGarrett D'Amore 
156395c635efSGarrett D'Amore static enum rofferr
156495c635efSGarrett D'Amore roff_cond_sub(ROFF_ARGS)
156595c635efSGarrett D'Amore {
156695c635efSGarrett D'Amore 	enum rofft	 t;
156795c635efSGarrett D'Amore 	char		*ep;
1568*260e9a87SYuri Pankov 	int		 rr;
156995c635efSGarrett D'Amore 
157095c635efSGarrett D'Amore 	rr = r->last->rule;
157195c635efSGarrett D'Amore 	roffnode_cleanscope(r);
1572*260e9a87SYuri Pankov 	t = roff_parse(r, buf->buf, &pos, ln, ppos);
157395c635efSGarrett D'Amore 
157495c635efSGarrett D'Amore 	/*
1575698f87a4SGarrett D'Amore 	 * Fully handle known macros when they are structurally
1576698f87a4SGarrett D'Amore 	 * required or when the conditional evaluated to true.
157795c635efSGarrett D'Amore 	 */
157895c635efSGarrett D'Amore 
1579*260e9a87SYuri Pankov 	if ((t != ROFF_MAX) &&
1580*260e9a87SYuri Pankov 	    (rr || roffs[t].flags & ROFFMAC_STRUCT)) {
1581698f87a4SGarrett D'Amore 		assert(roffs[t].proc);
1582*260e9a87SYuri Pankov 		return((*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs));
1583698f87a4SGarrett D'Amore 	}
1584698f87a4SGarrett D'Amore 
1585*260e9a87SYuri Pankov 	/*
1586*260e9a87SYuri Pankov 	 * If `\}' occurs on a macro line without a preceding macro,
1587*260e9a87SYuri Pankov 	 * drop the line completely.
1588*260e9a87SYuri Pankov 	 */
1589*260e9a87SYuri Pankov 
1590*260e9a87SYuri Pankov 	ep = buf->buf + pos;
1591*260e9a87SYuri Pankov 	if (ep[0] == '\\' && ep[1] == '}')
1592*260e9a87SYuri Pankov 		rr = 0;
1593*260e9a87SYuri Pankov 
1594698f87a4SGarrett D'Amore 	/* Always check for the closing delimiter `\}'. */
1595698f87a4SGarrett D'Amore 
1596*260e9a87SYuri Pankov 	while ((ep = strchr(ep, '\\')) != NULL) {
1597*260e9a87SYuri Pankov 		if (*(++ep) == '}') {
1598*260e9a87SYuri Pankov 			*ep = '&';
1599*260e9a87SYuri Pankov 			roff_ccond(r, ln, ep - buf->buf - 1);
160095c635efSGarrett D'Amore 		}
1601*260e9a87SYuri Pankov 		if (*ep != '\0')
1602*260e9a87SYuri Pankov 			++ep;
1603*260e9a87SYuri Pankov 	}
1604*260e9a87SYuri Pankov 	return(rr ? ROFF_CONT : ROFF_IGN);
160595c635efSGarrett D'Amore }
160695c635efSGarrett D'Amore 
160795c635efSGarrett D'Amore static enum rofferr
160895c635efSGarrett D'Amore roff_cond_text(ROFF_ARGS)
160995c635efSGarrett D'Amore {
161095c635efSGarrett D'Amore 	char		*ep;
1611*260e9a87SYuri Pankov 	int		 rr;
161295c635efSGarrett D'Amore 
161395c635efSGarrett D'Amore 	rr = r->last->rule;
161495c635efSGarrett D'Amore 	roffnode_cleanscope(r);
161595c635efSGarrett D'Amore 
1616*260e9a87SYuri Pankov 	ep = buf->buf + pos;
1617*260e9a87SYuri Pankov 	while ((ep = strchr(ep, '\\')) != NULL) {
1618*260e9a87SYuri Pankov 		if (*(++ep) == '}') {
161995c635efSGarrett D'Amore 			*ep = '&';
1620*260e9a87SYuri Pankov 			roff_ccond(r, ln, ep - buf->buf - 1);
162195c635efSGarrett D'Amore 		}
1622*260e9a87SYuri Pankov 		if (*ep != '\0')
1623*260e9a87SYuri Pankov 			++ep;
1624*260e9a87SYuri Pankov 	}
1625*260e9a87SYuri Pankov 	return(rr ? ROFF_CONT : ROFF_IGN);
162695c635efSGarrett D'Amore }
162795c635efSGarrett D'Amore 
1628*260e9a87SYuri Pankov /*
1629*260e9a87SYuri Pankov  * Parse a single signed integer number.  Stop at the first non-digit.
1630*260e9a87SYuri Pankov  * If there is at least one digit, return success and advance the
1631*260e9a87SYuri Pankov  * parse point, else return failure and let the parse point unchanged.
1632*260e9a87SYuri Pankov  * Ignore overflows, treat them just like the C language.
1633*260e9a87SYuri Pankov  */
1634698f87a4SGarrett D'Amore static int
1635*260e9a87SYuri Pankov roff_getnum(const char *v, int *pos, int *res, int flags)
1636698f87a4SGarrett D'Amore {
1637*260e9a87SYuri Pankov 	int	 myres, scaled, n, p;
1638*260e9a87SYuri Pankov 
1639*260e9a87SYuri Pankov 	if (NULL == res)
1640*260e9a87SYuri Pankov 		res = &myres;
1641698f87a4SGarrett D'Amore 
1642698f87a4SGarrett D'Amore 	p = *pos;
1643698f87a4SGarrett D'Amore 	n = v[p] == '-';
1644*260e9a87SYuri Pankov 	if (n || v[p] == '+')
1645*260e9a87SYuri Pankov 		p++;
1646*260e9a87SYuri Pankov 
1647*260e9a87SYuri Pankov 	if (flags & ROFFNUM_WHITE)
1648*260e9a87SYuri Pankov 		while (isspace((unsigned char)v[p]))
1649698f87a4SGarrett D'Amore 			p++;
1650698f87a4SGarrett D'Amore 
1651698f87a4SGarrett D'Amore 	for (*res = 0; isdigit((unsigned char)v[p]); p++)
1652*260e9a87SYuri Pankov 		*res = 10 * *res + v[p] - '0';
1653698f87a4SGarrett D'Amore 	if (p == *pos + n)
1654698f87a4SGarrett D'Amore 		return 0;
1655698f87a4SGarrett D'Amore 
1656698f87a4SGarrett D'Amore 	if (n)
1657698f87a4SGarrett D'Amore 		*res = -*res;
1658698f87a4SGarrett D'Amore 
1659*260e9a87SYuri Pankov 	/* Each number may be followed by one optional scaling unit. */
1660698f87a4SGarrett D'Amore 
1661*260e9a87SYuri Pankov 	switch (v[p]) {
1662*260e9a87SYuri Pankov 	case 'f':
1663*260e9a87SYuri Pankov 		scaled = *res * 65536;
1664698f87a4SGarrett D'Amore 		break;
1665*260e9a87SYuri Pankov 	case 'i':
1666*260e9a87SYuri Pankov 		scaled = *res * 240;
1667698f87a4SGarrett D'Amore 		break;
1668*260e9a87SYuri Pankov 	case 'c':
1669*260e9a87SYuri Pankov 		scaled = *res * 240 / 2.54;
1670*260e9a87SYuri Pankov 		break;
1671*260e9a87SYuri Pankov 	case 'v':
1672*260e9a87SYuri Pankov 		/* FALLTROUGH */
1673*260e9a87SYuri Pankov 	case 'P':
1674*260e9a87SYuri Pankov 		scaled = *res * 40;
1675*260e9a87SYuri Pankov 		break;
1676*260e9a87SYuri Pankov 	case 'm':
1677*260e9a87SYuri Pankov 		/* FALLTROUGH */
1678*260e9a87SYuri Pankov 	case 'n':
1679*260e9a87SYuri Pankov 		scaled = *res * 24;
1680*260e9a87SYuri Pankov 		break;
1681*260e9a87SYuri Pankov 	case 'p':
1682*260e9a87SYuri Pankov 		scaled = *res * 10 / 3;
1683*260e9a87SYuri Pankov 		break;
1684*260e9a87SYuri Pankov 	case 'u':
1685*260e9a87SYuri Pankov 		scaled = *res;
1686*260e9a87SYuri Pankov 		break;
1687*260e9a87SYuri Pankov 	case 'M':
1688*260e9a87SYuri Pankov 		scaled = *res * 6 / 25;
1689698f87a4SGarrett D'Amore 		break;
1690698f87a4SGarrett D'Amore 	default:
1691*260e9a87SYuri Pankov 		scaled = *res;
1692*260e9a87SYuri Pankov 		p--;
1693*260e9a87SYuri Pankov 		break;
1694*260e9a87SYuri Pankov 	}
1695*260e9a87SYuri Pankov 	if (flags & ROFFNUM_SCALE)
1696*260e9a87SYuri Pankov 		*res = scaled;
1697*260e9a87SYuri Pankov 
1698*260e9a87SYuri Pankov 	*pos = p + 1;
1699*260e9a87SYuri Pankov 	return(1);
1700*260e9a87SYuri Pankov }
1701*260e9a87SYuri Pankov 
1702*260e9a87SYuri Pankov /*
1703*260e9a87SYuri Pankov  * Evaluate a string comparison condition.
1704*260e9a87SYuri Pankov  * The first character is the delimiter.
1705*260e9a87SYuri Pankov  * Succeed if the string up to its second occurrence
1706*260e9a87SYuri Pankov  * matches the string up to its third occurence.
1707*260e9a87SYuri Pankov  * Advance the cursor after the third occurrence
1708*260e9a87SYuri Pankov  * or lacking that, to the end of the line.
1709*260e9a87SYuri Pankov  */
1710*260e9a87SYuri Pankov static int
1711*260e9a87SYuri Pankov roff_evalstrcond(const char *v, int *pos)
1712*260e9a87SYuri Pankov {
1713*260e9a87SYuri Pankov 	const char	*s1, *s2, *s3;
1714*260e9a87SYuri Pankov 	int		 match;
1715*260e9a87SYuri Pankov 
1716*260e9a87SYuri Pankov 	match = 0;
1717*260e9a87SYuri Pankov 	s1 = v + *pos;		/* initial delimiter */
1718*260e9a87SYuri Pankov 	s2 = s1 + 1;		/* for scanning the first string */
1719*260e9a87SYuri Pankov 	s3 = strchr(s2, *s1);	/* for scanning the second string */
1720*260e9a87SYuri Pankov 
1721*260e9a87SYuri Pankov 	if (NULL == s3)		/* found no middle delimiter */
1722*260e9a87SYuri Pankov 		goto out;
1723*260e9a87SYuri Pankov 
1724*260e9a87SYuri Pankov 	while ('\0' != *++s3) {
1725*260e9a87SYuri Pankov 		if (*s2 != *s3) {  /* mismatch */
1726*260e9a87SYuri Pankov 			s3 = strchr(s3, *s1);
1727*260e9a87SYuri Pankov 			break;
1728*260e9a87SYuri Pankov 		}
1729*260e9a87SYuri Pankov 		if (*s3 == *s1) {  /* found the final delimiter */
1730*260e9a87SYuri Pankov 			match = 1;
1731*260e9a87SYuri Pankov 			break;
1732*260e9a87SYuri Pankov 		}
1733*260e9a87SYuri Pankov 		s2++;
1734*260e9a87SYuri Pankov 	}
1735*260e9a87SYuri Pankov 
1736*260e9a87SYuri Pankov out:
1737*260e9a87SYuri Pankov 	if (NULL == s3)
1738*260e9a87SYuri Pankov 		s3 = strchr(s2, '\0');
1739*260e9a87SYuri Pankov 	else if (*s3 != '\0')
1740*260e9a87SYuri Pankov 		s3++;
1741*260e9a87SYuri Pankov 	*pos = s3 - v;
1742*260e9a87SYuri Pankov 	return(match);
1743*260e9a87SYuri Pankov }
1744*260e9a87SYuri Pankov 
1745*260e9a87SYuri Pankov /*
1746*260e9a87SYuri Pankov  * Evaluate an optionally negated single character, numerical,
1747*260e9a87SYuri Pankov  * or string condition.
1748*260e9a87SYuri Pankov  */
1749*260e9a87SYuri Pankov static int
1750*260e9a87SYuri Pankov roff_evalcond(struct roff *r, int ln, const char *v, int *pos)
1751*260e9a87SYuri Pankov {
1752*260e9a87SYuri Pankov 	int	 number, savepos, wanttrue;
1753*260e9a87SYuri Pankov 
1754*260e9a87SYuri Pankov 	if ('!' == v[*pos]) {
1755*260e9a87SYuri Pankov 		wanttrue = 0;
1756*260e9a87SYuri Pankov 		(*pos)++;
1757*260e9a87SYuri Pankov 	} else
1758*260e9a87SYuri Pankov 		wanttrue = 1;
1759*260e9a87SYuri Pankov 
1760*260e9a87SYuri Pankov 	switch (v[*pos]) {
1761*260e9a87SYuri Pankov 	case '\0':
1762*260e9a87SYuri Pankov 		return(0);
1763*260e9a87SYuri Pankov 	case 'n':
1764*260e9a87SYuri Pankov 		/* FALLTHROUGH */
1765*260e9a87SYuri Pankov 	case 'o':
1766*260e9a87SYuri Pankov 		(*pos)++;
1767*260e9a87SYuri Pankov 		return(wanttrue);
1768*260e9a87SYuri Pankov 	case 'c':
1769*260e9a87SYuri Pankov 		/* FALLTHROUGH */
1770*260e9a87SYuri Pankov 	case 'd':
1771*260e9a87SYuri Pankov 		/* FALLTHROUGH */
1772*260e9a87SYuri Pankov 	case 'e':
1773*260e9a87SYuri Pankov 		/* FALLTHROUGH */
1774*260e9a87SYuri Pankov 	case 'r':
1775*260e9a87SYuri Pankov 		/* FALLTHROUGH */
1776*260e9a87SYuri Pankov 	case 't':
1777*260e9a87SYuri Pankov 		/* FALLTHROUGH */
1778*260e9a87SYuri Pankov 	case 'v':
1779*260e9a87SYuri Pankov 		(*pos)++;
1780*260e9a87SYuri Pankov 		return(!wanttrue);
1781*260e9a87SYuri Pankov 	default:
1782*260e9a87SYuri Pankov 		break;
1783*260e9a87SYuri Pankov 	}
1784*260e9a87SYuri Pankov 
1785*260e9a87SYuri Pankov 	savepos = *pos;
1786*260e9a87SYuri Pankov 	if (roff_evalnum(r, ln, v, pos, &number, ROFFNUM_SCALE))
1787*260e9a87SYuri Pankov 		return((number > 0) == wanttrue);
1788*260e9a87SYuri Pankov 	else if (*pos == savepos)
1789*260e9a87SYuri Pankov 		return(roff_evalstrcond(v, pos) == wanttrue);
1790*260e9a87SYuri Pankov 	else
1791698f87a4SGarrett D'Amore 		return (0);
1792698f87a4SGarrett D'Amore }
1793698f87a4SGarrett D'Amore 
179495c635efSGarrett D'Amore static enum rofferr
179595c635efSGarrett D'Amore roff_line_ignore(ROFF_ARGS)
179695c635efSGarrett D'Amore {
179795c635efSGarrett D'Amore 
179895c635efSGarrett D'Amore 	return(ROFF_IGN);
179995c635efSGarrett D'Amore }
180095c635efSGarrett D'Amore 
1801*260e9a87SYuri Pankov static enum rofferr
1802*260e9a87SYuri Pankov roff_insec(ROFF_ARGS)
1803*260e9a87SYuri Pankov {
1804*260e9a87SYuri Pankov 
1805*260e9a87SYuri Pankov 	mandoc_msg(MANDOCERR_REQ_INSEC, r->parse,
1806*260e9a87SYuri Pankov 	    ln, ppos, roffs[tok].name);
1807*260e9a87SYuri Pankov 	return(ROFF_IGN);
1808*260e9a87SYuri Pankov }
1809*260e9a87SYuri Pankov 
1810*260e9a87SYuri Pankov static enum rofferr
1811*260e9a87SYuri Pankov roff_unsupp(ROFF_ARGS)
1812*260e9a87SYuri Pankov {
1813*260e9a87SYuri Pankov 
1814*260e9a87SYuri Pankov 	mandoc_msg(MANDOCERR_REQ_UNSUPP, r->parse,
1815*260e9a87SYuri Pankov 	    ln, ppos, roffs[tok].name);
1816*260e9a87SYuri Pankov 	return(ROFF_IGN);
1817*260e9a87SYuri Pankov }
1818*260e9a87SYuri Pankov 
181995c635efSGarrett D'Amore static enum rofferr
182095c635efSGarrett D'Amore roff_cond(ROFF_ARGS)
182195c635efSGarrett D'Amore {
1822698f87a4SGarrett D'Amore 
1823698f87a4SGarrett D'Amore 	roffnode_push(r, tok, NULL, ln, ppos);
182495c635efSGarrett D'Amore 
182595c635efSGarrett D'Amore 	/*
182695c635efSGarrett D'Amore 	 * An `.el' has no conditional body: it will consume the value
182795c635efSGarrett D'Amore 	 * of the current rstack entry set in prior `ie' calls or
182895c635efSGarrett D'Amore 	 * defaults to DENY.
182995c635efSGarrett D'Amore 	 *
183095c635efSGarrett D'Amore 	 * If we're not an `el', however, then evaluate the conditional.
183195c635efSGarrett D'Amore 	 */
183295c635efSGarrett D'Amore 
1833*260e9a87SYuri Pankov 	r->last->rule = tok == ROFF_el ?
1834*260e9a87SYuri Pankov 	    (r->rstackpos < 0 ? 0 : r->rstack[r->rstackpos--]) :
1835*260e9a87SYuri Pankov 	    roff_evalcond(r, ln, buf->buf, &pos);
183695c635efSGarrett D'Amore 
183795c635efSGarrett D'Amore 	/*
183895c635efSGarrett D'Amore 	 * An if-else will put the NEGATION of the current evaluated
183995c635efSGarrett D'Amore 	 * conditional into the stack of rules.
184095c635efSGarrett D'Amore 	 */
184195c635efSGarrett D'Amore 
1842*260e9a87SYuri Pankov 	if (tok == ROFF_ie) {
1843*260e9a87SYuri Pankov 		if (r->rstackpos + 1 == r->rstacksz) {
1844*260e9a87SYuri Pankov 			r->rstacksz += 16;
1845*260e9a87SYuri Pankov 			r->rstack = mandoc_reallocarray(r->rstack,
1846*260e9a87SYuri Pankov 			    r->rstacksz, sizeof(int));
184795c635efSGarrett D'Amore 		}
1848*260e9a87SYuri Pankov 		r->rstack[++r->rstackpos] = !r->last->rule;
184995c635efSGarrett D'Amore 	}
185095c635efSGarrett D'Amore 
185195c635efSGarrett D'Amore 	/* If the parent has false as its rule, then so do we. */
185295c635efSGarrett D'Amore 
1853*260e9a87SYuri Pankov 	if (r->last->parent && !r->last->parent->rule)
1854*260e9a87SYuri Pankov 		r->last->rule = 0;
185595c635efSGarrett D'Amore 
185695c635efSGarrett D'Amore 	/*
1857698f87a4SGarrett D'Amore 	 * Determine scope.
1858698f87a4SGarrett D'Amore 	 * If there is nothing on the line after the conditional,
1859698f87a4SGarrett D'Amore 	 * not even whitespace, use next-line scope.
186095c635efSGarrett D'Amore 	 */
186195c635efSGarrett D'Amore 
1862*260e9a87SYuri Pankov 	if (buf->buf[pos] == '\0') {
1863698f87a4SGarrett D'Amore 		r->last->endspan = 2;
1864698f87a4SGarrett D'Amore 		goto out;
1865698f87a4SGarrett D'Amore 	}
1866698f87a4SGarrett D'Amore 
1867*260e9a87SYuri Pankov 	while (buf->buf[pos] == ' ')
1868698f87a4SGarrett D'Amore 		pos++;
1869698f87a4SGarrett D'Amore 
1870698f87a4SGarrett D'Amore 	/* An opening brace requests multiline scope. */
187195c635efSGarrett D'Amore 
1872*260e9a87SYuri Pankov 	if (buf->buf[pos] == '\\' && buf->buf[pos + 1] == '{') {
187395c635efSGarrett D'Amore 		r->last->endspan = -1;
187495c635efSGarrett D'Amore 		pos += 2;
1875698f87a4SGarrett D'Amore 		goto out;
187695c635efSGarrett D'Amore 	}
187795c635efSGarrett D'Amore 
187895c635efSGarrett D'Amore 	/*
1879698f87a4SGarrett D'Amore 	 * Anything else following the conditional causes
1880698f87a4SGarrett D'Amore 	 * single-line scope.  Warn if the scope contains
1881698f87a4SGarrett D'Amore 	 * nothing but trailing whitespace.
188295c635efSGarrett D'Amore 	 */
188395c635efSGarrett D'Amore 
1884*260e9a87SYuri Pankov 	if (buf->buf[pos] == '\0')
1885*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_COND_EMPTY, r->parse,
1886*260e9a87SYuri Pankov 		    ln, ppos, roffs[tok].name);
188795c635efSGarrett D'Amore 
1888698f87a4SGarrett D'Amore 	r->last->endspan = 1;
188995c635efSGarrett D'Amore 
1890698f87a4SGarrett D'Amore out:
189195c635efSGarrett D'Amore 	*offs = pos;
189295c635efSGarrett D'Amore 	return(ROFF_RERUN);
189395c635efSGarrett D'Amore }
189495c635efSGarrett D'Amore 
189595c635efSGarrett D'Amore static enum rofferr
189695c635efSGarrett D'Amore roff_ds(ROFF_ARGS)
189795c635efSGarrett D'Amore {
1898*260e9a87SYuri Pankov 	char		*string;
1899*260e9a87SYuri Pankov 	const char	*name;
1900*260e9a87SYuri Pankov 	size_t		 namesz;
1901*260e9a87SYuri Pankov 
1902*260e9a87SYuri Pankov 	/* Ignore groff compatibility mode for now. */
1903*260e9a87SYuri Pankov 
1904*260e9a87SYuri Pankov 	if (tok == ROFF_ds1)
1905*260e9a87SYuri Pankov 		tok = ROFF_ds;
1906*260e9a87SYuri Pankov 	else if (tok == ROFF_as1)
1907*260e9a87SYuri Pankov 		tok = ROFF_as;
190895c635efSGarrett D'Amore 
190995c635efSGarrett D'Amore 	/*
1910*260e9a87SYuri Pankov 	 * The first word is the name of the string.
1911*260e9a87SYuri Pankov 	 * If it is empty or terminated by an escape sequence,
1912*260e9a87SYuri Pankov 	 * abort the `ds' request without defining anything.
191395c635efSGarrett D'Amore 	 */
191495c635efSGarrett D'Amore 
1915*260e9a87SYuri Pankov 	name = string = buf->buf + pos;
1916*260e9a87SYuri Pankov 	if (*name == '\0')
191795c635efSGarrett D'Amore 		return(ROFF_IGN);
191895c635efSGarrett D'Amore 
1919*260e9a87SYuri Pankov 	namesz = roff_getname(r, &string, ln, pos);
1920*260e9a87SYuri Pankov 	if (name[namesz] == '\\')
1921*260e9a87SYuri Pankov 		return(ROFF_IGN);
1922*260e9a87SYuri Pankov 
1923*260e9a87SYuri Pankov 	/* Read past the initial double-quote, if any. */
1924*260e9a87SYuri Pankov 	if (*string == '"')
192595c635efSGarrett D'Amore 		string++;
192695c635efSGarrett D'Amore 
192795c635efSGarrett D'Amore 	/* The rest is the value. */
1928*260e9a87SYuri Pankov 	roff_setstrn(&r->strtab, name, namesz, string, strlen(string),
1929*260e9a87SYuri Pankov 	    ROFF_as == tok);
193095c635efSGarrett D'Amore 	return(ROFF_IGN);
193195c635efSGarrett D'Amore }
193295c635efSGarrett D'Amore 
1933*260e9a87SYuri Pankov /*
1934*260e9a87SYuri Pankov  * Parse a single operator, one or two characters long.
1935*260e9a87SYuri Pankov  * If the operator is recognized, return success and advance the
1936*260e9a87SYuri Pankov  * parse point, else return failure and let the parse point unchanged.
1937*260e9a87SYuri Pankov  */
1938*260e9a87SYuri Pankov static int
1939*260e9a87SYuri Pankov roff_getop(const char *v, int *pos, char *res)
1940*260e9a87SYuri Pankov {
1941*260e9a87SYuri Pankov 
1942*260e9a87SYuri Pankov 	*res = v[*pos];
1943*260e9a87SYuri Pankov 
1944*260e9a87SYuri Pankov 	switch (*res) {
1945*260e9a87SYuri Pankov 	case '+':
1946*260e9a87SYuri Pankov 		/* FALLTHROUGH */
1947*260e9a87SYuri Pankov 	case '-':
1948*260e9a87SYuri Pankov 		/* FALLTHROUGH */
1949*260e9a87SYuri Pankov 	case '*':
1950*260e9a87SYuri Pankov 		/* FALLTHROUGH */
1951*260e9a87SYuri Pankov 	case '/':
1952*260e9a87SYuri Pankov 		/* FALLTHROUGH */
1953*260e9a87SYuri Pankov 	case '%':
1954*260e9a87SYuri Pankov 		/* FALLTHROUGH */
1955*260e9a87SYuri Pankov 	case '&':
1956*260e9a87SYuri Pankov 		/* FALLTHROUGH */
1957*260e9a87SYuri Pankov 	case ':':
1958*260e9a87SYuri Pankov 		break;
1959*260e9a87SYuri Pankov 	case '<':
1960*260e9a87SYuri Pankov 		switch (v[*pos + 1]) {
1961*260e9a87SYuri Pankov 		case '=':
1962*260e9a87SYuri Pankov 			*res = 'l';
1963*260e9a87SYuri Pankov 			(*pos)++;
1964*260e9a87SYuri Pankov 			break;
1965*260e9a87SYuri Pankov 		case '>':
1966*260e9a87SYuri Pankov 			*res = '!';
1967*260e9a87SYuri Pankov 			(*pos)++;
1968*260e9a87SYuri Pankov 			break;
1969*260e9a87SYuri Pankov 		case '?':
1970*260e9a87SYuri Pankov 			*res = 'i';
1971*260e9a87SYuri Pankov 			(*pos)++;
1972*260e9a87SYuri Pankov 			break;
1973*260e9a87SYuri Pankov 		default:
1974*260e9a87SYuri Pankov 			break;
1975*260e9a87SYuri Pankov 		}
1976*260e9a87SYuri Pankov 		break;
1977*260e9a87SYuri Pankov 	case '>':
1978*260e9a87SYuri Pankov 		switch (v[*pos + 1]) {
1979*260e9a87SYuri Pankov 		case '=':
1980*260e9a87SYuri Pankov 			*res = 'g';
1981*260e9a87SYuri Pankov 			(*pos)++;
1982*260e9a87SYuri Pankov 			break;
1983*260e9a87SYuri Pankov 		case '?':
1984*260e9a87SYuri Pankov 			*res = 'a';
1985*260e9a87SYuri Pankov 			(*pos)++;
1986*260e9a87SYuri Pankov 			break;
1987*260e9a87SYuri Pankov 		default:
1988*260e9a87SYuri Pankov 			break;
1989*260e9a87SYuri Pankov 		}
1990*260e9a87SYuri Pankov 		break;
1991*260e9a87SYuri Pankov 	case '=':
1992*260e9a87SYuri Pankov 		if ('=' == v[*pos + 1])
1993*260e9a87SYuri Pankov 			(*pos)++;
1994*260e9a87SYuri Pankov 		break;
1995*260e9a87SYuri Pankov 	default:
1996*260e9a87SYuri Pankov 		return(0);
1997*260e9a87SYuri Pankov 	}
1998*260e9a87SYuri Pankov 	(*pos)++;
1999*260e9a87SYuri Pankov 
2000*260e9a87SYuri Pankov 	return(*res);
2001*260e9a87SYuri Pankov }
2002*260e9a87SYuri Pankov 
2003*260e9a87SYuri Pankov /*
2004*260e9a87SYuri Pankov  * Evaluate either a parenthesized numeric expression
2005*260e9a87SYuri Pankov  * or a single signed integer number.
2006*260e9a87SYuri Pankov  */
2007*260e9a87SYuri Pankov static int
2008*260e9a87SYuri Pankov roff_evalpar(struct roff *r, int ln,
2009*260e9a87SYuri Pankov 	const char *v, int *pos, int *res, int flags)
2010*260e9a87SYuri Pankov {
2011*260e9a87SYuri Pankov 
2012*260e9a87SYuri Pankov 	if ('(' != v[*pos])
2013*260e9a87SYuri Pankov 		return(roff_getnum(v, pos, res, flags));
2014*260e9a87SYuri Pankov 
2015*260e9a87SYuri Pankov 	(*pos)++;
2016*260e9a87SYuri Pankov 	if ( ! roff_evalnum(r, ln, v, pos, res, flags | ROFFNUM_WHITE))
2017*260e9a87SYuri Pankov 		return(0);
2018*260e9a87SYuri Pankov 
2019*260e9a87SYuri Pankov 	/*
2020*260e9a87SYuri Pankov 	 * Omission of the closing parenthesis
2021*260e9a87SYuri Pankov 	 * is an error in validation mode,
2022*260e9a87SYuri Pankov 	 * but ignored in evaluation mode.
2023*260e9a87SYuri Pankov 	 */
2024*260e9a87SYuri Pankov 
2025*260e9a87SYuri Pankov 	if (')' == v[*pos])
2026*260e9a87SYuri Pankov 		(*pos)++;
2027*260e9a87SYuri Pankov 	else if (NULL == res)
2028*260e9a87SYuri Pankov 		return(0);
2029*260e9a87SYuri Pankov 
2030*260e9a87SYuri Pankov 	return(1);
2031*260e9a87SYuri Pankov }
2032*260e9a87SYuri Pankov 
2033*260e9a87SYuri Pankov /*
2034*260e9a87SYuri Pankov  * Evaluate a complete numeric expression.
2035*260e9a87SYuri Pankov  * Proceed left to right, there is no concept of precedence.
2036*260e9a87SYuri Pankov  */
2037*260e9a87SYuri Pankov static int
2038*260e9a87SYuri Pankov roff_evalnum(struct roff *r, int ln, const char *v,
2039*260e9a87SYuri Pankov 	int *pos, int *res, int flags)
2040*260e9a87SYuri Pankov {
2041*260e9a87SYuri Pankov 	int		 mypos, operand2;
2042*260e9a87SYuri Pankov 	char		 operator;
2043*260e9a87SYuri Pankov 
2044*260e9a87SYuri Pankov 	if (NULL == pos) {
2045*260e9a87SYuri Pankov 		mypos = 0;
2046*260e9a87SYuri Pankov 		pos = &mypos;
2047*260e9a87SYuri Pankov 	}
2048*260e9a87SYuri Pankov 
2049*260e9a87SYuri Pankov 	if (flags & ROFFNUM_WHITE)
2050*260e9a87SYuri Pankov 		while (isspace((unsigned char)v[*pos]))
2051*260e9a87SYuri Pankov 			(*pos)++;
2052*260e9a87SYuri Pankov 
2053*260e9a87SYuri Pankov 	if ( ! roff_evalpar(r, ln, v, pos, res, flags))
2054*260e9a87SYuri Pankov 		return(0);
2055*260e9a87SYuri Pankov 
2056*260e9a87SYuri Pankov 	while (1) {
2057*260e9a87SYuri Pankov 		if (flags & ROFFNUM_WHITE)
2058*260e9a87SYuri Pankov 			while (isspace((unsigned char)v[*pos]))
2059*260e9a87SYuri Pankov 				(*pos)++;
2060*260e9a87SYuri Pankov 
2061*260e9a87SYuri Pankov 		if ( ! roff_getop(v, pos, &operator))
2062*260e9a87SYuri Pankov 			break;
2063*260e9a87SYuri Pankov 
2064*260e9a87SYuri Pankov 		if (flags & ROFFNUM_WHITE)
2065*260e9a87SYuri Pankov 			while (isspace((unsigned char)v[*pos]))
2066*260e9a87SYuri Pankov 				(*pos)++;
2067*260e9a87SYuri Pankov 
2068*260e9a87SYuri Pankov 		if ( ! roff_evalpar(r, ln, v, pos, &operand2, flags))
2069*260e9a87SYuri Pankov 			return(0);
2070*260e9a87SYuri Pankov 
2071*260e9a87SYuri Pankov 		if (flags & ROFFNUM_WHITE)
2072*260e9a87SYuri Pankov 			while (isspace((unsigned char)v[*pos]))
2073*260e9a87SYuri Pankov 				(*pos)++;
2074*260e9a87SYuri Pankov 
2075*260e9a87SYuri Pankov 		if (NULL == res)
2076*260e9a87SYuri Pankov 			continue;
2077*260e9a87SYuri Pankov 
2078*260e9a87SYuri Pankov 		switch (operator) {
2079*260e9a87SYuri Pankov 		case '+':
2080*260e9a87SYuri Pankov 			*res += operand2;
2081*260e9a87SYuri Pankov 			break;
2082*260e9a87SYuri Pankov 		case '-':
2083*260e9a87SYuri Pankov 			*res -= operand2;
2084*260e9a87SYuri Pankov 			break;
2085*260e9a87SYuri Pankov 		case '*':
2086*260e9a87SYuri Pankov 			*res *= operand2;
2087*260e9a87SYuri Pankov 			break;
2088*260e9a87SYuri Pankov 		case '/':
2089*260e9a87SYuri Pankov 			if (operand2 == 0) {
2090*260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_DIVZERO,
2091*260e9a87SYuri Pankov 					r->parse, ln, *pos, v);
2092*260e9a87SYuri Pankov 				*res = 0;
2093*260e9a87SYuri Pankov 				break;
2094*260e9a87SYuri Pankov 			}
2095*260e9a87SYuri Pankov 			*res /= operand2;
2096*260e9a87SYuri Pankov 			break;
2097*260e9a87SYuri Pankov 		case '%':
2098*260e9a87SYuri Pankov 			if (operand2 == 0) {
2099*260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_DIVZERO,
2100*260e9a87SYuri Pankov 					r->parse, ln, *pos, v);
2101*260e9a87SYuri Pankov 				*res = 0;
2102*260e9a87SYuri Pankov 				break;
2103*260e9a87SYuri Pankov 			}
2104*260e9a87SYuri Pankov 			*res %= operand2;
2105*260e9a87SYuri Pankov 			break;
2106*260e9a87SYuri Pankov 		case '<':
2107*260e9a87SYuri Pankov 			*res = *res < operand2;
2108*260e9a87SYuri Pankov 			break;
2109*260e9a87SYuri Pankov 		case '>':
2110*260e9a87SYuri Pankov 			*res = *res > operand2;
2111*260e9a87SYuri Pankov 			break;
2112*260e9a87SYuri Pankov 		case 'l':
2113*260e9a87SYuri Pankov 			*res = *res <= operand2;
2114*260e9a87SYuri Pankov 			break;
2115*260e9a87SYuri Pankov 		case 'g':
2116*260e9a87SYuri Pankov 			*res = *res >= operand2;
2117*260e9a87SYuri Pankov 			break;
2118*260e9a87SYuri Pankov 		case '=':
2119*260e9a87SYuri Pankov 			*res = *res == operand2;
2120*260e9a87SYuri Pankov 			break;
2121*260e9a87SYuri Pankov 		case '!':
2122*260e9a87SYuri Pankov 			*res = *res != operand2;
2123*260e9a87SYuri Pankov 			break;
2124*260e9a87SYuri Pankov 		case '&':
2125*260e9a87SYuri Pankov 			*res = *res && operand2;
2126*260e9a87SYuri Pankov 			break;
2127*260e9a87SYuri Pankov 		case ':':
2128*260e9a87SYuri Pankov 			*res = *res || operand2;
2129*260e9a87SYuri Pankov 			break;
2130*260e9a87SYuri Pankov 		case 'i':
2131*260e9a87SYuri Pankov 			if (operand2 < *res)
2132*260e9a87SYuri Pankov 				*res = operand2;
2133*260e9a87SYuri Pankov 			break;
2134*260e9a87SYuri Pankov 		case 'a':
2135*260e9a87SYuri Pankov 			if (operand2 > *res)
2136*260e9a87SYuri Pankov 				*res = operand2;
2137*260e9a87SYuri Pankov 			break;
2138*260e9a87SYuri Pankov 		default:
2139*260e9a87SYuri Pankov 			abort();
2140*260e9a87SYuri Pankov 		}
2141*260e9a87SYuri Pankov 	}
2142*260e9a87SYuri Pankov 	return(1);
2143*260e9a87SYuri Pankov }
2144*260e9a87SYuri Pankov 
214595c635efSGarrett D'Amore void
2146698f87a4SGarrett D'Amore roff_setreg(struct roff *r, const char *name, int val, char sign)
214795c635efSGarrett D'Amore {
2148698f87a4SGarrett D'Amore 	struct roffreg	*reg;
214995c635efSGarrett D'Amore 
2150698f87a4SGarrett D'Amore 	/* Search for an existing register with the same name. */
2151698f87a4SGarrett D'Amore 	reg = r->regtab;
2152698f87a4SGarrett D'Amore 
2153698f87a4SGarrett D'Amore 	while (reg && strcmp(name, reg->key.p))
2154698f87a4SGarrett D'Amore 		reg = reg->next;
2155698f87a4SGarrett D'Amore 
2156698f87a4SGarrett D'Amore 	if (NULL == reg) {
2157698f87a4SGarrett D'Amore 		/* Create a new register. */
2158698f87a4SGarrett D'Amore 		reg = mandoc_malloc(sizeof(struct roffreg));
2159698f87a4SGarrett D'Amore 		reg->key.p = mandoc_strdup(name);
2160698f87a4SGarrett D'Amore 		reg->key.sz = strlen(name);
2161698f87a4SGarrett D'Amore 		reg->val = 0;
2162698f87a4SGarrett D'Amore 		reg->next = r->regtab;
2163698f87a4SGarrett D'Amore 		r->regtab = reg;
2164698f87a4SGarrett D'Amore 	}
2165698f87a4SGarrett D'Amore 
2166698f87a4SGarrett D'Amore 	if ('+' == sign)
2167698f87a4SGarrett D'Amore 		reg->val += val;
2168698f87a4SGarrett D'Amore 	else if ('-' == sign)
2169698f87a4SGarrett D'Amore 		reg->val -= val;
2170698f87a4SGarrett D'Amore 	else
2171698f87a4SGarrett D'Amore 		reg->val = val;
2172698f87a4SGarrett D'Amore }
2173698f87a4SGarrett D'Amore 
2174*260e9a87SYuri Pankov /*
2175*260e9a87SYuri Pankov  * Handle some predefined read-only number registers.
2176*260e9a87SYuri Pankov  * For now, return -1 if the requested register is not predefined;
2177*260e9a87SYuri Pankov  * in case a predefined read-only register having the value -1
2178*260e9a87SYuri Pankov  * were to turn up, another special value would have to be chosen.
2179*260e9a87SYuri Pankov  */
2180*260e9a87SYuri Pankov static int
2181*260e9a87SYuri Pankov roff_getregro(const char *name)
2182*260e9a87SYuri Pankov {
2183*260e9a87SYuri Pankov 
2184*260e9a87SYuri Pankov 	switch (*name) {
2185*260e9a87SYuri Pankov 	case 'A':  /* ASCII approximation mode is always off. */
2186*260e9a87SYuri Pankov 		return(0);
2187*260e9a87SYuri Pankov 	case 'g':  /* Groff compatibility mode is always on. */
2188*260e9a87SYuri Pankov 		return(1);
2189*260e9a87SYuri Pankov 	case 'H':  /* Fixed horizontal resolution. */
2190*260e9a87SYuri Pankov 		return (24);
2191*260e9a87SYuri Pankov 	case 'j':  /* Always adjust left margin only. */
2192*260e9a87SYuri Pankov 		return(0);
2193*260e9a87SYuri Pankov 	case 'T':  /* Some output device is always defined. */
2194*260e9a87SYuri Pankov 		return(1);
2195*260e9a87SYuri Pankov 	case 'V':  /* Fixed vertical resolution. */
2196*260e9a87SYuri Pankov 		return (40);
2197*260e9a87SYuri Pankov 	default:
2198*260e9a87SYuri Pankov 		return (-1);
2199*260e9a87SYuri Pankov 	}
2200*260e9a87SYuri Pankov }
2201*260e9a87SYuri Pankov 
2202698f87a4SGarrett D'Amore int
2203698f87a4SGarrett D'Amore roff_getreg(const struct roff *r, const char *name)
2204698f87a4SGarrett D'Amore {
2205698f87a4SGarrett D'Amore 	struct roffreg	*reg;
2206*260e9a87SYuri Pankov 	int		 val;
2207*260e9a87SYuri Pankov 
2208*260e9a87SYuri Pankov 	if ('.' == name[0] && '\0' != name[1] && '\0' == name[2]) {
2209*260e9a87SYuri Pankov 		val = roff_getregro(name + 1);
2210*260e9a87SYuri Pankov 		if (-1 != val)
2211*260e9a87SYuri Pankov 			return (val);
2212*260e9a87SYuri Pankov 	}
2213698f87a4SGarrett D'Amore 
2214698f87a4SGarrett D'Amore 	for (reg = r->regtab; reg; reg = reg->next)
2215698f87a4SGarrett D'Amore 		if (0 == strcmp(name, reg->key.p))
2216698f87a4SGarrett D'Amore 			return(reg->val);
2217698f87a4SGarrett D'Amore 
2218698f87a4SGarrett D'Amore 	return(0);
2219698f87a4SGarrett D'Amore }
2220698f87a4SGarrett D'Amore 
2221698f87a4SGarrett D'Amore static int
2222698f87a4SGarrett D'Amore roff_getregn(const struct roff *r, const char *name, size_t len)
2223698f87a4SGarrett D'Amore {
2224698f87a4SGarrett D'Amore 	struct roffreg	*reg;
2225*260e9a87SYuri Pankov 	int		 val;
2226*260e9a87SYuri Pankov 
2227*260e9a87SYuri Pankov 	if ('.' == name[0] && 2 == len) {
2228*260e9a87SYuri Pankov 		val = roff_getregro(name + 1);
2229*260e9a87SYuri Pankov 		if (-1 != val)
2230*260e9a87SYuri Pankov 			return (val);
2231*260e9a87SYuri Pankov 	}
2232698f87a4SGarrett D'Amore 
2233698f87a4SGarrett D'Amore 	for (reg = r->regtab; reg; reg = reg->next)
2234698f87a4SGarrett D'Amore 		if (len == reg->key.sz &&
2235698f87a4SGarrett D'Amore 		    0 == strncmp(name, reg->key.p, len))
2236698f87a4SGarrett D'Amore 			return(reg->val);
2237698f87a4SGarrett D'Amore 
2238698f87a4SGarrett D'Amore 	return(0);
2239698f87a4SGarrett D'Amore }
2240698f87a4SGarrett D'Amore 
2241698f87a4SGarrett D'Amore static void
2242698f87a4SGarrett D'Amore roff_freereg(struct roffreg *reg)
2243698f87a4SGarrett D'Amore {
2244698f87a4SGarrett D'Amore 	struct roffreg	*old_reg;
2245698f87a4SGarrett D'Amore 
2246698f87a4SGarrett D'Amore 	while (NULL != reg) {
2247698f87a4SGarrett D'Amore 		free(reg->key.p);
2248698f87a4SGarrett D'Amore 		old_reg = reg;
2249698f87a4SGarrett D'Amore 		reg = reg->next;
2250698f87a4SGarrett D'Amore 		free(old_reg);
2251698f87a4SGarrett D'Amore 	}
225295c635efSGarrett D'Amore }
225395c635efSGarrett D'Amore 
225495c635efSGarrett D'Amore static enum rofferr
225595c635efSGarrett D'Amore roff_nr(ROFF_ARGS)
225695c635efSGarrett D'Amore {
2257*260e9a87SYuri Pankov 	char		*key, *val;
2258*260e9a87SYuri Pankov 	size_t		 keysz;
225995c635efSGarrett D'Amore 	int		 iv;
2260698f87a4SGarrett D'Amore 	char		 sign;
226195c635efSGarrett D'Amore 
2262*260e9a87SYuri Pankov 	key = val = buf->buf + pos;
2263*260e9a87SYuri Pankov 	if (*key == '\0')
2264*260e9a87SYuri Pankov 		return(ROFF_IGN);
2265*260e9a87SYuri Pankov 
2266*260e9a87SYuri Pankov 	keysz = roff_getname(r, &val, ln, pos);
2267*260e9a87SYuri Pankov 	if (key[keysz] == '\\')
2268*260e9a87SYuri Pankov 		return(ROFF_IGN);
2269*260e9a87SYuri Pankov 	key[keysz] = '\0';
227095c635efSGarrett D'Amore 
2271698f87a4SGarrett D'Amore 	sign = *val;
2272*260e9a87SYuri Pankov 	if (sign == '+' || sign == '-')
2273698f87a4SGarrett D'Amore 		val++;
2274698f87a4SGarrett D'Amore 
2275*260e9a87SYuri Pankov 	if (roff_evalnum(r, ln, val, NULL, &iv, ROFFNUM_SCALE))
2276698f87a4SGarrett D'Amore 		roff_setreg(r, key, iv, sign);
227795c635efSGarrett D'Amore 
227895c635efSGarrett D'Amore 	return(ROFF_IGN);
227995c635efSGarrett D'Amore }
228095c635efSGarrett D'Amore 
2281*260e9a87SYuri Pankov static enum rofferr
2282*260e9a87SYuri Pankov roff_rr(ROFF_ARGS)
2283*260e9a87SYuri Pankov {
2284*260e9a87SYuri Pankov 	struct roffreg	*reg, **prev;
2285*260e9a87SYuri Pankov 	char		*name, *cp;
2286*260e9a87SYuri Pankov 	size_t		 namesz;
2287*260e9a87SYuri Pankov 
2288*260e9a87SYuri Pankov 	name = cp = buf->buf + pos;
2289*260e9a87SYuri Pankov 	if (*name == '\0')
2290*260e9a87SYuri Pankov 		return(ROFF_IGN);
2291*260e9a87SYuri Pankov 	namesz = roff_getname(r, &cp, ln, pos);
2292*260e9a87SYuri Pankov 	name[namesz] = '\0';
2293*260e9a87SYuri Pankov 
2294*260e9a87SYuri Pankov 	prev = &r->regtab;
2295*260e9a87SYuri Pankov 	while (1) {
2296*260e9a87SYuri Pankov 		reg = *prev;
2297*260e9a87SYuri Pankov 		if (reg == NULL || !strcmp(name, reg->key.p))
2298*260e9a87SYuri Pankov 			break;
2299*260e9a87SYuri Pankov 		prev = &reg->next;
2300*260e9a87SYuri Pankov 	}
2301*260e9a87SYuri Pankov 	if (reg != NULL) {
2302*260e9a87SYuri Pankov 		*prev = reg->next;
2303*260e9a87SYuri Pankov 		free(reg->key.p);
2304*260e9a87SYuri Pankov 		free(reg);
2305*260e9a87SYuri Pankov 	}
2306*260e9a87SYuri Pankov 	return(ROFF_IGN);
2307*260e9a87SYuri Pankov }
2308*260e9a87SYuri Pankov 
230995c635efSGarrett D'Amore static enum rofferr
231095c635efSGarrett D'Amore roff_rm(ROFF_ARGS)
231195c635efSGarrett D'Amore {
231295c635efSGarrett D'Amore 	const char	 *name;
231395c635efSGarrett D'Amore 	char		 *cp;
2314*260e9a87SYuri Pankov 	size_t		  namesz;
231595c635efSGarrett D'Amore 
2316*260e9a87SYuri Pankov 	cp = buf->buf + pos;
2317*260e9a87SYuri Pankov 	while (*cp != '\0') {
2318*260e9a87SYuri Pankov 		name = cp;
2319*260e9a87SYuri Pankov 		namesz = roff_getname(r, &cp, ln, (int)(cp - buf->buf));
2320*260e9a87SYuri Pankov 		roff_setstrn(&r->strtab, name, namesz, NULL, 0, 0);
2321*260e9a87SYuri Pankov 		if (name[namesz] == '\\')
2322*260e9a87SYuri Pankov 			break;
232395c635efSGarrett D'Amore 	}
232495c635efSGarrett D'Amore 	return(ROFF_IGN);
232595c635efSGarrett D'Amore }
232695c635efSGarrett D'Amore 
232795c635efSGarrett D'Amore static enum rofferr
2328698f87a4SGarrett D'Amore roff_it(ROFF_ARGS)
2329698f87a4SGarrett D'Amore {
2330698f87a4SGarrett D'Amore 	int		 iv;
2331698f87a4SGarrett D'Amore 
2332698f87a4SGarrett D'Amore 	/* Parse the number of lines. */
2333*260e9a87SYuri Pankov 
2334*260e9a87SYuri Pankov 	if ( ! roff_evalnum(r, ln, buf->buf, &pos, &iv, 0)) {
2335*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_IT_NONUM, r->parse,
2336*260e9a87SYuri Pankov 		    ln, ppos, buf->buf + 1);
2337698f87a4SGarrett D'Amore 		return(ROFF_IGN);
2338698f87a4SGarrett D'Amore 	}
2339698f87a4SGarrett D'Amore 
2340*260e9a87SYuri Pankov 	while (isspace((unsigned char)buf->buf[pos]))
2341*260e9a87SYuri Pankov 		pos++;
2342*260e9a87SYuri Pankov 
2343*260e9a87SYuri Pankov 	/*
2344*260e9a87SYuri Pankov 	 * Arm the input line trap.
2345*260e9a87SYuri Pankov 	 * Special-casing "an-trap" is an ugly workaround to cope
2346*260e9a87SYuri Pankov 	 * with DocBook stupidly fiddling with man(7) internals.
2347*260e9a87SYuri Pankov 	 */
2348*260e9a87SYuri Pankov 
2349698f87a4SGarrett D'Amore 	roffit_lines = iv;
2350*260e9a87SYuri Pankov 	roffit_macro = mandoc_strdup(iv != 1 ||
2351*260e9a87SYuri Pankov 	    strcmp(buf->buf + pos, "an-trap") ?
2352*260e9a87SYuri Pankov 	    buf->buf + pos : "br");
2353698f87a4SGarrett D'Amore 	return(ROFF_IGN);
2354698f87a4SGarrett D'Amore }
2355698f87a4SGarrett D'Amore 
2356698f87a4SGarrett D'Amore static enum rofferr
2357698f87a4SGarrett D'Amore roff_Dd(ROFF_ARGS)
2358698f87a4SGarrett D'Amore {
2359698f87a4SGarrett D'Amore 	const char *const	*cp;
2360698f87a4SGarrett D'Amore 
2361*260e9a87SYuri Pankov 	if ((r->options & (MPARSE_MDOC | MPARSE_QUICK)) == 0)
2362698f87a4SGarrett D'Amore 		for (cp = __mdoc_reserved; *cp; cp++)
2363698f87a4SGarrett D'Amore 			roff_setstr(r, *cp, NULL, 0);
2364698f87a4SGarrett D'Amore 
2365*260e9a87SYuri Pankov 	if (r->format == 0)
2366*260e9a87SYuri Pankov 		r->format = MPARSE_MDOC;
2367*260e9a87SYuri Pankov 
2368698f87a4SGarrett D'Amore 	return(ROFF_CONT);
2369698f87a4SGarrett D'Amore }
2370698f87a4SGarrett D'Amore 
2371698f87a4SGarrett D'Amore static enum rofferr
2372698f87a4SGarrett D'Amore roff_TH(ROFF_ARGS)
2373698f87a4SGarrett D'Amore {
2374698f87a4SGarrett D'Amore 	const char *const	*cp;
2375698f87a4SGarrett D'Amore 
2376*260e9a87SYuri Pankov 	if ((r->options & MPARSE_QUICK) == 0)
2377698f87a4SGarrett D'Amore 		for (cp = __man_reserved; *cp; cp++)
2378698f87a4SGarrett D'Amore 			roff_setstr(r, *cp, NULL, 0);
2379698f87a4SGarrett D'Amore 
2380*260e9a87SYuri Pankov 	if (r->format == 0)
2381*260e9a87SYuri Pankov 		r->format = MPARSE_MAN;
2382*260e9a87SYuri Pankov 
2383698f87a4SGarrett D'Amore 	return(ROFF_CONT);
2384698f87a4SGarrett D'Amore }
2385698f87a4SGarrett D'Amore 
2386698f87a4SGarrett D'Amore static enum rofferr
238795c635efSGarrett D'Amore roff_TE(ROFF_ARGS)
238895c635efSGarrett D'Amore {
238995c635efSGarrett D'Amore 
239095c635efSGarrett D'Amore 	if (NULL == r->tbl)
2391*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
2392*260e9a87SYuri Pankov 		    ln, ppos, "TE");
2393*260e9a87SYuri Pankov 	else if ( ! tbl_end(&r->tbl)) {
2394*260e9a87SYuri Pankov 		free(buf->buf);
2395*260e9a87SYuri Pankov 		buf->buf = mandoc_strdup(".sp");
2396*260e9a87SYuri Pankov 		buf->sz = 4;
2397*260e9a87SYuri Pankov 		return(ROFF_REPARSE);
2398*260e9a87SYuri Pankov 	}
239995c635efSGarrett D'Amore 	return(ROFF_IGN);
240095c635efSGarrett D'Amore }
240195c635efSGarrett D'Amore 
240295c635efSGarrett D'Amore static enum rofferr
240395c635efSGarrett D'Amore roff_T_(ROFF_ARGS)
240495c635efSGarrett D'Amore {
240595c635efSGarrett D'Amore 
240695c635efSGarrett D'Amore 	if (NULL == r->tbl)
2407*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
2408*260e9a87SYuri Pankov 		    ln, ppos, "T&");
240995c635efSGarrett D'Amore 	else
241095c635efSGarrett D'Amore 		tbl_restart(ppos, ln, r->tbl);
241195c635efSGarrett D'Amore 
241295c635efSGarrett D'Amore 	return(ROFF_IGN);
241395c635efSGarrett D'Amore }
241495c635efSGarrett D'Amore 
2415*260e9a87SYuri Pankov /*
2416*260e9a87SYuri Pankov  * Handle in-line equation delimiters.
2417*260e9a87SYuri Pankov  */
2418*260e9a87SYuri Pankov static enum rofferr
2419*260e9a87SYuri Pankov roff_eqndelim(struct roff *r, struct buf *buf, int pos)
242095c635efSGarrett D'Amore {
2421*260e9a87SYuri Pankov 	char		*cp1, *cp2;
2422*260e9a87SYuri Pankov 	const char	*bef_pr, *bef_nl, *mac, *aft_nl, *aft_pr;
242395c635efSGarrett D'Amore 
2424*260e9a87SYuri Pankov 	/*
2425*260e9a87SYuri Pankov 	 * Outside equations, look for an opening delimiter.
2426*260e9a87SYuri Pankov 	 * If we are inside an equation, we already know it is
2427*260e9a87SYuri Pankov 	 * in-line, or this function wouldn't have been called;
2428*260e9a87SYuri Pankov 	 * so look for a closing delimiter.
2429*260e9a87SYuri Pankov 	 */
2430*260e9a87SYuri Pankov 
2431*260e9a87SYuri Pankov 	cp1 = buf->buf + pos;
2432*260e9a87SYuri Pankov 	cp2 = strchr(cp1, r->eqn == NULL ?
2433*260e9a87SYuri Pankov 	    r->last_eqn->odelim : r->last_eqn->cdelim);
2434*260e9a87SYuri Pankov 	if (cp2 == NULL)
2435*260e9a87SYuri Pankov 		return(ROFF_CONT);
2436*260e9a87SYuri Pankov 
2437*260e9a87SYuri Pankov 	*cp2++ = '\0';
2438*260e9a87SYuri Pankov 	bef_pr = bef_nl = aft_nl = aft_pr = "";
2439*260e9a87SYuri Pankov 
2440*260e9a87SYuri Pankov 	/* Handle preceding text, protecting whitespace. */
2441*260e9a87SYuri Pankov 
2442*260e9a87SYuri Pankov 	if (*buf->buf != '\0') {
2443*260e9a87SYuri Pankov 		if (r->eqn == NULL)
2444*260e9a87SYuri Pankov 			bef_pr = "\\&";
2445*260e9a87SYuri Pankov 		bef_nl = "\n";
244695c635efSGarrett D'Amore 	}
244795c635efSGarrett D'Amore 
2448*260e9a87SYuri Pankov 	/*
2449*260e9a87SYuri Pankov 	 * Prepare replacing the delimiter with an equation macro
2450*260e9a87SYuri Pankov 	 * and drop leading white space from the equation.
2451*260e9a87SYuri Pankov 	 */
2452*260e9a87SYuri Pankov 
2453*260e9a87SYuri Pankov 	if (r->eqn == NULL) {
2454*260e9a87SYuri Pankov 		while (*cp2 == ' ')
2455*260e9a87SYuri Pankov 			cp2++;
2456*260e9a87SYuri Pankov 		mac = ".EQ";
2457*260e9a87SYuri Pankov 	} else
2458*260e9a87SYuri Pankov 		mac = ".EN";
2459*260e9a87SYuri Pankov 
2460*260e9a87SYuri Pankov 	/* Handle following text, protecting whitespace. */
2461*260e9a87SYuri Pankov 
2462*260e9a87SYuri Pankov 	if (*cp2 != '\0') {
2463*260e9a87SYuri Pankov 		aft_nl = "\n";
2464*260e9a87SYuri Pankov 		if (r->eqn != NULL)
2465*260e9a87SYuri Pankov 			aft_pr = "\\&";
2466*260e9a87SYuri Pankov 	}
2467*260e9a87SYuri Pankov 
2468*260e9a87SYuri Pankov 	/* Do the actual replacement. */
2469*260e9a87SYuri Pankov 
2470*260e9a87SYuri Pankov 	buf->sz = mandoc_asprintf(&cp1, "%s%s%s%s%s%s%s", buf->buf,
2471*260e9a87SYuri Pankov 	    bef_pr, bef_nl, mac, aft_nl, aft_pr, cp2) + 1;
2472*260e9a87SYuri Pankov 	free(buf->buf);
2473*260e9a87SYuri Pankov 	buf->buf = cp1;
2474*260e9a87SYuri Pankov 
2475*260e9a87SYuri Pankov 	/* Toggle the in-line state of the eqn subsystem. */
2476*260e9a87SYuri Pankov 
2477*260e9a87SYuri Pankov 	r->eqn_inline = r->eqn == NULL;
2478*260e9a87SYuri Pankov 	return(ROFF_REPARSE);
2479*260e9a87SYuri Pankov }
2480*260e9a87SYuri Pankov 
2481*260e9a87SYuri Pankov static enum rofferr
2482*260e9a87SYuri Pankov roff_EQ(ROFF_ARGS)
248395c635efSGarrett D'Amore {
248495c635efSGarrett D'Amore 	struct eqn_node *e;
248595c635efSGarrett D'Amore 
2486*260e9a87SYuri Pankov 	assert(r->eqn == NULL);
2487*260e9a87SYuri Pankov 	e = eqn_alloc(ppos, ln, r->parse);
248895c635efSGarrett D'Amore 
2489*260e9a87SYuri Pankov 	if (r->last_eqn) {
249095c635efSGarrett D'Amore 		r->last_eqn->next = e;
2491*260e9a87SYuri Pankov 		e->delim = r->last_eqn->delim;
2492*260e9a87SYuri Pankov 		e->odelim = r->last_eqn->odelim;
2493*260e9a87SYuri Pankov 		e->cdelim = r->last_eqn->cdelim;
2494*260e9a87SYuri Pankov 	} else
249595c635efSGarrett D'Amore 		r->first_eqn = r->last_eqn = e;
249695c635efSGarrett D'Amore 
249795c635efSGarrett D'Amore 	r->eqn = r->last_eqn = e;
249895c635efSGarrett D'Amore 
2499*260e9a87SYuri Pankov 	if (buf->buf[pos] != '\0')
2500*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos,
2501*260e9a87SYuri Pankov 		    ".EQ %s", buf->buf + pos);
250295c635efSGarrett D'Amore 
250395c635efSGarrett D'Amore 	return(ROFF_IGN);
250495c635efSGarrett D'Amore }
250595c635efSGarrett D'Amore 
250695c635efSGarrett D'Amore static enum rofferr
250795c635efSGarrett D'Amore roff_EN(ROFF_ARGS)
250895c635efSGarrett D'Amore {
250995c635efSGarrett D'Amore 
2510*260e9a87SYuri Pankov 	mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, ln, ppos, "EN");
251195c635efSGarrett D'Amore 	return(ROFF_IGN);
251295c635efSGarrett D'Amore }
251395c635efSGarrett D'Amore 
251495c635efSGarrett D'Amore static enum rofferr
251595c635efSGarrett D'Amore roff_TS(ROFF_ARGS)
251695c635efSGarrett D'Amore {
2517698f87a4SGarrett D'Amore 	struct tbl_node	*tbl;
251895c635efSGarrett D'Amore 
251995c635efSGarrett D'Amore 	if (r->tbl) {
2520*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BLK_BROKEN, r->parse,
2521*260e9a87SYuri Pankov 		    ln, ppos, "TS breaks TS");
252295c635efSGarrett D'Amore 		tbl_end(&r->tbl);
252395c635efSGarrett D'Amore 	}
252495c635efSGarrett D'Amore 
2525698f87a4SGarrett D'Amore 	tbl = tbl_alloc(ppos, ln, r->parse);
252695c635efSGarrett D'Amore 
252795c635efSGarrett D'Amore 	if (r->last_tbl)
2528698f87a4SGarrett D'Amore 		r->last_tbl->next = tbl;
252995c635efSGarrett D'Amore 	else
2530698f87a4SGarrett D'Amore 		r->first_tbl = r->last_tbl = tbl;
253195c635efSGarrett D'Amore 
2532698f87a4SGarrett D'Amore 	r->tbl = r->last_tbl = tbl;
2533698f87a4SGarrett D'Amore 	return(ROFF_IGN);
2534698f87a4SGarrett D'Amore }
2535698f87a4SGarrett D'Amore 
2536*260e9a87SYuri Pankov static enum rofferr
2537*260e9a87SYuri Pankov roff_brp(ROFF_ARGS)
2538*260e9a87SYuri Pankov {
2539*260e9a87SYuri Pankov 
2540*260e9a87SYuri Pankov 	buf->buf[pos - 1] = '\0';
2541*260e9a87SYuri Pankov 	return(ROFF_CONT);
2542*260e9a87SYuri Pankov }
2543*260e9a87SYuri Pankov 
2544698f87a4SGarrett D'Amore static enum rofferr
2545698f87a4SGarrett D'Amore roff_cc(ROFF_ARGS)
2546698f87a4SGarrett D'Amore {
2547698f87a4SGarrett D'Amore 	const char	*p;
2548698f87a4SGarrett D'Amore 
2549*260e9a87SYuri Pankov 	p = buf->buf + pos;
2550698f87a4SGarrett D'Amore 
2551*260e9a87SYuri Pankov 	if (*p == '\0' || (r->control = *p++) == '.')
2552698f87a4SGarrett D'Amore 		r->control = 0;
2553698f87a4SGarrett D'Amore 
2554*260e9a87SYuri Pankov 	if (*p != '\0')
2555*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse,
2556*260e9a87SYuri Pankov 		    ln, p - buf->buf, "cc ... %s", p);
2557698f87a4SGarrett D'Amore 
255895c635efSGarrett D'Amore 	return(ROFF_IGN);
255995c635efSGarrett D'Amore }
256095c635efSGarrett D'Amore 
256195c635efSGarrett D'Amore static enum rofferr
256295c635efSGarrett D'Amore roff_tr(ROFF_ARGS)
256395c635efSGarrett D'Amore {
256495c635efSGarrett D'Amore 	const char	*p, *first, *second;
256595c635efSGarrett D'Amore 	size_t		 fsz, ssz;
256695c635efSGarrett D'Amore 	enum mandoc_esc	 esc;
256795c635efSGarrett D'Amore 
2568*260e9a87SYuri Pankov 	p = buf->buf + pos;
256995c635efSGarrett D'Amore 
2570*260e9a87SYuri Pankov 	if (*p == '\0') {
2571*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse, ln, ppos, "tr");
257295c635efSGarrett D'Amore 		return(ROFF_IGN);
257395c635efSGarrett D'Amore 	}
257495c635efSGarrett D'Amore 
2575*260e9a87SYuri Pankov 	while (*p != '\0') {
257695c635efSGarrett D'Amore 		fsz = ssz = 1;
257795c635efSGarrett D'Amore 
257895c635efSGarrett D'Amore 		first = p++;
2579*260e9a87SYuri Pankov 		if (*first == '\\') {
258095c635efSGarrett D'Amore 			esc = mandoc_escape(&p, NULL, NULL);
2581*260e9a87SYuri Pankov 			if (esc == ESCAPE_ERROR) {
2582*260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
2583*260e9a87SYuri Pankov 				    ln, (int)(p - buf->buf), first);
258495c635efSGarrett D'Amore 				return(ROFF_IGN);
258595c635efSGarrett D'Amore 			}
258695c635efSGarrett D'Amore 			fsz = (size_t)(p - first);
258795c635efSGarrett D'Amore 		}
258895c635efSGarrett D'Amore 
258995c635efSGarrett D'Amore 		second = p++;
2590*260e9a87SYuri Pankov 		if (*second == '\\') {
259195c635efSGarrett D'Amore 			esc = mandoc_escape(&p, NULL, NULL);
2592*260e9a87SYuri Pankov 			if (esc == ESCAPE_ERROR) {
2593*260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
2594*260e9a87SYuri Pankov 				    ln, (int)(p - buf->buf), second);
259595c635efSGarrett D'Amore 				return(ROFF_IGN);
259695c635efSGarrett D'Amore 			}
259795c635efSGarrett D'Amore 			ssz = (size_t)(p - second);
2598*260e9a87SYuri Pankov 		} else if (*second == '\0') {
2599*260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_TR_ODD, r->parse,
2600*260e9a87SYuri Pankov 			    ln, first - buf->buf, "tr %s", first);
260195c635efSGarrett D'Amore 			second = " ";
260295c635efSGarrett D'Amore 			p--;
260395c635efSGarrett D'Amore 		}
260495c635efSGarrett D'Amore 
260595c635efSGarrett D'Amore 		if (fsz > 1) {
2606*260e9a87SYuri Pankov 			roff_setstrn(&r->xmbtab, first, fsz,
2607*260e9a87SYuri Pankov 			    second, ssz, 0);
260895c635efSGarrett D'Amore 			continue;
260995c635efSGarrett D'Amore 		}
261095c635efSGarrett D'Amore 
2611*260e9a87SYuri Pankov 		if (r->xtab == NULL)
2612*260e9a87SYuri Pankov 			r->xtab = mandoc_calloc(128,
2613*260e9a87SYuri Pankov 			    sizeof(struct roffstr));
261495c635efSGarrett D'Amore 
261595c635efSGarrett D'Amore 		free(r->xtab[(int)*first].p);
261695c635efSGarrett D'Amore 		r->xtab[(int)*first].p = mandoc_strndup(second, ssz);
261795c635efSGarrett D'Amore 		r->xtab[(int)*first].sz = ssz;
261895c635efSGarrett D'Amore 	}
261995c635efSGarrett D'Amore 
262095c635efSGarrett D'Amore 	return(ROFF_IGN);
262195c635efSGarrett D'Amore }
262295c635efSGarrett D'Amore 
262395c635efSGarrett D'Amore static enum rofferr
262495c635efSGarrett D'Amore roff_so(ROFF_ARGS)
262595c635efSGarrett D'Amore {
2626*260e9a87SYuri Pankov 	char *name, *cp;
262795c635efSGarrett D'Amore 
2628*260e9a87SYuri Pankov 	name = buf->buf + pos;
2629*260e9a87SYuri Pankov 	mandoc_vmsg(MANDOCERR_SO, r->parse, ln, ppos, "so %s", name);
263095c635efSGarrett D'Amore 
263195c635efSGarrett D'Amore 	/*
263295c635efSGarrett D'Amore 	 * Handle `so'.  Be EXTREMELY careful, as we shouldn't be
263395c635efSGarrett D'Amore 	 * opening anything that's not in our cwd or anything beneath
263495c635efSGarrett D'Amore 	 * it.  Thus, explicitly disallow traversing up the file-system
263595c635efSGarrett D'Amore 	 * or using absolute paths.
263695c635efSGarrett D'Amore 	 */
263795c635efSGarrett D'Amore 
2638*260e9a87SYuri Pankov 	if (*name == '/' || strstr(name, "../") || strstr(name, "/..")) {
2639*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_SO_PATH, r->parse, ln, ppos,
2640*260e9a87SYuri Pankov 		    ".so %s", name);
2641*260e9a87SYuri Pankov 		buf->sz = mandoc_asprintf(&cp,
2642*260e9a87SYuri Pankov 		    ".sp\nSee the file %s.\n.sp", name) + 1;
2643*260e9a87SYuri Pankov 		free(buf->buf);
2644*260e9a87SYuri Pankov 		buf->buf = cp;
2645*260e9a87SYuri Pankov 		*offs = 0;
2646*260e9a87SYuri Pankov 		return(ROFF_REPARSE);
264795c635efSGarrett D'Amore 	}
264895c635efSGarrett D'Amore 
264995c635efSGarrett D'Amore 	*offs = pos;
265095c635efSGarrett D'Amore 	return(ROFF_SO);
265195c635efSGarrett D'Amore }
265295c635efSGarrett D'Amore 
265395c635efSGarrett D'Amore static enum rofferr
265495c635efSGarrett D'Amore roff_userdef(ROFF_ARGS)
265595c635efSGarrett D'Amore {
2656*260e9a87SYuri Pankov 	const char	 *arg[9], *ap;
265795c635efSGarrett D'Amore 	char		 *cp, *n1, *n2;
265895c635efSGarrett D'Amore 	int		  i;
2659*260e9a87SYuri Pankov 	size_t		  asz, rsz;
266095c635efSGarrett D'Amore 
266195c635efSGarrett D'Amore 	/*
266295c635efSGarrett D'Amore 	 * Collect pointers to macro argument strings
2663698f87a4SGarrett D'Amore 	 * and NUL-terminate them.
266495c635efSGarrett D'Amore 	 */
2665*260e9a87SYuri Pankov 
2666*260e9a87SYuri Pankov 	cp = buf->buf + pos;
266795c635efSGarrett D'Amore 	for (i = 0; i < 9; i++)
2668*260e9a87SYuri Pankov 		arg[i] = *cp == '\0' ? "" :
266995c635efSGarrett D'Amore 		    mandoc_getarg(r->parse, &cp, ln, &pos);
267095c635efSGarrett D'Amore 
267195c635efSGarrett D'Amore 	/*
267295c635efSGarrett D'Amore 	 * Expand macro arguments.
267395c635efSGarrett D'Amore 	 */
2674*260e9a87SYuri Pankov 
2675*260e9a87SYuri Pankov 	buf->sz = strlen(r->current_string) + 1;
2676*260e9a87SYuri Pankov 	n1 = cp = mandoc_malloc(buf->sz);
2677*260e9a87SYuri Pankov 	memcpy(n1, r->current_string, buf->sz);
2678*260e9a87SYuri Pankov 	while (*cp != '\0') {
2679*260e9a87SYuri Pankov 
2680*260e9a87SYuri Pankov 		/* Scan ahead for the next argument invocation. */
2681*260e9a87SYuri Pankov 
2682*260e9a87SYuri Pankov 		if (*cp++ != '\\')
268395c635efSGarrett D'Amore 			continue;
2684*260e9a87SYuri Pankov 		if (*cp++ != '$')
2685*260e9a87SYuri Pankov 			continue;
2686*260e9a87SYuri Pankov 		i = *cp - '1';
2687*260e9a87SYuri Pankov 		if (0 > i || 8 < i)
2688*260e9a87SYuri Pankov 			continue;
2689*260e9a87SYuri Pankov 		cp -= 2;
2690*260e9a87SYuri Pankov 
2691*260e9a87SYuri Pankov 		/*
2692*260e9a87SYuri Pankov 		 * Determine the size of the expanded argument,
2693*260e9a87SYuri Pankov 		 * taking escaping of quotes into account.
2694*260e9a87SYuri Pankov 		 */
2695*260e9a87SYuri Pankov 
2696*260e9a87SYuri Pankov 		asz = 0;
2697*260e9a87SYuri Pankov 		for (ap = arg[i]; *ap != '\0'; ap++) {
2698*260e9a87SYuri Pankov 			asz++;
2699*260e9a87SYuri Pankov 			if (*ap == '"')
2700*260e9a87SYuri Pankov 				asz += 3;
2701*260e9a87SYuri Pankov 		}
2702*260e9a87SYuri Pankov 		if (asz != 3) {
2703*260e9a87SYuri Pankov 
2704*260e9a87SYuri Pankov 			/*
2705*260e9a87SYuri Pankov 			 * Determine the size of the rest of the
2706*260e9a87SYuri Pankov 			 * unexpanded macro, including the NUL.
2707*260e9a87SYuri Pankov 			 */
2708*260e9a87SYuri Pankov 
2709*260e9a87SYuri Pankov 			rsz = buf->sz - (cp - n1) - 3;
2710*260e9a87SYuri Pankov 
2711*260e9a87SYuri Pankov 			/*
2712*260e9a87SYuri Pankov 			 * When shrinking, move before
2713*260e9a87SYuri Pankov 			 * releasing the storage.
2714*260e9a87SYuri Pankov 			 */
2715*260e9a87SYuri Pankov 
2716*260e9a87SYuri Pankov 			if (asz < 3)
2717*260e9a87SYuri Pankov 				memmove(cp + asz, cp + 3, rsz);
2718*260e9a87SYuri Pankov 
2719*260e9a87SYuri Pankov 			/*
2720*260e9a87SYuri Pankov 			 * Resize the storage for the macro
2721*260e9a87SYuri Pankov 			 * and readjust the parse pointer.
2722*260e9a87SYuri Pankov 			 */
2723*260e9a87SYuri Pankov 
2724*260e9a87SYuri Pankov 			buf->sz += asz - 3;
2725*260e9a87SYuri Pankov 			n2 = mandoc_realloc(n1, buf->sz);
2726*260e9a87SYuri Pankov 			cp = n2 + (cp - n1);
2727*260e9a87SYuri Pankov 			n1 = n2;
2728*260e9a87SYuri Pankov 
2729*260e9a87SYuri Pankov 			/*
2730*260e9a87SYuri Pankov 			 * When growing, make room
2731*260e9a87SYuri Pankov 			 * for the expanded argument.
2732*260e9a87SYuri Pankov 			 */
2733*260e9a87SYuri Pankov 
2734*260e9a87SYuri Pankov 			if (asz > 3)
2735*260e9a87SYuri Pankov 				memmove(cp + asz, cp + 3, rsz);
273695c635efSGarrett D'Amore 		}
273795c635efSGarrett D'Amore 
2738*260e9a87SYuri Pankov 		/* Copy the expanded argument, escaping quotes. */
273995c635efSGarrett D'Amore 
2740*260e9a87SYuri Pankov 		n2 = cp;
2741*260e9a87SYuri Pankov 		for (ap = arg[i]; *ap != '\0'; ap++) {
2742*260e9a87SYuri Pankov 			if (*ap == '"') {
2743*260e9a87SYuri Pankov 				memcpy(n2, "\\(dq", 4);
2744*260e9a87SYuri Pankov 				n2 += 4;
2745*260e9a87SYuri Pankov 			} else
2746*260e9a87SYuri Pankov 				*n2++ = *ap;
2747*260e9a87SYuri Pankov 		}
274895c635efSGarrett D'Amore 	}
274995c635efSGarrett D'Amore 
275095c635efSGarrett D'Amore 	/*
275195c635efSGarrett D'Amore 	 * Replace the macro invocation
275295c635efSGarrett D'Amore 	 * by the expanded macro.
275395c635efSGarrett D'Amore 	 */
275495c635efSGarrett D'Amore 
2755*260e9a87SYuri Pankov 	free(buf->buf);
2756*260e9a87SYuri Pankov 	buf->buf = n1;
2757*260e9a87SYuri Pankov 	*offs = 0;
2758*260e9a87SYuri Pankov 
2759*260e9a87SYuri Pankov 	return(buf->sz > 1 && buf->buf[buf->sz - 2] == '\n' ?
276095c635efSGarrett D'Amore 	   ROFF_REPARSE : ROFF_APPEND);
276195c635efSGarrett D'Amore }
276295c635efSGarrett D'Amore 
2763*260e9a87SYuri Pankov static size_t
276495c635efSGarrett D'Amore roff_getname(struct roff *r, char **cpp, int ln, int pos)
276595c635efSGarrett D'Amore {
276695c635efSGarrett D'Amore 	char	 *name, *cp;
2767*260e9a87SYuri Pankov 	size_t	  namesz;
276895c635efSGarrett D'Amore 
276995c635efSGarrett D'Amore 	name = *cpp;
277095c635efSGarrett D'Amore 	if ('\0' == *name)
2771*260e9a87SYuri Pankov 		return(0);
277295c635efSGarrett D'Amore 
2773*260e9a87SYuri Pankov 	/* Read until end of name and terminate it with NUL. */
2774*260e9a87SYuri Pankov 	for (cp = name; 1; cp++) {
2775*260e9a87SYuri Pankov 		if ('\0' == *cp || ' ' == *cp) {
2776*260e9a87SYuri Pankov 			namesz = cp - name;
2777*260e9a87SYuri Pankov 			break;
2778*260e9a87SYuri Pankov 		}
277995c635efSGarrett D'Amore 		if ('\\' != *cp)
278095c635efSGarrett D'Amore 			continue;
2781*260e9a87SYuri Pankov 		namesz = cp - name;
2782*260e9a87SYuri Pankov 		if ('{' == cp[1] || '}' == cp[1])
2783*260e9a87SYuri Pankov 			break;
278495c635efSGarrett D'Amore 		cp++;
278595c635efSGarrett D'Amore 		if ('\\' == *cp)
278695c635efSGarrett D'Amore 			continue;
2787*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_NAMESC, r->parse, ln, pos,
2788*260e9a87SYuri Pankov 		    "%.*s", (int)(cp - name + 1), name);
2789*260e9a87SYuri Pankov 		mandoc_escape((const char **)&cp, NULL, NULL);
2790*260e9a87SYuri Pankov 		break;
279195c635efSGarrett D'Amore 	}
279295c635efSGarrett D'Amore 
279395c635efSGarrett D'Amore 	/* Read past spaces. */
279495c635efSGarrett D'Amore 	while (' ' == *cp)
279595c635efSGarrett D'Amore 		cp++;
279695c635efSGarrett D'Amore 
279795c635efSGarrett D'Amore 	*cpp = cp;
2798*260e9a87SYuri Pankov 	return(namesz);
279995c635efSGarrett D'Amore }
280095c635efSGarrett D'Amore 
280195c635efSGarrett D'Amore /*
280295c635efSGarrett D'Amore  * Store *string into the user-defined string called *name.
280395c635efSGarrett D'Amore  * To clear an existing entry, call with (*r, *name, NULL, 0).
2804*260e9a87SYuri Pankov  * append == 0: replace mode
2805*260e9a87SYuri Pankov  * append == 1: single-line append mode
2806*260e9a87SYuri Pankov  * append == 2: multiline append mode, append '\n' after each call
280795c635efSGarrett D'Amore  */
280895c635efSGarrett D'Amore static void
280995c635efSGarrett D'Amore roff_setstr(struct roff *r, const char *name, const char *string,
2810*260e9a87SYuri Pankov 	int append)
281195c635efSGarrett D'Amore {
281295c635efSGarrett D'Amore 
281395c635efSGarrett D'Amore 	roff_setstrn(&r->strtab, name, strlen(name), string,
2814*260e9a87SYuri Pankov 	    string ? strlen(string) : 0, append);
281595c635efSGarrett D'Amore }
281695c635efSGarrett D'Amore 
281795c635efSGarrett D'Amore static void
281895c635efSGarrett D'Amore roff_setstrn(struct roffkv **r, const char *name, size_t namesz,
2819*260e9a87SYuri Pankov 		const char *string, size_t stringsz, int append)
282095c635efSGarrett D'Amore {
282195c635efSGarrett D'Amore 	struct roffkv	*n;
282295c635efSGarrett D'Amore 	char		*c;
282395c635efSGarrett D'Amore 	int		 i;
282495c635efSGarrett D'Amore 	size_t		 oldch, newch;
282595c635efSGarrett D'Amore 
282695c635efSGarrett D'Amore 	/* Search for an existing string with the same name. */
282795c635efSGarrett D'Amore 	n = *r;
282895c635efSGarrett D'Amore 
2829*260e9a87SYuri Pankov 	while (n && (namesz != n->key.sz ||
2830*260e9a87SYuri Pankov 			strncmp(n->key.p, name, namesz)))
283195c635efSGarrett D'Amore 		n = n->next;
283295c635efSGarrett D'Amore 
283395c635efSGarrett D'Amore 	if (NULL == n) {
283495c635efSGarrett D'Amore 		/* Create a new string table entry. */
283595c635efSGarrett D'Amore 		n = mandoc_malloc(sizeof(struct roffkv));
283695c635efSGarrett D'Amore 		n->key.p = mandoc_strndup(name, namesz);
283795c635efSGarrett D'Amore 		n->key.sz = namesz;
283895c635efSGarrett D'Amore 		n->val.p = NULL;
283995c635efSGarrett D'Amore 		n->val.sz = 0;
284095c635efSGarrett D'Amore 		n->next = *r;
284195c635efSGarrett D'Amore 		*r = n;
2842*260e9a87SYuri Pankov 	} else if (0 == append) {
284395c635efSGarrett D'Amore 		free(n->val.p);
284495c635efSGarrett D'Amore 		n->val.p = NULL;
284595c635efSGarrett D'Amore 		n->val.sz = 0;
284695c635efSGarrett D'Amore 	}
284795c635efSGarrett D'Amore 
284895c635efSGarrett D'Amore 	if (NULL == string)
284995c635efSGarrett D'Amore 		return;
285095c635efSGarrett D'Amore 
285195c635efSGarrett D'Amore 	/*
285295c635efSGarrett D'Amore 	 * One additional byte for the '\n' in multiline mode,
285395c635efSGarrett D'Amore 	 * and one for the terminating '\0'.
285495c635efSGarrett D'Amore 	 */
2855*260e9a87SYuri Pankov 	newch = stringsz + (1 < append ? 2u : 1u);
285695c635efSGarrett D'Amore 
285795c635efSGarrett D'Amore 	if (NULL == n->val.p) {
285895c635efSGarrett D'Amore 		n->val.p = mandoc_malloc(newch);
285995c635efSGarrett D'Amore 		*n->val.p = '\0';
286095c635efSGarrett D'Amore 		oldch = 0;
286195c635efSGarrett D'Amore 	} else {
286295c635efSGarrett D'Amore 		oldch = n->val.sz;
286395c635efSGarrett D'Amore 		n->val.p = mandoc_realloc(n->val.p, oldch + newch);
286495c635efSGarrett D'Amore 	}
286595c635efSGarrett D'Amore 
286695c635efSGarrett D'Amore 	/* Skip existing content in the destination buffer. */
286795c635efSGarrett D'Amore 	c = n->val.p + (int)oldch;
286895c635efSGarrett D'Amore 
286995c635efSGarrett D'Amore 	/* Append new content to the destination buffer. */
287095c635efSGarrett D'Amore 	i = 0;
287195c635efSGarrett D'Amore 	while (i < (int)stringsz) {
287295c635efSGarrett D'Amore 		/*
287395c635efSGarrett D'Amore 		 * Rudimentary roff copy mode:
287495c635efSGarrett D'Amore 		 * Handle escaped backslashes.
287595c635efSGarrett D'Amore 		 */
287695c635efSGarrett D'Amore 		if ('\\' == string[i] && '\\' == string[i + 1])
287795c635efSGarrett D'Amore 			i++;
287895c635efSGarrett D'Amore 		*c++ = string[i++];
287995c635efSGarrett D'Amore 	}
288095c635efSGarrett D'Amore 
288195c635efSGarrett D'Amore 	/* Append terminating bytes. */
2882*260e9a87SYuri Pankov 	if (1 < append)
288395c635efSGarrett D'Amore 		*c++ = '\n';
288495c635efSGarrett D'Amore 
288595c635efSGarrett D'Amore 	*c = '\0';
288695c635efSGarrett D'Amore 	n->val.sz = (int)(c - n->val.p);
288795c635efSGarrett D'Amore }
288895c635efSGarrett D'Amore 
288995c635efSGarrett D'Amore static const char *
289095c635efSGarrett D'Amore roff_getstrn(const struct roff *r, const char *name, size_t len)
289195c635efSGarrett D'Amore {
289295c635efSGarrett D'Amore 	const struct roffkv *n;
2893*260e9a87SYuri Pankov 	int i;
289495c635efSGarrett D'Amore 
289595c635efSGarrett D'Amore 	for (n = r->strtab; n; n = n->next)
289695c635efSGarrett D'Amore 		if (0 == strncmp(name, n->key.p, len) &&
289795c635efSGarrett D'Amore 		    '\0' == n->key.p[(int)len])
289895c635efSGarrett D'Amore 			return(n->val.p);
289995c635efSGarrett D'Amore 
2900*260e9a87SYuri Pankov 	for (i = 0; i < PREDEFS_MAX; i++)
2901*260e9a87SYuri Pankov 		if (0 == strncmp(name, predefs[i].name, len) &&
2902*260e9a87SYuri Pankov 				'\0' == predefs[i].name[(int)len])
2903*260e9a87SYuri Pankov 			return(predefs[i].str);
2904*260e9a87SYuri Pankov 
290595c635efSGarrett D'Amore 	return(NULL);
290695c635efSGarrett D'Amore }
290795c635efSGarrett D'Amore 
290895c635efSGarrett D'Amore static void
290995c635efSGarrett D'Amore roff_freestr(struct roffkv *r)
291095c635efSGarrett D'Amore {
291195c635efSGarrett D'Amore 	struct roffkv	 *n, *nn;
291295c635efSGarrett D'Amore 
291395c635efSGarrett D'Amore 	for (n = r; n; n = nn) {
291495c635efSGarrett D'Amore 		free(n->key.p);
291595c635efSGarrett D'Amore 		free(n->val.p);
291695c635efSGarrett D'Amore 		nn = n->next;
291795c635efSGarrett D'Amore 		free(n);
291895c635efSGarrett D'Amore 	}
291995c635efSGarrett D'Amore }
292095c635efSGarrett D'Amore 
292195c635efSGarrett D'Amore const struct tbl_span *
292295c635efSGarrett D'Amore roff_span(const struct roff *r)
292395c635efSGarrett D'Amore {
292495c635efSGarrett D'Amore 
292595c635efSGarrett D'Amore 	return(r->tbl ? tbl_span(r->tbl) : NULL);
292695c635efSGarrett D'Amore }
292795c635efSGarrett D'Amore 
292895c635efSGarrett D'Amore const struct eqn *
292995c635efSGarrett D'Amore roff_eqn(const struct roff *r)
293095c635efSGarrett D'Amore {
293195c635efSGarrett D'Amore 
293295c635efSGarrett D'Amore 	return(r->last_eqn ? &r->last_eqn->eqn : NULL);
293395c635efSGarrett D'Amore }
293495c635efSGarrett D'Amore 
293595c635efSGarrett D'Amore /*
293695c635efSGarrett D'Amore  * Duplicate an input string, making the appropriate character
293795c635efSGarrett D'Amore  * conversations (as stipulated by `tr') along the way.
293895c635efSGarrett D'Amore  * Returns a heap-allocated string with all the replacements made.
293995c635efSGarrett D'Amore  */
294095c635efSGarrett D'Amore char *
294195c635efSGarrett D'Amore roff_strdup(const struct roff *r, const char *p)
294295c635efSGarrett D'Amore {
294395c635efSGarrett D'Amore 	const struct roffkv *cp;
294495c635efSGarrett D'Amore 	char		*res;
294595c635efSGarrett D'Amore 	const char	*pp;
294695c635efSGarrett D'Amore 	size_t		 ssz, sz;
294795c635efSGarrett D'Amore 	enum mandoc_esc	 esc;
294895c635efSGarrett D'Amore 
294995c635efSGarrett D'Amore 	if (NULL == r->xmbtab && NULL == r->xtab)
295095c635efSGarrett D'Amore 		return(mandoc_strdup(p));
295195c635efSGarrett D'Amore 	else if ('\0' == *p)
295295c635efSGarrett D'Amore 		return(mandoc_strdup(""));
295395c635efSGarrett D'Amore 
295495c635efSGarrett D'Amore 	/*
295595c635efSGarrett D'Amore 	 * Step through each character looking for term matches
295695c635efSGarrett D'Amore 	 * (remember that a `tr' can be invoked with an escape, which is
295795c635efSGarrett D'Amore 	 * a glyph but the escape is multi-character).
295895c635efSGarrett D'Amore 	 * We only do this if the character hash has been initialised
295995c635efSGarrett D'Amore 	 * and the string is >0 length.
296095c635efSGarrett D'Amore 	 */
296195c635efSGarrett D'Amore 
296295c635efSGarrett D'Amore 	res = NULL;
296395c635efSGarrett D'Amore 	ssz = 0;
296495c635efSGarrett D'Amore 
296595c635efSGarrett D'Amore 	while ('\0' != *p) {
296695c635efSGarrett D'Amore 		if ('\\' != *p && r->xtab && r->xtab[(int)*p].p) {
296795c635efSGarrett D'Amore 			sz = r->xtab[(int)*p].sz;
296895c635efSGarrett D'Amore 			res = mandoc_realloc(res, ssz + sz + 1);
296995c635efSGarrett D'Amore 			memcpy(res + ssz, r->xtab[(int)*p].p, sz);
297095c635efSGarrett D'Amore 			ssz += sz;
297195c635efSGarrett D'Amore 			p++;
297295c635efSGarrett D'Amore 			continue;
297395c635efSGarrett D'Amore 		} else if ('\\' != *p) {
297495c635efSGarrett D'Amore 			res = mandoc_realloc(res, ssz + 2);
297595c635efSGarrett D'Amore 			res[ssz++] = *p++;
297695c635efSGarrett D'Amore 			continue;
297795c635efSGarrett D'Amore 		}
297895c635efSGarrett D'Amore 
297995c635efSGarrett D'Amore 		/* Search for term matches. */
298095c635efSGarrett D'Amore 		for (cp = r->xmbtab; cp; cp = cp->next)
298195c635efSGarrett D'Amore 			if (0 == strncmp(p, cp->key.p, cp->key.sz))
298295c635efSGarrett D'Amore 				break;
298395c635efSGarrett D'Amore 
298495c635efSGarrett D'Amore 		if (NULL != cp) {
298595c635efSGarrett D'Amore 			/*
298695c635efSGarrett D'Amore 			 * A match has been found.
298795c635efSGarrett D'Amore 			 * Append the match to the array and move
298895c635efSGarrett D'Amore 			 * forward by its keysize.
298995c635efSGarrett D'Amore 			 */
2990*260e9a87SYuri Pankov 			res = mandoc_realloc(res,
2991*260e9a87SYuri Pankov 			    ssz + cp->val.sz + 1);
299295c635efSGarrett D'Amore 			memcpy(res + ssz, cp->val.p, cp->val.sz);
299395c635efSGarrett D'Amore 			ssz += cp->val.sz;
299495c635efSGarrett D'Amore 			p += (int)cp->key.sz;
299595c635efSGarrett D'Amore 			continue;
299695c635efSGarrett D'Amore 		}
299795c635efSGarrett D'Amore 
299895c635efSGarrett D'Amore 		/*
299995c635efSGarrett D'Amore 		 * Handle escapes carefully: we need to copy
300095c635efSGarrett D'Amore 		 * over just the escape itself, or else we might
300195c635efSGarrett D'Amore 		 * do replacements within the escape itself.
300295c635efSGarrett D'Amore 		 * Make sure to pass along the bogus string.
300395c635efSGarrett D'Amore 		 */
300495c635efSGarrett D'Amore 		pp = p++;
300595c635efSGarrett D'Amore 		esc = mandoc_escape(&p, NULL, NULL);
300695c635efSGarrett D'Amore 		if (ESCAPE_ERROR == esc) {
300795c635efSGarrett D'Amore 			sz = strlen(pp);
300895c635efSGarrett D'Amore 			res = mandoc_realloc(res, ssz + sz + 1);
300995c635efSGarrett D'Amore 			memcpy(res + ssz, pp, sz);
301095c635efSGarrett D'Amore 			break;
301195c635efSGarrett D'Amore 		}
301295c635efSGarrett D'Amore 		/*
301395c635efSGarrett D'Amore 		 * We bail out on bad escapes.
301495c635efSGarrett D'Amore 		 * No need to warn: we already did so when
301595c635efSGarrett D'Amore 		 * roff_res() was called.
301695c635efSGarrett D'Amore 		 */
301795c635efSGarrett D'Amore 		sz = (int)(p - pp);
301895c635efSGarrett D'Amore 		res = mandoc_realloc(res, ssz + sz + 1);
301995c635efSGarrett D'Amore 		memcpy(res + ssz, pp, sz);
302095c635efSGarrett D'Amore 		ssz += sz;
302195c635efSGarrett D'Amore 	}
302295c635efSGarrett D'Amore 
302395c635efSGarrett D'Amore 	res[(int)ssz] = '\0';
302495c635efSGarrett D'Amore 	return(res);
302595c635efSGarrett D'Amore }
3026698f87a4SGarrett D'Amore 
3027*260e9a87SYuri Pankov int
3028*260e9a87SYuri Pankov roff_getformat(const struct roff *r)
3029*260e9a87SYuri Pankov {
3030*260e9a87SYuri Pankov 
3031*260e9a87SYuri Pankov 	return(r->format);
3032*260e9a87SYuri Pankov }
3033*260e9a87SYuri Pankov 
3034698f87a4SGarrett D'Amore /*
3035698f87a4SGarrett D'Amore  * Find out whether a line is a macro line or not.
3036698f87a4SGarrett D'Amore  * If it is, adjust the current position and return one; if it isn't,
3037698f87a4SGarrett D'Amore  * return zero and don't change the current position.
3038698f87a4SGarrett D'Amore  * If the control character has been set with `.cc', then let that grain
3039698f87a4SGarrett D'Amore  * precedence.
3040698f87a4SGarrett D'Amore  * This is slighly contrary to groff, where using the non-breaking
3041698f87a4SGarrett D'Amore  * control character when `cc' has been invoked will cause the
3042698f87a4SGarrett D'Amore  * non-breaking macro contents to be printed verbatim.
3043698f87a4SGarrett D'Amore  */
3044698f87a4SGarrett D'Amore int
3045698f87a4SGarrett D'Amore roff_getcontrol(const struct roff *r, const char *cp, int *ppos)
3046698f87a4SGarrett D'Amore {
3047698f87a4SGarrett D'Amore 	int		pos;
3048698f87a4SGarrett D'Amore 
3049698f87a4SGarrett D'Amore 	pos = *ppos;
3050698f87a4SGarrett D'Amore 
3051698f87a4SGarrett D'Amore 	if (0 != r->control && cp[pos] == r->control)
3052698f87a4SGarrett D'Amore 		pos++;
3053698f87a4SGarrett D'Amore 	else if (0 != r->control)
3054698f87a4SGarrett D'Amore 		return(0);
3055698f87a4SGarrett D'Amore 	else if ('\\' == cp[pos] && '.' == cp[pos + 1])
3056698f87a4SGarrett D'Amore 		pos += 2;
3057698f87a4SGarrett D'Amore 	else if ('.' == cp[pos] || '\'' == cp[pos])
3058698f87a4SGarrett D'Amore 		pos++;
3059698f87a4SGarrett D'Amore 	else
3060698f87a4SGarrett D'Amore 		return(0);
3061698f87a4SGarrett D'Amore 
3062698f87a4SGarrett D'Amore 	while (' ' == cp[pos] || '\t' == cp[pos])
3063698f87a4SGarrett D'Amore 		pos++;
3064698f87a4SGarrett D'Amore 
3065698f87a4SGarrett D'Amore 	*ppos = pos;
3066698f87a4SGarrett D'Amore 	return(1);
3067698f87a4SGarrett D'Amore }
3068