1322249Sbapt/*	$Id: roff.c,v 1.324 2017/07/14 17:16:16 schwarze Exp $ */
2241675Suqs/*
3294113Sbapt * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4316420Sbapt * Copyright (c) 2010-2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
5241675Suqs *
6241675Suqs * Permission to use, copy, modify, and distribute this software for any
7241675Suqs * purpose with or without fee is hereby granted, provided that the above
8241675Suqs * copyright notice and this permission notice appear in all copies.
9241675Suqs *
10241675Suqs * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11241675Suqs * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12241675Suqs * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13241675Suqs * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14241675Suqs * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15241675Suqs * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16241675Suqs * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17241675Suqs */
18241675Suqs#include "config.h"
19241675Suqs
20275432Sbapt#include <sys/types.h>
21275432Sbapt
22241675Suqs#include <assert.h>
23241675Suqs#include <ctype.h>
24279527Sbapt#include <limits.h>
25322249Sbapt#include <stddef.h>
26322249Sbapt#include <stdint.h>
27261344Suqs#include <stdio.h>
28241675Suqs#include <stdlib.h>
29241675Suqs#include <string.h>
30241675Suqs
31241675Suqs#include "mandoc.h"
32274880Sbapt#include "mandoc_aux.h"
33322249Sbapt#include "mandoc_ohash.h"
34294113Sbapt#include "roff.h"
35275432Sbapt#include "libmandoc.h"
36294113Sbapt#include "roff_int.h"
37241675Suqs#include "libroff.h"
38241675Suqs
39241675Suqs/* Maximum number of string expansions per line, to break infinite loops. */
40241675Suqs#define	EXPAND_LIMIT	1000
41241675Suqs
42322249Sbapt/* Types of definitions of macros and strings. */
43322249Sbapt#define	ROFFDEF_USER	(1 << 1)  /* User-defined. */
44322249Sbapt#define	ROFFDEF_PRE	(1 << 2)  /* Predefined. */
45322249Sbapt#define	ROFFDEF_REN	(1 << 3)  /* Renamed standard macro. */
46322249Sbapt#define	ROFFDEF_STD	(1 << 4)  /* mdoc(7) or man(7) macro. */
47322249Sbapt#define	ROFFDEF_ANY	(ROFFDEF_USER | ROFFDEF_PRE | \
48322249Sbapt			 ROFFDEF_REN | ROFFDEF_STD)
49322249Sbapt
50294113Sbapt/* --- data types --------------------------------------------------------- */
51294113Sbapt
52241675Suqs/*
53241675Suqs * An incredibly-simple string buffer.
54241675Suqs */
55241675Suqsstruct	roffstr {
56241675Suqs	char		*p; /* nil-terminated buffer */
57241675Suqs	size_t		 sz; /* saved strlen(p) */
58241675Suqs};
59241675Suqs
60241675Suqs/*
61241675Suqs * A key-value roffstr pair as part of a singly-linked list.
62241675Suqs */
63241675Suqsstruct	roffkv {
64241675Suqs	struct roffstr	 key;
65241675Suqs	struct roffstr	 val;
66241675Suqs	struct roffkv	*next; /* next in list */
67241675Suqs};
68241675Suqs
69261344Suqs/*
70261344Suqs * A single number register as part of a singly-linked list.
71261344Suqs */
72261344Suqsstruct	roffreg {
73261344Suqs	struct roffstr	 key;
74261344Suqs	int		 val;
75261344Suqs	struct roffreg	*next;
76261344Suqs};
77261344Suqs
78322249Sbapt/*
79322249Sbapt * Association of request and macro names with token IDs.
80322249Sbapt */
81322249Sbaptstruct	roffreq {
82322249Sbapt	enum roff_tok	 tok;
83322249Sbapt	char		 name[];
84322249Sbapt};
85322249Sbapt
86241675Suqsstruct	roff {
87241675Suqs	struct mparse	*parse; /* parse point */
88322249Sbapt	struct roff_man	*man; /* mdoc or man parser */
89241675Suqs	struct roffnode	*last; /* leaf of stack */
90274880Sbapt	int		*rstack; /* stack of inverted `ie' values */
91322249Sbapt	struct ohash	*reqtab; /* request lookup table */
92261344Suqs	struct roffreg	*regtab; /* number registers */
93241675Suqs	struct roffkv	*strtab; /* user-defined strings & macros */
94322249Sbapt	struct roffkv	*rentab; /* renamed strings & macros */
95241675Suqs	struct roffkv	*xmbtab; /* multi-byte trans table (`tr') */
96241675Suqs	struct roffstr	*xtab; /* single-byte trans table (`tr') */
97241675Suqs	const char	*current_string; /* value of last called user macro */
98241675Suqs	struct tbl_node	*first_tbl; /* first table parsed */
99241675Suqs	struct tbl_node	*last_tbl; /* last table parsed */
100241675Suqs	struct tbl_node	*tbl; /* current table being parsed */
101322249Sbapt	struct eqn_node	*last_eqn; /* equation parser */
102322249Sbapt	struct eqn_node	*eqn; /* active equation parser */
103275432Sbapt	int		 eqn_inline; /* current equation is inline */
104274880Sbapt	int		 options; /* parse options */
105274880Sbapt	int		 rstacksz; /* current size limit of rstack */
106274880Sbapt	int		 rstackpos; /* position in rstack */
107275432Sbapt	int		 format; /* current file in mdoc or man format */
108294113Sbapt	int		 argc; /* number of args of the last macro */
109274880Sbapt	char		 control; /* control character */
110322249Sbapt	char		 escape; /* escape character */
111241675Suqs};
112241675Suqs
113241675Suqsstruct	roffnode {
114322249Sbapt	enum roff_tok	 tok; /* type of node */
115241675Suqs	struct roffnode	*parent; /* up one in stack */
116241675Suqs	int		 line; /* parse line */
117241675Suqs	int		 col; /* parse col */
118241675Suqs	char		*name; /* node name, e.g. macro name */
119241675Suqs	char		*end; /* end-rules: custom token */
120241675Suqs	int		 endspan; /* end-rules: next-line or infty */
121274880Sbapt	int		 rule; /* current evaluation rule */
122241675Suqs};
123241675Suqs
124241675Suqs#define	ROFF_ARGS	 struct roff *r, /* parse ctx */ \
125322249Sbapt			 enum roff_tok tok, /* tok of macro */ \
126275432Sbapt			 struct buf *buf, /* input buffer */ \
127241675Suqs			 int ln, /* parse line */ \
128241675Suqs			 int ppos, /* original pos in buffer */ \
129241675Suqs			 int pos, /* current pos in buffer */ \
130241675Suqs			 int *offs /* reset offset of buffer data */
131241675Suqs
132241675Suqstypedef	enum rofferr (*roffproc)(ROFF_ARGS);
133241675Suqs
134241675Suqsstruct	roffmac {
135241675Suqs	roffproc	 proc; /* process new macro */
136241675Suqs	roffproc	 text; /* process as child text of macro */
137241675Suqs	roffproc	 sub; /* process as child of macro */
138241675Suqs	int		 flags;
139241675Suqs#define	ROFFMAC_STRUCT	(1 << 0) /* always interpret */
140241675Suqs};
141241675Suqs
142241675Suqsstruct	predef {
143241675Suqs	const char	*name; /* predefined input name */
144241675Suqs	const char	*str; /* replacement symbol */
145241675Suqs};
146241675Suqs
147241675Suqs#define	PREDEF(__name, __str) \
148241675Suqs	{ (__name), (__str) },
149241675Suqs
150294113Sbapt/* --- function prototypes ------------------------------------------------ */
151294113Sbapt
152241675Suqsstatic	void		 roffnode_cleanscope(struct roff *);
153241675Suqsstatic	void		 roffnode_pop(struct roff *);
154322249Sbaptstatic	void		 roffnode_push(struct roff *, enum roff_tok,
155241675Suqs				const char *, int, int);
156322249Sbaptstatic	void		 roff_addtbl(struct roff_man *, struct tbl_node *);
157322249Sbaptstatic	enum rofferr	 roff_als(ROFF_ARGS);
158241675Suqsstatic	enum rofferr	 roff_block(ROFF_ARGS);
159241675Suqsstatic	enum rofferr	 roff_block_text(ROFF_ARGS);
160241675Suqsstatic	enum rofferr	 roff_block_sub(ROFF_ARGS);
161322249Sbaptstatic	enum rofferr	 roff_br(ROFF_ARGS);
162241675Suqsstatic	enum rofferr	 roff_cblock(ROFF_ARGS);
163261344Suqsstatic	enum rofferr	 roff_cc(ROFF_ARGS);
164274880Sbaptstatic	void		 roff_ccond(struct roff *, int, int);
165241675Suqsstatic	enum rofferr	 roff_cond(ROFF_ARGS);
166241675Suqsstatic	enum rofferr	 roff_cond_text(ROFF_ARGS);
167241675Suqsstatic	enum rofferr	 roff_cond_sub(ROFF_ARGS);
168241675Suqsstatic	enum rofferr	 roff_ds(ROFF_ARGS);
169322249Sbaptstatic	enum rofferr	 roff_ec(ROFF_ARGS);
170322249Sbaptstatic	enum rofferr	 roff_eo(ROFF_ARGS);
171275432Sbaptstatic	enum rofferr	 roff_eqndelim(struct roff *, struct buf *, int);
172294113Sbaptstatic	int		 roff_evalcond(struct roff *r, int, char *, int *);
173275432Sbaptstatic	int		 roff_evalnum(struct roff *, int,
174275432Sbapt				const char *, int *, int *, int);
175275432Sbaptstatic	int		 roff_evalpar(struct roff *, int,
176279527Sbapt				const char *, int *, int *, int);
177274880Sbaptstatic	int		 roff_evalstrcond(const char *, int *);
178241675Suqsstatic	void		 roff_free1(struct roff *);
179261344Suqsstatic	void		 roff_freereg(struct roffreg *);
180241675Suqsstatic	void		 roff_freestr(struct roffkv *);
181274880Sbaptstatic	size_t		 roff_getname(struct roff *, char **, int, int);
182279527Sbaptstatic	int		 roff_getnum(const char *, int *, int *, int);
183261344Suqsstatic	int		 roff_getop(const char *, int *, char *);
184261344Suqsstatic	int		 roff_getregn(const struct roff *,
185261344Suqs				const char *, size_t);
186294113Sbaptstatic	int		 roff_getregro(const struct roff *,
187294113Sbapt				const char *name);
188274880Sbaptstatic	const char	*roff_getstrn(const struct roff *,
189322249Sbapt				const char *, size_t, int *);
190294113Sbaptstatic	int		 roff_hasregn(const struct roff *,
191294113Sbapt				const char *, size_t);
192279527Sbaptstatic	enum rofferr	 roff_insec(ROFF_ARGS);
193261344Suqsstatic	enum rofferr	 roff_it(ROFF_ARGS);
194241675Suqsstatic	enum rofferr	 roff_line_ignore(ROFF_ARGS);
195294113Sbaptstatic	void		 roff_man_alloc1(struct roff_man *);
196294113Sbaptstatic	void		 roff_man_free1(struct roff_man *);
197322249Sbaptstatic	enum rofferr	 roff_manyarg(ROFF_ARGS);
198241675Suqsstatic	enum rofferr	 roff_nr(ROFF_ARGS);
199322249Sbaptstatic	enum rofferr	 roff_onearg(ROFF_ARGS);
200322249Sbaptstatic	enum roff_tok	 roff_parse(struct roff *, char *, int *,
201274880Sbapt				int, int);
202322249Sbaptstatic	enum rofferr	 roff_parsetext(struct roff *, struct buf *,
203322249Sbapt				int, int *);
204322249Sbaptstatic	enum rofferr	 roff_renamed(ROFF_ARGS);
205275432Sbaptstatic	enum rofferr	 roff_res(struct roff *, struct buf *, int, int);
206241675Suqsstatic	enum rofferr	 roff_rm(ROFF_ARGS);
207322249Sbaptstatic	enum rofferr	 roff_rn(ROFF_ARGS);
208274880Sbaptstatic	enum rofferr	 roff_rr(ROFF_ARGS);
209241675Suqsstatic	void		 roff_setstr(struct roff *,
210241675Suqs				const char *, const char *, int);
211274880Sbaptstatic	void		 roff_setstrn(struct roffkv **, const char *,
212241675Suqs				size_t, const char *, size_t, int);
213241675Suqsstatic	enum rofferr	 roff_so(ROFF_ARGS);
214241675Suqsstatic	enum rofferr	 roff_tr(ROFF_ARGS);
215261344Suqsstatic	enum rofferr	 roff_Dd(ROFF_ARGS);
216241675Suqsstatic	enum rofferr	 roff_TE(ROFF_ARGS);
217241675Suqsstatic	enum rofferr	 roff_TS(ROFF_ARGS);
218241675Suqsstatic	enum rofferr	 roff_EQ(ROFF_ARGS);
219241675Suqsstatic	enum rofferr	 roff_EN(ROFF_ARGS);
220241675Suqsstatic	enum rofferr	 roff_T_(ROFF_ARGS);
221279527Sbaptstatic	enum rofferr	 roff_unsupp(ROFF_ARGS);
222241675Suqsstatic	enum rofferr	 roff_userdef(ROFF_ARGS);
223241675Suqs
224294113Sbapt/* --- constant data ------------------------------------------------------ */
225294113Sbapt
226279527Sbapt#define	ROFFNUM_SCALE	(1 << 0)  /* Honour scaling in roff_getnum(). */
227279527Sbapt#define	ROFFNUM_WHITE	(1 << 1)  /* Skip whitespace in roff_evalnum(). */
228279527Sbapt
229322249Sbaptconst char *__roff_name[MAN_MAX + 1] = {
230322249Sbapt	"br",		"ce",		"ft",		"ll",
231322249Sbapt	"mc",		"po",		"rj",		"sp",
232322249Sbapt	"ta",		"ti",		NULL,
233322249Sbapt	"ab",		"ad",		"af",		"aln",
234322249Sbapt	"als",		"am",		"am1",		"ami",
235322249Sbapt	"ami1",		"as",		"as1",		"asciify",
236322249Sbapt	"backtrace",	"bd",		"bleedat",	"blm",
237322249Sbapt        "box",		"boxa",		"bp",		"BP",
238322249Sbapt	"break",	"breakchar",	"brnl",		"brp",
239322249Sbapt	"brpnl",	"c2",		"cc",
240322249Sbapt	"cf",		"cflags",	"ch",		"char",
241322249Sbapt	"chop",		"class",	"close",	"CL",
242322249Sbapt	"color",	"composite",	"continue",	"cp",
243322249Sbapt	"cropat",	"cs",		"cu",		"da",
244322249Sbapt	"dch",		"Dd",		"de",		"de1",
245322249Sbapt	"defcolor",	"dei",		"dei1",		"device",
246322249Sbapt	"devicem",	"di",		"do",		"ds",
247322249Sbapt	"ds1",		"dwh",		"dt",		"ec",
248322249Sbapt	"ecr",		"ecs",		"el",		"em",
249322249Sbapt	"EN",		"eo",		"EP",		"EQ",
250322249Sbapt	"errprint",	"ev",		"evc",		"ex",
251322249Sbapt	"fallback",	"fam",		"fc",		"fchar",
252322249Sbapt	"fcolor",	"fdeferlig",	"feature",	"fkern",
253322249Sbapt	"fl",		"flig",		"fp",		"fps",
254322249Sbapt	"fschar",	"fspacewidth",	"fspecial",	"ftr",
255322249Sbapt	"fzoom",	"gcolor",	"hc",		"hcode",
256322249Sbapt	"hidechar",	"hla",		"hlm",		"hpf",
257322249Sbapt	"hpfa",		"hpfcode",	"hw",		"hy",
258322249Sbapt	"hylang",	"hylen",	"hym",		"hypp",
259322249Sbapt	"hys",		"ie",		"if",		"ig",
260322249Sbapt	"index",	"it",		"itc",		"IX",
261322249Sbapt	"kern",		"kernafter",	"kernbefore",	"kernpair",
262322249Sbapt	"lc",		"lc_ctype",	"lds",		"length",
263322249Sbapt	"letadj",	"lf",		"lg",		"lhang",
264322249Sbapt	"linetabs",	"lnr",		"lnrf",		"lpfx",
265322249Sbapt	"ls",		"lsm",		"lt",
266322249Sbapt	"mediasize",	"minss",	"mk",		"mso",
267322249Sbapt	"na",		"ne",		"nh",		"nhychar",
268322249Sbapt	"nm",		"nn",		"nop",		"nr",
269322249Sbapt	"nrf",		"nroff",	"ns",		"nx",
270322249Sbapt	"open",		"opena",	"os",		"output",
271322249Sbapt	"padj",		"papersize",	"pc",		"pev",
272322249Sbapt	"pi",		"PI",		"pl",		"pm",
273322249Sbapt	"pn",		"pnr",		"ps",
274322249Sbapt	"psbb",		"pshape",	"pso",		"ptr",
275322249Sbapt	"pvs",		"rchar",	"rd",		"recursionlimit",
276322249Sbapt	"return",	"rfschar",	"rhang",
277322249Sbapt	"rm",		"rn",		"rnn",		"rr",
278322249Sbapt	"rs",		"rt",		"schar",	"sentchar",
279322249Sbapt	"shc",		"shift",	"sizes",	"so",
280322249Sbapt	"spacewidth",	"special",	"spreadwarn",	"ss",
281322249Sbapt	"sty",		"substring",	"sv",		"sy",
282322249Sbapt	"T&",		"tc",		"TE",
283322249Sbapt	"TH",		"tkf",		"tl",
284322249Sbapt	"tm",		"tm1",		"tmc",		"tr",
285322249Sbapt	"track",	"transchar",	"trf",		"trimat",
286322249Sbapt	"trin",		"trnt",		"troff",	"TS",
287322249Sbapt	"uf",		"ul",		"unformat",	"unwatch",
288322249Sbapt	"unwatchn",	"vpt",		"vs",		"warn",
289322249Sbapt	"warnscale",	"watch",	"watchlength",	"watchn",
290322249Sbapt	"wh",		"while",	"write",	"writec",
291322249Sbapt	"writem",	"xflag",	".",		NULL,
292322249Sbapt	NULL,		"text",
293322249Sbapt	"Dd",		"Dt",		"Os",		"Sh",
294322249Sbapt	"Ss",		"Pp",		"D1",		"Dl",
295322249Sbapt	"Bd",		"Ed",		"Bl",		"El",
296322249Sbapt	"It",		"Ad",		"An",		"Ap",
297322249Sbapt	"Ar",		"Cd",		"Cm",		"Dv",
298322249Sbapt	"Er",		"Ev",		"Ex",		"Fa",
299322249Sbapt	"Fd",		"Fl",		"Fn",		"Ft",
300322249Sbapt	"Ic",		"In",		"Li",		"Nd",
301322249Sbapt	"Nm",		"Op",		"Ot",		"Pa",
302322249Sbapt	"Rv",		"St",		"Va",		"Vt",
303322249Sbapt	"Xr",		"%A",		"%B",		"%D",
304322249Sbapt	"%I",		"%J",		"%N",		"%O",
305322249Sbapt	"%P",		"%R",		"%T",		"%V",
306322249Sbapt	"Ac",		"Ao",		"Aq",		"At",
307322249Sbapt	"Bc",		"Bf",		"Bo",		"Bq",
308322249Sbapt	"Bsx",		"Bx",		"Db",		"Dc",
309322249Sbapt	"Do",		"Dq",		"Ec",		"Ef",
310322249Sbapt	"Em",		"Eo",		"Fx",		"Ms",
311322249Sbapt	"No",		"Ns",		"Nx",		"Ox",
312322249Sbapt	"Pc",		"Pf",		"Po",		"Pq",
313322249Sbapt	"Qc",		"Ql",		"Qo",		"Qq",
314322249Sbapt	"Re",		"Rs",		"Sc",		"So",
315322249Sbapt	"Sq",		"Sm",		"Sx",		"Sy",
316322249Sbapt	"Tn",		"Ux",		"Xc",		"Xo",
317322249Sbapt	"Fo",		"Fc",		"Oo",		"Oc",
318322249Sbapt	"Bk",		"Ek",		"Bt",		"Hf",
319322249Sbapt	"Fr",		"Ud",		"Lb",		"Lp",
320322249Sbapt	"Lk",		"Mt",		"Brq",		"Bro",
321322249Sbapt	"Brc",		"%C",		"Es",		"En",
322322249Sbapt	"Dx",		"%Q",		"%U",		"Ta",
323322249Sbapt	NULL,
324322249Sbapt	"TH",		"SH",		"SS",		"TP",
325322249Sbapt	"LP",		"PP",		"P",		"IP",
326322249Sbapt	"HP",		"SM",		"SB",		"BI",
327322249Sbapt	"IB",		"BR",		"RB",		"R",
328322249Sbapt	"B",		"I",		"IR",		"RI",
329322249Sbapt	"nf",		"fi",
330322249Sbapt	"RE",		"RS",		"DT",		"UC",
331322249Sbapt	"PD",		"AT",		"in",
332322249Sbapt	"OP",		"EX",		"EE",		"UR",
333322249Sbapt	"UE",		"MT",		"ME",		NULL
334241675Suqs};
335322249Sbaptconst	char *const *roff_name = __roff_name;
336241675Suqs
337322249Sbaptstatic	struct roffmac	 roffs[TOKEN_NONE] = {
338322249Sbapt	{ roff_br, NULL, NULL, 0 },  /* br */
339322249Sbapt	{ roff_onearg, NULL, NULL, 0 },  /* ce */
340322249Sbapt	{ roff_onearg, NULL, NULL, 0 },  /* ft */
341322249Sbapt	{ roff_onearg, NULL, NULL, 0 },  /* ll */
342322249Sbapt	{ roff_onearg, NULL, NULL, 0 },  /* mc */
343322249Sbapt	{ roff_onearg, NULL, NULL, 0 },  /* po */
344322249Sbapt	{ roff_onearg, NULL, NULL, 0 },  /* rj */
345322249Sbapt	{ roff_onearg, NULL, NULL, 0 },  /* sp */
346322249Sbapt	{ roff_manyarg, NULL, NULL, 0 },  /* ta */
347322249Sbapt	{ roff_onearg, NULL, NULL, 0 },  /* ti */
348322249Sbapt	{ NULL, NULL, NULL, 0 },  /* ROFF_MAX */
349322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* ab */
350322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* ad */
351322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* af */
352322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* aln */
353322249Sbapt	{ roff_als, NULL, NULL, 0 },  /* als */
354322249Sbapt	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* am */
355322249Sbapt	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* am1 */
356322249Sbapt	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* ami */
357322249Sbapt	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* ami1 */
358322249Sbapt	{ roff_ds, NULL, NULL, 0 },  /* as */
359322249Sbapt	{ roff_ds, NULL, NULL, 0 },  /* as1 */
360322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* asciify */
361322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* backtrace */
362322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* bd */
363322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* bleedat */
364322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* blm */
365322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* box */
366322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* boxa */
367322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* bp */
368322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* BP */
369322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* break */
370322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* breakchar */
371322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* brnl */
372322249Sbapt	{ roff_br, NULL, NULL, 0 },  /* brp */
373322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* brpnl */
374322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* c2 */
375322249Sbapt	{ roff_cc, NULL, NULL, 0 },  /* cc */
376322249Sbapt	{ roff_insec, NULL, NULL, 0 },  /* cf */
377322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* cflags */
378322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* ch */
379322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* char */
380322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* chop */
381322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* class */
382322249Sbapt	{ roff_insec, NULL, NULL, 0 },  /* close */
383322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* CL */
384322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* color */
385322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* composite */
386322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* continue */
387322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* cp */
388322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* cropat */
389322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* cs */
390322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* cu */
391322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* da */
392322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* dch */
393322249Sbapt	{ roff_Dd, NULL, NULL, 0 },  /* Dd */
394322249Sbapt	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* de */
395322249Sbapt	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* de1 */
396322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* defcolor */
397322249Sbapt	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* dei */
398322249Sbapt	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* dei1 */
399322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* device */
400322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* devicem */
401322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* di */
402322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* do */
403322249Sbapt	{ roff_ds, NULL, NULL, 0 },  /* ds */
404322249Sbapt	{ roff_ds, NULL, NULL, 0 },  /* ds1 */
405322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* dwh */
406322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* dt */
407322249Sbapt	{ roff_ec, NULL, NULL, 0 },  /* ec */
408322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* ecr */
409322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* ecs */
410322249Sbapt	{ roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },  /* el */
411322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* em */
412322249Sbapt	{ roff_EN, NULL, NULL, 0 },  /* EN */
413322249Sbapt	{ roff_eo, NULL, NULL, 0 },  /* eo */
414322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* EP */
415322249Sbapt	{ roff_EQ, NULL, NULL, 0 },  /* EQ */
416322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* errprint */
417322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* ev */
418322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* evc */
419322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* ex */
420322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* fallback */
421322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* fam */
422322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* fc */
423322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* fchar */
424322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* fcolor */
425322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* fdeferlig */
426322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* feature */
427322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* fkern */
428322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* fl */
429322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* flig */
430322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* fp */
431322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* fps */
432322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* fschar */
433322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* fspacewidth */
434322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* fspecial */
435322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* ftr */
436322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* fzoom */
437322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* gcolor */
438322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* hc */
439322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* hcode */
440322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* hidechar */
441322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* hla */
442322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* hlm */
443322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* hpf */
444322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* hpfa */
445322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* hpfcode */
446322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* hw */
447322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* hy */
448322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* hylang */
449322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* hylen */
450322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* hym */
451322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* hypp */
452322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* hys */
453322249Sbapt	{ roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },  /* ie */
454322249Sbapt	{ roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },  /* if */
455322249Sbapt	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* ig */
456322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* index */
457322249Sbapt	{ roff_it, NULL, NULL, 0 },  /* it */
458322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* itc */
459322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* IX */
460322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* kern */
461322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* kernafter */
462322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* kernbefore */
463322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* kernpair */
464322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* lc */
465322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* lc_ctype */
466322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* lds */
467322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* length */
468322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* letadj */
469322249Sbapt	{ roff_insec, NULL, NULL, 0 },  /* lf */
470322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* lg */
471322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* lhang */
472322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* linetabs */
473322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* lnr */
474322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* lnrf */
475322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* lpfx */
476322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* ls */
477322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* lsm */
478322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* lt */
479322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* mediasize */
480322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* minss */
481322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* mk */
482322249Sbapt	{ roff_insec, NULL, NULL, 0 },  /* mso */
483322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* na */
484322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* ne */
485322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* nh */
486322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* nhychar */
487322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* nm */
488322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* nn */
489322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* nop */
490322249Sbapt	{ roff_nr, NULL, NULL, 0 },  /* nr */
491322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* nrf */
492322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* nroff */
493322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* ns */
494322249Sbapt	{ roff_insec, NULL, NULL, 0 },  /* nx */
495322249Sbapt	{ roff_insec, NULL, NULL, 0 },  /* open */
496322249Sbapt	{ roff_insec, NULL, NULL, 0 },  /* opena */
497322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* os */
498322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* output */
499322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* padj */
500322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* papersize */
501322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* pc */
502322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* pev */
503322249Sbapt	{ roff_insec, NULL, NULL, 0 },  /* pi */
504322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* PI */
505322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* pl */
506322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* pm */
507322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* pn */
508322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* pnr */
509322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* ps */
510322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* psbb */
511322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* pshape */
512322249Sbapt	{ roff_insec, NULL, NULL, 0 },  /* pso */
513322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* ptr */
514322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* pvs */
515322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* rchar */
516322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* rd */
517322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* recursionlimit */
518322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* return */
519322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* rfschar */
520322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* rhang */
521322249Sbapt	{ roff_rm, NULL, NULL, 0 },  /* rm */
522322249Sbapt	{ roff_rn, NULL, NULL, 0 },  /* rn */
523322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* rnn */
524322249Sbapt	{ roff_rr, NULL, NULL, 0 },  /* rr */
525322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* rs */
526322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* rt */
527322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* schar */
528322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* sentchar */
529322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* shc */
530322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* shift */
531322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* sizes */
532322249Sbapt	{ roff_so, NULL, NULL, 0 },  /* so */
533322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* spacewidth */
534322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* special */
535322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* spreadwarn */
536322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* ss */
537322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* sty */
538322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* substring */
539322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* sv */
540322249Sbapt	{ roff_insec, NULL, NULL, 0 },  /* sy */
541322249Sbapt	{ roff_T_, NULL, NULL, 0 },  /* T& */
542322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* tc */
543322249Sbapt	{ roff_TE, NULL, NULL, 0 },  /* TE */
544322249Sbapt	{ roff_Dd, NULL, NULL, 0 },  /* TH */
545322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* tkf */
546322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* tl */
547322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* tm */
548322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* tm1 */
549322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* tmc */
550322249Sbapt	{ roff_tr, NULL, NULL, 0 },  /* tr */
551322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* track */
552322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* transchar */
553322249Sbapt	{ roff_insec, NULL, NULL, 0 },  /* trf */
554322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* trimat */
555322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* trin */
556322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* trnt */
557322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* troff */
558322249Sbapt	{ roff_TS, NULL, NULL, 0 },  /* TS */
559322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* uf */
560322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* ul */
561322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* unformat */
562322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* unwatch */
563322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* unwatchn */
564322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* vpt */
565322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* vs */
566322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* warn */
567322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* warnscale */
568322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* watch */
569322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* watchlength */
570322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* watchn */
571322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* wh */
572322249Sbapt	{ roff_unsupp, NULL, NULL, 0 },  /* while */
573322249Sbapt	{ roff_insec, NULL, NULL, 0 },  /* write */
574322249Sbapt	{ roff_insec, NULL, NULL, 0 },  /* writec */
575322249Sbapt	{ roff_insec, NULL, NULL, 0 },  /* writem */
576322249Sbapt	{ roff_line_ignore, NULL, NULL, 0 },  /* xflag */
577322249Sbapt	{ roff_cblock, NULL, NULL, 0 },  /* . */
578322249Sbapt	{ roff_renamed, NULL, NULL, 0 },
579322249Sbapt	{ roff_userdef, NULL, NULL, 0 }
580261344Suqs};
581261344Suqs
582241675Suqs/* Array of injected predefined strings. */
583241675Suqs#define	PREDEFS_MAX	 38
584241675Suqsstatic	const struct predef predefs[PREDEFS_MAX] = {
585241675Suqs#include "predefs.in"
586241675Suqs};
587241675Suqs
588322249Sbaptstatic	int	 roffce_lines;	/* number of input lines to center */
589322249Sbaptstatic	struct roff_node *roffce_node;  /* active request */
590261344Suqsstatic	int	 roffit_lines;  /* number of lines to delay */
591261344Suqsstatic	char	*roffit_macro;  /* nil-terminated macro line */
592261344Suqs
593274880Sbapt
594294113Sbapt/* --- request table ------------------------------------------------------ */
595294113Sbapt
596322249Sbaptstruct ohash *
597322249Sbaptroffhash_alloc(enum roff_tok mintok, enum roff_tok maxtok)
598241675Suqs{
599322249Sbapt	struct ohash	*htab;
600322249Sbapt	struct roffreq	*req;
601322249Sbapt	enum roff_tok	 tok;
602322249Sbapt	size_t		 sz;
603322249Sbapt	unsigned int	 slot;
604241675Suqs
605322249Sbapt	htab = mandoc_malloc(sizeof(*htab));
606322249Sbapt	mandoc_ohash_init(htab, 8, offsetof(struct roffreq, name));
607241675Suqs
608322249Sbapt	for (tok = mintok; tok < maxtok; tok++) {
609322249Sbapt		if (roff_name[tok] == NULL)
610322249Sbapt			continue;
611322249Sbapt		sz = strlen(roff_name[tok]);
612322249Sbapt		req = mandoc_malloc(sizeof(*req) + sz + 1);
613322249Sbapt		req->tok = tok;
614322249Sbapt		memcpy(req->name, roff_name[tok], sz + 1);
615322249Sbapt		slot = ohash_qlookup(htab, req->name);
616322249Sbapt		ohash_insert(htab, slot, req);
617241675Suqs	}
618322249Sbapt	return htab;
619241675Suqs}
620241675Suqs
621322249Sbaptvoid
622322249Sbaptroffhash_free(struct ohash *htab)
623241675Suqs{
624322249Sbapt	struct roffreq	*req;
625322249Sbapt	unsigned int	 slot;
626241675Suqs
627322249Sbapt	if (htab == NULL)
628322249Sbapt		return;
629322249Sbapt	for (req = ohash_first(htab, &slot); req != NULL;
630322249Sbapt	     req = ohash_next(htab, &slot))
631322249Sbapt		free(req);
632322249Sbapt	ohash_delete(htab);
633322249Sbapt	free(htab);
634322249Sbapt}
635241675Suqs
636322249Sbaptenum roff_tok
637322249Sbaptroffhash_find(struct ohash *htab, const char *name, size_t sz)
638322249Sbapt{
639322249Sbapt	struct roffreq	*req;
640322249Sbapt	const char	*end;
641241675Suqs
642322249Sbapt	if (sz) {
643322249Sbapt		end = name + sz;
644322249Sbapt		req = ohash_find(htab, ohash_qlookupi(htab, name, &end));
645322249Sbapt	} else
646322249Sbapt		req = ohash_find(htab, ohash_qlookup(htab, name));
647322249Sbapt	return req == NULL ? TOKEN_NONE : req->tok;
648241675Suqs}
649241675Suqs
650294113Sbapt/* --- stack of request blocks -------------------------------------------- */
651294113Sbapt
652241675Suqs/*
653241675Suqs * Pop the current node off of the stack of roff instructions currently
654241675Suqs * pending.
655241675Suqs */
656241675Suqsstatic void
657241675Suqsroffnode_pop(struct roff *r)
658241675Suqs{
659241675Suqs	struct roffnode	*p;
660241675Suqs
661241675Suqs	assert(r->last);
662274880Sbapt	p = r->last;
663241675Suqs
664241675Suqs	r->last = r->last->parent;
665241675Suqs	free(p->name);
666241675Suqs	free(p->end);
667241675Suqs	free(p);
668241675Suqs}
669241675Suqs
670241675Suqs/*
671241675Suqs * Push a roff node onto the instruction stack.  This must later be
672241675Suqs * removed with roffnode_pop().
673241675Suqs */
674241675Suqsstatic void
675322249Sbaptroffnode_push(struct roff *r, enum roff_tok tok, const char *name,
676241675Suqs		int line, int col)
677241675Suqs{
678241675Suqs	struct roffnode	*p;
679241675Suqs
680241675Suqs	p = mandoc_calloc(1, sizeof(struct roffnode));
681241675Suqs	p->tok = tok;
682241675Suqs	if (name)
683241675Suqs		p->name = mandoc_strdup(name);
684241675Suqs	p->parent = r->last;
685241675Suqs	p->line = line;
686241675Suqs	p->col = col;
687274880Sbapt	p->rule = p->parent ? p->parent->rule : 0;
688241675Suqs
689241675Suqs	r->last = p;
690241675Suqs}
691241675Suqs
692294113Sbapt/* --- roff parser state data management ---------------------------------- */
693294113Sbapt
694241675Suqsstatic void
695241675Suqsroff_free1(struct roff *r)
696241675Suqs{
697261344Suqs	struct tbl_node	*tbl;
698241675Suqs	int		 i;
699241675Suqs
700261344Suqs	while (NULL != (tbl = r->first_tbl)) {
701261344Suqs		r->first_tbl = tbl->next;
702261344Suqs		tbl_free(tbl);
703241675Suqs	}
704241675Suqs	r->first_tbl = r->last_tbl = r->tbl = NULL;
705241675Suqs
706322249Sbapt	if (r->last_eqn != NULL)
707322249Sbapt		eqn_free(r->last_eqn);
708322249Sbapt	r->last_eqn = r->eqn = NULL;
709241675Suqs
710241675Suqs	while (r->last)
711241675Suqs		roffnode_pop(r);
712241675Suqs
713274880Sbapt	free (r->rstack);
714274880Sbapt	r->rstack = NULL;
715274880Sbapt	r->rstacksz = 0;
716274880Sbapt	r->rstackpos = -1;
717274880Sbapt
718274880Sbapt	roff_freereg(r->regtab);
719274880Sbapt	r->regtab = NULL;
720274880Sbapt
721241675Suqs	roff_freestr(r->strtab);
722322249Sbapt	roff_freestr(r->rentab);
723241675Suqs	roff_freestr(r->xmbtab);
724322249Sbapt	r->strtab = r->rentab = r->xmbtab = NULL;
725241675Suqs
726241675Suqs	if (r->xtab)
727241675Suqs		for (i = 0; i < 128; i++)
728241675Suqs			free(r->xtab[i].p);
729241675Suqs	free(r->xtab);
730241675Suqs	r->xtab = NULL;
731241675Suqs}
732241675Suqs
733241675Suqsvoid
734241675Suqsroff_reset(struct roff *r)
735241675Suqs{
736241675Suqs	roff_free1(r);
737275432Sbapt	r->format = r->options & (MPARSE_MDOC | MPARSE_MAN);
738322249Sbapt	r->control = '\0';
739322249Sbapt	r->escape = '\\';
740322249Sbapt	roffce_lines = 0;
741322249Sbapt	roffce_node = NULL;
742322249Sbapt	roffit_lines = 0;
743322249Sbapt	roffit_macro = NULL;
744241675Suqs}
745241675Suqs
746241675Suqsvoid
747241675Suqsroff_free(struct roff *r)
748241675Suqs{
749241675Suqs	roff_free1(r);
750322249Sbapt	roffhash_free(r->reqtab);
751241675Suqs	free(r);
752241675Suqs}
753241675Suqs
754241675Suqsstruct roff *
755294113Sbaptroff_alloc(struct mparse *parse, int options)
756241675Suqs{
757241675Suqs	struct roff	*r;
758241675Suqs
759241675Suqs	r = mandoc_calloc(1, sizeof(struct roff));
760241675Suqs	r->parse = parse;
761322249Sbapt	r->reqtab = roffhash_alloc(0, ROFF_USERDEF);
762274880Sbapt	r->options = options;
763275432Sbapt	r->format = options & (MPARSE_MDOC | MPARSE_MAN);
764241675Suqs	r->rstackpos = -1;
765322249Sbapt	r->escape = '\\';
766294113Sbapt	return r;
767241675Suqs}
768241675Suqs
769294113Sbapt/* --- syntax tree state data management ---------------------------------- */
770294113Sbapt
771294113Sbaptstatic void
772294113Sbaptroff_man_free1(struct roff_man *man)
773294113Sbapt{
774294113Sbapt
775294113Sbapt	if (man->first != NULL)
776294113Sbapt		roff_node_delete(man, man->first);
777294113Sbapt	free(man->meta.msec);
778294113Sbapt	free(man->meta.vol);
779294113Sbapt	free(man->meta.os);
780294113Sbapt	free(man->meta.arch);
781294113Sbapt	free(man->meta.title);
782294113Sbapt	free(man->meta.name);
783294113Sbapt	free(man->meta.date);
784294113Sbapt}
785294113Sbapt
786294113Sbaptstatic void
787294113Sbaptroff_man_alloc1(struct roff_man *man)
788294113Sbapt{
789294113Sbapt
790294113Sbapt	memset(&man->meta, 0, sizeof(man->meta));
791294113Sbapt	man->first = mandoc_calloc(1, sizeof(*man->first));
792294113Sbapt	man->first->type = ROFFT_ROOT;
793294113Sbapt	man->last = man->first;
794294113Sbapt	man->last_es = NULL;
795294113Sbapt	man->flags = 0;
796294113Sbapt	man->macroset = MACROSET_NONE;
797294113Sbapt	man->lastsec = man->lastnamed = SEC_NONE;
798294113Sbapt	man->next = ROFF_NEXT_CHILD;
799294113Sbapt}
800294113Sbapt
801294113Sbaptvoid
802294113Sbaptroff_man_reset(struct roff_man *man)
803294113Sbapt{
804294113Sbapt
805294113Sbapt	roff_man_free1(man);
806294113Sbapt	roff_man_alloc1(man);
807294113Sbapt}
808294113Sbapt
809294113Sbaptvoid
810294113Sbaptroff_man_free(struct roff_man *man)
811294113Sbapt{
812294113Sbapt
813294113Sbapt	roff_man_free1(man);
814294113Sbapt	free(man);
815294113Sbapt}
816294113Sbapt
817294113Sbaptstruct roff_man *
818294113Sbaptroff_man_alloc(struct roff *roff, struct mparse *parse,
819322249Sbapt	const char *os_s, int quick)
820294113Sbapt{
821294113Sbapt	struct roff_man *man;
822294113Sbapt
823294113Sbapt	man = mandoc_calloc(1, sizeof(*man));
824294113Sbapt	man->parse = parse;
825294113Sbapt	man->roff = roff;
826322249Sbapt	man->os_s = os_s;
827294113Sbapt	man->quick = quick;
828294113Sbapt	roff_man_alloc1(man);
829322249Sbapt	roff->man = man;
830294113Sbapt	return man;
831294113Sbapt}
832294113Sbapt
833294113Sbapt/* --- syntax tree handling ----------------------------------------------- */
834294113Sbapt
835294113Sbaptstruct roff_node *
836294113Sbaptroff_node_alloc(struct roff_man *man, int line, int pos,
837294113Sbapt	enum roff_type type, int tok)
838294113Sbapt{
839294113Sbapt	struct roff_node	*n;
840294113Sbapt
841294113Sbapt	n = mandoc_calloc(1, sizeof(*n));
842294113Sbapt	n->line = line;
843294113Sbapt	n->pos = pos;
844294113Sbapt	n->tok = tok;
845294113Sbapt	n->type = type;
846294113Sbapt	n->sec = man->lastsec;
847294113Sbapt
848294113Sbapt	if (man->flags & MDOC_SYNOPSIS)
849316420Sbapt		n->flags |= NODE_SYNPRETTY;
850294113Sbapt	else
851316420Sbapt		n->flags &= ~NODE_SYNPRETTY;
852294113Sbapt	if (man->flags & MDOC_NEWLINE)
853316420Sbapt		n->flags |= NODE_LINE;
854294113Sbapt	man->flags &= ~MDOC_NEWLINE;
855294113Sbapt
856294113Sbapt	return n;
857294113Sbapt}
858294113Sbapt
859294113Sbaptvoid
860294113Sbaptroff_node_append(struct roff_man *man, struct roff_node *n)
861294113Sbapt{
862294113Sbapt
863294113Sbapt	switch (man->next) {
864294113Sbapt	case ROFF_NEXT_SIBLING:
865294113Sbapt		if (man->last->next != NULL) {
866294113Sbapt			n->next = man->last->next;
867294113Sbapt			man->last->next->prev = n;
868294113Sbapt		} else
869294113Sbapt			man->last->parent->last = n;
870294113Sbapt		man->last->next = n;
871294113Sbapt		n->prev = man->last;
872294113Sbapt		n->parent = man->last->parent;
873294113Sbapt		break;
874294113Sbapt	case ROFF_NEXT_CHILD:
875316420Sbapt		if (man->last->child != NULL) {
876316420Sbapt			n->next = man->last->child;
877316420Sbapt			man->last->child->prev = n;
878316420Sbapt		} else
879316420Sbapt			man->last->last = n;
880294113Sbapt		man->last->child = n;
881294113Sbapt		n->parent = man->last;
882294113Sbapt		break;
883294113Sbapt	default:
884294113Sbapt		abort();
885294113Sbapt	}
886294113Sbapt	man->last = n;
887294113Sbapt
888294113Sbapt	switch (n->type) {
889294113Sbapt	case ROFFT_HEAD:
890294113Sbapt		n->parent->head = n;
891294113Sbapt		break;
892294113Sbapt	case ROFFT_BODY:
893294113Sbapt		if (n->end != ENDBODY_NOT)
894294113Sbapt			return;
895294113Sbapt		n->parent->body = n;
896294113Sbapt		break;
897294113Sbapt	case ROFFT_TAIL:
898294113Sbapt		n->parent->tail = n;
899294113Sbapt		break;
900294113Sbapt	default:
901294113Sbapt		return;
902294113Sbapt	}
903294113Sbapt
904294113Sbapt	/*
905294113Sbapt	 * Copy over the normalised-data pointer of our parent.  Not
906294113Sbapt	 * everybody has one, but copying a null pointer is fine.
907294113Sbapt	 */
908294113Sbapt
909294113Sbapt	n->norm = n->parent->norm;
910294113Sbapt	assert(n->parent->type == ROFFT_BLOCK);
911294113Sbapt}
912294113Sbapt
913294113Sbaptvoid
914294113Sbaptroff_word_alloc(struct roff_man *man, int line, int pos, const char *word)
915294113Sbapt{
916294113Sbapt	struct roff_node	*n;
917294113Sbapt
918294113Sbapt	n = roff_node_alloc(man, line, pos, ROFFT_TEXT, TOKEN_NONE);
919294113Sbapt	n->string = roff_strdup(man->roff, word);
920294113Sbapt	roff_node_append(man, n);
921316420Sbapt	n->flags |= NODE_VALID | NODE_ENDED;
922294113Sbapt	man->next = ROFF_NEXT_SIBLING;
923294113Sbapt}
924294113Sbapt
925294113Sbaptvoid
926294113Sbaptroff_word_append(struct roff_man *man, const char *word)
927294113Sbapt{
928294113Sbapt	struct roff_node	*n;
929294113Sbapt	char			*addstr, *newstr;
930294113Sbapt
931294113Sbapt	n = man->last;
932294113Sbapt	addstr = roff_strdup(man->roff, word);
933294113Sbapt	mandoc_asprintf(&newstr, "%s %s", n->string, addstr);
934294113Sbapt	free(addstr);
935294113Sbapt	free(n->string);
936294113Sbapt	n->string = newstr;
937294113Sbapt	man->next = ROFF_NEXT_SIBLING;
938294113Sbapt}
939294113Sbapt
940294113Sbaptvoid
941294113Sbaptroff_elem_alloc(struct roff_man *man, int line, int pos, int tok)
942294113Sbapt{
943294113Sbapt	struct roff_node	*n;
944294113Sbapt
945294113Sbapt	n = roff_node_alloc(man, line, pos, ROFFT_ELEM, tok);
946294113Sbapt	roff_node_append(man, n);
947294113Sbapt	man->next = ROFF_NEXT_CHILD;
948294113Sbapt}
949294113Sbapt
950294113Sbaptstruct roff_node *
951294113Sbaptroff_block_alloc(struct roff_man *man, int line, int pos, int tok)
952294113Sbapt{
953294113Sbapt	struct roff_node	*n;
954294113Sbapt
955294113Sbapt	n = roff_node_alloc(man, line, pos, ROFFT_BLOCK, tok);
956294113Sbapt	roff_node_append(man, n);
957294113Sbapt	man->next = ROFF_NEXT_CHILD;
958294113Sbapt	return n;
959294113Sbapt}
960294113Sbapt
961294113Sbaptstruct roff_node *
962294113Sbaptroff_head_alloc(struct roff_man *man, int line, int pos, int tok)
963294113Sbapt{
964294113Sbapt	struct roff_node	*n;
965294113Sbapt
966294113Sbapt	n = roff_node_alloc(man, line, pos, ROFFT_HEAD, tok);
967294113Sbapt	roff_node_append(man, n);
968294113Sbapt	man->next = ROFF_NEXT_CHILD;
969294113Sbapt	return n;
970294113Sbapt}
971294113Sbapt
972294113Sbaptstruct roff_node *
973294113Sbaptroff_body_alloc(struct roff_man *man, int line, int pos, int tok)
974294113Sbapt{
975294113Sbapt	struct roff_node	*n;
976294113Sbapt
977294113Sbapt	n = roff_node_alloc(man, line, pos, ROFFT_BODY, tok);
978294113Sbapt	roff_node_append(man, n);
979294113Sbapt	man->next = ROFF_NEXT_CHILD;
980294113Sbapt	return n;
981294113Sbapt}
982294113Sbapt
983322249Sbaptstatic void
984322249Sbaptroff_addtbl(struct roff_man *man, struct tbl_node *tbl)
985294113Sbapt{
986294113Sbapt	struct roff_node	*n;
987322249Sbapt	const struct tbl_span	*span;
988294113Sbapt
989294113Sbapt	if (man->macroset == MACROSET_MAN)
990322249Sbapt		man_breakscope(man, ROFF_TS);
991322249Sbapt	while ((span = tbl_span(tbl)) != NULL) {
992322249Sbapt		n = roff_node_alloc(man, tbl->line, 0, ROFFT_TBL, TOKEN_NONE);
993322249Sbapt		n->span = span;
994322249Sbapt		roff_node_append(man, n);
995322249Sbapt		n->flags |= NODE_VALID | NODE_ENDED;
996322249Sbapt		man->next = ROFF_NEXT_SIBLING;
997322249Sbapt	}
998294113Sbapt}
999294113Sbapt
1000294113Sbaptvoid
1001294113Sbaptroff_node_unlink(struct roff_man *man, struct roff_node *n)
1002294113Sbapt{
1003294113Sbapt
1004294113Sbapt	/* Adjust siblings. */
1005294113Sbapt
1006294113Sbapt	if (n->prev)
1007294113Sbapt		n->prev->next = n->next;
1008294113Sbapt	if (n->next)
1009294113Sbapt		n->next->prev = n->prev;
1010294113Sbapt
1011294113Sbapt	/* Adjust parent. */
1012294113Sbapt
1013294113Sbapt	if (n->parent != NULL) {
1014294113Sbapt		if (n->parent->child == n)
1015294113Sbapt			n->parent->child = n->next;
1016294113Sbapt		if (n->parent->last == n)
1017294113Sbapt			n->parent->last = n->prev;
1018294113Sbapt	}
1019294113Sbapt
1020294113Sbapt	/* Adjust parse point. */
1021294113Sbapt
1022294113Sbapt	if (man == NULL)
1023294113Sbapt		return;
1024294113Sbapt	if (man->last == n) {
1025294113Sbapt		if (n->prev == NULL) {
1026294113Sbapt			man->last = n->parent;
1027294113Sbapt			man->next = ROFF_NEXT_CHILD;
1028294113Sbapt		} else {
1029294113Sbapt			man->last = n->prev;
1030294113Sbapt			man->next = ROFF_NEXT_SIBLING;
1031294113Sbapt		}
1032294113Sbapt	}
1033294113Sbapt	if (man->first == n)
1034294113Sbapt		man->first = NULL;
1035294113Sbapt}
1036294113Sbapt
1037294113Sbaptvoid
1038294113Sbaptroff_node_free(struct roff_node *n)
1039294113Sbapt{
1040294113Sbapt
1041294113Sbapt	if (n->args != NULL)
1042294113Sbapt		mdoc_argv_free(n->args);
1043294113Sbapt	if (n->type == ROFFT_BLOCK || n->type == ROFFT_ELEM)
1044294113Sbapt		free(n->norm);
1045322249Sbapt	if (n->eqn != NULL)
1046322249Sbapt		eqn_box_free(n->eqn);
1047294113Sbapt	free(n->string);
1048294113Sbapt	free(n);
1049294113Sbapt}
1050294113Sbapt
1051294113Sbaptvoid
1052294113Sbaptroff_node_delete(struct roff_man *man, struct roff_node *n)
1053294113Sbapt{
1054294113Sbapt
1055294113Sbapt	while (n->child != NULL)
1056294113Sbapt		roff_node_delete(man, n->child);
1057294113Sbapt	roff_node_unlink(man, n);
1058294113Sbapt	roff_node_free(n);
1059294113Sbapt}
1060294113Sbapt
1061294113Sbaptvoid
1062294113Sbaptderoff(char **dest, const struct roff_node *n)
1063294113Sbapt{
1064294113Sbapt	char	*cp;
1065294113Sbapt	size_t	 sz;
1066294113Sbapt
1067294113Sbapt	if (n->type != ROFFT_TEXT) {
1068294113Sbapt		for (n = n->child; n != NULL; n = n->next)
1069294113Sbapt			deroff(dest, n);
1070294113Sbapt		return;
1071294113Sbapt	}
1072294113Sbapt
1073316420Sbapt	/* Skip leading whitespace. */
1074294113Sbapt
1075316420Sbapt	for (cp = n->string; *cp != '\0'; cp++) {
1076322249Sbapt		if (cp[0] == '\\' && cp[1] != '\0' &&
1077322249Sbapt		    strchr(" %&0^|~", cp[1]) != NULL)
1078294113Sbapt			cp++;
1079316420Sbapt		else if ( ! isspace((unsigned char)*cp))
1080294113Sbapt			break;
1081294113Sbapt	}
1082294113Sbapt
1083322249Sbapt	/* Skip trailing backslash. */
1084322249Sbapt
1085322249Sbapt	sz = strlen(cp);
1086322249Sbapt	if (sz > 0 && cp[sz - 1] == '\\')
1087322249Sbapt		sz--;
1088322249Sbapt
1089294113Sbapt	/* Skip trailing whitespace. */
1090294113Sbapt
1091322249Sbapt	for (; sz; sz--)
1092294113Sbapt		if ( ! isspace((unsigned char)cp[sz-1]))
1093294113Sbapt			break;
1094294113Sbapt
1095294113Sbapt	/* Skip empty strings. */
1096294113Sbapt
1097294113Sbapt	if (sz == 0)
1098294113Sbapt		return;
1099294113Sbapt
1100294113Sbapt	if (*dest == NULL) {
1101294113Sbapt		*dest = mandoc_strndup(cp, sz);
1102294113Sbapt		return;
1103294113Sbapt	}
1104294113Sbapt
1105294113Sbapt	mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp);
1106294113Sbapt	free(*dest);
1107294113Sbapt	*dest = cp;
1108294113Sbapt}
1109294113Sbapt
1110294113Sbapt/* --- main functions of the roff parser ---------------------------------- */
1111294113Sbapt
1112241675Suqs/*
1113274880Sbapt * In the current line, expand escape sequences that tend to get
1114274880Sbapt * used in numerical expressions and conditional requests.
1115274880Sbapt * Also check the syntax of the remaining escape sequences.
1116241675Suqs */
1117241675Suqsstatic enum rofferr
1118275432Sbaptroff_res(struct roff *r, struct buf *buf, int ln, int pos)
1119241675Suqs{
1120274880Sbapt	char		 ubuf[24]; /* buffer to print the number */
1121274880Sbapt	const char	*start;	/* start of the string to process */
1122274880Sbapt	char		*stesc;	/* start of an escape sequence ('\\') */
1123241675Suqs	const char	*stnam;	/* start of the name, after "[(*" */
1124241675Suqs	const char	*cp;	/* end of the name, e.g. before ']' */
1125241675Suqs	const char	*res;	/* the string to be substituted */
1126275432Sbapt	char		*nbuf;	/* new buffer to copy buf->buf to */
1127261344Suqs	size_t		 maxl;  /* expected length of the escape name */
1128261344Suqs	size_t		 naml;	/* actual length of the escape name */
1129275432Sbapt	enum mandoc_esc	 esc;	/* type of the escape sequence */
1130275432Sbapt	int		 inaml;	/* length returned from mandoc_escape() */
1131261344Suqs	int		 expand_count;	/* to avoid infinite loops */
1132274880Sbapt	int		 npos;	/* position in numeric expression */
1133274880Sbapt	int		 arg_complete; /* argument not interrupted by eol */
1134322249Sbapt	int		 done;	/* no more input available */
1135322249Sbapt	int		 deftype; /* type of definition to paste */
1136322249Sbapt	int		 rcsid;	/* kind of RCS id seen */
1137274880Sbapt	char		 term;	/* character terminating the escape */
1138241675Suqs
1139322249Sbapt	/* Search forward for comments. */
1140322249Sbapt
1141322249Sbapt	done = 0;
1142275432Sbapt	start = buf->buf + pos;
1143322249Sbapt	for (stesc = buf->buf + pos; *stesc != '\0'; stesc++) {
1144322249Sbapt		if (stesc[0] != r->escape || stesc[1] == '\0')
1145322249Sbapt			continue;
1146322249Sbapt		stesc++;
1147322249Sbapt		if (*stesc != '"' && *stesc != '#')
1148322249Sbapt			continue;
1149241675Suqs
1150322249Sbapt		/* Comment found, look for RCS id. */
1151322249Sbapt
1152322249Sbapt		rcsid = 0;
1153322249Sbapt		if ((cp = strstr(stesc, "$" "OpenBSD")) != NULL) {
1154322249Sbapt			rcsid = 1 << MANDOC_OS_OPENBSD;
1155322249Sbapt			cp += 8;
1156322249Sbapt		} else if ((cp = strstr(stesc, "$" "NetBSD")) != NULL) {
1157322249Sbapt			rcsid = 1 << MANDOC_OS_NETBSD;
1158322249Sbapt			cp += 7;
1159322249Sbapt		}
1160322249Sbapt		if (cp != NULL &&
1161322249Sbapt		    isalnum((unsigned char)*cp) == 0 &&
1162322249Sbapt		    strchr(cp, '$') != NULL) {
1163322249Sbapt			if (r->man->meta.rcsids & rcsid)
1164322249Sbapt				mandoc_msg(MANDOCERR_RCS_REP, r->parse,
1165322249Sbapt				    ln, stesc + 1 - buf->buf, stesc + 1);
1166322249Sbapt			r->man->meta.rcsids |= rcsid;
1167322249Sbapt		}
1168322249Sbapt
1169322249Sbapt		/* Handle trailing whitespace. */
1170322249Sbapt
1171322249Sbapt		cp = strchr(stesc--, '\0') - 1;
1172322249Sbapt		if (*cp == '\n') {
1173322249Sbapt			done = 1;
1174322249Sbapt			cp--;
1175322249Sbapt		}
1176322249Sbapt		if (*cp == ' ' || *cp == '\t')
1177322249Sbapt			mandoc_msg(MANDOCERR_SPACE_EOL, r->parse,
1178322249Sbapt			    ln, cp - buf->buf, NULL);
1179322249Sbapt		while (stesc > start && stesc[-1] == ' ')
1180322249Sbapt			stesc--;
1181322249Sbapt		*stesc = '\0';
1182322249Sbapt		break;
1183322249Sbapt	}
1184322249Sbapt	if (stesc == start)
1185322249Sbapt		return ROFF_CONT;
1186322249Sbapt	stesc--;
1187322249Sbapt
1188322249Sbapt	/* Notice the end of the input. */
1189322249Sbapt
1190322249Sbapt	if (*stesc == '\n') {
1191322249Sbapt		*stesc-- = '\0';
1192322249Sbapt		done = 1;
1193322249Sbapt	}
1194322249Sbapt
1195322249Sbapt	expand_count = 0;
1196322249Sbapt	while (stesc >= start) {
1197322249Sbapt
1198274880Sbapt		/* Search backwards for the next backslash. */
1199241675Suqs
1200322249Sbapt		if (*stesc != r->escape) {
1201322249Sbapt			if (*stesc == '\\') {
1202322249Sbapt				*stesc = '\0';
1203322249Sbapt				buf->sz = mandoc_asprintf(&nbuf, "%s\\e%s",
1204322249Sbapt				    buf->buf, stesc + 1) + 1;
1205322249Sbapt				start = nbuf + pos;
1206322249Sbapt				stesc = nbuf + (stesc - buf->buf);
1207322249Sbapt				free(buf->buf);
1208322249Sbapt				buf->buf = nbuf;
1209322249Sbapt			}
1210322249Sbapt			stesc--;
1211274880Sbapt			continue;
1212322249Sbapt		}
1213241675Suqs
1214274880Sbapt		/* If it is escaped, skip it. */
1215241675Suqs
1216274880Sbapt		for (cp = stesc - 1; cp >= start; cp--)
1217322249Sbapt			if (*cp != r->escape)
1218274880Sbapt				break;
1219274880Sbapt
1220275432Sbapt		if ((stesc - cp) % 2 == 0) {
1221322249Sbapt			while (stesc > cp)
1222322249Sbapt				*stesc-- = '\\';
1223274880Sbapt			continue;
1224322249Sbapt		} else if (stesc[1] != '\0') {
1225322249Sbapt			*stesc = '\\';
1226322249Sbapt		} else {
1227322249Sbapt			*stesc-- = '\0';
1228322249Sbapt			if (done)
1229322249Sbapt				continue;
1230322249Sbapt			else
1231322249Sbapt				return ROFF_APPEND;
1232274880Sbapt		}
1233274880Sbapt
1234274880Sbapt		/* Decide whether to expand or to check only. */
1235274880Sbapt
1236274880Sbapt		term = '\0';
1237274880Sbapt		cp = stesc + 1;
1238261344Suqs		switch (*cp) {
1239274880Sbapt		case '*':
1240261344Suqs			res = NULL;
1241261344Suqs			break;
1242274880Sbapt		case 'B':
1243274880Sbapt		case 'w':
1244274880Sbapt			term = cp[1];
1245274880Sbapt			/* FALLTHROUGH */
1246274880Sbapt		case 'n':
1247261344Suqs			res = ubuf;
1248261344Suqs			break;
1249261344Suqs		default:
1250275432Sbapt			esc = mandoc_escape(&cp, &stnam, &inaml);
1251275432Sbapt			if (esc == ESCAPE_ERROR ||
1252275432Sbapt			    (esc == ESCAPE_SPECIAL &&
1253294113Sbapt			     mchars_spec2cp(stnam, inaml) < 0))
1254274880Sbapt				mandoc_vmsg(MANDOCERR_ESC_BAD,
1255275432Sbapt				    r->parse, ln, (int)(stesc - buf->buf),
1256274880Sbapt				    "%.*s", (int)(cp - stesc), stesc);
1257322249Sbapt			stesc--;
1258274880Sbapt			continue;
1259241675Suqs		}
1260241675Suqs
1261274880Sbapt		if (EXPAND_LIMIT < ++expand_count) {
1262274880Sbapt			mandoc_msg(MANDOCERR_ROFFLOOP, r->parse,
1263275432Sbapt			    ln, (int)(stesc - buf->buf), NULL);
1264294113Sbapt			return ROFF_IGN;
1265274880Sbapt		}
1266241675Suqs
1267241675Suqs		/*
1268241675Suqs		 * The third character decides the length
1269261344Suqs		 * of the name of the string or register.
1270241675Suqs		 * Save a pointer to the name.
1271241675Suqs		 */
1272241675Suqs
1273275432Sbapt		if (term == '\0') {
1274274880Sbapt			switch (*++cp) {
1275274880Sbapt			case '\0':
1276274880Sbapt				maxl = 0;
1277274880Sbapt				break;
1278274880Sbapt			case '(':
1279274880Sbapt				cp++;
1280274880Sbapt				maxl = 2;
1281274880Sbapt				break;
1282274880Sbapt			case '[':
1283274880Sbapt				cp++;
1284274880Sbapt				term = ']';
1285274880Sbapt				maxl = 0;
1286274880Sbapt				break;
1287274880Sbapt			default:
1288274880Sbapt				maxl = 1;
1289274880Sbapt				break;
1290274880Sbapt			}
1291274880Sbapt		} else {
1292274880Sbapt			cp += 2;
1293241675Suqs			maxl = 0;
1294241675Suqs		}
1295241675Suqs		stnam = cp;
1296241675Suqs
1297241675Suqs		/* Advance to the end of the name. */
1298241675Suqs
1299279527Sbapt		naml = 0;
1300274880Sbapt		arg_complete = 1;
1301279527Sbapt		while (maxl == 0 || naml < maxl) {
1302275432Sbapt			if (*cp == '\0') {
1303274880Sbapt				mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
1304275432Sbapt				    ln, (int)(stesc - buf->buf), stesc);
1305274880Sbapt				arg_complete = 0;
1306274880Sbapt				break;
1307241675Suqs			}
1308275432Sbapt			if (maxl == 0 && *cp == term) {
1309274880Sbapt				cp++;
1310241675Suqs				break;
1311274880Sbapt			}
1312279527Sbapt			if (*cp++ != '\\' || stesc[1] != 'w') {
1313279527Sbapt				naml++;
1314279527Sbapt				continue;
1315279527Sbapt			}
1316279527Sbapt			switch (mandoc_escape(&cp, NULL, NULL)) {
1317279527Sbapt			case ESCAPE_SPECIAL:
1318279527Sbapt			case ESCAPE_UNICODE:
1319279527Sbapt			case ESCAPE_NUMBERED:
1320279527Sbapt			case ESCAPE_OVERSTRIKE:
1321279527Sbapt				naml++;
1322279527Sbapt				break;
1323279527Sbapt			default:
1324279527Sbapt				break;
1325279527Sbapt			}
1326241675Suqs		}
1327241675Suqs
1328241675Suqs		/*
1329241675Suqs		 * Retrieve the replacement string; if it is
1330241675Suqs		 * undefined, resume searching for escapes.
1331241675Suqs		 */
1332241675Suqs
1333274880Sbapt		switch (stesc[1]) {
1334274880Sbapt		case '*':
1335322249Sbapt			if (arg_complete) {
1336322249Sbapt				deftype = ROFFDEF_USER | ROFFDEF_PRE;
1337322249Sbapt				res = roff_getstrn(r, stnam, naml, &deftype);
1338322249Sbapt			}
1339274880Sbapt			break;
1340274880Sbapt		case 'B':
1341274880Sbapt			npos = 0;
1342274880Sbapt			ubuf[0] = arg_complete &&
1343279527Sbapt			    roff_evalnum(r, ln, stnam, &npos,
1344279527Sbapt			      NULL, ROFFNUM_SCALE) &&
1345274880Sbapt			    stnam + npos + 1 == cp ? '1' : '0';
1346274880Sbapt			ubuf[1] = '\0';
1347274880Sbapt			break;
1348274880Sbapt		case 'n':
1349274880Sbapt			if (arg_complete)
1350274880Sbapt				(void)snprintf(ubuf, sizeof(ubuf), "%d",
1351274880Sbapt				    roff_getregn(r, stnam, naml));
1352274880Sbapt			else
1353274880Sbapt				ubuf[0] = '\0';
1354274880Sbapt			break;
1355274880Sbapt		case 'w':
1356274880Sbapt			/* use even incomplete args */
1357274880Sbapt			(void)snprintf(ubuf, sizeof(ubuf), "%d",
1358274880Sbapt			    24 * (int)naml);
1359274880Sbapt			break;
1360274880Sbapt		}
1361241675Suqs
1362275432Sbapt		if (res == NULL) {
1363274880Sbapt			mandoc_vmsg(MANDOCERR_STR_UNDEF,
1364275432Sbapt			    r->parse, ln, (int)(stesc - buf->buf),
1365274880Sbapt			    "%.*s", (int)naml, stnam);
1366241675Suqs			res = "";
1367279527Sbapt		} else if (buf->sz + strlen(res) > SHRT_MAX) {
1368279527Sbapt			mandoc_msg(MANDOCERR_ROFFLOOP, r->parse,
1369279527Sbapt			    ln, (int)(stesc - buf->buf), NULL);
1370294113Sbapt			return ROFF_IGN;
1371241675Suqs		}
1372241675Suqs
1373241675Suqs		/* Replace the escape sequence by the string. */
1374241675Suqs
1375274880Sbapt		*stesc = '\0';
1376275432Sbapt		buf->sz = mandoc_asprintf(&nbuf, "%s%s%s",
1377275432Sbapt		    buf->buf, res, cp) + 1;
1378241675Suqs
1379274880Sbapt		/* Prepare for the next replacement. */
1380241675Suqs
1381274880Sbapt		start = nbuf + pos;
1382275432Sbapt		stesc = nbuf + (stesc - buf->buf) + strlen(res);
1383275432Sbapt		free(buf->buf);
1384275432Sbapt		buf->buf = nbuf;
1385241675Suqs	}
1386294113Sbapt	return ROFF_CONT;
1387241675Suqs}
1388241675Suqs
1389241675Suqs/*
1390294113Sbapt * Process text streams.
1391241675Suqs */
1392241675Suqsstatic enum rofferr
1393322249Sbaptroff_parsetext(struct roff *r, struct buf *buf, int pos, int *offs)
1394241675Suqs{
1395241675Suqs	size_t		 sz;
1396241675Suqs	const char	*start;
1397261344Suqs	char		*p;
1398261344Suqs	int		 isz;
1399241675Suqs	enum mandoc_esc	 esc;
1400241675Suqs
1401294113Sbapt	/* Spring the input line trap. */
1402294113Sbapt
1403294113Sbapt	if (roffit_lines == 1) {
1404294113Sbapt		isz = mandoc_asprintf(&p, "%s\n.%s", buf->buf, roffit_macro);
1405294113Sbapt		free(buf->buf);
1406294113Sbapt		buf->buf = p;
1407294113Sbapt		buf->sz = isz + 1;
1408294113Sbapt		*offs = 0;
1409294113Sbapt		free(roffit_macro);
1410294113Sbapt		roffit_lines = 0;
1411294113Sbapt		return ROFF_REPARSE;
1412294113Sbapt	} else if (roffit_lines > 1)
1413294113Sbapt		--roffit_lines;
1414294113Sbapt
1415322249Sbapt	if (roffce_node != NULL && buf->buf[pos] != '\0') {
1416322249Sbapt		if (roffce_lines < 1) {
1417322249Sbapt			r->man->last = roffce_node;
1418322249Sbapt			r->man->next = ROFF_NEXT_SIBLING;
1419322249Sbapt			roffce_lines = 0;
1420322249Sbapt			roffce_node = NULL;
1421322249Sbapt		} else
1422322249Sbapt			roffce_lines--;
1423322249Sbapt	}
1424322249Sbapt
1425294113Sbapt	/* Convert all breakable hyphens into ASCII_HYPH. */
1426294113Sbapt
1427275432Sbapt	start = p = buf->buf + pos;
1428241675Suqs
1429275432Sbapt	while (*p != '\0') {
1430241675Suqs		sz = strcspn(p, "-\\");
1431241675Suqs		p += sz;
1432241675Suqs
1433275432Sbapt		if (*p == '\0')
1434241675Suqs			break;
1435241675Suqs
1436275432Sbapt		if (*p == '\\') {
1437241675Suqs			/* Skip over escapes. */
1438241675Suqs			p++;
1439261344Suqs			esc = mandoc_escape((const char **)&p, NULL, NULL);
1440275432Sbapt			if (esc == ESCAPE_ERROR)
1441241675Suqs				break;
1442294113Sbapt			while (*p == '-')
1443294113Sbapt				p++;
1444241675Suqs			continue;
1445241675Suqs		} else if (p == start) {
1446241675Suqs			p++;
1447241675Suqs			continue;
1448241675Suqs		}
1449241675Suqs
1450241675Suqs		if (isalpha((unsigned char)p[-1]) &&
1451241675Suqs		    isalpha((unsigned char)p[1]))
1452241675Suqs			*p = ASCII_HYPH;
1453241675Suqs		p++;
1454241675Suqs	}
1455294113Sbapt	return ROFF_CONT;
1456241675Suqs}
1457241675Suqs
1458241675Suqsenum rofferr
1459275432Sbaptroff_parseln(struct roff *r, int ln, struct buf *buf, int *offs)
1460241675Suqs{
1461322249Sbapt	enum roff_tok	 t;
1462241675Suqs	enum rofferr	 e;
1463275432Sbapt	int		 pos;	/* parse point */
1464279527Sbapt	int		 spos;	/* saved parse point for messages */
1465275432Sbapt	int		 ppos;	/* original offset in buf->buf */
1466275432Sbapt	int		 ctl;	/* macro line (boolean) */
1467241675Suqs
1468275432Sbapt	ppos = pos = *offs;
1469241675Suqs
1470275432Sbapt	/* Handle in-line equation delimiters. */
1471275432Sbapt
1472275432Sbapt	if (r->tbl == NULL &&
1473275432Sbapt	    r->last_eqn != NULL && r->last_eqn->delim &&
1474275432Sbapt	    (r->eqn == NULL || r->eqn_inline)) {
1475275432Sbapt		e = roff_eqndelim(r, buf, pos);
1476275432Sbapt		if (e == ROFF_REPARSE)
1477294113Sbapt			return e;
1478275432Sbapt		assert(e == ROFF_CONT);
1479275432Sbapt	}
1480275432Sbapt
1481275432Sbapt	/* Expand some escape sequences. */
1482275432Sbapt
1483275432Sbapt	e = roff_res(r, buf, ln, pos);
1484322249Sbapt	if (e == ROFF_IGN || e == ROFF_APPEND)
1485294113Sbapt		return e;
1486275432Sbapt	assert(e == ROFF_CONT);
1487241675Suqs
1488275432Sbapt	ctl = roff_getcontrol(r, buf->buf, &pos);
1489241675Suqs
1490241675Suqs	/*
1491241675Suqs	 * First, if a scope is open and we're not a macro, pass the
1492279527Sbapt	 * text through the macro's filter.
1493279527Sbapt	 * Equations process all content themselves.
1494279527Sbapt	 * Tables process almost all content themselves, but we want
1495279527Sbapt	 * to warn about macros before passing it there.
1496241675Suqs	 */
1497241675Suqs
1498279527Sbapt	if (r->last != NULL && ! ctl) {
1499241675Suqs		t = r->last->tok;
1500275432Sbapt		e = (*roffs[t].text)(r, t, buf, ln, pos, pos, offs);
1501322249Sbapt		if (e == ROFF_IGN)
1502294113Sbapt			return e;
1503322249Sbapt		assert(e == ROFF_CONT);
1504261344Suqs	}
1505322249Sbapt	if (r->eqn != NULL && strncmp(buf->buf + ppos, ".EN", 3)) {
1506322249Sbapt		eqn_read(r->eqn, buf->buf + ppos);
1507322249Sbapt		return ROFF_IGN;
1508322249Sbapt	}
1509322249Sbapt	if (r->tbl != NULL && (ctl == 0 || buf->buf[pos] == '\0')) {
1510322249Sbapt		tbl_read(r->tbl, ln, buf->buf, ppos);
1511322249Sbapt		roff_addtbl(r->man, r->tbl);
1512322249Sbapt		return ROFF_IGN;
1513322249Sbapt	}
1514279527Sbapt	if ( ! ctl)
1515322249Sbapt		return roff_parsetext(r, buf, pos, offs);
1516241675Suqs
1517275432Sbapt	/* Skip empty request lines. */
1518275432Sbapt
1519275432Sbapt	if (buf->buf[pos] == '"') {
1520275432Sbapt		mandoc_msg(MANDOCERR_COMMENT_BAD, r->parse,
1521275432Sbapt		    ln, pos, NULL);
1522294113Sbapt		return ROFF_IGN;
1523275432Sbapt	} else if (buf->buf[pos] == '\0')
1524294113Sbapt		return ROFF_IGN;
1525275432Sbapt
1526241675Suqs	/*
1527241675Suqs	 * If a scope is open, go to the child handler for that macro,
1528241675Suqs	 * as it may want to preprocess before doing anything with it.
1529241675Suqs	 * Don't do so if an equation is open.
1530241675Suqs	 */
1531241675Suqs
1532241675Suqs	if (r->last) {
1533241675Suqs		t = r->last->tok;
1534294113Sbapt		return (*roffs[t].sub)(r, t, buf, ln, ppos, pos, offs);
1535241675Suqs	}
1536241675Suqs
1537279527Sbapt	/* No scope is open.  This is a new request or macro. */
1538279527Sbapt
1539279527Sbapt	spos = pos;
1540279527Sbapt	t = roff_parse(r, buf->buf, &pos, ln, ppos);
1541279527Sbapt
1542279527Sbapt	/* Tables ignore most macros. */
1543279527Sbapt
1544322249Sbapt	if (r->tbl != NULL && (t == TOKEN_NONE || t == ROFF_TS ||
1545322249Sbapt	    t == ROFF_br || t == ROFF_ce || t == ROFF_rj || t == ROFF_sp)) {
1546279527Sbapt		mandoc_msg(MANDOCERR_TBLMACRO, r->parse,
1547279527Sbapt		    ln, pos, buf->buf + spos);
1548322249Sbapt		if (t != TOKEN_NONE)
1549294113Sbapt			return ROFF_IGN;
1550279527Sbapt		while (buf->buf[pos] != '\0' && buf->buf[pos] != ' ')
1551279527Sbapt			pos++;
1552322249Sbapt		while (buf->buf[pos] == ' ')
1553279527Sbapt			pos++;
1554322249Sbapt		tbl_read(r->tbl, ln, buf->buf, pos);
1555322249Sbapt		roff_addtbl(r->man, r->tbl);
1556322249Sbapt		return ROFF_IGN;
1557279527Sbapt	}
1558279527Sbapt
1559322249Sbapt	/* For now, let high level macros abort .ce mode. */
1560322249Sbapt
1561322249Sbapt	if (ctl && roffce_node != NULL &&
1562322249Sbapt	    (t == TOKEN_NONE || t == ROFF_Dd || t == ROFF_EQ ||
1563322249Sbapt	     t == ROFF_TH || t == ROFF_TS)) {
1564322249Sbapt		r->man->last = roffce_node;
1565322249Sbapt		r->man->next = ROFF_NEXT_SIBLING;
1566322249Sbapt		roffce_lines = 0;
1567322249Sbapt		roffce_node = NULL;
1568322249Sbapt	}
1569322249Sbapt
1570241675Suqs	/*
1571279527Sbapt	 * This is neither a roff request nor a user-defined macro.
1572279527Sbapt	 * Let the standard macro set parsers handle it.
1573241675Suqs	 */
1574241675Suqs
1575322249Sbapt	if (t == TOKEN_NONE)
1576294113Sbapt		return ROFF_CONT;
1577241675Suqs
1578279527Sbapt	/* Execute a roff request or a user defined macro. */
1579279527Sbapt
1580322249Sbapt	return (*roffs[t].proc)(r, t, buf, ln, spos, pos, offs);
1581241675Suqs}
1582241675Suqs
1583241675Suqsvoid
1584241675Suqsroff_endparse(struct roff *r)
1585241675Suqs{
1586322249Sbapt	if (r->last != NULL)
1587274880Sbapt		mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
1588274880Sbapt		    r->last->line, r->last->col,
1589322249Sbapt		    roff_name[r->last->tok]);
1590241675Suqs
1591322249Sbapt	if (r->eqn != NULL) {
1592274880Sbapt		mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
1593322249Sbapt		    r->eqn->node->line, r->eqn->node->pos, "EQ");
1594322249Sbapt		eqn_parse(r->eqn);
1595322249Sbapt		r->eqn = NULL;
1596241675Suqs	}
1597241675Suqs
1598322249Sbapt	if (r->tbl != NULL) {
1599274880Sbapt		mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
1600274880Sbapt		    r->tbl->line, r->tbl->pos, "TS");
1601322249Sbapt		tbl_end(r->tbl);
1602322249Sbapt		r->tbl = NULL;
1603241675Suqs	}
1604241675Suqs}
1605241675Suqs
1606241675Suqs/*
1607241675Suqs * Parse a roff node's type from the input buffer.  This must be in the
1608241675Suqs * form of ".foo xxx" in the usual way.
1609241675Suqs */
1610322249Sbaptstatic enum roff_tok
1611274880Sbaptroff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos)
1612241675Suqs{
1613274880Sbapt	char		*cp;
1614241675Suqs	const char	*mac;
1615241675Suqs	size_t		 maclen;
1616322249Sbapt	int		 deftype;
1617322249Sbapt	enum roff_tok	 t;
1618241675Suqs
1619274880Sbapt	cp = buf + *pos;
1620274880Sbapt
1621274880Sbapt	if ('\0' == *cp || '"' == *cp || '\t' == *cp || ' ' == *cp)
1622322249Sbapt		return TOKEN_NONE;
1623241675Suqs
1624274880Sbapt	mac = cp;
1625274880Sbapt	maclen = roff_getname(r, &cp, ln, ppos);
1626241675Suqs
1627322249Sbapt	deftype = ROFFDEF_USER | ROFFDEF_REN;
1628322249Sbapt	r->current_string = roff_getstrn(r, mac, maclen, &deftype);
1629322249Sbapt	switch (deftype) {
1630322249Sbapt	case ROFFDEF_USER:
1631322249Sbapt		t = ROFF_USERDEF;
1632322249Sbapt		break;
1633322249Sbapt	case ROFFDEF_REN:
1634322249Sbapt		t = ROFF_RENAMED;
1635322249Sbapt		break;
1636322249Sbapt	default:
1637322249Sbapt		t = roffhash_find(r->reqtab, mac, maclen);
1638322249Sbapt		break;
1639322249Sbapt	}
1640322249Sbapt	if (t != TOKEN_NONE)
1641274880Sbapt		*pos = cp - buf;
1642294113Sbapt	return t;
1643241675Suqs}
1644241675Suqs
1645294113Sbapt/* --- handling of request blocks ----------------------------------------- */
1646294113Sbapt
1647241675Suqsstatic enum rofferr
1648241675Suqsroff_cblock(ROFF_ARGS)
1649241675Suqs{
1650241675Suqs
1651241675Suqs	/*
1652241675Suqs	 * A block-close `..' should only be invoked as a child of an
1653241675Suqs	 * ignore macro, otherwise raise a warning and just ignore it.
1654241675Suqs	 */
1655241675Suqs
1656275432Sbapt	if (r->last == NULL) {
1657274880Sbapt		mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1658274880Sbapt		    ln, ppos, "..");
1659294113Sbapt		return ROFF_IGN;
1660241675Suqs	}
1661241675Suqs
1662241675Suqs	switch (r->last->tok) {
1663274880Sbapt	case ROFF_am:
1664274880Sbapt		/* ROFF_am1 is remapped to ROFF_am in roff_block(). */
1665274880Sbapt	case ROFF_ami:
1666274880Sbapt	case ROFF_de:
1667241675Suqs		/* ROFF_de1 is remapped to ROFF_de in roff_block(). */
1668274880Sbapt	case ROFF_dei:
1669274880Sbapt	case ROFF_ig:
1670241675Suqs		break;
1671241675Suqs	default:
1672274880Sbapt		mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1673274880Sbapt		    ln, ppos, "..");
1674294113Sbapt		return ROFF_IGN;
1675241675Suqs	}
1676241675Suqs
1677275432Sbapt	if (buf->buf[pos] != '\0')
1678274880Sbapt		mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos,
1679275432Sbapt		    ".. %s", buf->buf + pos);
1680241675Suqs
1681241675Suqs	roffnode_pop(r);
1682241675Suqs	roffnode_cleanscope(r);
1683294113Sbapt	return ROFF_IGN;
1684241675Suqs
1685241675Suqs}
1686241675Suqs
1687241675Suqsstatic void
1688241675Suqsroffnode_cleanscope(struct roff *r)
1689241675Suqs{
1690241675Suqs
1691241675Suqs	while (r->last) {
1692261344Suqs		if (--r->last->endspan != 0)
1693241675Suqs			break;
1694241675Suqs		roffnode_pop(r);
1695241675Suqs	}
1696241675Suqs}
1697241675Suqs
1698274880Sbaptstatic void
1699274880Sbaptroff_ccond(struct roff *r, int ln, int ppos)
1700241675Suqs{
1701241675Suqs
1702241675Suqs	if (NULL == r->last) {
1703274880Sbapt		mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1704274880Sbapt		    ln, ppos, "\\}");
1705274880Sbapt		return;
1706241675Suqs	}
1707241675Suqs
1708241675Suqs	switch (r->last->tok) {
1709274880Sbapt	case ROFF_el:
1710274880Sbapt	case ROFF_ie:
1711274880Sbapt	case ROFF_if:
1712241675Suqs		break;
1713241675Suqs	default:
1714274880Sbapt		mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1715274880Sbapt		    ln, ppos, "\\}");
1716274880Sbapt		return;
1717241675Suqs	}
1718241675Suqs
1719241675Suqs	if (r->last->endspan > -1) {
1720274880Sbapt		mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
1721274880Sbapt		    ln, ppos, "\\}");
1722274880Sbapt		return;
1723241675Suqs	}
1724241675Suqs
1725241675Suqs	roffnode_pop(r);
1726241675Suqs	roffnode_cleanscope(r);
1727274880Sbapt	return;
1728241675Suqs}
1729241675Suqs
1730241675Suqsstatic enum rofferr
1731241675Suqsroff_block(ROFF_ARGS)
1732241675Suqs{
1733322249Sbapt	const char	*name, *value;
1734322249Sbapt	char		*call, *cp, *iname, *rname;
1735322249Sbapt	size_t		 csz, namesz, rsz;
1736322249Sbapt	int		 deftype;
1737241675Suqs
1738274880Sbapt	/* Ignore groff compatibility mode for now. */
1739241675Suqs
1740275432Sbapt	if (tok == ROFF_de1)
1741274880Sbapt		tok = ROFF_de;
1742279527Sbapt	else if (tok == ROFF_dei1)
1743279527Sbapt		tok = ROFF_dei;
1744275432Sbapt	else if (tok == ROFF_am1)
1745274880Sbapt		tok = ROFF_am;
1746279527Sbapt	else if (tok == ROFF_ami1)
1747279527Sbapt		tok = ROFF_ami;
1748241675Suqs
1749274880Sbapt	/* Parse the macro name argument. */
1750241675Suqs
1751275432Sbapt	cp = buf->buf + pos;
1752275432Sbapt	if (tok == ROFF_ig) {
1753274880Sbapt		iname = NULL;
1754274880Sbapt		namesz = 0;
1755274880Sbapt	} else {
1756274880Sbapt		iname = cp;
1757274880Sbapt		namesz = roff_getname(r, &cp, ln, ppos);
1758274880Sbapt		iname[namesz] = '\0';
1759274880Sbapt	}
1760241675Suqs
1761274880Sbapt	/* Resolve the macro name argument if it is indirect. */
1762241675Suqs
1763275432Sbapt	if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
1764322249Sbapt		deftype = ROFFDEF_USER;
1765322249Sbapt		name = roff_getstrn(r, iname, namesz, &deftype);
1766322249Sbapt		if (name == NULL) {
1767274880Sbapt			mandoc_vmsg(MANDOCERR_STR_UNDEF,
1768275432Sbapt			    r->parse, ln, (int)(iname - buf->buf),
1769274880Sbapt			    "%.*s", (int)namesz, iname);
1770274880Sbapt			namesz = 0;
1771274880Sbapt		} else
1772274880Sbapt			namesz = strlen(name);
1773274880Sbapt	} else
1774274880Sbapt		name = iname;
1775274880Sbapt
1776275432Sbapt	if (namesz == 0 && tok != ROFF_ig) {
1777274880Sbapt		mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse,
1778322249Sbapt		    ln, ppos, roff_name[tok]);
1779294113Sbapt		return ROFF_IGN;
1780241675Suqs	}
1781241675Suqs
1782241675Suqs	roffnode_push(r, tok, name, ln, ppos);
1783241675Suqs
1784241675Suqs	/*
1785241675Suqs	 * At the beginning of a `de' macro, clear the existing string
1786241675Suqs	 * with the same name, if there is one.  New content will be
1787274880Sbapt	 * appended from roff_block_text() in multiline mode.
1788241675Suqs	 */
1789241675Suqs
1790322249Sbapt	if (tok == ROFF_de || tok == ROFF_dei) {
1791274880Sbapt		roff_setstrn(&r->strtab, name, namesz, "", 0, 0);
1792322249Sbapt		roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
1793322249Sbapt	} else if (tok == ROFF_am || tok == ROFF_ami) {
1794322249Sbapt		deftype = ROFFDEF_ANY;
1795322249Sbapt		value = roff_getstrn(r, iname, namesz, &deftype);
1796322249Sbapt		switch (deftype) {  /* Before appending, ... */
1797322249Sbapt		case ROFFDEF_PRE: /* copy predefined to user-defined. */
1798322249Sbapt			roff_setstrn(&r->strtab, name, namesz,
1799322249Sbapt			    value, strlen(value), 0);
1800322249Sbapt			break;
1801322249Sbapt		case ROFFDEF_REN: /* call original standard macro. */
1802322249Sbapt			csz = mandoc_asprintf(&call, ".%.*s \\$* \\\"\n",
1803322249Sbapt			    (int)strlen(value), value);
1804322249Sbapt			roff_setstrn(&r->strtab, name, namesz, call, csz, 0);
1805322249Sbapt			roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
1806322249Sbapt			free(call);
1807322249Sbapt			break;
1808322249Sbapt		case ROFFDEF_STD:  /* rename and call standard macro. */
1809322249Sbapt			rsz = mandoc_asprintf(&rname, "__%s_renamed", name);
1810322249Sbapt			roff_setstrn(&r->rentab, rname, rsz, name, namesz, 0);
1811322249Sbapt			csz = mandoc_asprintf(&call, ".%.*s \\$* \\\"\n",
1812322249Sbapt			    (int)rsz, rname);
1813322249Sbapt			roff_setstrn(&r->strtab, name, namesz, call, csz, 0);
1814322249Sbapt			free(call);
1815322249Sbapt			free(rname);
1816322249Sbapt			break;
1817322249Sbapt		default:
1818322249Sbapt			break;
1819322249Sbapt		}
1820322249Sbapt	}
1821241675Suqs
1822275432Sbapt	if (*cp == '\0')
1823294113Sbapt		return ROFF_IGN;
1824241675Suqs
1825274880Sbapt	/* Get the custom end marker. */
1826241675Suqs
1827274880Sbapt	iname = cp;
1828274880Sbapt	namesz = roff_getname(r, &cp, ln, ppos);
1829241675Suqs
1830274880Sbapt	/* Resolve the end marker if it is indirect. */
1831241675Suqs
1832275432Sbapt	if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
1833322249Sbapt		deftype = ROFFDEF_USER;
1834322249Sbapt		name = roff_getstrn(r, iname, namesz, &deftype);
1835322249Sbapt		if (name == NULL) {
1836274880Sbapt			mandoc_vmsg(MANDOCERR_STR_UNDEF,
1837275432Sbapt			    r->parse, ln, (int)(iname - buf->buf),
1838274880Sbapt			    "%.*s", (int)namesz, iname);
1839274880Sbapt			namesz = 0;
1840274880Sbapt		} else
1841274880Sbapt			namesz = strlen(name);
1842274880Sbapt	} else
1843274880Sbapt		name = iname;
1844241675Suqs
1845274880Sbapt	if (namesz)
1846274880Sbapt		r->last->end = mandoc_strndup(name, namesz);
1847241675Suqs
1848275432Sbapt	if (*cp != '\0')
1849274880Sbapt		mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse,
1850322249Sbapt		    ln, pos, ".%s ... %s", roff_name[tok], cp);
1851241675Suqs
1852294113Sbapt	return ROFF_IGN;
1853241675Suqs}
1854241675Suqs
1855241675Suqsstatic enum rofferr
1856241675Suqsroff_block_sub(ROFF_ARGS)
1857241675Suqs{
1858322249Sbapt	enum roff_tok	t;
1859241675Suqs	int		i, j;
1860241675Suqs
1861241675Suqs	/*
1862241675Suqs	 * First check whether a custom macro exists at this level.  If
1863241675Suqs	 * it does, then check against it.  This is some of groff's
1864241675Suqs	 * stranger behaviours.  If we encountered a custom end-scope
1865241675Suqs	 * tag and that tag also happens to be a "real" macro, then we
1866241675Suqs	 * need to try interpreting it again as a real macro.  If it's
1867241675Suqs	 * not, then return ignore.  Else continue.
1868241675Suqs	 */
1869241675Suqs
1870241675Suqs	if (r->last->end) {
1871241675Suqs		for (i = pos, j = 0; r->last->end[j]; j++, i++)
1872275432Sbapt			if (buf->buf[i] != r->last->end[j])
1873241675Suqs				break;
1874241675Suqs
1875275432Sbapt		if (r->last->end[j] == '\0' &&
1876275432Sbapt		    (buf->buf[i] == '\0' ||
1877275432Sbapt		     buf->buf[i] == ' ' ||
1878275432Sbapt		     buf->buf[i] == '\t')) {
1879241675Suqs			roffnode_pop(r);
1880241675Suqs			roffnode_cleanscope(r);
1881241675Suqs
1882275432Sbapt			while (buf->buf[i] == ' ' || buf->buf[i] == '\t')
1883241675Suqs				i++;
1884241675Suqs
1885241675Suqs			pos = i;
1886275432Sbapt			if (roff_parse(r, buf->buf, &pos, ln, ppos) !=
1887322249Sbapt			    TOKEN_NONE)
1888294113Sbapt				return ROFF_RERUN;
1889294113Sbapt			return ROFF_IGN;
1890241675Suqs		}
1891241675Suqs	}
1892241675Suqs
1893241675Suqs	/*
1894241675Suqs	 * If we have no custom end-query or lookup failed, then try
1895241675Suqs	 * pulling it out of the hashtable.
1896241675Suqs	 */
1897241675Suqs
1898275432Sbapt	t = roff_parse(r, buf->buf, &pos, ln, ppos);
1899241675Suqs
1900275432Sbapt	if (t != ROFF_cblock) {
1901275432Sbapt		if (tok != ROFF_ig)
1902275432Sbapt			roff_setstr(r, r->last->name, buf->buf + ppos, 2);
1903294113Sbapt		return ROFF_IGN;
1904241675Suqs	}
1905241675Suqs
1906294113Sbapt	return (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs);
1907241675Suqs}
1908241675Suqs
1909241675Suqsstatic enum rofferr
1910241675Suqsroff_block_text(ROFF_ARGS)
1911241675Suqs{
1912241675Suqs
1913275432Sbapt	if (tok != ROFF_ig)
1914275432Sbapt		roff_setstr(r, r->last->name, buf->buf + pos, 2);
1915241675Suqs
1916294113Sbapt	return ROFF_IGN;
1917241675Suqs}
1918241675Suqs
1919241675Suqsstatic enum rofferr
1920241675Suqsroff_cond_sub(ROFF_ARGS)
1921241675Suqs{
1922322249Sbapt	enum roff_tok	 t;
1923241675Suqs	char		*ep;
1924274880Sbapt	int		 rr;
1925241675Suqs
1926241675Suqs	rr = r->last->rule;
1927241675Suqs	roffnode_cleanscope(r);
1928241675Suqs
1929241675Suqs	/*
1930274880Sbapt	 * If `\}' occurs on a macro line without a preceding macro,
1931274880Sbapt	 * drop the line completely.
1932274880Sbapt	 */
1933274880Sbapt
1934275432Sbapt	ep = buf->buf + pos;
1935275432Sbapt	if (ep[0] == '\\' && ep[1] == '}')
1936274880Sbapt		rr = 0;
1937274880Sbapt
1938261344Suqs	/* Always check for the closing delimiter `\}'. */
1939241675Suqs
1940275432Sbapt	while ((ep = strchr(ep, '\\')) != NULL) {
1941322249Sbapt		switch (ep[1]) {
1942322249Sbapt		case '}':
1943322249Sbapt			memmove(ep, ep + 2, strlen(ep + 2) + 1);
1944322249Sbapt			roff_ccond(r, ln, ep - buf->buf);
1945322249Sbapt			break;
1946322249Sbapt		case '\0':
1947322249Sbapt			++ep;
1948322249Sbapt			break;
1949322249Sbapt		default:
1950322249Sbapt			ep += 2;
1951322249Sbapt			break;
1952274880Sbapt		}
1953261344Suqs	}
1954322249Sbapt
1955322249Sbapt	/*
1956322249Sbapt	 * Fully handle known macros when they are structurally
1957322249Sbapt	 * required or when the conditional evaluated to true.
1958322249Sbapt	 */
1959322249Sbapt
1960322249Sbapt	t = roff_parse(r, buf->buf, &pos, ln, ppos);
1961322249Sbapt	return t != TOKEN_NONE && (rr || roffs[t].flags & ROFFMAC_STRUCT)
1962322249Sbapt	    ? (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs) : rr
1963322249Sbapt	    ? ROFF_CONT : ROFF_IGN;
1964241675Suqs}
1965241675Suqs
1966241675Suqsstatic enum rofferr
1967241675Suqsroff_cond_text(ROFF_ARGS)
1968241675Suqs{
1969241675Suqs	char		*ep;
1970274880Sbapt	int		 rr;
1971241675Suqs
1972241675Suqs	rr = r->last->rule;
1973241675Suqs	roffnode_cleanscope(r);
1974241675Suqs
1975275432Sbapt	ep = buf->buf + pos;
1976275432Sbapt	while ((ep = strchr(ep, '\\')) != NULL) {
1977275432Sbapt		if (*(++ep) == '}') {
1978274880Sbapt			*ep = '&';
1979275432Sbapt			roff_ccond(r, ln, ep - buf->buf - 1);
1980274880Sbapt		}
1981279527Sbapt		if (*ep != '\0')
1982279527Sbapt			++ep;
1983241675Suqs	}
1984294113Sbapt	return rr ? ROFF_CONT : ROFF_IGN;
1985241675Suqs}
1986241675Suqs
1987294113Sbapt/* --- handling of numeric and conditional expressions -------------------- */
1988294113Sbapt
1989274880Sbapt/*
1990274880Sbapt * Parse a single signed integer number.  Stop at the first non-digit.
1991274880Sbapt * If there is at least one digit, return success and advance the
1992274880Sbapt * parse point, else return failure and let the parse point unchanged.
1993274880Sbapt * Ignore overflows, treat them just like the C language.
1994274880Sbapt */
1995261344Suqsstatic int
1996279527Sbaptroff_getnum(const char *v, int *pos, int *res, int flags)
1997261344Suqs{
1998279527Sbapt	int	 myres, scaled, n, p;
1999261344Suqs
2000274880Sbapt	if (NULL == res)
2001274880Sbapt		res = &myres;
2002274880Sbapt
2003261344Suqs	p = *pos;
2004261344Suqs	n = v[p] == '-';
2005279527Sbapt	if (n || v[p] == '+')
2006261344Suqs		p++;
2007261344Suqs
2008279527Sbapt	if (flags & ROFFNUM_WHITE)
2009279527Sbapt		while (isspace((unsigned char)v[p]))
2010279527Sbapt			p++;
2011279527Sbapt
2012261344Suqs	for (*res = 0; isdigit((unsigned char)v[p]); p++)
2013274880Sbapt		*res = 10 * *res + v[p] - '0';
2014261344Suqs	if (p == *pos + n)
2015261344Suqs		return 0;
2016261344Suqs
2017261344Suqs	if (n)
2018261344Suqs		*res = -*res;
2019261344Suqs
2020279527Sbapt	/* Each number may be followed by one optional scaling unit. */
2021279527Sbapt
2022279527Sbapt	switch (v[p]) {
2023279527Sbapt	case 'f':
2024279527Sbapt		scaled = *res * 65536;
2025279527Sbapt		break;
2026279527Sbapt	case 'i':
2027279527Sbapt		scaled = *res * 240;
2028279527Sbapt		break;
2029279527Sbapt	case 'c':
2030279527Sbapt		scaled = *res * 240 / 2.54;
2031279527Sbapt		break;
2032279527Sbapt	case 'v':
2033279527Sbapt	case 'P':
2034279527Sbapt		scaled = *res * 40;
2035279527Sbapt		break;
2036279527Sbapt	case 'm':
2037279527Sbapt	case 'n':
2038279527Sbapt		scaled = *res * 24;
2039279527Sbapt		break;
2040279527Sbapt	case 'p':
2041279527Sbapt		scaled = *res * 10 / 3;
2042279527Sbapt		break;
2043279527Sbapt	case 'u':
2044279527Sbapt		scaled = *res;
2045279527Sbapt		break;
2046279527Sbapt	case 'M':
2047279527Sbapt		scaled = *res * 6 / 25;
2048279527Sbapt		break;
2049279527Sbapt	default:
2050279527Sbapt		scaled = *res;
2051279527Sbapt		p--;
2052279527Sbapt		break;
2053279527Sbapt	}
2054279527Sbapt	if (flags & ROFFNUM_SCALE)
2055279527Sbapt		*res = scaled;
2056279527Sbapt
2057279527Sbapt	*pos = p + 1;
2058294113Sbapt	return 1;
2059261344Suqs}
2060261344Suqs
2061274880Sbapt/*
2062274880Sbapt * Evaluate a string comparison condition.
2063274880Sbapt * The first character is the delimiter.
2064274880Sbapt * Succeed if the string up to its second occurrence
2065274880Sbapt * matches the string up to its third occurence.
2066274880Sbapt * Advance the cursor after the third occurrence
2067274880Sbapt * or lacking that, to the end of the line.
2068274880Sbapt */
2069261344Suqsstatic int
2070274880Sbaptroff_evalstrcond(const char *v, int *pos)
2071261344Suqs{
2072274880Sbapt	const char	*s1, *s2, *s3;
2073274880Sbapt	int		 match;
2074261344Suqs
2075274880Sbapt	match = 0;
2076274880Sbapt	s1 = v + *pos;		/* initial delimiter */
2077274880Sbapt	s2 = s1 + 1;		/* for scanning the first string */
2078274880Sbapt	s3 = strchr(s2, *s1);	/* for scanning the second string */
2079261344Suqs
2080274880Sbapt	if (NULL == s3)		/* found no middle delimiter */
2081274880Sbapt		goto out;
2082274880Sbapt
2083274880Sbapt	while ('\0' != *++s3) {
2084274880Sbapt		if (*s2 != *s3) {  /* mismatch */
2085274880Sbapt			s3 = strchr(s3, *s1);
2086274880Sbapt			break;
2087274880Sbapt		}
2088274880Sbapt		if (*s3 == *s1) {  /* found the final delimiter */
2089274880Sbapt			match = 1;
2090274880Sbapt			break;
2091274880Sbapt		}
2092274880Sbapt		s2++;
2093261344Suqs	}
2094261344Suqs
2095274880Sbaptout:
2096274880Sbapt	if (NULL == s3)
2097274880Sbapt		s3 = strchr(s2, '\0');
2098279527Sbapt	else if (*s3 != '\0')
2099274880Sbapt		s3++;
2100274880Sbapt	*pos = s3 - v;
2101294113Sbapt	return match;
2102261344Suqs}
2103261344Suqs
2104274880Sbapt/*
2105274880Sbapt * Evaluate an optionally negated single character, numerical,
2106274880Sbapt * or string condition.
2107274880Sbapt */
2108274880Sbaptstatic int
2109294113Sbaptroff_evalcond(struct roff *r, int ln, char *v, int *pos)
2110241675Suqs{
2111294113Sbapt	char	*cp, *name;
2112294113Sbapt	size_t	 sz;
2113322249Sbapt	int	 deftype, number, savepos, istrue, wanttrue;
2114241675Suqs
2115274880Sbapt	if ('!' == v[*pos]) {
2116274880Sbapt		wanttrue = 0;
2117274880Sbapt		(*pos)++;
2118274880Sbapt	} else
2119274880Sbapt		wanttrue = 1;
2120274880Sbapt
2121241675Suqs	switch (v[*pos]) {
2122279527Sbapt	case '\0':
2123294113Sbapt		return 0;
2124274880Sbapt	case 'n':
2125274880Sbapt	case 'o':
2126241675Suqs		(*pos)++;
2127294113Sbapt		return wanttrue;
2128274880Sbapt	case 'c':
2129274880Sbapt	case 'e':
2130274880Sbapt	case 't':
2131275432Sbapt	case 'v':
2132241675Suqs		(*pos)++;
2133294113Sbapt		return !wanttrue;
2134322249Sbapt	case 'd':
2135294113Sbapt	case 'r':
2136322249Sbapt		cp = v + *pos + 1;
2137322249Sbapt		while (*cp == ' ')
2138322249Sbapt			cp++;
2139322249Sbapt		name = cp;
2140322249Sbapt		sz = roff_getname(r, &cp, ln, cp - v);
2141322249Sbapt		if (sz == 0)
2142322249Sbapt			istrue = 0;
2143322249Sbapt		else if (v[*pos] == 'r')
2144322249Sbapt			istrue = roff_hasregn(r, name, sz);
2145322249Sbapt		else {
2146322249Sbapt			deftype = ROFFDEF_ANY;
2147322249Sbapt		        roff_getstrn(r, name, sz, &deftype);
2148322249Sbapt			istrue = !!deftype;
2149322249Sbapt		}
2150294113Sbapt		*pos = cp - v;
2151322249Sbapt		return istrue == wanttrue;
2152241675Suqs	default:
2153241675Suqs		break;
2154241675Suqs	}
2155241675Suqs
2156279527Sbapt	savepos = *pos;
2157279527Sbapt	if (roff_evalnum(r, ln, v, pos, &number, ROFFNUM_SCALE))
2158294113Sbapt		return (number > 0) == wanttrue;
2159279527Sbapt	else if (*pos == savepos)
2160294113Sbapt		return roff_evalstrcond(v, pos) == wanttrue;
2161274880Sbapt	else
2162294113Sbapt		return 0;
2163241675Suqs}
2164241675Suqs
2165241675Suqsstatic enum rofferr
2166241675Suqsroff_line_ignore(ROFF_ARGS)
2167241675Suqs{
2168241675Suqs
2169294113Sbapt	return ROFF_IGN;
2170241675Suqs}
2171241675Suqs
2172241675Suqsstatic enum rofferr
2173279527Sbaptroff_insec(ROFF_ARGS)
2174279527Sbapt{
2175279527Sbapt
2176279527Sbapt	mandoc_msg(MANDOCERR_REQ_INSEC, r->parse,
2177322249Sbapt	    ln, ppos, roff_name[tok]);
2178294113Sbapt	return ROFF_IGN;
2179279527Sbapt}
2180279527Sbapt
2181279527Sbaptstatic enum rofferr
2182279527Sbaptroff_unsupp(ROFF_ARGS)
2183279527Sbapt{
2184279527Sbapt
2185279527Sbapt	mandoc_msg(MANDOCERR_REQ_UNSUPP, r->parse,
2186322249Sbapt	    ln, ppos, roff_name[tok]);
2187294113Sbapt	return ROFF_IGN;
2188279527Sbapt}
2189279527Sbapt
2190279527Sbaptstatic enum rofferr
2191241675Suqsroff_cond(ROFF_ARGS)
2192241675Suqs{
2193241675Suqs
2194261344Suqs	roffnode_push(r, tok, NULL, ln, ppos);
2195261344Suqs
2196274880Sbapt	/*
2197241675Suqs	 * An `.el' has no conditional body: it will consume the value
2198241675Suqs	 * of the current rstack entry set in prior `ie' calls or
2199274880Sbapt	 * defaults to DENY.
2200241675Suqs	 *
2201241675Suqs	 * If we're not an `el', however, then evaluate the conditional.
2202241675Suqs	 */
2203241675Suqs
2204275432Sbapt	r->last->rule = tok == ROFF_el ?
2205274880Sbapt	    (r->rstackpos < 0 ? 0 : r->rstack[r->rstackpos--]) :
2206275432Sbapt	    roff_evalcond(r, ln, buf->buf, &pos);
2207241675Suqs
2208241675Suqs	/*
2209241675Suqs	 * An if-else will put the NEGATION of the current evaluated
2210241675Suqs	 * conditional into the stack of rules.
2211241675Suqs	 */
2212241675Suqs
2213275432Sbapt	if (tok == ROFF_ie) {
2214274880Sbapt		if (r->rstackpos + 1 == r->rstacksz) {
2215274880Sbapt			r->rstacksz += 16;
2216274880Sbapt			r->rstack = mandoc_reallocarray(r->rstack,
2217274880Sbapt			    r->rstacksz, sizeof(int));
2218241675Suqs		}
2219274880Sbapt		r->rstack[++r->rstackpos] = !r->last->rule;
2220241675Suqs	}
2221241675Suqs
2222241675Suqs	/* If the parent has false as its rule, then so do we. */
2223241675Suqs
2224274880Sbapt	if (r->last->parent && !r->last->parent->rule)
2225274880Sbapt		r->last->rule = 0;
2226241675Suqs
2227241675Suqs	/*
2228261344Suqs	 * Determine scope.
2229261344Suqs	 * If there is nothing on the line after the conditional,
2230261344Suqs	 * not even whitespace, use next-line scope.
2231241675Suqs	 */
2232241675Suqs
2233275432Sbapt	if (buf->buf[pos] == '\0') {
2234261344Suqs		r->last->endspan = 2;
2235261344Suqs		goto out;
2236261344Suqs	}
2237241675Suqs
2238275432Sbapt	while (buf->buf[pos] == ' ')
2239261344Suqs		pos++;
2240261344Suqs
2241261344Suqs	/* An opening brace requests multiline scope. */
2242261344Suqs
2243275432Sbapt	if (buf->buf[pos] == '\\' && buf->buf[pos + 1] == '{') {
2244241675Suqs		r->last->endspan = -1;
2245241675Suqs		pos += 2;
2246294113Sbapt		while (buf->buf[pos] == ' ')
2247294113Sbapt			pos++;
2248261344Suqs		goto out;
2249274880Sbapt	}
2250241675Suqs
2251241675Suqs	/*
2252261344Suqs	 * Anything else following the conditional causes
2253261344Suqs	 * single-line scope.  Warn if the scope contains
2254261344Suqs	 * nothing but trailing whitespace.
2255241675Suqs	 */
2256241675Suqs
2257275432Sbapt	if (buf->buf[pos] == '\0')
2258274880Sbapt		mandoc_msg(MANDOCERR_COND_EMPTY, r->parse,
2259322249Sbapt		    ln, ppos, roff_name[tok]);
2260241675Suqs
2261261344Suqs	r->last->endspan = 1;
2262241675Suqs
2263261344Suqsout:
2264241675Suqs	*offs = pos;
2265294113Sbapt	return ROFF_RERUN;
2266241675Suqs}
2267241675Suqs
2268241675Suqsstatic enum rofferr
2269241675Suqsroff_ds(ROFF_ARGS)
2270241675Suqs{
2271274880Sbapt	char		*string;
2272274880Sbapt	const char	*name;
2273274880Sbapt	size_t		 namesz;
2274241675Suqs
2275279527Sbapt	/* Ignore groff compatibility mode for now. */
2276279527Sbapt
2277279527Sbapt	if (tok == ROFF_ds1)
2278279527Sbapt		tok = ROFF_ds;
2279279527Sbapt	else if (tok == ROFF_as1)
2280279527Sbapt		tok = ROFF_as;
2281279527Sbapt
2282241675Suqs	/*
2283274880Sbapt	 * The first word is the name of the string.
2284274880Sbapt	 * If it is empty or terminated by an escape sequence,
2285274880Sbapt	 * abort the `ds' request without defining anything.
2286241675Suqs	 */
2287241675Suqs
2288275432Sbapt	name = string = buf->buf + pos;
2289275432Sbapt	if (*name == '\0')
2290294113Sbapt		return ROFF_IGN;
2291241675Suqs
2292274880Sbapt	namesz = roff_getname(r, &string, ln, pos);
2293275432Sbapt	if (name[namesz] == '\\')
2294294113Sbapt		return ROFF_IGN;
2295274880Sbapt
2296274880Sbapt	/* Read past the initial double-quote, if any. */
2297275432Sbapt	if (*string == '"')
2298241675Suqs		string++;
2299241675Suqs
2300241675Suqs	/* The rest is the value. */
2301274880Sbapt	roff_setstrn(&r->strtab, name, namesz, string, strlen(string),
2302274880Sbapt	    ROFF_as == tok);
2303322249Sbapt	roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
2304294113Sbapt	return ROFF_IGN;
2305241675Suqs}
2306241675Suqs
2307274880Sbapt/*
2308274880Sbapt * Parse a single operator, one or two characters long.
2309274880Sbapt * If the operator is recognized, return success and advance the
2310274880Sbapt * parse point, else return failure and let the parse point unchanged.
2311274880Sbapt */
2312274880Sbaptstatic int
2313274880Sbaptroff_getop(const char *v, int *pos, char *res)
2314274880Sbapt{
2315274880Sbapt
2316274880Sbapt	*res = v[*pos];
2317274880Sbapt
2318274880Sbapt	switch (*res) {
2319274880Sbapt	case '+':
2320274880Sbapt	case '-':
2321274880Sbapt	case '*':
2322274880Sbapt	case '/':
2323274880Sbapt	case '%':
2324274880Sbapt	case '&':
2325274880Sbapt	case ':':
2326274880Sbapt		break;
2327274880Sbapt	case '<':
2328274880Sbapt		switch (v[*pos + 1]) {
2329274880Sbapt		case '=':
2330274880Sbapt			*res = 'l';
2331274880Sbapt			(*pos)++;
2332274880Sbapt			break;
2333274880Sbapt		case '>':
2334274880Sbapt			*res = '!';
2335274880Sbapt			(*pos)++;
2336274880Sbapt			break;
2337274880Sbapt		case '?':
2338274880Sbapt			*res = 'i';
2339274880Sbapt			(*pos)++;
2340274880Sbapt			break;
2341274880Sbapt		default:
2342274880Sbapt			break;
2343274880Sbapt		}
2344274880Sbapt		break;
2345274880Sbapt	case '>':
2346274880Sbapt		switch (v[*pos + 1]) {
2347274880Sbapt		case '=':
2348274880Sbapt			*res = 'g';
2349274880Sbapt			(*pos)++;
2350274880Sbapt			break;
2351274880Sbapt		case '?':
2352274880Sbapt			*res = 'a';
2353274880Sbapt			(*pos)++;
2354274880Sbapt			break;
2355274880Sbapt		default:
2356274880Sbapt			break;
2357274880Sbapt		}
2358274880Sbapt		break;
2359274880Sbapt	case '=':
2360274880Sbapt		if ('=' == v[*pos + 1])
2361274880Sbapt			(*pos)++;
2362274880Sbapt		break;
2363274880Sbapt	default:
2364294113Sbapt		return 0;
2365274880Sbapt	}
2366274880Sbapt	(*pos)++;
2367274880Sbapt
2368294113Sbapt	return *res;
2369274880Sbapt}
2370274880Sbapt
2371274880Sbapt/*
2372274880Sbapt * Evaluate either a parenthesized numeric expression
2373274880Sbapt * or a single signed integer number.
2374274880Sbapt */
2375274880Sbaptstatic int
2376275432Sbaptroff_evalpar(struct roff *r, int ln,
2377279527Sbapt	const char *v, int *pos, int *res, int flags)
2378274880Sbapt{
2379274880Sbapt
2380274880Sbapt	if ('(' != v[*pos])
2381294113Sbapt		return roff_getnum(v, pos, res, flags);
2382274880Sbapt
2383274880Sbapt	(*pos)++;
2384279527Sbapt	if ( ! roff_evalnum(r, ln, v, pos, res, flags | ROFFNUM_WHITE))
2385294113Sbapt		return 0;
2386274880Sbapt
2387274880Sbapt	/*
2388274880Sbapt	 * Omission of the closing parenthesis
2389274880Sbapt	 * is an error in validation mode,
2390274880Sbapt	 * but ignored in evaluation mode.
2391274880Sbapt	 */
2392274880Sbapt
2393274880Sbapt	if (')' == v[*pos])
2394274880Sbapt		(*pos)++;
2395274880Sbapt	else if (NULL == res)
2396294113Sbapt		return 0;
2397274880Sbapt
2398294113Sbapt	return 1;
2399274880Sbapt}
2400274880Sbapt
2401274880Sbapt/*
2402274880Sbapt * Evaluate a complete numeric expression.
2403274880Sbapt * Proceed left to right, there is no concept of precedence.
2404274880Sbapt */
2405274880Sbaptstatic int
2406275432Sbaptroff_evalnum(struct roff *r, int ln, const char *v,
2407279527Sbapt	int *pos, int *res, int flags)
2408274880Sbapt{
2409274880Sbapt	int		 mypos, operand2;
2410274880Sbapt	char		 operator;
2411274880Sbapt
2412274880Sbapt	if (NULL == pos) {
2413274880Sbapt		mypos = 0;
2414274880Sbapt		pos = &mypos;
2415274880Sbapt	}
2416274880Sbapt
2417279527Sbapt	if (flags & ROFFNUM_WHITE)
2418274880Sbapt		while (isspace((unsigned char)v[*pos]))
2419274880Sbapt			(*pos)++;
2420274880Sbapt
2421279527Sbapt	if ( ! roff_evalpar(r, ln, v, pos, res, flags))
2422294113Sbapt		return 0;
2423274880Sbapt
2424274880Sbapt	while (1) {
2425279527Sbapt		if (flags & ROFFNUM_WHITE)
2426274880Sbapt			while (isspace((unsigned char)v[*pos]))
2427274880Sbapt				(*pos)++;
2428274880Sbapt
2429274880Sbapt		if ( ! roff_getop(v, pos, &operator))
2430274880Sbapt			break;
2431274880Sbapt
2432279527Sbapt		if (flags & ROFFNUM_WHITE)
2433274880Sbapt			while (isspace((unsigned char)v[*pos]))
2434274880Sbapt				(*pos)++;
2435274880Sbapt
2436279527Sbapt		if ( ! roff_evalpar(r, ln, v, pos, &operand2, flags))
2437294113Sbapt			return 0;
2438274880Sbapt
2439279527Sbapt		if (flags & ROFFNUM_WHITE)
2440274880Sbapt			while (isspace((unsigned char)v[*pos]))
2441274880Sbapt				(*pos)++;
2442274880Sbapt
2443274880Sbapt		if (NULL == res)
2444274880Sbapt			continue;
2445274880Sbapt
2446274880Sbapt		switch (operator) {
2447274880Sbapt		case '+':
2448274880Sbapt			*res += operand2;
2449274880Sbapt			break;
2450274880Sbapt		case '-':
2451274880Sbapt			*res -= operand2;
2452274880Sbapt			break;
2453274880Sbapt		case '*':
2454274880Sbapt			*res *= operand2;
2455274880Sbapt			break;
2456274880Sbapt		case '/':
2457279527Sbapt			if (operand2 == 0) {
2458275432Sbapt				mandoc_msg(MANDOCERR_DIVZERO,
2459275432Sbapt					r->parse, ln, *pos, v);
2460275432Sbapt				*res = 0;
2461275432Sbapt				break;
2462275432Sbapt			}
2463274880Sbapt			*res /= operand2;
2464274880Sbapt			break;
2465274880Sbapt		case '%':
2466279527Sbapt			if (operand2 == 0) {
2467279527Sbapt				mandoc_msg(MANDOCERR_DIVZERO,
2468279527Sbapt					r->parse, ln, *pos, v);
2469279527Sbapt				*res = 0;
2470279527Sbapt				break;
2471279527Sbapt			}
2472274880Sbapt			*res %= operand2;
2473274880Sbapt			break;
2474274880Sbapt		case '<':
2475274880Sbapt			*res = *res < operand2;
2476274880Sbapt			break;
2477274880Sbapt		case '>':
2478274880Sbapt			*res = *res > operand2;
2479274880Sbapt			break;
2480274880Sbapt		case 'l':
2481274880Sbapt			*res = *res <= operand2;
2482274880Sbapt			break;
2483274880Sbapt		case 'g':
2484274880Sbapt			*res = *res >= operand2;
2485274880Sbapt			break;
2486274880Sbapt		case '=':
2487274880Sbapt			*res = *res == operand2;
2488274880Sbapt			break;
2489274880Sbapt		case '!':
2490274880Sbapt			*res = *res != operand2;
2491274880Sbapt			break;
2492274880Sbapt		case '&':
2493274880Sbapt			*res = *res && operand2;
2494274880Sbapt			break;
2495274880Sbapt		case ':':
2496274880Sbapt			*res = *res || operand2;
2497274880Sbapt			break;
2498274880Sbapt		case 'i':
2499274880Sbapt			if (operand2 < *res)
2500274880Sbapt				*res = operand2;
2501274880Sbapt			break;
2502274880Sbapt		case 'a':
2503274880Sbapt			if (operand2 > *res)
2504274880Sbapt				*res = operand2;
2505274880Sbapt			break;
2506274880Sbapt		default:
2507274880Sbapt			abort();
2508274880Sbapt		}
2509274880Sbapt	}
2510294113Sbapt	return 1;
2511274880Sbapt}
2512274880Sbapt
2513294113Sbapt/* --- register management ------------------------------------------------ */
2514294113Sbapt
2515261344Suqsvoid
2516261344Suqsroff_setreg(struct roff *r, const char *name, int val, char sign)
2517261344Suqs{
2518261344Suqs	struct roffreg	*reg;
2519261344Suqs
2520261344Suqs	/* Search for an existing register with the same name. */
2521261344Suqs	reg = r->regtab;
2522261344Suqs
2523261344Suqs	while (reg && strcmp(name, reg->key.p))
2524261344Suqs		reg = reg->next;
2525261344Suqs
2526261344Suqs	if (NULL == reg) {
2527261344Suqs		/* Create a new register. */
2528261344Suqs		reg = mandoc_malloc(sizeof(struct roffreg));
2529261344Suqs		reg->key.p = mandoc_strdup(name);
2530261344Suqs		reg->key.sz = strlen(name);
2531261344Suqs		reg->val = 0;
2532261344Suqs		reg->next = r->regtab;
2533261344Suqs		r->regtab = reg;
2534261344Suqs	}
2535261344Suqs
2536261344Suqs	if ('+' == sign)
2537261344Suqs		reg->val += val;
2538261344Suqs	else if ('-' == sign)
2539261344Suqs		reg->val -= val;
2540261344Suqs	else
2541261344Suqs		reg->val = val;
2542261344Suqs}
2543261344Suqs
2544274880Sbapt/*
2545274880Sbapt * Handle some predefined read-only number registers.
2546274880Sbapt * For now, return -1 if the requested register is not predefined;
2547274880Sbapt * in case a predefined read-only register having the value -1
2548274880Sbapt * were to turn up, another special value would have to be chosen.
2549274880Sbapt */
2550274880Sbaptstatic int
2551294113Sbaptroff_getregro(const struct roff *r, const char *name)
2552274880Sbapt{
2553274880Sbapt
2554274880Sbapt	switch (*name) {
2555294113Sbapt	case '$':  /* Number of arguments of the last macro evaluated. */
2556294113Sbapt		return r->argc;
2557274880Sbapt	case 'A':  /* ASCII approximation mode is always off. */
2558294113Sbapt		return 0;
2559274880Sbapt	case 'g':  /* Groff compatibility mode is always on. */
2560294113Sbapt		return 1;
2561274880Sbapt	case 'H':  /* Fixed horizontal resolution. */
2562294113Sbapt		return 24;
2563274880Sbapt	case 'j':  /* Always adjust left margin only. */
2564294113Sbapt		return 0;
2565274880Sbapt	case 'T':  /* Some output device is always defined. */
2566294113Sbapt		return 1;
2567274880Sbapt	case 'V':  /* Fixed vertical resolution. */
2568294113Sbapt		return 40;
2569274880Sbapt	default:
2570294113Sbapt		return -1;
2571274880Sbapt	}
2572274880Sbapt}
2573274880Sbapt
2574241675Suqsint
2575261344Suqsroff_getreg(const struct roff *r, const char *name)
2576241675Suqs{
2577261344Suqs	struct roffreg	*reg;
2578274880Sbapt	int		 val;
2579241675Suqs
2580274880Sbapt	if ('.' == name[0] && '\0' != name[1] && '\0' == name[2]) {
2581294113Sbapt		val = roff_getregro(r, name + 1);
2582274880Sbapt		if (-1 != val)
2583294113Sbapt			return val;
2584274880Sbapt	}
2585274880Sbapt
2586261344Suqs	for (reg = r->regtab; reg; reg = reg->next)
2587261344Suqs		if (0 == strcmp(name, reg->key.p))
2588294113Sbapt			return reg->val;
2589261344Suqs
2590294113Sbapt	return 0;
2591241675Suqs}
2592241675Suqs
2593261344Suqsstatic int
2594261344Suqsroff_getregn(const struct roff *r, const char *name, size_t len)
2595241675Suqs{
2596261344Suqs	struct roffreg	*reg;
2597274880Sbapt	int		 val;
2598241675Suqs
2599274880Sbapt	if ('.' == name[0] && 2 == len) {
2600294113Sbapt		val = roff_getregro(r, name + 1);
2601274880Sbapt		if (-1 != val)
2602294113Sbapt			return val;
2603274880Sbapt	}
2604274880Sbapt
2605261344Suqs	for (reg = r->regtab; reg; reg = reg->next)
2606261344Suqs		if (len == reg->key.sz &&
2607261344Suqs		    0 == strncmp(name, reg->key.p, len))
2608294113Sbapt			return reg->val;
2609261344Suqs
2610294113Sbapt	return 0;
2611241675Suqs}
2612241675Suqs
2613294113Sbaptstatic int
2614294113Sbaptroff_hasregn(const struct roff *r, const char *name, size_t len)
2615294113Sbapt{
2616294113Sbapt	struct roffreg	*reg;
2617294113Sbapt	int		 val;
2618294113Sbapt
2619294113Sbapt	if ('.' == name[0] && 2 == len) {
2620294113Sbapt		val = roff_getregro(r, name + 1);
2621294113Sbapt		if (-1 != val)
2622294113Sbapt			return 1;
2623294113Sbapt	}
2624294113Sbapt
2625294113Sbapt	for (reg = r->regtab; reg; reg = reg->next)
2626294113Sbapt		if (len == reg->key.sz &&
2627294113Sbapt		    0 == strncmp(name, reg->key.p, len))
2628294113Sbapt			return 1;
2629294113Sbapt
2630294113Sbapt	return 0;
2631294113Sbapt}
2632294113Sbapt
2633261344Suqsstatic void
2634261344Suqsroff_freereg(struct roffreg *reg)
2635241675Suqs{
2636261344Suqs	struct roffreg	*old_reg;
2637241675Suqs
2638261344Suqs	while (NULL != reg) {
2639261344Suqs		free(reg->key.p);
2640261344Suqs		old_reg = reg;
2641261344Suqs		reg = reg->next;
2642261344Suqs		free(old_reg);
2643261344Suqs	}
2644241675Suqs}
2645241675Suqs
2646241675Suqsstatic enum rofferr
2647241675Suqsroff_nr(ROFF_ARGS)
2648241675Suqs{
2649274880Sbapt	char		*key, *val;
2650274880Sbapt	size_t		 keysz;
2651241675Suqs	int		 iv;
2652261344Suqs	char		 sign;
2653241675Suqs
2654275432Sbapt	key = val = buf->buf + pos;
2655275432Sbapt	if (*key == '\0')
2656294113Sbapt		return ROFF_IGN;
2657241675Suqs
2658274880Sbapt	keysz = roff_getname(r, &val, ln, pos);
2659275432Sbapt	if (key[keysz] == '\\')
2660294113Sbapt		return ROFF_IGN;
2661274880Sbapt	key[keysz] = '\0';
2662274880Sbapt
2663261344Suqs	sign = *val;
2664275432Sbapt	if (sign == '+' || sign == '-')
2665261344Suqs		val++;
2666241675Suqs
2667279527Sbapt	if (roff_evalnum(r, ln, val, NULL, &iv, ROFFNUM_SCALE))
2668274880Sbapt		roff_setreg(r, key, iv, sign);
2669261344Suqs
2670294113Sbapt	return ROFF_IGN;
2671274880Sbapt}
2672261344Suqs
2673274880Sbaptstatic enum rofferr
2674274880Sbaptroff_rr(ROFF_ARGS)
2675274880Sbapt{
2676274880Sbapt	struct roffreg	*reg, **prev;
2677274880Sbapt	char		*name, *cp;
2678274880Sbapt	size_t		 namesz;
2679274880Sbapt
2680275432Sbapt	name = cp = buf->buf + pos;
2681275432Sbapt	if (*name == '\0')
2682294113Sbapt		return ROFF_IGN;
2683274880Sbapt	namesz = roff_getname(r, &cp, ln, pos);
2684274880Sbapt	name[namesz] = '\0';
2685274880Sbapt
2686274880Sbapt	prev = &r->regtab;
2687274880Sbapt	while (1) {
2688274880Sbapt		reg = *prev;
2689275432Sbapt		if (reg == NULL || !strcmp(name, reg->key.p))
2690274880Sbapt			break;
2691274880Sbapt		prev = &reg->next;
2692274880Sbapt	}
2693275432Sbapt	if (reg != NULL) {
2694274880Sbapt		*prev = reg->next;
2695274880Sbapt		free(reg->key.p);
2696274880Sbapt		free(reg);
2697274880Sbapt	}
2698294113Sbapt	return ROFF_IGN;
2699241675Suqs}
2700241675Suqs
2701294113Sbapt/* --- handler functions for roff requests -------------------------------- */
2702294113Sbapt
2703241675Suqsstatic enum rofferr
2704241675Suqsroff_rm(ROFF_ARGS)
2705241675Suqs{
2706241675Suqs	const char	 *name;
2707241675Suqs	char		 *cp;
2708274880Sbapt	size_t		  namesz;
2709241675Suqs
2710275432Sbapt	cp = buf->buf + pos;
2711275432Sbapt	while (*cp != '\0') {
2712274880Sbapt		name = cp;
2713275432Sbapt		namesz = roff_getname(r, &cp, ln, (int)(cp - buf->buf));
2714274880Sbapt		roff_setstrn(&r->strtab, name, namesz, NULL, 0, 0);
2715322249Sbapt		roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
2716275432Sbapt		if (name[namesz] == '\\')
2717274880Sbapt			break;
2718241675Suqs	}
2719294113Sbapt	return ROFF_IGN;
2720241675Suqs}
2721241675Suqs
2722241675Suqsstatic enum rofferr
2723261344Suqsroff_it(ROFF_ARGS)
2724261344Suqs{
2725261344Suqs	int		 iv;
2726261344Suqs
2727261344Suqs	/* Parse the number of lines. */
2728279527Sbapt
2729279527Sbapt	if ( ! roff_evalnum(r, ln, buf->buf, &pos, &iv, 0)) {
2730274880Sbapt		mandoc_msg(MANDOCERR_IT_NONUM, r->parse,
2731275432Sbapt		    ln, ppos, buf->buf + 1);
2732294113Sbapt		return ROFF_IGN;
2733261344Suqs	}
2734261344Suqs
2735279527Sbapt	while (isspace((unsigned char)buf->buf[pos]))
2736279527Sbapt		pos++;
2737279527Sbapt
2738279527Sbapt	/*
2739279527Sbapt	 * Arm the input line trap.
2740279527Sbapt	 * Special-casing "an-trap" is an ugly workaround to cope
2741279527Sbapt	 * with DocBook stupidly fiddling with man(7) internals.
2742279527Sbapt	 */
2743279527Sbapt
2744261344Suqs	roffit_lines = iv;
2745279527Sbapt	roffit_macro = mandoc_strdup(iv != 1 ||
2746279527Sbapt	    strcmp(buf->buf + pos, "an-trap") ?
2747279527Sbapt	    buf->buf + pos : "br");
2748294113Sbapt	return ROFF_IGN;
2749261344Suqs}
2750261344Suqs
2751261344Suqsstatic enum rofferr
2752261344Suqsroff_Dd(ROFF_ARGS)
2753261344Suqs{
2754322249Sbapt	int		 mask;
2755322249Sbapt	enum roff_tok	 t, te;
2756261344Suqs
2757322249Sbapt	switch (tok) {
2758322249Sbapt	case ROFF_Dd:
2759322249Sbapt		tok = MDOC_Dd;
2760322249Sbapt		te = MDOC_MAX;
2761322249Sbapt		if (r->format == 0)
2762322249Sbapt			r->format = MPARSE_MDOC;
2763322249Sbapt		mask = MPARSE_MDOC | MPARSE_QUICK;
2764322249Sbapt		break;
2765322249Sbapt	case ROFF_TH:
2766322249Sbapt		tok = MAN_TH;
2767322249Sbapt		te = MAN_MAX;
2768322249Sbapt		if (r->format == 0)
2769322249Sbapt			r->format = MPARSE_MAN;
2770322249Sbapt		mask = MPARSE_QUICK;
2771322249Sbapt		break;
2772322249Sbapt	default:
2773322249Sbapt		abort();
2774322249Sbapt	}
2775322249Sbapt	if ((r->options & mask) == 0)
2776322249Sbapt		for (t = tok; t < te; t++)
2777322249Sbapt			roff_setstr(r, roff_name[t], NULL, 0);
2778294113Sbapt	return ROFF_CONT;
2779261344Suqs}
2780261344Suqs
2781261344Suqsstatic enum rofferr
2782241675Suqsroff_TE(ROFF_ARGS)
2783241675Suqs{
2784322249Sbapt	if (r->tbl == NULL) {
2785274880Sbapt		mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
2786274880Sbapt		    ln, ppos, "TE");
2787322249Sbapt		return ROFF_IGN;
2788322249Sbapt	}
2789322249Sbapt	if (tbl_end(r->tbl) == 0) {
2790322249Sbapt		r->tbl = NULL;
2791279527Sbapt		free(buf->buf);
2792279527Sbapt		buf->buf = mandoc_strdup(".sp");
2793279527Sbapt		buf->sz = 4;
2794294113Sbapt		return ROFF_REPARSE;
2795279527Sbapt	}
2796322249Sbapt	r->tbl = NULL;
2797294113Sbapt	return ROFF_IGN;
2798241675Suqs}
2799241675Suqs
2800241675Suqsstatic enum rofferr
2801241675Suqsroff_T_(ROFF_ARGS)
2802241675Suqs{
2803241675Suqs
2804241675Suqs	if (NULL == r->tbl)
2805274880Sbapt		mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
2806274880Sbapt		    ln, ppos, "T&");
2807241675Suqs	else
2808322249Sbapt		tbl_restart(ln, ppos, r->tbl);
2809241675Suqs
2810294113Sbapt	return ROFF_IGN;
2811241675Suqs}
2812241675Suqs
2813275432Sbapt/*
2814275432Sbapt * Handle in-line equation delimiters.
2815275432Sbapt */
2816275432Sbaptstatic enum rofferr
2817275432Sbaptroff_eqndelim(struct roff *r, struct buf *buf, int pos)
2818241675Suqs{
2819275432Sbapt	char		*cp1, *cp2;
2820275432Sbapt	const char	*bef_pr, *bef_nl, *mac, *aft_nl, *aft_pr;
2821241675Suqs
2822275432Sbapt	/*
2823275432Sbapt	 * Outside equations, look for an opening delimiter.
2824275432Sbapt	 * If we are inside an equation, we already know it is
2825275432Sbapt	 * in-line, or this function wouldn't have been called;
2826275432Sbapt	 * so look for a closing delimiter.
2827275432Sbapt	 */
2828275432Sbapt
2829275432Sbapt	cp1 = buf->buf + pos;
2830275432Sbapt	cp2 = strchr(cp1, r->eqn == NULL ?
2831275432Sbapt	    r->last_eqn->odelim : r->last_eqn->cdelim);
2832275432Sbapt	if (cp2 == NULL)
2833294113Sbapt		return ROFF_CONT;
2834275432Sbapt
2835275432Sbapt	*cp2++ = '\0';
2836275432Sbapt	bef_pr = bef_nl = aft_nl = aft_pr = "";
2837275432Sbapt
2838275432Sbapt	/* Handle preceding text, protecting whitespace. */
2839275432Sbapt
2840275432Sbapt	if (*buf->buf != '\0') {
2841275432Sbapt		if (r->eqn == NULL)
2842275432Sbapt			bef_pr = "\\&";
2843275432Sbapt		bef_nl = "\n";
2844275432Sbapt	}
2845275432Sbapt
2846275432Sbapt	/*
2847275432Sbapt	 * Prepare replacing the delimiter with an equation macro
2848275432Sbapt	 * and drop leading white space from the equation.
2849275432Sbapt	 */
2850275432Sbapt
2851275432Sbapt	if (r->eqn == NULL) {
2852275432Sbapt		while (*cp2 == ' ')
2853275432Sbapt			cp2++;
2854275432Sbapt		mac = ".EQ";
2855275432Sbapt	} else
2856275432Sbapt		mac = ".EN";
2857275432Sbapt
2858275432Sbapt	/* Handle following text, protecting whitespace. */
2859275432Sbapt
2860275432Sbapt	if (*cp2 != '\0') {
2861275432Sbapt		aft_nl = "\n";
2862275432Sbapt		if (r->eqn != NULL)
2863275432Sbapt			aft_pr = "\\&";
2864275432Sbapt	}
2865275432Sbapt
2866275432Sbapt	/* Do the actual replacement. */
2867275432Sbapt
2868275432Sbapt	buf->sz = mandoc_asprintf(&cp1, "%s%s%s%s%s%s%s", buf->buf,
2869275432Sbapt	    bef_pr, bef_nl, mac, aft_nl, aft_pr, cp2) + 1;
2870275432Sbapt	free(buf->buf);
2871275432Sbapt	buf->buf = cp1;
2872275432Sbapt
2873275432Sbapt	/* Toggle the in-line state of the eqn subsystem. */
2874275432Sbapt
2875275432Sbapt	r->eqn_inline = r->eqn == NULL;
2876294113Sbapt	return ROFF_REPARSE;
2877241675Suqs}
2878241675Suqs
2879275432Sbaptstatic enum rofferr
2880275432Sbaptroff_EQ(ROFF_ARGS)
2881241675Suqs{
2882322249Sbapt	struct roff_node	*n;
2883241675Suqs
2884322249Sbapt	if (r->man->macroset == MACROSET_MAN)
2885322249Sbapt		man_breakscope(r->man, ROFF_EQ);
2886322249Sbapt	n = roff_node_alloc(r->man, ln, ppos, ROFFT_EQN, TOKEN_NONE);
2887322249Sbapt	if (ln > r->man->last->line)
2888322249Sbapt		n->flags |= NODE_LINE;
2889322249Sbapt	n->eqn = mandoc_calloc(1, sizeof(*n->eqn));
2890322249Sbapt	n->eqn->expectargs = UINT_MAX;
2891322249Sbapt	roff_node_append(r->man, n);
2892322249Sbapt	r->man->next = ROFF_NEXT_SIBLING;
2893322249Sbapt
2894275432Sbapt	assert(r->eqn == NULL);
2895322249Sbapt	if (r->last_eqn == NULL)
2896322249Sbapt		r->last_eqn = eqn_alloc(r->parse);
2897322249Sbapt	else
2898322249Sbapt		eqn_reset(r->last_eqn);
2899322249Sbapt	r->eqn = r->last_eqn;
2900322249Sbapt	r->eqn->node = n;
2901241675Suqs
2902275432Sbapt	if (buf->buf[pos] != '\0')
2903275432Sbapt		mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos,
2904275432Sbapt		    ".EQ %s", buf->buf + pos);
2905241675Suqs
2906294113Sbapt	return ROFF_IGN;
2907241675Suqs}
2908241675Suqs
2909241675Suqsstatic enum rofferr
2910241675Suqsroff_EN(ROFF_ARGS)
2911241675Suqs{
2912322249Sbapt	if (r->eqn != NULL) {
2913322249Sbapt		eqn_parse(r->eqn);
2914322249Sbapt		r->eqn = NULL;
2915322249Sbapt	} else
2916322249Sbapt		mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, ln, ppos, "EN");
2917322249Sbapt	if (buf->buf[pos] != '\0')
2918322249Sbapt		mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos,
2919322249Sbapt		    "EN %s", buf->buf + pos);
2920294113Sbapt	return ROFF_IGN;
2921241675Suqs}
2922241675Suqs
2923241675Suqsstatic enum rofferr
2924241675Suqsroff_TS(ROFF_ARGS)
2925241675Suqs{
2926322249Sbapt	if (r->tbl != NULL) {
2927274880Sbapt		mandoc_msg(MANDOCERR_BLK_BROKEN, r->parse,
2928274880Sbapt		    ln, ppos, "TS breaks TS");
2929322249Sbapt		tbl_end(r->tbl);
2930241675Suqs	}
2931322249Sbapt	r->tbl = tbl_alloc(ppos, ln, r->parse);
2932241675Suqs	if (r->last_tbl)
2933322249Sbapt		r->last_tbl->next = r->tbl;
2934241675Suqs	else
2935322249Sbapt		r->first_tbl = r->tbl;
2936322249Sbapt	r->last_tbl = r->tbl;
2937322249Sbapt	return ROFF_IGN;
2938322249Sbapt}
2939241675Suqs
2940322249Sbaptstatic enum rofferr
2941322249Sbaptroff_onearg(ROFF_ARGS)
2942322249Sbapt{
2943322249Sbapt	struct roff_node	*n;
2944322249Sbapt	char			*cp;
2945322249Sbapt	int			 npos;
2946322249Sbapt
2947322249Sbapt	if (r->man->flags & (MAN_BLINE | MAN_ELINE) &&
2948322249Sbapt	    (tok == ROFF_ce || tok == ROFF_rj || tok == ROFF_sp ||
2949322249Sbapt	     tok == ROFF_ti))
2950322249Sbapt		man_breakscope(r->man, tok);
2951322249Sbapt
2952322249Sbapt	if (roffce_node != NULL && (tok == ROFF_ce || tok == ROFF_rj)) {
2953322249Sbapt		r->man->last = roffce_node;
2954322249Sbapt		r->man->next = ROFF_NEXT_SIBLING;
2955322249Sbapt	}
2956322249Sbapt
2957322249Sbapt	roff_elem_alloc(r->man, ln, ppos, tok);
2958322249Sbapt	n = r->man->last;
2959322249Sbapt
2960322249Sbapt	cp = buf->buf + pos;
2961322249Sbapt	if (*cp != '\0') {
2962322249Sbapt		while (*cp != '\0' && *cp != ' ')
2963322249Sbapt			cp++;
2964322249Sbapt		while (*cp == ' ')
2965322249Sbapt			*cp++ = '\0';
2966322249Sbapt		if (*cp != '\0')
2967322249Sbapt			mandoc_vmsg(MANDOCERR_ARG_EXCESS,
2968322249Sbapt			    r->parse, ln, cp - buf->buf,
2969322249Sbapt			    "%s ... %s", roff_name[tok], cp);
2970322249Sbapt		roff_word_alloc(r->man, ln, pos, buf->buf + pos);
2971322249Sbapt	}
2972322249Sbapt
2973322249Sbapt	if (tok == ROFF_ce || tok == ROFF_rj) {
2974322249Sbapt		if (r->man->last->type == ROFFT_ELEM) {
2975322249Sbapt			roff_word_alloc(r->man, ln, pos, "1");
2976322249Sbapt			r->man->last->flags |= NODE_NOSRC;
2977322249Sbapt		}
2978322249Sbapt		npos = 0;
2979322249Sbapt		if (roff_evalnum(r, ln, r->man->last->string, &npos,
2980322249Sbapt		    &roffce_lines, 0) == 0) {
2981322249Sbapt			mandoc_vmsg(MANDOCERR_CE_NONUM,
2982322249Sbapt			    r->parse, ln, pos, "ce %s", buf->buf + pos);
2983322249Sbapt			roffce_lines = 1;
2984322249Sbapt		}
2985322249Sbapt		if (roffce_lines < 1) {
2986322249Sbapt			r->man->last = r->man->last->parent;
2987322249Sbapt			roffce_node = NULL;
2988322249Sbapt			roffce_lines = 0;
2989322249Sbapt		} else
2990322249Sbapt			roffce_node = r->man->last->parent;
2991322249Sbapt	} else {
2992322249Sbapt		n->flags |= NODE_VALID | NODE_ENDED;
2993322249Sbapt		r->man->last = n;
2994322249Sbapt	}
2995322249Sbapt	n->flags |= NODE_LINE;
2996322249Sbapt	r->man->next = ROFF_NEXT_SIBLING;
2997294113Sbapt	return ROFF_IGN;
2998241675Suqs}
2999241675Suqs
3000241675Suqsstatic enum rofferr
3001322249Sbaptroff_manyarg(ROFF_ARGS)
3002279527Sbapt{
3003322249Sbapt	struct roff_node	*n;
3004322249Sbapt	char			*sp, *ep;
3005279527Sbapt
3006322249Sbapt	roff_elem_alloc(r->man, ln, ppos, tok);
3007322249Sbapt	n = r->man->last;
3008322249Sbapt
3009322249Sbapt	for (sp = ep = buf->buf + pos; *sp != '\0'; sp = ep) {
3010322249Sbapt		while (*ep != '\0' && *ep != ' ')
3011322249Sbapt			ep++;
3012322249Sbapt		while (*ep == ' ')
3013322249Sbapt			*ep++ = '\0';
3014322249Sbapt		roff_word_alloc(r->man, ln, sp - buf->buf, sp);
3015322249Sbapt	}
3016322249Sbapt
3017322249Sbapt	n->flags |= NODE_LINE | NODE_VALID | NODE_ENDED;
3018322249Sbapt	r->man->last = n;
3019322249Sbapt	r->man->next = ROFF_NEXT_SIBLING;
3020322249Sbapt	return ROFF_IGN;
3021279527Sbapt}
3022279527Sbapt
3023279527Sbaptstatic enum rofferr
3024322249Sbaptroff_als(ROFF_ARGS)
3025322249Sbapt{
3026322249Sbapt	char		*oldn, *newn, *end, *value;
3027322249Sbapt	size_t		 oldsz, newsz, valsz;
3028322249Sbapt
3029322249Sbapt	newn = oldn = buf->buf + pos;
3030322249Sbapt	if (*newn == '\0')
3031322249Sbapt		return ROFF_IGN;
3032322249Sbapt
3033322249Sbapt	newsz = roff_getname(r, &oldn, ln, pos);
3034322249Sbapt	if (newn[newsz] == '\\' || *oldn == '\0')
3035322249Sbapt		return ROFF_IGN;
3036322249Sbapt
3037322249Sbapt	end = oldn;
3038322249Sbapt	oldsz = roff_getname(r, &end, ln, oldn - buf->buf);
3039322249Sbapt	if (oldsz == 0)
3040322249Sbapt		return ROFF_IGN;
3041322249Sbapt
3042322249Sbapt	valsz = mandoc_asprintf(&value, ".%.*s \\$*\\\"\n",
3043322249Sbapt	    (int)oldsz, oldn);
3044322249Sbapt	roff_setstrn(&r->strtab, newn, newsz, value, valsz, 0);
3045322249Sbapt	roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
3046322249Sbapt	free(value);
3047322249Sbapt	return ROFF_IGN;
3048322249Sbapt}
3049322249Sbapt
3050322249Sbaptstatic enum rofferr
3051322249Sbaptroff_br(ROFF_ARGS)
3052322249Sbapt{
3053322249Sbapt	if (r->man->flags & (MAN_BLINE | MAN_ELINE))
3054322249Sbapt		man_breakscope(r->man, ROFF_br);
3055322249Sbapt	roff_elem_alloc(r->man, ln, ppos, ROFF_br);
3056322249Sbapt	if (buf->buf[pos] != '\0')
3057322249Sbapt		mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos,
3058322249Sbapt		    "%s %s", roff_name[tok], buf->buf + pos);
3059322249Sbapt	r->man->last->flags |= NODE_LINE | NODE_VALID | NODE_ENDED;
3060322249Sbapt	r->man->next = ROFF_NEXT_SIBLING;
3061322249Sbapt	return ROFF_IGN;
3062322249Sbapt}
3063322249Sbapt
3064322249Sbaptstatic enum rofferr
3065261344Suqsroff_cc(ROFF_ARGS)
3066261344Suqs{
3067261344Suqs	const char	*p;
3068261344Suqs
3069275432Sbapt	p = buf->buf + pos;
3070261344Suqs
3071275432Sbapt	if (*p == '\0' || (r->control = *p++) == '.')
3072322249Sbapt		r->control = '\0';
3073261344Suqs
3074275432Sbapt	if (*p != '\0')
3075279527Sbapt		mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse,
3076279527Sbapt		    ln, p - buf->buf, "cc ... %s", p);
3077261344Suqs
3078294113Sbapt	return ROFF_IGN;
3079261344Suqs}
3080261344Suqs
3081261344Suqsstatic enum rofferr
3082322249Sbaptroff_ec(ROFF_ARGS)
3083322249Sbapt{
3084322249Sbapt	const char	*p;
3085322249Sbapt
3086322249Sbapt	p = buf->buf + pos;
3087322249Sbapt	if (*p == '\0')
3088322249Sbapt		r->escape = '\\';
3089322249Sbapt	else {
3090322249Sbapt		r->escape = *p;
3091322249Sbapt		if (*++p != '\0')
3092322249Sbapt			mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse,
3093322249Sbapt			    ln, p - buf->buf, "ec ... %s", p);
3094322249Sbapt	}
3095322249Sbapt	return ROFF_IGN;
3096322249Sbapt}
3097322249Sbapt
3098322249Sbaptstatic enum rofferr
3099322249Sbaptroff_eo(ROFF_ARGS)
3100322249Sbapt{
3101322249Sbapt	r->escape = '\0';
3102322249Sbapt	if (buf->buf[pos] != '\0')
3103322249Sbapt		mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse,
3104322249Sbapt		    ln, pos, "eo %s", buf->buf + pos);
3105322249Sbapt	return ROFF_IGN;
3106322249Sbapt}
3107322249Sbapt
3108322249Sbaptstatic enum rofferr
3109241675Suqsroff_tr(ROFF_ARGS)
3110241675Suqs{
3111241675Suqs	const char	*p, *first, *second;
3112241675Suqs	size_t		 fsz, ssz;
3113241675Suqs	enum mandoc_esc	 esc;
3114241675Suqs
3115275432Sbapt	p = buf->buf + pos;
3116241675Suqs
3117275432Sbapt	if (*p == '\0') {
3118279527Sbapt		mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse, ln, ppos, "tr");
3119294113Sbapt		return ROFF_IGN;
3120241675Suqs	}
3121241675Suqs
3122275432Sbapt	while (*p != '\0') {
3123241675Suqs		fsz = ssz = 1;
3124241675Suqs
3125241675Suqs		first = p++;
3126275432Sbapt		if (*first == '\\') {
3127241675Suqs			esc = mandoc_escape(&p, NULL, NULL);
3128275432Sbapt			if (esc == ESCAPE_ERROR) {
3129274880Sbapt				mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
3130275432Sbapt				    ln, (int)(p - buf->buf), first);
3131294113Sbapt				return ROFF_IGN;
3132241675Suqs			}
3133241675Suqs			fsz = (size_t)(p - first);
3134241675Suqs		}
3135241675Suqs
3136241675Suqs		second = p++;
3137275432Sbapt		if (*second == '\\') {
3138241675Suqs			esc = mandoc_escape(&p, NULL, NULL);
3139275432Sbapt			if (esc == ESCAPE_ERROR) {
3140274880Sbapt				mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
3141275432Sbapt				    ln, (int)(p - buf->buf), second);
3142294113Sbapt				return ROFF_IGN;
3143241675Suqs			}
3144241675Suqs			ssz = (size_t)(p - second);
3145275432Sbapt		} else if (*second == '\0') {
3146279527Sbapt			mandoc_vmsg(MANDOCERR_TR_ODD, r->parse,
3147279527Sbapt			    ln, first - buf->buf, "tr %s", first);
3148241675Suqs			second = " ";
3149241675Suqs			p--;
3150241675Suqs		}
3151241675Suqs
3152241675Suqs		if (fsz > 1) {
3153274880Sbapt			roff_setstrn(&r->xmbtab, first, fsz,
3154274880Sbapt			    second, ssz, 0);
3155241675Suqs			continue;
3156241675Suqs		}
3157241675Suqs
3158275432Sbapt		if (r->xtab == NULL)
3159274880Sbapt			r->xtab = mandoc_calloc(128,
3160274880Sbapt			    sizeof(struct roffstr));
3161241675Suqs
3162241675Suqs		free(r->xtab[(int)*first].p);
3163241675Suqs		r->xtab[(int)*first].p = mandoc_strndup(second, ssz);
3164241675Suqs		r->xtab[(int)*first].sz = ssz;
3165241675Suqs	}
3166241675Suqs
3167294113Sbapt	return ROFF_IGN;
3168241675Suqs}
3169241675Suqs
3170241675Suqsstatic enum rofferr
3171322249Sbaptroff_rn(ROFF_ARGS)
3172322249Sbapt{
3173322249Sbapt	const char	*value;
3174322249Sbapt	char		*oldn, *newn, *end;
3175322249Sbapt	size_t		 oldsz, newsz;
3176322249Sbapt	int		 deftype;
3177322249Sbapt
3178322249Sbapt	oldn = newn = buf->buf + pos;
3179322249Sbapt	if (*oldn == '\0')
3180322249Sbapt		return ROFF_IGN;
3181322249Sbapt
3182322249Sbapt	oldsz = roff_getname(r, &newn, ln, pos);
3183322249Sbapt	if (oldn[oldsz] == '\\' || *newn == '\0')
3184322249Sbapt		return ROFF_IGN;
3185322249Sbapt
3186322249Sbapt	end = newn;
3187322249Sbapt	newsz = roff_getname(r, &end, ln, newn - buf->buf);
3188322249Sbapt	if (newsz == 0)
3189322249Sbapt		return ROFF_IGN;
3190322249Sbapt
3191322249Sbapt	deftype = ROFFDEF_ANY;
3192322249Sbapt	value = roff_getstrn(r, oldn, oldsz, &deftype);
3193322249Sbapt	switch (deftype) {
3194322249Sbapt	case ROFFDEF_USER:
3195322249Sbapt		roff_setstrn(&r->strtab, newn, newsz, value, strlen(value), 0);
3196322249Sbapt		roff_setstrn(&r->strtab, oldn, oldsz, NULL, 0, 0);
3197322249Sbapt		roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
3198322249Sbapt		break;
3199322249Sbapt	case ROFFDEF_PRE:
3200322249Sbapt		roff_setstrn(&r->strtab, newn, newsz, value, strlen(value), 0);
3201322249Sbapt		roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
3202322249Sbapt		break;
3203322249Sbapt	case ROFFDEF_REN:
3204322249Sbapt		roff_setstrn(&r->rentab, newn, newsz, value, strlen(value), 0);
3205322249Sbapt		roff_setstrn(&r->rentab, oldn, oldsz, NULL, 0, 0);
3206322249Sbapt		roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0);
3207322249Sbapt		break;
3208322249Sbapt	case ROFFDEF_STD:
3209322249Sbapt		roff_setstrn(&r->rentab, newn, newsz, oldn, oldsz, 0);
3210322249Sbapt		roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0);
3211322249Sbapt		break;
3212322249Sbapt	default:
3213322249Sbapt		roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0);
3214322249Sbapt		roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
3215322249Sbapt		break;
3216322249Sbapt	}
3217322249Sbapt	return ROFF_IGN;
3218322249Sbapt}
3219322249Sbapt
3220322249Sbaptstatic enum rofferr
3221241675Suqsroff_so(ROFF_ARGS)
3222241675Suqs{
3223279527Sbapt	char *name, *cp;
3224241675Suqs
3225275432Sbapt	name = buf->buf + pos;
3226274880Sbapt	mandoc_vmsg(MANDOCERR_SO, r->parse, ln, ppos, "so %s", name);
3227241675Suqs
3228241675Suqs	/*
3229241675Suqs	 * Handle `so'.  Be EXTREMELY careful, as we shouldn't be
3230241675Suqs	 * opening anything that's not in our cwd or anything beneath
3231241675Suqs	 * it.  Thus, explicitly disallow traversing up the file-system
3232241675Suqs	 * or using absolute paths.
3233241675Suqs	 */
3234241675Suqs
3235275432Sbapt	if (*name == '/' || strstr(name, "../") || strstr(name, "/..")) {
3236274880Sbapt		mandoc_vmsg(MANDOCERR_SO_PATH, r->parse, ln, ppos,
3237274880Sbapt		    ".so %s", name);
3238279527Sbapt		buf->sz = mandoc_asprintf(&cp,
3239279527Sbapt		    ".sp\nSee the file %s.\n.sp", name) + 1;
3240279527Sbapt		free(buf->buf);
3241279527Sbapt		buf->buf = cp;
3242279527Sbapt		*offs = 0;
3243294113Sbapt		return ROFF_REPARSE;
3244241675Suqs	}
3245241675Suqs
3246241675Suqs	*offs = pos;
3247294113Sbapt	return ROFF_SO;
3248241675Suqs}
3249241675Suqs
3250294113Sbapt/* --- user defined strings and macros ------------------------------------ */
3251294113Sbapt
3252241675Suqsstatic enum rofferr
3253241675Suqsroff_userdef(ROFF_ARGS)
3254241675Suqs{
3255322249Sbapt	const char	 *arg[16], *ap;
3256241675Suqs	char		 *cp, *n1, *n2;
3257322249Sbapt	int		  expand_count, i, ib, ie;
3258279527Sbapt	size_t		  asz, rsz;
3259241675Suqs
3260241675Suqs	/*
3261241675Suqs	 * Collect pointers to macro argument strings
3262261344Suqs	 * and NUL-terminate them.
3263241675Suqs	 */
3264279527Sbapt
3265294113Sbapt	r->argc = 0;
3266275432Sbapt	cp = buf->buf + pos;
3267322249Sbapt	for (i = 0; i < 16; i++) {
3268294113Sbapt		if (*cp == '\0')
3269294113Sbapt			arg[i] = "";
3270294113Sbapt		else {
3271294113Sbapt			arg[i] = mandoc_getarg(r->parse, &cp, ln, &pos);
3272294113Sbapt			r->argc = i + 1;
3273294113Sbapt		}
3274294113Sbapt	}
3275241675Suqs
3276241675Suqs	/*
3277241675Suqs	 * Expand macro arguments.
3278241675Suqs	 */
3279279527Sbapt
3280279527Sbapt	buf->sz = strlen(r->current_string) + 1;
3281322249Sbapt	n1 = n2 = cp = mandoc_malloc(buf->sz);
3282279527Sbapt	memcpy(n1, r->current_string, buf->sz);
3283322249Sbapt	expand_count = 0;
3284279527Sbapt	while (*cp != '\0') {
3285279527Sbapt
3286279527Sbapt		/* Scan ahead for the next argument invocation. */
3287279527Sbapt
3288279527Sbapt		if (*cp++ != '\\')
3289241675Suqs			continue;
3290279527Sbapt		if (*cp++ != '$')
3291279527Sbapt			continue;
3292294113Sbapt		if (*cp == '*') {  /* \\$* inserts all arguments */
3293294113Sbapt			ib = 0;
3294294113Sbapt			ie = r->argc - 1;
3295294113Sbapt		} else {  /* \\$1 .. \\$9 insert one argument */
3296294113Sbapt			ib = ie = *cp - '1';
3297294113Sbapt			if (ib < 0 || ib > 8)
3298294113Sbapt				continue;
3299294113Sbapt		}
3300279527Sbapt		cp -= 2;
3301279527Sbapt
3302279527Sbapt		/*
3303322249Sbapt		 * Prevent infinite recursion.
3304322249Sbapt		 */
3305322249Sbapt
3306322249Sbapt		if (cp >= n2)
3307322249Sbapt			expand_count = 1;
3308322249Sbapt		else if (++expand_count > EXPAND_LIMIT) {
3309322249Sbapt			mandoc_msg(MANDOCERR_ROFFLOOP, r->parse,
3310322249Sbapt			    ln, (int)(cp - n1), NULL);
3311322249Sbapt			free(buf->buf);
3312322249Sbapt			buf->buf = n1;
3313322249Sbapt			return ROFF_IGN;
3314322249Sbapt		}
3315322249Sbapt
3316322249Sbapt		/*
3317279527Sbapt		 * Determine the size of the expanded argument,
3318279527Sbapt		 * taking escaping of quotes into account.
3319279527Sbapt		 */
3320279527Sbapt
3321294113Sbapt		asz = ie > ib ? ie - ib : 0;  /* for blanks */
3322294113Sbapt		for (i = ib; i <= ie; i++) {
3323294113Sbapt			for (ap = arg[i]; *ap != '\0'; ap++) {
3324294113Sbapt				asz++;
3325294113Sbapt				if (*ap == '"')
3326294113Sbapt					asz += 3;
3327294113Sbapt			}
3328241675Suqs		}
3329279527Sbapt		if (asz != 3) {
3330279527Sbapt
3331279527Sbapt			/*
3332279527Sbapt			 * Determine the size of the rest of the
3333279527Sbapt			 * unexpanded macro, including the NUL.
3334279527Sbapt			 */
3335279527Sbapt
3336279527Sbapt			rsz = buf->sz - (cp - n1) - 3;
3337279527Sbapt
3338279527Sbapt			/*
3339279527Sbapt			 * When shrinking, move before
3340279527Sbapt			 * releasing the storage.
3341279527Sbapt			 */
3342279527Sbapt
3343279527Sbapt			if (asz < 3)
3344279527Sbapt				memmove(cp + asz, cp + 3, rsz);
3345279527Sbapt
3346279527Sbapt			/*
3347279527Sbapt			 * Resize the storage for the macro
3348279527Sbapt			 * and readjust the parse pointer.
3349279527Sbapt			 */
3350279527Sbapt
3351279527Sbapt			buf->sz += asz - 3;
3352279527Sbapt			n2 = mandoc_realloc(n1, buf->sz);
3353279527Sbapt			cp = n2 + (cp - n1);
3354279527Sbapt			n1 = n2;
3355279527Sbapt
3356279527Sbapt			/*
3357279527Sbapt			 * When growing, make room
3358279527Sbapt			 * for the expanded argument.
3359279527Sbapt			 */
3360279527Sbapt
3361279527Sbapt			if (asz > 3)
3362279527Sbapt				memmove(cp + asz, cp + 3, rsz);
3363279527Sbapt		}
3364279527Sbapt
3365279527Sbapt		/* Copy the expanded argument, escaping quotes. */
3366279527Sbapt
3367279527Sbapt		n2 = cp;
3368294113Sbapt		for (i = ib; i <= ie; i++) {
3369294113Sbapt			for (ap = arg[i]; *ap != '\0'; ap++) {
3370294113Sbapt				if (*ap == '"') {
3371294113Sbapt					memcpy(n2, "\\(dq", 4);
3372294113Sbapt					n2 += 4;
3373294113Sbapt				} else
3374294113Sbapt					*n2++ = *ap;
3375294113Sbapt			}
3376294113Sbapt			if (i < ie)
3377294113Sbapt				*n2++ = ' ';
3378279527Sbapt		}
3379241675Suqs	}
3380241675Suqs
3381241675Suqs	/*
3382241675Suqs	 * Replace the macro invocation
3383241675Suqs	 * by the expanded macro.
3384241675Suqs	 */
3385279527Sbapt
3386275432Sbapt	free(buf->buf);
3387275432Sbapt	buf->buf = n1;
3388279527Sbapt	*offs = 0;
3389241675Suqs
3390294113Sbapt	return buf->sz > 1 && buf->buf[buf->sz - 2] == '\n' ?
3391294113Sbapt	   ROFF_REPARSE : ROFF_APPEND;
3392241675Suqs}
3393241675Suqs
3394322249Sbapt/*
3395322249Sbapt * Calling a high-level macro that was renamed with .rn.
3396322249Sbapt * r->current_string has already been set up by roff_parse().
3397322249Sbapt */
3398322249Sbaptstatic enum rofferr
3399322249Sbaptroff_renamed(ROFF_ARGS)
3400322249Sbapt{
3401322249Sbapt	char	*nbuf;
3402322249Sbapt
3403322249Sbapt	buf->sz = mandoc_asprintf(&nbuf, ".%s%s%s", r->current_string,
3404322249Sbapt	    buf->buf[pos] == '\0' ? "" : " ", buf->buf + pos) + 1;
3405322249Sbapt	free(buf->buf);
3406322249Sbapt	buf->buf = nbuf;
3407322249Sbapt	return ROFF_CONT;
3408322249Sbapt}
3409322249Sbapt
3410274880Sbaptstatic size_t
3411241675Suqsroff_getname(struct roff *r, char **cpp, int ln, int pos)
3412241675Suqs{
3413241675Suqs	char	 *name, *cp;
3414274880Sbapt	size_t	  namesz;
3415241675Suqs
3416241675Suqs	name = *cpp;
3417241675Suqs	if ('\0' == *name)
3418294113Sbapt		return 0;
3419241675Suqs
3420274880Sbapt	/* Read until end of name and terminate it with NUL. */
3421274880Sbapt	for (cp = name; 1; cp++) {
3422274880Sbapt		if ('\0' == *cp || ' ' == *cp) {
3423274880Sbapt			namesz = cp - name;
3424274880Sbapt			break;
3425274880Sbapt		}
3426241675Suqs		if ('\\' != *cp)
3427241675Suqs			continue;
3428274880Sbapt		namesz = cp - name;
3429274880Sbapt		if ('{' == cp[1] || '}' == cp[1])
3430274880Sbapt			break;
3431241675Suqs		cp++;
3432241675Suqs		if ('\\' == *cp)
3433241675Suqs			continue;
3434274880Sbapt		mandoc_vmsg(MANDOCERR_NAMESC, r->parse, ln, pos,
3435274880Sbapt		    "%.*s", (int)(cp - name + 1), name);
3436274880Sbapt		mandoc_escape((const char **)&cp, NULL, NULL);
3437274880Sbapt		break;
3438241675Suqs	}
3439241675Suqs
3440241675Suqs	/* Read past spaces. */
3441241675Suqs	while (' ' == *cp)
3442241675Suqs		cp++;
3443241675Suqs
3444241675Suqs	*cpp = cp;
3445294113Sbapt	return namesz;
3446241675Suqs}
3447241675Suqs
3448241675Suqs/*
3449241675Suqs * Store *string into the user-defined string called *name.
3450241675Suqs * To clear an existing entry, call with (*r, *name, NULL, 0).
3451274880Sbapt * append == 0: replace mode
3452274880Sbapt * append == 1: single-line append mode
3453274880Sbapt * append == 2: multiline append mode, append '\n' after each call
3454241675Suqs */
3455241675Suqsstatic void
3456241675Suqsroff_setstr(struct roff *r, const char *name, const char *string,
3457274880Sbapt	int append)
3458241675Suqs{
3459322249Sbapt	size_t	 namesz;
3460241675Suqs
3461322249Sbapt	namesz = strlen(name);
3462322249Sbapt	roff_setstrn(&r->strtab, name, namesz, string,
3463274880Sbapt	    string ? strlen(string) : 0, append);
3464322249Sbapt	roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
3465241675Suqs}
3466241675Suqs
3467241675Suqsstatic void
3468241675Suqsroff_setstrn(struct roffkv **r, const char *name, size_t namesz,
3469274880Sbapt		const char *string, size_t stringsz, int append)
3470241675Suqs{
3471241675Suqs	struct roffkv	*n;
3472241675Suqs	char		*c;
3473241675Suqs	int		 i;
3474241675Suqs	size_t		 oldch, newch;
3475241675Suqs
3476241675Suqs	/* Search for an existing string with the same name. */
3477241675Suqs	n = *r;
3478241675Suqs
3479274880Sbapt	while (n && (namesz != n->key.sz ||
3480274880Sbapt			strncmp(n->key.p, name, namesz)))
3481241675Suqs		n = n->next;
3482241675Suqs
3483241675Suqs	if (NULL == n) {
3484241675Suqs		/* Create a new string table entry. */
3485241675Suqs		n = mandoc_malloc(sizeof(struct roffkv));
3486241675Suqs		n->key.p = mandoc_strndup(name, namesz);
3487241675Suqs		n->key.sz = namesz;
3488241675Suqs		n->val.p = NULL;
3489241675Suqs		n->val.sz = 0;
3490241675Suqs		n->next = *r;
3491241675Suqs		*r = n;
3492274880Sbapt	} else if (0 == append) {
3493241675Suqs		free(n->val.p);
3494241675Suqs		n->val.p = NULL;
3495241675Suqs		n->val.sz = 0;
3496241675Suqs	}
3497241675Suqs
3498241675Suqs	if (NULL == string)
3499241675Suqs		return;
3500241675Suqs
3501241675Suqs	/*
3502241675Suqs	 * One additional byte for the '\n' in multiline mode,
3503241675Suqs	 * and one for the terminating '\0'.
3504241675Suqs	 */
3505274880Sbapt	newch = stringsz + (1 < append ? 2u : 1u);
3506241675Suqs
3507241675Suqs	if (NULL == n->val.p) {
3508241675Suqs		n->val.p = mandoc_malloc(newch);
3509241675Suqs		*n->val.p = '\0';
3510241675Suqs		oldch = 0;
3511241675Suqs	} else {
3512241675Suqs		oldch = n->val.sz;
3513241675Suqs		n->val.p = mandoc_realloc(n->val.p, oldch + newch);
3514241675Suqs	}
3515241675Suqs
3516241675Suqs	/* Skip existing content in the destination buffer. */
3517241675Suqs	c = n->val.p + (int)oldch;
3518241675Suqs
3519241675Suqs	/* Append new content to the destination buffer. */
3520241675Suqs	i = 0;
3521241675Suqs	while (i < (int)stringsz) {
3522241675Suqs		/*
3523241675Suqs		 * Rudimentary roff copy mode:
3524241675Suqs		 * Handle escaped backslashes.
3525241675Suqs		 */
3526241675Suqs		if ('\\' == string[i] && '\\' == string[i + 1])
3527241675Suqs			i++;
3528241675Suqs		*c++ = string[i++];
3529241675Suqs	}
3530241675Suqs
3531241675Suqs	/* Append terminating bytes. */
3532274880Sbapt	if (1 < append)
3533241675Suqs		*c++ = '\n';
3534241675Suqs
3535241675Suqs	*c = '\0';
3536241675Suqs	n->val.sz = (int)(c - n->val.p);
3537241675Suqs}
3538241675Suqs
3539241675Suqsstatic const char *
3540322249Sbaptroff_getstrn(const struct roff *r, const char *name, size_t len,
3541322249Sbapt    int *deftype)
3542241675Suqs{
3543322249Sbapt	const struct roffkv	*n;
3544322249Sbapt	int			 i;
3545322249Sbapt	enum roff_tok		 tok;
3546241675Suqs
3547322249Sbapt	if (*deftype & ROFFDEF_USER) {
3548322249Sbapt		for (n = r->strtab; n != NULL; n = n->next) {
3549322249Sbapt			if (strncmp(name, n->key.p, len) == 0 &&
3550322249Sbapt			    n->key.p[len] == '\0' &&
3551322249Sbapt			    n->val.p != NULL) {
3552322249Sbapt				*deftype = ROFFDEF_USER;
3553322249Sbapt				return n->val.p;
3554322249Sbapt			}
3555322249Sbapt		}
3556322249Sbapt	}
3557322249Sbapt	if (*deftype & ROFFDEF_PRE) {
3558322249Sbapt		for (i = 0; i < PREDEFS_MAX; i++) {
3559322249Sbapt			if (strncmp(name, predefs[i].name, len) == 0 &&
3560322249Sbapt			    predefs[i].name[len] == '\0') {
3561322249Sbapt				*deftype = ROFFDEF_PRE;
3562322249Sbapt				return predefs[i].str;
3563322249Sbapt			}
3564322249Sbapt		}
3565322249Sbapt	}
3566322249Sbapt	if (*deftype & ROFFDEF_REN) {
3567322249Sbapt		for (n = r->rentab; n != NULL; n = n->next) {
3568322249Sbapt			if (strncmp(name, n->key.p, len) == 0 &&
3569322249Sbapt			    n->key.p[len] == '\0' &&
3570322249Sbapt			    n->val.p != NULL) {
3571322249Sbapt				*deftype = ROFFDEF_REN;
3572322249Sbapt				return n->val.p;
3573322249Sbapt			}
3574322249Sbapt		}
3575322249Sbapt	}
3576322249Sbapt	if (*deftype & ROFFDEF_STD) {
3577322249Sbapt		if (r->man->macroset != MACROSET_MAN) {
3578322249Sbapt			for (tok = MDOC_Dd; tok < MDOC_MAX; tok++) {
3579322249Sbapt				if (strncmp(name, roff_name[tok], len) == 0 &&
3580322249Sbapt				    roff_name[tok][len] == '\0') {
3581322249Sbapt					*deftype = ROFFDEF_STD;
3582322249Sbapt					return NULL;
3583322249Sbapt				}
3584322249Sbapt			}
3585322249Sbapt		}
3586322249Sbapt		if (r->man->macroset != MACROSET_MDOC) {
3587322249Sbapt			for (tok = MAN_TH; tok < MAN_MAX; tok++) {
3588322249Sbapt				if (strncmp(name, roff_name[tok], len) == 0 &&
3589322249Sbapt				    roff_name[tok][len] == '\0') {
3590322249Sbapt					*deftype = ROFFDEF_STD;
3591322249Sbapt					return NULL;
3592322249Sbapt				}
3593322249Sbapt			}
3594322249Sbapt		}
3595322249Sbapt	}
3596322249Sbapt	*deftype = 0;
3597294113Sbapt	return NULL;
3598241675Suqs}
3599241675Suqs
3600241675Suqsstatic void
3601241675Suqsroff_freestr(struct roffkv *r)
3602241675Suqs{
3603241675Suqs	struct roffkv	 *n, *nn;
3604241675Suqs
3605241675Suqs	for (n = r; n; n = nn) {
3606241675Suqs		free(n->key.p);
3607241675Suqs		free(n->val.p);
3608241675Suqs		nn = n->next;
3609241675Suqs		free(n);
3610241675Suqs	}
3611241675Suqs}
3612241675Suqs
3613294113Sbapt/* --- accessors and utility functions ------------------------------------ */
3614294113Sbapt
3615241675Suqs/*
3616241675Suqs * Duplicate an input string, making the appropriate character
3617241675Suqs * conversations (as stipulated by `tr') along the way.
3618241675Suqs * Returns a heap-allocated string with all the replacements made.
3619241675Suqs */
3620241675Suqschar *
3621241675Suqsroff_strdup(const struct roff *r, const char *p)
3622241675Suqs{
3623241675Suqs	const struct roffkv *cp;
3624241675Suqs	char		*res;
3625241675Suqs	const char	*pp;
3626241675Suqs	size_t		 ssz, sz;
3627241675Suqs	enum mandoc_esc	 esc;
3628241675Suqs
3629241675Suqs	if (NULL == r->xmbtab && NULL == r->xtab)
3630294113Sbapt		return mandoc_strdup(p);
3631241675Suqs	else if ('\0' == *p)
3632294113Sbapt		return mandoc_strdup("");
3633241675Suqs
3634241675Suqs	/*
3635241675Suqs	 * Step through each character looking for term matches
3636241675Suqs	 * (remember that a `tr' can be invoked with an escape, which is
3637241675Suqs	 * a glyph but the escape is multi-character).
3638241675Suqs	 * We only do this if the character hash has been initialised
3639241675Suqs	 * and the string is >0 length.
3640241675Suqs	 */
3641241675Suqs
3642241675Suqs	res = NULL;
3643241675Suqs	ssz = 0;
3644241675Suqs
3645241675Suqs	while ('\0' != *p) {
3646322249Sbapt		assert((unsigned int)*p < 128);
3647322249Sbapt		if ('\\' != *p && r->xtab && r->xtab[(unsigned int)*p].p) {
3648241675Suqs			sz = r->xtab[(int)*p].sz;
3649241675Suqs			res = mandoc_realloc(res, ssz + sz + 1);
3650241675Suqs			memcpy(res + ssz, r->xtab[(int)*p].p, sz);
3651241675Suqs			ssz += sz;
3652241675Suqs			p++;
3653241675Suqs			continue;
3654241675Suqs		} else if ('\\' != *p) {
3655241675Suqs			res = mandoc_realloc(res, ssz + 2);
3656241675Suqs			res[ssz++] = *p++;
3657241675Suqs			continue;
3658241675Suqs		}
3659241675Suqs
3660241675Suqs		/* Search for term matches. */
3661241675Suqs		for (cp = r->xmbtab; cp; cp = cp->next)
3662241675Suqs			if (0 == strncmp(p, cp->key.p, cp->key.sz))
3663241675Suqs				break;
3664241675Suqs
3665241675Suqs		if (NULL != cp) {
3666241675Suqs			/*
3667241675Suqs			 * A match has been found.
3668241675Suqs			 * Append the match to the array and move
3669241675Suqs			 * forward by its keysize.
3670241675Suqs			 */
3671274880Sbapt			res = mandoc_realloc(res,
3672274880Sbapt			    ssz + cp->val.sz + 1);
3673241675Suqs			memcpy(res + ssz, cp->val.p, cp->val.sz);
3674241675Suqs			ssz += cp->val.sz;
3675241675Suqs			p += (int)cp->key.sz;
3676241675Suqs			continue;
3677241675Suqs		}
3678241675Suqs
3679241675Suqs		/*
3680241675Suqs		 * Handle escapes carefully: we need to copy
3681241675Suqs		 * over just the escape itself, or else we might
3682241675Suqs		 * do replacements within the escape itself.
3683241675Suqs		 * Make sure to pass along the bogus string.
3684241675Suqs		 */
3685241675Suqs		pp = p++;
3686241675Suqs		esc = mandoc_escape(&p, NULL, NULL);
3687241675Suqs		if (ESCAPE_ERROR == esc) {
3688241675Suqs			sz = strlen(pp);
3689241675Suqs			res = mandoc_realloc(res, ssz + sz + 1);
3690241675Suqs			memcpy(res + ssz, pp, sz);
3691241675Suqs			break;
3692241675Suqs		}
3693274880Sbapt		/*
3694274880Sbapt		 * We bail out on bad escapes.
3695241675Suqs		 * No need to warn: we already did so when
3696241675Suqs		 * roff_res() was called.
3697241675Suqs		 */
3698241675Suqs		sz = (int)(p - pp);
3699241675Suqs		res = mandoc_realloc(res, ssz + sz + 1);
3700241675Suqs		memcpy(res + ssz, pp, sz);
3701241675Suqs		ssz += sz;
3702241675Suqs	}
3703241675Suqs
3704241675Suqs	res[(int)ssz] = '\0';
3705294113Sbapt	return res;
3706241675Suqs}
3707261344Suqs
3708275432Sbaptint
3709275432Sbaptroff_getformat(const struct roff *r)
3710275432Sbapt{
3711275432Sbapt
3712294113Sbapt	return r->format;
3713275432Sbapt}
3714275432Sbapt
3715261344Suqs/*
3716274880Sbapt * Find out whether a line is a macro line or not.
3717261344Suqs * If it is, adjust the current position and return one; if it isn't,
3718261344Suqs * return zero and don't change the current position.
3719261344Suqs * If the control character has been set with `.cc', then let that grain
3720261344Suqs * precedence.
3721261344Suqs * This is slighly contrary to groff, where using the non-breaking
3722261344Suqs * control character when `cc' has been invoked will cause the
3723261344Suqs * non-breaking macro contents to be printed verbatim.
3724261344Suqs */
3725261344Suqsint
3726261344Suqsroff_getcontrol(const struct roff *r, const char *cp, int *ppos)
3727261344Suqs{
3728261344Suqs	int		pos;
3729261344Suqs
3730261344Suqs	pos = *ppos;
3731261344Suqs
3732322249Sbapt	if (r->control != '\0' && cp[pos] == r->control)
3733261344Suqs		pos++;
3734322249Sbapt	else if (r->control != '\0')
3735294113Sbapt		return 0;
3736261344Suqs	else if ('\\' == cp[pos] && '.' == cp[pos + 1])
3737261344Suqs		pos += 2;
3738261344Suqs	else if ('.' == cp[pos] || '\'' == cp[pos])
3739261344Suqs		pos++;
3740261344Suqs	else
3741294113Sbapt		return 0;
3742261344Suqs
3743261344Suqs	while (' ' == cp[pos] || '\t' == cp[pos])
3744261344Suqs		pos++;
3745261344Suqs
3746261344Suqs	*ppos = pos;
3747294113Sbapt	return 1;
3748261344Suqs}
3749