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