xref: /illumos-gate/usr/src/cmd/mandoc/roff.c (revision 4d131170e62381276a07ffc0aeb1b62e527d940c)
1*4d131170SRobert Mustacchi /* $Id: roff.c,v 1.378 2021/08/10 12:55:04 schwarze Exp $ */
295c635efSGarrett D'Amore /*
3*4d131170SRobert Mustacchi  * Copyright (c) 2010-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org>
4371584c2SYuri Pankov  * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
595c635efSGarrett D'Amore  *
695c635efSGarrett D'Amore  * Permission to use, copy, modify, and distribute this software for any
795c635efSGarrett D'Amore  * purpose with or without fee is hereby granted, provided that the above
895c635efSGarrett D'Amore  * copyright notice and this permission notice appear in all copies.
995c635efSGarrett D'Amore  *
1095c635efSGarrett D'Amore  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1195c635efSGarrett D'Amore  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1295c635efSGarrett D'Amore  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1395c635efSGarrett D'Amore  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1495c635efSGarrett D'Amore  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1595c635efSGarrett D'Amore  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1695c635efSGarrett D'Amore  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*4d131170SRobert Mustacchi  *
18*4d131170SRobert Mustacchi  * Implementation of the roff(7) parser for mandoc(1).
1995c635efSGarrett D'Amore  */
2095c635efSGarrett D'Amore #include "config.h"
21260e9a87SYuri Pankov 
22260e9a87SYuri Pankov #include <sys/types.h>
2395c635efSGarrett D'Amore 
2495c635efSGarrett D'Amore #include <assert.h>
2595c635efSGarrett D'Amore #include <ctype.h>
26260e9a87SYuri Pankov #include <limits.h>
27c66b8046SYuri Pankov #include <stddef.h>
28c66b8046SYuri Pankov #include <stdint.h>
29698f87a4SGarrett D'Amore #include <stdio.h>
3095c635efSGarrett D'Amore #include <stdlib.h>
3195c635efSGarrett D'Amore #include <string.h>
3295c635efSGarrett D'Amore 
33260e9a87SYuri Pankov #include "mandoc_aux.h"
34c66b8046SYuri Pankov #include "mandoc_ohash.h"
35cec8643bSMichal Nowak #include "mandoc.h"
36371584c2SYuri Pankov #include "roff.h"
37cec8643bSMichal Nowak #include "mandoc_parse.h"
3895c635efSGarrett D'Amore #include "libmandoc.h"
39371584c2SYuri Pankov #include "roff_int.h"
40cec8643bSMichal Nowak #include "tbl_parse.h"
41cec8643bSMichal Nowak #include "eqn_parse.h"
42cec8643bSMichal Nowak 
43cec8643bSMichal Nowak /*
44cec8643bSMichal Nowak  * ASCII_ESC is used to signal from roff_getarg() to roff_expand()
45cec8643bSMichal Nowak  * that an escape sequence resulted from copy-in processing and
46cec8643bSMichal Nowak  * needs to be checked or interpolated.  As it is used nowhere
47cec8643bSMichal Nowak  * else, it is defined here rather than in a header file.
48cec8643bSMichal Nowak  */
49cec8643bSMichal Nowak #define	ASCII_ESC	27
5095c635efSGarrett D'Amore 
5195c635efSGarrett D'Amore /* Maximum number of string expansions per line, to break infinite loops. */
5295c635efSGarrett D'Amore #define	EXPAND_LIMIT	1000
5395c635efSGarrett D'Amore 
54c66b8046SYuri Pankov /* Types of definitions of macros and strings. */
55c66b8046SYuri Pankov #define	ROFFDEF_USER	(1 << 1)  /* User-defined. */
56c66b8046SYuri Pankov #define	ROFFDEF_PRE	(1 << 2)  /* Predefined. */
57c66b8046SYuri Pankov #define	ROFFDEF_REN	(1 << 3)  /* Renamed standard macro. */
58c66b8046SYuri Pankov #define	ROFFDEF_STD	(1 << 4)  /* mdoc(7) or man(7) macro. */
59c66b8046SYuri Pankov #define	ROFFDEF_ANY	(ROFFDEF_USER | ROFFDEF_PRE | \
60c66b8046SYuri Pankov 			 ROFFDEF_REN | ROFFDEF_STD)
616640c13bSYuri Pankov #define	ROFFDEF_UNDEF	(1 << 5)  /* Completely undefined. */
62371584c2SYuri Pankov 
63c66b8046SYuri Pankov /* --- data types --------------------------------------------------------- */
6495c635efSGarrett D'Amore 
6595c635efSGarrett D'Amore /*
6695c635efSGarrett D'Amore  * An incredibly-simple string buffer.
6795c635efSGarrett D'Amore  */
6895c635efSGarrett D'Amore struct	roffstr {
6995c635efSGarrett D'Amore 	char		*p; /* nil-terminated buffer */
7095c635efSGarrett D'Amore 	size_t		 sz; /* saved strlen(p) */
7195c635efSGarrett D'Amore };
7295c635efSGarrett D'Amore 
7395c635efSGarrett D'Amore /*
7495c635efSGarrett D'Amore  * A key-value roffstr pair as part of a singly-linked list.
7595c635efSGarrett D'Amore  */
7695c635efSGarrett D'Amore struct	roffkv {
7795c635efSGarrett D'Amore 	struct roffstr	 key;
7895c635efSGarrett D'Amore 	struct roffstr	 val;
7995c635efSGarrett D'Amore 	struct roffkv	*next; /* next in list */
8095c635efSGarrett D'Amore };
8195c635efSGarrett D'Amore 
82698f87a4SGarrett D'Amore /*
83698f87a4SGarrett D'Amore  * A single number register as part of a singly-linked list.
84698f87a4SGarrett D'Amore  */
85698f87a4SGarrett D'Amore struct	roffreg {
86698f87a4SGarrett D'Amore 	struct roffstr	 key;
87698f87a4SGarrett D'Amore 	int		 val;
886640c13bSYuri Pankov 	int		 step;
89698f87a4SGarrett D'Amore 	struct roffreg	*next;
90698f87a4SGarrett D'Amore };
91698f87a4SGarrett D'Amore 
92c66b8046SYuri Pankov /*
93c66b8046SYuri Pankov  * Association of request and macro names with token IDs.
94c66b8046SYuri Pankov  */
95c66b8046SYuri Pankov struct	roffreq {
96c66b8046SYuri Pankov 	enum roff_tok	 tok;
97c66b8046SYuri Pankov 	char		 name[];
98c66b8046SYuri Pankov };
99c66b8046SYuri Pankov 
100cec8643bSMichal Nowak /*
101cec8643bSMichal Nowak  * A macro processing context.
102cec8643bSMichal Nowak  * More than one is needed when macro calls are nested.
103cec8643bSMichal Nowak  */
104cec8643bSMichal Nowak struct	mctx {
105cec8643bSMichal Nowak 	char		**argv;
106cec8643bSMichal Nowak 	int		 argc;
107cec8643bSMichal Nowak 	int		 argsz;
108cec8643bSMichal Nowak };
109cec8643bSMichal Nowak 
11095c635efSGarrett D'Amore struct	roff {
111c66b8046SYuri Pankov 	struct roff_man	*man; /* mdoc or man parser */
11295c635efSGarrett D'Amore 	struct roffnode	*last; /* leaf of stack */
113cec8643bSMichal Nowak 	struct mctx	*mstack; /* stack of macro contexts */
114260e9a87SYuri Pankov 	int		*rstack; /* stack of inverted `ie' values */
115c66b8046SYuri Pankov 	struct ohash	*reqtab; /* request lookup table */
116698f87a4SGarrett D'Amore 	struct roffreg	*regtab; /* number registers */
11795c635efSGarrett D'Amore 	struct roffkv	*strtab; /* user-defined strings & macros */
118c66b8046SYuri Pankov 	struct roffkv	*rentab; /* renamed strings & macros */
11995c635efSGarrett D'Amore 	struct roffkv	*xmbtab; /* multi-byte trans table (`tr') */
12095c635efSGarrett D'Amore 	struct roffstr	*xtab; /* single-byte trans table (`tr') */
12195c635efSGarrett D'Amore 	const char	*current_string; /* value of last called user macro */
12295c635efSGarrett D'Amore 	struct tbl_node	*first_tbl; /* first table parsed */
12395c635efSGarrett D'Amore 	struct tbl_node	*last_tbl; /* last table parsed */
12495c635efSGarrett D'Amore 	struct tbl_node	*tbl; /* current table being parsed */
125c66b8046SYuri Pankov 	struct eqn_node	*last_eqn; /* equation parser */
126c66b8046SYuri Pankov 	struct eqn_node	*eqn; /* active equation parser */
127260e9a87SYuri Pankov 	int		 eqn_inline; /* current equation is inline */
128260e9a87SYuri Pankov 	int		 options; /* parse options */
129cec8643bSMichal Nowak 	int		 mstacksz; /* current size of mstack */
130cec8643bSMichal Nowak 	int		 mstackpos; /* position in mstack */
131260e9a87SYuri Pankov 	int		 rstacksz; /* current size limit of rstack */
132260e9a87SYuri Pankov 	int		 rstackpos; /* position in rstack */
133260e9a87SYuri Pankov 	int		 format; /* current file in mdoc or man format */
134260e9a87SYuri Pankov 	char		 control; /* control character */
135c66b8046SYuri Pankov 	char		 escape; /* escape character */
13695c635efSGarrett D'Amore };
13795c635efSGarrett D'Amore 
138*4d131170SRobert Mustacchi /*
139*4d131170SRobert Mustacchi  * A macro definition, condition, or ignored block.
140*4d131170SRobert Mustacchi  */
14195c635efSGarrett D'Amore struct	roffnode {
142c66b8046SYuri Pankov 	enum roff_tok	 tok; /* type of node */
14395c635efSGarrett D'Amore 	struct roffnode	*parent; /* up one in stack */
14495c635efSGarrett D'Amore 	int		 line; /* parse line */
14595c635efSGarrett D'Amore 	int		 col; /* parse col */
14695c635efSGarrett D'Amore 	char		*name; /* node name, e.g. macro name */
147*4d131170SRobert Mustacchi 	char		*end; /* custom end macro of the block */
148*4d131170SRobert Mustacchi 	int		 endspan; /* scope to: 1=eol 2=next line -1=\} */
149*4d131170SRobert Mustacchi 	int		 rule; /* content is: 1=evaluated 0=skipped */
15095c635efSGarrett D'Amore };
15195c635efSGarrett D'Amore 
15295c635efSGarrett D'Amore #define	ROFF_ARGS	 struct roff *r, /* parse ctx */ \
153c66b8046SYuri Pankov 			 enum roff_tok tok, /* tok of macro */ \
154260e9a87SYuri Pankov 			 struct buf *buf, /* input buffer */ \
15595c635efSGarrett D'Amore 			 int ln, /* parse line */ \
15695c635efSGarrett D'Amore 			 int ppos, /* original pos in buffer */ \
15795c635efSGarrett D'Amore 			 int pos, /* current pos in buffer */ \
15895c635efSGarrett D'Amore 			 int *offs /* reset offset of buffer data */
15995c635efSGarrett D'Amore 
160cec8643bSMichal Nowak typedef	int (*roffproc)(ROFF_ARGS);
16195c635efSGarrett D'Amore 
16295c635efSGarrett D'Amore struct	roffmac {
16395c635efSGarrett D'Amore 	roffproc	 proc; /* process new macro */
16495c635efSGarrett D'Amore 	roffproc	 text; /* process as child text of macro */
16595c635efSGarrett D'Amore 	roffproc	 sub; /* process as child of macro */
16695c635efSGarrett D'Amore 	int		 flags;
16795c635efSGarrett D'Amore #define	ROFFMAC_STRUCT	(1 << 0) /* always interpret */
16895c635efSGarrett D'Amore };
16995c635efSGarrett D'Amore 
17095c635efSGarrett D'Amore struct	predef {
17195c635efSGarrett D'Amore 	const char	*name; /* predefined input name */
17295c635efSGarrett D'Amore 	const char	*str; /* replacement symbol */
17395c635efSGarrett D'Amore };
17495c635efSGarrett D'Amore 
17595c635efSGarrett D'Amore #define	PREDEF(__name, __str) \
17695c635efSGarrett D'Amore 	{ (__name), (__str) },
17795c635efSGarrett D'Amore 
178371584c2SYuri Pankov /* --- function prototypes ------------------------------------------------ */
179371584c2SYuri Pankov 
180cec8643bSMichal Nowak static	int		 roffnode_cleanscope(struct roff *);
181cec8643bSMichal Nowak static	int		 roffnode_pop(struct roff *);
182c66b8046SYuri Pankov static	void		 roffnode_push(struct roff *, enum roff_tok,
18395c635efSGarrett D'Amore 				const char *, int, int);
184cec8643bSMichal Nowak static	void		 roff_addtbl(struct roff_man *, int, struct tbl_node *);
185cec8643bSMichal Nowak static	int		 roff_als(ROFF_ARGS);
186cec8643bSMichal Nowak static	int		 roff_block(ROFF_ARGS);
187cec8643bSMichal Nowak static	int		 roff_block_text(ROFF_ARGS);
188cec8643bSMichal Nowak static	int		 roff_block_sub(ROFF_ARGS);
189*4d131170SRobert Mustacchi static	int		 roff_break(ROFF_ARGS);
190cec8643bSMichal Nowak static	int		 roff_cblock(ROFF_ARGS);
191cec8643bSMichal Nowak static	int		 roff_cc(ROFF_ARGS);
192cec8643bSMichal Nowak static	int		 roff_ccond(struct roff *, int, int);
193cec8643bSMichal Nowak static	int		 roff_char(ROFF_ARGS);
194cec8643bSMichal Nowak static	int		 roff_cond(ROFF_ARGS);
195*4d131170SRobert Mustacchi static	int		 roff_cond_checkend(ROFF_ARGS);
196cec8643bSMichal Nowak static	int		 roff_cond_text(ROFF_ARGS);
197cec8643bSMichal Nowak static	int		 roff_cond_sub(ROFF_ARGS);
198cec8643bSMichal Nowak static	int		 roff_ds(ROFF_ARGS);
199cec8643bSMichal Nowak static	int		 roff_ec(ROFF_ARGS);
200cec8643bSMichal Nowak static	int		 roff_eo(ROFF_ARGS);
201cec8643bSMichal Nowak static	int		 roff_eqndelim(struct roff *, struct buf *, int);
202*4d131170SRobert Mustacchi static	int		 roff_evalcond(struct roff *, int, char *, int *);
203260e9a87SYuri Pankov static	int		 roff_evalnum(struct roff *, int,
204260e9a87SYuri Pankov 				const char *, int *, int *, int);
205260e9a87SYuri Pankov static	int		 roff_evalpar(struct roff *, int,
206260e9a87SYuri Pankov 				const char *, int *, int *, int);
207260e9a87SYuri Pankov static	int		 roff_evalstrcond(const char *, int *);
208cec8643bSMichal Nowak static	int		 roff_expand(struct roff *, struct buf *,
209cec8643bSMichal Nowak 				int, int, char);
21095c635efSGarrett D'Amore static	void		 roff_free1(struct roff *);
211698f87a4SGarrett D'Amore static	void		 roff_freereg(struct roffreg *);
21295c635efSGarrett D'Amore static	void		 roff_freestr(struct roffkv *);
213260e9a87SYuri Pankov static	size_t		 roff_getname(struct roff *, char **, int, int);
214260e9a87SYuri Pankov static	int		 roff_getnum(const char *, int *, int *, int);
215698f87a4SGarrett D'Amore static	int		 roff_getop(const char *, int *, char *);
2166640c13bSYuri Pankov static	int		 roff_getregn(struct roff *,
2176640c13bSYuri Pankov 				const char *, size_t, char);
218371584c2SYuri Pankov static	int		 roff_getregro(const struct roff *,
219371584c2SYuri Pankov 				const char *name);
2206640c13bSYuri Pankov static	const char	*roff_getstrn(struct roff *,
221c66b8046SYuri Pankov 				const char *, size_t, int *);
222371584c2SYuri Pankov static	int		 roff_hasregn(const struct roff *,
223371584c2SYuri Pankov 				const char *, size_t);
224cec8643bSMichal Nowak static	int		 roff_insec(ROFF_ARGS);
225cec8643bSMichal Nowak static	int		 roff_it(ROFF_ARGS);
226cec8643bSMichal Nowak static	int		 roff_line_ignore(ROFF_ARGS);
227371584c2SYuri Pankov static	void		 roff_man_alloc1(struct roff_man *);
228371584c2SYuri Pankov static	void		 roff_man_free1(struct roff_man *);
229cec8643bSMichal Nowak static	int		 roff_manyarg(ROFF_ARGS);
230cec8643bSMichal Nowak static	int		 roff_noarg(ROFF_ARGS);
231cec8643bSMichal Nowak static	int		 roff_nop(ROFF_ARGS);
232cec8643bSMichal Nowak static	int		 roff_nr(ROFF_ARGS);
233cec8643bSMichal Nowak static	int		 roff_onearg(ROFF_ARGS);
234c66b8046SYuri Pankov static	enum roff_tok	 roff_parse(struct roff *, char *, int *,
235260e9a87SYuri Pankov 				int, int);
236cec8643bSMichal Nowak static	int		 roff_parsetext(struct roff *, struct buf *,
237c66b8046SYuri Pankov 				int, int *);
238cec8643bSMichal Nowak static	int		 roff_renamed(ROFF_ARGS);
239cec8643bSMichal Nowak static	int		 roff_return(ROFF_ARGS);
240cec8643bSMichal Nowak static	int		 roff_rm(ROFF_ARGS);
241cec8643bSMichal Nowak static	int		 roff_rn(ROFF_ARGS);
242cec8643bSMichal Nowak static	int		 roff_rr(ROFF_ARGS);
2436640c13bSYuri Pankov static	void		 roff_setregn(struct roff *, const char *,
2446640c13bSYuri Pankov 				size_t, int, char, int);
24595c635efSGarrett D'Amore static	void		 roff_setstr(struct roff *,
24695c635efSGarrett D'Amore 				const char *, const char *, int);
24795c635efSGarrett D'Amore static	void		 roff_setstrn(struct roffkv **, const char *,
24895c635efSGarrett D'Amore 				size_t, const char *, size_t, int);
249cec8643bSMichal Nowak static	int		 roff_shift(ROFF_ARGS);
250cec8643bSMichal Nowak static	int		 roff_so(ROFF_ARGS);
251cec8643bSMichal Nowak static	int		 roff_tr(ROFF_ARGS);
252cec8643bSMichal Nowak static	int		 roff_Dd(ROFF_ARGS);
253cec8643bSMichal Nowak static	int		 roff_TE(ROFF_ARGS);
254cec8643bSMichal Nowak static	int		 roff_TS(ROFF_ARGS);
255cec8643bSMichal Nowak static	int		 roff_EQ(ROFF_ARGS);
256cec8643bSMichal Nowak static	int		 roff_EN(ROFF_ARGS);
257cec8643bSMichal Nowak static	int		 roff_T_(ROFF_ARGS);
258cec8643bSMichal Nowak static	int		 roff_unsupp(ROFF_ARGS);
259cec8643bSMichal Nowak static	int		 roff_userdef(ROFF_ARGS);
26095c635efSGarrett D'Amore 
261371584c2SYuri Pankov /* --- constant data ------------------------------------------------------ */
262371584c2SYuri Pankov 
263260e9a87SYuri Pankov #define	ROFFNUM_SCALE	(1 << 0)  /* Honour scaling in roff_getnum(). */
264260e9a87SYuri Pankov #define	ROFFNUM_WHITE	(1 << 1)  /* Skip whitespace in roff_evalnum(). */
265260e9a87SYuri Pankov 
266c66b8046SYuri Pankov const char *__roff_name[MAN_MAX + 1] = {
267cec8643bSMichal Nowak 	"br",		"ce",		"fi",		"ft",
268cec8643bSMichal Nowak 	"ll",		"mc",		"nf",
269cec8643bSMichal Nowak 	"po",		"rj",		"sp",
270c66b8046SYuri Pankov 	"ta",		"ti",		NULL,
271c66b8046SYuri Pankov 	"ab",		"ad",		"af",		"aln",
272c66b8046SYuri Pankov 	"als",		"am",		"am1",		"ami",
273c66b8046SYuri Pankov 	"ami1",		"as",		"as1",		"asciify",
274c66b8046SYuri Pankov 	"backtrace",	"bd",		"bleedat",	"blm",
275c66b8046SYuri Pankov         "box",		"boxa",		"bp",		"BP",
276c66b8046SYuri Pankov 	"break",	"breakchar",	"brnl",		"brp",
277c66b8046SYuri Pankov 	"brpnl",	"c2",		"cc",
278c66b8046SYuri Pankov 	"cf",		"cflags",	"ch",		"char",
279c66b8046SYuri Pankov 	"chop",		"class",	"close",	"CL",
280c66b8046SYuri Pankov 	"color",	"composite",	"continue",	"cp",
281c66b8046SYuri Pankov 	"cropat",	"cs",		"cu",		"da",
282c66b8046SYuri Pankov 	"dch",		"Dd",		"de",		"de1",
283c66b8046SYuri Pankov 	"defcolor",	"dei",		"dei1",		"device",
284c66b8046SYuri Pankov 	"devicem",	"di",		"do",		"ds",
285c66b8046SYuri Pankov 	"ds1",		"dwh",		"dt",		"ec",
286c66b8046SYuri Pankov 	"ecr",		"ecs",		"el",		"em",
287c66b8046SYuri Pankov 	"EN",		"eo",		"EP",		"EQ",
288c66b8046SYuri Pankov 	"errprint",	"ev",		"evc",		"ex",
289c66b8046SYuri Pankov 	"fallback",	"fam",		"fc",		"fchar",
290c66b8046SYuri Pankov 	"fcolor",	"fdeferlig",	"feature",	"fkern",
291c66b8046SYuri Pankov 	"fl",		"flig",		"fp",		"fps",
292c66b8046SYuri Pankov 	"fschar",	"fspacewidth",	"fspecial",	"ftr",
293c66b8046SYuri Pankov 	"fzoom",	"gcolor",	"hc",		"hcode",
294c66b8046SYuri Pankov 	"hidechar",	"hla",		"hlm",		"hpf",
295c66b8046SYuri Pankov 	"hpfa",		"hpfcode",	"hw",		"hy",
296c66b8046SYuri Pankov 	"hylang",	"hylen",	"hym",		"hypp",
297c66b8046SYuri Pankov 	"hys",		"ie",		"if",		"ig",
298c66b8046SYuri Pankov 	"index",	"it",		"itc",		"IX",
299c66b8046SYuri Pankov 	"kern",		"kernafter",	"kernbefore",	"kernpair",
300c66b8046SYuri Pankov 	"lc",		"lc_ctype",	"lds",		"length",
301c66b8046SYuri Pankov 	"letadj",	"lf",		"lg",		"lhang",
302c66b8046SYuri Pankov 	"linetabs",	"lnr",		"lnrf",		"lpfx",
303c66b8046SYuri Pankov 	"ls",		"lsm",		"lt",
304c66b8046SYuri Pankov 	"mediasize",	"minss",	"mk",		"mso",
305c66b8046SYuri Pankov 	"na",		"ne",		"nh",		"nhychar",
306c66b8046SYuri Pankov 	"nm",		"nn",		"nop",		"nr",
307c66b8046SYuri Pankov 	"nrf",		"nroff",	"ns",		"nx",
308c66b8046SYuri Pankov 	"open",		"opena",	"os",		"output",
309c66b8046SYuri Pankov 	"padj",		"papersize",	"pc",		"pev",
310c66b8046SYuri Pankov 	"pi",		"PI",		"pl",		"pm",
311c66b8046SYuri Pankov 	"pn",		"pnr",		"ps",
312c66b8046SYuri Pankov 	"psbb",		"pshape",	"pso",		"ptr",
313c66b8046SYuri Pankov 	"pvs",		"rchar",	"rd",		"recursionlimit",
314c66b8046SYuri Pankov 	"return",	"rfschar",	"rhang",
315c66b8046SYuri Pankov 	"rm",		"rn",		"rnn",		"rr",
316c66b8046SYuri Pankov 	"rs",		"rt",		"schar",	"sentchar",
317c66b8046SYuri Pankov 	"shc",		"shift",	"sizes",	"so",
318c66b8046SYuri Pankov 	"spacewidth",	"special",	"spreadwarn",	"ss",
319c66b8046SYuri Pankov 	"sty",		"substring",	"sv",		"sy",
320c66b8046SYuri Pankov 	"T&",		"tc",		"TE",
321c66b8046SYuri Pankov 	"TH",		"tkf",		"tl",
322c66b8046SYuri Pankov 	"tm",		"tm1",		"tmc",		"tr",
323c66b8046SYuri Pankov 	"track",	"transchar",	"trf",		"trimat",
324c66b8046SYuri Pankov 	"trin",		"trnt",		"troff",	"TS",
325c66b8046SYuri Pankov 	"uf",		"ul",		"unformat",	"unwatch",
326c66b8046SYuri Pankov 	"unwatchn",	"vpt",		"vs",		"warn",
327c66b8046SYuri Pankov 	"warnscale",	"watch",	"watchlength",	"watchn",
328c66b8046SYuri Pankov 	"wh",		"while",	"write",	"writec",
329c66b8046SYuri Pankov 	"writem",	"xflag",	".",		NULL,
330c66b8046SYuri Pankov 	NULL,		"text",
331c66b8046SYuri Pankov 	"Dd",		"Dt",		"Os",		"Sh",
332c66b8046SYuri Pankov 	"Ss",		"Pp",		"D1",		"Dl",
333c66b8046SYuri Pankov 	"Bd",		"Ed",		"Bl",		"El",
334c66b8046SYuri Pankov 	"It",		"Ad",		"An",		"Ap",
335c66b8046SYuri Pankov 	"Ar",		"Cd",		"Cm",		"Dv",
336c66b8046SYuri Pankov 	"Er",		"Ev",		"Ex",		"Fa",
337c66b8046SYuri Pankov 	"Fd",		"Fl",		"Fn",		"Ft",
338c66b8046SYuri Pankov 	"Ic",		"In",		"Li",		"Nd",
339c66b8046SYuri Pankov 	"Nm",		"Op",		"Ot",		"Pa",
340c66b8046SYuri Pankov 	"Rv",		"St",		"Va",		"Vt",
341c66b8046SYuri Pankov 	"Xr",		"%A",		"%B",		"%D",
342c66b8046SYuri Pankov 	"%I",		"%J",		"%N",		"%O",
343c66b8046SYuri Pankov 	"%P",		"%R",		"%T",		"%V",
344c66b8046SYuri Pankov 	"Ac",		"Ao",		"Aq",		"At",
345c66b8046SYuri Pankov 	"Bc",		"Bf",		"Bo",		"Bq",
346c66b8046SYuri Pankov 	"Bsx",		"Bx",		"Db",		"Dc",
347c66b8046SYuri Pankov 	"Do",		"Dq",		"Ec",		"Ef",
348c66b8046SYuri Pankov 	"Em",		"Eo",		"Fx",		"Ms",
349c66b8046SYuri Pankov 	"No",		"Ns",		"Nx",		"Ox",
350c66b8046SYuri Pankov 	"Pc",		"Pf",		"Po",		"Pq",
351c66b8046SYuri Pankov 	"Qc",		"Ql",		"Qo",		"Qq",
352c66b8046SYuri Pankov 	"Re",		"Rs",		"Sc",		"So",
353c66b8046SYuri Pankov 	"Sq",		"Sm",		"Sx",		"Sy",
354c66b8046SYuri Pankov 	"Tn",		"Ux",		"Xc",		"Xo",
355c66b8046SYuri Pankov 	"Fo",		"Fc",		"Oo",		"Oc",
356c66b8046SYuri Pankov 	"Bk",		"Ek",		"Bt",		"Hf",
357c66b8046SYuri Pankov 	"Fr",		"Ud",		"Lb",		"Lp",
358c66b8046SYuri Pankov 	"Lk",		"Mt",		"Brq",		"Bro",
359c66b8046SYuri Pankov 	"Brc",		"%C",		"Es",		"En",
360c66b8046SYuri Pankov 	"Dx",		"%Q",		"%U",		"Ta",
361*4d131170SRobert Mustacchi 	"Tg",		NULL,
362c66b8046SYuri Pankov 	"TH",		"SH",		"SS",		"TP",
363cec8643bSMichal Nowak 	"TQ",
364c66b8046SYuri Pankov 	"LP",		"PP",		"P",		"IP",
365c66b8046SYuri Pankov 	"HP",		"SM",		"SB",		"BI",
366c66b8046SYuri Pankov 	"IB",		"BR",		"RB",		"R",
367c66b8046SYuri Pankov 	"B",		"I",		"IR",		"RI",
368c66b8046SYuri Pankov 	"RE",		"RS",		"DT",		"UC",
369c66b8046SYuri Pankov 	"PD",		"AT",		"in",
370cec8643bSMichal Nowak 	"SY",		"YS",		"OP",
371cec8643bSMichal Nowak 	"EX",		"EE",		"UR",
372c66b8046SYuri Pankov 	"UE",		"MT",		"ME",		NULL
37395c635efSGarrett D'Amore };
374c66b8046SYuri Pankov const	char *const *roff_name = __roff_name;
37595c635efSGarrett D'Amore 
376c66b8046SYuri Pankov static	struct roffmac	 roffs[TOKEN_NONE] = {
377cec8643bSMichal Nowak 	{ roff_noarg, NULL, NULL, 0 },  /* br */
378c66b8046SYuri Pankov 	{ roff_onearg, NULL, NULL, 0 },  /* ce */
379cec8643bSMichal Nowak 	{ roff_noarg, NULL, NULL, 0 },  /* fi */
380c66b8046SYuri Pankov 	{ roff_onearg, NULL, NULL, 0 },  /* ft */
381c66b8046SYuri Pankov 	{ roff_onearg, NULL, NULL, 0 },  /* ll */
382c66b8046SYuri Pankov 	{ roff_onearg, NULL, NULL, 0 },  /* mc */
383cec8643bSMichal Nowak 	{ roff_noarg, NULL, NULL, 0 },  /* nf */
384c66b8046SYuri Pankov 	{ roff_onearg, NULL, NULL, 0 },  /* po */
385c66b8046SYuri Pankov 	{ roff_onearg, NULL, NULL, 0 },  /* rj */
386c66b8046SYuri Pankov 	{ roff_onearg, NULL, NULL, 0 },  /* sp */
387c66b8046SYuri Pankov 	{ roff_manyarg, NULL, NULL, 0 },  /* ta */
388c66b8046SYuri Pankov 	{ roff_onearg, NULL, NULL, 0 },  /* ti */
389c66b8046SYuri Pankov 	{ NULL, NULL, NULL, 0 },  /* ROFF_MAX */
390c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* ab */
391c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* ad */
392c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* af */
393c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* aln */
394c66b8046SYuri Pankov 	{ roff_als, NULL, NULL, 0 },  /* als */
395c66b8046SYuri Pankov 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* am */
396c66b8046SYuri Pankov 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* am1 */
397c66b8046SYuri Pankov 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* ami */
398c66b8046SYuri Pankov 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* ami1 */
399c66b8046SYuri Pankov 	{ roff_ds, NULL, NULL, 0 },  /* as */
400c66b8046SYuri Pankov 	{ roff_ds, NULL, NULL, 0 },  /* as1 */
401c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* asciify */
402c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* backtrace */
403c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* bd */
404c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* bleedat */
405c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* blm */
406c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* box */
407c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* boxa */
408c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* bp */
409c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* BP */
410*4d131170SRobert Mustacchi 	{ roff_break, NULL, NULL, 0 },  /* break */
411c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* breakchar */
412c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* brnl */
413cec8643bSMichal Nowak 	{ roff_noarg, NULL, NULL, 0 },  /* brp */
414c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* brpnl */
415c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* c2 */
416c66b8046SYuri Pankov 	{ roff_cc, NULL, NULL, 0 },  /* cc */
417c66b8046SYuri Pankov 	{ roff_insec, NULL, NULL, 0 },  /* cf */
418c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* cflags */
419c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* ch */
420cec8643bSMichal Nowak 	{ roff_char, NULL, NULL, 0 },  /* char */
421c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* chop */
422c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* class */
423c66b8046SYuri Pankov 	{ roff_insec, NULL, NULL, 0 },  /* close */
424c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* CL */
425c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* color */
426c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* composite */
427c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* continue */
428c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* cp */
429c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* cropat */
430c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* cs */
431c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* cu */
432c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* da */
433c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* dch */
434c66b8046SYuri Pankov 	{ roff_Dd, NULL, NULL, 0 },  /* Dd */
435c66b8046SYuri Pankov 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* de */
436c66b8046SYuri Pankov 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* de1 */
437c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* defcolor */
438c66b8046SYuri Pankov 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* dei */
439c66b8046SYuri Pankov 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* dei1 */
440c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* device */
441c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* devicem */
442c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* di */
443c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* do */
444c66b8046SYuri Pankov 	{ roff_ds, NULL, NULL, 0 },  /* ds */
445c66b8046SYuri Pankov 	{ roff_ds, NULL, NULL, 0 },  /* ds1 */
446c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* dwh */
447c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* dt */
448c66b8046SYuri Pankov 	{ roff_ec, NULL, NULL, 0 },  /* ec */
449c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* ecr */
450c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* ecs */
451c66b8046SYuri Pankov 	{ roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },  /* el */
452c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* em */
453c66b8046SYuri Pankov 	{ roff_EN, NULL, NULL, 0 },  /* EN */
454c66b8046SYuri Pankov 	{ roff_eo, NULL, NULL, 0 },  /* eo */
455c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* EP */
456c66b8046SYuri Pankov 	{ roff_EQ, NULL, NULL, 0 },  /* EQ */
457c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* errprint */
458c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* ev */
459c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* evc */
460c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* ex */
461c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* fallback */
462c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* fam */
463c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* fc */
464c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* fchar */
465c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* fcolor */
466c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* fdeferlig */
467c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* feature */
468c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* fkern */
469c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* fl */
470c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* flig */
471c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* fp */
472c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* fps */
473c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* fschar */
474c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* fspacewidth */
475c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* fspecial */
476c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* ftr */
477c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* fzoom */
478c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* gcolor */
479c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* hc */
480c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* hcode */
481c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* hidechar */
482c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* hla */
483c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* hlm */
484c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* hpf */
485c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* hpfa */
486c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* hpfcode */
487c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* hw */
488c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* hy */
489c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* hylang */
490c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* hylen */
491c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* hym */
492c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* hypp */
493c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* hys */
494c66b8046SYuri Pankov 	{ roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },  /* ie */
495c66b8046SYuri Pankov 	{ roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },  /* if */
496c66b8046SYuri Pankov 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* ig */
497c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* index */
498c66b8046SYuri Pankov 	{ roff_it, NULL, NULL, 0 },  /* it */
499c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* itc */
500c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* IX */
501c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* kern */
502c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* kernafter */
503c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* kernbefore */
504c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* kernpair */
505c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* lc */
506c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* lc_ctype */
507c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* lds */
508c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* length */
509c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* letadj */
510c66b8046SYuri Pankov 	{ roff_insec, NULL, NULL, 0 },  /* lf */
511c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* lg */
512c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* lhang */
513c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* linetabs */
514c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* lnr */
515c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* lnrf */
516c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* lpfx */
517c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* ls */
518c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* lsm */
519c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* lt */
520c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* mediasize */
521c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* minss */
522c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* mk */
523c66b8046SYuri Pankov 	{ roff_insec, NULL, NULL, 0 },  /* mso */
524c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* na */
525c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* ne */
526c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* nh */
527c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* nhychar */
528c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* nm */
529c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* nn */
530cec8643bSMichal Nowak 	{ roff_nop, NULL, NULL, 0 },  /* nop */
531c66b8046SYuri Pankov 	{ roff_nr, NULL, NULL, 0 },  /* nr */
532c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* nrf */
533c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* nroff */
534c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* ns */
535c66b8046SYuri Pankov 	{ roff_insec, NULL, NULL, 0 },  /* nx */
536c66b8046SYuri Pankov 	{ roff_insec, NULL, NULL, 0 },  /* open */
537c66b8046SYuri Pankov 	{ roff_insec, NULL, NULL, 0 },  /* opena */
538c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* os */
539c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* output */
540c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* padj */
541c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* papersize */
542c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* pc */
543c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* pev */
544c66b8046SYuri Pankov 	{ roff_insec, NULL, NULL, 0 },  /* pi */
545c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* PI */
546c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* pl */
547c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* pm */
548c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* pn */
549c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* pnr */
550c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* ps */
551c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* psbb */
552c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* pshape */
553c66b8046SYuri Pankov 	{ roff_insec, NULL, NULL, 0 },  /* pso */
554c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* ptr */
555c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* pvs */
556c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* rchar */
557c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* rd */
558c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* recursionlimit */
559cec8643bSMichal Nowak 	{ roff_return, NULL, NULL, 0 },  /* return */
560c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* rfschar */
561c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* rhang */
562c66b8046SYuri Pankov 	{ roff_rm, NULL, NULL, 0 },  /* rm */
563c66b8046SYuri Pankov 	{ roff_rn, NULL, NULL, 0 },  /* rn */
564c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* rnn */
565c66b8046SYuri Pankov 	{ roff_rr, NULL, NULL, 0 },  /* rr */
566c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* rs */
567c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* rt */
568c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* schar */
569c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* sentchar */
570c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* shc */
571cec8643bSMichal Nowak 	{ roff_shift, NULL, NULL, 0 },  /* shift */
572c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* sizes */
573c66b8046SYuri Pankov 	{ roff_so, NULL, NULL, 0 },  /* so */
574c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* spacewidth */
575c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* special */
576c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* spreadwarn */
577c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* ss */
578c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* sty */
579c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* substring */
580c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* sv */
581c66b8046SYuri Pankov 	{ roff_insec, NULL, NULL, 0 },  /* sy */
582c66b8046SYuri Pankov 	{ roff_T_, NULL, NULL, 0 },  /* T& */
583c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* tc */
584c66b8046SYuri Pankov 	{ roff_TE, NULL, NULL, 0 },  /* TE */
585c66b8046SYuri Pankov 	{ roff_Dd, NULL, NULL, 0 },  /* TH */
586c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* tkf */
587c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* tl */
588c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* tm */
589c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* tm1 */
590c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* tmc */
591c66b8046SYuri Pankov 	{ roff_tr, NULL, NULL, 0 },  /* tr */
592c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* track */
593c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* transchar */
594c66b8046SYuri Pankov 	{ roff_insec, NULL, NULL, 0 },  /* trf */
595c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* trimat */
596c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* trin */
597c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* trnt */
598c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* troff */
599c66b8046SYuri Pankov 	{ roff_TS, NULL, NULL, 0 },  /* TS */
600c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* uf */
601c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* ul */
602c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* unformat */
603c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* unwatch */
604c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* unwatchn */
605c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* vpt */
606c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* vs */
607c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* warn */
608c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* warnscale */
609c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* watch */
610c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* watchlength */
611c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* watchn */
612c66b8046SYuri Pankov 	{ roff_unsupp, NULL, NULL, 0 },  /* wh */
613cec8643bSMichal Nowak 	{ roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /*while*/
614c66b8046SYuri Pankov 	{ roff_insec, NULL, NULL, 0 },  /* write */
615c66b8046SYuri Pankov 	{ roff_insec, NULL, NULL, 0 },  /* writec */
616c66b8046SYuri Pankov 	{ roff_insec, NULL, NULL, 0 },  /* writem */
617c66b8046SYuri Pankov 	{ roff_line_ignore, NULL, NULL, 0 },  /* xflag */
618c66b8046SYuri Pankov 	{ roff_cblock, NULL, NULL, 0 },  /* . */
619c66b8046SYuri Pankov 	{ roff_renamed, NULL, NULL, 0 },
620c66b8046SYuri Pankov 	{ roff_userdef, NULL, NULL, 0 }
621698f87a4SGarrett D'Amore };
622698f87a4SGarrett D'Amore 
62395c635efSGarrett D'Amore /* Array of injected predefined strings. */
62495c635efSGarrett D'Amore #define	PREDEFS_MAX	 38
62595c635efSGarrett D'Amore static	const struct predef predefs[PREDEFS_MAX] = {
62695c635efSGarrett D'Amore #include "predefs.in"
62795c635efSGarrett D'Amore };
62895c635efSGarrett D'Amore 
629c66b8046SYuri Pankov static	int	 roffce_lines;	/* number of input lines to center */
630c66b8046SYuri Pankov static	struct roff_node *roffce_node;  /* active request */
631698f87a4SGarrett D'Amore static	int	 roffit_lines;  /* number of lines to delay */
632698f87a4SGarrett D'Amore static	char	*roffit_macro;  /* nil-terminated macro line */
633698f87a4SGarrett D'Amore 
634260e9a87SYuri Pankov 
635371584c2SYuri Pankov /* --- request table ------------------------------------------------------ */
636371584c2SYuri Pankov 
637c66b8046SYuri Pankov struct ohash *
roffhash_alloc(enum roff_tok mintok,enum roff_tok maxtok)638c66b8046SYuri Pankov roffhash_alloc(enum roff_tok mintok, enum roff_tok maxtok)
63995c635efSGarrett D'Amore {
640c66b8046SYuri Pankov 	struct ohash	*htab;
641c66b8046SYuri Pankov 	struct roffreq	*req;
642c66b8046SYuri Pankov 	enum roff_tok	 tok;
643c66b8046SYuri Pankov 	size_t		 sz;
644c66b8046SYuri Pankov 	unsigned int	 slot;
64595c635efSGarrett D'Amore 
646c66b8046SYuri Pankov 	htab = mandoc_malloc(sizeof(*htab));
647c66b8046SYuri Pankov 	mandoc_ohash_init(htab, 8, offsetof(struct roffreq, name));
64895c635efSGarrett D'Amore 
649c66b8046SYuri Pankov 	for (tok = mintok; tok < maxtok; tok++) {
650c66b8046SYuri Pankov 		if (roff_name[tok] == NULL)
651c66b8046SYuri Pankov 			continue;
652c66b8046SYuri Pankov 		sz = strlen(roff_name[tok]);
653c66b8046SYuri Pankov 		req = mandoc_malloc(sizeof(*req) + sz + 1);
654c66b8046SYuri Pankov 		req->tok = tok;
655c66b8046SYuri Pankov 		memcpy(req->name, roff_name[tok], sz + 1);
656c66b8046SYuri Pankov 		slot = ohash_qlookup(htab, req->name);
657c66b8046SYuri Pankov 		ohash_insert(htab, slot, req);
658c66b8046SYuri Pankov 	}
659c66b8046SYuri Pankov 	return htab;
660c66b8046SYuri Pankov }
66195c635efSGarrett D'Amore 
662c66b8046SYuri Pankov void
roffhash_free(struct ohash * htab)663c66b8046SYuri Pankov roffhash_free(struct ohash *htab)
664c66b8046SYuri Pankov {
665c66b8046SYuri Pankov 	struct roffreq	*req;
666c66b8046SYuri Pankov 	unsigned int	 slot;
667c66b8046SYuri Pankov 
668c66b8046SYuri Pankov 	if (htab == NULL)
669c66b8046SYuri Pankov 		return;
670c66b8046SYuri Pankov 	for (req = ohash_first(htab, &slot); req != NULL;
671c66b8046SYuri Pankov 	     req = ohash_next(htab, &slot))
672c66b8046SYuri Pankov 		free(req);
673c66b8046SYuri Pankov 	ohash_delete(htab);
674c66b8046SYuri Pankov 	free(htab);
675c66b8046SYuri Pankov }
676c66b8046SYuri Pankov 
677c66b8046SYuri Pankov enum roff_tok
roffhash_find(struct ohash * htab,const char * name,size_t sz)678c66b8046SYuri Pankov roffhash_find(struct ohash *htab, const char *name, size_t sz)
679c66b8046SYuri Pankov {
680c66b8046SYuri Pankov 	struct roffreq	*req;
681c66b8046SYuri Pankov 	const char	*end;
682c66b8046SYuri Pankov 
683c66b8046SYuri Pankov 	if (sz) {
684c66b8046SYuri Pankov 		end = name + sz;
685c66b8046SYuri Pankov 		req = ohash_find(htab, ohash_qlookupi(htab, name, &end));
68695c635efSGarrett D'Amore 	} else
687c66b8046SYuri Pankov 		req = ohash_find(htab, ohash_qlookup(htab, name));
688c66b8046SYuri Pankov 	return req == NULL ? TOKEN_NONE : req->tok;
68995c635efSGarrett D'Amore }
69095c635efSGarrett D'Amore 
691371584c2SYuri Pankov /* --- stack of request blocks -------------------------------------------- */
692371584c2SYuri Pankov 
69395c635efSGarrett D'Amore /*
69495c635efSGarrett D'Amore  * Pop the current node off of the stack of roff instructions currently
695*4d131170SRobert Mustacchi  * pending.  Return 1 if it is a loop or 0 otherwise.
69695c635efSGarrett D'Amore  */
697cec8643bSMichal Nowak static int
roffnode_pop(struct roff * r)69895c635efSGarrett D'Amore roffnode_pop(struct roff *r)
69995c635efSGarrett D'Amore {
70095c635efSGarrett D'Amore 	struct roffnode	*p;
701cec8643bSMichal Nowak 	int		 inloop;
70295c635efSGarrett D'Amore 
70395c635efSGarrett D'Amore 	p = r->last;
704cec8643bSMichal Nowak 	inloop = p->tok == ROFF_while;
705cec8643bSMichal Nowak 	r->last = p->parent;
70695c635efSGarrett D'Amore 	free(p->name);
70795c635efSGarrett D'Amore 	free(p->end);
70895c635efSGarrett D'Amore 	free(p);
709cec8643bSMichal Nowak 	return inloop;
71095c635efSGarrett D'Amore }
71195c635efSGarrett D'Amore 
71295c635efSGarrett D'Amore /*
71395c635efSGarrett D'Amore  * Push a roff node onto the instruction stack.  This must later be
71495c635efSGarrett D'Amore  * removed with roffnode_pop().
71595c635efSGarrett D'Amore  */
71695c635efSGarrett D'Amore static void
roffnode_push(struct roff * r,enum roff_tok tok,const char * name,int line,int col)717c66b8046SYuri Pankov roffnode_push(struct roff *r, enum roff_tok tok, const char *name,
71895c635efSGarrett D'Amore 		int line, int col)
71995c635efSGarrett D'Amore {
72095c635efSGarrett D'Amore 	struct roffnode	*p;
72195c635efSGarrett D'Amore 
72295c635efSGarrett D'Amore 	p = mandoc_calloc(1, sizeof(struct roffnode));
72395c635efSGarrett D'Amore 	p->tok = tok;
72495c635efSGarrett D'Amore 	if (name)
72595c635efSGarrett D'Amore 		p->name = mandoc_strdup(name);
72695c635efSGarrett D'Amore 	p->parent = r->last;
72795c635efSGarrett D'Amore 	p->line = line;
72895c635efSGarrett D'Amore 	p->col = col;
729260e9a87SYuri Pankov 	p->rule = p->parent ? p->parent->rule : 0;
73095c635efSGarrett D'Amore 
73195c635efSGarrett D'Amore 	r->last = p;
73295c635efSGarrett D'Amore }
73395c635efSGarrett D'Amore 
734371584c2SYuri Pankov /* --- roff parser state data management ---------------------------------- */
735371584c2SYuri Pankov 
73695c635efSGarrett D'Amore static void
roff_free1(struct roff * r)73795c635efSGarrett D'Amore roff_free1(struct roff *r)
73895c635efSGarrett D'Amore {
73995c635efSGarrett D'Amore 	int		 i;
74095c635efSGarrett D'Amore 
741cec8643bSMichal Nowak 	tbl_free(r->first_tbl);
74295c635efSGarrett D'Amore 	r->first_tbl = r->last_tbl = r->tbl = NULL;
74395c635efSGarrett D'Amore 
744c66b8046SYuri Pankov 	eqn_free(r->last_eqn);
745c66b8046SYuri Pankov 	r->last_eqn = r->eqn = NULL;
74695c635efSGarrett D'Amore 
747cec8643bSMichal Nowak 	while (r->mstackpos >= 0)
748cec8643bSMichal Nowak 		roff_userret(r);
749cec8643bSMichal Nowak 
75095c635efSGarrett D'Amore 	while (r->last)
75195c635efSGarrett D'Amore 		roffnode_pop(r);
75295c635efSGarrett D'Amore 
753260e9a87SYuri Pankov 	free (r->rstack);
754260e9a87SYuri Pankov 	r->rstack = NULL;
755260e9a87SYuri Pankov 	r->rstacksz = 0;
756260e9a87SYuri Pankov 	r->rstackpos = -1;
75795c635efSGarrett D'Amore 
758698f87a4SGarrett D'Amore 	roff_freereg(r->regtab);
759698f87a4SGarrett D'Amore 	r->regtab = NULL;
760698f87a4SGarrett D'Amore 
761260e9a87SYuri Pankov 	roff_freestr(r->strtab);
762c66b8046SYuri Pankov 	roff_freestr(r->rentab);
763260e9a87SYuri Pankov 	roff_freestr(r->xmbtab);
764c66b8046SYuri Pankov 	r->strtab = r->rentab = r->xmbtab = NULL;
765260e9a87SYuri Pankov 
76695c635efSGarrett D'Amore 	if (r->xtab)
76795c635efSGarrett D'Amore 		for (i = 0; i < 128; i++)
76895c635efSGarrett D'Amore 			free(r->xtab[i].p);
76995c635efSGarrett D'Amore 	free(r->xtab);
77095c635efSGarrett D'Amore 	r->xtab = NULL;
77195c635efSGarrett D'Amore }
77295c635efSGarrett D'Amore 
77395c635efSGarrett D'Amore void
roff_reset(struct roff * r)77495c635efSGarrett D'Amore roff_reset(struct roff *r)
77595c635efSGarrett D'Amore {
77695c635efSGarrett D'Amore 	roff_free1(r);
777*4d131170SRobert Mustacchi 	r->options |= MPARSE_COMMENT;
778260e9a87SYuri Pankov 	r->format = r->options & (MPARSE_MDOC | MPARSE_MAN);
779c66b8046SYuri Pankov 	r->control = '\0';
780c66b8046SYuri Pankov 	r->escape = '\\';
781c66b8046SYuri Pankov 	roffce_lines = 0;
782c66b8046SYuri Pankov 	roffce_node = NULL;
783c66b8046SYuri Pankov 	roffit_lines = 0;
784c66b8046SYuri Pankov 	roffit_macro = NULL;
78595c635efSGarrett D'Amore }
78695c635efSGarrett D'Amore 
78795c635efSGarrett D'Amore void
roff_free(struct roff * r)78895c635efSGarrett D'Amore roff_free(struct roff *r)
78995c635efSGarrett D'Amore {
790cec8643bSMichal Nowak 	int		 i;
791cec8643bSMichal Nowak 
79295c635efSGarrett D'Amore 	roff_free1(r);
793cec8643bSMichal Nowak 	for (i = 0; i < r->mstacksz; i++)
794cec8643bSMichal Nowak 		free(r->mstack[i].argv);
795cec8643bSMichal Nowak 	free(r->mstack);
796c66b8046SYuri Pankov 	roffhash_free(r->reqtab);
79795c635efSGarrett D'Amore 	free(r);
79895c635efSGarrett D'Amore }
79995c635efSGarrett D'Amore 
80095c635efSGarrett D'Amore struct roff *
roff_alloc(int options)801cec8643bSMichal Nowak roff_alloc(int options)
80295c635efSGarrett D'Amore {
80395c635efSGarrett D'Amore 	struct roff	*r;
80495c635efSGarrett D'Amore 
80595c635efSGarrett D'Amore 	r = mandoc_calloc(1, sizeof(struct roff));
8066640c13bSYuri Pankov 	r->reqtab = roffhash_alloc(0, ROFF_RENAMED);
807*4d131170SRobert Mustacchi 	r->options = options | MPARSE_COMMENT;
808260e9a87SYuri Pankov 	r->format = options & (MPARSE_MDOC | MPARSE_MAN);
809cec8643bSMichal Nowak 	r->mstackpos = -1;
81095c635efSGarrett D'Amore 	r->rstackpos = -1;
811c66b8046SYuri Pankov 	r->escape = '\\';
812371584c2SYuri Pankov 	return r;
81395c635efSGarrett D'Amore }
81495c635efSGarrett D'Amore 
815371584c2SYuri Pankov /* --- syntax tree state data management ---------------------------------- */
816371584c2SYuri Pankov 
817371584c2SYuri Pankov static void
roff_man_free1(struct roff_man * man)818371584c2SYuri Pankov roff_man_free1(struct roff_man *man)
819371584c2SYuri Pankov {
820cec8643bSMichal Nowak 	if (man->meta.first != NULL)
821cec8643bSMichal Nowak 		roff_node_delete(man, man->meta.first);
822371584c2SYuri Pankov 	free(man->meta.msec);
823371584c2SYuri Pankov 	free(man->meta.vol);
824371584c2SYuri Pankov 	free(man->meta.os);
825371584c2SYuri Pankov 	free(man->meta.arch);
826371584c2SYuri Pankov 	free(man->meta.title);
827371584c2SYuri Pankov 	free(man->meta.name);
828371584c2SYuri Pankov 	free(man->meta.date);
829cec8643bSMichal Nowak 	free(man->meta.sodest);
830cec8643bSMichal Nowak }
831cec8643bSMichal Nowak 
832cec8643bSMichal Nowak void
roff_state_reset(struct roff_man * man)833cec8643bSMichal Nowak roff_state_reset(struct roff_man *man)
834cec8643bSMichal Nowak {
835cec8643bSMichal Nowak 	man->last = man->meta.first;
836cec8643bSMichal Nowak 	man->last_es = NULL;
837cec8643bSMichal Nowak 	man->flags = 0;
838cec8643bSMichal Nowak 	man->lastsec = man->lastnamed = SEC_NONE;
839cec8643bSMichal Nowak 	man->next = ROFF_NEXT_CHILD;
840cec8643bSMichal Nowak 	roff_setreg(man->roff, "nS", 0, '=');
841371584c2SYuri Pankov }
842371584c2SYuri Pankov 
843371584c2SYuri Pankov static void
roff_man_alloc1(struct roff_man * man)844371584c2SYuri Pankov roff_man_alloc1(struct roff_man *man)
845371584c2SYuri Pankov {
846371584c2SYuri Pankov 	memset(&man->meta, 0, sizeof(man->meta));
847cec8643bSMichal Nowak 	man->meta.first = mandoc_calloc(1, sizeof(*man->meta.first));
848cec8643bSMichal Nowak 	man->meta.first->type = ROFFT_ROOT;
849cec8643bSMichal Nowak 	man->meta.macroset = MACROSET_NONE;
850cec8643bSMichal Nowak 	roff_state_reset(man);
851371584c2SYuri Pankov }
852371584c2SYuri Pankov 
853371584c2SYuri Pankov void
roff_man_reset(struct roff_man * man)854371584c2SYuri Pankov roff_man_reset(struct roff_man *man)
855371584c2SYuri Pankov {
856371584c2SYuri Pankov 	roff_man_free1(man);
857371584c2SYuri Pankov 	roff_man_alloc1(man);
858371584c2SYuri Pankov }
859371584c2SYuri Pankov 
860371584c2SYuri Pankov void
roff_man_free(struct roff_man * man)861371584c2SYuri Pankov roff_man_free(struct roff_man *man)
862371584c2SYuri Pankov {
863371584c2SYuri Pankov 	roff_man_free1(man);
864371584c2SYuri Pankov 	free(man);
865371584c2SYuri Pankov }
866371584c2SYuri Pankov 
867371584c2SYuri Pankov struct roff_man *
roff_man_alloc(struct roff * roff,const char * os_s,int quick)868cec8643bSMichal Nowak roff_man_alloc(struct roff *roff, const char *os_s, int quick)
869371584c2SYuri Pankov {
870371584c2SYuri Pankov 	struct roff_man *man;
871371584c2SYuri Pankov 
872371584c2SYuri Pankov 	man = mandoc_calloc(1, sizeof(*man));
873371584c2SYuri Pankov 	man->roff = roff;
874c66b8046SYuri Pankov 	man->os_s = os_s;
875371584c2SYuri Pankov 	man->quick = quick;
876371584c2SYuri Pankov 	roff_man_alloc1(man);
877c66b8046SYuri Pankov 	roff->man = man;
878371584c2SYuri Pankov 	return man;
879371584c2SYuri Pankov }
880371584c2SYuri Pankov 
881371584c2SYuri Pankov /* --- syntax tree handling ----------------------------------------------- */
882371584c2SYuri Pankov 
883371584c2SYuri Pankov struct roff_node *
roff_node_alloc(struct roff_man * man,int line,int pos,enum roff_type type,int tok)884371584c2SYuri Pankov roff_node_alloc(struct roff_man *man, int line, int pos,
885371584c2SYuri Pankov 	enum roff_type type, int tok)
886371584c2SYuri Pankov {
887371584c2SYuri Pankov 	struct roff_node	*n;
888371584c2SYuri Pankov 
889371584c2SYuri Pankov 	n = mandoc_calloc(1, sizeof(*n));
890371584c2SYuri Pankov 	n->line = line;
891371584c2SYuri Pankov 	n->pos = pos;
892371584c2SYuri Pankov 	n->tok = tok;
893371584c2SYuri Pankov 	n->type = type;
894371584c2SYuri Pankov 	n->sec = man->lastsec;
895371584c2SYuri Pankov 
896371584c2SYuri Pankov 	if (man->flags & MDOC_SYNOPSIS)
897a40ea1a7SYuri Pankov 		n->flags |= NODE_SYNPRETTY;
898371584c2SYuri Pankov 	else
899a40ea1a7SYuri Pankov 		n->flags &= ~NODE_SYNPRETTY;
900cec8643bSMichal Nowak 	if ((man->flags & (ROFF_NOFILL | ROFF_NONOFILL)) == ROFF_NOFILL)
901cec8643bSMichal Nowak 		n->flags |= NODE_NOFILL;
902cec8643bSMichal Nowak 	else
903cec8643bSMichal Nowak 		n->flags &= ~NODE_NOFILL;
904371584c2SYuri Pankov 	if (man->flags & MDOC_NEWLINE)
905a40ea1a7SYuri Pankov 		n->flags |= NODE_LINE;
906371584c2SYuri Pankov 	man->flags &= ~MDOC_NEWLINE;
907371584c2SYuri Pankov 
908371584c2SYuri Pankov 	return n;
909371584c2SYuri Pankov }
910371584c2SYuri Pankov 
911371584c2SYuri Pankov void
roff_node_append(struct roff_man * man,struct roff_node * n)912371584c2SYuri Pankov roff_node_append(struct roff_man *man, struct roff_node *n)
913371584c2SYuri Pankov {
914371584c2SYuri Pankov 
915371584c2SYuri Pankov 	switch (man->next) {
916371584c2SYuri Pankov 	case ROFF_NEXT_SIBLING:
917371584c2SYuri Pankov 		if (man->last->next != NULL) {
918371584c2SYuri Pankov 			n->next = man->last->next;
919371584c2SYuri Pankov 			man->last->next->prev = n;
920371584c2SYuri Pankov 		} else
921371584c2SYuri Pankov 			man->last->parent->last = n;
922371584c2SYuri Pankov 		man->last->next = n;
923371584c2SYuri Pankov 		n->prev = man->last;
924371584c2SYuri Pankov 		n->parent = man->last->parent;
925371584c2SYuri Pankov 		break;
926371584c2SYuri Pankov 	case ROFF_NEXT_CHILD:
927a40ea1a7SYuri Pankov 		if (man->last->child != NULL) {
928a40ea1a7SYuri Pankov 			n->next = man->last->child;
929a40ea1a7SYuri Pankov 			man->last->child->prev = n;
930a40ea1a7SYuri Pankov 		} else
931a40ea1a7SYuri Pankov 			man->last->last = n;
932371584c2SYuri Pankov 		man->last->child = n;
933371584c2SYuri Pankov 		n->parent = man->last;
934371584c2SYuri Pankov 		break;
935371584c2SYuri Pankov 	default:
936371584c2SYuri Pankov 		abort();
937371584c2SYuri Pankov 	}
938371584c2SYuri Pankov 	man->last = n;
939371584c2SYuri Pankov 
940371584c2SYuri Pankov 	switch (n->type) {
941371584c2SYuri Pankov 	case ROFFT_HEAD:
942371584c2SYuri Pankov 		n->parent->head = n;
943371584c2SYuri Pankov 		break;
944371584c2SYuri Pankov 	case ROFFT_BODY:
945371584c2SYuri Pankov 		if (n->end != ENDBODY_NOT)
946371584c2SYuri Pankov 			return;
947371584c2SYuri Pankov 		n->parent->body = n;
948371584c2SYuri Pankov 		break;
949371584c2SYuri Pankov 	case ROFFT_TAIL:
950371584c2SYuri Pankov 		n->parent->tail = n;
951371584c2SYuri Pankov 		break;
952371584c2SYuri Pankov 	default:
953371584c2SYuri Pankov 		return;
954371584c2SYuri Pankov 	}
955371584c2SYuri Pankov 
956371584c2SYuri Pankov 	/*
957371584c2SYuri Pankov 	 * Copy over the normalised-data pointer of our parent.  Not
958371584c2SYuri Pankov 	 * everybody has one, but copying a null pointer is fine.
959371584c2SYuri Pankov 	 */
960371584c2SYuri Pankov 
961371584c2SYuri Pankov 	n->norm = n->parent->norm;
962371584c2SYuri Pankov 	assert(n->parent->type == ROFFT_BLOCK);
963371584c2SYuri Pankov }
964371584c2SYuri Pankov 
965371584c2SYuri Pankov void
roff_word_alloc(struct roff_man * man,int line,int pos,const char * word)966371584c2SYuri Pankov roff_word_alloc(struct roff_man *man, int line, int pos, const char *word)
967371584c2SYuri Pankov {
968371584c2SYuri Pankov 	struct roff_node	*n;
969371584c2SYuri Pankov 
970371584c2SYuri Pankov 	n = roff_node_alloc(man, line, pos, ROFFT_TEXT, TOKEN_NONE);
971371584c2SYuri Pankov 	n->string = roff_strdup(man->roff, word);
972371584c2SYuri Pankov 	roff_node_append(man, n);
973a40ea1a7SYuri Pankov 	n->flags |= NODE_VALID | NODE_ENDED;
974371584c2SYuri Pankov 	man->next = ROFF_NEXT_SIBLING;
975371584c2SYuri Pankov }
976371584c2SYuri Pankov 
977371584c2SYuri Pankov void
roff_word_append(struct roff_man * man,const char * word)978371584c2SYuri Pankov roff_word_append(struct roff_man *man, const char *word)
979371584c2SYuri Pankov {
980371584c2SYuri Pankov 	struct roff_node	*n;
981371584c2SYuri Pankov 	char			*addstr, *newstr;
982371584c2SYuri Pankov 
983371584c2SYuri Pankov 	n = man->last;
984371584c2SYuri Pankov 	addstr = roff_strdup(man->roff, word);
985371584c2SYuri Pankov 	mandoc_asprintf(&newstr, "%s %s", n->string, addstr);
986371584c2SYuri Pankov 	free(addstr);
987371584c2SYuri Pankov 	free(n->string);
988371584c2SYuri Pankov 	n->string = newstr;
989371584c2SYuri Pankov 	man->next = ROFF_NEXT_SIBLING;
990371584c2SYuri Pankov }
991371584c2SYuri Pankov 
992371584c2SYuri Pankov void
roff_elem_alloc(struct roff_man * man,int line,int pos,int tok)993371584c2SYuri Pankov roff_elem_alloc(struct roff_man *man, int line, int pos, int tok)
994371584c2SYuri Pankov {
995371584c2SYuri Pankov 	struct roff_node	*n;
996371584c2SYuri Pankov 
997371584c2SYuri Pankov 	n = roff_node_alloc(man, line, pos, ROFFT_ELEM, tok);
998371584c2SYuri Pankov 	roff_node_append(man, n);
999371584c2SYuri Pankov 	man->next = ROFF_NEXT_CHILD;
1000371584c2SYuri Pankov }
1001371584c2SYuri Pankov 
1002371584c2SYuri Pankov struct roff_node *
roff_block_alloc(struct roff_man * man,int line,int pos,int tok)1003371584c2SYuri Pankov roff_block_alloc(struct roff_man *man, int line, int pos, int tok)
1004371584c2SYuri Pankov {
1005371584c2SYuri Pankov 	struct roff_node	*n;
1006371584c2SYuri Pankov 
1007371584c2SYuri Pankov 	n = roff_node_alloc(man, line, pos, ROFFT_BLOCK, tok);
1008371584c2SYuri Pankov 	roff_node_append(man, n);
1009371584c2SYuri Pankov 	man->next = ROFF_NEXT_CHILD;
1010371584c2SYuri Pankov 	return n;
1011371584c2SYuri Pankov }
1012371584c2SYuri Pankov 
1013371584c2SYuri Pankov struct roff_node *
roff_head_alloc(struct roff_man * man,int line,int pos,int tok)1014371584c2SYuri Pankov roff_head_alloc(struct roff_man *man, int line, int pos, int tok)
1015371584c2SYuri Pankov {
1016371584c2SYuri Pankov 	struct roff_node	*n;
1017371584c2SYuri Pankov 
1018371584c2SYuri Pankov 	n = roff_node_alloc(man, line, pos, ROFFT_HEAD, tok);
1019371584c2SYuri Pankov 	roff_node_append(man, n);
1020371584c2SYuri Pankov 	man->next = ROFF_NEXT_CHILD;
1021371584c2SYuri Pankov 	return n;
1022371584c2SYuri Pankov }
1023371584c2SYuri Pankov 
1024371584c2SYuri Pankov struct roff_node *
roff_body_alloc(struct roff_man * man,int line,int pos,int tok)1025371584c2SYuri Pankov roff_body_alloc(struct roff_man *man, int line, int pos, int tok)
1026371584c2SYuri Pankov {
1027371584c2SYuri Pankov 	struct roff_node	*n;
1028371584c2SYuri Pankov 
1029371584c2SYuri Pankov 	n = roff_node_alloc(man, line, pos, ROFFT_BODY, tok);
1030371584c2SYuri Pankov 	roff_node_append(man, n);
1031371584c2SYuri Pankov 	man->next = ROFF_NEXT_CHILD;
1032371584c2SYuri Pankov 	return n;
1033371584c2SYuri Pankov }
1034371584c2SYuri Pankov 
1035c66b8046SYuri Pankov static void
roff_addtbl(struct roff_man * man,int line,struct tbl_node * tbl)1036cec8643bSMichal Nowak roff_addtbl(struct roff_man *man, int line, struct tbl_node *tbl)
1037371584c2SYuri Pankov {
1038371584c2SYuri Pankov 	struct roff_node	*n;
1039cec8643bSMichal Nowak 	struct tbl_span		*span;
1040371584c2SYuri Pankov 
1041cec8643bSMichal Nowak 	if (man->meta.macroset == MACROSET_MAN)
1042c66b8046SYuri Pankov 		man_breakscope(man, ROFF_TS);
1043c66b8046SYuri Pankov 	while ((span = tbl_span(tbl)) != NULL) {
1044cec8643bSMichal Nowak 		n = roff_node_alloc(man, line, 0, ROFFT_TBL, TOKEN_NONE);
1045c66b8046SYuri Pankov 		n->span = span;
1046371584c2SYuri Pankov 		roff_node_append(man, n);
1047a40ea1a7SYuri Pankov 		n->flags |= NODE_VALID | NODE_ENDED;
1048371584c2SYuri Pankov 		man->next = ROFF_NEXT_SIBLING;
1049371584c2SYuri Pankov 	}
1050c66b8046SYuri Pankov }
1051371584c2SYuri Pankov 
1052371584c2SYuri Pankov void
roff_node_unlink(struct roff_man * man,struct roff_node * n)1053371584c2SYuri Pankov roff_node_unlink(struct roff_man *man, struct roff_node *n)
1054371584c2SYuri Pankov {
1055371584c2SYuri Pankov 
1056371584c2SYuri Pankov 	/* Adjust siblings. */
1057371584c2SYuri Pankov 
1058371584c2SYuri Pankov 	if (n->prev)
1059371584c2SYuri Pankov 		n->prev->next = n->next;
1060371584c2SYuri Pankov 	if (n->next)
1061371584c2SYuri Pankov 		n->next->prev = n->prev;
1062371584c2SYuri Pankov 
1063371584c2SYuri Pankov 	/* Adjust parent. */
1064371584c2SYuri Pankov 
1065371584c2SYuri Pankov 	if (n->parent != NULL) {
1066371584c2SYuri Pankov 		if (n->parent->child == n)
1067371584c2SYuri Pankov 			n->parent->child = n->next;
1068371584c2SYuri Pankov 		if (n->parent->last == n)
1069371584c2SYuri Pankov 			n->parent->last = n->prev;
1070371584c2SYuri Pankov 	}
1071371584c2SYuri Pankov 
1072371584c2SYuri Pankov 	/* Adjust parse point. */
1073371584c2SYuri Pankov 
1074371584c2SYuri Pankov 	if (man == NULL)
1075371584c2SYuri Pankov 		return;
1076371584c2SYuri Pankov 	if (man->last == n) {
1077371584c2SYuri Pankov 		if (n->prev == NULL) {
1078371584c2SYuri Pankov 			man->last = n->parent;
1079371584c2SYuri Pankov 			man->next = ROFF_NEXT_CHILD;
1080371584c2SYuri Pankov 		} else {
1081371584c2SYuri Pankov 			man->last = n->prev;
1082371584c2SYuri Pankov 			man->next = ROFF_NEXT_SIBLING;
1083371584c2SYuri Pankov 		}
1084371584c2SYuri Pankov 	}
1085cec8643bSMichal Nowak 	if (man->meta.first == n)
1086cec8643bSMichal Nowak 		man->meta.first = NULL;
1087cec8643bSMichal Nowak }
1088cec8643bSMichal Nowak 
1089cec8643bSMichal Nowak void
roff_node_relink(struct roff_man * man,struct roff_node * n)1090cec8643bSMichal Nowak roff_node_relink(struct roff_man *man, struct roff_node *n)
1091cec8643bSMichal Nowak {
1092cec8643bSMichal Nowak 	roff_node_unlink(man, n);
1093cec8643bSMichal Nowak 	n->prev = n->next = NULL;
1094cec8643bSMichal Nowak 	roff_node_append(man, n);
1095371584c2SYuri Pankov }
1096371584c2SYuri Pankov 
1097371584c2SYuri Pankov void
roff_node_free(struct roff_node * n)1098371584c2SYuri Pankov roff_node_free(struct roff_node *n)
1099371584c2SYuri Pankov {
1100371584c2SYuri Pankov 
1101371584c2SYuri Pankov 	if (n->args != NULL)
1102371584c2SYuri Pankov 		mdoc_argv_free(n->args);
1103371584c2SYuri Pankov 	if (n->type == ROFFT_BLOCK || n->type == ROFFT_ELEM)
1104371584c2SYuri Pankov 		free(n->norm);
1105c66b8046SYuri Pankov 	eqn_box_free(n->eqn);
1106371584c2SYuri Pankov 	free(n->string);
1107*4d131170SRobert Mustacchi 	free(n->tag);
1108371584c2SYuri Pankov 	free(n);
1109371584c2SYuri Pankov }
1110371584c2SYuri Pankov 
1111371584c2SYuri Pankov void
roff_node_delete(struct roff_man * man,struct roff_node * n)1112371584c2SYuri Pankov roff_node_delete(struct roff_man *man, struct roff_node *n)
1113371584c2SYuri Pankov {
1114371584c2SYuri Pankov 
1115371584c2SYuri Pankov 	while (n->child != NULL)
1116371584c2SYuri Pankov 		roff_node_delete(man, n->child);
1117371584c2SYuri Pankov 	roff_node_unlink(man, n);
1118371584c2SYuri Pankov 	roff_node_free(n);
1119371584c2SYuri Pankov }
1120371584c2SYuri Pankov 
1121*4d131170SRobert Mustacchi int
roff_node_transparent(struct roff_node * n)1122*4d131170SRobert Mustacchi roff_node_transparent(struct roff_node *n)
1123*4d131170SRobert Mustacchi {
1124*4d131170SRobert Mustacchi 	if (n == NULL)
1125*4d131170SRobert Mustacchi 		return 0;
1126*4d131170SRobert Mustacchi 	if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
1127*4d131170SRobert Mustacchi 		return 1;
1128*4d131170SRobert Mustacchi 	return roff_tok_transparent(n->tok);
1129*4d131170SRobert Mustacchi }
1130*4d131170SRobert Mustacchi 
1131*4d131170SRobert Mustacchi int
roff_tok_transparent(enum roff_tok tok)1132*4d131170SRobert Mustacchi roff_tok_transparent(enum roff_tok tok)
1133*4d131170SRobert Mustacchi {
1134*4d131170SRobert Mustacchi 	switch (tok) {
1135*4d131170SRobert Mustacchi 	case ROFF_ft:
1136*4d131170SRobert Mustacchi 	case ROFF_ll:
1137*4d131170SRobert Mustacchi 	case ROFF_mc:
1138*4d131170SRobert Mustacchi 	case ROFF_po:
1139*4d131170SRobert Mustacchi 	case ROFF_ta:
1140*4d131170SRobert Mustacchi 	case MDOC_Db:
1141*4d131170SRobert Mustacchi 	case MDOC_Es:
1142*4d131170SRobert Mustacchi 	case MDOC_Sm:
1143*4d131170SRobert Mustacchi 	case MDOC_Tg:
1144*4d131170SRobert Mustacchi 	case MAN_DT:
1145*4d131170SRobert Mustacchi 	case MAN_UC:
1146*4d131170SRobert Mustacchi 	case MAN_PD:
1147*4d131170SRobert Mustacchi 	case MAN_AT:
1148*4d131170SRobert Mustacchi 		return 1;
1149*4d131170SRobert Mustacchi 	default:
1150*4d131170SRobert Mustacchi 		return 0;
1151*4d131170SRobert Mustacchi 	}
1152*4d131170SRobert Mustacchi }
1153*4d131170SRobert Mustacchi 
1154*4d131170SRobert Mustacchi struct roff_node *
roff_node_child(struct roff_node * n)1155*4d131170SRobert Mustacchi roff_node_child(struct roff_node *n)
1156*4d131170SRobert Mustacchi {
1157*4d131170SRobert Mustacchi 	for (n = n->child; roff_node_transparent(n); n = n->next)
1158*4d131170SRobert Mustacchi 		continue;
1159*4d131170SRobert Mustacchi 	return n;
1160*4d131170SRobert Mustacchi }
1161*4d131170SRobert Mustacchi 
1162*4d131170SRobert Mustacchi struct roff_node *
roff_node_prev(struct roff_node * n)1163*4d131170SRobert Mustacchi roff_node_prev(struct roff_node *n)
1164*4d131170SRobert Mustacchi {
1165*4d131170SRobert Mustacchi 	do {
1166*4d131170SRobert Mustacchi 		n = n->prev;
1167*4d131170SRobert Mustacchi 	} while (roff_node_transparent(n));
1168*4d131170SRobert Mustacchi 	return n;
1169*4d131170SRobert Mustacchi }
1170*4d131170SRobert Mustacchi 
1171*4d131170SRobert Mustacchi struct roff_node *
roff_node_next(struct roff_node * n)1172*4d131170SRobert Mustacchi roff_node_next(struct roff_node *n)
1173*4d131170SRobert Mustacchi {
1174*4d131170SRobert Mustacchi 	do {
1175*4d131170SRobert Mustacchi 		n = n->next;
1176*4d131170SRobert Mustacchi 	} while (roff_node_transparent(n));
1177*4d131170SRobert Mustacchi 	return n;
1178*4d131170SRobert Mustacchi }
1179*4d131170SRobert Mustacchi 
1180371584c2SYuri Pankov void
deroff(char ** dest,const struct roff_node * n)1181371584c2SYuri Pankov deroff(char **dest, const struct roff_node *n)
1182371584c2SYuri Pankov {
1183371584c2SYuri Pankov 	char	*cp;
1184371584c2SYuri Pankov 	size_t	 sz;
1185371584c2SYuri Pankov 
1186*4d131170SRobert Mustacchi 	if (n->string == NULL) {
1187371584c2SYuri Pankov 		for (n = n->child; n != NULL; n = n->next)
1188371584c2SYuri Pankov 			deroff(dest, n);
1189371584c2SYuri Pankov 		return;
1190371584c2SYuri Pankov 	}
1191371584c2SYuri Pankov 
1192a40ea1a7SYuri Pankov 	/* Skip leading whitespace. */
1193371584c2SYuri Pankov 
1194a40ea1a7SYuri Pankov 	for (cp = n->string; *cp != '\0'; cp++) {
1195a40ea1a7SYuri Pankov 		if (cp[0] == '\\' && cp[1] != '\0' &&
1196a40ea1a7SYuri Pankov 		    strchr(" %&0^|~", cp[1]) != NULL)
1197371584c2SYuri Pankov 			cp++;
1198a40ea1a7SYuri Pankov 		else if ( ! isspace((unsigned char)*cp))
1199371584c2SYuri Pankov 			break;
1200371584c2SYuri Pankov 	}
1201371584c2SYuri Pankov 
1202a40ea1a7SYuri Pankov 	/* Skip trailing backslash. */
1203a40ea1a7SYuri Pankov 
1204a40ea1a7SYuri Pankov 	sz = strlen(cp);
1205c66b8046SYuri Pankov 	if (sz > 0 && cp[sz - 1] == '\\')
1206a40ea1a7SYuri Pankov 		sz--;
1207a40ea1a7SYuri Pankov 
1208371584c2SYuri Pankov 	/* Skip trailing whitespace. */
1209371584c2SYuri Pankov 
1210a40ea1a7SYuri Pankov 	for (; sz; sz--)
1211371584c2SYuri Pankov 		if ( ! isspace((unsigned char)cp[sz-1]))
1212371584c2SYuri Pankov 			break;
1213371584c2SYuri Pankov 
1214371584c2SYuri Pankov 	/* Skip empty strings. */
1215371584c2SYuri Pankov 
1216371584c2SYuri Pankov 	if (sz == 0)
1217371584c2SYuri Pankov 		return;
1218371584c2SYuri Pankov 
1219371584c2SYuri Pankov 	if (*dest == NULL) {
1220371584c2SYuri Pankov 		*dest = mandoc_strndup(cp, sz);
1221371584c2SYuri Pankov 		return;
1222371584c2SYuri Pankov 	}
1223371584c2SYuri Pankov 
1224371584c2SYuri Pankov 	mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp);
1225371584c2SYuri Pankov 	free(*dest);
1226371584c2SYuri Pankov 	*dest = cp;
1227371584c2SYuri Pankov }
1228371584c2SYuri Pankov 
1229371584c2SYuri Pankov /* --- main functions of the roff parser ---------------------------------- */
1230371584c2SYuri Pankov 
123195c635efSGarrett D'Amore /*
1232cec8643bSMichal Nowak  * In the current line, expand escape sequences that produce parsable
1233cec8643bSMichal Nowak  * input text.  Also check the syntax of the remaining escape sequences,
1234cec8643bSMichal Nowak  * which typically produce output glyphs or change formatter state.
123595c635efSGarrett D'Amore  */
1236cec8643bSMichal Nowak static int
roff_expand(struct roff * r,struct buf * buf,int ln,int pos,char newesc)1237cec8643bSMichal Nowak roff_expand(struct roff *r, struct buf *buf, int ln, int pos, char newesc)
123895c635efSGarrett D'Amore {
1239cec8643bSMichal Nowak 	struct mctx	*ctx;	/* current macro call context */
1240260e9a87SYuri Pankov 	char		 ubuf[24]; /* buffer to print the number */
12416640c13bSYuri Pankov 	struct roff_node *n;	/* used for header comments */
1242260e9a87SYuri Pankov 	const char	*start;	/* start of the string to process */
1243260e9a87SYuri Pankov 	char		*stesc;	/* start of an escape sequence ('\\') */
1244cec8643bSMichal Nowak 	const char	*esct;	/* type of esccape sequence */
12456640c13bSYuri Pankov 	char		*ep;	/* end of comment string */
124695c635efSGarrett D'Amore 	const char	*stnam;	/* start of the name, after "[(*" */
124795c635efSGarrett D'Amore 	const char	*cp;	/* end of the name, e.g. before ']' */
124895c635efSGarrett D'Amore 	const char	*res;	/* the string to be substituted */
1249260e9a87SYuri Pankov 	char		*nbuf;	/* new buffer to copy buf->buf to */
1250698f87a4SGarrett D'Amore 	size_t		 maxl;  /* expected length of the escape name */
1251698f87a4SGarrett D'Amore 	size_t		 naml;	/* actual length of the escape name */
1252cec8643bSMichal Nowak 	size_t		 asz;	/* length of the replacement */
1253cec8643bSMichal Nowak 	size_t		 rsz;	/* length of the rest of the string */
1254260e9a87SYuri Pankov 	int		 inaml;	/* length returned from mandoc_escape() */
1255698f87a4SGarrett D'Amore 	int		 expand_count;	/* to avoid infinite loops */
1256260e9a87SYuri Pankov 	int		 npos;	/* position in numeric expression */
1257260e9a87SYuri Pankov 	int		 arg_complete; /* argument not interrupted by eol */
1258cec8643bSMichal Nowak 	int		 quote_args; /* true for \\$@, false for \\$* */
1259c66b8046SYuri Pankov 	int		 done;	/* no more input available */
1260c66b8046SYuri Pankov 	int		 deftype; /* type of definition to paste */
1261c66b8046SYuri Pankov 	int		 rcsid;	/* kind of RCS id seen */
1262cec8643bSMichal Nowak 	enum mandocerr	 err;	/* for escape sequence problems */
12636640c13bSYuri Pankov 	char		 sign;	/* increment number register */
1264260e9a87SYuri Pankov 	char		 term;	/* character terminating the escape */
126595c635efSGarrett D'Amore 
1266c66b8046SYuri Pankov 	/* Search forward for comments. */
1267c66b8046SYuri Pankov 
1268c66b8046SYuri Pankov 	done = 0;
1269260e9a87SYuri Pankov 	start = buf->buf + pos;
1270c66b8046SYuri Pankov 	for (stesc = buf->buf + pos; *stesc != '\0'; stesc++) {
1271cec8643bSMichal Nowak 		if (stesc[0] != newesc || stesc[1] == '\0')
1272c66b8046SYuri Pankov 			continue;
1273c66b8046SYuri Pankov 		stesc++;
1274c66b8046SYuri Pankov 		if (*stesc != '"' && *stesc != '#')
1275c66b8046SYuri Pankov 			continue;
1276c66b8046SYuri Pankov 
1277c66b8046SYuri Pankov 		/* Comment found, look for RCS id. */
1278c66b8046SYuri Pankov 
1279c66b8046SYuri Pankov 		rcsid = 0;
1280c66b8046SYuri Pankov 		if ((cp = strstr(stesc, "$" "OpenBSD")) != NULL) {
1281c66b8046SYuri Pankov 			rcsid = 1 << MANDOC_OS_OPENBSD;
1282c66b8046SYuri Pankov 			cp += 8;
1283c66b8046SYuri Pankov 		} else if ((cp = strstr(stesc, "$" "NetBSD")) != NULL) {
1284c66b8046SYuri Pankov 			rcsid = 1 << MANDOC_OS_NETBSD;
1285c66b8046SYuri Pankov 			cp += 7;
1286c66b8046SYuri Pankov 		}
1287c66b8046SYuri Pankov 		if (cp != NULL &&
1288c66b8046SYuri Pankov 		    isalnum((unsigned char)*cp) == 0 &&
1289c66b8046SYuri Pankov 		    strchr(cp, '$') != NULL) {
1290c66b8046SYuri Pankov 			if (r->man->meta.rcsids & rcsid)
1291cec8643bSMichal Nowak 				mandoc_msg(MANDOCERR_RCS_REP, ln,
1292cec8643bSMichal Nowak 				    (int)(stesc - buf->buf) + 1,
1293cec8643bSMichal Nowak 				    "%s", stesc + 1);
1294c66b8046SYuri Pankov 			r->man->meta.rcsids |= rcsid;
1295c66b8046SYuri Pankov 		}
1296c66b8046SYuri Pankov 
1297c66b8046SYuri Pankov 		/* Handle trailing whitespace. */
1298c66b8046SYuri Pankov 
12996640c13bSYuri Pankov 		ep = strchr(stesc--, '\0') - 1;
13006640c13bSYuri Pankov 		if (*ep == '\n') {
1301c66b8046SYuri Pankov 			done = 1;
13026640c13bSYuri Pankov 			ep--;
1303c66b8046SYuri Pankov 		}
13046640c13bSYuri Pankov 		if (*ep == ' ' || *ep == '\t')
1305cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_SPACE_EOL,
1306cec8643bSMichal Nowak 			    ln, (int)(ep - buf->buf), NULL);
13076640c13bSYuri Pankov 
13086640c13bSYuri Pankov 		/*
13096640c13bSYuri Pankov 		 * Save comments preceding the title macro
13106640c13bSYuri Pankov 		 * in the syntax tree.
13116640c13bSYuri Pankov 		 */
13126640c13bSYuri Pankov 
1313*4d131170SRobert Mustacchi 		if (newesc != ASCII_ESC && r->options & MPARSE_COMMENT) {
13146640c13bSYuri Pankov 			while (*ep == ' ' || *ep == '\t')
13156640c13bSYuri Pankov 				ep--;
13166640c13bSYuri Pankov 			ep[1] = '\0';
13176640c13bSYuri Pankov 			n = roff_node_alloc(r->man,
13186640c13bSYuri Pankov 			    ln, stesc + 1 - buf->buf,
13196640c13bSYuri Pankov 			    ROFFT_COMMENT, TOKEN_NONE);
13206640c13bSYuri Pankov 			n->string = mandoc_strdup(stesc + 2);
13216640c13bSYuri Pankov 			roff_node_append(r->man, n);
13226640c13bSYuri Pankov 			n->flags |= NODE_VALID | NODE_ENDED;
13236640c13bSYuri Pankov 			r->man->next = ROFF_NEXT_SIBLING;
13246640c13bSYuri Pankov 		}
13256640c13bSYuri Pankov 
1326cec8643bSMichal Nowak 		/* Line continuation with comment. */
13276640c13bSYuri Pankov 
1328cec8643bSMichal Nowak 		if (stesc[1] == '#') {
1329cec8643bSMichal Nowak 			*stesc = '\0';
1330cec8643bSMichal Nowak 			return ROFF_IGN | ROFF_APPEND;
1331cec8643bSMichal Nowak 		}
1332cec8643bSMichal Nowak 
1333cec8643bSMichal Nowak 		/* Discard normal comments. */
1334cec8643bSMichal Nowak 
1335cec8643bSMichal Nowak 		while (stesc > start && stesc[-1] == ' ' &&
1336cec8643bSMichal Nowak 		    (stesc == start + 1 || stesc[-2] != '\\'))
1337c66b8046SYuri Pankov 			stesc--;
1338c66b8046SYuri Pankov 		*stesc = '\0';
1339c66b8046SYuri Pankov 		break;
1340c66b8046SYuri Pankov 	}
1341c66b8046SYuri Pankov 	if (stesc == start)
1342c66b8046SYuri Pankov 		return ROFF_CONT;
1343c66b8046SYuri Pankov 	stesc--;
1344c66b8046SYuri Pankov 
1345c66b8046SYuri Pankov 	/* Notice the end of the input. */
1346c66b8046SYuri Pankov 
1347c66b8046SYuri Pankov 	if (*stesc == '\n') {
1348c66b8046SYuri Pankov 		*stesc-- = '\0';
1349c66b8046SYuri Pankov 		done = 1;
1350c66b8046SYuri Pankov 	}
1351c66b8046SYuri Pankov 
1352c66b8046SYuri Pankov 	expand_count = 0;
1353c66b8046SYuri Pankov 	while (stesc >= start) {
1354cec8643bSMichal Nowak 		if (*stesc != newesc) {
135595c635efSGarrett D'Amore 
1356cec8643bSMichal Nowak 			/*
1357cec8643bSMichal Nowak 			 * If we have a non-standard escape character,
1358cec8643bSMichal Nowak 			 * escape literal backslashes because all
1359cec8643bSMichal Nowak 			 * processing in subsequent functions uses
1360cec8643bSMichal Nowak 			 * the standard escaping rules.
1361cec8643bSMichal Nowak 			 */
136295c635efSGarrett D'Amore 
1363cec8643bSMichal Nowak 			if (newesc != ASCII_ESC && *stesc == '\\') {
1364c66b8046SYuri Pankov 				*stesc = '\0';
1365c66b8046SYuri Pankov 				buf->sz = mandoc_asprintf(&nbuf, "%s\\e%s",
1366c66b8046SYuri Pankov 				    buf->buf, stesc + 1) + 1;
1367c66b8046SYuri Pankov 				start = nbuf + pos;
1368c66b8046SYuri Pankov 				stesc = nbuf + (stesc - buf->buf);
1369c66b8046SYuri Pankov 				free(buf->buf);
1370c66b8046SYuri Pankov 				buf->buf = nbuf;
1371c66b8046SYuri Pankov 			}
1372cec8643bSMichal Nowak 
1373cec8643bSMichal Nowak 			/* Search backwards for the next escape. */
1374cec8643bSMichal Nowak 
1375c66b8046SYuri Pankov 			stesc--;
1376260e9a87SYuri Pankov 			continue;
1377c66b8046SYuri Pankov 		}
137895c635efSGarrett D'Amore 
1379260e9a87SYuri Pankov 		/* If it is escaped, skip it. */
138095c635efSGarrett D'Amore 
1381260e9a87SYuri Pankov 		for (cp = stesc - 1; cp >= start; cp--)
1382c66b8046SYuri Pankov 			if (*cp != r->escape)
1383260e9a87SYuri Pankov 				break;
1384260e9a87SYuri Pankov 
1385260e9a87SYuri Pankov 		if ((stesc - cp) % 2 == 0) {
1386c66b8046SYuri Pankov 			while (stesc > cp)
1387c66b8046SYuri Pankov 				*stesc-- = '\\';
1388260e9a87SYuri Pankov 			continue;
1389c66b8046SYuri Pankov 		} else if (stesc[1] != '\0') {
1390c66b8046SYuri Pankov 			*stesc = '\\';
1391c66b8046SYuri Pankov 		} else {
1392c66b8046SYuri Pankov 			*stesc-- = '\0';
1393c66b8046SYuri Pankov 			if (done)
1394c66b8046SYuri Pankov 				continue;
1395c66b8046SYuri Pankov 			else
1396cec8643bSMichal Nowak 				return ROFF_IGN | ROFF_APPEND;
1397260e9a87SYuri Pankov 		}
1398260e9a87SYuri Pankov 
1399260e9a87SYuri Pankov 		/* Decide whether to expand or to check only. */
1400260e9a87SYuri Pankov 
1401260e9a87SYuri Pankov 		term = '\0';
1402260e9a87SYuri Pankov 		cp = stesc + 1;
1403cec8643bSMichal Nowak 		if (*cp == 'E')
1404cec8643bSMichal Nowak 			cp++;
1405cec8643bSMichal Nowak 		esct = cp;
1406cec8643bSMichal Nowak 		switch (*esct) {
1407260e9a87SYuri Pankov 		case '*':
1408cec8643bSMichal Nowak 		case '$':
1409698f87a4SGarrett D'Amore 			res = NULL;
1410698f87a4SGarrett D'Amore 			break;
1411260e9a87SYuri Pankov 		case 'B':
1412260e9a87SYuri Pankov 		case 'w':
1413260e9a87SYuri Pankov 			term = cp[1];
1414260e9a87SYuri Pankov 			/* FALLTHROUGH */
1415260e9a87SYuri Pankov 		case 'n':
14166640c13bSYuri Pankov 			sign = cp[1];
14176640c13bSYuri Pankov 			if (sign == '+' || sign == '-')
14186640c13bSYuri Pankov 				cp++;
1419698f87a4SGarrett D'Amore 			res = ubuf;
1420698f87a4SGarrett D'Amore 			break;
1421698f87a4SGarrett D'Amore 		default:
1422cec8643bSMichal Nowak 			err = MANDOCERR_OK;
1423cec8643bSMichal Nowak 			switch(mandoc_escape(&cp, &stnam, &inaml)) {
1424cec8643bSMichal Nowak 			case ESCAPE_SPECIAL:
1425cec8643bSMichal Nowak 				if (mchars_spec2cp(stnam, inaml) >= 0)
1426cec8643bSMichal Nowak 					break;
1427cec8643bSMichal Nowak 				/* FALLTHROUGH */
1428cec8643bSMichal Nowak 			case ESCAPE_ERROR:
1429cec8643bSMichal Nowak 				err = MANDOCERR_ESC_BAD;
1430cec8643bSMichal Nowak 				break;
1431cec8643bSMichal Nowak 			case ESCAPE_UNDEF:
1432cec8643bSMichal Nowak 				err = MANDOCERR_ESC_UNDEF;
1433cec8643bSMichal Nowak 				break;
1434cec8643bSMichal Nowak 			case ESCAPE_UNSUPP:
1435cec8643bSMichal Nowak 				err = MANDOCERR_ESC_UNSUPP;
1436cec8643bSMichal Nowak 				break;
1437cec8643bSMichal Nowak 			default:
1438cec8643bSMichal Nowak 				break;
1439cec8643bSMichal Nowak 			}
1440cec8643bSMichal Nowak 			if (err != MANDOCERR_OK)
1441cec8643bSMichal Nowak 				mandoc_msg(err, ln, (int)(stesc - buf->buf),
1442260e9a87SYuri Pankov 				    "%.*s", (int)(cp - stesc), stesc);
1443c66b8046SYuri Pankov 			stesc--;
144495c635efSGarrett D'Amore 			continue;
144595c635efSGarrett D'Amore 		}
144695c635efSGarrett D'Amore 
1447260e9a87SYuri Pankov 		if (EXPAND_LIMIT < ++expand_count) {
1448cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_ROFFLOOP,
1449260e9a87SYuri Pankov 			    ln, (int)(stesc - buf->buf), NULL);
1450371584c2SYuri Pankov 			return ROFF_IGN;
1451260e9a87SYuri Pankov 		}
145295c635efSGarrett D'Amore 
145395c635efSGarrett D'Amore 		/*
145495c635efSGarrett D'Amore 		 * The third character decides the length
1455698f87a4SGarrett D'Amore 		 * of the name of the string or register.
145695c635efSGarrett D'Amore 		 * Save a pointer to the name.
145795c635efSGarrett D'Amore 		 */
145895c635efSGarrett D'Amore 
1459260e9a87SYuri Pankov 		if (term == '\0') {
1460260e9a87SYuri Pankov 			switch (*++cp) {
1461260e9a87SYuri Pankov 			case '\0':
1462260e9a87SYuri Pankov 				maxl = 0;
1463260e9a87SYuri Pankov 				break;
1464260e9a87SYuri Pankov 			case '(':
146595c635efSGarrett D'Amore 				cp++;
146695c635efSGarrett D'Amore 				maxl = 2;
146795c635efSGarrett D'Amore 				break;
1468260e9a87SYuri Pankov 			case '[':
146995c635efSGarrett D'Amore 				cp++;
1470260e9a87SYuri Pankov 				term = ']';
147195c635efSGarrett D'Amore 				maxl = 0;
147295c635efSGarrett D'Amore 				break;
147395c635efSGarrett D'Amore 			default:
147495c635efSGarrett D'Amore 				maxl = 1;
147595c635efSGarrett D'Amore 				break;
147695c635efSGarrett D'Amore 			}
1477260e9a87SYuri Pankov 		} else {
1478260e9a87SYuri Pankov 			cp += 2;
1479260e9a87SYuri Pankov 			maxl = 0;
1480260e9a87SYuri Pankov 		}
148195c635efSGarrett D'Amore 		stnam = cp;
148295c635efSGarrett D'Amore 
148395c635efSGarrett D'Amore 		/* Advance to the end of the name. */
148495c635efSGarrett D'Amore 
1485260e9a87SYuri Pankov 		naml = 0;
1486260e9a87SYuri Pankov 		arg_complete = 1;
1487260e9a87SYuri Pankov 		while (maxl == 0 || naml < maxl) {
1488260e9a87SYuri Pankov 			if (*cp == '\0') {
1489cec8643bSMichal Nowak 				mandoc_msg(MANDOCERR_ESC_BAD, ln,
1490cec8643bSMichal Nowak 				    (int)(stesc - buf->buf), "%s", stesc);
1491260e9a87SYuri Pankov 				arg_complete = 0;
149295c635efSGarrett D'Amore 				break;
149395c635efSGarrett D'Amore 			}
1494260e9a87SYuri Pankov 			if (maxl == 0 && *cp == term) {
1495260e9a87SYuri Pankov 				cp++;
1496260e9a87SYuri Pankov 				break;
1497260e9a87SYuri Pankov 			}
1498cec8643bSMichal Nowak 			if (*cp++ != '\\' || *esct != 'w') {
1499260e9a87SYuri Pankov 				naml++;
1500260e9a87SYuri Pankov 				continue;
1501260e9a87SYuri Pankov 			}
1502260e9a87SYuri Pankov 			switch (mandoc_escape(&cp, NULL, NULL)) {
1503260e9a87SYuri Pankov 			case ESCAPE_SPECIAL:
1504260e9a87SYuri Pankov 			case ESCAPE_UNICODE:
1505260e9a87SYuri Pankov 			case ESCAPE_NUMBERED:
1506cec8643bSMichal Nowak 			case ESCAPE_UNDEF:
1507260e9a87SYuri Pankov 			case ESCAPE_OVERSTRIKE:
1508260e9a87SYuri Pankov 				naml++;
1509260e9a87SYuri Pankov 				break;
1510260e9a87SYuri Pankov 			default:
1511260e9a87SYuri Pankov 				break;
1512260e9a87SYuri Pankov 			}
1513260e9a87SYuri Pankov 		}
151495c635efSGarrett D'Amore 
151595c635efSGarrett D'Amore 		/*
151695c635efSGarrett D'Amore 		 * Retrieve the replacement string; if it is
151795c635efSGarrett D'Amore 		 * undefined, resume searching for escapes.
151895c635efSGarrett D'Amore 		 */
151995c635efSGarrett D'Amore 
1520cec8643bSMichal Nowak 		switch (*esct) {
1521260e9a87SYuri Pankov 		case '*':
1522c66b8046SYuri Pankov 			if (arg_complete) {
1523c66b8046SYuri Pankov 				deftype = ROFFDEF_USER | ROFFDEF_PRE;
1524c66b8046SYuri Pankov 				res = roff_getstrn(r, stnam, naml, &deftype);
1525cec8643bSMichal Nowak 
1526cec8643bSMichal Nowak 				/*
1527cec8643bSMichal Nowak 				 * If not overriden, let \*(.T
1528cec8643bSMichal Nowak 				 * through to the formatters.
1529cec8643bSMichal Nowak 				 */
1530cec8643bSMichal Nowak 
1531cec8643bSMichal Nowak 				if (res == NULL && naml == 2 &&
1532cec8643bSMichal Nowak 				    stnam[0] == '.' && stnam[1] == 'T') {
1533cec8643bSMichal Nowak 					roff_setstrn(&r->strtab,
1534cec8643bSMichal Nowak 					    ".T", 2, NULL, 0, 0);
1535cec8643bSMichal Nowak 					stesc--;
1536cec8643bSMichal Nowak 					continue;
1537cec8643bSMichal Nowak 				}
1538c66b8046SYuri Pankov 			}
1539260e9a87SYuri Pankov 			break;
1540cec8643bSMichal Nowak 		case '$':
1541cec8643bSMichal Nowak 			if (r->mstackpos < 0) {
1542cec8643bSMichal Nowak 				mandoc_msg(MANDOCERR_ARG_UNDEF, ln,
1543cec8643bSMichal Nowak 				    (int)(stesc - buf->buf), "%.3s", stesc);
1544cec8643bSMichal Nowak 				break;
1545cec8643bSMichal Nowak 			}
1546cec8643bSMichal Nowak 			ctx = r->mstack + r->mstackpos;
1547cec8643bSMichal Nowak 			npos = esct[1] - '1';
1548cec8643bSMichal Nowak 			if (npos >= 0 && npos <= 8) {
1549cec8643bSMichal Nowak 				res = npos < ctx->argc ?
1550cec8643bSMichal Nowak 				    ctx->argv[npos] : "";
1551cec8643bSMichal Nowak 				break;
1552cec8643bSMichal Nowak 			}
1553cec8643bSMichal Nowak 			if (esct[1] == '*')
1554cec8643bSMichal Nowak 				quote_args = 0;
1555cec8643bSMichal Nowak 			else if (esct[1] == '@')
1556cec8643bSMichal Nowak 				quote_args = 1;
1557cec8643bSMichal Nowak 			else {
1558cec8643bSMichal Nowak 				mandoc_msg(MANDOCERR_ARG_NONUM, ln,
1559cec8643bSMichal Nowak 				    (int)(stesc - buf->buf), "%.3s", stesc);
1560cec8643bSMichal Nowak 				break;
1561cec8643bSMichal Nowak 			}
1562cec8643bSMichal Nowak 			asz = 0;
1563cec8643bSMichal Nowak 			for (npos = 0; npos < ctx->argc; npos++) {
1564cec8643bSMichal Nowak 				if (npos)
1565cec8643bSMichal Nowak 					asz++;  /* blank */
1566cec8643bSMichal Nowak 				if (quote_args)
1567cec8643bSMichal Nowak 					asz += 2;  /* quotes */
1568cec8643bSMichal Nowak 				asz += strlen(ctx->argv[npos]);
1569cec8643bSMichal Nowak 			}
1570cec8643bSMichal Nowak 			if (asz != 3) {
1571cec8643bSMichal Nowak 				rsz = buf->sz - (stesc - buf->buf) - 3;
1572cec8643bSMichal Nowak 				if (asz < 3)
1573cec8643bSMichal Nowak 					memmove(stesc + asz, stesc + 3, rsz);
1574cec8643bSMichal Nowak 				buf->sz += asz - 3;
1575cec8643bSMichal Nowak 				nbuf = mandoc_realloc(buf->buf, buf->sz);
1576cec8643bSMichal Nowak 				start = nbuf + pos;
1577cec8643bSMichal Nowak 				stesc = nbuf + (stesc - buf->buf);
1578cec8643bSMichal Nowak 				buf->buf = nbuf;
1579cec8643bSMichal Nowak 				if (asz > 3)
1580cec8643bSMichal Nowak 					memmove(stesc + asz, stesc + 3, rsz);
1581cec8643bSMichal Nowak 			}
1582cec8643bSMichal Nowak 			for (npos = 0; npos < ctx->argc; npos++) {
1583cec8643bSMichal Nowak 				if (npos)
1584cec8643bSMichal Nowak 					*stesc++ = ' ';
1585cec8643bSMichal Nowak 				if (quote_args)
1586cec8643bSMichal Nowak 					*stesc++ = '"';
1587cec8643bSMichal Nowak 				cp = ctx->argv[npos];
1588cec8643bSMichal Nowak 				while (*cp != '\0')
1589cec8643bSMichal Nowak 					*stesc++ = *cp++;
1590cec8643bSMichal Nowak 				if (quote_args)
1591cec8643bSMichal Nowak 					*stesc++ = '"';
1592cec8643bSMichal Nowak 			}
1593cec8643bSMichal Nowak 			continue;
1594260e9a87SYuri Pankov 		case 'B':
1595260e9a87SYuri Pankov 			npos = 0;
1596260e9a87SYuri Pankov 			ubuf[0] = arg_complete &&
1597260e9a87SYuri Pankov 			    roff_evalnum(r, ln, stnam, &npos,
1598260e9a87SYuri Pankov 			      NULL, ROFFNUM_SCALE) &&
1599260e9a87SYuri Pankov 			    stnam + npos + 1 == cp ? '1' : '0';
1600260e9a87SYuri Pankov 			ubuf[1] = '\0';
1601260e9a87SYuri Pankov 			break;
1602260e9a87SYuri Pankov 		case 'n':
1603260e9a87SYuri Pankov 			if (arg_complete)
1604260e9a87SYuri Pankov 				(void)snprintf(ubuf, sizeof(ubuf), "%d",
16056640c13bSYuri Pankov 				    roff_getregn(r, stnam, naml, sign));
1606260e9a87SYuri Pankov 			else
1607260e9a87SYuri Pankov 				ubuf[0] = '\0';
1608260e9a87SYuri Pankov 			break;
1609260e9a87SYuri Pankov 		case 'w':
1610260e9a87SYuri Pankov 			/* use even incomplete args */
1611260e9a87SYuri Pankov 			(void)snprintf(ubuf, sizeof(ubuf), "%d",
1612260e9a87SYuri Pankov 			    24 * (int)naml);
1613260e9a87SYuri Pankov 			break;
1614260e9a87SYuri Pankov 		}
161595c635efSGarrett D'Amore 
1616260e9a87SYuri Pankov 		if (res == NULL) {
1617cec8643bSMichal Nowak 			if (*esct == '*')
1618cec8643bSMichal Nowak 				mandoc_msg(MANDOCERR_STR_UNDEF,
1619cec8643bSMichal Nowak 				    ln, (int)(stesc - buf->buf),
1620260e9a87SYuri Pankov 				    "%.*s", (int)naml, stnam);
162195c635efSGarrett D'Amore 			res = "";
1622260e9a87SYuri Pankov 		} else if (buf->sz + strlen(res) > SHRT_MAX) {
1623cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_ROFFLOOP,
1624260e9a87SYuri Pankov 			    ln, (int)(stesc - buf->buf), NULL);
1625371584c2SYuri Pankov 			return ROFF_IGN;
162695c635efSGarrett D'Amore 		}
162795c635efSGarrett D'Amore 
162895c635efSGarrett D'Amore 		/* Replace the escape sequence by the string. */
162995c635efSGarrett D'Amore 
1630260e9a87SYuri Pankov 		*stesc = '\0';
1631260e9a87SYuri Pankov 		buf->sz = mandoc_asprintf(&nbuf, "%s%s%s",
1632260e9a87SYuri Pankov 		    buf->buf, res, cp) + 1;
163395c635efSGarrett D'Amore 
1634260e9a87SYuri Pankov 		/* Prepare for the next replacement. */
163595c635efSGarrett D'Amore 
1636260e9a87SYuri Pankov 		start = nbuf + pos;
1637260e9a87SYuri Pankov 		stesc = nbuf + (stesc - buf->buf) + strlen(res);
1638260e9a87SYuri Pankov 		free(buf->buf);
1639260e9a87SYuri Pankov 		buf->buf = nbuf;
164095c635efSGarrett D'Amore 	}
1641371584c2SYuri Pankov 	return ROFF_CONT;
164295c635efSGarrett D'Amore }
164395c635efSGarrett D'Amore 
164495c635efSGarrett D'Amore /*
1645cec8643bSMichal Nowak  * Parse a quoted or unquoted roff-style request or macro argument.
1646cec8643bSMichal Nowak  * Return a pointer to the parsed argument, which is either the original
1647cec8643bSMichal Nowak  * pointer or advanced by one byte in case the argument is quoted.
1648cec8643bSMichal Nowak  * NUL-terminate the argument in place.
1649cec8643bSMichal Nowak  * Collapse pairs of quotes inside quoted arguments.
1650cec8643bSMichal Nowak  * Advance the argument pointer to the next argument,
1651cec8643bSMichal Nowak  * or to the NUL byte terminating the argument line.
1652cec8643bSMichal Nowak  */
1653cec8643bSMichal Nowak char *
roff_getarg(struct roff * r,char ** cpp,int ln,int * pos)1654cec8643bSMichal Nowak roff_getarg(struct roff *r, char **cpp, int ln, int *pos)
1655cec8643bSMichal Nowak {
1656cec8643bSMichal Nowak 	struct buf	 buf;
1657cec8643bSMichal Nowak 	char		*cp, *start;
1658cec8643bSMichal Nowak 	int		 newesc, pairs, quoted, white;
1659cec8643bSMichal Nowak 
1660cec8643bSMichal Nowak 	/* Quoting can only start with a new word. */
1661cec8643bSMichal Nowak 	start = *cpp;
1662cec8643bSMichal Nowak 	quoted = 0;
1663cec8643bSMichal Nowak 	if ('"' == *start) {
1664cec8643bSMichal Nowak 		quoted = 1;
1665cec8643bSMichal Nowak 		start++;
1666cec8643bSMichal Nowak 	}
1667cec8643bSMichal Nowak 
1668cec8643bSMichal Nowak 	newesc = pairs = white = 0;
1669cec8643bSMichal Nowak 	for (cp = start; '\0' != *cp; cp++) {
1670cec8643bSMichal Nowak 
1671cec8643bSMichal Nowak 		/*
1672cec8643bSMichal Nowak 		 * Move the following text left
1673cec8643bSMichal Nowak 		 * after quoted quotes and after "\\" and "\t".
1674cec8643bSMichal Nowak 		 */
1675cec8643bSMichal Nowak 		if (pairs)
1676cec8643bSMichal Nowak 			cp[-pairs] = cp[0];
1677cec8643bSMichal Nowak 
1678cec8643bSMichal Nowak 		if ('\\' == cp[0]) {
1679cec8643bSMichal Nowak 			/*
1680cec8643bSMichal Nowak 			 * In copy mode, translate double to single
1681cec8643bSMichal Nowak 			 * backslashes and backslash-t to literal tabs.
1682cec8643bSMichal Nowak 			 */
1683cec8643bSMichal Nowak 			switch (cp[1]) {
1684cec8643bSMichal Nowak 			case 'a':
1685cec8643bSMichal Nowak 			case 't':
1686cec8643bSMichal Nowak 				cp[-pairs] = '\t';
1687cec8643bSMichal Nowak 				pairs++;
1688cec8643bSMichal Nowak 				cp++;
1689cec8643bSMichal Nowak 				break;
1690cec8643bSMichal Nowak 			case '\\':
1691cec8643bSMichal Nowak 				newesc = 1;
1692cec8643bSMichal Nowak 				cp[-pairs] = ASCII_ESC;
1693cec8643bSMichal Nowak 				pairs++;
1694cec8643bSMichal Nowak 				cp++;
1695cec8643bSMichal Nowak 				break;
1696cec8643bSMichal Nowak 			case ' ':
1697cec8643bSMichal Nowak 				/* Skip escaped blanks. */
1698cec8643bSMichal Nowak 				if (0 == quoted)
1699cec8643bSMichal Nowak 					cp++;
1700cec8643bSMichal Nowak 				break;
1701cec8643bSMichal Nowak 			default:
1702cec8643bSMichal Nowak 				break;
1703cec8643bSMichal Nowak 			}
1704cec8643bSMichal Nowak 		} else if (0 == quoted) {
1705cec8643bSMichal Nowak 			if (' ' == cp[0]) {
1706cec8643bSMichal Nowak 				/* Unescaped blanks end unquoted args. */
1707cec8643bSMichal Nowak 				white = 1;
1708cec8643bSMichal Nowak 				break;
1709cec8643bSMichal Nowak 			}
1710cec8643bSMichal Nowak 		} else if ('"' == cp[0]) {
1711cec8643bSMichal Nowak 			if ('"' == cp[1]) {
1712cec8643bSMichal Nowak 				/* Quoted quotes collapse. */
1713cec8643bSMichal Nowak 				pairs++;
1714cec8643bSMichal Nowak 				cp++;
1715cec8643bSMichal Nowak 			} else {
1716cec8643bSMichal Nowak 				/* Unquoted quotes end quoted args. */
1717cec8643bSMichal Nowak 				quoted = 2;
1718cec8643bSMichal Nowak 				break;
1719cec8643bSMichal Nowak 			}
1720cec8643bSMichal Nowak 		}
1721cec8643bSMichal Nowak 	}
1722cec8643bSMichal Nowak 
1723cec8643bSMichal Nowak 	/* Quoted argument without a closing quote. */
1724cec8643bSMichal Nowak 	if (1 == quoted)
1725cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_ARG_QUOTE, ln, *pos, NULL);
1726cec8643bSMichal Nowak 
1727cec8643bSMichal Nowak 	/* NUL-terminate this argument and move to the next one. */
1728cec8643bSMichal Nowak 	if (pairs)
1729cec8643bSMichal Nowak 		cp[-pairs] = '\0';
1730cec8643bSMichal Nowak 	if ('\0' != *cp) {
1731cec8643bSMichal Nowak 		*cp++ = '\0';
1732cec8643bSMichal Nowak 		while (' ' == *cp)
1733cec8643bSMichal Nowak 			cp++;
1734cec8643bSMichal Nowak 	}
1735cec8643bSMichal Nowak 	*pos += (int)(cp - start) + (quoted ? 1 : 0);
1736cec8643bSMichal Nowak 	*cpp = cp;
1737cec8643bSMichal Nowak 
1738cec8643bSMichal Nowak 	if ('\0' == *cp && (white || ' ' == cp[-1]))
1739cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_SPACE_EOL, ln, *pos, NULL);
1740cec8643bSMichal Nowak 
1741cec8643bSMichal Nowak 	start = mandoc_strdup(start);
1742cec8643bSMichal Nowak 	if (newesc == 0)
1743cec8643bSMichal Nowak 		return start;
1744cec8643bSMichal Nowak 
1745cec8643bSMichal Nowak 	buf.buf = start;
1746cec8643bSMichal Nowak 	buf.sz = strlen(start) + 1;
1747cec8643bSMichal Nowak 	buf.next = NULL;
1748cec8643bSMichal Nowak 	if (roff_expand(r, &buf, ln, 0, ASCII_ESC) & ROFF_IGN) {
1749cec8643bSMichal Nowak 		free(buf.buf);
1750cec8643bSMichal Nowak 		buf.buf = mandoc_strdup("");
1751cec8643bSMichal Nowak 	}
1752cec8643bSMichal Nowak 	return buf.buf;
1753cec8643bSMichal Nowak }
1754cec8643bSMichal Nowak 
1755cec8643bSMichal Nowak 
1756cec8643bSMichal Nowak /*
1757371584c2SYuri Pankov  * Process text streams.
175895c635efSGarrett D'Amore  */
1759cec8643bSMichal Nowak static int
roff_parsetext(struct roff * r,struct buf * buf,int pos,int * offs)1760c66b8046SYuri Pankov roff_parsetext(struct roff *r, struct buf *buf, int pos, int *offs)
176195c635efSGarrett D'Amore {
176295c635efSGarrett D'Amore 	size_t		 sz;
176395c635efSGarrett D'Amore 	const char	*start;
1764698f87a4SGarrett D'Amore 	char		*p;
1765698f87a4SGarrett D'Amore 	int		 isz;
176695c635efSGarrett D'Amore 	enum mandoc_esc	 esc;
176795c635efSGarrett D'Amore 
1768371584c2SYuri Pankov 	/* Spring the input line trap. */
1769371584c2SYuri Pankov 
1770371584c2SYuri Pankov 	if (roffit_lines == 1) {
1771371584c2SYuri Pankov 		isz = mandoc_asprintf(&p, "%s\n.%s", buf->buf, roffit_macro);
1772371584c2SYuri Pankov 		free(buf->buf);
1773371584c2SYuri Pankov 		buf->buf = p;
1774371584c2SYuri Pankov 		buf->sz = isz + 1;
1775371584c2SYuri Pankov 		*offs = 0;
1776371584c2SYuri Pankov 		free(roffit_macro);
1777371584c2SYuri Pankov 		roffit_lines = 0;
1778371584c2SYuri Pankov 		return ROFF_REPARSE;
1779371584c2SYuri Pankov 	} else if (roffit_lines > 1)
1780371584c2SYuri Pankov 		--roffit_lines;
1781371584c2SYuri Pankov 
1782c66b8046SYuri Pankov 	if (roffce_node != NULL && buf->buf[pos] != '\0') {
1783c66b8046SYuri Pankov 		if (roffce_lines < 1) {
1784c66b8046SYuri Pankov 			r->man->last = roffce_node;
1785c66b8046SYuri Pankov 			r->man->next = ROFF_NEXT_SIBLING;
1786c66b8046SYuri Pankov 			roffce_lines = 0;
1787c66b8046SYuri Pankov 			roffce_node = NULL;
1788c66b8046SYuri Pankov 		} else
1789c66b8046SYuri Pankov 			roffce_lines--;
1790c66b8046SYuri Pankov 	}
1791c66b8046SYuri Pankov 
1792371584c2SYuri Pankov 	/* Convert all breakable hyphens into ASCII_HYPH. */
1793371584c2SYuri Pankov 
1794260e9a87SYuri Pankov 	start = p = buf->buf + pos;
179595c635efSGarrett D'Amore 
1796260e9a87SYuri Pankov 	while (*p != '\0') {
179795c635efSGarrett D'Amore 		sz = strcspn(p, "-\\");
179895c635efSGarrett D'Amore 		p += sz;
179995c635efSGarrett D'Amore 
1800260e9a87SYuri Pankov 		if (*p == '\0')
180195c635efSGarrett D'Amore 			break;
180295c635efSGarrett D'Amore 
1803260e9a87SYuri Pankov 		if (*p == '\\') {
180495c635efSGarrett D'Amore 			/* Skip over escapes. */
180595c635efSGarrett D'Amore 			p++;
1806698f87a4SGarrett D'Amore 			esc = mandoc_escape((const char **)&p, NULL, NULL);
1807260e9a87SYuri Pankov 			if (esc == ESCAPE_ERROR)
180895c635efSGarrett D'Amore 				break;
1809371584c2SYuri Pankov 			while (*p == '-')
1810371584c2SYuri Pankov 				p++;
181195c635efSGarrett D'Amore 			continue;
181295c635efSGarrett D'Amore 		} else if (p == start) {
181395c635efSGarrett D'Amore 			p++;
181495c635efSGarrett D'Amore 			continue;
181595c635efSGarrett D'Amore 		}
181695c635efSGarrett D'Amore 
181795c635efSGarrett D'Amore 		if (isalpha((unsigned char)p[-1]) &&
181895c635efSGarrett D'Amore 		    isalpha((unsigned char)p[1]))
181995c635efSGarrett D'Amore 			*p = ASCII_HYPH;
182095c635efSGarrett D'Amore 		p++;
182195c635efSGarrett D'Amore 	}
1822371584c2SYuri Pankov 	return ROFF_CONT;
182395c635efSGarrett D'Amore }
182495c635efSGarrett D'Amore 
1825cec8643bSMichal Nowak int
roff_parseln(struct roff * r,int ln,struct buf * buf,int * offs,size_t len)1826*4d131170SRobert Mustacchi roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs, size_t len)
182795c635efSGarrett D'Amore {
1828c66b8046SYuri Pankov 	enum roff_tok	 t;
1829cec8643bSMichal Nowak 	int		 e;
1830260e9a87SYuri Pankov 	int		 pos;	/* parse point */
1831260e9a87SYuri Pankov 	int		 spos;	/* saved parse point for messages */
1832260e9a87SYuri Pankov 	int		 ppos;	/* original offset in buf->buf */
1833260e9a87SYuri Pankov 	int		 ctl;	/* macro line (boolean) */
183495c635efSGarrett D'Amore 
1835260e9a87SYuri Pankov 	ppos = pos = *offs;
183695c635efSGarrett D'Amore 
1837*4d131170SRobert Mustacchi 	if (len > 80 && r->tbl == NULL && r->eqn == NULL &&
1838*4d131170SRobert Mustacchi 	    (r->man->flags & ROFF_NOFILL) == 0 &&
1839*4d131170SRobert Mustacchi 	    strchr(" .\\", buf->buf[pos]) == NULL &&
1840*4d131170SRobert Mustacchi 	    buf->buf[pos] != r->control &&
1841*4d131170SRobert Mustacchi 	    strcspn(buf->buf, " ") < 80)
1842*4d131170SRobert Mustacchi 		mandoc_msg(MANDOCERR_TEXT_LONG, ln, (int)len - 1,
1843*4d131170SRobert Mustacchi 		    "%.20s...", buf->buf + pos);
1844*4d131170SRobert Mustacchi 
1845260e9a87SYuri Pankov 	/* Handle in-line equation delimiters. */
1846260e9a87SYuri Pankov 
1847260e9a87SYuri Pankov 	if (r->tbl == NULL &&
1848260e9a87SYuri Pankov 	    r->last_eqn != NULL && r->last_eqn->delim &&
1849260e9a87SYuri Pankov 	    (r->eqn == NULL || r->eqn_inline)) {
1850260e9a87SYuri Pankov 		e = roff_eqndelim(r, buf, pos);
1851260e9a87SYuri Pankov 		if (e == ROFF_REPARSE)
1852371584c2SYuri Pankov 			return e;
1853260e9a87SYuri Pankov 		assert(e == ROFF_CONT);
1854260e9a87SYuri Pankov 	}
185595c635efSGarrett D'Amore 
1856260e9a87SYuri Pankov 	/* Expand some escape sequences. */
1857260e9a87SYuri Pankov 
1858cec8643bSMichal Nowak 	e = roff_expand(r, buf, ln, pos, r->escape);
1859cec8643bSMichal Nowak 	if ((e & ROFF_MASK) == ROFF_IGN)
1860371584c2SYuri Pankov 		return e;
1861260e9a87SYuri Pankov 	assert(e == ROFF_CONT);
1862260e9a87SYuri Pankov 
1863260e9a87SYuri Pankov 	ctl = roff_getcontrol(r, buf->buf, &pos);
186495c635efSGarrett D'Amore 
186595c635efSGarrett D'Amore 	/*
186695c635efSGarrett D'Amore 	 * First, if a scope is open and we're not a macro, pass the
1867260e9a87SYuri Pankov 	 * text through the macro's filter.
1868260e9a87SYuri Pankov 	 * Equations process all content themselves.
1869260e9a87SYuri Pankov 	 * Tables process almost all content themselves, but we want
1870260e9a87SYuri Pankov 	 * to warn about macros before passing it there.
187195c635efSGarrett D'Amore 	 */
187295c635efSGarrett D'Amore 
1873260e9a87SYuri Pankov 	if (r->last != NULL && ! ctl) {
187495c635efSGarrett D'Amore 		t = r->last->tok;
1875260e9a87SYuri Pankov 		e = (*roffs[t].text)(r, t, buf, ln, pos, pos, offs);
1876cec8643bSMichal Nowak 		if ((e & ROFF_MASK) == ROFF_IGN)
1877371584c2SYuri Pankov 			return e;
1878cec8643bSMichal Nowak 		e &= ~ROFF_MASK;
1879cec8643bSMichal Nowak 	} else
1880cec8643bSMichal Nowak 		e = ROFF_IGN;
1881c66b8046SYuri Pankov 	if (r->eqn != NULL && strncmp(buf->buf + ppos, ".EN", 3)) {
1882c66b8046SYuri Pankov 		eqn_read(r->eqn, buf->buf + ppos);
1883cec8643bSMichal Nowak 		return e;
1884c66b8046SYuri Pankov 	}
1885c66b8046SYuri Pankov 	if (r->tbl != NULL && (ctl == 0 || buf->buf[pos] == '\0')) {
1886c66b8046SYuri Pankov 		tbl_read(r->tbl, ln, buf->buf, ppos);
1887cec8643bSMichal Nowak 		roff_addtbl(r->man, ln, r->tbl);
1888cec8643bSMichal Nowak 		return e;
1889c66b8046SYuri Pankov 	}
1890*4d131170SRobert Mustacchi 	if ( ! ctl) {
1891*4d131170SRobert Mustacchi 		r->options &= ~MPARSE_COMMENT;
1892cec8643bSMichal Nowak 		return roff_parsetext(r, buf, pos, offs) | e;
1893*4d131170SRobert Mustacchi 	}
1894260e9a87SYuri Pankov 
1895260e9a87SYuri Pankov 	/* Skip empty request lines. */
1896260e9a87SYuri Pankov 
1897260e9a87SYuri Pankov 	if (buf->buf[pos] == '"') {
1898cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_COMMENT_BAD, ln, pos, NULL);
1899371584c2SYuri Pankov 		return ROFF_IGN;
1900260e9a87SYuri Pankov 	} else if (buf->buf[pos] == '\0')
1901371584c2SYuri Pankov 		return ROFF_IGN;
190295c635efSGarrett D'Amore 
190395c635efSGarrett D'Amore 	/*
190495c635efSGarrett D'Amore 	 * If a scope is open, go to the child handler for that macro,
190595c635efSGarrett D'Amore 	 * as it may want to preprocess before doing anything with it.
190695c635efSGarrett D'Amore 	 * Don't do so if an equation is open.
190795c635efSGarrett D'Amore 	 */
190895c635efSGarrett D'Amore 
190995c635efSGarrett D'Amore 	if (r->last) {
191095c635efSGarrett D'Amore 		t = r->last->tok;
1911371584c2SYuri Pankov 		return (*roffs[t].sub)(r, t, buf, ln, ppos, pos, offs);
1912260e9a87SYuri Pankov 	}
1913260e9a87SYuri Pankov 
1914260e9a87SYuri Pankov 	/* No scope is open.  This is a new request or macro. */
1915260e9a87SYuri Pankov 
1916*4d131170SRobert Mustacchi 	r->options &= ~MPARSE_COMMENT;
1917260e9a87SYuri Pankov 	spos = pos;
1918260e9a87SYuri Pankov 	t = roff_parse(r, buf->buf, &pos, ln, ppos);
1919260e9a87SYuri Pankov 
1920260e9a87SYuri Pankov 	/* Tables ignore most macros. */
1921260e9a87SYuri Pankov 
1922c66b8046SYuri Pankov 	if (r->tbl != NULL && (t == TOKEN_NONE || t == ROFF_TS ||
1923c66b8046SYuri Pankov 	    t == ROFF_br || t == ROFF_ce || t == ROFF_rj || t == ROFF_sp)) {
1924cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_TBLMACRO,
1925cec8643bSMichal Nowak 		    ln, pos, "%s", buf->buf + spos);
1926c66b8046SYuri Pankov 		if (t != TOKEN_NONE)
1927371584c2SYuri Pankov 			return ROFF_IGN;
1928260e9a87SYuri Pankov 		while (buf->buf[pos] != '\0' && buf->buf[pos] != ' ')
1929260e9a87SYuri Pankov 			pos++;
1930c66b8046SYuri Pankov 		while (buf->buf[pos] == ' ')
1931260e9a87SYuri Pankov 			pos++;
1932c66b8046SYuri Pankov 		tbl_read(r->tbl, ln, buf->buf, pos);
1933cec8643bSMichal Nowak 		roff_addtbl(r->man, ln, r->tbl);
1934c66b8046SYuri Pankov 		return ROFF_IGN;
1935c66b8046SYuri Pankov 	}
1936c66b8046SYuri Pankov 
1937c66b8046SYuri Pankov 	/* For now, let high level macros abort .ce mode. */
1938c66b8046SYuri Pankov 
1939c66b8046SYuri Pankov 	if (ctl && roffce_node != NULL &&
1940c66b8046SYuri Pankov 	    (t == TOKEN_NONE || t == ROFF_Dd || t == ROFF_EQ ||
1941c66b8046SYuri Pankov 	     t == ROFF_TH || t == ROFF_TS)) {
1942c66b8046SYuri Pankov 		r->man->last = roffce_node;
1943c66b8046SYuri Pankov 		r->man->next = ROFF_NEXT_SIBLING;
1944c66b8046SYuri Pankov 		roffce_lines = 0;
1945c66b8046SYuri Pankov 		roffce_node = NULL;
194695c635efSGarrett D'Amore 	}
194795c635efSGarrett D'Amore 
194895c635efSGarrett D'Amore 	/*
1949260e9a87SYuri Pankov 	 * This is neither a roff request nor a user-defined macro.
1950260e9a87SYuri Pankov 	 * Let the standard macro set parsers handle it.
195195c635efSGarrett D'Amore 	 */
195295c635efSGarrett D'Amore 
1953c66b8046SYuri Pankov 	if (t == TOKEN_NONE)
1954371584c2SYuri Pankov 		return ROFF_CONT;
195595c635efSGarrett D'Amore 
1956260e9a87SYuri Pankov 	/* Execute a roff request or a user defined macro. */
195795c635efSGarrett D'Amore 
1958c66b8046SYuri Pankov 	return (*roffs[t].proc)(r, t, buf, ln, spos, pos, offs);
1959260e9a87SYuri Pankov }
196095c635efSGarrett D'Amore 
1961cec8643bSMichal Nowak /*
1962cec8643bSMichal Nowak  * Internal interface function to tell the roff parser that execution
1963cec8643bSMichal Nowak  * of the current macro ended.  This is required because macro
1964cec8643bSMichal Nowak  * definitions usually do not end with a .return request.
1965cec8643bSMichal Nowak  */
1966cec8643bSMichal Nowak void
roff_userret(struct roff * r)1967cec8643bSMichal Nowak roff_userret(struct roff *r)
1968cec8643bSMichal Nowak {
1969cec8643bSMichal Nowak 	struct mctx	*ctx;
1970cec8643bSMichal Nowak 	int		 i;
1971cec8643bSMichal Nowak 
1972cec8643bSMichal Nowak 	assert(r->mstackpos >= 0);
1973cec8643bSMichal Nowak 	ctx = r->mstack + r->mstackpos;
1974cec8643bSMichal Nowak 	for (i = 0; i < ctx->argc; i++)
1975cec8643bSMichal Nowak 		free(ctx->argv[i]);
1976cec8643bSMichal Nowak 	ctx->argc = 0;
1977cec8643bSMichal Nowak 	r->mstackpos--;
1978cec8643bSMichal Nowak }
1979cec8643bSMichal Nowak 
198095c635efSGarrett D'Amore void
roff_endparse(struct roff * r)198195c635efSGarrett D'Amore roff_endparse(struct roff *r)
198295c635efSGarrett D'Amore {
1983c66b8046SYuri Pankov 	if (r->last != NULL)
1984cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BLK_NOEND, r->last->line,
1985cec8643bSMichal Nowak 		    r->last->col, "%s", roff_name[r->last->tok]);
198695c635efSGarrett D'Amore 
1987c66b8046SYuri Pankov 	if (r->eqn != NULL) {
1988cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BLK_NOEND,
1989c66b8046SYuri Pankov 		    r->eqn->node->line, r->eqn->node->pos, "EQ");
1990c66b8046SYuri Pankov 		eqn_parse(r->eqn);
1991c66b8046SYuri Pankov 		r->eqn = NULL;
199295c635efSGarrett D'Amore 	}
199395c635efSGarrett D'Amore 
1994c66b8046SYuri Pankov 	if (r->tbl != NULL) {
1995cec8643bSMichal Nowak 		tbl_end(r->tbl, 1);
1996c66b8046SYuri Pankov 		r->tbl = NULL;
199795c635efSGarrett D'Amore 	}
199895c635efSGarrett D'Amore }
199995c635efSGarrett D'Amore 
200095c635efSGarrett D'Amore /*
200195c635efSGarrett D'Amore  * Parse a roff node's type from the input buffer.  This must be in the
200295c635efSGarrett D'Amore  * form of ".foo xxx" in the usual way.
200395c635efSGarrett D'Amore  */
2004c66b8046SYuri Pankov static enum roff_tok
roff_parse(struct roff * r,char * buf,int * pos,int ln,int ppos)2005260e9a87SYuri Pankov roff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos)
200695c635efSGarrett D'Amore {
2007260e9a87SYuri Pankov 	char		*cp;
200895c635efSGarrett D'Amore 	const char	*mac;
200995c635efSGarrett D'Amore 	size_t		 maclen;
2010c66b8046SYuri Pankov 	int		 deftype;
2011c66b8046SYuri Pankov 	enum roff_tok	 t;
201295c635efSGarrett D'Amore 
2013260e9a87SYuri Pankov 	cp = buf + *pos;
2014260e9a87SYuri Pankov 
2015260e9a87SYuri Pankov 	if ('\0' == *cp || '"' == *cp || '\t' == *cp || ' ' == *cp)
2016c66b8046SYuri Pankov 		return TOKEN_NONE;
201795c635efSGarrett D'Amore 
2018260e9a87SYuri Pankov 	mac = cp;
2019260e9a87SYuri Pankov 	maclen = roff_getname(r, &cp, ln, ppos);
202095c635efSGarrett D'Amore 
2021c66b8046SYuri Pankov 	deftype = ROFFDEF_USER | ROFFDEF_REN;
2022c66b8046SYuri Pankov 	r->current_string = roff_getstrn(r, mac, maclen, &deftype);
2023c66b8046SYuri Pankov 	switch (deftype) {
2024c66b8046SYuri Pankov 	case ROFFDEF_USER:
2025c66b8046SYuri Pankov 		t = ROFF_USERDEF;
2026c66b8046SYuri Pankov 		break;
2027c66b8046SYuri Pankov 	case ROFFDEF_REN:
2028c66b8046SYuri Pankov 		t = ROFF_RENAMED;
2029c66b8046SYuri Pankov 		break;
2030c66b8046SYuri Pankov 	default:
2031c66b8046SYuri Pankov 		t = roffhash_find(r->reqtab, mac, maclen);
2032c66b8046SYuri Pankov 		break;
2033c66b8046SYuri Pankov 	}
2034c66b8046SYuri Pankov 	if (t != TOKEN_NONE)
2035260e9a87SYuri Pankov 		*pos = cp - buf;
20366640c13bSYuri Pankov 	else if (deftype == ROFFDEF_UNDEF) {
20376640c13bSYuri Pankov 		/* Using an undefined macro defines it to be empty. */
20386640c13bSYuri Pankov 		roff_setstrn(&r->strtab, mac, maclen, "", 0, 0);
20396640c13bSYuri Pankov 		roff_setstrn(&r->rentab, mac, maclen, NULL, 0, 0);
20406640c13bSYuri Pankov 	}
2041371584c2SYuri Pankov 	return t;
204295c635efSGarrett D'Amore }
204395c635efSGarrett D'Amore 
2044371584c2SYuri Pankov /* --- handling of request blocks ----------------------------------------- */
2045371584c2SYuri Pankov 
2046*4d131170SRobert Mustacchi /*
2047*4d131170SRobert Mustacchi  * Close a macro definition block or an "ignore" block.
2048*4d131170SRobert Mustacchi  */
2049cec8643bSMichal Nowak static int
roff_cblock(ROFF_ARGS)205095c635efSGarrett D'Amore roff_cblock(ROFF_ARGS)
205195c635efSGarrett D'Amore {
2052*4d131170SRobert Mustacchi 	int	 rr;
205395c635efSGarrett D'Amore 
2054260e9a87SYuri Pankov 	if (r->last == NULL) {
2055cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "..");
2056371584c2SYuri Pankov 		return ROFF_IGN;
205795c635efSGarrett D'Amore 	}
205895c635efSGarrett D'Amore 
205995c635efSGarrett D'Amore 	switch (r->last->tok) {
2060260e9a87SYuri Pankov 	case ROFF_am:
2061260e9a87SYuri Pankov 	case ROFF_ami:
2062260e9a87SYuri Pankov 	case ROFF_de:
2063260e9a87SYuri Pankov 	case ROFF_dei:
2064260e9a87SYuri Pankov 	case ROFF_ig:
206595c635efSGarrett D'Amore 		break;
2066*4d131170SRobert Mustacchi 	case ROFF_am1:
2067*4d131170SRobert Mustacchi 	case ROFF_de1:
2068*4d131170SRobert Mustacchi 		/* Remapped in roff_block(). */
2069*4d131170SRobert Mustacchi 		abort();
207095c635efSGarrett D'Amore 	default:
2071cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "..");
2072371584c2SYuri Pankov 		return ROFF_IGN;
207395c635efSGarrett D'Amore 	}
207495c635efSGarrett D'Amore 
2075*4d131170SRobert Mustacchi 	roffnode_pop(r);
2076*4d131170SRobert Mustacchi 	roffnode_cleanscope(r);
2077*4d131170SRobert Mustacchi 
2078*4d131170SRobert Mustacchi 	/*
2079*4d131170SRobert Mustacchi 	 * If a conditional block with braces is still open,
2080*4d131170SRobert Mustacchi 	 * check for "\}" block end markers.
2081*4d131170SRobert Mustacchi 	 */
2082*4d131170SRobert Mustacchi 
2083*4d131170SRobert Mustacchi 	if (r->last != NULL && r->last->endspan < 0) {
2084*4d131170SRobert Mustacchi 		rr = 1;  /* If arguments follow "\}", warn about them. */
2085*4d131170SRobert Mustacchi 		roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr);
2086*4d131170SRobert Mustacchi 	}
2087*4d131170SRobert Mustacchi 
2088260e9a87SYuri Pankov 	if (buf->buf[pos] != '\0')
2089cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
2090260e9a87SYuri Pankov 		    ".. %s", buf->buf + pos);
209195c635efSGarrett D'Amore 
2092371584c2SYuri Pankov 	return ROFF_IGN;
209395c635efSGarrett D'Amore }
209495c635efSGarrett D'Amore 
2095*4d131170SRobert Mustacchi /*
2096*4d131170SRobert Mustacchi  * Pop all nodes ending at the end of the current input line.
2097*4d131170SRobert Mustacchi  * Return the number of loops ended.
2098*4d131170SRobert Mustacchi  */
2099cec8643bSMichal Nowak static int
roffnode_cleanscope(struct roff * r)210095c635efSGarrett D'Amore roffnode_cleanscope(struct roff *r)
210195c635efSGarrett D'Amore {
2102cec8643bSMichal Nowak 	int inloop;
210395c635efSGarrett D'Amore 
2104cec8643bSMichal Nowak 	inloop = 0;
2105*4d131170SRobert Mustacchi 	while (r->last != NULL && r->last->endspan > 0) {
2106698f87a4SGarrett D'Amore 		if (--r->last->endspan != 0)
210795c635efSGarrett D'Amore 			break;
2108cec8643bSMichal Nowak 		inloop += roffnode_pop(r);
210995c635efSGarrett D'Amore 	}
2110cec8643bSMichal Nowak 	return inloop;
211195c635efSGarrett D'Amore }
211295c635efSGarrett D'Amore 
2113*4d131170SRobert Mustacchi /*
2114*4d131170SRobert Mustacchi  * Handle the closing "\}" of a conditional block.
2115*4d131170SRobert Mustacchi  * Apart from generating warnings, this only pops nodes.
2116*4d131170SRobert Mustacchi  * Return the number of loops ended.
2117*4d131170SRobert Mustacchi  */
2118cec8643bSMichal Nowak static int
roff_ccond(struct roff * r,int ln,int ppos)2119260e9a87SYuri Pankov roff_ccond(struct roff *r, int ln, int ppos)
212095c635efSGarrett D'Amore {
212195c635efSGarrett D'Amore 	if (NULL == r->last) {
2122cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}");
2123cec8643bSMichal Nowak 		return 0;
212495c635efSGarrett D'Amore 	}
212595c635efSGarrett D'Amore 
212695c635efSGarrett D'Amore 	switch (r->last->tok) {
2127260e9a87SYuri Pankov 	case ROFF_el:
2128260e9a87SYuri Pankov 	case ROFF_ie:
2129260e9a87SYuri Pankov 	case ROFF_if:
2130cec8643bSMichal Nowak 	case ROFF_while:
213195c635efSGarrett D'Amore 		break;
213295c635efSGarrett D'Amore 	default:
2133cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}");
2134cec8643bSMichal Nowak 		return 0;
213595c635efSGarrett D'Amore 	}
213695c635efSGarrett D'Amore 
213795c635efSGarrett D'Amore 	if (r->last->endspan > -1) {
2138cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}");
2139cec8643bSMichal Nowak 		return 0;
214095c635efSGarrett D'Amore 	}
214195c635efSGarrett D'Amore 
2142cec8643bSMichal Nowak 	return roffnode_pop(r) + roffnode_cleanscope(r);
214395c635efSGarrett D'Amore }
214495c635efSGarrett D'Amore 
2145cec8643bSMichal Nowak static int
roff_block(ROFF_ARGS)214695c635efSGarrett D'Amore roff_block(ROFF_ARGS)
214795c635efSGarrett D'Amore {
2148c66b8046SYuri Pankov 	const char	*name, *value;
2149c66b8046SYuri Pankov 	char		*call, *cp, *iname, *rname;
2150c66b8046SYuri Pankov 	size_t		 csz, namesz, rsz;
2151c66b8046SYuri Pankov 	int		 deftype;
215295c635efSGarrett D'Amore 
2153260e9a87SYuri Pankov 	/* Ignore groff compatibility mode for now. */
215495c635efSGarrett D'Amore 
2155260e9a87SYuri Pankov 	if (tok == ROFF_de1)
2156260e9a87SYuri Pankov 		tok = ROFF_de;
2157260e9a87SYuri Pankov 	else if (tok == ROFF_dei1)
2158260e9a87SYuri Pankov 		tok = ROFF_dei;
2159260e9a87SYuri Pankov 	else if (tok == ROFF_am1)
2160260e9a87SYuri Pankov 		tok = ROFF_am;
2161260e9a87SYuri Pankov 	else if (tok == ROFF_ami1)
2162260e9a87SYuri Pankov 		tok = ROFF_ami;
2163260e9a87SYuri Pankov 
2164260e9a87SYuri Pankov 	/* Parse the macro name argument. */
2165260e9a87SYuri Pankov 
2166260e9a87SYuri Pankov 	cp = buf->buf + pos;
2167260e9a87SYuri Pankov 	if (tok == ROFF_ig) {
2168260e9a87SYuri Pankov 		iname = NULL;
2169260e9a87SYuri Pankov 		namesz = 0;
2170260e9a87SYuri Pankov 	} else {
2171260e9a87SYuri Pankov 		iname = cp;
2172260e9a87SYuri Pankov 		namesz = roff_getname(r, &cp, ln, ppos);
2173260e9a87SYuri Pankov 		iname[namesz] = '\0';
217495c635efSGarrett D'Amore 	}
217595c635efSGarrett D'Amore 
2176260e9a87SYuri Pankov 	/* Resolve the macro name argument if it is indirect. */
217795c635efSGarrett D'Amore 
2178260e9a87SYuri Pankov 	if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
2179c66b8046SYuri Pankov 		deftype = ROFFDEF_USER;
2180c66b8046SYuri Pankov 		name = roff_getstrn(r, iname, namesz, &deftype);
2181c66b8046SYuri Pankov 		if (name == NULL) {
2182cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_STR_UNDEF,
2183cec8643bSMichal Nowak 			    ln, (int)(iname - buf->buf),
2184260e9a87SYuri Pankov 			    "%.*s", (int)namesz, iname);
2185260e9a87SYuri Pankov 			namesz = 0;
2186260e9a87SYuri Pankov 		} else
2187260e9a87SYuri Pankov 			namesz = strlen(name);
2188260e9a87SYuri Pankov 	} else
2189260e9a87SYuri Pankov 		name = iname;
219095c635efSGarrett D'Amore 
2191260e9a87SYuri Pankov 	if (namesz == 0 && tok != ROFF_ig) {
2192cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_REQ_EMPTY,
2193cec8643bSMichal Nowak 		    ln, ppos, "%s", roff_name[tok]);
2194371584c2SYuri Pankov 		return ROFF_IGN;
219595c635efSGarrett D'Amore 	}
219695c635efSGarrett D'Amore 
219795c635efSGarrett D'Amore 	roffnode_push(r, tok, name, ln, ppos);
219895c635efSGarrett D'Amore 
219995c635efSGarrett D'Amore 	/*
220095c635efSGarrett D'Amore 	 * At the beginning of a `de' macro, clear the existing string
220195c635efSGarrett D'Amore 	 * with the same name, if there is one.  New content will be
2202260e9a87SYuri Pankov 	 * appended from roff_block_text() in multiline mode.
220395c635efSGarrett D'Amore 	 */
220495c635efSGarrett D'Amore 
2205c66b8046SYuri Pankov 	if (tok == ROFF_de || tok == ROFF_dei) {
2206260e9a87SYuri Pankov 		roff_setstrn(&r->strtab, name, namesz, "", 0, 0);
2207c66b8046SYuri Pankov 		roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
2208c66b8046SYuri Pankov 	} else if (tok == ROFF_am || tok == ROFF_ami) {
2209c66b8046SYuri Pankov 		deftype = ROFFDEF_ANY;
2210c66b8046SYuri Pankov 		value = roff_getstrn(r, iname, namesz, &deftype);
2211c66b8046SYuri Pankov 		switch (deftype) {  /* Before appending, ... */
2212c66b8046SYuri Pankov 		case ROFFDEF_PRE: /* copy predefined to user-defined. */
2213c66b8046SYuri Pankov 			roff_setstrn(&r->strtab, name, namesz,
2214c66b8046SYuri Pankov 			    value, strlen(value), 0);
2215c66b8046SYuri Pankov 			break;
2216c66b8046SYuri Pankov 		case ROFFDEF_REN: /* call original standard macro. */
2217c66b8046SYuri Pankov 			csz = mandoc_asprintf(&call, ".%.*s \\$* \\\"\n",
2218c66b8046SYuri Pankov 			    (int)strlen(value), value);
2219c66b8046SYuri Pankov 			roff_setstrn(&r->strtab, name, namesz, call, csz, 0);
2220c66b8046SYuri Pankov 			roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
2221c66b8046SYuri Pankov 			free(call);
2222c66b8046SYuri Pankov 			break;
2223c66b8046SYuri Pankov 		case ROFFDEF_STD:  /* rename and call standard macro. */
2224c66b8046SYuri Pankov 			rsz = mandoc_asprintf(&rname, "__%s_renamed", name);
2225c66b8046SYuri Pankov 			roff_setstrn(&r->rentab, rname, rsz, name, namesz, 0);
2226c66b8046SYuri Pankov 			csz = mandoc_asprintf(&call, ".%.*s \\$* \\\"\n",
2227c66b8046SYuri Pankov 			    (int)rsz, rname);
2228c66b8046SYuri Pankov 			roff_setstrn(&r->strtab, name, namesz, call, csz, 0);
2229c66b8046SYuri Pankov 			free(call);
2230c66b8046SYuri Pankov 			free(rname);
2231c66b8046SYuri Pankov 			break;
2232c66b8046SYuri Pankov 		default:
2233c66b8046SYuri Pankov 			break;
2234c66b8046SYuri Pankov 		}
2235c66b8046SYuri Pankov 	}
223695c635efSGarrett D'Amore 
2237260e9a87SYuri Pankov 	if (*cp == '\0')
2238371584c2SYuri Pankov 		return ROFF_IGN;
223995c635efSGarrett D'Amore 
2240260e9a87SYuri Pankov 	/* Get the custom end marker. */
224195c635efSGarrett D'Amore 
2242260e9a87SYuri Pankov 	iname = cp;
2243260e9a87SYuri Pankov 	namesz = roff_getname(r, &cp, ln, ppos);
224495c635efSGarrett D'Amore 
2245260e9a87SYuri Pankov 	/* Resolve the end marker if it is indirect. */
224695c635efSGarrett D'Amore 
2247260e9a87SYuri Pankov 	if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
2248c66b8046SYuri Pankov 		deftype = ROFFDEF_USER;
2249c66b8046SYuri Pankov 		name = roff_getstrn(r, iname, namesz, &deftype);
2250c66b8046SYuri Pankov 		if (name == NULL) {
2251cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_STR_UNDEF,
2252cec8643bSMichal Nowak 			    ln, (int)(iname - buf->buf),
2253260e9a87SYuri Pankov 			    "%.*s", (int)namesz, iname);
2254260e9a87SYuri Pankov 			namesz = 0;
2255260e9a87SYuri Pankov 		} else
2256260e9a87SYuri Pankov 			namesz = strlen(name);
2257260e9a87SYuri Pankov 	} else
2258260e9a87SYuri Pankov 		name = iname;
225995c635efSGarrett D'Amore 
2260260e9a87SYuri Pankov 	if (namesz)
2261260e9a87SYuri Pankov 		r->last->end = mandoc_strndup(name, namesz);
226295c635efSGarrett D'Amore 
2263260e9a87SYuri Pankov 	if (*cp != '\0')
2264cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_ARG_EXCESS,
2265c66b8046SYuri Pankov 		    ln, pos, ".%s ... %s", roff_name[tok], cp);
226695c635efSGarrett D'Amore 
2267371584c2SYuri Pankov 	return ROFF_IGN;
226895c635efSGarrett D'Amore }
226995c635efSGarrett D'Amore 
2270cec8643bSMichal Nowak static int
roff_block_sub(ROFF_ARGS)227195c635efSGarrett D'Amore roff_block_sub(ROFF_ARGS)
227295c635efSGarrett D'Amore {
2273c66b8046SYuri Pankov 	enum roff_tok	t;
227495c635efSGarrett D'Amore 	int		i, j;
227595c635efSGarrett D'Amore 
227695c635efSGarrett D'Amore 	/*
227795c635efSGarrett D'Amore 	 * First check whether a custom macro exists at this level.  If
227895c635efSGarrett D'Amore 	 * it does, then check against it.  This is some of groff's
227995c635efSGarrett D'Amore 	 * stranger behaviours.  If we encountered a custom end-scope
228095c635efSGarrett D'Amore 	 * tag and that tag also happens to be a "real" macro, then we
228195c635efSGarrett D'Amore 	 * need to try interpreting it again as a real macro.  If it's
228295c635efSGarrett D'Amore 	 * not, then return ignore.  Else continue.
228395c635efSGarrett D'Amore 	 */
228495c635efSGarrett D'Amore 
228595c635efSGarrett D'Amore 	if (r->last->end) {
228695c635efSGarrett D'Amore 		for (i = pos, j = 0; r->last->end[j]; j++, i++)
2287260e9a87SYuri Pankov 			if (buf->buf[i] != r->last->end[j])
228895c635efSGarrett D'Amore 				break;
228995c635efSGarrett D'Amore 
2290260e9a87SYuri Pankov 		if (r->last->end[j] == '\0' &&
2291260e9a87SYuri Pankov 		    (buf->buf[i] == '\0' ||
2292260e9a87SYuri Pankov 		     buf->buf[i] == ' ' ||
2293260e9a87SYuri Pankov 		     buf->buf[i] == '\t')) {
229495c635efSGarrett D'Amore 			roffnode_pop(r);
229595c635efSGarrett D'Amore 			roffnode_cleanscope(r);
229695c635efSGarrett D'Amore 
2297260e9a87SYuri Pankov 			while (buf->buf[i] == ' ' || buf->buf[i] == '\t')
229895c635efSGarrett D'Amore 				i++;
229995c635efSGarrett D'Amore 
230095c635efSGarrett D'Amore 			pos = i;
2301260e9a87SYuri Pankov 			if (roff_parse(r, buf->buf, &pos, ln, ppos) !=
2302c66b8046SYuri Pankov 			    TOKEN_NONE)
2303371584c2SYuri Pankov 				return ROFF_RERUN;
2304371584c2SYuri Pankov 			return ROFF_IGN;
230595c635efSGarrett D'Amore 		}
230695c635efSGarrett D'Amore 	}
230795c635efSGarrett D'Amore 
230895c635efSGarrett D'Amore 	/*
230995c635efSGarrett D'Amore 	 * If we have no custom end-query or lookup failed, then try
231095c635efSGarrett D'Amore 	 * pulling it out of the hashtable.
231195c635efSGarrett D'Amore 	 */
231295c635efSGarrett D'Amore 
2313260e9a87SYuri Pankov 	t = roff_parse(r, buf->buf, &pos, ln, ppos);
231495c635efSGarrett D'Amore 
2315260e9a87SYuri Pankov 	if (t != ROFF_cblock) {
2316260e9a87SYuri Pankov 		if (tok != ROFF_ig)
2317260e9a87SYuri Pankov 			roff_setstr(r, r->last->name, buf->buf + ppos, 2);
2318371584c2SYuri Pankov 		return ROFF_IGN;
231995c635efSGarrett D'Amore 	}
232095c635efSGarrett D'Amore 
2321371584c2SYuri Pankov 	return (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs);
232295c635efSGarrett D'Amore }
232395c635efSGarrett D'Amore 
2324cec8643bSMichal Nowak static int
roff_block_text(ROFF_ARGS)232595c635efSGarrett D'Amore roff_block_text(ROFF_ARGS)
232695c635efSGarrett D'Amore {
232795c635efSGarrett D'Amore 
2328260e9a87SYuri Pankov 	if (tok != ROFF_ig)
2329260e9a87SYuri Pankov 		roff_setstr(r, r->last->name, buf->buf + pos, 2);
233095c635efSGarrett D'Amore 
2331371584c2SYuri Pankov 	return ROFF_IGN;
233295c635efSGarrett D'Amore }
233395c635efSGarrett D'Amore 
2334*4d131170SRobert Mustacchi /*
2335*4d131170SRobert Mustacchi  * Check for a closing "\}" and handle it.
2336*4d131170SRobert Mustacchi  * In this function, the final "int *offs" argument is used for
2337*4d131170SRobert Mustacchi  * different purposes than elsewhere:
2338*4d131170SRobert Mustacchi  * Input: *offs == 0: caller wants to discard arguments following \}
2339*4d131170SRobert Mustacchi  *        *offs == 1: caller wants to preserve text following \}
2340*4d131170SRobert Mustacchi  * Output: *offs = 0: tell caller to discard input line
2341*4d131170SRobert Mustacchi  *         *offs = 1: tell caller to use input line
2342*4d131170SRobert Mustacchi  */
2343cec8643bSMichal Nowak static int
roff_cond_checkend(ROFF_ARGS)2344*4d131170SRobert Mustacchi roff_cond_checkend(ROFF_ARGS)
234595c635efSGarrett D'Amore {
234695c635efSGarrett D'Amore 	char		*ep;
2347cec8643bSMichal Nowak 	int		 endloop, irc, rr;
234895c635efSGarrett D'Amore 
2349cec8643bSMichal Nowak 	irc = ROFF_IGN;
235095c635efSGarrett D'Amore 	rr = r->last->rule;
2351cec8643bSMichal Nowak 	endloop = tok != ROFF_while ? ROFF_IGN :
2352cec8643bSMichal Nowak 	    rr ? ROFF_LOOPCONT : ROFF_LOOPEXIT;
2353cec8643bSMichal Nowak 	if (roffnode_cleanscope(r))
2354cec8643bSMichal Nowak 		irc |= endloop;
2355698f87a4SGarrett D'Amore 
2356260e9a87SYuri Pankov 	/*
2357*4d131170SRobert Mustacchi 	 * If "\}" occurs on a macro line without a preceding macro or
2358*4d131170SRobert Mustacchi 	 * a text line contains nothing else, drop the line completely.
2359260e9a87SYuri Pankov 	 */
2360260e9a87SYuri Pankov 
2361260e9a87SYuri Pankov 	ep = buf->buf + pos;
2362*4d131170SRobert Mustacchi 	if (ep[0] == '\\' && ep[1] == '}' && (ep[2] == '\0' || *offs == 0))
2363260e9a87SYuri Pankov 		rr = 0;
2364260e9a87SYuri Pankov 
2365cec8643bSMichal Nowak 	/*
2366*4d131170SRobert Mustacchi 	 * The closing delimiter "\}" rewinds the conditional scope
2367cec8643bSMichal Nowak 	 * but is otherwise ignored when interpreting the line.
2368cec8643bSMichal Nowak 	 */
2369698f87a4SGarrett D'Amore 
2370260e9a87SYuri Pankov 	while ((ep = strchr(ep, '\\')) != NULL) {
2371c66b8046SYuri Pankov 		switch (ep[1]) {
2372c66b8046SYuri Pankov 		case '}':
2373*4d131170SRobert Mustacchi 			if (ep[2] == '\0')
2374*4d131170SRobert Mustacchi 				ep[0] = '\0';
2375*4d131170SRobert Mustacchi 			else if (rr)
2376*4d131170SRobert Mustacchi 				ep[1] = '&';
2377*4d131170SRobert Mustacchi 			else
2378c66b8046SYuri Pankov 				memmove(ep, ep + 2, strlen(ep + 2) + 1);
2379cec8643bSMichal Nowak 			if (roff_ccond(r, ln, ep - buf->buf))
2380cec8643bSMichal Nowak 				irc |= endloop;
2381c66b8046SYuri Pankov 			break;
2382c66b8046SYuri Pankov 		case '\0':
2383260e9a87SYuri Pankov 			++ep;
2384c66b8046SYuri Pankov 			break;
2385c66b8046SYuri Pankov 		default:
2386c66b8046SYuri Pankov 			ep += 2;
2387c66b8046SYuri Pankov 			break;
2388260e9a87SYuri Pankov 		}
2389c66b8046SYuri Pankov 	}
2390*4d131170SRobert Mustacchi 	*offs = rr;
2391*4d131170SRobert Mustacchi 	return irc;
2392*4d131170SRobert Mustacchi }
2393*4d131170SRobert Mustacchi 
2394*4d131170SRobert Mustacchi /*
2395*4d131170SRobert Mustacchi  * Parse and process a request or macro line in conditional scope.
2396*4d131170SRobert Mustacchi  */
2397*4d131170SRobert Mustacchi static int
roff_cond_sub(ROFF_ARGS)2398*4d131170SRobert Mustacchi roff_cond_sub(ROFF_ARGS)
2399*4d131170SRobert Mustacchi {
2400*4d131170SRobert Mustacchi 	struct roffnode	*bl;
2401*4d131170SRobert Mustacchi 	int		 irc, rr;
2402*4d131170SRobert Mustacchi 	enum roff_tok	 t;
2403*4d131170SRobert Mustacchi 
2404*4d131170SRobert Mustacchi 	rr = 0;  /* If arguments follow "\}", skip them. */
2405*4d131170SRobert Mustacchi 	irc = roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr);
2406*4d131170SRobert Mustacchi 	t = roff_parse(r, buf->buf, &pos, ln, ppos);
2407*4d131170SRobert Mustacchi 
2408*4d131170SRobert Mustacchi 	/* For now, let high level macros abort .ce mode. */
2409*4d131170SRobert Mustacchi 
2410*4d131170SRobert Mustacchi 	if (roffce_node != NULL &&
2411*4d131170SRobert Mustacchi 	    (t == TOKEN_NONE || t == ROFF_Dd || t == ROFF_EQ ||
2412*4d131170SRobert Mustacchi              t == ROFF_TH || t == ROFF_TS)) {
2413*4d131170SRobert Mustacchi 		r->man->last = roffce_node;
2414*4d131170SRobert Mustacchi 		r->man->next = ROFF_NEXT_SIBLING;
2415*4d131170SRobert Mustacchi 		roffce_lines = 0;
2416*4d131170SRobert Mustacchi 		roffce_node = NULL;
2417*4d131170SRobert Mustacchi 	}
2418c66b8046SYuri Pankov 
2419c66b8046SYuri Pankov 	/*
2420c66b8046SYuri Pankov 	 * Fully handle known macros when they are structurally
2421c66b8046SYuri Pankov 	 * required or when the conditional evaluated to true.
2422c66b8046SYuri Pankov 	 */
2423c66b8046SYuri Pankov 
2424*4d131170SRobert Mustacchi 	if (t == ROFF_break) {
2425*4d131170SRobert Mustacchi 		if (irc & ROFF_LOOPMASK)
2426*4d131170SRobert Mustacchi 			irc = ROFF_IGN | ROFF_LOOPEXIT;
2427*4d131170SRobert Mustacchi 		else if (rr) {
2428*4d131170SRobert Mustacchi 			for (bl = r->last; bl != NULL; bl = bl->parent) {
2429*4d131170SRobert Mustacchi 				bl->rule = 0;
2430*4d131170SRobert Mustacchi 				if (bl->tok == ROFF_while)
2431*4d131170SRobert Mustacchi 					break;
2432*4d131170SRobert Mustacchi 			}
2433*4d131170SRobert Mustacchi 		}
2434*4d131170SRobert Mustacchi 	} else if (t != TOKEN_NONE &&
2435*4d131170SRobert Mustacchi 	    (rr || roffs[t].flags & ROFFMAC_STRUCT))
2436*4d131170SRobert Mustacchi 		irc |= (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs);
2437*4d131170SRobert Mustacchi 	else
2438*4d131170SRobert Mustacchi 		irc |= rr ? ROFF_CONT : ROFF_IGN;
2439cec8643bSMichal Nowak 	return irc;
244095c635efSGarrett D'Amore }
244195c635efSGarrett D'Amore 
2442*4d131170SRobert Mustacchi /*
2443*4d131170SRobert Mustacchi  * Parse and process a text line in conditional scope.
2444*4d131170SRobert Mustacchi  */
2445cec8643bSMichal Nowak static int
roff_cond_text(ROFF_ARGS)244695c635efSGarrett D'Amore roff_cond_text(ROFF_ARGS)
244795c635efSGarrett D'Amore {
2448*4d131170SRobert Mustacchi 	int	 irc, rr;
244995c635efSGarrett D'Amore 
2450*4d131170SRobert Mustacchi 	rr = 1;  /* If arguments follow "\}", preserve them. */
2451*4d131170SRobert Mustacchi 	irc = roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr);
2452cec8643bSMichal Nowak 	if (rr)
2453cec8643bSMichal Nowak 		irc |= ROFF_CONT;
2454cec8643bSMichal Nowak 	return irc;
245595c635efSGarrett D'Amore }
245695c635efSGarrett D'Amore 
2457371584c2SYuri Pankov /* --- handling of numeric and conditional expressions -------------------- */
2458371584c2SYuri Pankov 
2459260e9a87SYuri Pankov /*
2460260e9a87SYuri Pankov  * Parse a single signed integer number.  Stop at the first non-digit.
2461260e9a87SYuri Pankov  * If there is at least one digit, return success and advance the
2462260e9a87SYuri Pankov  * parse point, else return failure and let the parse point unchanged.
2463260e9a87SYuri Pankov  * Ignore overflows, treat them just like the C language.
2464260e9a87SYuri Pankov  */
2465698f87a4SGarrett D'Amore static int
roff_getnum(const char * v,int * pos,int * res,int flags)2466260e9a87SYuri Pankov roff_getnum(const char *v, int *pos, int *res, int flags)
2467698f87a4SGarrett D'Amore {
2468260e9a87SYuri Pankov 	int	 myres, scaled, n, p;
2469260e9a87SYuri Pankov 
2470260e9a87SYuri Pankov 	if (NULL == res)
2471260e9a87SYuri Pankov 		res = &myres;
2472698f87a4SGarrett D'Amore 
2473698f87a4SGarrett D'Amore 	p = *pos;
2474698f87a4SGarrett D'Amore 	n = v[p] == '-';
2475260e9a87SYuri Pankov 	if (n || v[p] == '+')
2476260e9a87SYuri Pankov 		p++;
2477260e9a87SYuri Pankov 
2478260e9a87SYuri Pankov 	if (flags & ROFFNUM_WHITE)
2479260e9a87SYuri Pankov 		while (isspace((unsigned char)v[p]))
2480698f87a4SGarrett D'Amore 			p++;
2481698f87a4SGarrett D'Amore 
2482698f87a4SGarrett D'Amore 	for (*res = 0; isdigit((unsigned char)v[p]); p++)
2483260e9a87SYuri Pankov 		*res = 10 * *res + v[p] - '0';
2484698f87a4SGarrett D'Amore 	if (p == *pos + n)
2485698f87a4SGarrett D'Amore 		return 0;
2486698f87a4SGarrett D'Amore 
2487698f87a4SGarrett D'Amore 	if (n)
2488698f87a4SGarrett D'Amore 		*res = -*res;
2489698f87a4SGarrett D'Amore 
2490260e9a87SYuri Pankov 	/* Each number may be followed by one optional scaling unit. */
2491698f87a4SGarrett D'Amore 
2492260e9a87SYuri Pankov 	switch (v[p]) {
2493260e9a87SYuri Pankov 	case 'f':
2494260e9a87SYuri Pankov 		scaled = *res * 65536;
2495698f87a4SGarrett D'Amore 		break;
2496260e9a87SYuri Pankov 	case 'i':
2497260e9a87SYuri Pankov 		scaled = *res * 240;
2498698f87a4SGarrett D'Amore 		break;
2499260e9a87SYuri Pankov 	case 'c':
2500260e9a87SYuri Pankov 		scaled = *res * 240 / 2.54;
2501260e9a87SYuri Pankov 		break;
2502260e9a87SYuri Pankov 	case 'v':
2503260e9a87SYuri Pankov 	case 'P':
2504260e9a87SYuri Pankov 		scaled = *res * 40;
2505260e9a87SYuri Pankov 		break;
2506260e9a87SYuri Pankov 	case 'm':
2507260e9a87SYuri Pankov 	case 'n':
2508260e9a87SYuri Pankov 		scaled = *res * 24;
2509260e9a87SYuri Pankov 		break;
2510260e9a87SYuri Pankov 	case 'p':
2511260e9a87SYuri Pankov 		scaled = *res * 10 / 3;
2512260e9a87SYuri Pankov 		break;
2513260e9a87SYuri Pankov 	case 'u':
2514260e9a87SYuri Pankov 		scaled = *res;
2515260e9a87SYuri Pankov 		break;
2516260e9a87SYuri Pankov 	case 'M':
2517260e9a87SYuri Pankov 		scaled = *res * 6 / 25;
2518698f87a4SGarrett D'Amore 		break;
2519698f87a4SGarrett D'Amore 	default:
2520260e9a87SYuri Pankov 		scaled = *res;
2521260e9a87SYuri Pankov 		p--;
2522260e9a87SYuri Pankov 		break;
2523260e9a87SYuri Pankov 	}
2524260e9a87SYuri Pankov 	if (flags & ROFFNUM_SCALE)
2525260e9a87SYuri Pankov 		*res = scaled;
2526260e9a87SYuri Pankov 
2527260e9a87SYuri Pankov 	*pos = p + 1;
2528371584c2SYuri Pankov 	return 1;
2529260e9a87SYuri Pankov }
2530260e9a87SYuri Pankov 
2531260e9a87SYuri Pankov /*
2532260e9a87SYuri Pankov  * Evaluate a string comparison condition.
2533260e9a87SYuri Pankov  * The first character is the delimiter.
2534260e9a87SYuri Pankov  * Succeed if the string up to its second occurrence
2535260e9a87SYuri Pankov  * matches the string up to its third occurence.
2536260e9a87SYuri Pankov  * Advance the cursor after the third occurrence
2537260e9a87SYuri Pankov  * or lacking that, to the end of the line.
2538260e9a87SYuri Pankov  */
2539260e9a87SYuri Pankov static int
roff_evalstrcond(const char * v,int * pos)2540260e9a87SYuri Pankov roff_evalstrcond(const char *v, int *pos)
2541260e9a87SYuri Pankov {
2542260e9a87SYuri Pankov 	const char	*s1, *s2, *s3;
2543260e9a87SYuri Pankov 	int		 match;
2544260e9a87SYuri Pankov 
2545260e9a87SYuri Pankov 	match = 0;
2546260e9a87SYuri Pankov 	s1 = v + *pos;		/* initial delimiter */
2547260e9a87SYuri Pankov 	s2 = s1 + 1;		/* for scanning the first string */
2548260e9a87SYuri Pankov 	s3 = strchr(s2, *s1);	/* for scanning the second string */
2549260e9a87SYuri Pankov 
2550260e9a87SYuri Pankov 	if (NULL == s3)		/* found no middle delimiter */
2551260e9a87SYuri Pankov 		goto out;
2552260e9a87SYuri Pankov 
2553260e9a87SYuri Pankov 	while ('\0' != *++s3) {
2554260e9a87SYuri Pankov 		if (*s2 != *s3) {  /* mismatch */
2555260e9a87SYuri Pankov 			s3 = strchr(s3, *s1);
2556260e9a87SYuri Pankov 			break;
2557260e9a87SYuri Pankov 		}
2558260e9a87SYuri Pankov 		if (*s3 == *s1) {  /* found the final delimiter */
2559260e9a87SYuri Pankov 			match = 1;
2560260e9a87SYuri Pankov 			break;
2561260e9a87SYuri Pankov 		}
2562260e9a87SYuri Pankov 		s2++;
2563260e9a87SYuri Pankov 	}
2564260e9a87SYuri Pankov 
2565260e9a87SYuri Pankov out:
2566260e9a87SYuri Pankov 	if (NULL == s3)
2567260e9a87SYuri Pankov 		s3 = strchr(s2, '\0');
2568260e9a87SYuri Pankov 	else if (*s3 != '\0')
2569260e9a87SYuri Pankov 		s3++;
2570260e9a87SYuri Pankov 	*pos = s3 - v;
2571371584c2SYuri Pankov 	return match;
2572260e9a87SYuri Pankov }
2573260e9a87SYuri Pankov 
2574260e9a87SYuri Pankov /*
2575260e9a87SYuri Pankov  * Evaluate an optionally negated single character, numerical,
2576260e9a87SYuri Pankov  * or string condition.
2577260e9a87SYuri Pankov  */
2578260e9a87SYuri Pankov static int
roff_evalcond(struct roff * r,int ln,char * v,int * pos)2579371584c2SYuri Pankov roff_evalcond(struct roff *r, int ln, char *v, int *pos)
2580260e9a87SYuri Pankov {
2581cec8643bSMichal Nowak 	const char	*start, *end;
2582371584c2SYuri Pankov 	char		*cp, *name;
2583371584c2SYuri Pankov 	size_t		 sz;
2584cec8643bSMichal Nowak 	int		 deftype, len, number, savepos, istrue, wanttrue;
2585260e9a87SYuri Pankov 
2586260e9a87SYuri Pankov 	if ('!' == v[*pos]) {
2587260e9a87SYuri Pankov 		wanttrue = 0;
2588260e9a87SYuri Pankov 		(*pos)++;
2589260e9a87SYuri Pankov 	} else
2590260e9a87SYuri Pankov 		wanttrue = 1;
2591260e9a87SYuri Pankov 
2592260e9a87SYuri Pankov 	switch (v[*pos]) {
2593260e9a87SYuri Pankov 	case '\0':
2594371584c2SYuri Pankov 		return 0;
2595260e9a87SYuri Pankov 	case 'n':
2596260e9a87SYuri Pankov 	case 'o':
2597260e9a87SYuri Pankov 		(*pos)++;
2598371584c2SYuri Pankov 		return wanttrue;
2599260e9a87SYuri Pankov 	case 'e':
2600260e9a87SYuri Pankov 	case 't':
2601260e9a87SYuri Pankov 	case 'v':
2602260e9a87SYuri Pankov 		(*pos)++;
2603371584c2SYuri Pankov 		return !wanttrue;
2604cec8643bSMichal Nowak 	case 'c':
2605cec8643bSMichal Nowak 		do {
2606cec8643bSMichal Nowak 			(*pos)++;
2607cec8643bSMichal Nowak 		} while (v[*pos] == ' ');
2608cec8643bSMichal Nowak 
2609cec8643bSMichal Nowak 		/*
2610cec8643bSMichal Nowak 		 * Quirk for groff compatibility:
2611cec8643bSMichal Nowak 		 * The horizontal tab is neither available nor unavailable.
2612cec8643bSMichal Nowak 		 */
2613cec8643bSMichal Nowak 
2614cec8643bSMichal Nowak 		if (v[*pos] == '\t') {
2615cec8643bSMichal Nowak 			(*pos)++;
2616cec8643bSMichal Nowak 			return 0;
2617cec8643bSMichal Nowak 		}
2618cec8643bSMichal Nowak 
2619cec8643bSMichal Nowak 		/* Printable ASCII characters are available. */
2620cec8643bSMichal Nowak 
2621cec8643bSMichal Nowak 		if (v[*pos] != '\\') {
2622cec8643bSMichal Nowak 			(*pos)++;
2623cec8643bSMichal Nowak 			return wanttrue;
2624cec8643bSMichal Nowak 		}
2625cec8643bSMichal Nowak 
2626cec8643bSMichal Nowak 		end = v + ++*pos;
2627cec8643bSMichal Nowak 		switch (mandoc_escape(&end, &start, &len)) {
2628cec8643bSMichal Nowak 		case ESCAPE_SPECIAL:
2629cec8643bSMichal Nowak 			istrue = mchars_spec2cp(start, len) != -1;
2630cec8643bSMichal Nowak 			break;
2631cec8643bSMichal Nowak 		case ESCAPE_UNICODE:
2632cec8643bSMichal Nowak 			istrue = 1;
2633cec8643bSMichal Nowak 			break;
2634cec8643bSMichal Nowak 		case ESCAPE_NUMBERED:
2635cec8643bSMichal Nowak 			istrue = mchars_num2char(start, len) != -1;
2636cec8643bSMichal Nowak 			break;
2637cec8643bSMichal Nowak 		default:
2638cec8643bSMichal Nowak 			istrue = !wanttrue;
2639cec8643bSMichal Nowak 			break;
2640cec8643bSMichal Nowak 		}
2641cec8643bSMichal Nowak 		*pos = end - v;
2642cec8643bSMichal Nowak 		return istrue == wanttrue;
2643c66b8046SYuri Pankov 	case 'd':
2644371584c2SYuri Pankov 	case 'r':
2645c66b8046SYuri Pankov 		cp = v + *pos + 1;
2646c66b8046SYuri Pankov 		while (*cp == ' ')
2647c66b8046SYuri Pankov 			cp++;
2648c66b8046SYuri Pankov 		name = cp;
2649c66b8046SYuri Pankov 		sz = roff_getname(r, &cp, ln, cp - v);
2650c66b8046SYuri Pankov 		if (sz == 0)
2651c66b8046SYuri Pankov 			istrue = 0;
2652c66b8046SYuri Pankov 		else if (v[*pos] == 'r')
2653c66b8046SYuri Pankov 			istrue = roff_hasregn(r, name, sz);
2654c66b8046SYuri Pankov 		else {
2655c66b8046SYuri Pankov 			deftype = ROFFDEF_ANY;
2656c66b8046SYuri Pankov 		        roff_getstrn(r, name, sz, &deftype);
2657c66b8046SYuri Pankov 			istrue = !!deftype;
2658c66b8046SYuri Pankov 		}
2659cec8643bSMichal Nowak 		*pos = (name + sz) - v;
2660c66b8046SYuri Pankov 		return istrue == wanttrue;
2661260e9a87SYuri Pankov 	default:
2662260e9a87SYuri Pankov 		break;
2663260e9a87SYuri Pankov 	}
2664260e9a87SYuri Pankov 
2665260e9a87SYuri Pankov 	savepos = *pos;
2666260e9a87SYuri Pankov 	if (roff_evalnum(r, ln, v, pos, &number, ROFFNUM_SCALE))
2667371584c2SYuri Pankov 		return (number > 0) == wanttrue;
2668260e9a87SYuri Pankov 	else if (*pos == savepos)
2669371584c2SYuri Pankov 		return roff_evalstrcond(v, pos) == wanttrue;
2670260e9a87SYuri Pankov 	else
2671371584c2SYuri Pankov 		return 0;
2672698f87a4SGarrett D'Amore }
2673698f87a4SGarrett D'Amore 
2674cec8643bSMichal Nowak static int
roff_line_ignore(ROFF_ARGS)267595c635efSGarrett D'Amore roff_line_ignore(ROFF_ARGS)
267695c635efSGarrett D'Amore {
267795c635efSGarrett D'Amore 
2678371584c2SYuri Pankov 	return ROFF_IGN;
267995c635efSGarrett D'Amore }
268095c635efSGarrett D'Amore 
2681cec8643bSMichal Nowak static int
roff_insec(ROFF_ARGS)2682260e9a87SYuri Pankov roff_insec(ROFF_ARGS)
2683260e9a87SYuri Pankov {
2684260e9a87SYuri Pankov 
2685cec8643bSMichal Nowak 	mandoc_msg(MANDOCERR_REQ_INSEC, ln, ppos, "%s", roff_name[tok]);
2686371584c2SYuri Pankov 	return ROFF_IGN;
2687260e9a87SYuri Pankov }
2688260e9a87SYuri Pankov 
2689cec8643bSMichal Nowak static int
roff_unsupp(ROFF_ARGS)2690260e9a87SYuri Pankov roff_unsupp(ROFF_ARGS)
2691260e9a87SYuri Pankov {
2692260e9a87SYuri Pankov 
2693cec8643bSMichal Nowak 	mandoc_msg(MANDOCERR_REQ_UNSUPP, ln, ppos, "%s", roff_name[tok]);
2694371584c2SYuri Pankov 	return ROFF_IGN;
2695260e9a87SYuri Pankov }
2696260e9a87SYuri Pankov 
2697cec8643bSMichal Nowak static int
roff_cond(ROFF_ARGS)269895c635efSGarrett D'Amore roff_cond(ROFF_ARGS)
269995c635efSGarrett D'Amore {
2700cec8643bSMichal Nowak 	int	 irc;
2701698f87a4SGarrett D'Amore 
2702698f87a4SGarrett D'Amore 	roffnode_push(r, tok, NULL, ln, ppos);
270395c635efSGarrett D'Amore 
270495c635efSGarrett D'Amore 	/*
270595c635efSGarrett D'Amore 	 * An `.el' has no conditional body: it will consume the value
270695c635efSGarrett D'Amore 	 * of the current rstack entry set in prior `ie' calls or
270795c635efSGarrett D'Amore 	 * defaults to DENY.
270895c635efSGarrett D'Amore 	 *
270995c635efSGarrett D'Amore 	 * If we're not an `el', however, then evaluate the conditional.
271095c635efSGarrett D'Amore 	 */
271195c635efSGarrett D'Amore 
2712260e9a87SYuri Pankov 	r->last->rule = tok == ROFF_el ?
2713260e9a87SYuri Pankov 	    (r->rstackpos < 0 ? 0 : r->rstack[r->rstackpos--]) :
2714260e9a87SYuri Pankov 	    roff_evalcond(r, ln, buf->buf, &pos);
271595c635efSGarrett D'Amore 
271695c635efSGarrett D'Amore 	/*
271795c635efSGarrett D'Amore 	 * An if-else will put the NEGATION of the current evaluated
271895c635efSGarrett D'Amore 	 * conditional into the stack of rules.
271995c635efSGarrett D'Amore 	 */
272095c635efSGarrett D'Amore 
2721260e9a87SYuri Pankov 	if (tok == ROFF_ie) {
2722260e9a87SYuri Pankov 		if (r->rstackpos + 1 == r->rstacksz) {
2723260e9a87SYuri Pankov 			r->rstacksz += 16;
2724260e9a87SYuri Pankov 			r->rstack = mandoc_reallocarray(r->rstack,
2725260e9a87SYuri Pankov 			    r->rstacksz, sizeof(int));
272695c635efSGarrett D'Amore 		}
2727260e9a87SYuri Pankov 		r->rstack[++r->rstackpos] = !r->last->rule;
272895c635efSGarrett D'Amore 	}
272995c635efSGarrett D'Amore 
273095c635efSGarrett D'Amore 	/* If the parent has false as its rule, then so do we. */
273195c635efSGarrett D'Amore 
2732260e9a87SYuri Pankov 	if (r->last->parent && !r->last->parent->rule)
2733260e9a87SYuri Pankov 		r->last->rule = 0;
273495c635efSGarrett D'Amore 
273595c635efSGarrett D'Amore 	/*
2736698f87a4SGarrett D'Amore 	 * Determine scope.
2737698f87a4SGarrett D'Amore 	 * If there is nothing on the line after the conditional,
2738698f87a4SGarrett D'Amore 	 * not even whitespace, use next-line scope.
2739cec8643bSMichal Nowak 	 * Except that .while does not support next-line scope.
274095c635efSGarrett D'Amore 	 */
274195c635efSGarrett D'Amore 
2742cec8643bSMichal Nowak 	if (buf->buf[pos] == '\0' && tok != ROFF_while) {
2743698f87a4SGarrett D'Amore 		r->last->endspan = 2;
2744698f87a4SGarrett D'Amore 		goto out;
2745698f87a4SGarrett D'Amore 	}
2746698f87a4SGarrett D'Amore 
2747260e9a87SYuri Pankov 	while (buf->buf[pos] == ' ')
2748698f87a4SGarrett D'Amore 		pos++;
2749698f87a4SGarrett D'Amore 
2750698f87a4SGarrett D'Amore 	/* An opening brace requests multiline scope. */
275195c635efSGarrett D'Amore 
2752260e9a87SYuri Pankov 	if (buf->buf[pos] == '\\' && buf->buf[pos + 1] == '{') {
275395c635efSGarrett D'Amore 		r->last->endspan = -1;
275495c635efSGarrett D'Amore 		pos += 2;
2755371584c2SYuri Pankov 		while (buf->buf[pos] == ' ')
2756371584c2SYuri Pankov 			pos++;
2757698f87a4SGarrett D'Amore 		goto out;
275895c635efSGarrett D'Amore 	}
275995c635efSGarrett D'Amore 
276095c635efSGarrett D'Amore 	/*
2761698f87a4SGarrett D'Amore 	 * Anything else following the conditional causes
2762698f87a4SGarrett D'Amore 	 * single-line scope.  Warn if the scope contains
2763698f87a4SGarrett D'Amore 	 * nothing but trailing whitespace.
276495c635efSGarrett D'Amore 	 */
276595c635efSGarrett D'Amore 
2766260e9a87SYuri Pankov 	if (buf->buf[pos] == '\0')
2767cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_COND_EMPTY,
2768cec8643bSMichal Nowak 		    ln, ppos, "%s", roff_name[tok]);
276995c635efSGarrett D'Amore 
2770698f87a4SGarrett D'Amore 	r->last->endspan = 1;
277195c635efSGarrett D'Amore 
2772698f87a4SGarrett D'Amore out:
277395c635efSGarrett D'Amore 	*offs = pos;
2774cec8643bSMichal Nowak 	irc = ROFF_RERUN;
2775cec8643bSMichal Nowak 	if (tok == ROFF_while)
2776cec8643bSMichal Nowak 		irc |= ROFF_WHILE;
2777cec8643bSMichal Nowak 	return irc;
277895c635efSGarrett D'Amore }
277995c635efSGarrett D'Amore 
2780cec8643bSMichal Nowak static int
roff_ds(ROFF_ARGS)278195c635efSGarrett D'Amore roff_ds(ROFF_ARGS)
278295c635efSGarrett D'Amore {
2783260e9a87SYuri Pankov 	char		*string;
2784260e9a87SYuri Pankov 	const char	*name;
2785260e9a87SYuri Pankov 	size_t		 namesz;
2786260e9a87SYuri Pankov 
2787260e9a87SYuri Pankov 	/* Ignore groff compatibility mode for now. */
2788260e9a87SYuri Pankov 
2789260e9a87SYuri Pankov 	if (tok == ROFF_ds1)
2790260e9a87SYuri Pankov 		tok = ROFF_ds;
2791260e9a87SYuri Pankov 	else if (tok == ROFF_as1)
2792260e9a87SYuri Pankov 		tok = ROFF_as;
279395c635efSGarrett D'Amore 
279495c635efSGarrett D'Amore 	/*
2795260e9a87SYuri Pankov 	 * The first word is the name of the string.
2796260e9a87SYuri Pankov 	 * If it is empty or terminated by an escape sequence,
2797260e9a87SYuri Pankov 	 * abort the `ds' request without defining anything.
279895c635efSGarrett D'Amore 	 */
279995c635efSGarrett D'Amore 
2800260e9a87SYuri Pankov 	name = string = buf->buf + pos;
2801260e9a87SYuri Pankov 	if (*name == '\0')
2802371584c2SYuri Pankov 		return ROFF_IGN;
280395c635efSGarrett D'Amore 
2804260e9a87SYuri Pankov 	namesz = roff_getname(r, &string, ln, pos);
2805cec8643bSMichal Nowak 	switch (name[namesz]) {
2806cec8643bSMichal Nowak 	case '\\':
2807371584c2SYuri Pankov 		return ROFF_IGN;
2808cec8643bSMichal Nowak 	case '\t':
2809cec8643bSMichal Nowak 		string = buf->buf + pos + namesz;
2810cec8643bSMichal Nowak 		break;
2811cec8643bSMichal Nowak 	default:
2812cec8643bSMichal Nowak 		break;
2813cec8643bSMichal Nowak 	}
2814260e9a87SYuri Pankov 
2815260e9a87SYuri Pankov 	/* Read past the initial double-quote, if any. */
2816260e9a87SYuri Pankov 	if (*string == '"')
281795c635efSGarrett D'Amore 		string++;
281895c635efSGarrett D'Amore 
281995c635efSGarrett D'Amore 	/* The rest is the value. */
2820260e9a87SYuri Pankov 	roff_setstrn(&r->strtab, name, namesz, string, strlen(string),
2821260e9a87SYuri Pankov 	    ROFF_as == tok);
2822c66b8046SYuri Pankov 	roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
2823371584c2SYuri Pankov 	return ROFF_IGN;
282495c635efSGarrett D'Amore }
282595c635efSGarrett D'Amore 
2826260e9a87SYuri Pankov /*
2827260e9a87SYuri Pankov  * Parse a single operator, one or two characters long.
2828260e9a87SYuri Pankov  * If the operator is recognized, return success and advance the
2829260e9a87SYuri Pankov  * parse point, else return failure and let the parse point unchanged.
2830260e9a87SYuri Pankov  */
2831260e9a87SYuri Pankov static int
roff_getop(const char * v,int * pos,char * res)2832260e9a87SYuri Pankov roff_getop(const char *v, int *pos, char *res)
2833260e9a87SYuri Pankov {
2834260e9a87SYuri Pankov 
2835260e9a87SYuri Pankov 	*res = v[*pos];
2836260e9a87SYuri Pankov 
2837260e9a87SYuri Pankov 	switch (*res) {
2838260e9a87SYuri Pankov 	case '+':
2839260e9a87SYuri Pankov 	case '-':
2840260e9a87SYuri Pankov 	case '*':
2841260e9a87SYuri Pankov 	case '/':
2842260e9a87SYuri Pankov 	case '%':
2843260e9a87SYuri Pankov 	case '&':
2844260e9a87SYuri Pankov 	case ':':
2845260e9a87SYuri Pankov 		break;
2846260e9a87SYuri Pankov 	case '<':
2847260e9a87SYuri Pankov 		switch (v[*pos + 1]) {
2848260e9a87SYuri Pankov 		case '=':
2849260e9a87SYuri Pankov 			*res = 'l';
2850260e9a87SYuri Pankov 			(*pos)++;
2851260e9a87SYuri Pankov 			break;
2852260e9a87SYuri Pankov 		case '>':
2853260e9a87SYuri Pankov 			*res = '!';
2854260e9a87SYuri Pankov 			(*pos)++;
2855260e9a87SYuri Pankov 			break;
2856260e9a87SYuri Pankov 		case '?':
2857260e9a87SYuri Pankov 			*res = 'i';
2858260e9a87SYuri Pankov 			(*pos)++;
2859260e9a87SYuri Pankov 			break;
2860260e9a87SYuri Pankov 		default:
2861260e9a87SYuri Pankov 			break;
2862260e9a87SYuri Pankov 		}
2863260e9a87SYuri Pankov 		break;
2864260e9a87SYuri Pankov 	case '>':
2865260e9a87SYuri Pankov 		switch (v[*pos + 1]) {
2866260e9a87SYuri Pankov 		case '=':
2867260e9a87SYuri Pankov 			*res = 'g';
2868260e9a87SYuri Pankov 			(*pos)++;
2869260e9a87SYuri Pankov 			break;
2870260e9a87SYuri Pankov 		case '?':
2871260e9a87SYuri Pankov 			*res = 'a';
2872260e9a87SYuri Pankov 			(*pos)++;
2873260e9a87SYuri Pankov 			break;
2874260e9a87SYuri Pankov 		default:
2875260e9a87SYuri Pankov 			break;
2876260e9a87SYuri Pankov 		}
2877260e9a87SYuri Pankov 		break;
2878260e9a87SYuri Pankov 	case '=':
2879260e9a87SYuri Pankov 		if ('=' == v[*pos + 1])
2880260e9a87SYuri Pankov 			(*pos)++;
2881260e9a87SYuri Pankov 		break;
2882260e9a87SYuri Pankov 	default:
2883371584c2SYuri Pankov 		return 0;
2884260e9a87SYuri Pankov 	}
2885260e9a87SYuri Pankov 	(*pos)++;
2886260e9a87SYuri Pankov 
2887371584c2SYuri Pankov 	return *res;
2888260e9a87SYuri Pankov }
2889260e9a87SYuri Pankov 
2890260e9a87SYuri Pankov /*
2891260e9a87SYuri Pankov  * Evaluate either a parenthesized numeric expression
2892260e9a87SYuri Pankov  * or a single signed integer number.
2893260e9a87SYuri Pankov  */
2894260e9a87SYuri Pankov static int
roff_evalpar(struct roff * r,int ln,const char * v,int * pos,int * res,int flags)2895260e9a87SYuri Pankov roff_evalpar(struct roff *r, int ln,
2896260e9a87SYuri Pankov 	const char *v, int *pos, int *res, int flags)
2897260e9a87SYuri Pankov {
2898260e9a87SYuri Pankov 
2899260e9a87SYuri Pankov 	if ('(' != v[*pos])
2900371584c2SYuri Pankov 		return roff_getnum(v, pos, res, flags);
2901260e9a87SYuri Pankov 
2902260e9a87SYuri Pankov 	(*pos)++;
2903260e9a87SYuri Pankov 	if ( ! roff_evalnum(r, ln, v, pos, res, flags | ROFFNUM_WHITE))
2904371584c2SYuri Pankov 		return 0;
2905260e9a87SYuri Pankov 
2906260e9a87SYuri Pankov 	/*
2907260e9a87SYuri Pankov 	 * Omission of the closing parenthesis
2908260e9a87SYuri Pankov 	 * is an error in validation mode,
2909260e9a87SYuri Pankov 	 * but ignored in evaluation mode.
2910260e9a87SYuri Pankov 	 */
2911260e9a87SYuri Pankov 
2912260e9a87SYuri Pankov 	if (')' == v[*pos])
2913260e9a87SYuri Pankov 		(*pos)++;
2914260e9a87SYuri Pankov 	else if (NULL == res)
2915371584c2SYuri Pankov 		return 0;
2916260e9a87SYuri Pankov 
2917371584c2SYuri Pankov 	return 1;
2918260e9a87SYuri Pankov }
2919260e9a87SYuri Pankov 
2920260e9a87SYuri Pankov /*
2921260e9a87SYuri Pankov  * Evaluate a complete numeric expression.
2922260e9a87SYuri Pankov  * Proceed left to right, there is no concept of precedence.
2923260e9a87SYuri Pankov  */
2924260e9a87SYuri Pankov static int
roff_evalnum(struct roff * r,int ln,const char * v,int * pos,int * res,int flags)2925260e9a87SYuri Pankov roff_evalnum(struct roff *r, int ln, const char *v,
2926260e9a87SYuri Pankov 	int *pos, int *res, int flags)
2927260e9a87SYuri Pankov {
2928260e9a87SYuri Pankov 	int		 mypos, operand2;
2929260e9a87SYuri Pankov 	char		 operator;
2930260e9a87SYuri Pankov 
2931260e9a87SYuri Pankov 	if (NULL == pos) {
2932260e9a87SYuri Pankov 		mypos = 0;
2933260e9a87SYuri Pankov 		pos = &mypos;
2934260e9a87SYuri Pankov 	}
2935260e9a87SYuri Pankov 
2936260e9a87SYuri Pankov 	if (flags & ROFFNUM_WHITE)
2937260e9a87SYuri Pankov 		while (isspace((unsigned char)v[*pos]))
2938260e9a87SYuri Pankov 			(*pos)++;
2939260e9a87SYuri Pankov 
2940260e9a87SYuri Pankov 	if ( ! roff_evalpar(r, ln, v, pos, res, flags))
2941371584c2SYuri Pankov 		return 0;
2942260e9a87SYuri Pankov 
2943260e9a87SYuri Pankov 	while (1) {
2944260e9a87SYuri Pankov 		if (flags & ROFFNUM_WHITE)
2945260e9a87SYuri Pankov 			while (isspace((unsigned char)v[*pos]))
2946260e9a87SYuri Pankov 				(*pos)++;
2947260e9a87SYuri Pankov 
2948260e9a87SYuri Pankov 		if ( ! roff_getop(v, pos, &operator))
2949260e9a87SYuri Pankov 			break;
2950260e9a87SYuri Pankov 
2951260e9a87SYuri Pankov 		if (flags & ROFFNUM_WHITE)
2952260e9a87SYuri Pankov 			while (isspace((unsigned char)v[*pos]))
2953260e9a87SYuri Pankov 				(*pos)++;
2954260e9a87SYuri Pankov 
2955260e9a87SYuri Pankov 		if ( ! roff_evalpar(r, ln, v, pos, &operand2, flags))
2956371584c2SYuri Pankov 			return 0;
2957260e9a87SYuri Pankov 
2958260e9a87SYuri Pankov 		if (flags & ROFFNUM_WHITE)
2959260e9a87SYuri Pankov 			while (isspace((unsigned char)v[*pos]))
2960260e9a87SYuri Pankov 				(*pos)++;
2961260e9a87SYuri Pankov 
2962260e9a87SYuri Pankov 		if (NULL == res)
2963260e9a87SYuri Pankov 			continue;
2964260e9a87SYuri Pankov 
2965260e9a87SYuri Pankov 		switch (operator) {
2966260e9a87SYuri Pankov 		case '+':
2967260e9a87SYuri Pankov 			*res += operand2;
2968260e9a87SYuri Pankov 			break;
2969260e9a87SYuri Pankov 		case '-':
2970260e9a87SYuri Pankov 			*res -= operand2;
2971260e9a87SYuri Pankov 			break;
2972260e9a87SYuri Pankov 		case '*':
2973260e9a87SYuri Pankov 			*res *= operand2;
2974260e9a87SYuri Pankov 			break;
2975260e9a87SYuri Pankov 		case '/':
2976260e9a87SYuri Pankov 			if (operand2 == 0) {
2977260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_DIVZERO,
2978cec8643bSMichal Nowak 					ln, *pos, "%s", v);
2979260e9a87SYuri Pankov 				*res = 0;
2980260e9a87SYuri Pankov 				break;
2981260e9a87SYuri Pankov 			}
2982260e9a87SYuri Pankov 			*res /= operand2;
2983260e9a87SYuri Pankov 			break;
2984260e9a87SYuri Pankov 		case '%':
2985260e9a87SYuri Pankov 			if (operand2 == 0) {
2986260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_DIVZERO,
2987cec8643bSMichal Nowak 					ln, *pos, "%s", v);
2988260e9a87SYuri Pankov 				*res = 0;
2989260e9a87SYuri Pankov 				break;
2990260e9a87SYuri Pankov 			}
2991260e9a87SYuri Pankov 			*res %= operand2;
2992260e9a87SYuri Pankov 			break;
2993260e9a87SYuri Pankov 		case '<':
2994260e9a87SYuri Pankov 			*res = *res < operand2;
2995260e9a87SYuri Pankov 			break;
2996260e9a87SYuri Pankov 		case '>':
2997260e9a87SYuri Pankov 			*res = *res > operand2;
2998260e9a87SYuri Pankov 			break;
2999260e9a87SYuri Pankov 		case 'l':
3000260e9a87SYuri Pankov 			*res = *res <= operand2;
3001260e9a87SYuri Pankov 			break;
3002260e9a87SYuri Pankov 		case 'g':
3003260e9a87SYuri Pankov 			*res = *res >= operand2;
3004260e9a87SYuri Pankov 			break;
3005260e9a87SYuri Pankov 		case '=':
3006260e9a87SYuri Pankov 			*res = *res == operand2;
3007260e9a87SYuri Pankov 			break;
3008260e9a87SYuri Pankov 		case '!':
3009260e9a87SYuri Pankov 			*res = *res != operand2;
3010260e9a87SYuri Pankov 			break;
3011260e9a87SYuri Pankov 		case '&':
3012260e9a87SYuri Pankov 			*res = *res && operand2;
3013260e9a87SYuri Pankov 			break;
3014260e9a87SYuri Pankov 		case ':':
3015260e9a87SYuri Pankov 			*res = *res || operand2;
3016260e9a87SYuri Pankov 			break;
3017260e9a87SYuri Pankov 		case 'i':
3018260e9a87SYuri Pankov 			if (operand2 < *res)
3019260e9a87SYuri Pankov 				*res = operand2;
3020260e9a87SYuri Pankov 			break;
3021260e9a87SYuri Pankov 		case 'a':
3022260e9a87SYuri Pankov 			if (operand2 > *res)
3023260e9a87SYuri Pankov 				*res = operand2;
3024260e9a87SYuri Pankov 			break;
3025260e9a87SYuri Pankov 		default:
3026260e9a87SYuri Pankov 			abort();
3027260e9a87SYuri Pankov 		}
3028260e9a87SYuri Pankov 	}
3029371584c2SYuri Pankov 	return 1;
3030260e9a87SYuri Pankov }
3031260e9a87SYuri Pankov 
3032371584c2SYuri Pankov /* --- register management ------------------------------------------------ */
3033371584c2SYuri Pankov 
303495c635efSGarrett D'Amore void
roff_setreg(struct roff * r,const char * name,int val,char sign)3035698f87a4SGarrett D'Amore roff_setreg(struct roff *r, const char *name, int val, char sign)
303695c635efSGarrett D'Amore {
30376640c13bSYuri Pankov 	roff_setregn(r, name, strlen(name), val, sign, INT_MIN);
30386640c13bSYuri Pankov }
30396640c13bSYuri Pankov 
30406640c13bSYuri Pankov static void
roff_setregn(struct roff * r,const char * name,size_t len,int val,char sign,int step)30416640c13bSYuri Pankov roff_setregn(struct roff *r, const char *name, size_t len,
30426640c13bSYuri Pankov     int val, char sign, int step)
30436640c13bSYuri Pankov {
3044698f87a4SGarrett D'Amore 	struct roffreg	*reg;
304595c635efSGarrett D'Amore 
3046698f87a4SGarrett D'Amore 	/* Search for an existing register with the same name. */
3047698f87a4SGarrett D'Amore 	reg = r->regtab;
3048698f87a4SGarrett D'Amore 
30496640c13bSYuri Pankov 	while (reg != NULL && (reg->key.sz != len ||
30506640c13bSYuri Pankov 	    strncmp(reg->key.p, name, len) != 0))
3051698f87a4SGarrett D'Amore 		reg = reg->next;
3052698f87a4SGarrett D'Amore 
3053698f87a4SGarrett D'Amore 	if (NULL == reg) {
3054698f87a4SGarrett D'Amore 		/* Create a new register. */
3055698f87a4SGarrett D'Amore 		reg = mandoc_malloc(sizeof(struct roffreg));
30566640c13bSYuri Pankov 		reg->key.p = mandoc_strndup(name, len);
30576640c13bSYuri Pankov 		reg->key.sz = len;
3058698f87a4SGarrett D'Amore 		reg->val = 0;
30596640c13bSYuri Pankov 		reg->step = 0;
3060698f87a4SGarrett D'Amore 		reg->next = r->regtab;
3061698f87a4SGarrett D'Amore 		r->regtab = reg;
3062698f87a4SGarrett D'Amore 	}
3063698f87a4SGarrett D'Amore 
3064698f87a4SGarrett D'Amore 	if ('+' == sign)
3065698f87a4SGarrett D'Amore 		reg->val += val;
3066698f87a4SGarrett D'Amore 	else if ('-' == sign)
3067698f87a4SGarrett D'Amore 		reg->val -= val;
3068698f87a4SGarrett D'Amore 	else
3069698f87a4SGarrett D'Amore 		reg->val = val;
30706640c13bSYuri Pankov 	if (step != INT_MIN)
30716640c13bSYuri Pankov 		reg->step = step;
3072698f87a4SGarrett D'Amore }
3073698f87a4SGarrett D'Amore 
3074260e9a87SYuri Pankov /*
3075260e9a87SYuri Pankov  * Handle some predefined read-only number registers.
3076260e9a87SYuri Pankov  * For now, return -1 if the requested register is not predefined;
3077260e9a87SYuri Pankov  * in case a predefined read-only register having the value -1
3078260e9a87SYuri Pankov  * were to turn up, another special value would have to be chosen.
3079260e9a87SYuri Pankov  */
3080260e9a87SYuri Pankov static int
roff_getregro(const struct roff * r,const char * name)3081371584c2SYuri Pankov roff_getregro(const struct roff *r, const char *name)
3082260e9a87SYuri Pankov {
3083260e9a87SYuri Pankov 
3084260e9a87SYuri Pankov 	switch (*name) {
3085371584c2SYuri Pankov 	case '$':  /* Number of arguments of the last macro evaluated. */
3086cec8643bSMichal Nowak 		return r->mstackpos < 0 ? 0 : r->mstack[r->mstackpos].argc;
3087260e9a87SYuri Pankov 	case 'A':  /* ASCII approximation mode is always off. */
3088371584c2SYuri Pankov 		return 0;
3089260e9a87SYuri Pankov 	case 'g':  /* Groff compatibility mode is always on. */
3090371584c2SYuri Pankov 		return 1;
3091260e9a87SYuri Pankov 	case 'H':  /* Fixed horizontal resolution. */
3092371584c2SYuri Pankov 		return 24;
3093260e9a87SYuri Pankov 	case 'j':  /* Always adjust left margin only. */
3094371584c2SYuri Pankov 		return 0;
3095260e9a87SYuri Pankov 	case 'T':  /* Some output device is always defined. */
3096371584c2SYuri Pankov 		return 1;
3097260e9a87SYuri Pankov 	case 'V':  /* Fixed vertical resolution. */
3098371584c2SYuri Pankov 		return 40;
3099260e9a87SYuri Pankov 	default:
3100371584c2SYuri Pankov 		return -1;
3101260e9a87SYuri Pankov 	}
3102260e9a87SYuri Pankov }
3103260e9a87SYuri Pankov 
3104698f87a4SGarrett D'Amore int
roff_getreg(struct roff * r,const char * name)31056640c13bSYuri Pankov roff_getreg(struct roff *r, const char *name)
3106698f87a4SGarrett D'Amore {
31076640c13bSYuri Pankov 	return roff_getregn(r, name, strlen(name), '\0');
3108698f87a4SGarrett D'Amore }
3109698f87a4SGarrett D'Amore 
3110698f87a4SGarrett D'Amore static int
roff_getregn(struct roff * r,const char * name,size_t len,char sign)31116640c13bSYuri Pankov roff_getregn(struct roff *r, const char *name, size_t len, char sign)
3112698f87a4SGarrett D'Amore {
3113698f87a4SGarrett D'Amore 	struct roffreg	*reg;
3114260e9a87SYuri Pankov 	int		 val;
3115260e9a87SYuri Pankov 
3116260e9a87SYuri Pankov 	if ('.' == name[0] && 2 == len) {
3117371584c2SYuri Pankov 		val = roff_getregro(r, name + 1);
3118260e9a87SYuri Pankov 		if (-1 != val)
3119371584c2SYuri Pankov 			return val;
3120260e9a87SYuri Pankov 	}
3121698f87a4SGarrett D'Amore 
31226640c13bSYuri Pankov 	for (reg = r->regtab; reg; reg = reg->next) {
3123698f87a4SGarrett D'Amore 		if (len == reg->key.sz &&
31246640c13bSYuri Pankov 		    0 == strncmp(name, reg->key.p, len)) {
31256640c13bSYuri Pankov 			switch (sign) {
31266640c13bSYuri Pankov 			case '+':
31276640c13bSYuri Pankov 				reg->val += reg->step;
31286640c13bSYuri Pankov 				break;
31296640c13bSYuri Pankov 			case '-':
31306640c13bSYuri Pankov 				reg->val -= reg->step;
31316640c13bSYuri Pankov 				break;
31326640c13bSYuri Pankov 			default:
31336640c13bSYuri Pankov 				break;
31346640c13bSYuri Pankov 			}
3135371584c2SYuri Pankov 			return reg->val;
31366640c13bSYuri Pankov 		}
31376640c13bSYuri Pankov 	}
3138698f87a4SGarrett D'Amore 
31396640c13bSYuri Pankov 	roff_setregn(r, name, len, 0, '\0', INT_MIN);
3140371584c2SYuri Pankov 	return 0;
3141371584c2SYuri Pankov }
3142371584c2SYuri Pankov 
3143371584c2SYuri Pankov static int
roff_hasregn(const struct roff * r,const char * name,size_t len)3144371584c2SYuri Pankov roff_hasregn(const struct roff *r, const char *name, size_t len)
3145371584c2SYuri Pankov {
3146371584c2SYuri Pankov 	struct roffreg	*reg;
3147371584c2SYuri Pankov 	int		 val;
3148371584c2SYuri Pankov 
3149371584c2SYuri Pankov 	if ('.' == name[0] && 2 == len) {
3150371584c2SYuri Pankov 		val = roff_getregro(r, name + 1);
3151371584c2SYuri Pankov 		if (-1 != val)
3152371584c2SYuri Pankov 			return 1;
3153371584c2SYuri Pankov 	}
3154371584c2SYuri Pankov 
3155371584c2SYuri Pankov 	for (reg = r->regtab; reg; reg = reg->next)
3156371584c2SYuri Pankov 		if (len == reg->key.sz &&
3157371584c2SYuri Pankov 		    0 == strncmp(name, reg->key.p, len))
3158371584c2SYuri Pankov 			return 1;
3159371584c2SYuri Pankov 
3160371584c2SYuri Pankov 	return 0;
3161698f87a4SGarrett D'Amore }
3162698f87a4SGarrett D'Amore 
3163698f87a4SGarrett D'Amore static void
roff_freereg(struct roffreg * reg)3164698f87a4SGarrett D'Amore roff_freereg(struct roffreg *reg)
3165698f87a4SGarrett D'Amore {
3166698f87a4SGarrett D'Amore 	struct roffreg	*old_reg;
3167698f87a4SGarrett D'Amore 
3168698f87a4SGarrett D'Amore 	while (NULL != reg) {
3169698f87a4SGarrett D'Amore 		free(reg->key.p);
3170698f87a4SGarrett D'Amore 		old_reg = reg;
3171698f87a4SGarrett D'Amore 		reg = reg->next;
3172698f87a4SGarrett D'Amore 		free(old_reg);
3173698f87a4SGarrett D'Amore 	}
317495c635efSGarrett D'Amore }
317595c635efSGarrett D'Amore 
3176cec8643bSMichal Nowak static int
roff_nr(ROFF_ARGS)317795c635efSGarrett D'Amore roff_nr(ROFF_ARGS)
317895c635efSGarrett D'Amore {
31796640c13bSYuri Pankov 	char		*key, *val, *step;
3180260e9a87SYuri Pankov 	size_t		 keysz;
31816640c13bSYuri Pankov 	int		 iv, is, len;
3182698f87a4SGarrett D'Amore 	char		 sign;
318395c635efSGarrett D'Amore 
3184260e9a87SYuri Pankov 	key = val = buf->buf + pos;
3185260e9a87SYuri Pankov 	if (*key == '\0')
3186371584c2SYuri Pankov 		return ROFF_IGN;
3187260e9a87SYuri Pankov 
3188260e9a87SYuri Pankov 	keysz = roff_getname(r, &val, ln, pos);
3189cec8643bSMichal Nowak 	if (key[keysz] == '\\' || key[keysz] == '\t')
3190371584c2SYuri Pankov 		return ROFF_IGN;
319195c635efSGarrett D'Amore 
3192698f87a4SGarrett D'Amore 	sign = *val;
3193260e9a87SYuri Pankov 	if (sign == '+' || sign == '-')
3194698f87a4SGarrett D'Amore 		val++;
3195698f87a4SGarrett D'Amore 
31966640c13bSYuri Pankov 	len = 0;
31976640c13bSYuri Pankov 	if (roff_evalnum(r, ln, val, &len, &iv, ROFFNUM_SCALE) == 0)
31986640c13bSYuri Pankov 		return ROFF_IGN;
319995c635efSGarrett D'Amore 
32006640c13bSYuri Pankov 	step = val + len;
32016640c13bSYuri Pankov 	while (isspace((unsigned char)*step))
32026640c13bSYuri Pankov 		step++;
32036640c13bSYuri Pankov 	if (roff_evalnum(r, ln, step, NULL, &is, 0) == 0)
32046640c13bSYuri Pankov 		is = INT_MIN;
32056640c13bSYuri Pankov 
32066640c13bSYuri Pankov 	roff_setregn(r, key, keysz, iv, sign, is);
3207371584c2SYuri Pankov 	return ROFF_IGN;
320895c635efSGarrett D'Amore }
320995c635efSGarrett D'Amore 
3210cec8643bSMichal Nowak static int
roff_rr(ROFF_ARGS)3211260e9a87SYuri Pankov roff_rr(ROFF_ARGS)
3212260e9a87SYuri Pankov {
3213260e9a87SYuri Pankov 	struct roffreg	*reg, **prev;
3214260e9a87SYuri Pankov 	char		*name, *cp;
3215260e9a87SYuri Pankov 	size_t		 namesz;
3216260e9a87SYuri Pankov 
3217260e9a87SYuri Pankov 	name = cp = buf->buf + pos;
3218260e9a87SYuri Pankov 	if (*name == '\0')
3219371584c2SYuri Pankov 		return ROFF_IGN;
3220260e9a87SYuri Pankov 	namesz = roff_getname(r, &cp, ln, pos);
3221260e9a87SYuri Pankov 	name[namesz] = '\0';
3222260e9a87SYuri Pankov 
3223260e9a87SYuri Pankov 	prev = &r->regtab;
3224260e9a87SYuri Pankov 	while (1) {
3225260e9a87SYuri Pankov 		reg = *prev;
3226260e9a87SYuri Pankov 		if (reg == NULL || !strcmp(name, reg->key.p))
3227260e9a87SYuri Pankov 			break;
3228260e9a87SYuri Pankov 		prev = &reg->next;
3229260e9a87SYuri Pankov 	}
3230260e9a87SYuri Pankov 	if (reg != NULL) {
3231260e9a87SYuri Pankov 		*prev = reg->next;
3232260e9a87SYuri Pankov 		free(reg->key.p);
3233260e9a87SYuri Pankov 		free(reg);
3234260e9a87SYuri Pankov 	}
3235371584c2SYuri Pankov 	return ROFF_IGN;
3236260e9a87SYuri Pankov }
3237260e9a87SYuri Pankov 
3238371584c2SYuri Pankov /* --- handler functions for roff requests -------------------------------- */
3239371584c2SYuri Pankov 
3240cec8643bSMichal Nowak static int
roff_rm(ROFF_ARGS)324195c635efSGarrett D'Amore roff_rm(ROFF_ARGS)
324295c635efSGarrett D'Amore {
324395c635efSGarrett D'Amore 	const char	 *name;
324495c635efSGarrett D'Amore 	char		 *cp;
3245260e9a87SYuri Pankov 	size_t		  namesz;
324695c635efSGarrett D'Amore 
3247260e9a87SYuri Pankov 	cp = buf->buf + pos;
3248260e9a87SYuri Pankov 	while (*cp != '\0') {
3249260e9a87SYuri Pankov 		name = cp;
3250260e9a87SYuri Pankov 		namesz = roff_getname(r, &cp, ln, (int)(cp - buf->buf));
3251260e9a87SYuri Pankov 		roff_setstrn(&r->strtab, name, namesz, NULL, 0, 0);
3252c66b8046SYuri Pankov 		roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
3253cec8643bSMichal Nowak 		if (name[namesz] == '\\' || name[namesz] == '\t')
3254260e9a87SYuri Pankov 			break;
325595c635efSGarrett D'Amore 	}
3256371584c2SYuri Pankov 	return ROFF_IGN;
325795c635efSGarrett D'Amore }
325895c635efSGarrett D'Amore 
3259cec8643bSMichal Nowak static int
roff_it(ROFF_ARGS)3260698f87a4SGarrett D'Amore roff_it(ROFF_ARGS)
3261698f87a4SGarrett D'Amore {
3262698f87a4SGarrett D'Amore 	int		 iv;
3263698f87a4SGarrett D'Amore 
3264698f87a4SGarrett D'Amore 	/* Parse the number of lines. */
3265260e9a87SYuri Pankov 
3266260e9a87SYuri Pankov 	if ( ! roff_evalnum(r, ln, buf->buf, &pos, &iv, 0)) {
3267cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_IT_NONUM,
3268cec8643bSMichal Nowak 		    ln, ppos, "%s", buf->buf + 1);
3269371584c2SYuri Pankov 		return ROFF_IGN;
3270698f87a4SGarrett D'Amore 	}
3271698f87a4SGarrett D'Amore 
3272260e9a87SYuri Pankov 	while (isspace((unsigned char)buf->buf[pos]))
3273260e9a87SYuri Pankov 		pos++;
3274260e9a87SYuri Pankov 
3275260e9a87SYuri Pankov 	/*
3276260e9a87SYuri Pankov 	 * Arm the input line trap.
3277260e9a87SYuri Pankov 	 * Special-casing "an-trap" is an ugly workaround to cope
3278260e9a87SYuri Pankov 	 * with DocBook stupidly fiddling with man(7) internals.
3279260e9a87SYuri Pankov 	 */
3280260e9a87SYuri Pankov 
3281698f87a4SGarrett D'Amore 	roffit_lines = iv;
3282260e9a87SYuri Pankov 	roffit_macro = mandoc_strdup(iv != 1 ||
3283260e9a87SYuri Pankov 	    strcmp(buf->buf + pos, "an-trap") ?
3284260e9a87SYuri Pankov 	    buf->buf + pos : "br");
3285371584c2SYuri Pankov 	return ROFF_IGN;
3286698f87a4SGarrett D'Amore }
3287698f87a4SGarrett D'Amore 
3288cec8643bSMichal Nowak static int
roff_Dd(ROFF_ARGS)3289698f87a4SGarrett D'Amore roff_Dd(ROFF_ARGS)
3290698f87a4SGarrett D'Amore {
3291c66b8046SYuri Pankov 	int		 mask;
3292c66b8046SYuri Pankov 	enum roff_tok	 t, te;
3293698f87a4SGarrett D'Amore 
3294c66b8046SYuri Pankov 	switch (tok) {
3295c66b8046SYuri Pankov 	case ROFF_Dd:
3296c66b8046SYuri Pankov 		tok = MDOC_Dd;
3297c66b8046SYuri Pankov 		te = MDOC_MAX;
3298260e9a87SYuri Pankov 		if (r->format == 0)
3299260e9a87SYuri Pankov 			r->format = MPARSE_MDOC;
3300c66b8046SYuri Pankov 		mask = MPARSE_MDOC | MPARSE_QUICK;
3301c66b8046SYuri Pankov 		break;
3302c66b8046SYuri Pankov 	case ROFF_TH:
3303c66b8046SYuri Pankov 		tok = MAN_TH;
3304c66b8046SYuri Pankov 		te = MAN_MAX;
3305260e9a87SYuri Pankov 		if (r->format == 0)
3306260e9a87SYuri Pankov 			r->format = MPARSE_MAN;
3307c66b8046SYuri Pankov 		mask = MPARSE_QUICK;
3308c66b8046SYuri Pankov 		break;
3309c66b8046SYuri Pankov 	default:
3310c66b8046SYuri Pankov 		abort();
3311c66b8046SYuri Pankov 	}
3312c66b8046SYuri Pankov 	if ((r->options & mask) == 0)
3313c66b8046SYuri Pankov 		for (t = tok; t < te; t++)
3314c66b8046SYuri Pankov 			roff_setstr(r, roff_name[t], NULL, 0);
3315371584c2SYuri Pankov 	return ROFF_CONT;
3316698f87a4SGarrett D'Amore }
3317698f87a4SGarrett D'Amore 
3318cec8643bSMichal Nowak static int
roff_TE(ROFF_ARGS)331995c635efSGarrett D'Amore roff_TE(ROFF_ARGS)
332095c635efSGarrett D'Amore {
3321cec8643bSMichal Nowak 	r->man->flags &= ~ROFF_NONOFILL;
3322c66b8046SYuri Pankov 	if (r->tbl == NULL) {
3323cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "TE");
3324c66b8046SYuri Pankov 		return ROFF_IGN;
3325c66b8046SYuri Pankov 	}
3326cec8643bSMichal Nowak 	if (tbl_end(r->tbl, 0) == 0) {
3327c66b8046SYuri Pankov 		r->tbl = NULL;
3328260e9a87SYuri Pankov 		free(buf->buf);
3329260e9a87SYuri Pankov 		buf->buf = mandoc_strdup(".sp");
3330260e9a87SYuri Pankov 		buf->sz = 4;
33316640c13bSYuri Pankov 		*offs = 0;
3332371584c2SYuri Pankov 		return ROFF_REPARSE;
3333260e9a87SYuri Pankov 	}
3334c66b8046SYuri Pankov 	r->tbl = NULL;
3335371584c2SYuri Pankov 	return ROFF_IGN;
333695c635efSGarrett D'Amore }
333795c635efSGarrett D'Amore 
3338cec8643bSMichal Nowak static int
roff_T_(ROFF_ARGS)333995c635efSGarrett D'Amore roff_T_(ROFF_ARGS)
334095c635efSGarrett D'Amore {
334195c635efSGarrett D'Amore 
334295c635efSGarrett D'Amore 	if (NULL == r->tbl)
3343cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "T&");
334495c635efSGarrett D'Amore 	else
3345c66b8046SYuri Pankov 		tbl_restart(ln, ppos, r->tbl);
334695c635efSGarrett D'Amore 
3347371584c2SYuri Pankov 	return ROFF_IGN;
334895c635efSGarrett D'Amore }
334995c635efSGarrett D'Amore 
3350260e9a87SYuri Pankov /*
3351260e9a87SYuri Pankov  * Handle in-line equation delimiters.
3352260e9a87SYuri Pankov  */
3353cec8643bSMichal Nowak static int
roff_eqndelim(struct roff * r,struct buf * buf,int pos)3354260e9a87SYuri Pankov roff_eqndelim(struct roff *r, struct buf *buf, int pos)
335595c635efSGarrett D'Amore {
3356260e9a87SYuri Pankov 	char		*cp1, *cp2;
3357260e9a87SYuri Pankov 	const char	*bef_pr, *bef_nl, *mac, *aft_nl, *aft_pr;
335895c635efSGarrett D'Amore 
3359260e9a87SYuri Pankov 	/*
3360260e9a87SYuri Pankov 	 * Outside equations, look for an opening delimiter.
3361260e9a87SYuri Pankov 	 * If we are inside an equation, we already know it is
3362260e9a87SYuri Pankov 	 * in-line, or this function wouldn't have been called;
3363260e9a87SYuri Pankov 	 * so look for a closing delimiter.
3364260e9a87SYuri Pankov 	 */
3365260e9a87SYuri Pankov 
3366260e9a87SYuri Pankov 	cp1 = buf->buf + pos;
3367260e9a87SYuri Pankov 	cp2 = strchr(cp1, r->eqn == NULL ?
3368260e9a87SYuri Pankov 	    r->last_eqn->odelim : r->last_eqn->cdelim);
3369260e9a87SYuri Pankov 	if (cp2 == NULL)
3370371584c2SYuri Pankov 		return ROFF_CONT;
3371260e9a87SYuri Pankov 
3372260e9a87SYuri Pankov 	*cp2++ = '\0';
3373260e9a87SYuri Pankov 	bef_pr = bef_nl = aft_nl = aft_pr = "";
3374260e9a87SYuri Pankov 
3375260e9a87SYuri Pankov 	/* Handle preceding text, protecting whitespace. */
3376260e9a87SYuri Pankov 
3377260e9a87SYuri Pankov 	if (*buf->buf != '\0') {
3378260e9a87SYuri Pankov 		if (r->eqn == NULL)
3379260e9a87SYuri Pankov 			bef_pr = "\\&";
3380260e9a87SYuri Pankov 		bef_nl = "\n";
338195c635efSGarrett D'Amore 	}
338295c635efSGarrett D'Amore 
3383260e9a87SYuri Pankov 	/*
3384260e9a87SYuri Pankov 	 * Prepare replacing the delimiter with an equation macro
3385260e9a87SYuri Pankov 	 * and drop leading white space from the equation.
3386260e9a87SYuri Pankov 	 */
3387260e9a87SYuri Pankov 
3388260e9a87SYuri Pankov 	if (r->eqn == NULL) {
3389260e9a87SYuri Pankov 		while (*cp2 == ' ')
3390260e9a87SYuri Pankov 			cp2++;
3391260e9a87SYuri Pankov 		mac = ".EQ";
3392260e9a87SYuri Pankov 	} else
3393260e9a87SYuri Pankov 		mac = ".EN";
3394260e9a87SYuri Pankov 
3395260e9a87SYuri Pankov 	/* Handle following text, protecting whitespace. */
3396260e9a87SYuri Pankov 
3397260e9a87SYuri Pankov 	if (*cp2 != '\0') {
3398260e9a87SYuri Pankov 		aft_nl = "\n";
3399260e9a87SYuri Pankov 		if (r->eqn != NULL)
3400260e9a87SYuri Pankov 			aft_pr = "\\&";
3401260e9a87SYuri Pankov 	}
3402260e9a87SYuri Pankov 
3403260e9a87SYuri Pankov 	/* Do the actual replacement. */
3404260e9a87SYuri Pankov 
3405260e9a87SYuri Pankov 	buf->sz = mandoc_asprintf(&cp1, "%s%s%s%s%s%s%s", buf->buf,
3406260e9a87SYuri Pankov 	    bef_pr, bef_nl, mac, aft_nl, aft_pr, cp2) + 1;
3407260e9a87SYuri Pankov 	free(buf->buf);
3408260e9a87SYuri Pankov 	buf->buf = cp1;
3409260e9a87SYuri Pankov 
3410260e9a87SYuri Pankov 	/* Toggle the in-line state of the eqn subsystem. */
3411260e9a87SYuri Pankov 
3412260e9a87SYuri Pankov 	r->eqn_inline = r->eqn == NULL;
3413371584c2SYuri Pankov 	return ROFF_REPARSE;
3414260e9a87SYuri Pankov }
3415260e9a87SYuri Pankov 
3416cec8643bSMichal Nowak static int
roff_EQ(ROFF_ARGS)3417260e9a87SYuri Pankov roff_EQ(ROFF_ARGS)
341895c635efSGarrett D'Amore {
3419c66b8046SYuri Pankov 	struct roff_node	*n;
3420c66b8046SYuri Pankov 
3421cec8643bSMichal Nowak 	if (r->man->meta.macroset == MACROSET_MAN)
3422c66b8046SYuri Pankov 		man_breakscope(r->man, ROFF_EQ);
3423c66b8046SYuri Pankov 	n = roff_node_alloc(r->man, ln, ppos, ROFFT_EQN, TOKEN_NONE);
3424c66b8046SYuri Pankov 	if (ln > r->man->last->line)
3425c66b8046SYuri Pankov 		n->flags |= NODE_LINE;
3426cec8643bSMichal Nowak 	n->eqn = eqn_box_new();
3427c66b8046SYuri Pankov 	roff_node_append(r->man, n);
3428c66b8046SYuri Pankov 	r->man->next = ROFF_NEXT_SIBLING;
342995c635efSGarrett D'Amore 
3430260e9a87SYuri Pankov 	assert(r->eqn == NULL);
3431c66b8046SYuri Pankov 	if (r->last_eqn == NULL)
3432cec8643bSMichal Nowak 		r->last_eqn = eqn_alloc();
3433c66b8046SYuri Pankov 	else
3434c66b8046SYuri Pankov 		eqn_reset(r->last_eqn);
3435c66b8046SYuri Pankov 	r->eqn = r->last_eqn;
3436c66b8046SYuri Pankov 	r->eqn->node = n;
343795c635efSGarrett D'Amore 
3438260e9a87SYuri Pankov 	if (buf->buf[pos] != '\0')
3439cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
3440260e9a87SYuri Pankov 		    ".EQ %s", buf->buf + pos);
344195c635efSGarrett D'Amore 
3442371584c2SYuri Pankov 	return ROFF_IGN;
344395c635efSGarrett D'Amore }
344495c635efSGarrett D'Amore 
3445cec8643bSMichal Nowak static int
roff_EN(ROFF_ARGS)344695c635efSGarrett D'Amore roff_EN(ROFF_ARGS)
344795c635efSGarrett D'Amore {
3448c66b8046SYuri Pankov 	if (r->eqn != NULL) {
3449c66b8046SYuri Pankov 		eqn_parse(r->eqn);
3450c66b8046SYuri Pankov 		r->eqn = NULL;
3451c66b8046SYuri Pankov 	} else
3452cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "EN");
3453c66b8046SYuri Pankov 	if (buf->buf[pos] != '\0')
3454cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
3455c66b8046SYuri Pankov 		    "EN %s", buf->buf + pos);
3456371584c2SYuri Pankov 	return ROFF_IGN;
345795c635efSGarrett D'Amore }
345895c635efSGarrett D'Amore 
3459cec8643bSMichal Nowak static int
roff_TS(ROFF_ARGS)346095c635efSGarrett D'Amore roff_TS(ROFF_ARGS)
346195c635efSGarrett D'Amore {
3462c66b8046SYuri Pankov 	if (r->tbl != NULL) {
3463cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BLK_BROKEN, ln, ppos, "TS breaks TS");
3464cec8643bSMichal Nowak 		tbl_end(r->tbl, 0);
346595c635efSGarrett D'Amore 	}
3466cec8643bSMichal Nowak 	r->man->flags |= ROFF_NONOFILL;
3467cec8643bSMichal Nowak 	r->tbl = tbl_alloc(ppos, ln, r->last_tbl);
3468cec8643bSMichal Nowak 	if (r->last_tbl == NULL)
3469c66b8046SYuri Pankov 		r->first_tbl = r->tbl;
3470c66b8046SYuri Pankov 	r->last_tbl = r->tbl;
3471371584c2SYuri Pankov 	return ROFF_IGN;
3472698f87a4SGarrett D'Amore }
3473698f87a4SGarrett D'Amore 
3474cec8643bSMichal Nowak static int
roff_noarg(ROFF_ARGS)3475cec8643bSMichal Nowak roff_noarg(ROFF_ARGS)
3476cec8643bSMichal Nowak {
3477cec8643bSMichal Nowak 	if (r->man->flags & (MAN_BLINE | MAN_ELINE))
3478cec8643bSMichal Nowak 		man_breakscope(r->man, tok);
3479cec8643bSMichal Nowak 	if (tok == ROFF_brp)
3480cec8643bSMichal Nowak 		tok = ROFF_br;
3481cec8643bSMichal Nowak 	roff_elem_alloc(r->man, ln, ppos, tok);
3482cec8643bSMichal Nowak 	if (buf->buf[pos] != '\0')
3483cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
3484cec8643bSMichal Nowak 		   "%s %s", roff_name[tok], buf->buf + pos);
3485cec8643bSMichal Nowak 	if (tok == ROFF_nf)
3486cec8643bSMichal Nowak 		r->man->flags |= ROFF_NOFILL;
3487cec8643bSMichal Nowak 	else if (tok == ROFF_fi)
3488cec8643bSMichal Nowak 		r->man->flags &= ~ROFF_NOFILL;
3489cec8643bSMichal Nowak 	r->man->last->flags |= NODE_LINE | NODE_VALID | NODE_ENDED;
3490cec8643bSMichal Nowak 	r->man->next = ROFF_NEXT_SIBLING;
3491cec8643bSMichal Nowak 	return ROFF_IGN;
3492cec8643bSMichal Nowak }
3493cec8643bSMichal Nowak 
3494cec8643bSMichal Nowak static int
roff_onearg(ROFF_ARGS)3495c66b8046SYuri Pankov roff_onearg(ROFF_ARGS)
3496260e9a87SYuri Pankov {
3497c66b8046SYuri Pankov 	struct roff_node	*n;
3498c66b8046SYuri Pankov 	char			*cp;
3499c66b8046SYuri Pankov 	int			 npos;
3500260e9a87SYuri Pankov 
3501c66b8046SYuri Pankov 	if (r->man->flags & (MAN_BLINE | MAN_ELINE) &&
3502c66b8046SYuri Pankov 	    (tok == ROFF_ce || tok == ROFF_rj || tok == ROFF_sp ||
3503c66b8046SYuri Pankov 	     tok == ROFF_ti))
3504c66b8046SYuri Pankov 		man_breakscope(r->man, tok);
3505c66b8046SYuri Pankov 
3506c66b8046SYuri Pankov 	if (roffce_node != NULL && (tok == ROFF_ce || tok == ROFF_rj)) {
3507c66b8046SYuri Pankov 		r->man->last = roffce_node;
3508c66b8046SYuri Pankov 		r->man->next = ROFF_NEXT_SIBLING;
3509c66b8046SYuri Pankov 	}
3510c66b8046SYuri Pankov 
3511c66b8046SYuri Pankov 	roff_elem_alloc(r->man, ln, ppos, tok);
3512c66b8046SYuri Pankov 	n = r->man->last;
3513c66b8046SYuri Pankov 
3514c66b8046SYuri Pankov 	cp = buf->buf + pos;
3515c66b8046SYuri Pankov 	if (*cp != '\0') {
3516c66b8046SYuri Pankov 		while (*cp != '\0' && *cp != ' ')
3517c66b8046SYuri Pankov 			cp++;
3518c66b8046SYuri Pankov 		while (*cp == ' ')
3519c66b8046SYuri Pankov 			*cp++ = '\0';
3520c66b8046SYuri Pankov 		if (*cp != '\0')
3521cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_ARG_EXCESS,
3522cec8643bSMichal Nowak 			    ln, (int)(cp - buf->buf),
3523c66b8046SYuri Pankov 			    "%s ... %s", roff_name[tok], cp);
3524c66b8046SYuri Pankov 		roff_word_alloc(r->man, ln, pos, buf->buf + pos);
3525c66b8046SYuri Pankov 	}
3526c66b8046SYuri Pankov 
3527c66b8046SYuri Pankov 	if (tok == ROFF_ce || tok == ROFF_rj) {
3528c66b8046SYuri Pankov 		if (r->man->last->type == ROFFT_ELEM) {
3529c66b8046SYuri Pankov 			roff_word_alloc(r->man, ln, pos, "1");
3530c66b8046SYuri Pankov 			r->man->last->flags |= NODE_NOSRC;
3531c66b8046SYuri Pankov 		}
3532c66b8046SYuri Pankov 		npos = 0;
3533c66b8046SYuri Pankov 		if (roff_evalnum(r, ln, r->man->last->string, &npos,
3534c66b8046SYuri Pankov 		    &roffce_lines, 0) == 0) {
3535cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_CE_NONUM,
3536cec8643bSMichal Nowak 			    ln, pos, "ce %s", buf->buf + pos);
3537c66b8046SYuri Pankov 			roffce_lines = 1;
3538c66b8046SYuri Pankov 		}
3539c66b8046SYuri Pankov 		if (roffce_lines < 1) {
3540c66b8046SYuri Pankov 			r->man->last = r->man->last->parent;
3541c66b8046SYuri Pankov 			roffce_node = NULL;
3542c66b8046SYuri Pankov 			roffce_lines = 0;
3543c66b8046SYuri Pankov 		} else
3544c66b8046SYuri Pankov 			roffce_node = r->man->last->parent;
3545c66b8046SYuri Pankov 	} else {
3546c66b8046SYuri Pankov 		n->flags |= NODE_VALID | NODE_ENDED;
3547c66b8046SYuri Pankov 		r->man->last = n;
3548c66b8046SYuri Pankov 	}
3549c66b8046SYuri Pankov 	n->flags |= NODE_LINE;
3550c66b8046SYuri Pankov 	r->man->next = ROFF_NEXT_SIBLING;
3551c66b8046SYuri Pankov 	return ROFF_IGN;
3552c66b8046SYuri Pankov }
3553c66b8046SYuri Pankov 
3554cec8643bSMichal Nowak static int
roff_manyarg(ROFF_ARGS)3555c66b8046SYuri Pankov roff_manyarg(ROFF_ARGS)
3556c66b8046SYuri Pankov {
3557c66b8046SYuri Pankov 	struct roff_node	*n;
3558c66b8046SYuri Pankov 	char			*sp, *ep;
3559c66b8046SYuri Pankov 
3560c66b8046SYuri Pankov 	roff_elem_alloc(r->man, ln, ppos, tok);
3561c66b8046SYuri Pankov 	n = r->man->last;
3562c66b8046SYuri Pankov 
3563c66b8046SYuri Pankov 	for (sp = ep = buf->buf + pos; *sp != '\0'; sp = ep) {
3564c66b8046SYuri Pankov 		while (*ep != '\0' && *ep != ' ')
3565c66b8046SYuri Pankov 			ep++;
3566c66b8046SYuri Pankov 		while (*ep == ' ')
3567c66b8046SYuri Pankov 			*ep++ = '\0';
3568c66b8046SYuri Pankov 		roff_word_alloc(r->man, ln, sp - buf->buf, sp);
3569c66b8046SYuri Pankov 	}
3570c66b8046SYuri Pankov 
3571c66b8046SYuri Pankov 	n->flags |= NODE_LINE | NODE_VALID | NODE_ENDED;
3572c66b8046SYuri Pankov 	r->man->last = n;
3573c66b8046SYuri Pankov 	r->man->next = ROFF_NEXT_SIBLING;
3574c66b8046SYuri Pankov 	return ROFF_IGN;
3575c66b8046SYuri Pankov }
3576c66b8046SYuri Pankov 
3577cec8643bSMichal Nowak static int
roff_als(ROFF_ARGS)3578c66b8046SYuri Pankov roff_als(ROFF_ARGS)
3579c66b8046SYuri Pankov {
3580c66b8046SYuri Pankov 	char		*oldn, *newn, *end, *value;
3581c66b8046SYuri Pankov 	size_t		 oldsz, newsz, valsz;
3582c66b8046SYuri Pankov 
3583c66b8046SYuri Pankov 	newn = oldn = buf->buf + pos;
3584c66b8046SYuri Pankov 	if (*newn == '\0')
3585c66b8046SYuri Pankov 		return ROFF_IGN;
3586c66b8046SYuri Pankov 
3587c66b8046SYuri Pankov 	newsz = roff_getname(r, &oldn, ln, pos);
3588cec8643bSMichal Nowak 	if (newn[newsz] == '\\' || newn[newsz] == '\t' || *oldn == '\0')
3589c66b8046SYuri Pankov 		return ROFF_IGN;
3590c66b8046SYuri Pankov 
3591c66b8046SYuri Pankov 	end = oldn;
3592c66b8046SYuri Pankov 	oldsz = roff_getname(r, &end, ln, oldn - buf->buf);
3593c66b8046SYuri Pankov 	if (oldsz == 0)
3594c66b8046SYuri Pankov 		return ROFF_IGN;
3595c66b8046SYuri Pankov 
3596cec8643bSMichal Nowak 	valsz = mandoc_asprintf(&value, ".%.*s \\$@\\\"\n",
3597c66b8046SYuri Pankov 	    (int)oldsz, oldn);
3598c66b8046SYuri Pankov 	roff_setstrn(&r->strtab, newn, newsz, value, valsz, 0);
3599c66b8046SYuri Pankov 	roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
3600c66b8046SYuri Pankov 	free(value);
3601c66b8046SYuri Pankov 	return ROFF_IGN;
3602c66b8046SYuri Pankov }
3603c66b8046SYuri Pankov 
3604*4d131170SRobert Mustacchi /*
3605*4d131170SRobert Mustacchi  * The .break request only makes sense inside conditionals,
3606*4d131170SRobert Mustacchi  * and that case is already handled in roff_cond_sub().
3607*4d131170SRobert Mustacchi  */
3608*4d131170SRobert Mustacchi static int
roff_break(ROFF_ARGS)3609*4d131170SRobert Mustacchi roff_break(ROFF_ARGS)
3610*4d131170SRobert Mustacchi {
3611*4d131170SRobert Mustacchi 	mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, pos, "break");
3612*4d131170SRobert Mustacchi 	return ROFF_IGN;
3613*4d131170SRobert Mustacchi }
3614*4d131170SRobert Mustacchi 
3615cec8643bSMichal Nowak static int
roff_cc(ROFF_ARGS)3616698f87a4SGarrett D'Amore roff_cc(ROFF_ARGS)
3617698f87a4SGarrett D'Amore {
3618698f87a4SGarrett D'Amore 	const char	*p;
3619698f87a4SGarrett D'Amore 
3620260e9a87SYuri Pankov 	p = buf->buf + pos;
3621698f87a4SGarrett D'Amore 
3622260e9a87SYuri Pankov 	if (*p == '\0' || (r->control = *p++) == '.')
3623c66b8046SYuri Pankov 		r->control = '\0';
3624698f87a4SGarrett D'Amore 
3625260e9a87SYuri Pankov 	if (*p != '\0')
3626cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_ARG_EXCESS,
3627260e9a87SYuri Pankov 		    ln, p - buf->buf, "cc ... %s", p);
3628698f87a4SGarrett D'Amore 
3629371584c2SYuri Pankov 	return ROFF_IGN;
363095c635efSGarrett D'Amore }
363195c635efSGarrett D'Amore 
3632cec8643bSMichal Nowak static int
roff_char(ROFF_ARGS)3633cec8643bSMichal Nowak roff_char(ROFF_ARGS)
3634cec8643bSMichal Nowak {
3635cec8643bSMichal Nowak 	const char	*p, *kp, *vp;
3636cec8643bSMichal Nowak 	size_t		 ksz, vsz;
3637cec8643bSMichal Nowak 	int		 font;
3638cec8643bSMichal Nowak 
3639cec8643bSMichal Nowak 	/* Parse the character to be replaced. */
3640cec8643bSMichal Nowak 
3641cec8643bSMichal Nowak 	kp = buf->buf + pos;
3642cec8643bSMichal Nowak 	p = kp + 1;
3643cec8643bSMichal Nowak 	if (*kp == '\0' || (*kp == '\\' &&
3644cec8643bSMichal Nowak 	     mandoc_escape(&p, NULL, NULL) != ESCAPE_SPECIAL) ||
3645cec8643bSMichal Nowak 	    (*p != ' ' && *p != '\0')) {
3646cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_CHAR_ARG, ln, pos, "char %s", kp);
3647cec8643bSMichal Nowak 		return ROFF_IGN;
3648cec8643bSMichal Nowak 	}
3649cec8643bSMichal Nowak 	ksz = p - kp;
3650cec8643bSMichal Nowak 	while (*p == ' ')
3651cec8643bSMichal Nowak 		p++;
3652cec8643bSMichal Nowak 
3653cec8643bSMichal Nowak 	/*
3654cec8643bSMichal Nowak 	 * If the replacement string contains a font escape sequence,
3655cec8643bSMichal Nowak 	 * we have to restore the font at the end.
3656cec8643bSMichal Nowak 	 */
3657cec8643bSMichal Nowak 
3658cec8643bSMichal Nowak 	vp = p;
3659cec8643bSMichal Nowak 	vsz = strlen(p);
3660cec8643bSMichal Nowak 	font = 0;
3661cec8643bSMichal Nowak 	while (*p != '\0') {
3662cec8643bSMichal Nowak 		if (*p++ != '\\')
3663cec8643bSMichal Nowak 			continue;
3664cec8643bSMichal Nowak 		switch (mandoc_escape(&p, NULL, NULL)) {
3665cec8643bSMichal Nowak 		case ESCAPE_FONT:
3666cec8643bSMichal Nowak 		case ESCAPE_FONTROMAN:
3667cec8643bSMichal Nowak 		case ESCAPE_FONTITALIC:
3668cec8643bSMichal Nowak 		case ESCAPE_FONTBOLD:
3669cec8643bSMichal Nowak 		case ESCAPE_FONTBI:
3670*4d131170SRobert Mustacchi 		case ESCAPE_FONTCR:
3671*4d131170SRobert Mustacchi 		case ESCAPE_FONTCB:
3672*4d131170SRobert Mustacchi 		case ESCAPE_FONTCI:
3673cec8643bSMichal Nowak 		case ESCAPE_FONTPREV:
3674cec8643bSMichal Nowak 			font++;
3675cec8643bSMichal Nowak 			break;
3676cec8643bSMichal Nowak 		default:
3677cec8643bSMichal Nowak 			break;
3678cec8643bSMichal Nowak 		}
3679cec8643bSMichal Nowak 	}
3680cec8643bSMichal Nowak 	if (font > 1)
3681cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_CHAR_FONT,
3682cec8643bSMichal Nowak 		    ln, (int)(vp - buf->buf), "%s", vp);
3683cec8643bSMichal Nowak 
3684cec8643bSMichal Nowak 	/*
3685cec8643bSMichal Nowak 	 * Approximate the effect of .char using the .tr tables.
3686cec8643bSMichal Nowak 	 * XXX In groff, .char and .tr interact differently.
3687cec8643bSMichal Nowak 	 */
3688cec8643bSMichal Nowak 
3689cec8643bSMichal Nowak 	if (ksz == 1) {
3690cec8643bSMichal Nowak 		if (r->xtab == NULL)
3691cec8643bSMichal Nowak 			r->xtab = mandoc_calloc(128, sizeof(*r->xtab));
3692cec8643bSMichal Nowak 		assert((unsigned int)*kp < 128);
3693cec8643bSMichal Nowak 		free(r->xtab[(int)*kp].p);
3694cec8643bSMichal Nowak 		r->xtab[(int)*kp].sz = mandoc_asprintf(&r->xtab[(int)*kp].p,
3695cec8643bSMichal Nowak 		    "%s%s", vp, font ? "\fP" : "");
3696cec8643bSMichal Nowak 	} else {
3697cec8643bSMichal Nowak 		roff_setstrn(&r->xmbtab, kp, ksz, vp, vsz, 0);
3698cec8643bSMichal Nowak 		if (font)
3699cec8643bSMichal Nowak 			roff_setstrn(&r->xmbtab, kp, ksz, "\\fP", 3, 1);
3700cec8643bSMichal Nowak 	}
3701cec8643bSMichal Nowak 	return ROFF_IGN;
3702cec8643bSMichal Nowak }
3703cec8643bSMichal Nowak 
3704cec8643bSMichal Nowak static int
roff_ec(ROFF_ARGS)3705c66b8046SYuri Pankov roff_ec(ROFF_ARGS)
3706c66b8046SYuri Pankov {
3707c66b8046SYuri Pankov 	const char	*p;
3708c66b8046SYuri Pankov 
3709c66b8046SYuri Pankov 	p = buf->buf + pos;
3710c66b8046SYuri Pankov 	if (*p == '\0')
3711c66b8046SYuri Pankov 		r->escape = '\\';
3712c66b8046SYuri Pankov 	else {
3713c66b8046SYuri Pankov 		r->escape = *p;
3714c66b8046SYuri Pankov 		if (*++p != '\0')
3715cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_ARG_EXCESS, ln,
3716cec8643bSMichal Nowak 			    (int)(p - buf->buf), "ec ... %s", p);
3717c66b8046SYuri Pankov 	}
3718c66b8046SYuri Pankov 	return ROFF_IGN;
3719c66b8046SYuri Pankov }
3720c66b8046SYuri Pankov 
3721cec8643bSMichal Nowak static int
roff_eo(ROFF_ARGS)3722c66b8046SYuri Pankov roff_eo(ROFF_ARGS)
3723c66b8046SYuri Pankov {
3724c66b8046SYuri Pankov 	r->escape = '\0';
3725c66b8046SYuri Pankov 	if (buf->buf[pos] != '\0')
3726cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_ARG_SKIP,
3727c66b8046SYuri Pankov 		    ln, pos, "eo %s", buf->buf + pos);
3728c66b8046SYuri Pankov 	return ROFF_IGN;
3729c66b8046SYuri Pankov }
3730c66b8046SYuri Pankov 
3731cec8643bSMichal Nowak static int
roff_nop(ROFF_ARGS)3732cec8643bSMichal Nowak roff_nop(ROFF_ARGS)
3733cec8643bSMichal Nowak {
3734cec8643bSMichal Nowak 	while (buf->buf[pos] == ' ')
3735cec8643bSMichal Nowak 		pos++;
3736cec8643bSMichal Nowak 	*offs = pos;
3737cec8643bSMichal Nowak 	return ROFF_RERUN;
3738cec8643bSMichal Nowak }
3739cec8643bSMichal Nowak 
3740cec8643bSMichal Nowak static int
roff_tr(ROFF_ARGS)374195c635efSGarrett D'Amore roff_tr(ROFF_ARGS)
374295c635efSGarrett D'Amore {
374395c635efSGarrett D'Amore 	const char	*p, *first, *second;
374495c635efSGarrett D'Amore 	size_t		 fsz, ssz;
374595c635efSGarrett D'Amore 	enum mandoc_esc	 esc;
374695c635efSGarrett D'Amore 
3747260e9a87SYuri Pankov 	p = buf->buf + pos;
374895c635efSGarrett D'Amore 
3749260e9a87SYuri Pankov 	if (*p == '\0') {
3750cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_REQ_EMPTY, ln, ppos, "tr");
3751371584c2SYuri Pankov 		return ROFF_IGN;
375295c635efSGarrett D'Amore 	}
375395c635efSGarrett D'Amore 
3754260e9a87SYuri Pankov 	while (*p != '\0') {
375595c635efSGarrett D'Amore 		fsz = ssz = 1;
375695c635efSGarrett D'Amore 
375795c635efSGarrett D'Amore 		first = p++;
3758260e9a87SYuri Pankov 		if (*first == '\\') {
375995c635efSGarrett D'Amore 			esc = mandoc_escape(&p, NULL, NULL);
3760260e9a87SYuri Pankov 			if (esc == ESCAPE_ERROR) {
3761cec8643bSMichal Nowak 				mandoc_msg(MANDOCERR_ESC_BAD, ln,
3762cec8643bSMichal Nowak 				    (int)(p - buf->buf), "%s", first);
3763371584c2SYuri Pankov 				return ROFF_IGN;
376495c635efSGarrett D'Amore 			}
376595c635efSGarrett D'Amore 			fsz = (size_t)(p - first);
376695c635efSGarrett D'Amore 		}
376795c635efSGarrett D'Amore 
376895c635efSGarrett D'Amore 		second = p++;
3769260e9a87SYuri Pankov 		if (*second == '\\') {
377095c635efSGarrett D'Amore 			esc = mandoc_escape(&p, NULL, NULL);
3771260e9a87SYuri Pankov 			if (esc == ESCAPE_ERROR) {
3772cec8643bSMichal Nowak 				mandoc_msg(MANDOCERR_ESC_BAD, ln,
3773cec8643bSMichal Nowak 				    (int)(p - buf->buf), "%s", second);
3774371584c2SYuri Pankov 				return ROFF_IGN;
377595c635efSGarrett D'Amore 			}
377695c635efSGarrett D'Amore 			ssz = (size_t)(p - second);
3777260e9a87SYuri Pankov 		} else if (*second == '\0') {
3778cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_TR_ODD, ln,
3779cec8643bSMichal Nowak 			    (int)(first - buf->buf), "tr %s", first);
378095c635efSGarrett D'Amore 			second = " ";
378195c635efSGarrett D'Amore 			p--;
378295c635efSGarrett D'Amore 		}
378395c635efSGarrett D'Amore 
378495c635efSGarrett D'Amore 		if (fsz > 1) {
3785260e9a87SYuri Pankov 			roff_setstrn(&r->xmbtab, first, fsz,
3786260e9a87SYuri Pankov 			    second, ssz, 0);
378795c635efSGarrett D'Amore 			continue;
378895c635efSGarrett D'Amore 		}
378995c635efSGarrett D'Amore 
3790260e9a87SYuri Pankov 		if (r->xtab == NULL)
3791260e9a87SYuri Pankov 			r->xtab = mandoc_calloc(128,
3792260e9a87SYuri Pankov 			    sizeof(struct roffstr));
379395c635efSGarrett D'Amore 
379495c635efSGarrett D'Amore 		free(r->xtab[(int)*first].p);
379595c635efSGarrett D'Amore 		r->xtab[(int)*first].p = mandoc_strndup(second, ssz);
379695c635efSGarrett D'Amore 		r->xtab[(int)*first].sz = ssz;
379795c635efSGarrett D'Amore 	}
379895c635efSGarrett D'Amore 
3799371584c2SYuri Pankov 	return ROFF_IGN;
380095c635efSGarrett D'Amore }
380195c635efSGarrett D'Amore 
3802cec8643bSMichal Nowak /*
3803cec8643bSMichal Nowak  * Implementation of the .return request.
3804cec8643bSMichal Nowak  * There is no need to call roff_userret() from here.
3805cec8643bSMichal Nowak  * The read module will call that after rewinding the reader stack
3806cec8643bSMichal Nowak  * to the place from where the current macro was called.
3807cec8643bSMichal Nowak  */
3808cec8643bSMichal Nowak static int
roff_return(ROFF_ARGS)3809cec8643bSMichal Nowak roff_return(ROFF_ARGS)
3810cec8643bSMichal Nowak {
3811cec8643bSMichal Nowak 	if (r->mstackpos >= 0)
3812cec8643bSMichal Nowak 		return ROFF_IGN | ROFF_USERRET;
3813cec8643bSMichal Nowak 
3814cec8643bSMichal Nowak 	mandoc_msg(MANDOCERR_REQ_NOMAC, ln, ppos, "return");
3815cec8643bSMichal Nowak 	return ROFF_IGN;
3816cec8643bSMichal Nowak }
3817cec8643bSMichal Nowak 
3818cec8643bSMichal Nowak static int
roff_rn(ROFF_ARGS)3819c66b8046SYuri Pankov roff_rn(ROFF_ARGS)
3820c66b8046SYuri Pankov {
3821c66b8046SYuri Pankov 	const char	*value;
3822c66b8046SYuri Pankov 	char		*oldn, *newn, *end;
3823c66b8046SYuri Pankov 	size_t		 oldsz, newsz;
3824c66b8046SYuri Pankov 	int		 deftype;
3825c66b8046SYuri Pankov 
3826c66b8046SYuri Pankov 	oldn = newn = buf->buf + pos;
3827c66b8046SYuri Pankov 	if (*oldn == '\0')
3828c66b8046SYuri Pankov 		return ROFF_IGN;
3829c66b8046SYuri Pankov 
3830c66b8046SYuri Pankov 	oldsz = roff_getname(r, &newn, ln, pos);
3831cec8643bSMichal Nowak 	if (oldn[oldsz] == '\\' || oldn[oldsz] == '\t' || *newn == '\0')
3832c66b8046SYuri Pankov 		return ROFF_IGN;
3833c66b8046SYuri Pankov 
3834c66b8046SYuri Pankov 	end = newn;
3835c66b8046SYuri Pankov 	newsz = roff_getname(r, &end, ln, newn - buf->buf);
3836c66b8046SYuri Pankov 	if (newsz == 0)
3837c66b8046SYuri Pankov 		return ROFF_IGN;
3838c66b8046SYuri Pankov 
3839c66b8046SYuri Pankov 	deftype = ROFFDEF_ANY;
3840c66b8046SYuri Pankov 	value = roff_getstrn(r, oldn, oldsz, &deftype);
3841c66b8046SYuri Pankov 	switch (deftype) {
3842c66b8046SYuri Pankov 	case ROFFDEF_USER:
3843c66b8046SYuri Pankov 		roff_setstrn(&r->strtab, newn, newsz, value, strlen(value), 0);
3844c66b8046SYuri Pankov 		roff_setstrn(&r->strtab, oldn, oldsz, NULL, 0, 0);
3845c66b8046SYuri Pankov 		roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
3846c66b8046SYuri Pankov 		break;
3847c66b8046SYuri Pankov 	case ROFFDEF_PRE:
3848c66b8046SYuri Pankov 		roff_setstrn(&r->strtab, newn, newsz, value, strlen(value), 0);
3849c66b8046SYuri Pankov 		roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
3850c66b8046SYuri Pankov 		break;
3851c66b8046SYuri Pankov 	case ROFFDEF_REN:
3852c66b8046SYuri Pankov 		roff_setstrn(&r->rentab, newn, newsz, value, strlen(value), 0);
3853c66b8046SYuri Pankov 		roff_setstrn(&r->rentab, oldn, oldsz, NULL, 0, 0);
3854c66b8046SYuri Pankov 		roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0);
3855c66b8046SYuri Pankov 		break;
3856c66b8046SYuri Pankov 	case ROFFDEF_STD:
3857c66b8046SYuri Pankov 		roff_setstrn(&r->rentab, newn, newsz, oldn, oldsz, 0);
3858c66b8046SYuri Pankov 		roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0);
3859c66b8046SYuri Pankov 		break;
3860c66b8046SYuri Pankov 	default:
3861c66b8046SYuri Pankov 		roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0);
3862c66b8046SYuri Pankov 		roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
3863c66b8046SYuri Pankov 		break;
3864c66b8046SYuri Pankov 	}
3865c66b8046SYuri Pankov 	return ROFF_IGN;
3866c66b8046SYuri Pankov }
3867c66b8046SYuri Pankov 
3868cec8643bSMichal Nowak static int
roff_shift(ROFF_ARGS)3869cec8643bSMichal Nowak roff_shift(ROFF_ARGS)
3870cec8643bSMichal Nowak {
3871cec8643bSMichal Nowak 	struct mctx	*ctx;
3872cec8643bSMichal Nowak 	int		 levels, i;
3873cec8643bSMichal Nowak 
3874cec8643bSMichal Nowak 	levels = 1;
3875cec8643bSMichal Nowak 	if (buf->buf[pos] != '\0' &&
3876cec8643bSMichal Nowak 	    roff_evalnum(r, ln, buf->buf, &pos, &levels, 0) == 0) {
3877cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_CE_NONUM,
3878cec8643bSMichal Nowak 		    ln, pos, "shift %s", buf->buf + pos);
3879cec8643bSMichal Nowak 		levels = 1;
3880cec8643bSMichal Nowak 	}
3881cec8643bSMichal Nowak 	if (r->mstackpos < 0) {
3882cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_REQ_NOMAC, ln, ppos, "shift");
3883cec8643bSMichal Nowak 		return ROFF_IGN;
3884cec8643bSMichal Nowak 	}
3885cec8643bSMichal Nowak 	ctx = r->mstack + r->mstackpos;
3886cec8643bSMichal Nowak 	if (levels > ctx->argc) {
3887cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_SHIFT,
3888cec8643bSMichal Nowak 		    ln, pos, "%d, but max is %d", levels, ctx->argc);
3889cec8643bSMichal Nowak 		levels = ctx->argc;
3890cec8643bSMichal Nowak 	}
3891cec8643bSMichal Nowak 	if (levels == 0)
3892cec8643bSMichal Nowak 		return ROFF_IGN;
3893cec8643bSMichal Nowak 	for (i = 0; i < levels; i++)
3894cec8643bSMichal Nowak 		free(ctx->argv[i]);
3895cec8643bSMichal Nowak 	ctx->argc -= levels;
3896cec8643bSMichal Nowak 	for (i = 0; i < ctx->argc; i++)
3897cec8643bSMichal Nowak 		ctx->argv[i] = ctx->argv[i + levels];
3898cec8643bSMichal Nowak 	return ROFF_IGN;
3899cec8643bSMichal Nowak }
3900cec8643bSMichal Nowak 
3901cec8643bSMichal Nowak static int
roff_so(ROFF_ARGS)390295c635efSGarrett D'Amore roff_so(ROFF_ARGS)
390395c635efSGarrett D'Amore {
3904260e9a87SYuri Pankov 	char *name, *cp;
390595c635efSGarrett D'Amore 
3906260e9a87SYuri Pankov 	name = buf->buf + pos;
3907cec8643bSMichal Nowak 	mandoc_msg(MANDOCERR_SO, ln, ppos, "so %s", name);
390895c635efSGarrett D'Amore 
390995c635efSGarrett D'Amore 	/*
391095c635efSGarrett D'Amore 	 * Handle `so'.  Be EXTREMELY careful, as we shouldn't be
391195c635efSGarrett D'Amore 	 * opening anything that's not in our cwd or anything beneath
391295c635efSGarrett D'Amore 	 * it.  Thus, explicitly disallow traversing up the file-system
391395c635efSGarrett D'Amore 	 * or using absolute paths.
391495c635efSGarrett D'Amore 	 */
391595c635efSGarrett D'Amore 
3916260e9a87SYuri Pankov 	if (*name == '/' || strstr(name, "../") || strstr(name, "/..")) {
3917cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_SO_PATH, ln, ppos, ".so %s", name);
3918260e9a87SYuri Pankov 		buf->sz = mandoc_asprintf(&cp,
3919260e9a87SYuri Pankov 		    ".sp\nSee the file %s.\n.sp", name) + 1;
3920260e9a87SYuri Pankov 		free(buf->buf);
3921260e9a87SYuri Pankov 		buf->buf = cp;
3922260e9a87SYuri Pankov 		*offs = 0;
3923371584c2SYuri Pankov 		return ROFF_REPARSE;
392495c635efSGarrett D'Amore 	}
392595c635efSGarrett D'Amore 
392695c635efSGarrett D'Amore 	*offs = pos;
3927371584c2SYuri Pankov 	return ROFF_SO;
392895c635efSGarrett D'Amore }
392995c635efSGarrett D'Amore 
3930371584c2SYuri Pankov /* --- user defined strings and macros ------------------------------------ */
3931371584c2SYuri Pankov 
3932cec8643bSMichal Nowak static int
roff_userdef(ROFF_ARGS)393395c635efSGarrett D'Amore roff_userdef(ROFF_ARGS)
393495c635efSGarrett D'Amore {
3935cec8643bSMichal Nowak 	struct mctx	 *ctx;
3936cec8643bSMichal Nowak 	char		 *arg, *ap, *dst, *src;
3937cec8643bSMichal Nowak 	size_t		  sz;
393895c635efSGarrett D'Amore 
3939*4d131170SRobert Mustacchi 	/* If the macro is empty, ignore it altogether. */
3940*4d131170SRobert Mustacchi 
3941*4d131170SRobert Mustacchi 	if (*r->current_string == '\0')
3942*4d131170SRobert Mustacchi 		return ROFF_IGN;
3943*4d131170SRobert Mustacchi 
3944cec8643bSMichal Nowak 	/* Initialize a new macro stack context. */
3945260e9a87SYuri Pankov 
3946cec8643bSMichal Nowak 	if (++r->mstackpos == r->mstacksz) {
3947cec8643bSMichal Nowak 		r->mstack = mandoc_recallocarray(r->mstack,
3948cec8643bSMichal Nowak 		    r->mstacksz, r->mstacksz + 8, sizeof(*r->mstack));
3949cec8643bSMichal Nowak 		r->mstacksz += 8;
3950371584c2SYuri Pankov 	}
3951cec8643bSMichal Nowak 	ctx = r->mstack + r->mstackpos;
3952cec8643bSMichal Nowak 	ctx->argsz = 0;
3953cec8643bSMichal Nowak 	ctx->argc = 0;
3954cec8643bSMichal Nowak 	ctx->argv = NULL;
3955cec8643bSMichal Nowak 
3956cec8643bSMichal Nowak 	/*
3957cec8643bSMichal Nowak 	 * Collect pointers to macro argument strings,
3958cec8643bSMichal Nowak 	 * NUL-terminating them and escaping quotes.
3959cec8643bSMichal Nowak 	 */
3960cec8643bSMichal Nowak 
3961cec8643bSMichal Nowak 	src = buf->buf + pos;
3962cec8643bSMichal Nowak 	while (*src != '\0') {
3963cec8643bSMichal Nowak 		if (ctx->argc == ctx->argsz) {
3964cec8643bSMichal Nowak 			ctx->argsz += 8;
3965cec8643bSMichal Nowak 			ctx->argv = mandoc_reallocarray(ctx->argv,
3966cec8643bSMichal Nowak 			    ctx->argsz, sizeof(*ctx->argv));
3967371584c2SYuri Pankov 		}
3968cec8643bSMichal Nowak 		arg = roff_getarg(r, &src, ln, &pos);
3969cec8643bSMichal Nowak 		sz = 1;  /* For the terminating NUL. */
3970cec8643bSMichal Nowak 		for (ap = arg; *ap != '\0'; ap++)
3971cec8643bSMichal Nowak 			sz += *ap == '"' ? 4 : 1;
3972cec8643bSMichal Nowak 		ctx->argv[ctx->argc++] = dst = mandoc_malloc(sz);
3973cec8643bSMichal Nowak 		for (ap = arg; *ap != '\0'; ap++) {
3974260e9a87SYuri Pankov 			if (*ap == '"') {
3975cec8643bSMichal Nowak 				memcpy(dst, "\\(dq", 4);
3976cec8643bSMichal Nowak 				dst += 4;
3977260e9a87SYuri Pankov 			} else
3978cec8643bSMichal Nowak 				*dst++ = *ap;
3979260e9a87SYuri Pankov 		}
3980cec8643bSMichal Nowak 		*dst = '\0';
3981cec8643bSMichal Nowak 		free(arg);
398295c635efSGarrett D'Amore 	}
398395c635efSGarrett D'Amore 
3984cec8643bSMichal Nowak 	/* Replace the macro invocation by the macro definition. */
398595c635efSGarrett D'Amore 
3986260e9a87SYuri Pankov 	free(buf->buf);
3987cec8643bSMichal Nowak 	buf->buf = mandoc_strdup(r->current_string);
3988cec8643bSMichal Nowak 	buf->sz = strlen(buf->buf) + 1;
3989260e9a87SYuri Pankov 	*offs = 0;
3990260e9a87SYuri Pankov 
3991*4d131170SRobert Mustacchi 	return buf->buf[buf->sz - 2] == '\n' ?
3992cec8643bSMichal Nowak 	    ROFF_REPARSE | ROFF_USERCALL : ROFF_IGN | ROFF_APPEND;
399395c635efSGarrett D'Amore }
399495c635efSGarrett D'Amore 
3995c66b8046SYuri Pankov /*
3996c66b8046SYuri Pankov  * Calling a high-level macro that was renamed with .rn.
3997c66b8046SYuri Pankov  * r->current_string has already been set up by roff_parse().
3998c66b8046SYuri Pankov  */
3999cec8643bSMichal Nowak static int
roff_renamed(ROFF_ARGS)4000c66b8046SYuri Pankov roff_renamed(ROFF_ARGS)
4001c66b8046SYuri Pankov {
4002c66b8046SYuri Pankov 	char	*nbuf;
4003c66b8046SYuri Pankov 
4004c66b8046SYuri Pankov 	buf->sz = mandoc_asprintf(&nbuf, ".%s%s%s", r->current_string,
4005c66b8046SYuri Pankov 	    buf->buf[pos] == '\0' ? "" : " ", buf->buf + pos) + 1;
4006c66b8046SYuri Pankov 	free(buf->buf);
4007c66b8046SYuri Pankov 	buf->buf = nbuf;
40086640c13bSYuri Pankov 	*offs = 0;
4009c66b8046SYuri Pankov 	return ROFF_CONT;
4010c66b8046SYuri Pankov }
4011c66b8046SYuri Pankov 
4012cec8643bSMichal Nowak /*
4013cec8643bSMichal Nowak  * Measure the length in bytes of the roff identifier at *cpp
4014cec8643bSMichal Nowak  * and advance the pointer to the next word.
4015cec8643bSMichal Nowak  */
4016260e9a87SYuri Pankov static size_t
roff_getname(struct roff * r,char ** cpp,int ln,int pos)401795c635efSGarrett D'Amore roff_getname(struct roff *r, char **cpp, int ln, int pos)
401895c635efSGarrett D'Amore {
401995c635efSGarrett D'Amore 	char	 *name, *cp;
4020260e9a87SYuri Pankov 	size_t	  namesz;
402195c635efSGarrett D'Amore 
402295c635efSGarrett D'Amore 	name = *cpp;
4023cec8643bSMichal Nowak 	if (*name == '\0')
4024371584c2SYuri Pankov 		return 0;
402595c635efSGarrett D'Amore 
4026cec8643bSMichal Nowak 	/* Advance cp to the byte after the end of the name. */
4027cec8643bSMichal Nowak 
4028260e9a87SYuri Pankov 	for (cp = name; 1; cp++) {
4029260e9a87SYuri Pankov 		namesz = cp - name;
4030cec8643bSMichal Nowak 		if (*cp == '\0')
4031cec8643bSMichal Nowak 			break;
4032cec8643bSMichal Nowak 		if (*cp == ' ' || *cp == '\t') {
4033cec8643bSMichal Nowak 			cp++;
4034260e9a87SYuri Pankov 			break;
4035260e9a87SYuri Pankov 		}
4036cec8643bSMichal Nowak 		if (*cp != '\\')
403795c635efSGarrett D'Amore 			continue;
4038cec8643bSMichal Nowak 		if (cp[1] == '{' || cp[1] == '}')
4039260e9a87SYuri Pankov 			break;
4040cec8643bSMichal Nowak 		if (*++cp == '\\')
404195c635efSGarrett D'Amore 			continue;
4042cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_NAMESC, ln, pos,
4043260e9a87SYuri Pankov 		    "%.*s", (int)(cp - name + 1), name);
4044260e9a87SYuri Pankov 		mandoc_escape((const char **)&cp, NULL, NULL);
4045260e9a87SYuri Pankov 		break;
404695c635efSGarrett D'Amore 	}
404795c635efSGarrett D'Amore 
404895c635efSGarrett D'Amore 	/* Read past spaces. */
4049cec8643bSMichal Nowak 
4050cec8643bSMichal Nowak 	while (*cp == ' ')
405195c635efSGarrett D'Amore 		cp++;
405295c635efSGarrett D'Amore 
405395c635efSGarrett D'Amore 	*cpp = cp;
4054371584c2SYuri Pankov 	return namesz;
405595c635efSGarrett D'Amore }
405695c635efSGarrett D'Amore 
405795c635efSGarrett D'Amore /*
405895c635efSGarrett D'Amore  * Store *string into the user-defined string called *name.
405995c635efSGarrett D'Amore  * To clear an existing entry, call with (*r, *name, NULL, 0).
4060260e9a87SYuri Pankov  * append == 0: replace mode
4061260e9a87SYuri Pankov  * append == 1: single-line append mode
4062260e9a87SYuri Pankov  * append == 2: multiline append mode, append '\n' after each call
406395c635efSGarrett D'Amore  */
406495c635efSGarrett D'Amore static void
roff_setstr(struct roff * r,const char * name,const char * string,int append)406595c635efSGarrett D'Amore roff_setstr(struct roff *r, const char *name, const char *string,
4066260e9a87SYuri Pankov 	int append)
406795c635efSGarrett D'Amore {
4068c66b8046SYuri Pankov 	size_t	 namesz;
406995c635efSGarrett D'Amore 
4070c66b8046SYuri Pankov 	namesz = strlen(name);
4071c66b8046SYuri Pankov 	roff_setstrn(&r->strtab, name, namesz, string,
4072260e9a87SYuri Pankov 	    string ? strlen(string) : 0, append);
4073c66b8046SYuri Pankov 	roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
407495c635efSGarrett D'Amore }
407595c635efSGarrett D'Amore 
407695c635efSGarrett D'Amore static void
roff_setstrn(struct roffkv ** r,const char * name,size_t namesz,const char * string,size_t stringsz,int append)407795c635efSGarrett D'Amore roff_setstrn(struct roffkv **r, const char *name, size_t namesz,
4078260e9a87SYuri Pankov 		const char *string, size_t stringsz, int append)
407995c635efSGarrett D'Amore {
408095c635efSGarrett D'Amore 	struct roffkv	*n;
408195c635efSGarrett D'Amore 	char		*c;
408295c635efSGarrett D'Amore 	int		 i;
408395c635efSGarrett D'Amore 	size_t		 oldch, newch;
408495c635efSGarrett D'Amore 
408595c635efSGarrett D'Amore 	/* Search for an existing string with the same name. */
408695c635efSGarrett D'Amore 	n = *r;
408795c635efSGarrett D'Amore 
4088260e9a87SYuri Pankov 	while (n && (namesz != n->key.sz ||
4089260e9a87SYuri Pankov 			strncmp(n->key.p, name, namesz)))
409095c635efSGarrett D'Amore 		n = n->next;
409195c635efSGarrett D'Amore 
409295c635efSGarrett D'Amore 	if (NULL == n) {
409395c635efSGarrett D'Amore 		/* Create a new string table entry. */
409495c635efSGarrett D'Amore 		n = mandoc_malloc(sizeof(struct roffkv));
409595c635efSGarrett D'Amore 		n->key.p = mandoc_strndup(name, namesz);
409695c635efSGarrett D'Amore 		n->key.sz = namesz;
409795c635efSGarrett D'Amore 		n->val.p = NULL;
409895c635efSGarrett D'Amore 		n->val.sz = 0;
409995c635efSGarrett D'Amore 		n->next = *r;
410095c635efSGarrett D'Amore 		*r = n;
4101260e9a87SYuri Pankov 	} else if (0 == append) {
410295c635efSGarrett D'Amore 		free(n->val.p);
410395c635efSGarrett D'Amore 		n->val.p = NULL;
410495c635efSGarrett D'Amore 		n->val.sz = 0;
410595c635efSGarrett D'Amore 	}
410695c635efSGarrett D'Amore 
410795c635efSGarrett D'Amore 	if (NULL == string)
410895c635efSGarrett D'Amore 		return;
410995c635efSGarrett D'Amore 
411095c635efSGarrett D'Amore 	/*
411195c635efSGarrett D'Amore 	 * One additional byte for the '\n' in multiline mode,
411295c635efSGarrett D'Amore 	 * and one for the terminating '\0'.
411395c635efSGarrett D'Amore 	 */
4114260e9a87SYuri Pankov 	newch = stringsz + (1 < append ? 2u : 1u);
411595c635efSGarrett D'Amore 
411695c635efSGarrett D'Amore 	if (NULL == n->val.p) {
411795c635efSGarrett D'Amore 		n->val.p = mandoc_malloc(newch);
411895c635efSGarrett D'Amore 		*n->val.p = '\0';
411995c635efSGarrett D'Amore 		oldch = 0;
412095c635efSGarrett D'Amore 	} else {
412195c635efSGarrett D'Amore 		oldch = n->val.sz;
412295c635efSGarrett D'Amore 		n->val.p = mandoc_realloc(n->val.p, oldch + newch);
412395c635efSGarrett D'Amore 	}
412495c635efSGarrett D'Amore 
412595c635efSGarrett D'Amore 	/* Skip existing content in the destination buffer. */
412695c635efSGarrett D'Amore 	c = n->val.p + (int)oldch;
412795c635efSGarrett D'Amore 
412895c635efSGarrett D'Amore 	/* Append new content to the destination buffer. */
412995c635efSGarrett D'Amore 	i = 0;
413095c635efSGarrett D'Amore 	while (i < (int)stringsz) {
413195c635efSGarrett D'Amore 		/*
413295c635efSGarrett D'Amore 		 * Rudimentary roff copy mode:
413395c635efSGarrett D'Amore 		 * Handle escaped backslashes.
413495c635efSGarrett D'Amore 		 */
413595c635efSGarrett D'Amore 		if ('\\' == string[i] && '\\' == string[i + 1])
413695c635efSGarrett D'Amore 			i++;
413795c635efSGarrett D'Amore 		*c++ = string[i++];
413895c635efSGarrett D'Amore 	}
413995c635efSGarrett D'Amore 
414095c635efSGarrett D'Amore 	/* Append terminating bytes. */
4141260e9a87SYuri Pankov 	if (1 < append)
414295c635efSGarrett D'Amore 		*c++ = '\n';
414395c635efSGarrett D'Amore 
414495c635efSGarrett D'Amore 	*c = '\0';
414595c635efSGarrett D'Amore 	n->val.sz = (int)(c - n->val.p);
414695c635efSGarrett D'Amore }
414795c635efSGarrett D'Amore 
414895c635efSGarrett D'Amore static const char *
roff_getstrn(struct roff * r,const char * name,size_t len,int * deftype)41496640c13bSYuri Pankov roff_getstrn(struct roff *r, const char *name, size_t len,
4150c66b8046SYuri Pankov     int *deftype)
415195c635efSGarrett D'Amore {
415295c635efSGarrett D'Amore 	const struct roffkv	*n;
41536640c13bSYuri Pankov 	int			 found, i;
4154c66b8046SYuri Pankov 	enum roff_tok		 tok;
415595c635efSGarrett D'Amore 
41566640c13bSYuri Pankov 	found = 0;
4157c66b8046SYuri Pankov 	for (n = r->strtab; n != NULL; n = n->next) {
41586640c13bSYuri Pankov 		if (strncmp(name, n->key.p, len) != 0 ||
41596640c13bSYuri Pankov 		    n->key.p[len] != '\0' || n->val.p == NULL)
41606640c13bSYuri Pankov 			continue;
41616640c13bSYuri Pankov 		if (*deftype & ROFFDEF_USER) {
4162c66b8046SYuri Pankov 			*deftype = ROFFDEF_USER;
4163371584c2SYuri Pankov 			return n->val.p;
41646640c13bSYuri Pankov 		} else {
41656640c13bSYuri Pankov 			found = 1;
41666640c13bSYuri Pankov 			break;
4167c66b8046SYuri Pankov 		}
4168c66b8046SYuri Pankov 	}
4169c66b8046SYuri Pankov 	for (n = r->rentab; n != NULL; n = n->next) {
41706640c13bSYuri Pankov 		if (strncmp(name, n->key.p, len) != 0 ||
41716640c13bSYuri Pankov 		    n->key.p[len] != '\0' || n->val.p == NULL)
41726640c13bSYuri Pankov 			continue;
41736640c13bSYuri Pankov 		if (*deftype & ROFFDEF_REN) {
4174c66b8046SYuri Pankov 			*deftype = ROFFDEF_REN;
4175c66b8046SYuri Pankov 			return n->val.p;
41766640c13bSYuri Pankov 		} else {
41776640c13bSYuri Pankov 			found = 1;
41786640c13bSYuri Pankov 			break;
4179c66b8046SYuri Pankov 		}
4180c66b8046SYuri Pankov 	}
41816640c13bSYuri Pankov 	for (i = 0; i < PREDEFS_MAX; i++) {
41826640c13bSYuri Pankov 		if (strncmp(name, predefs[i].name, len) != 0 ||
41836640c13bSYuri Pankov 		    predefs[i].name[len] != '\0')
41846640c13bSYuri Pankov 			continue;
41856640c13bSYuri Pankov 		if (*deftype & ROFFDEF_PRE) {
41866640c13bSYuri Pankov 			*deftype = ROFFDEF_PRE;
41876640c13bSYuri Pankov 			return predefs[i].str;
41886640c13bSYuri Pankov 		} else {
41896640c13bSYuri Pankov 			found = 1;
41906640c13bSYuri Pankov 			break;
4191c66b8046SYuri Pankov 		}
41926640c13bSYuri Pankov 	}
4193cec8643bSMichal Nowak 	if (r->man->meta.macroset != MACROSET_MAN) {
4194c66b8046SYuri Pankov 		for (tok = MDOC_Dd; tok < MDOC_MAX; tok++) {
41956640c13bSYuri Pankov 			if (strncmp(name, roff_name[tok], len) != 0 ||
41966640c13bSYuri Pankov 			    roff_name[tok][len] != '\0')
41976640c13bSYuri Pankov 				continue;
41986640c13bSYuri Pankov 			if (*deftype & ROFFDEF_STD) {
4199c66b8046SYuri Pankov 				*deftype = ROFFDEF_STD;
4200c66b8046SYuri Pankov 				return NULL;
42016640c13bSYuri Pankov 			} else {
42026640c13bSYuri Pankov 				found = 1;
42036640c13bSYuri Pankov 				break;
4204c66b8046SYuri Pankov 			}
4205c66b8046SYuri Pankov 		}
4206c66b8046SYuri Pankov 	}
4207cec8643bSMichal Nowak 	if (r->man->meta.macroset != MACROSET_MDOC) {
4208c66b8046SYuri Pankov 		for (tok = MAN_TH; tok < MAN_MAX; tok++) {
42096640c13bSYuri Pankov 			if (strncmp(name, roff_name[tok], len) != 0 ||
42106640c13bSYuri Pankov 			    roff_name[tok][len] != '\0')
42116640c13bSYuri Pankov 				continue;
42126640c13bSYuri Pankov 			if (*deftype & ROFFDEF_STD) {
4213c66b8046SYuri Pankov 				*deftype = ROFFDEF_STD;
4214c66b8046SYuri Pankov 				return NULL;
42156640c13bSYuri Pankov 			} else {
42166640c13bSYuri Pankov 				found = 1;
42176640c13bSYuri Pankov 				break;
4218c66b8046SYuri Pankov 			}
4219c66b8046SYuri Pankov 		}
4220c66b8046SYuri Pankov 	}
42216640c13bSYuri Pankov 
42226640c13bSYuri Pankov 	if (found == 0 && *deftype != ROFFDEF_ANY) {
42236640c13bSYuri Pankov 		if (*deftype & ROFFDEF_REN) {
42246640c13bSYuri Pankov 			/*
42256640c13bSYuri Pankov 			 * This might still be a request,
42266640c13bSYuri Pankov 			 * so do not treat it as undefined yet.
42276640c13bSYuri Pankov 			 */
42286640c13bSYuri Pankov 			*deftype = ROFFDEF_UNDEF;
42296640c13bSYuri Pankov 			return NULL;
4230c66b8046SYuri Pankov 		}
42316640c13bSYuri Pankov 
42326640c13bSYuri Pankov 		/* Using an undefined string defines it to be empty. */
42336640c13bSYuri Pankov 
42346640c13bSYuri Pankov 		roff_setstrn(&r->strtab, name, len, "", 0, 0);
42356640c13bSYuri Pankov 		roff_setstrn(&r->rentab, name, len, NULL, 0, 0);
42366640c13bSYuri Pankov 	}
42376640c13bSYuri Pankov 
4238c66b8046SYuri Pankov 	*deftype = 0;
4239371584c2SYuri Pankov 	return NULL;
424095c635efSGarrett D'Amore }
424195c635efSGarrett D'Amore 
424295c635efSGarrett D'Amore static void
roff_freestr(struct roffkv * r)424395c635efSGarrett D'Amore roff_freestr(struct roffkv *r)
424495c635efSGarrett D'Amore {
424595c635efSGarrett D'Amore 	struct roffkv	 *n, *nn;
424695c635efSGarrett D'Amore 
424795c635efSGarrett D'Amore 	for (n = r; n; n = nn) {
424895c635efSGarrett D'Amore 		free(n->key.p);
424995c635efSGarrett D'Amore 		free(n->val.p);
425095c635efSGarrett D'Amore 		nn = n->next;
425195c635efSGarrett D'Amore 		free(n);
425295c635efSGarrett D'Amore 	}
425395c635efSGarrett D'Amore }
425495c635efSGarrett D'Amore 
4255371584c2SYuri Pankov /* --- accessors and utility functions ------------------------------------ */
4256371584c2SYuri Pankov 
425795c635efSGarrett D'Amore /*
425895c635efSGarrett D'Amore  * Duplicate an input string, making the appropriate character
425995c635efSGarrett D'Amore  * conversations (as stipulated by `tr') along the way.
426095c635efSGarrett D'Amore  * Returns a heap-allocated string with all the replacements made.
426195c635efSGarrett D'Amore  */
426295c635efSGarrett D'Amore char *
roff_strdup(const struct roff * r,const char * p)426395c635efSGarrett D'Amore roff_strdup(const struct roff *r, const char *p)
426495c635efSGarrett D'Amore {
426595c635efSGarrett D'Amore 	const struct roffkv *cp;
426695c635efSGarrett D'Amore 	char		*res;
426795c635efSGarrett D'Amore 	const char	*pp;
426895c635efSGarrett D'Amore 	size_t		 ssz, sz;
426995c635efSGarrett D'Amore 	enum mandoc_esc	 esc;
427095c635efSGarrett D'Amore 
427195c635efSGarrett D'Amore 	if (NULL == r->xmbtab && NULL == r->xtab)
4272371584c2SYuri Pankov 		return mandoc_strdup(p);
427395c635efSGarrett D'Amore 	else if ('\0' == *p)
4274371584c2SYuri Pankov 		return mandoc_strdup("");
427595c635efSGarrett D'Amore 
427695c635efSGarrett D'Amore 	/*
427795c635efSGarrett D'Amore 	 * Step through each character looking for term matches
427895c635efSGarrett D'Amore 	 * (remember that a `tr' can be invoked with an escape, which is
427995c635efSGarrett D'Amore 	 * a glyph but the escape is multi-character).
428095c635efSGarrett D'Amore 	 * We only do this if the character hash has been initialised
428195c635efSGarrett D'Amore 	 * and the string is >0 length.
428295c635efSGarrett D'Amore 	 */
428395c635efSGarrett D'Amore 
428495c635efSGarrett D'Amore 	res = NULL;
428595c635efSGarrett D'Amore 	ssz = 0;
428695c635efSGarrett D'Amore 
428795c635efSGarrett D'Amore 	while ('\0' != *p) {
4288a40ea1a7SYuri Pankov 		assert((unsigned int)*p < 128);
4289a40ea1a7SYuri Pankov 		if ('\\' != *p && r->xtab && r->xtab[(unsigned int)*p].p) {
429095c635efSGarrett D'Amore 			sz = r->xtab[(int)*p].sz;
429195c635efSGarrett D'Amore 			res = mandoc_realloc(res, ssz + sz + 1);
429295c635efSGarrett D'Amore 			memcpy(res + ssz, r->xtab[(int)*p].p, sz);
429395c635efSGarrett D'Amore 			ssz += sz;
429495c635efSGarrett D'Amore 			p++;
429595c635efSGarrett D'Amore 			continue;
429695c635efSGarrett D'Amore 		} else if ('\\' != *p) {
429795c635efSGarrett D'Amore 			res = mandoc_realloc(res, ssz + 2);
429895c635efSGarrett D'Amore 			res[ssz++] = *p++;
429995c635efSGarrett D'Amore 			continue;
430095c635efSGarrett D'Amore 		}
430195c635efSGarrett D'Amore 
430295c635efSGarrett D'Amore 		/* Search for term matches. */
430395c635efSGarrett D'Amore 		for (cp = r->xmbtab; cp; cp = cp->next)
430495c635efSGarrett D'Amore 			if (0 == strncmp(p, cp->key.p, cp->key.sz))
430595c635efSGarrett D'Amore 				break;
430695c635efSGarrett D'Amore 
430795c635efSGarrett D'Amore 		if (NULL != cp) {
430895c635efSGarrett D'Amore 			/*
430995c635efSGarrett D'Amore 			 * A match has been found.
431095c635efSGarrett D'Amore 			 * Append the match to the array and move
431195c635efSGarrett D'Amore 			 * forward by its keysize.
431295c635efSGarrett D'Amore 			 */
4313260e9a87SYuri Pankov 			res = mandoc_realloc(res,
4314260e9a87SYuri Pankov 			    ssz + cp->val.sz + 1);
431595c635efSGarrett D'Amore 			memcpy(res + ssz, cp->val.p, cp->val.sz);
431695c635efSGarrett D'Amore 			ssz += cp->val.sz;
431795c635efSGarrett D'Amore 			p += (int)cp->key.sz;
431895c635efSGarrett D'Amore 			continue;
431995c635efSGarrett D'Amore 		}
432095c635efSGarrett D'Amore 
432195c635efSGarrett D'Amore 		/*
432295c635efSGarrett D'Amore 		 * Handle escapes carefully: we need to copy
432395c635efSGarrett D'Amore 		 * over just the escape itself, or else we might
432495c635efSGarrett D'Amore 		 * do replacements within the escape itself.
432595c635efSGarrett D'Amore 		 * Make sure to pass along the bogus string.
432695c635efSGarrett D'Amore 		 */
432795c635efSGarrett D'Amore 		pp = p++;
432895c635efSGarrett D'Amore 		esc = mandoc_escape(&p, NULL, NULL);
432995c635efSGarrett D'Amore 		if (ESCAPE_ERROR == esc) {
433095c635efSGarrett D'Amore 			sz = strlen(pp);
433195c635efSGarrett D'Amore 			res = mandoc_realloc(res, ssz + sz + 1);
433295c635efSGarrett D'Amore 			memcpy(res + ssz, pp, sz);
433395c635efSGarrett D'Amore 			break;
433495c635efSGarrett D'Amore 		}
433595c635efSGarrett D'Amore 		/*
433695c635efSGarrett D'Amore 		 * We bail out on bad escapes.
433795c635efSGarrett D'Amore 		 * No need to warn: we already did so when
4338cec8643bSMichal Nowak 		 * roff_expand() was called.
433995c635efSGarrett D'Amore 		 */
434095c635efSGarrett D'Amore 		sz = (int)(p - pp);
434195c635efSGarrett D'Amore 		res = mandoc_realloc(res, ssz + sz + 1);
434295c635efSGarrett D'Amore 		memcpy(res + ssz, pp, sz);
434395c635efSGarrett D'Amore 		ssz += sz;
434495c635efSGarrett D'Amore 	}
434595c635efSGarrett D'Amore 
434695c635efSGarrett D'Amore 	res[(int)ssz] = '\0';
4347371584c2SYuri Pankov 	return res;
434895c635efSGarrett D'Amore }
4349698f87a4SGarrett D'Amore 
4350260e9a87SYuri Pankov int
roff_getformat(const struct roff * r)4351260e9a87SYuri Pankov roff_getformat(const struct roff *r)
4352260e9a87SYuri Pankov {
4353260e9a87SYuri Pankov 
4354371584c2SYuri Pankov 	return r->format;
4355260e9a87SYuri Pankov }
4356260e9a87SYuri Pankov 
4357698f87a4SGarrett D'Amore /*
4358698f87a4SGarrett D'Amore  * Find out whether a line is a macro line or not.
4359698f87a4SGarrett D'Amore  * If it is, adjust the current position and return one; if it isn't,
4360698f87a4SGarrett D'Amore  * return zero and don't change the current position.
4361698f87a4SGarrett D'Amore  * If the control character has been set with `.cc', then let that grain
4362698f87a4SGarrett D'Amore  * precedence.
4363698f87a4SGarrett D'Amore  * This is slighly contrary to groff, where using the non-breaking
4364698f87a4SGarrett D'Amore  * control character when `cc' has been invoked will cause the
4365698f87a4SGarrett D'Amore  * non-breaking macro contents to be printed verbatim.
4366698f87a4SGarrett D'Amore  */
4367698f87a4SGarrett D'Amore int
roff_getcontrol(const struct roff * r,const char * cp,int * ppos)4368698f87a4SGarrett D'Amore roff_getcontrol(const struct roff *r, const char *cp, int *ppos)
4369698f87a4SGarrett D'Amore {
4370698f87a4SGarrett D'Amore 	int		pos;
4371698f87a4SGarrett D'Amore 
4372698f87a4SGarrett D'Amore 	pos = *ppos;
4373698f87a4SGarrett D'Amore 
4374c66b8046SYuri Pankov 	if (r->control != '\0' && cp[pos] == r->control)
4375698f87a4SGarrett D'Amore 		pos++;
4376c66b8046SYuri Pankov 	else if (r->control != '\0')
4377371584c2SYuri Pankov 		return 0;
4378698f87a4SGarrett D'Amore 	else if ('\\' == cp[pos] && '.' == cp[pos + 1])
4379698f87a4SGarrett D'Amore 		pos += 2;
4380698f87a4SGarrett D'Amore 	else if ('.' == cp[pos] || '\'' == cp[pos])
4381698f87a4SGarrett D'Amore 		pos++;
4382698f87a4SGarrett D'Amore 	else
4383371584c2SYuri Pankov 		return 0;
4384698f87a4SGarrett D'Amore 
4385698f87a4SGarrett D'Amore 	while (' ' == cp[pos] || '\t' == cp[pos])
4386698f87a4SGarrett D'Amore 		pos++;
4387698f87a4SGarrett D'Amore 
4388698f87a4SGarrett D'Amore 	*ppos = pos;
4389371584c2SYuri Pankov 	return 1;
4390698f87a4SGarrett D'Amore }
4391