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
roffhash_init(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
roffhash_find(const char * p,size_t s)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
roffnode_pop(struct roff * r)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
roffnode_push(struct roff * r,enum rofft tok,const char * name,int line,int col)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
roff_free1(struct roff * r)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
roff_reset(struct roff * r)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
roff_free(struct roff * r)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 *
roff_alloc(struct mparse * parse,const struct mchars * mchars,int options)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
roff_res(struct roff * r,struct buf * buf,int ln,int pos)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
roff_parsetext(struct buf * buf,int pos,int * offs)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
roff_parseln(struct roff * r,int ln,struct buf * buf,int * offs)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
roff_endparse(struct roff * r)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
roff_parse(struct roff * r,char * buf,int * pos,int ln,int ppos)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
roff_cblock(ROFF_ARGS)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
roffnode_cleanscope(struct roff * r)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
roff_ccond(struct roff * r,int ln,int ppos)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
roff_block(ROFF_ARGS)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
roff_block_sub(ROFF_ARGS)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
roff_block_text(ROFF_ARGS)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
roff_cond_sub(ROFF_ARGS)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
roff_cond_text(ROFF_ARGS)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
roff_getnum(const char * v,int * pos,int * res,int flags)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
roff_evalstrcond(const char * v,int * pos)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
roff_evalcond(struct roff * r,int ln,const char * v,int * pos)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
roff_line_ignore(ROFF_ARGS)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
roff_insec(ROFF_ARGS)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
roff_unsupp(ROFF_ARGS)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
roff_cond(ROFF_ARGS)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
roff_ds(ROFF_ARGS)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
roff_getop(const char * v,int * pos,char * res)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
roff_evalpar(struct roff * r,int ln,const char * v,int * pos,int * res,int flags)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
roff_evalnum(struct roff * r,int ln,const char * v,int * pos,int * res,int flags)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
roff_setreg(struct roff * r,const char * name,int val,char sign)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
roff_getregro(const char * name)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
roff_getreg(const struct roff * r,const char * name)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
roff_getregn(const struct roff * r,const char * name,size_t len)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
roff_freereg(struct roffreg * reg)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
roff_nr(ROFF_ARGS)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
roff_rr(ROFF_ARGS)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 = ®->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
roff_rm(ROFF_ARGS)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
roff_it(ROFF_ARGS)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
roff_Dd(ROFF_ARGS)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
roff_TH(ROFF_ARGS)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
roff_TE(ROFF_ARGS)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
roff_T_(ROFF_ARGS)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
roff_eqndelim(struct roff * r,struct buf * buf,int pos)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
roff_EQ(ROFF_ARGS)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
roff_EN(ROFF_ARGS)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
roff_TS(ROFF_ARGS)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
roff_brp(ROFF_ARGS)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
roff_cc(ROFF_ARGS)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
roff_tr(ROFF_ARGS)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
roff_so(ROFF_ARGS)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
roff_userdef(ROFF_ARGS)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
roff_getname(struct roff * r,char ** cpp,int ln,int pos)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
roff_setstr(struct roff * r,const char * name,const char * string,int append)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
roff_setstrn(struct roffkv ** r,const char * name,size_t namesz,const char * string,size_t stringsz,int append)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 *
roff_getstrn(const struct roff * r,const char * name,size_t len)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
roff_freestr(struct roffkv * r)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 *
roff_span(const struct roff * r)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 *
roff_eqn(const struct roff * r)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 *
roff_strdup(const struct roff * r,const char * p)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
roff_getformat(const struct roff * r)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
roff_getcontrol(const struct roff * r,const char * cp,int * ppos)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