xref: /freebsd/contrib/mandoc/roff.c (revision c1c95add8c80843ba15d784f95c361d795b1f593)
1*c1c95addSBrooks Davis /* $Id: roff.c,v 1.400 2023/10/24 20:53:12 schwarze Exp $ */
261d06d6bSBaptiste Daroussin /*
3*c1c95addSBrooks Davis  * Copyright (c) 2010-2015, 2017-2023 Ingo Schwarze <schwarze@openbsd.org>
461d06d6bSBaptiste Daroussin  * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
561d06d6bSBaptiste Daroussin  *
661d06d6bSBaptiste Daroussin  * Permission to use, copy, modify, and distribute this software for any
761d06d6bSBaptiste Daroussin  * purpose with or without fee is hereby granted, provided that the above
861d06d6bSBaptiste Daroussin  * copyright notice and this permission notice appear in all copies.
961d06d6bSBaptiste Daroussin  *
1061d06d6bSBaptiste Daroussin  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1161d06d6bSBaptiste Daroussin  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1261d06d6bSBaptiste Daroussin  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1361d06d6bSBaptiste Daroussin  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1461d06d6bSBaptiste Daroussin  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1561d06d6bSBaptiste Daroussin  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1661d06d6bSBaptiste Daroussin  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
176d38604fSBaptiste Daroussin  *
186d38604fSBaptiste Daroussin  * Implementation of the roff(7) parser for mandoc(1).
1961d06d6bSBaptiste Daroussin  */
2061d06d6bSBaptiste Daroussin #include "config.h"
2161d06d6bSBaptiste Daroussin 
2261d06d6bSBaptiste Daroussin #include <sys/types.h>
2361d06d6bSBaptiste Daroussin 
2461d06d6bSBaptiste Daroussin #include <assert.h>
2561d06d6bSBaptiste Daroussin #include <ctype.h>
2661d06d6bSBaptiste Daroussin #include <limits.h>
2761d06d6bSBaptiste Daroussin #include <stddef.h>
2861d06d6bSBaptiste Daroussin #include <stdint.h>
2961d06d6bSBaptiste Daroussin #include <stdio.h>
3061d06d6bSBaptiste Daroussin #include <stdlib.h>
3161d06d6bSBaptiste Daroussin #include <string.h>
3261d06d6bSBaptiste Daroussin 
3361d06d6bSBaptiste Daroussin #include "mandoc_aux.h"
3461d06d6bSBaptiste Daroussin #include "mandoc_ohash.h"
357295610fSBaptiste Daroussin #include "mandoc.h"
3661d06d6bSBaptiste Daroussin #include "roff.h"
377295610fSBaptiste Daroussin #include "mandoc_parse.h"
3861d06d6bSBaptiste Daroussin #include "libmandoc.h"
3961d06d6bSBaptiste Daroussin #include "roff_int.h"
407295610fSBaptiste Daroussin #include "tbl_parse.h"
417295610fSBaptiste Daroussin #include "eqn_parse.h"
427295610fSBaptiste Daroussin 
4361d06d6bSBaptiste Daroussin /* Maximum number of string expansions per line, to break infinite loops. */
4461d06d6bSBaptiste Daroussin #define	EXPAND_LIMIT	1000
4561d06d6bSBaptiste Daroussin 
4661d06d6bSBaptiste Daroussin /* Types of definitions of macros and strings. */
4761d06d6bSBaptiste Daroussin #define	ROFFDEF_USER	(1 << 1)  /* User-defined. */
4861d06d6bSBaptiste Daroussin #define	ROFFDEF_PRE	(1 << 2)  /* Predefined. */
4961d06d6bSBaptiste Daroussin #define	ROFFDEF_REN	(1 << 3)  /* Renamed standard macro. */
5061d06d6bSBaptiste Daroussin #define	ROFFDEF_STD	(1 << 4)  /* mdoc(7) or man(7) macro. */
5161d06d6bSBaptiste Daroussin #define	ROFFDEF_ANY	(ROFFDEF_USER | ROFFDEF_PRE | \
5261d06d6bSBaptiste Daroussin 			 ROFFDEF_REN | ROFFDEF_STD)
5361d06d6bSBaptiste Daroussin #define	ROFFDEF_UNDEF	(1 << 5)  /* Completely undefined. */
5461d06d6bSBaptiste Daroussin 
5561d06d6bSBaptiste Daroussin /* --- data types --------------------------------------------------------- */
5661d06d6bSBaptiste Daroussin 
5761d06d6bSBaptiste Daroussin /*
5861d06d6bSBaptiste Daroussin  * An incredibly-simple string buffer.
5961d06d6bSBaptiste Daroussin  */
6061d06d6bSBaptiste Daroussin struct	roffstr {
6161d06d6bSBaptiste Daroussin 	char		*p; /* nil-terminated buffer */
6261d06d6bSBaptiste Daroussin 	size_t		 sz; /* saved strlen(p) */
6361d06d6bSBaptiste Daroussin };
6461d06d6bSBaptiste Daroussin 
6561d06d6bSBaptiste Daroussin /*
6661d06d6bSBaptiste Daroussin  * A key-value roffstr pair as part of a singly-linked list.
6761d06d6bSBaptiste Daroussin  */
6861d06d6bSBaptiste Daroussin struct	roffkv {
6961d06d6bSBaptiste Daroussin 	struct roffstr	 key;
7061d06d6bSBaptiste Daroussin 	struct roffstr	 val;
7161d06d6bSBaptiste Daroussin 	struct roffkv	*next; /* next in list */
7261d06d6bSBaptiste Daroussin };
7361d06d6bSBaptiste Daroussin 
7461d06d6bSBaptiste Daroussin /*
7561d06d6bSBaptiste Daroussin  * A single number register as part of a singly-linked list.
7661d06d6bSBaptiste Daroussin  */
7761d06d6bSBaptiste Daroussin struct	roffreg {
7861d06d6bSBaptiste Daroussin 	struct roffstr	 key;
7961d06d6bSBaptiste Daroussin 	int		 val;
8061d06d6bSBaptiste Daroussin 	int		 step;
8161d06d6bSBaptiste Daroussin 	struct roffreg	*next;
8261d06d6bSBaptiste Daroussin };
8361d06d6bSBaptiste Daroussin 
8461d06d6bSBaptiste Daroussin /*
8561d06d6bSBaptiste Daroussin  * Association of request and macro names with token IDs.
8661d06d6bSBaptiste Daroussin  */
8761d06d6bSBaptiste Daroussin struct	roffreq {
8861d06d6bSBaptiste Daroussin 	enum roff_tok	 tok;
8961d06d6bSBaptiste Daroussin 	char		 name[];
9061d06d6bSBaptiste Daroussin };
9161d06d6bSBaptiste Daroussin 
927295610fSBaptiste Daroussin /*
937295610fSBaptiste Daroussin  * A macro processing context.
947295610fSBaptiste Daroussin  * More than one is needed when macro calls are nested.
957295610fSBaptiste Daroussin  */
967295610fSBaptiste Daroussin struct	mctx {
977295610fSBaptiste Daroussin 	char		**argv;
987295610fSBaptiste Daroussin 	int		 argc;
997295610fSBaptiste Daroussin 	int		 argsz;
1007295610fSBaptiste Daroussin };
1017295610fSBaptiste Daroussin 
10261d06d6bSBaptiste Daroussin struct	roff {
10361d06d6bSBaptiste Daroussin 	struct roff_man	*man; /* mdoc or man parser */
10461d06d6bSBaptiste Daroussin 	struct roffnode	*last; /* leaf of stack */
1057295610fSBaptiste Daroussin 	struct mctx	*mstack; /* stack of macro contexts */
10661d06d6bSBaptiste Daroussin 	int		*rstack; /* stack of inverted `ie' values */
10761d06d6bSBaptiste Daroussin 	struct ohash	*reqtab; /* request lookup table */
10861d06d6bSBaptiste Daroussin 	struct roffreg	*regtab; /* number registers */
10961d06d6bSBaptiste Daroussin 	struct roffkv	*strtab; /* user-defined strings & macros */
11061d06d6bSBaptiste Daroussin 	struct roffkv	*rentab; /* renamed strings & macros */
11161d06d6bSBaptiste Daroussin 	struct roffkv	*xmbtab; /* multi-byte trans table (`tr') */
11261d06d6bSBaptiste Daroussin 	struct roffstr	*xtab; /* single-byte trans table (`tr') */
11361d06d6bSBaptiste Daroussin 	const char	*current_string; /* value of last called user macro */
11461d06d6bSBaptiste Daroussin 	struct tbl_node	*first_tbl; /* first table parsed */
11561d06d6bSBaptiste Daroussin 	struct tbl_node	*last_tbl; /* last table parsed */
11661d06d6bSBaptiste Daroussin 	struct tbl_node	*tbl; /* current table being parsed */
11761d06d6bSBaptiste Daroussin 	struct eqn_node	*last_eqn; /* equation parser */
11861d06d6bSBaptiste Daroussin 	struct eqn_node	*eqn; /* active equation parser */
11961d06d6bSBaptiste Daroussin 	int		 eqn_inline; /* current equation is inline */
12061d06d6bSBaptiste Daroussin 	int		 options; /* parse options */
1217295610fSBaptiste Daroussin 	int		 mstacksz; /* current size of mstack */
1227295610fSBaptiste Daroussin 	int		 mstackpos; /* position in mstack */
12361d06d6bSBaptiste Daroussin 	int		 rstacksz; /* current size limit of rstack */
12461d06d6bSBaptiste Daroussin 	int		 rstackpos; /* position in rstack */
12561d06d6bSBaptiste Daroussin 	int		 format; /* current file in mdoc or man format */
12661d06d6bSBaptiste Daroussin 	char		 control; /* control character */
12761d06d6bSBaptiste Daroussin 	char		 escape; /* escape character */
12861d06d6bSBaptiste Daroussin };
12961d06d6bSBaptiste Daroussin 
13045a5aec3SBaptiste Daroussin /*
13145a5aec3SBaptiste Daroussin  * A macro definition, condition, or ignored block.
13245a5aec3SBaptiste Daroussin  */
13361d06d6bSBaptiste Daroussin struct	roffnode {
13461d06d6bSBaptiste Daroussin 	enum roff_tok	 tok; /* type of node */
13561d06d6bSBaptiste Daroussin 	struct roffnode	*parent; /* up one in stack */
13661d06d6bSBaptiste Daroussin 	int		 line; /* parse line */
13761d06d6bSBaptiste Daroussin 	int		 col; /* parse col */
13861d06d6bSBaptiste Daroussin 	char		*name; /* node name, e.g. macro name */
13945a5aec3SBaptiste Daroussin 	char		*end; /* custom end macro of the block */
14045a5aec3SBaptiste Daroussin 	int		 endspan; /* scope to: 1=eol 2=next line -1=\} */
14145a5aec3SBaptiste Daroussin 	int		 rule; /* content is: 1=evaluated 0=skipped */
14261d06d6bSBaptiste Daroussin };
14361d06d6bSBaptiste Daroussin 
14461d06d6bSBaptiste Daroussin #define	ROFF_ARGS	 struct roff *r, /* parse ctx */ \
14561d06d6bSBaptiste Daroussin 			 enum roff_tok tok, /* tok of macro */ \
14661d06d6bSBaptiste Daroussin 			 struct buf *buf, /* input buffer */ \
14761d06d6bSBaptiste Daroussin 			 int ln, /* parse line */ \
14861d06d6bSBaptiste Daroussin 			 int ppos, /* original pos in buffer */ \
14961d06d6bSBaptiste Daroussin 			 int pos, /* current pos in buffer */ \
15061d06d6bSBaptiste Daroussin 			 int *offs /* reset offset of buffer data */
15161d06d6bSBaptiste Daroussin 
1527295610fSBaptiste Daroussin typedef	int (*roffproc)(ROFF_ARGS);
15361d06d6bSBaptiste Daroussin 
15461d06d6bSBaptiste Daroussin struct	roffmac {
15561d06d6bSBaptiste Daroussin 	roffproc	 proc; /* process new macro */
15661d06d6bSBaptiste Daroussin 	roffproc	 text; /* process as child text of macro */
15761d06d6bSBaptiste Daroussin 	roffproc	 sub; /* process as child of macro */
15861d06d6bSBaptiste Daroussin 	int		 flags;
15961d06d6bSBaptiste Daroussin #define	ROFFMAC_STRUCT	(1 << 0) /* always interpret */
16061d06d6bSBaptiste Daroussin };
16161d06d6bSBaptiste Daroussin 
16261d06d6bSBaptiste Daroussin struct	predef {
16361d06d6bSBaptiste Daroussin 	const char	*name; /* predefined input name */
16461d06d6bSBaptiste Daroussin 	const char	*str; /* replacement symbol */
16561d06d6bSBaptiste Daroussin };
16661d06d6bSBaptiste Daroussin 
16761d06d6bSBaptiste Daroussin #define	PREDEF(__name, __str) \
16861d06d6bSBaptiste Daroussin 	{ (__name), (__str) },
16961d06d6bSBaptiste Daroussin 
17061d06d6bSBaptiste Daroussin /* --- function prototypes ------------------------------------------------ */
17161d06d6bSBaptiste Daroussin 
1727295610fSBaptiste Daroussin static	int		 roffnode_cleanscope(struct roff *);
1737295610fSBaptiste Daroussin static	int		 roffnode_pop(struct roff *);
17461d06d6bSBaptiste Daroussin static	void		 roffnode_push(struct roff *, enum roff_tok,
17561d06d6bSBaptiste Daroussin 				const char *, int, int);
1767295610fSBaptiste Daroussin static	void		 roff_addtbl(struct roff_man *, int, struct tbl_node *);
1777295610fSBaptiste Daroussin static	int		 roff_als(ROFF_ARGS);
1787295610fSBaptiste Daroussin static	int		 roff_block(ROFF_ARGS);
1797295610fSBaptiste Daroussin static	int		 roff_block_text(ROFF_ARGS);
1807295610fSBaptiste Daroussin static	int		 roff_block_sub(ROFF_ARGS);
18145a5aec3SBaptiste Daroussin static	int		 roff_break(ROFF_ARGS);
1827295610fSBaptiste Daroussin static	int		 roff_cblock(ROFF_ARGS);
1837295610fSBaptiste Daroussin static	int		 roff_cc(ROFF_ARGS);
1847295610fSBaptiste Daroussin static	int		 roff_ccond(struct roff *, int, int);
1857295610fSBaptiste Daroussin static	int		 roff_char(ROFF_ARGS);
1867295610fSBaptiste Daroussin static	int		 roff_cond(ROFF_ARGS);
1876d38604fSBaptiste Daroussin static	int		 roff_cond_checkend(ROFF_ARGS);
1887295610fSBaptiste Daroussin static	int		 roff_cond_text(ROFF_ARGS);
1897295610fSBaptiste Daroussin static	int		 roff_cond_sub(ROFF_ARGS);
1907295610fSBaptiste Daroussin static	int		 roff_ds(ROFF_ARGS);
1917295610fSBaptiste Daroussin static	int		 roff_ec(ROFF_ARGS);
1927295610fSBaptiste Daroussin static	int		 roff_eo(ROFF_ARGS);
1937295610fSBaptiste Daroussin static	int		 roff_eqndelim(struct roff *, struct buf *, int);
1946d38604fSBaptiste Daroussin static	int		 roff_evalcond(struct roff *, int, char *, int *);
19561d06d6bSBaptiste Daroussin static	int		 roff_evalnum(struct roff *, int,
19661d06d6bSBaptiste Daroussin 				const char *, int *, int *, int);
19761d06d6bSBaptiste Daroussin static	int		 roff_evalpar(struct roff *, int,
19861d06d6bSBaptiste Daroussin 				const char *, int *, int *, int);
19961d06d6bSBaptiste Daroussin static	int		 roff_evalstrcond(const char *, int *);
2007295610fSBaptiste Daroussin static	int		 roff_expand(struct roff *, struct buf *,
2017295610fSBaptiste Daroussin 				int, int, char);
202*c1c95addSBrooks Davis static	void		 roff_expand_patch(struct buf *, int,
203*c1c95addSBrooks Davis 				const char *, int);
20461d06d6bSBaptiste Daroussin static	void		 roff_free1(struct roff *);
20561d06d6bSBaptiste Daroussin static	void		 roff_freereg(struct roffreg *);
20661d06d6bSBaptiste Daroussin static	void		 roff_freestr(struct roffkv *);
20761d06d6bSBaptiste Daroussin static	size_t		 roff_getname(struct roff *, char **, int, int);
20861d06d6bSBaptiste Daroussin static	int		 roff_getnum(const char *, int *, int *, int);
20961d06d6bSBaptiste Daroussin static	int		 roff_getop(const char *, int *, char *);
21061d06d6bSBaptiste Daroussin static	int		 roff_getregn(struct roff *,
21161d06d6bSBaptiste Daroussin 				const char *, size_t, char);
21261d06d6bSBaptiste Daroussin static	int		 roff_getregro(const struct roff *,
21361d06d6bSBaptiste Daroussin 				const char *name);
21461d06d6bSBaptiste Daroussin static	const char	*roff_getstrn(struct roff *,
21561d06d6bSBaptiste Daroussin 				const char *, size_t, int *);
21661d06d6bSBaptiste Daroussin static	int		 roff_hasregn(const struct roff *,
21761d06d6bSBaptiste Daroussin 				const char *, size_t);
2187295610fSBaptiste Daroussin static	int		 roff_insec(ROFF_ARGS);
2197295610fSBaptiste Daroussin static	int		 roff_it(ROFF_ARGS);
2207295610fSBaptiste Daroussin static	int		 roff_line_ignore(ROFF_ARGS);
22161d06d6bSBaptiste Daroussin static	void		 roff_man_alloc1(struct roff_man *);
22261d06d6bSBaptiste Daroussin static	void		 roff_man_free1(struct roff_man *);
2237295610fSBaptiste Daroussin static	int		 roff_manyarg(ROFF_ARGS);
224*c1c95addSBrooks Davis static	int		 roff_mc(ROFF_ARGS);
2257295610fSBaptiste Daroussin static	int		 roff_noarg(ROFF_ARGS);
2267295610fSBaptiste Daroussin static	int		 roff_nop(ROFF_ARGS);
2277295610fSBaptiste Daroussin static	int		 roff_nr(ROFF_ARGS);
2287295610fSBaptiste Daroussin static	int		 roff_onearg(ROFF_ARGS);
22961d06d6bSBaptiste Daroussin static	enum roff_tok	 roff_parse(struct roff *, char *, int *,
23061d06d6bSBaptiste Daroussin 				int, int);
231*c1c95addSBrooks Davis static	int		 roff_parse_comment(struct roff *, struct buf *,
232*c1c95addSBrooks Davis 				int, int, char);
2337295610fSBaptiste Daroussin static	int		 roff_parsetext(struct roff *, struct buf *,
23461d06d6bSBaptiste Daroussin 				int, int *);
2357295610fSBaptiste Daroussin static	int		 roff_renamed(ROFF_ARGS);
236*c1c95addSBrooks Davis static	int		 roff_req_or_macro(ROFF_ARGS);
2377295610fSBaptiste Daroussin static	int		 roff_return(ROFF_ARGS);
2387295610fSBaptiste Daroussin static	int		 roff_rm(ROFF_ARGS);
2397295610fSBaptiste Daroussin static	int		 roff_rn(ROFF_ARGS);
2407295610fSBaptiste Daroussin static	int		 roff_rr(ROFF_ARGS);
24161d06d6bSBaptiste Daroussin static	void		 roff_setregn(struct roff *, const char *,
24261d06d6bSBaptiste Daroussin 				size_t, int, char, int);
24361d06d6bSBaptiste Daroussin static	void		 roff_setstr(struct roff *,
24461d06d6bSBaptiste Daroussin 				const char *, const char *, int);
24561d06d6bSBaptiste Daroussin static	void		 roff_setstrn(struct roffkv **, const char *,
24661d06d6bSBaptiste Daroussin 				size_t, const char *, size_t, int);
2477295610fSBaptiste Daroussin static	int		 roff_shift(ROFF_ARGS);
2487295610fSBaptiste Daroussin static	int		 roff_so(ROFF_ARGS);
2497295610fSBaptiste Daroussin static	int		 roff_tr(ROFF_ARGS);
2507295610fSBaptiste Daroussin static	int		 roff_Dd(ROFF_ARGS);
2517295610fSBaptiste Daroussin static	int		 roff_TE(ROFF_ARGS);
2527295610fSBaptiste Daroussin static	int		 roff_TS(ROFF_ARGS);
2537295610fSBaptiste Daroussin static	int		 roff_EQ(ROFF_ARGS);
2547295610fSBaptiste Daroussin static	int		 roff_EN(ROFF_ARGS);
2557295610fSBaptiste Daroussin static	int		 roff_T_(ROFF_ARGS);
2567295610fSBaptiste Daroussin static	int		 roff_unsupp(ROFF_ARGS);
2577295610fSBaptiste Daroussin static	int		 roff_userdef(ROFF_ARGS);
25861d06d6bSBaptiste Daroussin 
25961d06d6bSBaptiste Daroussin /* --- constant data ------------------------------------------------------ */
26061d06d6bSBaptiste Daroussin 
26161d06d6bSBaptiste Daroussin #define	ROFFNUM_SCALE	(1 << 0)  /* Honour scaling in roff_getnum(). */
26261d06d6bSBaptiste Daroussin #define	ROFFNUM_WHITE	(1 << 1)  /* Skip whitespace in roff_evalnum(). */
26361d06d6bSBaptiste Daroussin 
26461d06d6bSBaptiste Daroussin const char *__roff_name[MAN_MAX + 1] = {
2657295610fSBaptiste Daroussin 	"br",		"ce",		"fi",		"ft",
2667295610fSBaptiste Daroussin 	"ll",		"mc",		"nf",
2677295610fSBaptiste Daroussin 	"po",		"rj",		"sp",
26861d06d6bSBaptiste Daroussin 	"ta",		"ti",		NULL,
26961d06d6bSBaptiste Daroussin 	"ab",		"ad",		"af",		"aln",
27061d06d6bSBaptiste Daroussin 	"als",		"am",		"am1",		"ami",
27161d06d6bSBaptiste Daroussin 	"ami1",		"as",		"as1",		"asciify",
27261d06d6bSBaptiste Daroussin 	"backtrace",	"bd",		"bleedat",	"blm",
27361d06d6bSBaptiste Daroussin         "box",		"boxa",		"bp",		"BP",
27461d06d6bSBaptiste Daroussin 	"break",	"breakchar",	"brnl",		"brp",
27561d06d6bSBaptiste Daroussin 	"brpnl",	"c2",		"cc",
27661d06d6bSBaptiste Daroussin 	"cf",		"cflags",	"ch",		"char",
27761d06d6bSBaptiste Daroussin 	"chop",		"class",	"close",	"CL",
27861d06d6bSBaptiste Daroussin 	"color",	"composite",	"continue",	"cp",
27961d06d6bSBaptiste Daroussin 	"cropat",	"cs",		"cu",		"da",
28061d06d6bSBaptiste Daroussin 	"dch",		"Dd",		"de",		"de1",
28161d06d6bSBaptiste Daroussin 	"defcolor",	"dei",		"dei1",		"device",
28261d06d6bSBaptiste Daroussin 	"devicem",	"di",		"do",		"ds",
28361d06d6bSBaptiste Daroussin 	"ds1",		"dwh",		"dt",		"ec",
28461d06d6bSBaptiste Daroussin 	"ecr",		"ecs",		"el",		"em",
28561d06d6bSBaptiste Daroussin 	"EN",		"eo",		"EP",		"EQ",
28661d06d6bSBaptiste Daroussin 	"errprint",	"ev",		"evc",		"ex",
28761d06d6bSBaptiste Daroussin 	"fallback",	"fam",		"fc",		"fchar",
28861d06d6bSBaptiste Daroussin 	"fcolor",	"fdeferlig",	"feature",	"fkern",
28961d06d6bSBaptiste Daroussin 	"fl",		"flig",		"fp",		"fps",
29061d06d6bSBaptiste Daroussin 	"fschar",	"fspacewidth",	"fspecial",	"ftr",
29161d06d6bSBaptiste Daroussin 	"fzoom",	"gcolor",	"hc",		"hcode",
29261d06d6bSBaptiste Daroussin 	"hidechar",	"hla",		"hlm",		"hpf",
29361d06d6bSBaptiste Daroussin 	"hpfa",		"hpfcode",	"hw",		"hy",
29461d06d6bSBaptiste Daroussin 	"hylang",	"hylen",	"hym",		"hypp",
29561d06d6bSBaptiste Daroussin 	"hys",		"ie",		"if",		"ig",
29661d06d6bSBaptiste Daroussin 	"index",	"it",		"itc",		"IX",
29761d06d6bSBaptiste Daroussin 	"kern",		"kernafter",	"kernbefore",	"kernpair",
29861d06d6bSBaptiste Daroussin 	"lc",		"lc_ctype",	"lds",		"length",
29961d06d6bSBaptiste Daroussin 	"letadj",	"lf",		"lg",		"lhang",
30061d06d6bSBaptiste Daroussin 	"linetabs",	"lnr",		"lnrf",		"lpfx",
30161d06d6bSBaptiste Daroussin 	"ls",		"lsm",		"lt",
30261d06d6bSBaptiste Daroussin 	"mediasize",	"minss",	"mk",		"mso",
30361d06d6bSBaptiste Daroussin 	"na",		"ne",		"nh",		"nhychar",
30461d06d6bSBaptiste Daroussin 	"nm",		"nn",		"nop",		"nr",
30561d06d6bSBaptiste Daroussin 	"nrf",		"nroff",	"ns",		"nx",
30661d06d6bSBaptiste Daroussin 	"open",		"opena",	"os",		"output",
30761d06d6bSBaptiste Daroussin 	"padj",		"papersize",	"pc",		"pev",
30861d06d6bSBaptiste Daroussin 	"pi",		"PI",		"pl",		"pm",
30961d06d6bSBaptiste Daroussin 	"pn",		"pnr",		"ps",
31061d06d6bSBaptiste Daroussin 	"psbb",		"pshape",	"pso",		"ptr",
31161d06d6bSBaptiste Daroussin 	"pvs",		"rchar",	"rd",		"recursionlimit",
31261d06d6bSBaptiste Daroussin 	"return",	"rfschar",	"rhang",
31361d06d6bSBaptiste Daroussin 	"rm",		"rn",		"rnn",		"rr",
31461d06d6bSBaptiste Daroussin 	"rs",		"rt",		"schar",	"sentchar",
31561d06d6bSBaptiste Daroussin 	"shc",		"shift",	"sizes",	"so",
31661d06d6bSBaptiste Daroussin 	"spacewidth",	"special",	"spreadwarn",	"ss",
31761d06d6bSBaptiste Daroussin 	"sty",		"substring",	"sv",		"sy",
31861d06d6bSBaptiste Daroussin 	"T&",		"tc",		"TE",
31961d06d6bSBaptiste Daroussin 	"TH",		"tkf",		"tl",
32061d06d6bSBaptiste Daroussin 	"tm",		"tm1",		"tmc",		"tr",
32161d06d6bSBaptiste Daroussin 	"track",	"transchar",	"trf",		"trimat",
32261d06d6bSBaptiste Daroussin 	"trin",		"trnt",		"troff",	"TS",
32361d06d6bSBaptiste Daroussin 	"uf",		"ul",		"unformat",	"unwatch",
32461d06d6bSBaptiste Daroussin 	"unwatchn",	"vpt",		"vs",		"warn",
32561d06d6bSBaptiste Daroussin 	"warnscale",	"watch",	"watchlength",	"watchn",
32661d06d6bSBaptiste Daroussin 	"wh",		"while",	"write",	"writec",
32761d06d6bSBaptiste Daroussin 	"writem",	"xflag",	".",		NULL,
32861d06d6bSBaptiste Daroussin 	NULL,		"text",
32961d06d6bSBaptiste Daroussin 	"Dd",		"Dt",		"Os",		"Sh",
33061d06d6bSBaptiste Daroussin 	"Ss",		"Pp",		"D1",		"Dl",
33161d06d6bSBaptiste Daroussin 	"Bd",		"Ed",		"Bl",		"El",
33261d06d6bSBaptiste Daroussin 	"It",		"Ad",		"An",		"Ap",
33361d06d6bSBaptiste Daroussin 	"Ar",		"Cd",		"Cm",		"Dv",
33461d06d6bSBaptiste Daroussin 	"Er",		"Ev",		"Ex",		"Fa",
33561d06d6bSBaptiste Daroussin 	"Fd",		"Fl",		"Fn",		"Ft",
33661d06d6bSBaptiste Daroussin 	"Ic",		"In",		"Li",		"Nd",
33761d06d6bSBaptiste Daroussin 	"Nm",		"Op",		"Ot",		"Pa",
33861d06d6bSBaptiste Daroussin 	"Rv",		"St",		"Va",		"Vt",
33961d06d6bSBaptiste Daroussin 	"Xr",		"%A",		"%B",		"%D",
34061d06d6bSBaptiste Daroussin 	"%I",		"%J",		"%N",		"%O",
34161d06d6bSBaptiste Daroussin 	"%P",		"%R",		"%T",		"%V",
34261d06d6bSBaptiste Daroussin 	"Ac",		"Ao",		"Aq",		"At",
34361d06d6bSBaptiste Daroussin 	"Bc",		"Bf",		"Bo",		"Bq",
34461d06d6bSBaptiste Daroussin 	"Bsx",		"Bx",		"Db",		"Dc",
34561d06d6bSBaptiste Daroussin 	"Do",		"Dq",		"Ec",		"Ef",
34661d06d6bSBaptiste Daroussin 	"Em",		"Eo",		"Fx",		"Ms",
34761d06d6bSBaptiste Daroussin 	"No",		"Ns",		"Nx",		"Ox",
34861d06d6bSBaptiste Daroussin 	"Pc",		"Pf",		"Po",		"Pq",
34961d06d6bSBaptiste Daroussin 	"Qc",		"Ql",		"Qo",		"Qq",
35061d06d6bSBaptiste Daroussin 	"Re",		"Rs",		"Sc",		"So",
35161d06d6bSBaptiste Daroussin 	"Sq",		"Sm",		"Sx",		"Sy",
35261d06d6bSBaptiste Daroussin 	"Tn",		"Ux",		"Xc",		"Xo",
35361d06d6bSBaptiste Daroussin 	"Fo",		"Fc",		"Oo",		"Oc",
35461d06d6bSBaptiste Daroussin 	"Bk",		"Ek",		"Bt",		"Hf",
35561d06d6bSBaptiste Daroussin 	"Fr",		"Ud",		"Lb",		"Lp",
35661d06d6bSBaptiste Daroussin 	"Lk",		"Mt",		"Brq",		"Bro",
35761d06d6bSBaptiste Daroussin 	"Brc",		"%C",		"Es",		"En",
35861d06d6bSBaptiste Daroussin 	"Dx",		"%Q",		"%U",		"Ta",
3596d38604fSBaptiste Daroussin 	"Tg",		NULL,
36061d06d6bSBaptiste Daroussin 	"TH",		"SH",		"SS",		"TP",
3617295610fSBaptiste Daroussin 	"TQ",
36261d06d6bSBaptiste Daroussin 	"LP",		"PP",		"P",		"IP",
36361d06d6bSBaptiste Daroussin 	"HP",		"SM",		"SB",		"BI",
36461d06d6bSBaptiste Daroussin 	"IB",		"BR",		"RB",		"R",
36561d06d6bSBaptiste Daroussin 	"B",		"I",		"IR",		"RI",
36661d06d6bSBaptiste Daroussin 	"RE",		"RS",		"DT",		"UC",
36761d06d6bSBaptiste Daroussin 	"PD",		"AT",		"in",
3687295610fSBaptiste Daroussin 	"SY",		"YS",		"OP",
3697295610fSBaptiste Daroussin 	"EX",		"EE",		"UR",
370*c1c95addSBrooks Davis 	"UE",		"MT",		"ME",		"MR",
371*c1c95addSBrooks Davis 	NULL
37261d06d6bSBaptiste Daroussin };
37361d06d6bSBaptiste Daroussin const	char *const *roff_name = __roff_name;
37461d06d6bSBaptiste Daroussin 
37561d06d6bSBaptiste Daroussin static	struct roffmac	 roffs[TOKEN_NONE] = {
3767295610fSBaptiste Daroussin 	{ roff_noarg, NULL, NULL, 0 },  /* br */
37761d06d6bSBaptiste Daroussin 	{ roff_onearg, NULL, NULL, 0 },  /* ce */
3787295610fSBaptiste Daroussin 	{ roff_noarg, NULL, NULL, 0 },  /* fi */
37961d06d6bSBaptiste Daroussin 	{ roff_onearg, NULL, NULL, 0 },  /* ft */
38061d06d6bSBaptiste Daroussin 	{ roff_onearg, NULL, NULL, 0 },  /* ll */
381*c1c95addSBrooks Davis 	{ roff_mc, NULL, NULL, 0 },  /* mc */
3827295610fSBaptiste Daroussin 	{ roff_noarg, NULL, NULL, 0 },  /* nf */
38361d06d6bSBaptiste Daroussin 	{ roff_onearg, NULL, NULL, 0 },  /* po */
38461d06d6bSBaptiste Daroussin 	{ roff_onearg, NULL, NULL, 0 },  /* rj */
38561d06d6bSBaptiste Daroussin 	{ roff_onearg, NULL, NULL, 0 },  /* sp */
38661d06d6bSBaptiste Daroussin 	{ roff_manyarg, NULL, NULL, 0 },  /* ta */
38761d06d6bSBaptiste Daroussin 	{ roff_onearg, NULL, NULL, 0 },  /* ti */
38861d06d6bSBaptiste Daroussin 	{ NULL, NULL, NULL, 0 },  /* ROFF_MAX */
38961d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* ab */
39061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* ad */
39161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* af */
39261d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* aln */
39361d06d6bSBaptiste Daroussin 	{ roff_als, NULL, NULL, 0 },  /* als */
39461d06d6bSBaptiste Daroussin 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* am */
39561d06d6bSBaptiste Daroussin 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* am1 */
39661d06d6bSBaptiste Daroussin 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* ami */
39761d06d6bSBaptiste Daroussin 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* ami1 */
39861d06d6bSBaptiste Daroussin 	{ roff_ds, NULL, NULL, 0 },  /* as */
39961d06d6bSBaptiste Daroussin 	{ roff_ds, NULL, NULL, 0 },  /* as1 */
40061d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* asciify */
40161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* backtrace */
40261d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* bd */
40361d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* bleedat */
40461d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* blm */
40561d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* box */
40661d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* boxa */
40761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* bp */
40861d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* BP */
40945a5aec3SBaptiste Daroussin 	{ roff_break, NULL, NULL, 0 },  /* break */
41061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* breakchar */
41161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* brnl */
4127295610fSBaptiste Daroussin 	{ roff_noarg, NULL, NULL, 0 },  /* brp */
41361d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* brpnl */
41461d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* c2 */
41561d06d6bSBaptiste Daroussin 	{ roff_cc, NULL, NULL, 0 },  /* cc */
41661d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* cf */
41761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* cflags */
41861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* ch */
4197295610fSBaptiste Daroussin 	{ roff_char, NULL, NULL, 0 },  /* char */
42061d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* chop */
42161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* class */
42261d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* close */
42361d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* CL */
42461d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* color */
42561d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* composite */
42661d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* continue */
42761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* cp */
42861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* cropat */
42961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* cs */
43061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* cu */
43161d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* da */
43261d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* dch */
43361d06d6bSBaptiste Daroussin 	{ roff_Dd, NULL, NULL, 0 },  /* Dd */
43461d06d6bSBaptiste Daroussin 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* de */
43561d06d6bSBaptiste Daroussin 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* de1 */
43661d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* defcolor */
43761d06d6bSBaptiste Daroussin 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* dei */
43861d06d6bSBaptiste Daroussin 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* dei1 */
43961d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* device */
44061d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* devicem */
44161d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* di */
44261d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* do */
44361d06d6bSBaptiste Daroussin 	{ roff_ds, NULL, NULL, 0 },  /* ds */
44461d06d6bSBaptiste Daroussin 	{ roff_ds, NULL, NULL, 0 },  /* ds1 */
44561d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* dwh */
44661d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* dt */
44761d06d6bSBaptiste Daroussin 	{ roff_ec, NULL, NULL, 0 },  /* ec */
44861d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* ecr */
44961d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* ecs */
45061d06d6bSBaptiste Daroussin 	{ roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },  /* el */
45161d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* em */
45261d06d6bSBaptiste Daroussin 	{ roff_EN, NULL, NULL, 0 },  /* EN */
45361d06d6bSBaptiste Daroussin 	{ roff_eo, NULL, NULL, 0 },  /* eo */
45461d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* EP */
45561d06d6bSBaptiste Daroussin 	{ roff_EQ, NULL, NULL, 0 },  /* EQ */
45661d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* errprint */
45761d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* ev */
45861d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* evc */
45961d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* ex */
46061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* fallback */
46161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* fam */
46261d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* fc */
46361d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* fchar */
46461d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* fcolor */
46561d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* fdeferlig */
46661d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* feature */
46761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* fkern */
46861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* fl */
46961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* flig */
47061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* fp */
47161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* fps */
47261d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* fschar */
47361d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* fspacewidth */
47461d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* fspecial */
47561d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* ftr */
47661d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* fzoom */
47761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* gcolor */
47861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hc */
47961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hcode */
48061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hidechar */
48161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hla */
48261d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hlm */
48361d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hpf */
48461d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hpfa */
48561d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hpfcode */
48661d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hw */
48761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hy */
48861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hylang */
48961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hylen */
49061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hym */
49161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hypp */
49261d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hys */
49361d06d6bSBaptiste Daroussin 	{ roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },  /* ie */
49461d06d6bSBaptiste Daroussin 	{ roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },  /* if */
49561d06d6bSBaptiste Daroussin 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* ig */
49661d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* index */
49761d06d6bSBaptiste Daroussin 	{ roff_it, NULL, NULL, 0 },  /* it */
49861d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* itc */
49961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* IX */
50061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* kern */
50161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* kernafter */
50261d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* kernbefore */
50361d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* kernpair */
50461d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* lc */
50561d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* lc_ctype */
50661d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* lds */
50761d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* length */
50861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* letadj */
50961d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* lf */
51061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* lg */
51161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* lhang */
51261d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* linetabs */
51361d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* lnr */
51461d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* lnrf */
51561d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* lpfx */
51661d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* ls */
51761d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* lsm */
51861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* lt */
51961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* mediasize */
52061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* minss */
52161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* mk */
52261d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* mso */
52361d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* na */
52461d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* ne */
52561d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* nh */
52661d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* nhychar */
52761d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* nm */
52861d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* nn */
5297295610fSBaptiste Daroussin 	{ roff_nop, NULL, NULL, 0 },  /* nop */
53061d06d6bSBaptiste Daroussin 	{ roff_nr, NULL, NULL, 0 },  /* nr */
53161d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* nrf */
53261d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* nroff */
53361d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* ns */
53461d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* nx */
53561d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* open */
53661d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* opena */
53761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* os */
53861d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* output */
53961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* padj */
54061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* papersize */
54161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* pc */
54261d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* pev */
54361d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* pi */
54461d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* PI */
54561d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* pl */
54661d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* pm */
54761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* pn */
54861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* pnr */
54961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* ps */
55061d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* psbb */
55161d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* pshape */
55261d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* pso */
55361d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* ptr */
55461d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* pvs */
55561d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* rchar */
55661d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* rd */
55761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* recursionlimit */
5587295610fSBaptiste Daroussin 	{ roff_return, NULL, NULL, 0 },  /* return */
55961d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* rfschar */
56061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* rhang */
56161d06d6bSBaptiste Daroussin 	{ roff_rm, NULL, NULL, 0 },  /* rm */
56261d06d6bSBaptiste Daroussin 	{ roff_rn, NULL, NULL, 0 },  /* rn */
56361d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* rnn */
56461d06d6bSBaptiste Daroussin 	{ roff_rr, NULL, NULL, 0 },  /* rr */
56561d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* rs */
56661d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* rt */
56761d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* schar */
56861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* sentchar */
56961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* shc */
5707295610fSBaptiste Daroussin 	{ roff_shift, NULL, NULL, 0 },  /* shift */
57161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* sizes */
57261d06d6bSBaptiste Daroussin 	{ roff_so, NULL, NULL, 0 },  /* so */
57361d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* spacewidth */
57461d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* special */
57561d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* spreadwarn */
57661d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* ss */
57761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* sty */
57861d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* substring */
57961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* sv */
58061d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* sy */
58161d06d6bSBaptiste Daroussin 	{ roff_T_, NULL, NULL, 0 },  /* T& */
58261d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* tc */
58361d06d6bSBaptiste Daroussin 	{ roff_TE, NULL, NULL, 0 },  /* TE */
58461d06d6bSBaptiste Daroussin 	{ roff_Dd, NULL, NULL, 0 },  /* TH */
58561d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* tkf */
58661d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* tl */
58761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* tm */
58861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* tm1 */
58961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* tmc */
59061d06d6bSBaptiste Daroussin 	{ roff_tr, NULL, NULL, 0 },  /* tr */
59161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* track */
59261d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* transchar */
59361d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* trf */
59461d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* trimat */
59561d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* trin */
59661d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* trnt */
59761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* troff */
59861d06d6bSBaptiste Daroussin 	{ roff_TS, NULL, NULL, 0 },  /* TS */
59961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* uf */
60061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* ul */
60161d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* unformat */
60261d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* unwatch */
60361d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* unwatchn */
60461d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* vpt */
60561d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* vs */
60661d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* warn */
60761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* warnscale */
60861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* watch */
60961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* watchlength */
61061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* watchn */
61161d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* wh */
6127295610fSBaptiste Daroussin 	{ roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /*while*/
61361d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* write */
61461d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* writec */
61561d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* writem */
61661d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* xflag */
61761d06d6bSBaptiste Daroussin 	{ roff_cblock, NULL, NULL, 0 },  /* . */
61861d06d6bSBaptiste Daroussin 	{ roff_renamed, NULL, NULL, 0 },
61961d06d6bSBaptiste Daroussin 	{ roff_userdef, NULL, NULL, 0 }
62061d06d6bSBaptiste Daroussin };
62161d06d6bSBaptiste Daroussin 
62261d06d6bSBaptiste Daroussin /* Array of injected predefined strings. */
62361d06d6bSBaptiste Daroussin #define	PREDEFS_MAX	 38
62461d06d6bSBaptiste Daroussin static	const struct predef predefs[PREDEFS_MAX] = {
62561d06d6bSBaptiste Daroussin #include "predefs.in"
62661d06d6bSBaptiste Daroussin };
62761d06d6bSBaptiste Daroussin 
62861d06d6bSBaptiste Daroussin static	int	 roffce_lines;	/* number of input lines to center */
62961d06d6bSBaptiste Daroussin static	struct roff_node *roffce_node;  /* active request */
63061d06d6bSBaptiste Daroussin static	int	 roffit_lines;  /* number of lines to delay */
63161d06d6bSBaptiste Daroussin static	char	*roffit_macro;  /* nil-terminated macro line */
63261d06d6bSBaptiste Daroussin 
63361d06d6bSBaptiste Daroussin 
63461d06d6bSBaptiste Daroussin /* --- request table ------------------------------------------------------ */
63561d06d6bSBaptiste Daroussin 
63661d06d6bSBaptiste Daroussin struct ohash *
roffhash_alloc(enum roff_tok mintok,enum roff_tok maxtok)63761d06d6bSBaptiste Daroussin roffhash_alloc(enum roff_tok mintok, enum roff_tok maxtok)
63861d06d6bSBaptiste Daroussin {
63961d06d6bSBaptiste Daroussin 	struct ohash	*htab;
64061d06d6bSBaptiste Daroussin 	struct roffreq	*req;
64161d06d6bSBaptiste Daroussin 	enum roff_tok	 tok;
64261d06d6bSBaptiste Daroussin 	size_t		 sz;
64361d06d6bSBaptiste Daroussin 	unsigned int	 slot;
64461d06d6bSBaptiste Daroussin 
64561d06d6bSBaptiste Daroussin 	htab = mandoc_malloc(sizeof(*htab));
64661d06d6bSBaptiste Daroussin 	mandoc_ohash_init(htab, 8, offsetof(struct roffreq, name));
64761d06d6bSBaptiste Daroussin 
64861d06d6bSBaptiste Daroussin 	for (tok = mintok; tok < maxtok; tok++) {
64961d06d6bSBaptiste Daroussin 		if (roff_name[tok] == NULL)
65061d06d6bSBaptiste Daroussin 			continue;
65161d06d6bSBaptiste Daroussin 		sz = strlen(roff_name[tok]);
65261d06d6bSBaptiste Daroussin 		req = mandoc_malloc(sizeof(*req) + sz + 1);
65361d06d6bSBaptiste Daroussin 		req->tok = tok;
65461d06d6bSBaptiste Daroussin 		memcpy(req->name, roff_name[tok], sz + 1);
65561d06d6bSBaptiste Daroussin 		slot = ohash_qlookup(htab, req->name);
65661d06d6bSBaptiste Daroussin 		ohash_insert(htab, slot, req);
65761d06d6bSBaptiste Daroussin 	}
65861d06d6bSBaptiste Daroussin 	return htab;
65961d06d6bSBaptiste Daroussin }
66061d06d6bSBaptiste Daroussin 
66161d06d6bSBaptiste Daroussin void
roffhash_free(struct ohash * htab)66261d06d6bSBaptiste Daroussin roffhash_free(struct ohash *htab)
66361d06d6bSBaptiste Daroussin {
66461d06d6bSBaptiste Daroussin 	struct roffreq	*req;
66561d06d6bSBaptiste Daroussin 	unsigned int	 slot;
66661d06d6bSBaptiste Daroussin 
66761d06d6bSBaptiste Daroussin 	if (htab == NULL)
66861d06d6bSBaptiste Daroussin 		return;
66961d06d6bSBaptiste Daroussin 	for (req = ohash_first(htab, &slot); req != NULL;
67061d06d6bSBaptiste Daroussin 	     req = ohash_next(htab, &slot))
67161d06d6bSBaptiste Daroussin 		free(req);
67261d06d6bSBaptiste Daroussin 	ohash_delete(htab);
67361d06d6bSBaptiste Daroussin 	free(htab);
67461d06d6bSBaptiste Daroussin }
67561d06d6bSBaptiste Daroussin 
67661d06d6bSBaptiste Daroussin enum roff_tok
roffhash_find(struct ohash * htab,const char * name,size_t sz)67761d06d6bSBaptiste Daroussin roffhash_find(struct ohash *htab, const char *name, size_t sz)
67861d06d6bSBaptiste Daroussin {
67961d06d6bSBaptiste Daroussin 	struct roffreq	*req;
68061d06d6bSBaptiste Daroussin 	const char	*end;
68161d06d6bSBaptiste Daroussin 
68261d06d6bSBaptiste Daroussin 	if (sz) {
68361d06d6bSBaptiste Daroussin 		end = name + sz;
68461d06d6bSBaptiste Daroussin 		req = ohash_find(htab, ohash_qlookupi(htab, name, &end));
68561d06d6bSBaptiste Daroussin 	} else
68661d06d6bSBaptiste Daroussin 		req = ohash_find(htab, ohash_qlookup(htab, name));
68761d06d6bSBaptiste Daroussin 	return req == NULL ? TOKEN_NONE : req->tok;
68861d06d6bSBaptiste Daroussin }
68961d06d6bSBaptiste Daroussin 
69061d06d6bSBaptiste Daroussin /* --- stack of request blocks -------------------------------------------- */
69161d06d6bSBaptiste Daroussin 
69261d06d6bSBaptiste Daroussin /*
69361d06d6bSBaptiste Daroussin  * Pop the current node off of the stack of roff instructions currently
69445a5aec3SBaptiste Daroussin  * pending.  Return 1 if it is a loop or 0 otherwise.
69561d06d6bSBaptiste Daroussin  */
6967295610fSBaptiste Daroussin static int
roffnode_pop(struct roff * r)69761d06d6bSBaptiste Daroussin roffnode_pop(struct roff *r)
69861d06d6bSBaptiste Daroussin {
69961d06d6bSBaptiste Daroussin 	struct roffnode	*p;
7007295610fSBaptiste Daroussin 	int		 inloop;
70161d06d6bSBaptiste Daroussin 
70261d06d6bSBaptiste Daroussin 	p = r->last;
7037295610fSBaptiste Daroussin 	inloop = p->tok == ROFF_while;
7047295610fSBaptiste Daroussin 	r->last = p->parent;
70561d06d6bSBaptiste Daroussin 	free(p->name);
70661d06d6bSBaptiste Daroussin 	free(p->end);
70761d06d6bSBaptiste Daroussin 	free(p);
7087295610fSBaptiste Daroussin 	return inloop;
70961d06d6bSBaptiste Daroussin }
71061d06d6bSBaptiste Daroussin 
71161d06d6bSBaptiste Daroussin /*
71261d06d6bSBaptiste Daroussin  * Push a roff node onto the instruction stack.  This must later be
71361d06d6bSBaptiste Daroussin  * removed with roffnode_pop().
71461d06d6bSBaptiste Daroussin  */
71561d06d6bSBaptiste Daroussin static void
roffnode_push(struct roff * r,enum roff_tok tok,const char * name,int line,int col)71661d06d6bSBaptiste Daroussin roffnode_push(struct roff *r, enum roff_tok tok, const char *name,
71761d06d6bSBaptiste Daroussin 		int line, int col)
71861d06d6bSBaptiste Daroussin {
71961d06d6bSBaptiste Daroussin 	struct roffnode	*p;
72061d06d6bSBaptiste Daroussin 
72161d06d6bSBaptiste Daroussin 	p = mandoc_calloc(1, sizeof(struct roffnode));
72261d06d6bSBaptiste Daroussin 	p->tok = tok;
72361d06d6bSBaptiste Daroussin 	if (name)
72461d06d6bSBaptiste Daroussin 		p->name = mandoc_strdup(name);
72561d06d6bSBaptiste Daroussin 	p->parent = r->last;
72661d06d6bSBaptiste Daroussin 	p->line = line;
72761d06d6bSBaptiste Daroussin 	p->col = col;
72861d06d6bSBaptiste Daroussin 	p->rule = p->parent ? p->parent->rule : 0;
72961d06d6bSBaptiste Daroussin 
73061d06d6bSBaptiste Daroussin 	r->last = p;
73161d06d6bSBaptiste Daroussin }
73261d06d6bSBaptiste Daroussin 
73361d06d6bSBaptiste Daroussin /* --- roff parser state data management ---------------------------------- */
73461d06d6bSBaptiste Daroussin 
73561d06d6bSBaptiste Daroussin static void
roff_free1(struct roff * r)73661d06d6bSBaptiste Daroussin roff_free1(struct roff *r)
73761d06d6bSBaptiste Daroussin {
73861d06d6bSBaptiste Daroussin 	int		 i;
73961d06d6bSBaptiste Daroussin 
7407295610fSBaptiste Daroussin 	tbl_free(r->first_tbl);
74161d06d6bSBaptiste Daroussin 	r->first_tbl = r->last_tbl = r->tbl = NULL;
74261d06d6bSBaptiste Daroussin 
74361d06d6bSBaptiste Daroussin 	eqn_free(r->last_eqn);
74461d06d6bSBaptiste Daroussin 	r->last_eqn = r->eqn = NULL;
74561d06d6bSBaptiste Daroussin 
7467295610fSBaptiste Daroussin 	while (r->mstackpos >= 0)
7477295610fSBaptiste Daroussin 		roff_userret(r);
7487295610fSBaptiste Daroussin 
74961d06d6bSBaptiste Daroussin 	while (r->last)
75061d06d6bSBaptiste Daroussin 		roffnode_pop(r);
75161d06d6bSBaptiste Daroussin 
75261d06d6bSBaptiste Daroussin 	free (r->rstack);
75361d06d6bSBaptiste Daroussin 	r->rstack = NULL;
75461d06d6bSBaptiste Daroussin 	r->rstacksz = 0;
75561d06d6bSBaptiste Daroussin 	r->rstackpos = -1;
75661d06d6bSBaptiste Daroussin 
75761d06d6bSBaptiste Daroussin 	roff_freereg(r->regtab);
75861d06d6bSBaptiste Daroussin 	r->regtab = NULL;
75961d06d6bSBaptiste Daroussin 
76061d06d6bSBaptiste Daroussin 	roff_freestr(r->strtab);
76161d06d6bSBaptiste Daroussin 	roff_freestr(r->rentab);
76261d06d6bSBaptiste Daroussin 	roff_freestr(r->xmbtab);
76361d06d6bSBaptiste Daroussin 	r->strtab = r->rentab = r->xmbtab = NULL;
76461d06d6bSBaptiste Daroussin 
76561d06d6bSBaptiste Daroussin 	if (r->xtab)
76661d06d6bSBaptiste Daroussin 		for (i = 0; i < 128; i++)
76761d06d6bSBaptiste Daroussin 			free(r->xtab[i].p);
76861d06d6bSBaptiste Daroussin 	free(r->xtab);
76961d06d6bSBaptiste Daroussin 	r->xtab = NULL;
77061d06d6bSBaptiste Daroussin }
77161d06d6bSBaptiste Daroussin 
77261d06d6bSBaptiste Daroussin void
roff_reset(struct roff * r)77361d06d6bSBaptiste Daroussin roff_reset(struct roff *r)
77461d06d6bSBaptiste Daroussin {
77561d06d6bSBaptiste Daroussin 	roff_free1(r);
7766d38604fSBaptiste Daroussin 	r->options |= MPARSE_COMMENT;
77761d06d6bSBaptiste Daroussin 	r->format = r->options & (MPARSE_MDOC | MPARSE_MAN);
77861d06d6bSBaptiste Daroussin 	r->control = '\0';
77961d06d6bSBaptiste Daroussin 	r->escape = '\\';
78061d06d6bSBaptiste Daroussin 	roffce_lines = 0;
78161d06d6bSBaptiste Daroussin 	roffce_node = NULL;
78261d06d6bSBaptiste Daroussin 	roffit_lines = 0;
78361d06d6bSBaptiste Daroussin 	roffit_macro = NULL;
78461d06d6bSBaptiste Daroussin }
78561d06d6bSBaptiste Daroussin 
78661d06d6bSBaptiste Daroussin void
roff_free(struct roff * r)78761d06d6bSBaptiste Daroussin roff_free(struct roff *r)
78861d06d6bSBaptiste Daroussin {
7897295610fSBaptiste Daroussin 	int		 i;
7907295610fSBaptiste Daroussin 
79161d06d6bSBaptiste Daroussin 	roff_free1(r);
7927295610fSBaptiste Daroussin 	for (i = 0; i < r->mstacksz; i++)
7937295610fSBaptiste Daroussin 		free(r->mstack[i].argv);
7947295610fSBaptiste Daroussin 	free(r->mstack);
79561d06d6bSBaptiste Daroussin 	roffhash_free(r->reqtab);
79661d06d6bSBaptiste Daroussin 	free(r);
79761d06d6bSBaptiste Daroussin }
79861d06d6bSBaptiste Daroussin 
79961d06d6bSBaptiste Daroussin struct roff *
roff_alloc(int options)8007295610fSBaptiste Daroussin roff_alloc(int options)
80161d06d6bSBaptiste Daroussin {
80261d06d6bSBaptiste Daroussin 	struct roff	*r;
80361d06d6bSBaptiste Daroussin 
80461d06d6bSBaptiste Daroussin 	r = mandoc_calloc(1, sizeof(struct roff));
80561d06d6bSBaptiste Daroussin 	r->reqtab = roffhash_alloc(0, ROFF_RENAMED);
8066d38604fSBaptiste Daroussin 	r->options = options | MPARSE_COMMENT;
80761d06d6bSBaptiste Daroussin 	r->format = options & (MPARSE_MDOC | MPARSE_MAN);
8087295610fSBaptiste Daroussin 	r->mstackpos = -1;
80961d06d6bSBaptiste Daroussin 	r->rstackpos = -1;
81061d06d6bSBaptiste Daroussin 	r->escape = '\\';
81161d06d6bSBaptiste Daroussin 	return r;
81261d06d6bSBaptiste Daroussin }
81361d06d6bSBaptiste Daroussin 
81461d06d6bSBaptiste Daroussin /* --- syntax tree state data management ---------------------------------- */
81561d06d6bSBaptiste Daroussin 
81661d06d6bSBaptiste Daroussin static void
roff_man_free1(struct roff_man * man)81761d06d6bSBaptiste Daroussin roff_man_free1(struct roff_man *man)
81861d06d6bSBaptiste Daroussin {
8197295610fSBaptiste Daroussin 	if (man->meta.first != NULL)
8207295610fSBaptiste Daroussin 		roff_node_delete(man, man->meta.first);
82161d06d6bSBaptiste Daroussin 	free(man->meta.msec);
82261d06d6bSBaptiste Daroussin 	free(man->meta.vol);
82361d06d6bSBaptiste Daroussin 	free(man->meta.os);
82461d06d6bSBaptiste Daroussin 	free(man->meta.arch);
82561d06d6bSBaptiste Daroussin 	free(man->meta.title);
82661d06d6bSBaptiste Daroussin 	free(man->meta.name);
82761d06d6bSBaptiste Daroussin 	free(man->meta.date);
8287295610fSBaptiste Daroussin 	free(man->meta.sodest);
8297295610fSBaptiste Daroussin }
8307295610fSBaptiste Daroussin 
8317295610fSBaptiste Daroussin void
roff_state_reset(struct roff_man * man)8327295610fSBaptiste Daroussin roff_state_reset(struct roff_man *man)
8337295610fSBaptiste Daroussin {
8347295610fSBaptiste Daroussin 	man->last = man->meta.first;
8357295610fSBaptiste Daroussin 	man->last_es = NULL;
8367295610fSBaptiste Daroussin 	man->flags = 0;
8377295610fSBaptiste Daroussin 	man->lastsec = man->lastnamed = SEC_NONE;
8387295610fSBaptiste Daroussin 	man->next = ROFF_NEXT_CHILD;
8397295610fSBaptiste Daroussin 	roff_setreg(man->roff, "nS", 0, '=');
84061d06d6bSBaptiste Daroussin }
84161d06d6bSBaptiste Daroussin 
84261d06d6bSBaptiste Daroussin static void
roff_man_alloc1(struct roff_man * man)84361d06d6bSBaptiste Daroussin roff_man_alloc1(struct roff_man *man)
84461d06d6bSBaptiste Daroussin {
84561d06d6bSBaptiste Daroussin 	memset(&man->meta, 0, sizeof(man->meta));
8467295610fSBaptiste Daroussin 	man->meta.first = mandoc_calloc(1, sizeof(*man->meta.first));
8477295610fSBaptiste Daroussin 	man->meta.first->type = ROFFT_ROOT;
8487295610fSBaptiste Daroussin 	man->meta.macroset = MACROSET_NONE;
8497295610fSBaptiste Daroussin 	roff_state_reset(man);
85061d06d6bSBaptiste Daroussin }
85161d06d6bSBaptiste Daroussin 
85261d06d6bSBaptiste Daroussin void
roff_man_reset(struct roff_man * man)85361d06d6bSBaptiste Daroussin roff_man_reset(struct roff_man *man)
85461d06d6bSBaptiste Daroussin {
85561d06d6bSBaptiste Daroussin 	roff_man_free1(man);
85661d06d6bSBaptiste Daroussin 	roff_man_alloc1(man);
85761d06d6bSBaptiste Daroussin }
85861d06d6bSBaptiste Daroussin 
85961d06d6bSBaptiste Daroussin void
roff_man_free(struct roff_man * man)86061d06d6bSBaptiste Daroussin roff_man_free(struct roff_man *man)
86161d06d6bSBaptiste Daroussin {
86261d06d6bSBaptiste Daroussin 	roff_man_free1(man);
863*c1c95addSBrooks Davis 	free(man->os_r);
86461d06d6bSBaptiste Daroussin 	free(man);
86561d06d6bSBaptiste Daroussin }
86661d06d6bSBaptiste Daroussin 
86761d06d6bSBaptiste Daroussin struct roff_man *
roff_man_alloc(struct roff * roff,const char * os_s,int quick)8687295610fSBaptiste Daroussin roff_man_alloc(struct roff *roff, const char *os_s, int quick)
86961d06d6bSBaptiste Daroussin {
87061d06d6bSBaptiste Daroussin 	struct roff_man *man;
87161d06d6bSBaptiste Daroussin 
87261d06d6bSBaptiste Daroussin 	man = mandoc_calloc(1, sizeof(*man));
87361d06d6bSBaptiste Daroussin 	man->roff = roff;
87461d06d6bSBaptiste Daroussin 	man->os_s = os_s;
87561d06d6bSBaptiste Daroussin 	man->quick = quick;
87661d06d6bSBaptiste Daroussin 	roff_man_alloc1(man);
87761d06d6bSBaptiste Daroussin 	roff->man = man;
87861d06d6bSBaptiste Daroussin 	return man;
87961d06d6bSBaptiste Daroussin }
88061d06d6bSBaptiste Daroussin 
88161d06d6bSBaptiste Daroussin /* --- syntax tree handling ----------------------------------------------- */
88261d06d6bSBaptiste Daroussin 
88361d06d6bSBaptiste Daroussin struct roff_node *
roff_node_alloc(struct roff_man * man,int line,int pos,enum roff_type type,int tok)88461d06d6bSBaptiste Daroussin roff_node_alloc(struct roff_man *man, int line, int pos,
88561d06d6bSBaptiste Daroussin 	enum roff_type type, int tok)
88661d06d6bSBaptiste Daroussin {
88761d06d6bSBaptiste Daroussin 	struct roff_node	*n;
88861d06d6bSBaptiste Daroussin 
88961d06d6bSBaptiste Daroussin 	n = mandoc_calloc(1, sizeof(*n));
89061d06d6bSBaptiste Daroussin 	n->line = line;
89161d06d6bSBaptiste Daroussin 	n->pos = pos;
89261d06d6bSBaptiste Daroussin 	n->tok = tok;
89361d06d6bSBaptiste Daroussin 	n->type = type;
89461d06d6bSBaptiste Daroussin 	n->sec = man->lastsec;
89561d06d6bSBaptiste Daroussin 
89661d06d6bSBaptiste Daroussin 	if (man->flags & MDOC_SYNOPSIS)
89761d06d6bSBaptiste Daroussin 		n->flags |= NODE_SYNPRETTY;
89861d06d6bSBaptiste Daroussin 	else
89961d06d6bSBaptiste Daroussin 		n->flags &= ~NODE_SYNPRETTY;
9007295610fSBaptiste Daroussin 	if ((man->flags & (ROFF_NOFILL | ROFF_NONOFILL)) == ROFF_NOFILL)
9017295610fSBaptiste Daroussin 		n->flags |= NODE_NOFILL;
9027295610fSBaptiste Daroussin 	else
9037295610fSBaptiste Daroussin 		n->flags &= ~NODE_NOFILL;
90461d06d6bSBaptiste Daroussin 	if (man->flags & MDOC_NEWLINE)
90561d06d6bSBaptiste Daroussin 		n->flags |= NODE_LINE;
90661d06d6bSBaptiste Daroussin 	man->flags &= ~MDOC_NEWLINE;
90761d06d6bSBaptiste Daroussin 
90861d06d6bSBaptiste Daroussin 	return n;
90961d06d6bSBaptiste Daroussin }
91061d06d6bSBaptiste Daroussin 
91161d06d6bSBaptiste Daroussin void
roff_node_append(struct roff_man * man,struct roff_node * n)91261d06d6bSBaptiste Daroussin roff_node_append(struct roff_man *man, struct roff_node *n)
91361d06d6bSBaptiste Daroussin {
91461d06d6bSBaptiste Daroussin 
91561d06d6bSBaptiste Daroussin 	switch (man->next) {
91661d06d6bSBaptiste Daroussin 	case ROFF_NEXT_SIBLING:
91761d06d6bSBaptiste Daroussin 		if (man->last->next != NULL) {
91861d06d6bSBaptiste Daroussin 			n->next = man->last->next;
91961d06d6bSBaptiste Daroussin 			man->last->next->prev = n;
92061d06d6bSBaptiste Daroussin 		} else
92161d06d6bSBaptiste Daroussin 			man->last->parent->last = n;
92261d06d6bSBaptiste Daroussin 		man->last->next = n;
92361d06d6bSBaptiste Daroussin 		n->prev = man->last;
92461d06d6bSBaptiste Daroussin 		n->parent = man->last->parent;
92561d06d6bSBaptiste Daroussin 		break;
92661d06d6bSBaptiste Daroussin 	case ROFF_NEXT_CHILD:
92761d06d6bSBaptiste Daroussin 		if (man->last->child != NULL) {
92861d06d6bSBaptiste Daroussin 			n->next = man->last->child;
92961d06d6bSBaptiste Daroussin 			man->last->child->prev = n;
93061d06d6bSBaptiste Daroussin 		} else
93161d06d6bSBaptiste Daroussin 			man->last->last = n;
93261d06d6bSBaptiste Daroussin 		man->last->child = n;
93361d06d6bSBaptiste Daroussin 		n->parent = man->last;
93461d06d6bSBaptiste Daroussin 		break;
93561d06d6bSBaptiste Daroussin 	default:
93661d06d6bSBaptiste Daroussin 		abort();
93761d06d6bSBaptiste Daroussin 	}
93861d06d6bSBaptiste Daroussin 	man->last = n;
93961d06d6bSBaptiste Daroussin 
94061d06d6bSBaptiste Daroussin 	switch (n->type) {
94161d06d6bSBaptiste Daroussin 	case ROFFT_HEAD:
94261d06d6bSBaptiste Daroussin 		n->parent->head = n;
94361d06d6bSBaptiste Daroussin 		break;
94461d06d6bSBaptiste Daroussin 	case ROFFT_BODY:
94561d06d6bSBaptiste Daroussin 		if (n->end != ENDBODY_NOT)
94661d06d6bSBaptiste Daroussin 			return;
94761d06d6bSBaptiste Daroussin 		n->parent->body = n;
94861d06d6bSBaptiste Daroussin 		break;
94961d06d6bSBaptiste Daroussin 	case ROFFT_TAIL:
95061d06d6bSBaptiste Daroussin 		n->parent->tail = n;
95161d06d6bSBaptiste Daroussin 		break;
95261d06d6bSBaptiste Daroussin 	default:
95361d06d6bSBaptiste Daroussin 		return;
95461d06d6bSBaptiste Daroussin 	}
95561d06d6bSBaptiste Daroussin 
95661d06d6bSBaptiste Daroussin 	/*
95761d06d6bSBaptiste Daroussin 	 * Copy over the normalised-data pointer of our parent.  Not
95861d06d6bSBaptiste Daroussin 	 * everybody has one, but copying a null pointer is fine.
95961d06d6bSBaptiste Daroussin 	 */
96061d06d6bSBaptiste Daroussin 
96161d06d6bSBaptiste Daroussin 	n->norm = n->parent->norm;
96261d06d6bSBaptiste Daroussin 	assert(n->parent->type == ROFFT_BLOCK);
96361d06d6bSBaptiste Daroussin }
96461d06d6bSBaptiste Daroussin 
96561d06d6bSBaptiste Daroussin void
roff_word_alloc(struct roff_man * man,int line,int pos,const char * word)96661d06d6bSBaptiste Daroussin roff_word_alloc(struct roff_man *man, int line, int pos, const char *word)
96761d06d6bSBaptiste Daroussin {
96861d06d6bSBaptiste Daroussin 	struct roff_node	*n;
96961d06d6bSBaptiste Daroussin 
97061d06d6bSBaptiste Daroussin 	n = roff_node_alloc(man, line, pos, ROFFT_TEXT, TOKEN_NONE);
97161d06d6bSBaptiste Daroussin 	n->string = roff_strdup(man->roff, word);
97261d06d6bSBaptiste Daroussin 	roff_node_append(man, n);
97361d06d6bSBaptiste Daroussin 	n->flags |= NODE_VALID | NODE_ENDED;
97461d06d6bSBaptiste Daroussin 	man->next = ROFF_NEXT_SIBLING;
97561d06d6bSBaptiste Daroussin }
97661d06d6bSBaptiste Daroussin 
97761d06d6bSBaptiste Daroussin void
roff_word_append(struct roff_man * man,const char * word)97861d06d6bSBaptiste Daroussin roff_word_append(struct roff_man *man, const char *word)
97961d06d6bSBaptiste Daroussin {
98061d06d6bSBaptiste Daroussin 	struct roff_node	*n;
98161d06d6bSBaptiste Daroussin 	char			*addstr, *newstr;
98261d06d6bSBaptiste Daroussin 
98361d06d6bSBaptiste Daroussin 	n = man->last;
98461d06d6bSBaptiste Daroussin 	addstr = roff_strdup(man->roff, word);
98561d06d6bSBaptiste Daroussin 	mandoc_asprintf(&newstr, "%s %s", n->string, addstr);
98661d06d6bSBaptiste Daroussin 	free(addstr);
98761d06d6bSBaptiste Daroussin 	free(n->string);
98861d06d6bSBaptiste Daroussin 	n->string = newstr;
98961d06d6bSBaptiste Daroussin 	man->next = ROFF_NEXT_SIBLING;
99061d06d6bSBaptiste Daroussin }
99161d06d6bSBaptiste Daroussin 
99261d06d6bSBaptiste Daroussin void
roff_elem_alloc(struct roff_man * man,int line,int pos,int tok)99361d06d6bSBaptiste Daroussin roff_elem_alloc(struct roff_man *man, int line, int pos, int tok)
99461d06d6bSBaptiste Daroussin {
99561d06d6bSBaptiste Daroussin 	struct roff_node	*n;
99661d06d6bSBaptiste Daroussin 
99761d06d6bSBaptiste Daroussin 	n = roff_node_alloc(man, line, pos, ROFFT_ELEM, tok);
99861d06d6bSBaptiste Daroussin 	roff_node_append(man, n);
99961d06d6bSBaptiste Daroussin 	man->next = ROFF_NEXT_CHILD;
100061d06d6bSBaptiste Daroussin }
100161d06d6bSBaptiste Daroussin 
100261d06d6bSBaptiste Daroussin struct roff_node *
roff_block_alloc(struct roff_man * man,int line,int pos,int tok)100361d06d6bSBaptiste Daroussin roff_block_alloc(struct roff_man *man, int line, int pos, int tok)
100461d06d6bSBaptiste Daroussin {
100561d06d6bSBaptiste Daroussin 	struct roff_node	*n;
100661d06d6bSBaptiste Daroussin 
100761d06d6bSBaptiste Daroussin 	n = roff_node_alloc(man, line, pos, ROFFT_BLOCK, tok);
100861d06d6bSBaptiste Daroussin 	roff_node_append(man, n);
100961d06d6bSBaptiste Daroussin 	man->next = ROFF_NEXT_CHILD;
101061d06d6bSBaptiste Daroussin 	return n;
101161d06d6bSBaptiste Daroussin }
101261d06d6bSBaptiste Daroussin 
101361d06d6bSBaptiste Daroussin struct roff_node *
roff_head_alloc(struct roff_man * man,int line,int pos,int tok)101461d06d6bSBaptiste Daroussin roff_head_alloc(struct roff_man *man, int line, int pos, int tok)
101561d06d6bSBaptiste Daroussin {
101661d06d6bSBaptiste Daroussin 	struct roff_node	*n;
101761d06d6bSBaptiste Daroussin 
101861d06d6bSBaptiste Daroussin 	n = roff_node_alloc(man, line, pos, ROFFT_HEAD, tok);
101961d06d6bSBaptiste Daroussin 	roff_node_append(man, n);
102061d06d6bSBaptiste Daroussin 	man->next = ROFF_NEXT_CHILD;
102161d06d6bSBaptiste Daroussin 	return n;
102261d06d6bSBaptiste Daroussin }
102361d06d6bSBaptiste Daroussin 
102461d06d6bSBaptiste Daroussin struct roff_node *
roff_body_alloc(struct roff_man * man,int line,int pos,int tok)102561d06d6bSBaptiste Daroussin roff_body_alloc(struct roff_man *man, int line, int pos, int tok)
102661d06d6bSBaptiste Daroussin {
102761d06d6bSBaptiste Daroussin 	struct roff_node	*n;
102861d06d6bSBaptiste Daroussin 
102961d06d6bSBaptiste Daroussin 	n = roff_node_alloc(man, line, pos, ROFFT_BODY, tok);
103061d06d6bSBaptiste Daroussin 	roff_node_append(man, n);
103161d06d6bSBaptiste Daroussin 	man->next = ROFF_NEXT_CHILD;
103261d06d6bSBaptiste Daroussin 	return n;
103361d06d6bSBaptiste Daroussin }
103461d06d6bSBaptiste Daroussin 
103561d06d6bSBaptiste Daroussin static void
roff_addtbl(struct roff_man * man,int line,struct tbl_node * tbl)10367295610fSBaptiste Daroussin roff_addtbl(struct roff_man *man, int line, struct tbl_node *tbl)
103761d06d6bSBaptiste Daroussin {
103861d06d6bSBaptiste Daroussin 	struct roff_node	*n;
10397295610fSBaptiste Daroussin 	struct tbl_span		*span;
104061d06d6bSBaptiste Daroussin 
10417295610fSBaptiste Daroussin 	if (man->meta.macroset == MACROSET_MAN)
104261d06d6bSBaptiste Daroussin 		man_breakscope(man, ROFF_TS);
104361d06d6bSBaptiste Daroussin 	while ((span = tbl_span(tbl)) != NULL) {
10447295610fSBaptiste Daroussin 		n = roff_node_alloc(man, line, 0, ROFFT_TBL, TOKEN_NONE);
104561d06d6bSBaptiste Daroussin 		n->span = span;
104661d06d6bSBaptiste Daroussin 		roff_node_append(man, n);
104761d06d6bSBaptiste Daroussin 		n->flags |= NODE_VALID | NODE_ENDED;
104861d06d6bSBaptiste Daroussin 		man->next = ROFF_NEXT_SIBLING;
104961d06d6bSBaptiste Daroussin 	}
105061d06d6bSBaptiste Daroussin }
105161d06d6bSBaptiste Daroussin 
105261d06d6bSBaptiste Daroussin void
roff_node_unlink(struct roff_man * man,struct roff_node * n)105361d06d6bSBaptiste Daroussin roff_node_unlink(struct roff_man *man, struct roff_node *n)
105461d06d6bSBaptiste Daroussin {
105561d06d6bSBaptiste Daroussin 
105661d06d6bSBaptiste Daroussin 	/* Adjust siblings. */
105761d06d6bSBaptiste Daroussin 
105861d06d6bSBaptiste Daroussin 	if (n->prev)
105961d06d6bSBaptiste Daroussin 		n->prev->next = n->next;
106061d06d6bSBaptiste Daroussin 	if (n->next)
106161d06d6bSBaptiste Daroussin 		n->next->prev = n->prev;
106261d06d6bSBaptiste Daroussin 
106361d06d6bSBaptiste Daroussin 	/* Adjust parent. */
106461d06d6bSBaptiste Daroussin 
106561d06d6bSBaptiste Daroussin 	if (n->parent != NULL) {
106661d06d6bSBaptiste Daroussin 		if (n->parent->child == n)
106761d06d6bSBaptiste Daroussin 			n->parent->child = n->next;
106861d06d6bSBaptiste Daroussin 		if (n->parent->last == n)
106961d06d6bSBaptiste Daroussin 			n->parent->last = n->prev;
107061d06d6bSBaptiste Daroussin 	}
107161d06d6bSBaptiste Daroussin 
107261d06d6bSBaptiste Daroussin 	/* Adjust parse point. */
107361d06d6bSBaptiste Daroussin 
107461d06d6bSBaptiste Daroussin 	if (man == NULL)
107561d06d6bSBaptiste Daroussin 		return;
107661d06d6bSBaptiste Daroussin 	if (man->last == n) {
107761d06d6bSBaptiste Daroussin 		if (n->prev == NULL) {
107861d06d6bSBaptiste Daroussin 			man->last = n->parent;
107961d06d6bSBaptiste Daroussin 			man->next = ROFF_NEXT_CHILD;
108061d06d6bSBaptiste Daroussin 		} else {
108161d06d6bSBaptiste Daroussin 			man->last = n->prev;
108261d06d6bSBaptiste Daroussin 			man->next = ROFF_NEXT_SIBLING;
108361d06d6bSBaptiste Daroussin 		}
108461d06d6bSBaptiste Daroussin 	}
10857295610fSBaptiste Daroussin 	if (man->meta.first == n)
10867295610fSBaptiste Daroussin 		man->meta.first = NULL;
10877295610fSBaptiste Daroussin }
10887295610fSBaptiste Daroussin 
10897295610fSBaptiste Daroussin void
roff_node_relink(struct roff_man * man,struct roff_node * n)10907295610fSBaptiste Daroussin roff_node_relink(struct roff_man *man, struct roff_node *n)
10917295610fSBaptiste Daroussin {
10927295610fSBaptiste Daroussin 	roff_node_unlink(man, n);
10937295610fSBaptiste Daroussin 	n->prev = n->next = NULL;
10947295610fSBaptiste Daroussin 	roff_node_append(man, n);
109561d06d6bSBaptiste Daroussin }
109661d06d6bSBaptiste Daroussin 
109761d06d6bSBaptiste Daroussin void
roff_node_free(struct roff_node * n)109861d06d6bSBaptiste Daroussin roff_node_free(struct roff_node *n)
109961d06d6bSBaptiste Daroussin {
110061d06d6bSBaptiste Daroussin 
110161d06d6bSBaptiste Daroussin 	if (n->args != NULL)
110261d06d6bSBaptiste Daroussin 		mdoc_argv_free(n->args);
110361d06d6bSBaptiste Daroussin 	if (n->type == ROFFT_BLOCK || n->type == ROFFT_ELEM)
110461d06d6bSBaptiste Daroussin 		free(n->norm);
110561d06d6bSBaptiste Daroussin 	eqn_box_free(n->eqn);
110661d06d6bSBaptiste Daroussin 	free(n->string);
11076d38604fSBaptiste Daroussin 	free(n->tag);
110861d06d6bSBaptiste Daroussin 	free(n);
110961d06d6bSBaptiste Daroussin }
111061d06d6bSBaptiste Daroussin 
111161d06d6bSBaptiste Daroussin void
roff_node_delete(struct roff_man * man,struct roff_node * n)111261d06d6bSBaptiste Daroussin roff_node_delete(struct roff_man *man, struct roff_node *n)
111361d06d6bSBaptiste Daroussin {
111461d06d6bSBaptiste Daroussin 
111561d06d6bSBaptiste Daroussin 	while (n->child != NULL)
111661d06d6bSBaptiste Daroussin 		roff_node_delete(man, n->child);
111761d06d6bSBaptiste Daroussin 	roff_node_unlink(man, n);
111861d06d6bSBaptiste Daroussin 	roff_node_free(n);
111961d06d6bSBaptiste Daroussin }
112061d06d6bSBaptiste Daroussin 
11216d38604fSBaptiste Daroussin int
roff_node_transparent(struct roff_node * n)11226d38604fSBaptiste Daroussin roff_node_transparent(struct roff_node *n)
11236d38604fSBaptiste Daroussin {
11246d38604fSBaptiste Daroussin 	if (n == NULL)
11256d38604fSBaptiste Daroussin 		return 0;
11266d38604fSBaptiste Daroussin 	if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
11276d38604fSBaptiste Daroussin 		return 1;
11286d38604fSBaptiste Daroussin 	return roff_tok_transparent(n->tok);
11296d38604fSBaptiste Daroussin }
11306d38604fSBaptiste Daroussin 
11316d38604fSBaptiste Daroussin int
roff_tok_transparent(enum roff_tok tok)11326d38604fSBaptiste Daroussin roff_tok_transparent(enum roff_tok tok)
11336d38604fSBaptiste Daroussin {
11346d38604fSBaptiste Daroussin 	switch (tok) {
11356d38604fSBaptiste Daroussin 	case ROFF_ft:
11366d38604fSBaptiste Daroussin 	case ROFF_ll:
11376d38604fSBaptiste Daroussin 	case ROFF_mc:
11386d38604fSBaptiste Daroussin 	case ROFF_po:
11396d38604fSBaptiste Daroussin 	case ROFF_ta:
11406d38604fSBaptiste Daroussin 	case MDOC_Db:
11416d38604fSBaptiste Daroussin 	case MDOC_Es:
11426d38604fSBaptiste Daroussin 	case MDOC_Sm:
11436d38604fSBaptiste Daroussin 	case MDOC_Tg:
11446d38604fSBaptiste Daroussin 	case MAN_DT:
11456d38604fSBaptiste Daroussin 	case MAN_UC:
11466d38604fSBaptiste Daroussin 	case MAN_PD:
11476d38604fSBaptiste Daroussin 	case MAN_AT:
11486d38604fSBaptiste Daroussin 		return 1;
11496d38604fSBaptiste Daroussin 	default:
11506d38604fSBaptiste Daroussin 		return 0;
11516d38604fSBaptiste Daroussin 	}
11526d38604fSBaptiste Daroussin }
11536d38604fSBaptiste Daroussin 
11546d38604fSBaptiste Daroussin struct roff_node *
roff_node_child(struct roff_node * n)11556d38604fSBaptiste Daroussin roff_node_child(struct roff_node *n)
11566d38604fSBaptiste Daroussin {
11576d38604fSBaptiste Daroussin 	for (n = n->child; roff_node_transparent(n); n = n->next)
11586d38604fSBaptiste Daroussin 		continue;
11596d38604fSBaptiste Daroussin 	return n;
11606d38604fSBaptiste Daroussin }
11616d38604fSBaptiste Daroussin 
11626d38604fSBaptiste Daroussin struct roff_node *
roff_node_prev(struct roff_node * n)11636d38604fSBaptiste Daroussin roff_node_prev(struct roff_node *n)
11646d38604fSBaptiste Daroussin {
11656d38604fSBaptiste Daroussin 	do {
11666d38604fSBaptiste Daroussin 		n = n->prev;
11676d38604fSBaptiste Daroussin 	} while (roff_node_transparent(n));
11686d38604fSBaptiste Daroussin 	return n;
11696d38604fSBaptiste Daroussin }
11706d38604fSBaptiste Daroussin 
11716d38604fSBaptiste Daroussin struct roff_node *
roff_node_next(struct roff_node * n)11726d38604fSBaptiste Daroussin roff_node_next(struct roff_node *n)
11736d38604fSBaptiste Daroussin {
11746d38604fSBaptiste Daroussin 	do {
11756d38604fSBaptiste Daroussin 		n = n->next;
11766d38604fSBaptiste Daroussin 	} while (roff_node_transparent(n));
11776d38604fSBaptiste Daroussin 	return n;
11786d38604fSBaptiste Daroussin }
11796d38604fSBaptiste Daroussin 
118061d06d6bSBaptiste Daroussin void
deroff(char ** dest,const struct roff_node * n)118161d06d6bSBaptiste Daroussin deroff(char **dest, const struct roff_node *n)
118261d06d6bSBaptiste Daroussin {
118361d06d6bSBaptiste Daroussin 	char	*cp;
118461d06d6bSBaptiste Daroussin 	size_t	 sz;
118561d06d6bSBaptiste Daroussin 
11866d38604fSBaptiste Daroussin 	if (n->string == NULL) {
118761d06d6bSBaptiste Daroussin 		for (n = n->child; n != NULL; n = n->next)
118861d06d6bSBaptiste Daroussin 			deroff(dest, n);
118961d06d6bSBaptiste Daroussin 		return;
119061d06d6bSBaptiste Daroussin 	}
119161d06d6bSBaptiste Daroussin 
119261d06d6bSBaptiste Daroussin 	/* Skip leading whitespace. */
119361d06d6bSBaptiste Daroussin 
119461d06d6bSBaptiste Daroussin 	for (cp = n->string; *cp != '\0'; cp++) {
119561d06d6bSBaptiste Daroussin 		if (cp[0] == '\\' && cp[1] != '\0' &&
119661d06d6bSBaptiste Daroussin 		    strchr(" %&0^|~", cp[1]) != NULL)
119761d06d6bSBaptiste Daroussin 			cp++;
119861d06d6bSBaptiste Daroussin 		else if ( ! isspace((unsigned char)*cp))
119961d06d6bSBaptiste Daroussin 			break;
120061d06d6bSBaptiste Daroussin 	}
120161d06d6bSBaptiste Daroussin 
120261d06d6bSBaptiste Daroussin 	/* Skip trailing backslash. */
120361d06d6bSBaptiste Daroussin 
120461d06d6bSBaptiste Daroussin 	sz = strlen(cp);
120561d06d6bSBaptiste Daroussin 	if (sz > 0 && cp[sz - 1] == '\\')
120661d06d6bSBaptiste Daroussin 		sz--;
120761d06d6bSBaptiste Daroussin 
120861d06d6bSBaptiste Daroussin 	/* Skip trailing whitespace. */
120961d06d6bSBaptiste Daroussin 
121061d06d6bSBaptiste Daroussin 	for (; sz; sz--)
121161d06d6bSBaptiste Daroussin 		if ( ! isspace((unsigned char)cp[sz-1]))
121261d06d6bSBaptiste Daroussin 			break;
121361d06d6bSBaptiste Daroussin 
121461d06d6bSBaptiste Daroussin 	/* Skip empty strings. */
121561d06d6bSBaptiste Daroussin 
121661d06d6bSBaptiste Daroussin 	if (sz == 0)
121761d06d6bSBaptiste Daroussin 		return;
121861d06d6bSBaptiste Daroussin 
121961d06d6bSBaptiste Daroussin 	if (*dest == NULL) {
122061d06d6bSBaptiste Daroussin 		*dest = mandoc_strndup(cp, sz);
122161d06d6bSBaptiste Daroussin 		return;
122261d06d6bSBaptiste Daroussin 	}
122361d06d6bSBaptiste Daroussin 
122461d06d6bSBaptiste Daroussin 	mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp);
122561d06d6bSBaptiste Daroussin 	free(*dest);
122661d06d6bSBaptiste Daroussin 	*dest = cp;
122761d06d6bSBaptiste Daroussin }
122861d06d6bSBaptiste Daroussin 
122961d06d6bSBaptiste Daroussin /* --- main functions of the roff parser ---------------------------------- */
123061d06d6bSBaptiste Daroussin 
123161d06d6bSBaptiste Daroussin /*
1232*c1c95addSBrooks Davis  * Save comments preceding the title macro, for example in order to
1233*c1c95addSBrooks Davis  * preserve Copyright and license headers in HTML output,
1234*c1c95addSBrooks Davis  * provide diagnostics about RCS ids and trailing whitespace in comments,
1235*c1c95addSBrooks Davis  * then discard comments including preceding whitespace.
1236*c1c95addSBrooks Davis  * This function also handles input line continuation.
123761d06d6bSBaptiste Daroussin  */
12387295610fSBaptiste Daroussin static int
roff_parse_comment(struct roff * r,struct buf * buf,int ln,int pos,char ec)1239*c1c95addSBrooks Davis roff_parse_comment(struct roff *r, struct buf *buf, int ln, int pos, char ec)
124061d06d6bSBaptiste Daroussin {
124161d06d6bSBaptiste Daroussin 	struct roff_node *n;	/* used for header comments */
124261d06d6bSBaptiste Daroussin 	const char	*start;	/* start of the string to process */
1243*c1c95addSBrooks Davis 	const char	*cp;	/* for RCS id parsing */
124461d06d6bSBaptiste Daroussin 	char		*stesc;	/* start of an escape sequence ('\\') */
124561d06d6bSBaptiste Daroussin 	char		*ep;	/* end of comment string */
124661d06d6bSBaptiste Daroussin 	int		 rcsid;	/* kind of RCS id seen */
124761d06d6bSBaptiste Daroussin 
1248*c1c95addSBrooks Davis 	for (start = stesc = buf->buf + pos;; stesc++) {
1249*c1c95addSBrooks Davis 		/*
1250*c1c95addSBrooks Davis 		 * XXX Ugly hack: Remove the newline character that
1251*c1c95addSBrooks Davis 		 * mparse_buf_r() appended to mark the end of input
1252*c1c95addSBrooks Davis 		 * if it is not preceded by an escape character.
1253*c1c95addSBrooks Davis 		 */
1254*c1c95addSBrooks Davis 		if (stesc[0] == '\n') {
1255*c1c95addSBrooks Davis 			assert(stesc[1] == '\0');
1256*c1c95addSBrooks Davis 			stesc[0] = '\0';
1257*c1c95addSBrooks Davis 		}
125861d06d6bSBaptiste Daroussin 
1259*c1c95addSBrooks Davis 		/* The line ends without continuation or comment. */
1260*c1c95addSBrooks Davis 		if (stesc[0] == '\0')
1261*c1c95addSBrooks Davis 			return ROFF_CONT;
1262*c1c95addSBrooks Davis 
1263*c1c95addSBrooks Davis 		/* Unescaped byte: skip it. */
1264*c1c95addSBrooks Davis 		if (stesc[0] != ec)
126561d06d6bSBaptiste Daroussin 			continue;
1266*c1c95addSBrooks Davis 
1267*c1c95addSBrooks Davis 		/*
1268*c1c95addSBrooks Davis 		 * XXX Ugly hack: Do not attempt to append another line
1269*c1c95addSBrooks Davis 		 * if the function mparse_buf_r() appended a newline
1270*c1c95addSBrooks Davis 		 * character to indicate the end of input.
1271*c1c95addSBrooks Davis 		 */
1272*c1c95addSBrooks Davis 		if (stesc[1] == '\n') {
1273*c1c95addSBrooks Davis 			assert(stesc[2] == '\0');
1274*c1c95addSBrooks Davis 			stesc[0] = '\0';
1275*c1c95addSBrooks Davis 			return ROFF_CONT;
1276*c1c95addSBrooks Davis 		}
1277*c1c95addSBrooks Davis 
1278*c1c95addSBrooks Davis 		/*
1279*c1c95addSBrooks Davis 		 * An escape character at the end of an input line
1280*c1c95addSBrooks Davis 		 * requests line continuation.
1281*c1c95addSBrooks Davis 		 */
1282*c1c95addSBrooks Davis 		if (stesc[1] == '\0') {
1283*c1c95addSBrooks Davis 			stesc[0] = '\0';
1284*c1c95addSBrooks Davis 			return ROFF_IGN | ROFF_APPEND;
1285*c1c95addSBrooks Davis 		}
1286*c1c95addSBrooks Davis 
1287*c1c95addSBrooks Davis 		/* Found a comment: process it. */
1288*c1c95addSBrooks Davis 		if (stesc[1] == '"' || stesc[1] == '#')
1289*c1c95addSBrooks Davis 			break;
1290*c1c95addSBrooks Davis 
1291*c1c95addSBrooks Davis 		/* Escaped escape character: skip them both. */
1292*c1c95addSBrooks Davis 		if (stesc[1] == ec)
129361d06d6bSBaptiste Daroussin 			stesc++;
1294*c1c95addSBrooks Davis 	}
129561d06d6bSBaptiste Daroussin 
1296*c1c95addSBrooks Davis 	/* Look for an RCS id in the comment. */
129761d06d6bSBaptiste Daroussin 
129861d06d6bSBaptiste Daroussin 	rcsid = 0;
1299*c1c95addSBrooks Davis 	if ((cp = strstr(stesc + 2, "$" "OpenBSD")) != NULL) {
130061d06d6bSBaptiste Daroussin 		rcsid = 1 << MANDOC_OS_OPENBSD;
130161d06d6bSBaptiste Daroussin 		cp += 8;
1302*c1c95addSBrooks Davis 	} else if ((cp = strstr(stesc + 2, "$" "NetBSD")) != NULL) {
130361d06d6bSBaptiste Daroussin 		rcsid = 1 << MANDOC_OS_NETBSD;
130461d06d6bSBaptiste Daroussin 		cp += 7;
130561d06d6bSBaptiste Daroussin 	}
1306*c1c95addSBrooks Davis 	if (cp != NULL && isalnum((unsigned char)*cp) == 0 &&
130761d06d6bSBaptiste Daroussin 	    strchr(cp, '$') != NULL) {
130861d06d6bSBaptiste Daroussin 		if (r->man->meta.rcsids & rcsid)
13097295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_RCS_REP, ln,
1310*c1c95addSBrooks Davis 			    (int)(stesc - buf->buf) + 2, "%s", stesc + 1);
131161d06d6bSBaptiste Daroussin 		r->man->meta.rcsids |= rcsid;
131261d06d6bSBaptiste Daroussin 	}
131361d06d6bSBaptiste Daroussin 
1314*c1c95addSBrooks Davis 	/* Warn about trailing whitespace at the end of the comment. */
131561d06d6bSBaptiste Daroussin 
1316*c1c95addSBrooks Davis 	ep = strchr(stesc + 2, '\0') - 1;
1317*c1c95addSBrooks Davis 	if (*ep == '\n')
1318*c1c95addSBrooks Davis 		*ep-- = '\0';
131961d06d6bSBaptiste Daroussin 	if (*ep == ' ' || *ep == '\t')
13207295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_SPACE_EOL,
13217295610fSBaptiste Daroussin 		    ln, (int)(ep - buf->buf), NULL);
132261d06d6bSBaptiste Daroussin 
1323*c1c95addSBrooks Davis 	/* Save comments preceding the title macro in the syntax tree. */
132461d06d6bSBaptiste Daroussin 
1325*c1c95addSBrooks Davis 	if (r->options & MPARSE_COMMENT) {
132661d06d6bSBaptiste Daroussin 		while (*ep == ' ' || *ep == '\t')
132761d06d6bSBaptiste Daroussin 			ep--;
132861d06d6bSBaptiste Daroussin 		ep[1] = '\0';
1329*c1c95addSBrooks Davis 		n = roff_node_alloc(r->man, ln, stesc + 1 - buf->buf,
133061d06d6bSBaptiste Daroussin 		    ROFFT_COMMENT, TOKEN_NONE);
133161d06d6bSBaptiste Daroussin 		n->string = mandoc_strdup(stesc + 2);
133261d06d6bSBaptiste Daroussin 		roff_node_append(r->man, n);
133361d06d6bSBaptiste Daroussin 		n->flags |= NODE_VALID | NODE_ENDED;
133461d06d6bSBaptiste Daroussin 		r->man->next = ROFF_NEXT_SIBLING;
133561d06d6bSBaptiste Daroussin 	}
133661d06d6bSBaptiste Daroussin 
1337*c1c95addSBrooks Davis 	/* The comment requests line continuation. */
133861d06d6bSBaptiste Daroussin 
13397295610fSBaptiste Daroussin 	if (stesc[1] == '#') {
13407295610fSBaptiste Daroussin 		*stesc = '\0';
13417295610fSBaptiste Daroussin 		return ROFF_IGN | ROFF_APPEND;
13427295610fSBaptiste Daroussin 	}
13437295610fSBaptiste Daroussin 
1344*c1c95addSBrooks Davis 	/* Discard the comment including preceding whitespace. */
13457295610fSBaptiste Daroussin 
13467295610fSBaptiste Daroussin 	while (stesc > start && stesc[-1] == ' ' &&
13477295610fSBaptiste Daroussin 	    (stesc == start + 1 || stesc[-2] != '\\'))
134861d06d6bSBaptiste Daroussin 		stesc--;
134961d06d6bSBaptiste Daroussin 	*stesc = '\0';
135061d06d6bSBaptiste Daroussin 	return ROFF_CONT;
135161d06d6bSBaptiste Daroussin }
135261d06d6bSBaptiste Daroussin 
1353*c1c95addSBrooks Davis /*
1354*c1c95addSBrooks Davis  * In the current line, expand escape sequences that produce parsable
1355*c1c95addSBrooks Davis  * input text.  Also check the syntax of the remaining escape sequences,
1356*c1c95addSBrooks Davis  * which typically produce output glyphs or change formatter state.
1357*c1c95addSBrooks Davis  */
1358*c1c95addSBrooks Davis static int
roff_expand(struct roff * r,struct buf * buf,int ln,int pos,char ec)1359*c1c95addSBrooks Davis roff_expand(struct roff *r, struct buf *buf, int ln, int pos, char ec)
1360*c1c95addSBrooks Davis {
1361*c1c95addSBrooks Davis 	char		 ubuf[24];	/* buffer to print a number */
1362*c1c95addSBrooks Davis 	struct mctx	*ctx;		/* current macro call context */
1363*c1c95addSBrooks Davis 	const char	*res;		/* the string to be pasted */
1364*c1c95addSBrooks Davis 	const char	*src;		/* source for copying */
1365*c1c95addSBrooks Davis 	char		*dst;		/* destination for copying */
1366*c1c95addSBrooks Davis 	enum mandoc_esc	 subtype;	/* return value from roff_escape */
1367*c1c95addSBrooks Davis 	int		 iesc;		/* index of leading escape char */
1368*c1c95addSBrooks Davis 	int		 inam;		/* index of the escape name */
1369*c1c95addSBrooks Davis 	int		 iarg;		/* index beginning the argument */
1370*c1c95addSBrooks Davis 	int		 iendarg;	/* index right after the argument */
1371*c1c95addSBrooks Davis 	int		 iend;		/* index right after the sequence */
1372*c1c95addSBrooks Davis 	int		 isrc, idst;	/* to reduce \\ and \. in names */
1373*c1c95addSBrooks Davis 	int		 deftype;	/* type of definition to paste */
1374*c1c95addSBrooks Davis 	int		 argi;		/* macro argument index */
1375*c1c95addSBrooks Davis 	int		 quote_args;	/* true for \\$@, false for \\$* */
1376*c1c95addSBrooks Davis 	int		 asz;		/* length of the replacement */
1377*c1c95addSBrooks Davis 	int		 rsz;		/* length of the rest of the string */
1378*c1c95addSBrooks Davis 	int		 npos;		/* position in numeric expression */
1379*c1c95addSBrooks Davis 	int		 expand_count;	/* to avoid infinite loops */
1380*c1c95addSBrooks Davis 
138161d06d6bSBaptiste Daroussin 	expand_count = 0;
1382*c1c95addSBrooks Davis 	while (buf->buf[pos] != '\0') {
138361d06d6bSBaptiste Daroussin 
13847295610fSBaptiste Daroussin 		/*
1385*c1c95addSBrooks Davis 		 * Skip plain ASCII characters.
13867295610fSBaptiste Daroussin 		 * If we have a non-standard escape character,
1387*c1c95addSBrooks Davis 		 * escape literal backslashes because all processing in
1388*c1c95addSBrooks Davis 		 * subsequent functions uses the standard escaping rules.
13897295610fSBaptiste Daroussin 		 */
139061d06d6bSBaptiste Daroussin 
1391*c1c95addSBrooks Davis 		if (buf->buf[pos] != ec) {
1392*c1c95addSBrooks Davis 			if (buf->buf[pos] == '\\') {
1393*c1c95addSBrooks Davis 				roff_expand_patch(buf, pos, "\\e", pos + 1);
1394*c1c95addSBrooks Davis 				pos++;
139561d06d6bSBaptiste Daroussin 			}
1396*c1c95addSBrooks Davis 			pos++;
139761d06d6bSBaptiste Daroussin 			continue;
139861d06d6bSBaptiste Daroussin 		}
139961d06d6bSBaptiste Daroussin 
1400*c1c95addSBrooks Davis 		/*
1401*c1c95addSBrooks Davis 		 * Parse escape sequences,
1402*c1c95addSBrooks Davis 		 * issue diagnostic messages when appropriate,
1403*c1c95addSBrooks Davis 		 * and skip sequences that do not need expansion.
1404*c1c95addSBrooks Davis 		 * If we have a non-standard escape character, translate
1405*c1c95addSBrooks Davis 		 * it to backslashes and translate backslashes to \e.
1406*c1c95addSBrooks Davis 		 */
140761d06d6bSBaptiste Daroussin 
1408*c1c95addSBrooks Davis 		if (roff_escape(buf->buf, ln, pos, &iesc, &inam,
1409*c1c95addSBrooks Davis 		    &iarg, &iendarg, &iend) != ESCAPE_EXPAND) {
1410*c1c95addSBrooks Davis 			while (pos < iend) {
1411*c1c95addSBrooks Davis 				if (buf->buf[pos] == ec) {
1412*c1c95addSBrooks Davis 					buf->buf[pos] = '\\';
1413*c1c95addSBrooks Davis 					if (pos + 1 < iend)
1414*c1c95addSBrooks Davis 						pos++;
1415*c1c95addSBrooks Davis 				} else if (buf->buf[pos] == '\\') {
1416*c1c95addSBrooks Davis 					roff_expand_patch(buf,
1417*c1c95addSBrooks Davis 					    pos, "\\e", pos + 1);
1418*c1c95addSBrooks Davis 					pos++;
1419*c1c95addSBrooks Davis 					iend++;
1420*c1c95addSBrooks Davis 				}
1421*c1c95addSBrooks Davis 				pos++;
1422*c1c95addSBrooks Davis 			}
142361d06d6bSBaptiste Daroussin 			continue;
142461d06d6bSBaptiste Daroussin 		}
142561d06d6bSBaptiste Daroussin 
1426*c1c95addSBrooks Davis 		/* Reduce \\ and \. in names. */
142761d06d6bSBaptiste Daroussin 
1428*c1c95addSBrooks Davis 		if (buf->buf[inam] == '*' || buf->buf[inam] == 'n') {
1429*c1c95addSBrooks Davis 			isrc = idst = iarg;
1430*c1c95addSBrooks Davis 			while (isrc < iendarg) {
1431*c1c95addSBrooks Davis 				if (isrc + 1 < iendarg &&
1432*c1c95addSBrooks Davis 				    buf->buf[isrc] == '\\' &&
1433*c1c95addSBrooks Davis 				    (buf->buf[isrc + 1] == '\\' ||
1434*c1c95addSBrooks Davis 				     buf->buf[isrc + 1] == '.'))
1435*c1c95addSBrooks Davis 					isrc++;
1436*c1c95addSBrooks Davis 				buf->buf[idst++] = buf->buf[isrc++];
1437*c1c95addSBrooks Davis 			}
1438*c1c95addSBrooks Davis 			iendarg -= isrc - idst;
1439*c1c95addSBrooks Davis 		}
1440*c1c95addSBrooks Davis 
1441*c1c95addSBrooks Davis 		/* Handle expansion. */
1442*c1c95addSBrooks Davis 
144361d06d6bSBaptiste Daroussin 		res = NULL;
1444*c1c95addSBrooks Davis 		switch (buf->buf[inam]) {
144561d06d6bSBaptiste Daroussin 		case '*':
1446*c1c95addSBrooks Davis 			if (iendarg == iarg)
1447*c1c95addSBrooks Davis 				break;
144861d06d6bSBaptiste Daroussin 			deftype = ROFFDEF_USER | ROFFDEF_PRE;
1449*c1c95addSBrooks Davis 			if ((res = roff_getstrn(r, buf->buf + iarg,
1450*c1c95addSBrooks Davis 			    iendarg - iarg, &deftype)) != NULL)
1451*c1c95addSBrooks Davis 				break;
14527295610fSBaptiste Daroussin 
14537295610fSBaptiste Daroussin 			/*
1454*c1c95addSBrooks Davis 			 * If not overridden,
1455*c1c95addSBrooks Davis 			 * let \*(.T through to the formatters.
14567295610fSBaptiste Daroussin 			 */
14577295610fSBaptiste Daroussin 
1458*c1c95addSBrooks Davis 			if (iendarg - iarg == 2 &&
1459*c1c95addSBrooks Davis 			    buf->buf[iarg] == '.' &&
1460*c1c95addSBrooks Davis 			    buf->buf[iarg + 1] == 'T') {
1461*c1c95addSBrooks Davis 				roff_setstrn(&r->strtab, ".T", 2, NULL, 0, 0);
1462*c1c95addSBrooks Davis 				pos = iend;
14637295610fSBaptiste Daroussin 				continue;
14647295610fSBaptiste Daroussin 			}
1465*c1c95addSBrooks Davis 
1466*c1c95addSBrooks Davis 			mandoc_msg(MANDOCERR_STR_UNDEF, ln, iesc,
1467*c1c95addSBrooks Davis 			    "%.*s", iendarg - iarg, buf->buf + iarg);
146861d06d6bSBaptiste Daroussin 			break;
1469*c1c95addSBrooks Davis 
14707295610fSBaptiste Daroussin 		case '$':
14717295610fSBaptiste Daroussin 			if (r->mstackpos < 0) {
1472*c1c95addSBrooks Davis 				mandoc_msg(MANDOCERR_ARG_UNDEF, ln, iesc,
1473*c1c95addSBrooks Davis 				    "%.*s", iend - iesc, buf->buf + iesc);
14747295610fSBaptiste Daroussin 				break;
14757295610fSBaptiste Daroussin 			}
14767295610fSBaptiste Daroussin 			ctx = r->mstack + r->mstackpos;
1477*c1c95addSBrooks Davis 			argi = buf->buf[iarg] - '1';
1478*c1c95addSBrooks Davis 			if (argi >= 0 && argi <= 8) {
1479*c1c95addSBrooks Davis 				if (argi < ctx->argc)
1480*c1c95addSBrooks Davis 					res = ctx->argv[argi];
14817295610fSBaptiste Daroussin 				break;
14827295610fSBaptiste Daroussin 			}
1483*c1c95addSBrooks Davis 			if (buf->buf[iarg] == '*')
14847295610fSBaptiste Daroussin 				quote_args = 0;
1485*c1c95addSBrooks Davis 			else if (buf->buf[iarg] == '@')
14867295610fSBaptiste Daroussin 				quote_args = 1;
14877295610fSBaptiste Daroussin 			else {
1488*c1c95addSBrooks Davis 				mandoc_msg(MANDOCERR_ARG_NONUM, ln, iesc,
1489*c1c95addSBrooks Davis 				    "%.*s", iend - iesc, buf->buf + iesc);
14907295610fSBaptiste Daroussin 				break;
14917295610fSBaptiste Daroussin 			}
14927295610fSBaptiste Daroussin 			asz = 0;
1493*c1c95addSBrooks Davis 			for (argi = 0; argi < ctx->argc; argi++) {
1494*c1c95addSBrooks Davis 				if (argi)
14957295610fSBaptiste Daroussin 					asz++;  /* blank */
14967295610fSBaptiste Daroussin 				if (quote_args)
14977295610fSBaptiste Daroussin 					asz += 2;  /* quotes */
1498*c1c95addSBrooks Davis 				asz += strlen(ctx->argv[argi]);
14997295610fSBaptiste Daroussin 			}
1500*c1c95addSBrooks Davis 			if (asz != iend - iesc) {
1501*c1c95addSBrooks Davis 				rsz = buf->sz - iend;
1502*c1c95addSBrooks Davis 				if (asz < iend - iesc)
1503*c1c95addSBrooks Davis 					memmove(buf->buf + iesc + asz,
1504*c1c95addSBrooks Davis 					    buf->buf + iend, rsz);
1505*c1c95addSBrooks Davis 				buf->sz = iesc + asz + rsz;
1506*c1c95addSBrooks Davis 				buf->buf = mandoc_realloc(buf->buf, buf->sz);
1507*c1c95addSBrooks Davis 				if (asz > iend - iesc)
1508*c1c95addSBrooks Davis 					memmove(buf->buf + iesc + asz,
1509*c1c95addSBrooks Davis 					    buf->buf + iend, rsz);
15107295610fSBaptiste Daroussin 			}
1511*c1c95addSBrooks Davis 			dst = buf->buf + iesc;
1512*c1c95addSBrooks Davis 			for (argi = 0; argi < ctx->argc; argi++) {
1513*c1c95addSBrooks Davis 				if (argi)
1514*c1c95addSBrooks Davis 					*dst++ = ' ';
15157295610fSBaptiste Daroussin 				if (quote_args)
1516*c1c95addSBrooks Davis 					*dst++ = '"';
1517*c1c95addSBrooks Davis 				src = ctx->argv[argi];
1518*c1c95addSBrooks Davis 				while (*src != '\0')
1519*c1c95addSBrooks Davis 					*dst++ = *src++;
15207295610fSBaptiste Daroussin 				if (quote_args)
1521*c1c95addSBrooks Davis 					*dst++ = '"';
15227295610fSBaptiste Daroussin 			}
15237295610fSBaptiste Daroussin 			continue;
1524*c1c95addSBrooks Davis 		case 'A':
1525*c1c95addSBrooks Davis 			ubuf[0] = iendarg > iarg ? '1' : '0';
1526*c1c95addSBrooks Davis 			ubuf[1] = '\0';
1527*c1c95addSBrooks Davis 			res = ubuf;
1528*c1c95addSBrooks Davis 			break;
152961d06d6bSBaptiste Daroussin 		case 'B':
153061d06d6bSBaptiste Daroussin 			npos = 0;
1531*c1c95addSBrooks Davis 			ubuf[0] = iendarg > iarg && iend > iendarg &&
1532*c1c95addSBrooks Davis 			    roff_evalnum(r, ln, buf->buf + iarg, &npos,
153361d06d6bSBaptiste Daroussin 					 NULL, ROFFNUM_SCALE) &&
1534*c1c95addSBrooks Davis 			    npos == iendarg - iarg ? '1' : '0';
153561d06d6bSBaptiste Daroussin 			ubuf[1] = '\0';
1536*c1c95addSBrooks Davis 			res = ubuf;
1537*c1c95addSBrooks Davis 			break;
1538*c1c95addSBrooks Davis 		case 'V':
1539*c1c95addSBrooks Davis 			mandoc_msg(MANDOCERR_UNSUPP, ln, iesc,
1540*c1c95addSBrooks Davis 			    "%.*s", iend - iesc, buf->buf + iesc);
1541*c1c95addSBrooks Davis 			roff_expand_patch(buf, iendarg, "}", iend);
1542*c1c95addSBrooks Davis 			roff_expand_patch(buf, iesc, "${", iarg);
1543*c1c95addSBrooks Davis 			continue;
1544*c1c95addSBrooks Davis 		case 'g':
154561d06d6bSBaptiste Daroussin 			break;
154661d06d6bSBaptiste Daroussin 		case 'n':
1547*c1c95addSBrooks Davis 			if (iendarg > iarg)
154861d06d6bSBaptiste Daroussin 				(void)snprintf(ubuf, sizeof(ubuf), "%d",
1549*c1c95addSBrooks Davis 				    roff_getregn(r, buf->buf + iarg,
1550*c1c95addSBrooks Davis 				    iendarg - iarg, buf->buf[inam + 1]));
155161d06d6bSBaptiste Daroussin 			else
155261d06d6bSBaptiste Daroussin 				ubuf[0] = '\0';
1553*c1c95addSBrooks Davis 			res = ubuf;
155461d06d6bSBaptiste Daroussin 			break;
155561d06d6bSBaptiste Daroussin 		case 'w':
1556*c1c95addSBrooks Davis 			rsz = 0;
1557*c1c95addSBrooks Davis 			subtype = ESCAPE_UNDEF;
1558*c1c95addSBrooks Davis 			while (iarg < iendarg) {
1559*c1c95addSBrooks Davis 				asz = subtype == ESCAPE_SKIPCHAR ? 0 : 1;
1560*c1c95addSBrooks Davis 				if (buf->buf[iarg] != '\\') {
1561*c1c95addSBrooks Davis 					rsz += asz;
1562*c1c95addSBrooks Davis 					iarg++;
1563*c1c95addSBrooks Davis 					continue;
1564*c1c95addSBrooks Davis 				}
1565*c1c95addSBrooks Davis 				switch ((subtype = roff_escape(buf->buf, 0,
1566*c1c95addSBrooks Davis 				    iarg, NULL, NULL, NULL, NULL, &iarg))) {
1567*c1c95addSBrooks Davis 				case ESCAPE_SPECIAL:
1568*c1c95addSBrooks Davis 				case ESCAPE_NUMBERED:
1569*c1c95addSBrooks Davis 				case ESCAPE_UNICODE:
1570*c1c95addSBrooks Davis 				case ESCAPE_OVERSTRIKE:
1571*c1c95addSBrooks Davis 				case ESCAPE_UNDEF:
1572*c1c95addSBrooks Davis 					break;
1573*c1c95addSBrooks Davis 				case ESCAPE_DEVICE:
1574*c1c95addSBrooks Davis 					asz *= 8;
1575*c1c95addSBrooks Davis 					break;
1576*c1c95addSBrooks Davis 				case ESCAPE_EXPAND:
1577*c1c95addSBrooks Davis 					abort();
1578*c1c95addSBrooks Davis 				default:
1579*c1c95addSBrooks Davis 					continue;
1580*c1c95addSBrooks Davis 				}
1581*c1c95addSBrooks Davis 				rsz += asz;
1582*c1c95addSBrooks Davis 			}
1583*c1c95addSBrooks Davis 			(void)snprintf(ubuf, sizeof(ubuf), "%d", rsz * 24);
1584*c1c95addSBrooks Davis 			res = ubuf;
1585*c1c95addSBrooks Davis 			break;
1586*c1c95addSBrooks Davis 		default:
158761d06d6bSBaptiste Daroussin 			break;
158861d06d6bSBaptiste Daroussin 		}
1589*c1c95addSBrooks Davis 		if (res == NULL)
159061d06d6bSBaptiste Daroussin 			res = "";
1591*c1c95addSBrooks Davis 		if (++expand_count > EXPAND_LIMIT ||
1592*c1c95addSBrooks Davis 		    buf->sz + strlen(res) > SHRT_MAX) {
1593*c1c95addSBrooks Davis 			mandoc_msg(MANDOCERR_ROFFLOOP, ln, iesc, NULL);
159461d06d6bSBaptiste Daroussin 			return ROFF_IGN;
159561d06d6bSBaptiste Daroussin 		}
1596*c1c95addSBrooks Davis 		roff_expand_patch(buf, iesc, res, iend);
159761d06d6bSBaptiste Daroussin 	}
159861d06d6bSBaptiste Daroussin 	return ROFF_CONT;
159961d06d6bSBaptiste Daroussin }
160061d06d6bSBaptiste Daroussin 
160161d06d6bSBaptiste Daroussin /*
1602*c1c95addSBrooks Davis  * Replace the substring from the start position (inclusive)
1603*c1c95addSBrooks Davis  * to end position (exclusive) with the repl(acement) string.
1604*c1c95addSBrooks Davis  */
1605*c1c95addSBrooks Davis static void
roff_expand_patch(struct buf * buf,int start,const char * repl,int end)1606*c1c95addSBrooks Davis roff_expand_patch(struct buf *buf, int start, const char *repl, int end)
1607*c1c95addSBrooks Davis {
1608*c1c95addSBrooks Davis 	char	*nbuf;
1609*c1c95addSBrooks Davis 
1610*c1c95addSBrooks Davis 	buf->sz = mandoc_asprintf(&nbuf, "%.*s%s%s", start, buf->buf,
1611*c1c95addSBrooks Davis 	    repl, buf->buf + end) + 1;
1612*c1c95addSBrooks Davis 	free(buf->buf);
1613*c1c95addSBrooks Davis 	buf->buf = nbuf;
1614*c1c95addSBrooks Davis }
1615*c1c95addSBrooks Davis 
1616*c1c95addSBrooks Davis /*
16177295610fSBaptiste Daroussin  * Parse a quoted or unquoted roff-style request or macro argument.
16187295610fSBaptiste Daroussin  * Return a pointer to the parsed argument, which is either the original
16197295610fSBaptiste Daroussin  * pointer or advanced by one byte in case the argument is quoted.
16207295610fSBaptiste Daroussin  * NUL-terminate the argument in place.
16217295610fSBaptiste Daroussin  * Collapse pairs of quotes inside quoted arguments.
16227295610fSBaptiste Daroussin  * Advance the argument pointer to the next argument,
16237295610fSBaptiste Daroussin  * or to the NUL byte terminating the argument line.
16247295610fSBaptiste Daroussin  */
16257295610fSBaptiste Daroussin char *
roff_getarg(struct roff * r,char ** cpp,int ln,int * pos)16267295610fSBaptiste Daroussin roff_getarg(struct roff *r, char **cpp, int ln, int *pos)
16277295610fSBaptiste Daroussin {
16287295610fSBaptiste Daroussin 	struct buf	 buf;
16297295610fSBaptiste Daroussin 	char		*cp, *start;
16307295610fSBaptiste Daroussin 	int		 newesc, pairs, quoted, white;
16317295610fSBaptiste Daroussin 
16327295610fSBaptiste Daroussin 	/* Quoting can only start with a new word. */
16337295610fSBaptiste Daroussin 	start = *cpp;
16347295610fSBaptiste Daroussin 	quoted = 0;
16357295610fSBaptiste Daroussin 	if ('"' == *start) {
16367295610fSBaptiste Daroussin 		quoted = 1;
16377295610fSBaptiste Daroussin 		start++;
16387295610fSBaptiste Daroussin 	}
16397295610fSBaptiste Daroussin 
16407295610fSBaptiste Daroussin 	newesc = pairs = white = 0;
16417295610fSBaptiste Daroussin 	for (cp = start; '\0' != *cp; cp++) {
16427295610fSBaptiste Daroussin 
16437295610fSBaptiste Daroussin 		/*
16447295610fSBaptiste Daroussin 		 * Move the following text left
16457295610fSBaptiste Daroussin 		 * after quoted quotes and after "\\" and "\t".
16467295610fSBaptiste Daroussin 		 */
16477295610fSBaptiste Daroussin 		if (pairs)
16487295610fSBaptiste Daroussin 			cp[-pairs] = cp[0];
16497295610fSBaptiste Daroussin 
16507295610fSBaptiste Daroussin 		if ('\\' == cp[0]) {
16517295610fSBaptiste Daroussin 			/*
16527295610fSBaptiste Daroussin 			 * In copy mode, translate double to single
16537295610fSBaptiste Daroussin 			 * backslashes and backslash-t to literal tabs.
16547295610fSBaptiste Daroussin 			 */
16557295610fSBaptiste Daroussin 			switch (cp[1]) {
16567295610fSBaptiste Daroussin 			case 'a':
16577295610fSBaptiste Daroussin 			case 't':
16587295610fSBaptiste Daroussin 				cp[-pairs] = '\t';
16597295610fSBaptiste Daroussin 				pairs++;
16607295610fSBaptiste Daroussin 				cp++;
16617295610fSBaptiste Daroussin 				break;
16627295610fSBaptiste Daroussin 			case '\\':
1663*c1c95addSBrooks Davis 				cp[-pairs] = '\\';
16647295610fSBaptiste Daroussin 				newesc = 1;
16657295610fSBaptiste Daroussin 				pairs++;
16667295610fSBaptiste Daroussin 				cp++;
16677295610fSBaptiste Daroussin 				break;
16687295610fSBaptiste Daroussin 			case ' ':
16697295610fSBaptiste Daroussin 				/* Skip escaped blanks. */
16707295610fSBaptiste Daroussin 				if (0 == quoted)
16717295610fSBaptiste Daroussin 					cp++;
16727295610fSBaptiste Daroussin 				break;
16737295610fSBaptiste Daroussin 			default:
16747295610fSBaptiste Daroussin 				break;
16757295610fSBaptiste Daroussin 			}
16767295610fSBaptiste Daroussin 		} else if (0 == quoted) {
16777295610fSBaptiste Daroussin 			if (' ' == cp[0]) {
16787295610fSBaptiste Daroussin 				/* Unescaped blanks end unquoted args. */
16797295610fSBaptiste Daroussin 				white = 1;
16807295610fSBaptiste Daroussin 				break;
16817295610fSBaptiste Daroussin 			}
16827295610fSBaptiste Daroussin 		} else if ('"' == cp[0]) {
16837295610fSBaptiste Daroussin 			if ('"' == cp[1]) {
16847295610fSBaptiste Daroussin 				/* Quoted quotes collapse. */
16857295610fSBaptiste Daroussin 				pairs++;
16867295610fSBaptiste Daroussin 				cp++;
16877295610fSBaptiste Daroussin 			} else {
16887295610fSBaptiste Daroussin 				/* Unquoted quotes end quoted args. */
16897295610fSBaptiste Daroussin 				quoted = 2;
16907295610fSBaptiste Daroussin 				break;
16917295610fSBaptiste Daroussin 			}
16927295610fSBaptiste Daroussin 		}
16937295610fSBaptiste Daroussin 	}
16947295610fSBaptiste Daroussin 
16957295610fSBaptiste Daroussin 	/* Quoted argument without a closing quote. */
16967295610fSBaptiste Daroussin 	if (1 == quoted)
16977295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_QUOTE, ln, *pos, NULL);
16987295610fSBaptiste Daroussin 
16997295610fSBaptiste Daroussin 	/* NUL-terminate this argument and move to the next one. */
17007295610fSBaptiste Daroussin 	if (pairs)
17017295610fSBaptiste Daroussin 		cp[-pairs] = '\0';
17027295610fSBaptiste Daroussin 	if ('\0' != *cp) {
17037295610fSBaptiste Daroussin 		*cp++ = '\0';
17047295610fSBaptiste Daroussin 		while (' ' == *cp)
17057295610fSBaptiste Daroussin 			cp++;
17067295610fSBaptiste Daroussin 	}
17077295610fSBaptiste Daroussin 	*pos += (int)(cp - start) + (quoted ? 1 : 0);
17087295610fSBaptiste Daroussin 	*cpp = cp;
17097295610fSBaptiste Daroussin 
17107295610fSBaptiste Daroussin 	if ('\0' == *cp && (white || ' ' == cp[-1]))
17117295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_SPACE_EOL, ln, *pos, NULL);
17127295610fSBaptiste Daroussin 
17137295610fSBaptiste Daroussin 	start = mandoc_strdup(start);
17147295610fSBaptiste Daroussin 	if (newesc == 0)
17157295610fSBaptiste Daroussin 		return start;
17167295610fSBaptiste Daroussin 
17177295610fSBaptiste Daroussin 	buf.buf = start;
17187295610fSBaptiste Daroussin 	buf.sz = strlen(start) + 1;
17197295610fSBaptiste Daroussin 	buf.next = NULL;
1720*c1c95addSBrooks Davis 	if (roff_expand(r, &buf, ln, 0, '\\') == ROFF_IGN) {
17217295610fSBaptiste Daroussin 		free(buf.buf);
17227295610fSBaptiste Daroussin 		buf.buf = mandoc_strdup("");
17237295610fSBaptiste Daroussin 	}
17247295610fSBaptiste Daroussin 	return buf.buf;
17257295610fSBaptiste Daroussin }
17267295610fSBaptiste Daroussin 
17277295610fSBaptiste Daroussin 
17287295610fSBaptiste Daroussin /*
172961d06d6bSBaptiste Daroussin  * Process text streams.
173061d06d6bSBaptiste Daroussin  */
17317295610fSBaptiste Daroussin static int
roff_parsetext(struct roff * r,struct buf * buf,int pos,int * offs)173261d06d6bSBaptiste Daroussin roff_parsetext(struct roff *r, struct buf *buf, int pos, int *offs)
173361d06d6bSBaptiste Daroussin {
173461d06d6bSBaptiste Daroussin 	size_t		 sz;
173561d06d6bSBaptiste Daroussin 	const char	*start;
173661d06d6bSBaptiste Daroussin 	char		*p;
173761d06d6bSBaptiste Daroussin 	int		 isz;
173861d06d6bSBaptiste Daroussin 	enum mandoc_esc	 esc;
173961d06d6bSBaptiste Daroussin 
174061d06d6bSBaptiste Daroussin 	/* Spring the input line trap. */
174161d06d6bSBaptiste Daroussin 
174261d06d6bSBaptiste Daroussin 	if (roffit_lines == 1) {
174361d06d6bSBaptiste Daroussin 		isz = mandoc_asprintf(&p, "%s\n.%s", buf->buf, roffit_macro);
174461d06d6bSBaptiste Daroussin 		free(buf->buf);
174561d06d6bSBaptiste Daroussin 		buf->buf = p;
174661d06d6bSBaptiste Daroussin 		buf->sz = isz + 1;
174761d06d6bSBaptiste Daroussin 		*offs = 0;
174861d06d6bSBaptiste Daroussin 		free(roffit_macro);
174961d06d6bSBaptiste Daroussin 		roffit_lines = 0;
175061d06d6bSBaptiste Daroussin 		return ROFF_REPARSE;
175161d06d6bSBaptiste Daroussin 	} else if (roffit_lines > 1)
175261d06d6bSBaptiste Daroussin 		--roffit_lines;
175361d06d6bSBaptiste Daroussin 
175461d06d6bSBaptiste Daroussin 	if (roffce_node != NULL && buf->buf[pos] != '\0') {
175561d06d6bSBaptiste Daroussin 		if (roffce_lines < 1) {
175661d06d6bSBaptiste Daroussin 			r->man->last = roffce_node;
175761d06d6bSBaptiste Daroussin 			r->man->next = ROFF_NEXT_SIBLING;
175861d06d6bSBaptiste Daroussin 			roffce_lines = 0;
175961d06d6bSBaptiste Daroussin 			roffce_node = NULL;
176061d06d6bSBaptiste Daroussin 		} else
176161d06d6bSBaptiste Daroussin 			roffce_lines--;
176261d06d6bSBaptiste Daroussin 	}
176361d06d6bSBaptiste Daroussin 
176461d06d6bSBaptiste Daroussin 	/* Convert all breakable hyphens into ASCII_HYPH. */
176561d06d6bSBaptiste Daroussin 
176661d06d6bSBaptiste Daroussin 	start = p = buf->buf + pos;
176761d06d6bSBaptiste Daroussin 
176861d06d6bSBaptiste Daroussin 	while (*p != '\0') {
176961d06d6bSBaptiste Daroussin 		sz = strcspn(p, "-\\");
177061d06d6bSBaptiste Daroussin 		p += sz;
177161d06d6bSBaptiste Daroussin 
177261d06d6bSBaptiste Daroussin 		if (*p == '\0')
177361d06d6bSBaptiste Daroussin 			break;
177461d06d6bSBaptiste Daroussin 
177561d06d6bSBaptiste Daroussin 		if (*p == '\\') {
177661d06d6bSBaptiste Daroussin 			/* Skip over escapes. */
177761d06d6bSBaptiste Daroussin 			p++;
177861d06d6bSBaptiste Daroussin 			esc = mandoc_escape((const char **)&p, NULL, NULL);
177961d06d6bSBaptiste Daroussin 			if (esc == ESCAPE_ERROR)
178061d06d6bSBaptiste Daroussin 				break;
178161d06d6bSBaptiste Daroussin 			while (*p == '-')
178261d06d6bSBaptiste Daroussin 				p++;
178361d06d6bSBaptiste Daroussin 			continue;
178461d06d6bSBaptiste Daroussin 		} else if (p == start) {
178561d06d6bSBaptiste Daroussin 			p++;
178661d06d6bSBaptiste Daroussin 			continue;
178761d06d6bSBaptiste Daroussin 		}
178861d06d6bSBaptiste Daroussin 
178961d06d6bSBaptiste Daroussin 		if (isalpha((unsigned char)p[-1]) &&
179061d06d6bSBaptiste Daroussin 		    isalpha((unsigned char)p[1]))
179161d06d6bSBaptiste Daroussin 			*p = ASCII_HYPH;
179261d06d6bSBaptiste Daroussin 		p++;
179361d06d6bSBaptiste Daroussin 	}
179461d06d6bSBaptiste Daroussin 	return ROFF_CONT;
179561d06d6bSBaptiste Daroussin }
179661d06d6bSBaptiste Daroussin 
17977295610fSBaptiste Daroussin int
roff_parseln(struct roff * r,int ln,struct buf * buf,int * offs,size_t len)17986d38604fSBaptiste Daroussin roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs, size_t len)
179961d06d6bSBaptiste Daroussin {
180061d06d6bSBaptiste Daroussin 	enum roff_tok	 t;
18017295610fSBaptiste Daroussin 	int		 e;
180261d06d6bSBaptiste Daroussin 	int		 pos;	/* parse point */
180361d06d6bSBaptiste Daroussin 	int		 spos;	/* saved parse point for messages */
180461d06d6bSBaptiste Daroussin 	int		 ppos;	/* original offset in buf->buf */
180561d06d6bSBaptiste Daroussin 	int		 ctl;	/* macro line (boolean) */
180661d06d6bSBaptiste Daroussin 
180761d06d6bSBaptiste Daroussin 	ppos = pos = *offs;
180861d06d6bSBaptiste Daroussin 
18096d38604fSBaptiste Daroussin 	if (len > 80 && r->tbl == NULL && r->eqn == NULL &&
18106d38604fSBaptiste Daroussin 	    (r->man->flags & ROFF_NOFILL) == 0 &&
18116d38604fSBaptiste Daroussin 	    strchr(" .\\", buf->buf[pos]) == NULL &&
18126d38604fSBaptiste Daroussin 	    buf->buf[pos] != r->control &&
18136d38604fSBaptiste Daroussin 	    strcspn(buf->buf, " ") < 80)
18146d38604fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_TEXT_LONG, ln, (int)len - 1,
18156d38604fSBaptiste Daroussin 		    "%.20s...", buf->buf + pos);
18166d38604fSBaptiste Daroussin 
181761d06d6bSBaptiste Daroussin 	/* Handle in-line equation delimiters. */
181861d06d6bSBaptiste Daroussin 
181961d06d6bSBaptiste Daroussin 	if (r->tbl == NULL &&
182061d06d6bSBaptiste Daroussin 	    r->last_eqn != NULL && r->last_eqn->delim &&
182161d06d6bSBaptiste Daroussin 	    (r->eqn == NULL || r->eqn_inline)) {
182261d06d6bSBaptiste Daroussin 		e = roff_eqndelim(r, buf, pos);
182361d06d6bSBaptiste Daroussin 		if (e == ROFF_REPARSE)
182461d06d6bSBaptiste Daroussin 			return e;
182561d06d6bSBaptiste Daroussin 		assert(e == ROFF_CONT);
182661d06d6bSBaptiste Daroussin 	}
182761d06d6bSBaptiste Daroussin 
1828*c1c95addSBrooks Davis 	/* Handle comments and escape sequences. */
1829*c1c95addSBrooks Davis 
1830*c1c95addSBrooks Davis 	e = roff_parse_comment(r, buf, ln, pos, r->escape);
1831*c1c95addSBrooks Davis 	if ((e & ROFF_MASK) == ROFF_IGN)
1832*c1c95addSBrooks Davis 		return e;
1833*c1c95addSBrooks Davis 	assert(e == ROFF_CONT);
183461d06d6bSBaptiste Daroussin 
18357295610fSBaptiste Daroussin 	e = roff_expand(r, buf, ln, pos, r->escape);
18367295610fSBaptiste Daroussin 	if ((e & ROFF_MASK) == ROFF_IGN)
183761d06d6bSBaptiste Daroussin 		return e;
183861d06d6bSBaptiste Daroussin 	assert(e == ROFF_CONT);
183961d06d6bSBaptiste Daroussin 
184061d06d6bSBaptiste Daroussin 	ctl = roff_getcontrol(r, buf->buf, &pos);
184161d06d6bSBaptiste Daroussin 
184261d06d6bSBaptiste Daroussin 	/*
184361d06d6bSBaptiste Daroussin 	 * First, if a scope is open and we're not a macro, pass the
184461d06d6bSBaptiste Daroussin 	 * text through the macro's filter.
184561d06d6bSBaptiste Daroussin 	 * Equations process all content themselves.
184661d06d6bSBaptiste Daroussin 	 * Tables process almost all content themselves, but we want
184761d06d6bSBaptiste Daroussin 	 * to warn about macros before passing it there.
184861d06d6bSBaptiste Daroussin 	 */
184961d06d6bSBaptiste Daroussin 
185061d06d6bSBaptiste Daroussin 	if (r->last != NULL && ! ctl) {
185161d06d6bSBaptiste Daroussin 		t = r->last->tok;
185261d06d6bSBaptiste Daroussin 		e = (*roffs[t].text)(r, t, buf, ln, pos, pos, offs);
18537295610fSBaptiste Daroussin 		if ((e & ROFF_MASK) == ROFF_IGN)
185461d06d6bSBaptiste Daroussin 			return e;
18557295610fSBaptiste Daroussin 		e &= ~ROFF_MASK;
18567295610fSBaptiste Daroussin 	} else
18577295610fSBaptiste Daroussin 		e = ROFF_IGN;
185861d06d6bSBaptiste Daroussin 	if (r->eqn != NULL && strncmp(buf->buf + ppos, ".EN", 3)) {
185961d06d6bSBaptiste Daroussin 		eqn_read(r->eqn, buf->buf + ppos);
18607295610fSBaptiste Daroussin 		return e;
186161d06d6bSBaptiste Daroussin 	}
186261d06d6bSBaptiste Daroussin 	if (r->tbl != NULL && (ctl == 0 || buf->buf[pos] == '\0')) {
186361d06d6bSBaptiste Daroussin 		tbl_read(r->tbl, ln, buf->buf, ppos);
18647295610fSBaptiste Daroussin 		roff_addtbl(r->man, ln, r->tbl);
18657295610fSBaptiste Daroussin 		return e;
186661d06d6bSBaptiste Daroussin 	}
18676d38604fSBaptiste Daroussin 	if ( ! ctl) {
18686d38604fSBaptiste Daroussin 		r->options &= ~MPARSE_COMMENT;
18697295610fSBaptiste Daroussin 		return roff_parsetext(r, buf, pos, offs) | e;
18706d38604fSBaptiste Daroussin 	}
187161d06d6bSBaptiste Daroussin 
187261d06d6bSBaptiste Daroussin 	/* Skip empty request lines. */
187361d06d6bSBaptiste Daroussin 
187461d06d6bSBaptiste Daroussin 	if (buf->buf[pos] == '"') {
18757295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_COMMENT_BAD, ln, pos, NULL);
187661d06d6bSBaptiste Daroussin 		return ROFF_IGN;
187761d06d6bSBaptiste Daroussin 	} else if (buf->buf[pos] == '\0')
187861d06d6bSBaptiste Daroussin 		return ROFF_IGN;
187961d06d6bSBaptiste Daroussin 
188061d06d6bSBaptiste Daroussin 	/*
188161d06d6bSBaptiste Daroussin 	 * If a scope is open, go to the child handler for that macro,
188261d06d6bSBaptiste Daroussin 	 * as it may want to preprocess before doing anything with it.
188361d06d6bSBaptiste Daroussin 	 */
188461d06d6bSBaptiste Daroussin 
188561d06d6bSBaptiste Daroussin 	if (r->last) {
188661d06d6bSBaptiste Daroussin 		t = r->last->tok;
188761d06d6bSBaptiste Daroussin 		return (*roffs[t].sub)(r, t, buf, ln, ppos, pos, offs);
188861d06d6bSBaptiste Daroussin 	}
188961d06d6bSBaptiste Daroussin 
18906d38604fSBaptiste Daroussin 	r->options &= ~MPARSE_COMMENT;
189161d06d6bSBaptiste Daroussin 	spos = pos;
189261d06d6bSBaptiste Daroussin 	t = roff_parse(r, buf->buf, &pos, ln, ppos);
1893*c1c95addSBrooks Davis 	return roff_req_or_macro(r, t, buf, ln, spos, pos, offs);
1894*c1c95addSBrooks Davis }
189561d06d6bSBaptiste Daroussin 
1896*c1c95addSBrooks Davis /*
1897*c1c95addSBrooks Davis  * Handle a new request or macro.
1898*c1c95addSBrooks Davis  * May be called outside any scope or from inside a conditional scope.
1899*c1c95addSBrooks Davis  */
1900*c1c95addSBrooks Davis static int
roff_req_or_macro(ROFF_ARGS)1901*c1c95addSBrooks Davis roff_req_or_macro(ROFF_ARGS) {
190261d06d6bSBaptiste Daroussin 
1903*c1c95addSBrooks Davis 	/* For now, tables ignore most macros and some request. */
1904*c1c95addSBrooks Davis 
1905*c1c95addSBrooks Davis 	if (r->tbl != NULL && (tok == TOKEN_NONE || tok == ROFF_TS ||
1906*c1c95addSBrooks Davis 	    tok == ROFF_br || tok == ROFF_ce || tok == ROFF_rj ||
1907*c1c95addSBrooks Davis 	    tok == ROFF_sp)) {
19087295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_TBLMACRO,
1909*c1c95addSBrooks Davis 		    ln, ppos, "%s", buf->buf + ppos);
1910*c1c95addSBrooks Davis 		if (tok != TOKEN_NONE)
191161d06d6bSBaptiste Daroussin 			return ROFF_IGN;
191261d06d6bSBaptiste Daroussin 		while (buf->buf[pos] != '\0' && buf->buf[pos] != ' ')
191361d06d6bSBaptiste Daroussin 			pos++;
191461d06d6bSBaptiste Daroussin 		while (buf->buf[pos] == ' ')
191561d06d6bSBaptiste Daroussin 			pos++;
191661d06d6bSBaptiste Daroussin 		tbl_read(r->tbl, ln, buf->buf, pos);
19177295610fSBaptiste Daroussin 		roff_addtbl(r->man, ln, r->tbl);
191861d06d6bSBaptiste Daroussin 		return ROFF_IGN;
191961d06d6bSBaptiste Daroussin 	}
192061d06d6bSBaptiste Daroussin 
192161d06d6bSBaptiste Daroussin 	/* For now, let high level macros abort .ce mode. */
192261d06d6bSBaptiste Daroussin 
1923*c1c95addSBrooks Davis 	if (roffce_node != NULL &&
1924*c1c95addSBrooks Davis 	    (tok == TOKEN_NONE || tok == ROFF_Dd || tok == ROFF_EQ ||
1925*c1c95addSBrooks Davis 	     tok == ROFF_TH || tok == ROFF_TS)) {
192661d06d6bSBaptiste Daroussin 		r->man->last = roffce_node;
192761d06d6bSBaptiste Daroussin 		r->man->next = ROFF_NEXT_SIBLING;
192861d06d6bSBaptiste Daroussin 		roffce_lines = 0;
192961d06d6bSBaptiste Daroussin 		roffce_node = NULL;
193061d06d6bSBaptiste Daroussin 	}
193161d06d6bSBaptiste Daroussin 
193261d06d6bSBaptiste Daroussin 	/*
193361d06d6bSBaptiste Daroussin 	 * This is neither a roff request nor a user-defined macro.
193461d06d6bSBaptiste Daroussin 	 * Let the standard macro set parsers handle it.
193561d06d6bSBaptiste Daroussin 	 */
193661d06d6bSBaptiste Daroussin 
1937*c1c95addSBrooks Davis 	if (tok == TOKEN_NONE)
193861d06d6bSBaptiste Daroussin 		return ROFF_CONT;
193961d06d6bSBaptiste Daroussin 
1940*c1c95addSBrooks Davis 	/* Execute a roff request or a user-defined macro. */
194161d06d6bSBaptiste Daroussin 
1942*c1c95addSBrooks Davis 	return (*roffs[tok].proc)(r, tok, buf, ln, ppos, pos, offs);
194361d06d6bSBaptiste Daroussin }
194461d06d6bSBaptiste Daroussin 
19457295610fSBaptiste Daroussin /*
19467295610fSBaptiste Daroussin  * Internal interface function to tell the roff parser that execution
19477295610fSBaptiste Daroussin  * of the current macro ended.  This is required because macro
19487295610fSBaptiste Daroussin  * definitions usually do not end with a .return request.
19497295610fSBaptiste Daroussin  */
19507295610fSBaptiste Daroussin void
roff_userret(struct roff * r)19517295610fSBaptiste Daroussin roff_userret(struct roff *r)
19527295610fSBaptiste Daroussin {
19537295610fSBaptiste Daroussin 	struct mctx	*ctx;
19547295610fSBaptiste Daroussin 	int		 i;
19557295610fSBaptiste Daroussin 
19567295610fSBaptiste Daroussin 	assert(r->mstackpos >= 0);
19577295610fSBaptiste Daroussin 	ctx = r->mstack + r->mstackpos;
19587295610fSBaptiste Daroussin 	for (i = 0; i < ctx->argc; i++)
19597295610fSBaptiste Daroussin 		free(ctx->argv[i]);
19607295610fSBaptiste Daroussin 	ctx->argc = 0;
19617295610fSBaptiste Daroussin 	r->mstackpos--;
19627295610fSBaptiste Daroussin }
19637295610fSBaptiste Daroussin 
196461d06d6bSBaptiste Daroussin void
roff_endparse(struct roff * r)196561d06d6bSBaptiste Daroussin roff_endparse(struct roff *r)
196661d06d6bSBaptiste Daroussin {
196761d06d6bSBaptiste Daroussin 	if (r->last != NULL)
19687295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_NOEND, r->last->line,
19697295610fSBaptiste Daroussin 		    r->last->col, "%s", roff_name[r->last->tok]);
197061d06d6bSBaptiste Daroussin 
197161d06d6bSBaptiste Daroussin 	if (r->eqn != NULL) {
19727295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_NOEND,
197361d06d6bSBaptiste Daroussin 		    r->eqn->node->line, r->eqn->node->pos, "EQ");
197461d06d6bSBaptiste Daroussin 		eqn_parse(r->eqn);
197561d06d6bSBaptiste Daroussin 		r->eqn = NULL;
197661d06d6bSBaptiste Daroussin 	}
197761d06d6bSBaptiste Daroussin 
197861d06d6bSBaptiste Daroussin 	if (r->tbl != NULL) {
19797295610fSBaptiste Daroussin 		tbl_end(r->tbl, 1);
198061d06d6bSBaptiste Daroussin 		r->tbl = NULL;
198161d06d6bSBaptiste Daroussin 	}
198261d06d6bSBaptiste Daroussin }
198361d06d6bSBaptiste Daroussin 
198461d06d6bSBaptiste Daroussin /*
1985*c1c95addSBrooks Davis  * Parse the request or macro name at buf[*pos].
1986*c1c95addSBrooks Davis  * Return ROFF_RENAMED, ROFF_USERDEF, or a ROFF_* token value.
1987*c1c95addSBrooks Davis  * For empty, undefined, mdoc(7), and man(7) macros, return TOKEN_NONE.
1988*c1c95addSBrooks Davis  * As a side effect, set r->current_string to the definition or to NULL.
198961d06d6bSBaptiste Daroussin  */
199061d06d6bSBaptiste Daroussin static enum roff_tok
roff_parse(struct roff * r,char * buf,int * pos,int ln,int ppos)199161d06d6bSBaptiste Daroussin roff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos)
199261d06d6bSBaptiste Daroussin {
199361d06d6bSBaptiste Daroussin 	char		*cp;
199461d06d6bSBaptiste Daroussin 	const char	*mac;
199561d06d6bSBaptiste Daroussin 	size_t		 maclen;
199661d06d6bSBaptiste Daroussin 	int		 deftype;
199761d06d6bSBaptiste Daroussin 	enum roff_tok	 t;
199861d06d6bSBaptiste Daroussin 
199961d06d6bSBaptiste Daroussin 	cp = buf + *pos;
200061d06d6bSBaptiste Daroussin 
200161d06d6bSBaptiste Daroussin 	if ('\0' == *cp || '"' == *cp || '\t' == *cp || ' ' == *cp)
200261d06d6bSBaptiste Daroussin 		return TOKEN_NONE;
200361d06d6bSBaptiste Daroussin 
200461d06d6bSBaptiste Daroussin 	mac = cp;
200561d06d6bSBaptiste Daroussin 	maclen = roff_getname(r, &cp, ln, ppos);
200661d06d6bSBaptiste Daroussin 
200761d06d6bSBaptiste Daroussin 	deftype = ROFFDEF_USER | ROFFDEF_REN;
200861d06d6bSBaptiste Daroussin 	r->current_string = roff_getstrn(r, mac, maclen, &deftype);
200961d06d6bSBaptiste Daroussin 	switch (deftype) {
201061d06d6bSBaptiste Daroussin 	case ROFFDEF_USER:
201161d06d6bSBaptiste Daroussin 		t = ROFF_USERDEF;
201261d06d6bSBaptiste Daroussin 		break;
201361d06d6bSBaptiste Daroussin 	case ROFFDEF_REN:
201461d06d6bSBaptiste Daroussin 		t = ROFF_RENAMED;
201561d06d6bSBaptiste Daroussin 		break;
201661d06d6bSBaptiste Daroussin 	default:
201761d06d6bSBaptiste Daroussin 		t = roffhash_find(r->reqtab, mac, maclen);
201861d06d6bSBaptiste Daroussin 		break;
201961d06d6bSBaptiste Daroussin 	}
202061d06d6bSBaptiste Daroussin 	if (t != TOKEN_NONE)
202161d06d6bSBaptiste Daroussin 		*pos = cp - buf;
202261d06d6bSBaptiste Daroussin 	else if (deftype == ROFFDEF_UNDEF) {
202361d06d6bSBaptiste Daroussin 		/* Using an undefined macro defines it to be empty. */
202461d06d6bSBaptiste Daroussin 		roff_setstrn(&r->strtab, mac, maclen, "", 0, 0);
202561d06d6bSBaptiste Daroussin 		roff_setstrn(&r->rentab, mac, maclen, NULL, 0, 0);
202661d06d6bSBaptiste Daroussin 	}
202761d06d6bSBaptiste Daroussin 	return t;
202861d06d6bSBaptiste Daroussin }
202961d06d6bSBaptiste Daroussin 
203061d06d6bSBaptiste Daroussin /* --- handling of request blocks ----------------------------------------- */
203161d06d6bSBaptiste Daroussin 
20326d38604fSBaptiste Daroussin /*
20336d38604fSBaptiste Daroussin  * Close a macro definition block or an "ignore" block.
20346d38604fSBaptiste Daroussin  */
20357295610fSBaptiste Daroussin static int
roff_cblock(ROFF_ARGS)203661d06d6bSBaptiste Daroussin roff_cblock(ROFF_ARGS)
203761d06d6bSBaptiste Daroussin {
20386d38604fSBaptiste Daroussin 	int	 rr;
203961d06d6bSBaptiste Daroussin 
204061d06d6bSBaptiste Daroussin 	if (r->last == NULL) {
20417295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "..");
204261d06d6bSBaptiste Daroussin 		return ROFF_IGN;
204361d06d6bSBaptiste Daroussin 	}
204461d06d6bSBaptiste Daroussin 
204561d06d6bSBaptiste Daroussin 	switch (r->last->tok) {
204661d06d6bSBaptiste Daroussin 	case ROFF_am:
204761d06d6bSBaptiste Daroussin 	case ROFF_ami:
204861d06d6bSBaptiste Daroussin 	case ROFF_de:
204961d06d6bSBaptiste Daroussin 	case ROFF_dei:
205061d06d6bSBaptiste Daroussin 	case ROFF_ig:
205161d06d6bSBaptiste Daroussin 		break;
20526d38604fSBaptiste Daroussin 	case ROFF_am1:
20536d38604fSBaptiste Daroussin 	case ROFF_de1:
20546d38604fSBaptiste Daroussin 		/* Remapped in roff_block(). */
20556d38604fSBaptiste Daroussin 		abort();
205661d06d6bSBaptiste Daroussin 	default:
20577295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "..");
205861d06d6bSBaptiste Daroussin 		return ROFF_IGN;
205961d06d6bSBaptiste Daroussin 	}
206061d06d6bSBaptiste Daroussin 
20616d38604fSBaptiste Daroussin 	roffnode_pop(r);
20626d38604fSBaptiste Daroussin 	roffnode_cleanscope(r);
20636d38604fSBaptiste Daroussin 
20646d38604fSBaptiste Daroussin 	/*
20656d38604fSBaptiste Daroussin 	 * If a conditional block with braces is still open,
20666d38604fSBaptiste Daroussin 	 * check for "\}" block end markers.
20676d38604fSBaptiste Daroussin 	 */
20686d38604fSBaptiste Daroussin 
20696d38604fSBaptiste Daroussin 	if (r->last != NULL && r->last->endspan < 0) {
20706d38604fSBaptiste Daroussin 		rr = 1;  /* If arguments follow "\}", warn about them. */
20716d38604fSBaptiste Daroussin 		roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr);
20726d38604fSBaptiste Daroussin 	}
20736d38604fSBaptiste Daroussin 
207461d06d6bSBaptiste Daroussin 	if (buf->buf[pos] != '\0')
20757295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
207661d06d6bSBaptiste Daroussin 		    ".. %s", buf->buf + pos);
207761d06d6bSBaptiste Daroussin 
207861d06d6bSBaptiste Daroussin 	return ROFF_IGN;
207961d06d6bSBaptiste Daroussin }
208061d06d6bSBaptiste Daroussin 
208145a5aec3SBaptiste Daroussin /*
208245a5aec3SBaptiste Daroussin  * Pop all nodes ending at the end of the current input line.
208345a5aec3SBaptiste Daroussin  * Return the number of loops ended.
208445a5aec3SBaptiste Daroussin  */
20857295610fSBaptiste Daroussin static int
roffnode_cleanscope(struct roff * r)208661d06d6bSBaptiste Daroussin roffnode_cleanscope(struct roff *r)
208761d06d6bSBaptiste Daroussin {
20887295610fSBaptiste Daroussin 	int inloop;
208961d06d6bSBaptiste Daroussin 
20907295610fSBaptiste Daroussin 	inloop = 0;
20916d38604fSBaptiste Daroussin 	while (r->last != NULL && r->last->endspan > 0) {
209261d06d6bSBaptiste Daroussin 		if (--r->last->endspan != 0)
209361d06d6bSBaptiste Daroussin 			break;
20947295610fSBaptiste Daroussin 		inloop += roffnode_pop(r);
209561d06d6bSBaptiste Daroussin 	}
20967295610fSBaptiste Daroussin 	return inloop;
209761d06d6bSBaptiste Daroussin }
209861d06d6bSBaptiste Daroussin 
209945a5aec3SBaptiste Daroussin /*
21006d38604fSBaptiste Daroussin  * Handle the closing "\}" of a conditional block.
210145a5aec3SBaptiste Daroussin  * Apart from generating warnings, this only pops nodes.
210245a5aec3SBaptiste Daroussin  * Return the number of loops ended.
210345a5aec3SBaptiste Daroussin  */
21047295610fSBaptiste Daroussin static int
roff_ccond(struct roff * r,int ln,int ppos)210561d06d6bSBaptiste Daroussin roff_ccond(struct roff *r, int ln, int ppos)
210661d06d6bSBaptiste Daroussin {
210761d06d6bSBaptiste Daroussin 	if (NULL == r->last) {
21087295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}");
21097295610fSBaptiste Daroussin 		return 0;
211061d06d6bSBaptiste Daroussin 	}
211161d06d6bSBaptiste Daroussin 
211261d06d6bSBaptiste Daroussin 	switch (r->last->tok) {
211361d06d6bSBaptiste Daroussin 	case ROFF_el:
211461d06d6bSBaptiste Daroussin 	case ROFF_ie:
211561d06d6bSBaptiste Daroussin 	case ROFF_if:
21167295610fSBaptiste Daroussin 	case ROFF_while:
211761d06d6bSBaptiste Daroussin 		break;
211861d06d6bSBaptiste Daroussin 	default:
21197295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}");
21207295610fSBaptiste Daroussin 		return 0;
212161d06d6bSBaptiste Daroussin 	}
212261d06d6bSBaptiste Daroussin 
212361d06d6bSBaptiste Daroussin 	if (r->last->endspan > -1) {
21247295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}");
21257295610fSBaptiste Daroussin 		return 0;
212661d06d6bSBaptiste Daroussin 	}
212761d06d6bSBaptiste Daroussin 
21287295610fSBaptiste Daroussin 	return roffnode_pop(r) + roffnode_cleanscope(r);
212961d06d6bSBaptiste Daroussin }
213061d06d6bSBaptiste Daroussin 
21317295610fSBaptiste Daroussin static int
roff_block(ROFF_ARGS)213261d06d6bSBaptiste Daroussin roff_block(ROFF_ARGS)
213361d06d6bSBaptiste Daroussin {
213461d06d6bSBaptiste Daroussin 	const char	*name, *value;
213561d06d6bSBaptiste Daroussin 	char		*call, *cp, *iname, *rname;
213661d06d6bSBaptiste Daroussin 	size_t		 csz, namesz, rsz;
213761d06d6bSBaptiste Daroussin 	int		 deftype;
213861d06d6bSBaptiste Daroussin 
213961d06d6bSBaptiste Daroussin 	/* Ignore groff compatibility mode for now. */
214061d06d6bSBaptiste Daroussin 
214161d06d6bSBaptiste Daroussin 	if (tok == ROFF_de1)
214261d06d6bSBaptiste Daroussin 		tok = ROFF_de;
214361d06d6bSBaptiste Daroussin 	else if (tok == ROFF_dei1)
214461d06d6bSBaptiste Daroussin 		tok = ROFF_dei;
214561d06d6bSBaptiste Daroussin 	else if (tok == ROFF_am1)
214661d06d6bSBaptiste Daroussin 		tok = ROFF_am;
214761d06d6bSBaptiste Daroussin 	else if (tok == ROFF_ami1)
214861d06d6bSBaptiste Daroussin 		tok = ROFF_ami;
214961d06d6bSBaptiste Daroussin 
215061d06d6bSBaptiste Daroussin 	/* Parse the macro name argument. */
215161d06d6bSBaptiste Daroussin 
215261d06d6bSBaptiste Daroussin 	cp = buf->buf + pos;
215361d06d6bSBaptiste Daroussin 	if (tok == ROFF_ig) {
215461d06d6bSBaptiste Daroussin 		iname = NULL;
215561d06d6bSBaptiste Daroussin 		namesz = 0;
215661d06d6bSBaptiste Daroussin 	} else {
215761d06d6bSBaptiste Daroussin 		iname = cp;
215861d06d6bSBaptiste Daroussin 		namesz = roff_getname(r, &cp, ln, ppos);
215961d06d6bSBaptiste Daroussin 		iname[namesz] = '\0';
216061d06d6bSBaptiste Daroussin 	}
216161d06d6bSBaptiste Daroussin 
216261d06d6bSBaptiste Daroussin 	/* Resolve the macro name argument if it is indirect. */
216361d06d6bSBaptiste Daroussin 
216461d06d6bSBaptiste Daroussin 	if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
216561d06d6bSBaptiste Daroussin 		deftype = ROFFDEF_USER;
216661d06d6bSBaptiste Daroussin 		name = roff_getstrn(r, iname, namesz, &deftype);
216761d06d6bSBaptiste Daroussin 		if (name == NULL) {
21687295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_STR_UNDEF,
21697295610fSBaptiste Daroussin 			    ln, (int)(iname - buf->buf),
217061d06d6bSBaptiste Daroussin 			    "%.*s", (int)namesz, iname);
217161d06d6bSBaptiste Daroussin 			namesz = 0;
217261d06d6bSBaptiste Daroussin 		} else
217361d06d6bSBaptiste Daroussin 			namesz = strlen(name);
217461d06d6bSBaptiste Daroussin 	} else
217561d06d6bSBaptiste Daroussin 		name = iname;
217661d06d6bSBaptiste Daroussin 
217761d06d6bSBaptiste Daroussin 	if (namesz == 0 && tok != ROFF_ig) {
21787295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_REQ_EMPTY,
21797295610fSBaptiste Daroussin 		    ln, ppos, "%s", roff_name[tok]);
218061d06d6bSBaptiste Daroussin 		return ROFF_IGN;
218161d06d6bSBaptiste Daroussin 	}
218261d06d6bSBaptiste Daroussin 
218361d06d6bSBaptiste Daroussin 	roffnode_push(r, tok, name, ln, ppos);
218461d06d6bSBaptiste Daroussin 
218561d06d6bSBaptiste Daroussin 	/*
218661d06d6bSBaptiste Daroussin 	 * At the beginning of a `de' macro, clear the existing string
218761d06d6bSBaptiste Daroussin 	 * with the same name, if there is one.  New content will be
218861d06d6bSBaptiste Daroussin 	 * appended from roff_block_text() in multiline mode.
218961d06d6bSBaptiste Daroussin 	 */
219061d06d6bSBaptiste Daroussin 
219161d06d6bSBaptiste Daroussin 	if (tok == ROFF_de || tok == ROFF_dei) {
219261d06d6bSBaptiste Daroussin 		roff_setstrn(&r->strtab, name, namesz, "", 0, 0);
219361d06d6bSBaptiste Daroussin 		roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
219461d06d6bSBaptiste Daroussin 	} else if (tok == ROFF_am || tok == ROFF_ami) {
219561d06d6bSBaptiste Daroussin 		deftype = ROFFDEF_ANY;
219661d06d6bSBaptiste Daroussin 		value = roff_getstrn(r, iname, namesz, &deftype);
219761d06d6bSBaptiste Daroussin 		switch (deftype) {  /* Before appending, ... */
219861d06d6bSBaptiste Daroussin 		case ROFFDEF_PRE: /* copy predefined to user-defined. */
219961d06d6bSBaptiste Daroussin 			roff_setstrn(&r->strtab, name, namesz,
220061d06d6bSBaptiste Daroussin 			    value, strlen(value), 0);
220161d06d6bSBaptiste Daroussin 			break;
220261d06d6bSBaptiste Daroussin 		case ROFFDEF_REN: /* call original standard macro. */
220361d06d6bSBaptiste Daroussin 			csz = mandoc_asprintf(&call, ".%.*s \\$* \\\"\n",
220461d06d6bSBaptiste Daroussin 			    (int)strlen(value), value);
220561d06d6bSBaptiste Daroussin 			roff_setstrn(&r->strtab, name, namesz, call, csz, 0);
220661d06d6bSBaptiste Daroussin 			roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
220761d06d6bSBaptiste Daroussin 			free(call);
220861d06d6bSBaptiste Daroussin 			break;
220961d06d6bSBaptiste Daroussin 		case ROFFDEF_STD:  /* rename and call standard macro. */
221061d06d6bSBaptiste Daroussin 			rsz = mandoc_asprintf(&rname, "__%s_renamed", name);
221161d06d6bSBaptiste Daroussin 			roff_setstrn(&r->rentab, rname, rsz, name, namesz, 0);
221261d06d6bSBaptiste Daroussin 			csz = mandoc_asprintf(&call, ".%.*s \\$* \\\"\n",
221361d06d6bSBaptiste Daroussin 			    (int)rsz, rname);
221461d06d6bSBaptiste Daroussin 			roff_setstrn(&r->strtab, name, namesz, call, csz, 0);
221561d06d6bSBaptiste Daroussin 			free(call);
221661d06d6bSBaptiste Daroussin 			free(rname);
221761d06d6bSBaptiste Daroussin 			break;
221861d06d6bSBaptiste Daroussin 		default:
221961d06d6bSBaptiste Daroussin 			break;
222061d06d6bSBaptiste Daroussin 		}
222161d06d6bSBaptiste Daroussin 	}
222261d06d6bSBaptiste Daroussin 
222361d06d6bSBaptiste Daroussin 	if (*cp == '\0')
222461d06d6bSBaptiste Daroussin 		return ROFF_IGN;
222561d06d6bSBaptiste Daroussin 
222661d06d6bSBaptiste Daroussin 	/* Get the custom end marker. */
222761d06d6bSBaptiste Daroussin 
222861d06d6bSBaptiste Daroussin 	iname = cp;
222961d06d6bSBaptiste Daroussin 	namesz = roff_getname(r, &cp, ln, ppos);
223061d06d6bSBaptiste Daroussin 
223161d06d6bSBaptiste Daroussin 	/* Resolve the end marker if it is indirect. */
223261d06d6bSBaptiste Daroussin 
223361d06d6bSBaptiste Daroussin 	if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
223461d06d6bSBaptiste Daroussin 		deftype = ROFFDEF_USER;
223561d06d6bSBaptiste Daroussin 		name = roff_getstrn(r, iname, namesz, &deftype);
223661d06d6bSBaptiste Daroussin 		if (name == NULL) {
22377295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_STR_UNDEF,
22387295610fSBaptiste Daroussin 			    ln, (int)(iname - buf->buf),
223961d06d6bSBaptiste Daroussin 			    "%.*s", (int)namesz, iname);
224061d06d6bSBaptiste Daroussin 			namesz = 0;
224161d06d6bSBaptiste Daroussin 		} else
224261d06d6bSBaptiste Daroussin 			namesz = strlen(name);
224361d06d6bSBaptiste Daroussin 	} else
224461d06d6bSBaptiste Daroussin 		name = iname;
224561d06d6bSBaptiste Daroussin 
224661d06d6bSBaptiste Daroussin 	if (namesz)
224761d06d6bSBaptiste Daroussin 		r->last->end = mandoc_strndup(name, namesz);
224861d06d6bSBaptiste Daroussin 
224961d06d6bSBaptiste Daroussin 	if (*cp != '\0')
22507295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_EXCESS,
225161d06d6bSBaptiste Daroussin 		    ln, pos, ".%s ... %s", roff_name[tok], cp);
225261d06d6bSBaptiste Daroussin 
225361d06d6bSBaptiste Daroussin 	return ROFF_IGN;
225461d06d6bSBaptiste Daroussin }
225561d06d6bSBaptiste Daroussin 
22567295610fSBaptiste Daroussin static int
roff_block_sub(ROFF_ARGS)225761d06d6bSBaptiste Daroussin roff_block_sub(ROFF_ARGS)
225861d06d6bSBaptiste Daroussin {
225961d06d6bSBaptiste Daroussin 	enum roff_tok	t;
226061d06d6bSBaptiste Daroussin 	int		i, j;
226161d06d6bSBaptiste Daroussin 
226261d06d6bSBaptiste Daroussin 	/*
2263*c1c95addSBrooks Davis 	 * If a custom end marker is a user-defined or predefined macro
2264*c1c95addSBrooks Davis 	 * or a request, interpret it.
226561d06d6bSBaptiste Daroussin 	 */
226661d06d6bSBaptiste Daroussin 
226761d06d6bSBaptiste Daroussin 	if (r->last->end) {
226861d06d6bSBaptiste Daroussin 		for (i = pos, j = 0; r->last->end[j]; j++, i++)
226961d06d6bSBaptiste Daroussin 			if (buf->buf[i] != r->last->end[j])
227061d06d6bSBaptiste Daroussin 				break;
227161d06d6bSBaptiste Daroussin 
227261d06d6bSBaptiste Daroussin 		if (r->last->end[j] == '\0' &&
227361d06d6bSBaptiste Daroussin 		    (buf->buf[i] == '\0' ||
227461d06d6bSBaptiste Daroussin 		     buf->buf[i] == ' ' ||
227561d06d6bSBaptiste Daroussin 		     buf->buf[i] == '\t')) {
227661d06d6bSBaptiste Daroussin 			roffnode_pop(r);
227761d06d6bSBaptiste Daroussin 			roffnode_cleanscope(r);
227861d06d6bSBaptiste Daroussin 
227961d06d6bSBaptiste Daroussin 			while (buf->buf[i] == ' ' || buf->buf[i] == '\t')
228061d06d6bSBaptiste Daroussin 				i++;
228161d06d6bSBaptiste Daroussin 
228261d06d6bSBaptiste Daroussin 			pos = i;
228361d06d6bSBaptiste Daroussin 			if (roff_parse(r, buf->buf, &pos, ln, ppos) !=
228461d06d6bSBaptiste Daroussin 			    TOKEN_NONE)
228561d06d6bSBaptiste Daroussin 				return ROFF_RERUN;
228661d06d6bSBaptiste Daroussin 			return ROFF_IGN;
228761d06d6bSBaptiste Daroussin 		}
228861d06d6bSBaptiste Daroussin 	}
228961d06d6bSBaptiste Daroussin 
2290*c1c95addSBrooks Davis 	/* Handle the standard end marker. */
229161d06d6bSBaptiste Daroussin 
229261d06d6bSBaptiste Daroussin 	t = roff_parse(r, buf->buf, &pos, ln, ppos);
2293*c1c95addSBrooks Davis 	if (t == ROFF_cblock)
2294*c1c95addSBrooks Davis 		return roff_cblock(r, t, buf, ln, ppos, pos, offs);
229561d06d6bSBaptiste Daroussin 
2296*c1c95addSBrooks Davis 	/* Not an end marker, so append the line to the block. */
2297*c1c95addSBrooks Davis 
229861d06d6bSBaptiste Daroussin 	if (tok != ROFF_ig)
229961d06d6bSBaptiste Daroussin 		roff_setstr(r, r->last->name, buf->buf + ppos, 2);
230061d06d6bSBaptiste Daroussin 	return ROFF_IGN;
230161d06d6bSBaptiste Daroussin }
230261d06d6bSBaptiste Daroussin 
23037295610fSBaptiste Daroussin static int
roff_block_text(ROFF_ARGS)230461d06d6bSBaptiste Daroussin roff_block_text(ROFF_ARGS)
230561d06d6bSBaptiste Daroussin {
230661d06d6bSBaptiste Daroussin 
230761d06d6bSBaptiste Daroussin 	if (tok != ROFF_ig)
230861d06d6bSBaptiste Daroussin 		roff_setstr(r, r->last->name, buf->buf + pos, 2);
230961d06d6bSBaptiste Daroussin 
231061d06d6bSBaptiste Daroussin 	return ROFF_IGN;
231161d06d6bSBaptiste Daroussin }
231261d06d6bSBaptiste Daroussin 
23136d38604fSBaptiste Daroussin /*
23146d38604fSBaptiste Daroussin  * Check for a closing "\}" and handle it.
23156d38604fSBaptiste Daroussin  * In this function, the final "int *offs" argument is used for
23166d38604fSBaptiste Daroussin  * different purposes than elsewhere:
23176d38604fSBaptiste Daroussin  * Input: *offs == 0: caller wants to discard arguments following \}
23186d38604fSBaptiste Daroussin  *        *offs == 1: caller wants to preserve text following \}
23196d38604fSBaptiste Daroussin  * Output: *offs = 0: tell caller to discard input line
23206d38604fSBaptiste Daroussin  *         *offs = 1: tell caller to use input line
23216d38604fSBaptiste Daroussin  */
23227295610fSBaptiste Daroussin static int
roff_cond_checkend(ROFF_ARGS)23236d38604fSBaptiste Daroussin roff_cond_checkend(ROFF_ARGS)
232461d06d6bSBaptiste Daroussin {
232561d06d6bSBaptiste Daroussin 	char		*ep;
23267295610fSBaptiste Daroussin 	int		 endloop, irc, rr;
232761d06d6bSBaptiste Daroussin 
23287295610fSBaptiste Daroussin 	irc = ROFF_IGN;
232961d06d6bSBaptiste Daroussin 	rr = r->last->rule;
23307295610fSBaptiste Daroussin 	endloop = tok != ROFF_while ? ROFF_IGN :
23317295610fSBaptiste Daroussin 	    rr ? ROFF_LOOPCONT : ROFF_LOOPEXIT;
23327295610fSBaptiste Daroussin 	if (roffnode_cleanscope(r))
23337295610fSBaptiste Daroussin 		irc |= endloop;
233461d06d6bSBaptiste Daroussin 
233561d06d6bSBaptiste Daroussin 	/*
23366d38604fSBaptiste Daroussin 	 * If "\}" occurs on a macro line without a preceding macro or
23376d38604fSBaptiste Daroussin 	 * a text line contains nothing else, drop the line completely.
233861d06d6bSBaptiste Daroussin 	 */
233961d06d6bSBaptiste Daroussin 
234061d06d6bSBaptiste Daroussin 	ep = buf->buf + pos;
23416d38604fSBaptiste Daroussin 	if (ep[0] == '\\' && ep[1] == '}' && (ep[2] == '\0' || *offs == 0))
234261d06d6bSBaptiste Daroussin 		rr = 0;
234361d06d6bSBaptiste Daroussin 
23447295610fSBaptiste Daroussin 	/*
23456d38604fSBaptiste Daroussin 	 * The closing delimiter "\}" rewinds the conditional scope
23467295610fSBaptiste Daroussin 	 * but is otherwise ignored when interpreting the line.
23477295610fSBaptiste Daroussin 	 */
234861d06d6bSBaptiste Daroussin 
234961d06d6bSBaptiste Daroussin 	while ((ep = strchr(ep, '\\')) != NULL) {
235061d06d6bSBaptiste Daroussin 		switch (ep[1]) {
235161d06d6bSBaptiste Daroussin 		case '}':
23526d38604fSBaptiste Daroussin 			if (ep[2] == '\0')
23536d38604fSBaptiste Daroussin 				ep[0] = '\0';
23546d38604fSBaptiste Daroussin 			else if (rr)
23556d38604fSBaptiste Daroussin 				ep[1] = '&';
23566d38604fSBaptiste Daroussin 			else
235761d06d6bSBaptiste Daroussin 				memmove(ep, ep + 2, strlen(ep + 2) + 1);
23587295610fSBaptiste Daroussin 			if (roff_ccond(r, ln, ep - buf->buf))
23597295610fSBaptiste Daroussin 				irc |= endloop;
236061d06d6bSBaptiste Daroussin 			break;
236161d06d6bSBaptiste Daroussin 		case '\0':
236261d06d6bSBaptiste Daroussin 			++ep;
236361d06d6bSBaptiste Daroussin 			break;
236461d06d6bSBaptiste Daroussin 		default:
236561d06d6bSBaptiste Daroussin 			ep += 2;
236661d06d6bSBaptiste Daroussin 			break;
236761d06d6bSBaptiste Daroussin 		}
236861d06d6bSBaptiste Daroussin 	}
23696d38604fSBaptiste Daroussin 	*offs = rr;
23706d38604fSBaptiste Daroussin 	return irc;
23716d38604fSBaptiste Daroussin }
23726d38604fSBaptiste Daroussin 
23736d38604fSBaptiste Daroussin /*
23746d38604fSBaptiste Daroussin  * Parse and process a request or macro line in conditional scope.
23756d38604fSBaptiste Daroussin  */
23766d38604fSBaptiste Daroussin static int
roff_cond_sub(ROFF_ARGS)23776d38604fSBaptiste Daroussin roff_cond_sub(ROFF_ARGS)
23786d38604fSBaptiste Daroussin {
23796d38604fSBaptiste Daroussin 	struct roffnode	*bl;
2380*c1c95addSBrooks Davis 	int		 irc, rr, spos;
23816d38604fSBaptiste Daroussin 	enum roff_tok	 t;
23826d38604fSBaptiste Daroussin 
23836d38604fSBaptiste Daroussin 	rr = 0;  /* If arguments follow "\}", skip them. */
23846d38604fSBaptiste Daroussin 	irc = roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr);
2385*c1c95addSBrooks Davis 	spos = pos;
23866d38604fSBaptiste Daroussin 	t = roff_parse(r, buf->buf, &pos, ln, ppos);
23876d38604fSBaptiste Daroussin 
238861d06d6bSBaptiste Daroussin 	/*
2389*c1c95addSBrooks Davis 	 * Handle requests and macros if the conditional evaluated
2390*c1c95addSBrooks Davis 	 * to true or if they are structurally required.
2391*c1c95addSBrooks Davis 	 * The .break request is always handled specially.
239261d06d6bSBaptiste Daroussin 	 */
239361d06d6bSBaptiste Daroussin 
239445a5aec3SBaptiste Daroussin 	if (t == ROFF_break) {
239545a5aec3SBaptiste Daroussin 		if (irc & ROFF_LOOPMASK)
239645a5aec3SBaptiste Daroussin 			irc = ROFF_IGN | ROFF_LOOPEXIT;
239745a5aec3SBaptiste Daroussin 		else if (rr) {
239845a5aec3SBaptiste Daroussin 			for (bl = r->last; bl != NULL; bl = bl->parent) {
239945a5aec3SBaptiste Daroussin 				bl->rule = 0;
240045a5aec3SBaptiste Daroussin 				if (bl->tok == ROFF_while)
240145a5aec3SBaptiste Daroussin 					break;
240245a5aec3SBaptiste Daroussin 			}
240345a5aec3SBaptiste Daroussin 		}
2404*c1c95addSBrooks Davis 	} else if (rr || (t < TOKEN_NONE && roffs[t].flags & ROFFMAC_STRUCT)) {
2405*c1c95addSBrooks Davis 		irc |= roff_req_or_macro(r, t, buf, ln, spos, pos, offs);
2406*c1c95addSBrooks Davis 		if (irc & ROFF_WHILE)
2407*c1c95addSBrooks Davis 			irc &= ~(ROFF_LOOPCONT | ROFF_LOOPEXIT);
2408*c1c95addSBrooks Davis 	}
24097295610fSBaptiste Daroussin 	return irc;
241061d06d6bSBaptiste Daroussin }
241161d06d6bSBaptiste Daroussin 
24126d38604fSBaptiste Daroussin /*
24136d38604fSBaptiste Daroussin  * Parse and process a text line in conditional scope.
24146d38604fSBaptiste Daroussin  */
24157295610fSBaptiste Daroussin static int
roff_cond_text(ROFF_ARGS)241661d06d6bSBaptiste Daroussin roff_cond_text(ROFF_ARGS)
241761d06d6bSBaptiste Daroussin {
24186d38604fSBaptiste Daroussin 	int	 irc, rr;
241961d06d6bSBaptiste Daroussin 
24206d38604fSBaptiste Daroussin 	rr = 1;  /* If arguments follow "\}", preserve them. */
24216d38604fSBaptiste Daroussin 	irc = roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr);
24227295610fSBaptiste Daroussin 	if (rr)
24237295610fSBaptiste Daroussin 		irc |= ROFF_CONT;
24247295610fSBaptiste Daroussin 	return irc;
242561d06d6bSBaptiste Daroussin }
242661d06d6bSBaptiste Daroussin 
242761d06d6bSBaptiste Daroussin /* --- handling of numeric and conditional expressions -------------------- */
242861d06d6bSBaptiste Daroussin 
242961d06d6bSBaptiste Daroussin /*
243061d06d6bSBaptiste Daroussin  * Parse a single signed integer number.  Stop at the first non-digit.
243161d06d6bSBaptiste Daroussin  * If there is at least one digit, return success and advance the
243261d06d6bSBaptiste Daroussin  * parse point, else return failure and let the parse point unchanged.
243361d06d6bSBaptiste Daroussin  * Ignore overflows, treat them just like the C language.
243461d06d6bSBaptiste Daroussin  */
243561d06d6bSBaptiste Daroussin static int
roff_getnum(const char * v,int * pos,int * res,int flags)243661d06d6bSBaptiste Daroussin roff_getnum(const char *v, int *pos, int *res, int flags)
243761d06d6bSBaptiste Daroussin {
243861d06d6bSBaptiste Daroussin 	int	 myres, scaled, n, p;
243961d06d6bSBaptiste Daroussin 
244061d06d6bSBaptiste Daroussin 	if (NULL == res)
244161d06d6bSBaptiste Daroussin 		res = &myres;
244261d06d6bSBaptiste Daroussin 
244361d06d6bSBaptiste Daroussin 	p = *pos;
244461d06d6bSBaptiste Daroussin 	n = v[p] == '-';
244561d06d6bSBaptiste Daroussin 	if (n || v[p] == '+')
244661d06d6bSBaptiste Daroussin 		p++;
244761d06d6bSBaptiste Daroussin 
244861d06d6bSBaptiste Daroussin 	if (flags & ROFFNUM_WHITE)
244961d06d6bSBaptiste Daroussin 		while (isspace((unsigned char)v[p]))
245061d06d6bSBaptiste Daroussin 			p++;
245161d06d6bSBaptiste Daroussin 
245261d06d6bSBaptiste Daroussin 	for (*res = 0; isdigit((unsigned char)v[p]); p++)
245361d06d6bSBaptiste Daroussin 		*res = 10 * *res + v[p] - '0';
245461d06d6bSBaptiste Daroussin 	if (p == *pos + n)
245561d06d6bSBaptiste Daroussin 		return 0;
245661d06d6bSBaptiste Daroussin 
245761d06d6bSBaptiste Daroussin 	if (n)
245861d06d6bSBaptiste Daroussin 		*res = -*res;
245961d06d6bSBaptiste Daroussin 
246061d06d6bSBaptiste Daroussin 	/* Each number may be followed by one optional scaling unit. */
246161d06d6bSBaptiste Daroussin 
246261d06d6bSBaptiste Daroussin 	switch (v[p]) {
246361d06d6bSBaptiste Daroussin 	case 'f':
246461d06d6bSBaptiste Daroussin 		scaled = *res * 65536;
246561d06d6bSBaptiste Daroussin 		break;
246661d06d6bSBaptiste Daroussin 	case 'i':
246761d06d6bSBaptiste Daroussin 		scaled = *res * 240;
246861d06d6bSBaptiste Daroussin 		break;
246961d06d6bSBaptiste Daroussin 	case 'c':
247061d06d6bSBaptiste Daroussin 		scaled = *res * 240 / 2.54;
247161d06d6bSBaptiste Daroussin 		break;
247261d06d6bSBaptiste Daroussin 	case 'v':
247361d06d6bSBaptiste Daroussin 	case 'P':
247461d06d6bSBaptiste Daroussin 		scaled = *res * 40;
247561d06d6bSBaptiste Daroussin 		break;
247661d06d6bSBaptiste Daroussin 	case 'm':
247761d06d6bSBaptiste Daroussin 	case 'n':
247861d06d6bSBaptiste Daroussin 		scaled = *res * 24;
247961d06d6bSBaptiste Daroussin 		break;
248061d06d6bSBaptiste Daroussin 	case 'p':
248161d06d6bSBaptiste Daroussin 		scaled = *res * 10 / 3;
248261d06d6bSBaptiste Daroussin 		break;
248361d06d6bSBaptiste Daroussin 	case 'u':
248461d06d6bSBaptiste Daroussin 		scaled = *res;
248561d06d6bSBaptiste Daroussin 		break;
248661d06d6bSBaptiste Daroussin 	case 'M':
248761d06d6bSBaptiste Daroussin 		scaled = *res * 6 / 25;
248861d06d6bSBaptiste Daroussin 		break;
248961d06d6bSBaptiste Daroussin 	default:
249061d06d6bSBaptiste Daroussin 		scaled = *res;
249161d06d6bSBaptiste Daroussin 		p--;
249261d06d6bSBaptiste Daroussin 		break;
249361d06d6bSBaptiste Daroussin 	}
249461d06d6bSBaptiste Daroussin 	if (flags & ROFFNUM_SCALE)
249561d06d6bSBaptiste Daroussin 		*res = scaled;
249661d06d6bSBaptiste Daroussin 
249761d06d6bSBaptiste Daroussin 	*pos = p + 1;
249861d06d6bSBaptiste Daroussin 	return 1;
249961d06d6bSBaptiste Daroussin }
250061d06d6bSBaptiste Daroussin 
250161d06d6bSBaptiste Daroussin /*
250261d06d6bSBaptiste Daroussin  * Evaluate a string comparison condition.
250361d06d6bSBaptiste Daroussin  * The first character is the delimiter.
250461d06d6bSBaptiste Daroussin  * Succeed if the string up to its second occurrence
2505*c1c95addSBrooks Davis  * matches the string up to its third occurrence.
250661d06d6bSBaptiste Daroussin  * Advance the cursor after the third occurrence
250761d06d6bSBaptiste Daroussin  * or lacking that, to the end of the line.
250861d06d6bSBaptiste Daroussin  */
250961d06d6bSBaptiste Daroussin static int
roff_evalstrcond(const char * v,int * pos)251061d06d6bSBaptiste Daroussin roff_evalstrcond(const char *v, int *pos)
251161d06d6bSBaptiste Daroussin {
251261d06d6bSBaptiste Daroussin 	const char	*s1, *s2, *s3;
251361d06d6bSBaptiste Daroussin 	int		 match;
251461d06d6bSBaptiste Daroussin 
251561d06d6bSBaptiste Daroussin 	match = 0;
251661d06d6bSBaptiste Daroussin 	s1 = v + *pos;		/* initial delimiter */
251761d06d6bSBaptiste Daroussin 	s2 = s1 + 1;		/* for scanning the first string */
251861d06d6bSBaptiste Daroussin 	s3 = strchr(s2, *s1);	/* for scanning the second string */
251961d06d6bSBaptiste Daroussin 
252061d06d6bSBaptiste Daroussin 	if (NULL == s3)		/* found no middle delimiter */
252161d06d6bSBaptiste Daroussin 		goto out;
252261d06d6bSBaptiste Daroussin 
252361d06d6bSBaptiste Daroussin 	while ('\0' != *++s3) {
252461d06d6bSBaptiste Daroussin 		if (*s2 != *s3) {  /* mismatch */
252561d06d6bSBaptiste Daroussin 			s3 = strchr(s3, *s1);
252661d06d6bSBaptiste Daroussin 			break;
252761d06d6bSBaptiste Daroussin 		}
252861d06d6bSBaptiste Daroussin 		if (*s3 == *s1) {  /* found the final delimiter */
252961d06d6bSBaptiste Daroussin 			match = 1;
253061d06d6bSBaptiste Daroussin 			break;
253161d06d6bSBaptiste Daroussin 		}
253261d06d6bSBaptiste Daroussin 		s2++;
253361d06d6bSBaptiste Daroussin 	}
253461d06d6bSBaptiste Daroussin 
253561d06d6bSBaptiste Daroussin out:
253661d06d6bSBaptiste Daroussin 	if (NULL == s3)
253761d06d6bSBaptiste Daroussin 		s3 = strchr(s2, '\0');
253861d06d6bSBaptiste Daroussin 	else if (*s3 != '\0')
253961d06d6bSBaptiste Daroussin 		s3++;
254061d06d6bSBaptiste Daroussin 	*pos = s3 - v;
254161d06d6bSBaptiste Daroussin 	return match;
254261d06d6bSBaptiste Daroussin }
254361d06d6bSBaptiste Daroussin 
254461d06d6bSBaptiste Daroussin /*
254561d06d6bSBaptiste Daroussin  * Evaluate an optionally negated single character, numerical,
254661d06d6bSBaptiste Daroussin  * or string condition.
254761d06d6bSBaptiste Daroussin  */
254861d06d6bSBaptiste Daroussin static int
roff_evalcond(struct roff * r,int ln,char * v,int * pos)254961d06d6bSBaptiste Daroussin roff_evalcond(struct roff *r, int ln, char *v, int *pos)
255061d06d6bSBaptiste Daroussin {
25517295610fSBaptiste Daroussin 	const char	*start, *end;
255261d06d6bSBaptiste Daroussin 	char		*cp, *name;
255361d06d6bSBaptiste Daroussin 	size_t		 sz;
25547295610fSBaptiste Daroussin 	int		 deftype, len, number, savepos, istrue, wanttrue;
255561d06d6bSBaptiste Daroussin 
255661d06d6bSBaptiste Daroussin 	if ('!' == v[*pos]) {
255761d06d6bSBaptiste Daroussin 		wanttrue = 0;
255861d06d6bSBaptiste Daroussin 		(*pos)++;
255961d06d6bSBaptiste Daroussin 	} else
256061d06d6bSBaptiste Daroussin 		wanttrue = 1;
256161d06d6bSBaptiste Daroussin 
256261d06d6bSBaptiste Daroussin 	switch (v[*pos]) {
256361d06d6bSBaptiste Daroussin 	case '\0':
256461d06d6bSBaptiste Daroussin 		return 0;
256561d06d6bSBaptiste Daroussin 	case 'n':
256661d06d6bSBaptiste Daroussin 	case 'o':
256761d06d6bSBaptiste Daroussin 		(*pos)++;
256861d06d6bSBaptiste Daroussin 		return wanttrue;
256961d06d6bSBaptiste Daroussin 	case 'e':
257061d06d6bSBaptiste Daroussin 	case 't':
257161d06d6bSBaptiste Daroussin 	case 'v':
257261d06d6bSBaptiste Daroussin 		(*pos)++;
257361d06d6bSBaptiste Daroussin 		return !wanttrue;
25747295610fSBaptiste Daroussin 	case 'c':
25757295610fSBaptiste Daroussin 		do {
25767295610fSBaptiste Daroussin 			(*pos)++;
25777295610fSBaptiste Daroussin 		} while (v[*pos] == ' ');
25787295610fSBaptiste Daroussin 
25797295610fSBaptiste Daroussin 		/*
25807295610fSBaptiste Daroussin 		 * Quirk for groff compatibility:
25817295610fSBaptiste Daroussin 		 * The horizontal tab is neither available nor unavailable.
25827295610fSBaptiste Daroussin 		 */
25837295610fSBaptiste Daroussin 
25847295610fSBaptiste Daroussin 		if (v[*pos] == '\t') {
25857295610fSBaptiste Daroussin 			(*pos)++;
25867295610fSBaptiste Daroussin 			return 0;
25877295610fSBaptiste Daroussin 		}
25887295610fSBaptiste Daroussin 
25897295610fSBaptiste Daroussin 		/* Printable ASCII characters are available. */
25907295610fSBaptiste Daroussin 
25917295610fSBaptiste Daroussin 		if (v[*pos] != '\\') {
25927295610fSBaptiste Daroussin 			(*pos)++;
25937295610fSBaptiste Daroussin 			return wanttrue;
25947295610fSBaptiste Daroussin 		}
25957295610fSBaptiste Daroussin 
25967295610fSBaptiste Daroussin 		end = v + ++*pos;
25977295610fSBaptiste Daroussin 		switch (mandoc_escape(&end, &start, &len)) {
25987295610fSBaptiste Daroussin 		case ESCAPE_SPECIAL:
25997295610fSBaptiste Daroussin 			istrue = mchars_spec2cp(start, len) != -1;
26007295610fSBaptiste Daroussin 			break;
26017295610fSBaptiste Daroussin 		case ESCAPE_UNICODE:
26027295610fSBaptiste Daroussin 			istrue = 1;
26037295610fSBaptiste Daroussin 			break;
26047295610fSBaptiste Daroussin 		case ESCAPE_NUMBERED:
26057295610fSBaptiste Daroussin 			istrue = mchars_num2char(start, len) != -1;
26067295610fSBaptiste Daroussin 			break;
26077295610fSBaptiste Daroussin 		default:
26087295610fSBaptiste Daroussin 			istrue = !wanttrue;
26097295610fSBaptiste Daroussin 			break;
26107295610fSBaptiste Daroussin 		}
26117295610fSBaptiste Daroussin 		*pos = end - v;
26127295610fSBaptiste Daroussin 		return istrue == wanttrue;
261361d06d6bSBaptiste Daroussin 	case 'd':
261461d06d6bSBaptiste Daroussin 	case 'r':
261561d06d6bSBaptiste Daroussin 		cp = v + *pos + 1;
261661d06d6bSBaptiste Daroussin 		while (*cp == ' ')
261761d06d6bSBaptiste Daroussin 			cp++;
261861d06d6bSBaptiste Daroussin 		name = cp;
261961d06d6bSBaptiste Daroussin 		sz = roff_getname(r, &cp, ln, cp - v);
262061d06d6bSBaptiste Daroussin 		if (sz == 0)
262161d06d6bSBaptiste Daroussin 			istrue = 0;
262261d06d6bSBaptiste Daroussin 		else if (v[*pos] == 'r')
262361d06d6bSBaptiste Daroussin 			istrue = roff_hasregn(r, name, sz);
262461d06d6bSBaptiste Daroussin 		else {
262561d06d6bSBaptiste Daroussin 			deftype = ROFFDEF_ANY;
262661d06d6bSBaptiste Daroussin 		        roff_getstrn(r, name, sz, &deftype);
262761d06d6bSBaptiste Daroussin 			istrue = !!deftype;
262861d06d6bSBaptiste Daroussin 		}
26297295610fSBaptiste Daroussin 		*pos = (name + sz) - v;
263061d06d6bSBaptiste Daroussin 		return istrue == wanttrue;
263161d06d6bSBaptiste Daroussin 	default:
263261d06d6bSBaptiste Daroussin 		break;
263361d06d6bSBaptiste Daroussin 	}
263461d06d6bSBaptiste Daroussin 
263561d06d6bSBaptiste Daroussin 	savepos = *pos;
263661d06d6bSBaptiste Daroussin 	if (roff_evalnum(r, ln, v, pos, &number, ROFFNUM_SCALE))
263761d06d6bSBaptiste Daroussin 		return (number > 0) == wanttrue;
263861d06d6bSBaptiste Daroussin 	else if (*pos == savepos)
263961d06d6bSBaptiste Daroussin 		return roff_evalstrcond(v, pos) == wanttrue;
264061d06d6bSBaptiste Daroussin 	else
264161d06d6bSBaptiste Daroussin 		return 0;
264261d06d6bSBaptiste Daroussin }
264361d06d6bSBaptiste Daroussin 
26447295610fSBaptiste Daroussin static int
roff_line_ignore(ROFF_ARGS)264561d06d6bSBaptiste Daroussin roff_line_ignore(ROFF_ARGS)
264661d06d6bSBaptiste Daroussin {
264761d06d6bSBaptiste Daroussin 
264861d06d6bSBaptiste Daroussin 	return ROFF_IGN;
264961d06d6bSBaptiste Daroussin }
265061d06d6bSBaptiste Daroussin 
26517295610fSBaptiste Daroussin static int
roff_insec(ROFF_ARGS)265261d06d6bSBaptiste Daroussin roff_insec(ROFF_ARGS)
265361d06d6bSBaptiste Daroussin {
265461d06d6bSBaptiste Daroussin 
26557295610fSBaptiste Daroussin 	mandoc_msg(MANDOCERR_REQ_INSEC, ln, ppos, "%s", roff_name[tok]);
265661d06d6bSBaptiste Daroussin 	return ROFF_IGN;
265761d06d6bSBaptiste Daroussin }
265861d06d6bSBaptiste Daroussin 
26597295610fSBaptiste Daroussin static int
roff_unsupp(ROFF_ARGS)266061d06d6bSBaptiste Daroussin roff_unsupp(ROFF_ARGS)
266161d06d6bSBaptiste Daroussin {
266261d06d6bSBaptiste Daroussin 
26637295610fSBaptiste Daroussin 	mandoc_msg(MANDOCERR_REQ_UNSUPP, ln, ppos, "%s", roff_name[tok]);
266461d06d6bSBaptiste Daroussin 	return ROFF_IGN;
266561d06d6bSBaptiste Daroussin }
266661d06d6bSBaptiste Daroussin 
26677295610fSBaptiste Daroussin static int
roff_cond(ROFF_ARGS)266861d06d6bSBaptiste Daroussin roff_cond(ROFF_ARGS)
266961d06d6bSBaptiste Daroussin {
26707295610fSBaptiste Daroussin 	int	 irc;
267161d06d6bSBaptiste Daroussin 
267261d06d6bSBaptiste Daroussin 	roffnode_push(r, tok, NULL, ln, ppos);
267361d06d6bSBaptiste Daroussin 
267461d06d6bSBaptiste Daroussin 	/*
267561d06d6bSBaptiste Daroussin 	 * An `.el' has no conditional body: it will consume the value
267661d06d6bSBaptiste Daroussin 	 * of the current rstack entry set in prior `ie' calls or
267761d06d6bSBaptiste Daroussin 	 * defaults to DENY.
267861d06d6bSBaptiste Daroussin 	 *
267961d06d6bSBaptiste Daroussin 	 * If we're not an `el', however, then evaluate the conditional.
268061d06d6bSBaptiste Daroussin 	 */
268161d06d6bSBaptiste Daroussin 
268261d06d6bSBaptiste Daroussin 	r->last->rule = tok == ROFF_el ?
268361d06d6bSBaptiste Daroussin 	    (r->rstackpos < 0 ? 0 : r->rstack[r->rstackpos--]) :
268461d06d6bSBaptiste Daroussin 	    roff_evalcond(r, ln, buf->buf, &pos);
268561d06d6bSBaptiste Daroussin 
268661d06d6bSBaptiste Daroussin 	/*
268761d06d6bSBaptiste Daroussin 	 * An if-else will put the NEGATION of the current evaluated
268861d06d6bSBaptiste Daroussin 	 * conditional into the stack of rules.
268961d06d6bSBaptiste Daroussin 	 */
269061d06d6bSBaptiste Daroussin 
269161d06d6bSBaptiste Daroussin 	if (tok == ROFF_ie) {
269261d06d6bSBaptiste Daroussin 		if (r->rstackpos + 1 == r->rstacksz) {
269361d06d6bSBaptiste Daroussin 			r->rstacksz += 16;
269461d06d6bSBaptiste Daroussin 			r->rstack = mandoc_reallocarray(r->rstack,
269561d06d6bSBaptiste Daroussin 			    r->rstacksz, sizeof(int));
269661d06d6bSBaptiste Daroussin 		}
269761d06d6bSBaptiste Daroussin 		r->rstack[++r->rstackpos] = !r->last->rule;
269861d06d6bSBaptiste Daroussin 	}
269961d06d6bSBaptiste Daroussin 
270061d06d6bSBaptiste Daroussin 	/* If the parent has false as its rule, then so do we. */
270161d06d6bSBaptiste Daroussin 
270261d06d6bSBaptiste Daroussin 	if (r->last->parent && !r->last->parent->rule)
270361d06d6bSBaptiste Daroussin 		r->last->rule = 0;
270461d06d6bSBaptiste Daroussin 
270561d06d6bSBaptiste Daroussin 	/*
270661d06d6bSBaptiste Daroussin 	 * Determine scope.
270761d06d6bSBaptiste Daroussin 	 * If there is nothing on the line after the conditional,
270861d06d6bSBaptiste Daroussin 	 * not even whitespace, use next-line scope.
27097295610fSBaptiste Daroussin 	 * Except that .while does not support next-line scope.
271061d06d6bSBaptiste Daroussin 	 */
271161d06d6bSBaptiste Daroussin 
27127295610fSBaptiste Daroussin 	if (buf->buf[pos] == '\0' && tok != ROFF_while) {
271361d06d6bSBaptiste Daroussin 		r->last->endspan = 2;
271461d06d6bSBaptiste Daroussin 		goto out;
271561d06d6bSBaptiste Daroussin 	}
271661d06d6bSBaptiste Daroussin 
271761d06d6bSBaptiste Daroussin 	while (buf->buf[pos] == ' ')
271861d06d6bSBaptiste Daroussin 		pos++;
271961d06d6bSBaptiste Daroussin 
272061d06d6bSBaptiste Daroussin 	/* An opening brace requests multiline scope. */
272161d06d6bSBaptiste Daroussin 
272261d06d6bSBaptiste Daroussin 	if (buf->buf[pos] == '\\' && buf->buf[pos + 1] == '{') {
272361d06d6bSBaptiste Daroussin 		r->last->endspan = -1;
272461d06d6bSBaptiste Daroussin 		pos += 2;
272561d06d6bSBaptiste Daroussin 		while (buf->buf[pos] == ' ')
272661d06d6bSBaptiste Daroussin 			pos++;
272761d06d6bSBaptiste Daroussin 		goto out;
272861d06d6bSBaptiste Daroussin 	}
272961d06d6bSBaptiste Daroussin 
273061d06d6bSBaptiste Daroussin 	/*
273161d06d6bSBaptiste Daroussin 	 * Anything else following the conditional causes
273261d06d6bSBaptiste Daroussin 	 * single-line scope.  Warn if the scope contains
273361d06d6bSBaptiste Daroussin 	 * nothing but trailing whitespace.
273461d06d6bSBaptiste Daroussin 	 */
273561d06d6bSBaptiste Daroussin 
273661d06d6bSBaptiste Daroussin 	if (buf->buf[pos] == '\0')
27377295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_COND_EMPTY,
27387295610fSBaptiste Daroussin 		    ln, ppos, "%s", roff_name[tok]);
273961d06d6bSBaptiste Daroussin 
274061d06d6bSBaptiste Daroussin 	r->last->endspan = 1;
274161d06d6bSBaptiste Daroussin 
274261d06d6bSBaptiste Daroussin out:
274361d06d6bSBaptiste Daroussin 	*offs = pos;
27447295610fSBaptiste Daroussin 	irc = ROFF_RERUN;
27457295610fSBaptiste Daroussin 	if (tok == ROFF_while)
27467295610fSBaptiste Daroussin 		irc |= ROFF_WHILE;
27477295610fSBaptiste Daroussin 	return irc;
274861d06d6bSBaptiste Daroussin }
274961d06d6bSBaptiste Daroussin 
27507295610fSBaptiste Daroussin static int
roff_ds(ROFF_ARGS)275161d06d6bSBaptiste Daroussin roff_ds(ROFF_ARGS)
275261d06d6bSBaptiste Daroussin {
275361d06d6bSBaptiste Daroussin 	char		*string;
275461d06d6bSBaptiste Daroussin 	const char	*name;
275561d06d6bSBaptiste Daroussin 	size_t		 namesz;
275661d06d6bSBaptiste Daroussin 
275761d06d6bSBaptiste Daroussin 	/* Ignore groff compatibility mode for now. */
275861d06d6bSBaptiste Daroussin 
275961d06d6bSBaptiste Daroussin 	if (tok == ROFF_ds1)
276061d06d6bSBaptiste Daroussin 		tok = ROFF_ds;
276161d06d6bSBaptiste Daroussin 	else if (tok == ROFF_as1)
276261d06d6bSBaptiste Daroussin 		tok = ROFF_as;
276361d06d6bSBaptiste Daroussin 
276461d06d6bSBaptiste Daroussin 	/*
276561d06d6bSBaptiste Daroussin 	 * The first word is the name of the string.
276661d06d6bSBaptiste Daroussin 	 * If it is empty or terminated by an escape sequence,
276761d06d6bSBaptiste Daroussin 	 * abort the `ds' request without defining anything.
276861d06d6bSBaptiste Daroussin 	 */
276961d06d6bSBaptiste Daroussin 
277061d06d6bSBaptiste Daroussin 	name = string = buf->buf + pos;
277161d06d6bSBaptiste Daroussin 	if (*name == '\0')
277261d06d6bSBaptiste Daroussin 		return ROFF_IGN;
277361d06d6bSBaptiste Daroussin 
277461d06d6bSBaptiste Daroussin 	namesz = roff_getname(r, &string, ln, pos);
27757295610fSBaptiste Daroussin 	switch (name[namesz]) {
27767295610fSBaptiste Daroussin 	case '\\':
277761d06d6bSBaptiste Daroussin 		return ROFF_IGN;
27787295610fSBaptiste Daroussin 	case '\t':
27797295610fSBaptiste Daroussin 		string = buf->buf + pos + namesz;
27807295610fSBaptiste Daroussin 		break;
27817295610fSBaptiste Daroussin 	default:
27827295610fSBaptiste Daroussin 		break;
27837295610fSBaptiste Daroussin 	}
278461d06d6bSBaptiste Daroussin 
278561d06d6bSBaptiste Daroussin 	/* Read past the initial double-quote, if any. */
278661d06d6bSBaptiste Daroussin 	if (*string == '"')
278761d06d6bSBaptiste Daroussin 		string++;
278861d06d6bSBaptiste Daroussin 
278961d06d6bSBaptiste Daroussin 	/* The rest is the value. */
279061d06d6bSBaptiste Daroussin 	roff_setstrn(&r->strtab, name, namesz, string, strlen(string),
279161d06d6bSBaptiste Daroussin 	    ROFF_as == tok);
279261d06d6bSBaptiste Daroussin 	roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
279361d06d6bSBaptiste Daroussin 	return ROFF_IGN;
279461d06d6bSBaptiste Daroussin }
279561d06d6bSBaptiste Daroussin 
279661d06d6bSBaptiste Daroussin /*
279761d06d6bSBaptiste Daroussin  * Parse a single operator, one or two characters long.
279861d06d6bSBaptiste Daroussin  * If the operator is recognized, return success and advance the
279961d06d6bSBaptiste Daroussin  * parse point, else return failure and let the parse point unchanged.
280061d06d6bSBaptiste Daroussin  */
280161d06d6bSBaptiste Daroussin static int
roff_getop(const char * v,int * pos,char * res)280261d06d6bSBaptiste Daroussin roff_getop(const char *v, int *pos, char *res)
280361d06d6bSBaptiste Daroussin {
280461d06d6bSBaptiste Daroussin 
280561d06d6bSBaptiste Daroussin 	*res = v[*pos];
280661d06d6bSBaptiste Daroussin 
280761d06d6bSBaptiste Daroussin 	switch (*res) {
280861d06d6bSBaptiste Daroussin 	case '+':
280961d06d6bSBaptiste Daroussin 	case '-':
281061d06d6bSBaptiste Daroussin 	case '*':
281161d06d6bSBaptiste Daroussin 	case '/':
281261d06d6bSBaptiste Daroussin 	case '%':
281361d06d6bSBaptiste Daroussin 	case '&':
281461d06d6bSBaptiste Daroussin 	case ':':
281561d06d6bSBaptiste Daroussin 		break;
281661d06d6bSBaptiste Daroussin 	case '<':
281761d06d6bSBaptiste Daroussin 		switch (v[*pos + 1]) {
281861d06d6bSBaptiste Daroussin 		case '=':
281961d06d6bSBaptiste Daroussin 			*res = 'l';
282061d06d6bSBaptiste Daroussin 			(*pos)++;
282161d06d6bSBaptiste Daroussin 			break;
282261d06d6bSBaptiste Daroussin 		case '>':
282361d06d6bSBaptiste Daroussin 			*res = '!';
282461d06d6bSBaptiste Daroussin 			(*pos)++;
282561d06d6bSBaptiste Daroussin 			break;
282661d06d6bSBaptiste Daroussin 		case '?':
282761d06d6bSBaptiste Daroussin 			*res = 'i';
282861d06d6bSBaptiste Daroussin 			(*pos)++;
282961d06d6bSBaptiste Daroussin 			break;
283061d06d6bSBaptiste Daroussin 		default:
283161d06d6bSBaptiste Daroussin 			break;
283261d06d6bSBaptiste Daroussin 		}
283361d06d6bSBaptiste Daroussin 		break;
283461d06d6bSBaptiste Daroussin 	case '>':
283561d06d6bSBaptiste Daroussin 		switch (v[*pos + 1]) {
283661d06d6bSBaptiste Daroussin 		case '=':
283761d06d6bSBaptiste Daroussin 			*res = 'g';
283861d06d6bSBaptiste Daroussin 			(*pos)++;
283961d06d6bSBaptiste Daroussin 			break;
284061d06d6bSBaptiste Daroussin 		case '?':
284161d06d6bSBaptiste Daroussin 			*res = 'a';
284261d06d6bSBaptiste Daroussin 			(*pos)++;
284361d06d6bSBaptiste Daroussin 			break;
284461d06d6bSBaptiste Daroussin 		default:
284561d06d6bSBaptiste Daroussin 			break;
284661d06d6bSBaptiste Daroussin 		}
284761d06d6bSBaptiste Daroussin 		break;
284861d06d6bSBaptiste Daroussin 	case '=':
284961d06d6bSBaptiste Daroussin 		if ('=' == v[*pos + 1])
285061d06d6bSBaptiste Daroussin 			(*pos)++;
285161d06d6bSBaptiste Daroussin 		break;
285261d06d6bSBaptiste Daroussin 	default:
285361d06d6bSBaptiste Daroussin 		return 0;
285461d06d6bSBaptiste Daroussin 	}
285561d06d6bSBaptiste Daroussin 	(*pos)++;
285661d06d6bSBaptiste Daroussin 
285761d06d6bSBaptiste Daroussin 	return *res;
285861d06d6bSBaptiste Daroussin }
285961d06d6bSBaptiste Daroussin 
286061d06d6bSBaptiste Daroussin /*
286161d06d6bSBaptiste Daroussin  * Evaluate either a parenthesized numeric expression
286261d06d6bSBaptiste Daroussin  * or a single signed integer number.
286361d06d6bSBaptiste Daroussin  */
286461d06d6bSBaptiste Daroussin static int
roff_evalpar(struct roff * r,int ln,const char * v,int * pos,int * res,int flags)286561d06d6bSBaptiste Daroussin roff_evalpar(struct roff *r, int ln,
286661d06d6bSBaptiste Daroussin 	const char *v, int *pos, int *res, int flags)
286761d06d6bSBaptiste Daroussin {
286861d06d6bSBaptiste Daroussin 
286961d06d6bSBaptiste Daroussin 	if ('(' != v[*pos])
287061d06d6bSBaptiste Daroussin 		return roff_getnum(v, pos, res, flags);
287161d06d6bSBaptiste Daroussin 
287261d06d6bSBaptiste Daroussin 	(*pos)++;
287361d06d6bSBaptiste Daroussin 	if ( ! roff_evalnum(r, ln, v, pos, res, flags | ROFFNUM_WHITE))
287461d06d6bSBaptiste Daroussin 		return 0;
287561d06d6bSBaptiste Daroussin 
287661d06d6bSBaptiste Daroussin 	/*
287761d06d6bSBaptiste Daroussin 	 * Omission of the closing parenthesis
287861d06d6bSBaptiste Daroussin 	 * is an error in validation mode,
287961d06d6bSBaptiste Daroussin 	 * but ignored in evaluation mode.
288061d06d6bSBaptiste Daroussin 	 */
288161d06d6bSBaptiste Daroussin 
288261d06d6bSBaptiste Daroussin 	if (')' == v[*pos])
288361d06d6bSBaptiste Daroussin 		(*pos)++;
288461d06d6bSBaptiste Daroussin 	else if (NULL == res)
288561d06d6bSBaptiste Daroussin 		return 0;
288661d06d6bSBaptiste Daroussin 
288761d06d6bSBaptiste Daroussin 	return 1;
288861d06d6bSBaptiste Daroussin }
288961d06d6bSBaptiste Daroussin 
289061d06d6bSBaptiste Daroussin /*
289161d06d6bSBaptiste Daroussin  * Evaluate a complete numeric expression.
289261d06d6bSBaptiste Daroussin  * Proceed left to right, there is no concept of precedence.
289361d06d6bSBaptiste Daroussin  */
289461d06d6bSBaptiste Daroussin static int
roff_evalnum(struct roff * r,int ln,const char * v,int * pos,int * res,int flags)289561d06d6bSBaptiste Daroussin roff_evalnum(struct roff *r, int ln, const char *v,
289661d06d6bSBaptiste Daroussin 	int *pos, int *res, int flags)
289761d06d6bSBaptiste Daroussin {
289861d06d6bSBaptiste Daroussin 	int		 mypos, operand2;
289961d06d6bSBaptiste Daroussin 	char		 operator;
290061d06d6bSBaptiste Daroussin 
290161d06d6bSBaptiste Daroussin 	if (NULL == pos) {
290261d06d6bSBaptiste Daroussin 		mypos = 0;
290361d06d6bSBaptiste Daroussin 		pos = &mypos;
290461d06d6bSBaptiste Daroussin 	}
290561d06d6bSBaptiste Daroussin 
290661d06d6bSBaptiste Daroussin 	if (flags & ROFFNUM_WHITE)
290761d06d6bSBaptiste Daroussin 		while (isspace((unsigned char)v[*pos]))
290861d06d6bSBaptiste Daroussin 			(*pos)++;
290961d06d6bSBaptiste Daroussin 
291061d06d6bSBaptiste Daroussin 	if ( ! roff_evalpar(r, ln, v, pos, res, flags))
291161d06d6bSBaptiste Daroussin 		return 0;
291261d06d6bSBaptiste Daroussin 
291361d06d6bSBaptiste Daroussin 	while (1) {
291461d06d6bSBaptiste Daroussin 		if (flags & ROFFNUM_WHITE)
291561d06d6bSBaptiste Daroussin 			while (isspace((unsigned char)v[*pos]))
291661d06d6bSBaptiste Daroussin 				(*pos)++;
291761d06d6bSBaptiste Daroussin 
291861d06d6bSBaptiste Daroussin 		if ( ! roff_getop(v, pos, &operator))
291961d06d6bSBaptiste Daroussin 			break;
292061d06d6bSBaptiste Daroussin 
292161d06d6bSBaptiste Daroussin 		if (flags & ROFFNUM_WHITE)
292261d06d6bSBaptiste Daroussin 			while (isspace((unsigned char)v[*pos]))
292361d06d6bSBaptiste Daroussin 				(*pos)++;
292461d06d6bSBaptiste Daroussin 
292561d06d6bSBaptiste Daroussin 		if ( ! roff_evalpar(r, ln, v, pos, &operand2, flags))
292661d06d6bSBaptiste Daroussin 			return 0;
292761d06d6bSBaptiste Daroussin 
292861d06d6bSBaptiste Daroussin 		if (flags & ROFFNUM_WHITE)
292961d06d6bSBaptiste Daroussin 			while (isspace((unsigned char)v[*pos]))
293061d06d6bSBaptiste Daroussin 				(*pos)++;
293161d06d6bSBaptiste Daroussin 
293261d06d6bSBaptiste Daroussin 		if (NULL == res)
293361d06d6bSBaptiste Daroussin 			continue;
293461d06d6bSBaptiste Daroussin 
293561d06d6bSBaptiste Daroussin 		switch (operator) {
293661d06d6bSBaptiste Daroussin 		case '+':
293761d06d6bSBaptiste Daroussin 			*res += operand2;
293861d06d6bSBaptiste Daroussin 			break;
293961d06d6bSBaptiste Daroussin 		case '-':
294061d06d6bSBaptiste Daroussin 			*res -= operand2;
294161d06d6bSBaptiste Daroussin 			break;
294261d06d6bSBaptiste Daroussin 		case '*':
294361d06d6bSBaptiste Daroussin 			*res *= operand2;
294461d06d6bSBaptiste Daroussin 			break;
294561d06d6bSBaptiste Daroussin 		case '/':
294661d06d6bSBaptiste Daroussin 			if (operand2 == 0) {
294761d06d6bSBaptiste Daroussin 				mandoc_msg(MANDOCERR_DIVZERO,
29487295610fSBaptiste Daroussin 					ln, *pos, "%s", v);
294961d06d6bSBaptiste Daroussin 				*res = 0;
295061d06d6bSBaptiste Daroussin 				break;
295161d06d6bSBaptiste Daroussin 			}
295261d06d6bSBaptiste Daroussin 			*res /= operand2;
295361d06d6bSBaptiste Daroussin 			break;
295461d06d6bSBaptiste Daroussin 		case '%':
295561d06d6bSBaptiste Daroussin 			if (operand2 == 0) {
295661d06d6bSBaptiste Daroussin 				mandoc_msg(MANDOCERR_DIVZERO,
29577295610fSBaptiste Daroussin 					ln, *pos, "%s", v);
295861d06d6bSBaptiste Daroussin 				*res = 0;
295961d06d6bSBaptiste Daroussin 				break;
296061d06d6bSBaptiste Daroussin 			}
296161d06d6bSBaptiste Daroussin 			*res %= operand2;
296261d06d6bSBaptiste Daroussin 			break;
296361d06d6bSBaptiste Daroussin 		case '<':
296461d06d6bSBaptiste Daroussin 			*res = *res < operand2;
296561d06d6bSBaptiste Daroussin 			break;
296661d06d6bSBaptiste Daroussin 		case '>':
296761d06d6bSBaptiste Daroussin 			*res = *res > operand2;
296861d06d6bSBaptiste Daroussin 			break;
296961d06d6bSBaptiste Daroussin 		case 'l':
297061d06d6bSBaptiste Daroussin 			*res = *res <= operand2;
297161d06d6bSBaptiste Daroussin 			break;
297261d06d6bSBaptiste Daroussin 		case 'g':
297361d06d6bSBaptiste Daroussin 			*res = *res >= operand2;
297461d06d6bSBaptiste Daroussin 			break;
297561d06d6bSBaptiste Daroussin 		case '=':
297661d06d6bSBaptiste Daroussin 			*res = *res == operand2;
297761d06d6bSBaptiste Daroussin 			break;
297861d06d6bSBaptiste Daroussin 		case '!':
297961d06d6bSBaptiste Daroussin 			*res = *res != operand2;
298061d06d6bSBaptiste Daroussin 			break;
298161d06d6bSBaptiste Daroussin 		case '&':
298261d06d6bSBaptiste Daroussin 			*res = *res && operand2;
298361d06d6bSBaptiste Daroussin 			break;
298461d06d6bSBaptiste Daroussin 		case ':':
298561d06d6bSBaptiste Daroussin 			*res = *res || operand2;
298661d06d6bSBaptiste Daroussin 			break;
298761d06d6bSBaptiste Daroussin 		case 'i':
298861d06d6bSBaptiste Daroussin 			if (operand2 < *res)
298961d06d6bSBaptiste Daroussin 				*res = operand2;
299061d06d6bSBaptiste Daroussin 			break;
299161d06d6bSBaptiste Daroussin 		case 'a':
299261d06d6bSBaptiste Daroussin 			if (operand2 > *res)
299361d06d6bSBaptiste Daroussin 				*res = operand2;
299461d06d6bSBaptiste Daroussin 			break;
299561d06d6bSBaptiste Daroussin 		default:
299661d06d6bSBaptiste Daroussin 			abort();
299761d06d6bSBaptiste Daroussin 		}
299861d06d6bSBaptiste Daroussin 	}
299961d06d6bSBaptiste Daroussin 	return 1;
300061d06d6bSBaptiste Daroussin }
300161d06d6bSBaptiste Daroussin 
300261d06d6bSBaptiste Daroussin /* --- register management ------------------------------------------------ */
300361d06d6bSBaptiste Daroussin 
300461d06d6bSBaptiste Daroussin void
roff_setreg(struct roff * r,const char * name,int val,char sign)300561d06d6bSBaptiste Daroussin roff_setreg(struct roff *r, const char *name, int val, char sign)
300661d06d6bSBaptiste Daroussin {
300761d06d6bSBaptiste Daroussin 	roff_setregn(r, name, strlen(name), val, sign, INT_MIN);
300861d06d6bSBaptiste Daroussin }
300961d06d6bSBaptiste Daroussin 
301061d06d6bSBaptiste Daroussin static void
roff_setregn(struct roff * r,const char * name,size_t len,int val,char sign,int step)301161d06d6bSBaptiste Daroussin roff_setregn(struct roff *r, const char *name, size_t len,
301261d06d6bSBaptiste Daroussin     int val, char sign, int step)
301361d06d6bSBaptiste Daroussin {
301461d06d6bSBaptiste Daroussin 	struct roffreg	*reg;
301561d06d6bSBaptiste Daroussin 
301661d06d6bSBaptiste Daroussin 	/* Search for an existing register with the same name. */
301761d06d6bSBaptiste Daroussin 	reg = r->regtab;
301861d06d6bSBaptiste Daroussin 
301961d06d6bSBaptiste Daroussin 	while (reg != NULL && (reg->key.sz != len ||
302061d06d6bSBaptiste Daroussin 	    strncmp(reg->key.p, name, len) != 0))
302161d06d6bSBaptiste Daroussin 		reg = reg->next;
302261d06d6bSBaptiste Daroussin 
302361d06d6bSBaptiste Daroussin 	if (NULL == reg) {
302461d06d6bSBaptiste Daroussin 		/* Create a new register. */
302561d06d6bSBaptiste Daroussin 		reg = mandoc_malloc(sizeof(struct roffreg));
302661d06d6bSBaptiste Daroussin 		reg->key.p = mandoc_strndup(name, len);
302761d06d6bSBaptiste Daroussin 		reg->key.sz = len;
302861d06d6bSBaptiste Daroussin 		reg->val = 0;
302961d06d6bSBaptiste Daroussin 		reg->step = 0;
303061d06d6bSBaptiste Daroussin 		reg->next = r->regtab;
303161d06d6bSBaptiste Daroussin 		r->regtab = reg;
303261d06d6bSBaptiste Daroussin 	}
303361d06d6bSBaptiste Daroussin 
303461d06d6bSBaptiste Daroussin 	if ('+' == sign)
303561d06d6bSBaptiste Daroussin 		reg->val += val;
303661d06d6bSBaptiste Daroussin 	else if ('-' == sign)
303761d06d6bSBaptiste Daroussin 		reg->val -= val;
303861d06d6bSBaptiste Daroussin 	else
303961d06d6bSBaptiste Daroussin 		reg->val = val;
304061d06d6bSBaptiste Daroussin 	if (step != INT_MIN)
304161d06d6bSBaptiste Daroussin 		reg->step = step;
304261d06d6bSBaptiste Daroussin }
304361d06d6bSBaptiste Daroussin 
304461d06d6bSBaptiste Daroussin /*
304561d06d6bSBaptiste Daroussin  * Handle some predefined read-only number registers.
304661d06d6bSBaptiste Daroussin  * For now, return -1 if the requested register is not predefined;
304761d06d6bSBaptiste Daroussin  * in case a predefined read-only register having the value -1
304861d06d6bSBaptiste Daroussin  * were to turn up, another special value would have to be chosen.
304961d06d6bSBaptiste Daroussin  */
305061d06d6bSBaptiste Daroussin static int
roff_getregro(const struct roff * r,const char * name)305161d06d6bSBaptiste Daroussin roff_getregro(const struct roff *r, const char *name)
305261d06d6bSBaptiste Daroussin {
305361d06d6bSBaptiste Daroussin 
305461d06d6bSBaptiste Daroussin 	switch (*name) {
305561d06d6bSBaptiste Daroussin 	case '$':  /* Number of arguments of the last macro evaluated. */
30567295610fSBaptiste Daroussin 		return r->mstackpos < 0 ? 0 : r->mstack[r->mstackpos].argc;
305761d06d6bSBaptiste Daroussin 	case 'A':  /* ASCII approximation mode is always off. */
305861d06d6bSBaptiste Daroussin 		return 0;
305961d06d6bSBaptiste Daroussin 	case 'g':  /* Groff compatibility mode is always on. */
306061d06d6bSBaptiste Daroussin 		return 1;
306161d06d6bSBaptiste Daroussin 	case 'H':  /* Fixed horizontal resolution. */
306261d06d6bSBaptiste Daroussin 		return 24;
306361d06d6bSBaptiste Daroussin 	case 'j':  /* Always adjust left margin only. */
306461d06d6bSBaptiste Daroussin 		return 0;
306561d06d6bSBaptiste Daroussin 	case 'T':  /* Some output device is always defined. */
306661d06d6bSBaptiste Daroussin 		return 1;
306761d06d6bSBaptiste Daroussin 	case 'V':  /* Fixed vertical resolution. */
306861d06d6bSBaptiste Daroussin 		return 40;
306961d06d6bSBaptiste Daroussin 	default:
307061d06d6bSBaptiste Daroussin 		return -1;
307161d06d6bSBaptiste Daroussin 	}
307261d06d6bSBaptiste Daroussin }
307361d06d6bSBaptiste Daroussin 
307461d06d6bSBaptiste Daroussin int
roff_getreg(struct roff * r,const char * name)307561d06d6bSBaptiste Daroussin roff_getreg(struct roff *r, const char *name)
307661d06d6bSBaptiste Daroussin {
307761d06d6bSBaptiste Daroussin 	return roff_getregn(r, name, strlen(name), '\0');
307861d06d6bSBaptiste Daroussin }
307961d06d6bSBaptiste Daroussin 
308061d06d6bSBaptiste Daroussin static int
roff_getregn(struct roff * r,const char * name,size_t len,char sign)308161d06d6bSBaptiste Daroussin roff_getregn(struct roff *r, const char *name, size_t len, char sign)
308261d06d6bSBaptiste Daroussin {
308361d06d6bSBaptiste Daroussin 	struct roffreg	*reg;
308461d06d6bSBaptiste Daroussin 	int		 val;
308561d06d6bSBaptiste Daroussin 
308661d06d6bSBaptiste Daroussin 	if ('.' == name[0] && 2 == len) {
308761d06d6bSBaptiste Daroussin 		val = roff_getregro(r, name + 1);
308861d06d6bSBaptiste Daroussin 		if (-1 != val)
308961d06d6bSBaptiste Daroussin 			return val;
309061d06d6bSBaptiste Daroussin 	}
309161d06d6bSBaptiste Daroussin 
309261d06d6bSBaptiste Daroussin 	for (reg = r->regtab; reg; reg = reg->next) {
309361d06d6bSBaptiste Daroussin 		if (len == reg->key.sz &&
309461d06d6bSBaptiste Daroussin 		    0 == strncmp(name, reg->key.p, len)) {
309561d06d6bSBaptiste Daroussin 			switch (sign) {
309661d06d6bSBaptiste Daroussin 			case '+':
309761d06d6bSBaptiste Daroussin 				reg->val += reg->step;
309861d06d6bSBaptiste Daroussin 				break;
309961d06d6bSBaptiste Daroussin 			case '-':
310061d06d6bSBaptiste Daroussin 				reg->val -= reg->step;
310161d06d6bSBaptiste Daroussin 				break;
310261d06d6bSBaptiste Daroussin 			default:
310361d06d6bSBaptiste Daroussin 				break;
310461d06d6bSBaptiste Daroussin 			}
310561d06d6bSBaptiste Daroussin 			return reg->val;
310661d06d6bSBaptiste Daroussin 		}
310761d06d6bSBaptiste Daroussin 	}
310861d06d6bSBaptiste Daroussin 
310961d06d6bSBaptiste Daroussin 	roff_setregn(r, name, len, 0, '\0', INT_MIN);
311061d06d6bSBaptiste Daroussin 	return 0;
311161d06d6bSBaptiste Daroussin }
311261d06d6bSBaptiste Daroussin 
311361d06d6bSBaptiste Daroussin static int
roff_hasregn(const struct roff * r,const char * name,size_t len)311461d06d6bSBaptiste Daroussin roff_hasregn(const struct roff *r, const char *name, size_t len)
311561d06d6bSBaptiste Daroussin {
311661d06d6bSBaptiste Daroussin 	struct roffreg	*reg;
311761d06d6bSBaptiste Daroussin 	int		 val;
311861d06d6bSBaptiste Daroussin 
311961d06d6bSBaptiste Daroussin 	if ('.' == name[0] && 2 == len) {
312061d06d6bSBaptiste Daroussin 		val = roff_getregro(r, name + 1);
312161d06d6bSBaptiste Daroussin 		if (-1 != val)
312261d06d6bSBaptiste Daroussin 			return 1;
312361d06d6bSBaptiste Daroussin 	}
312461d06d6bSBaptiste Daroussin 
312561d06d6bSBaptiste Daroussin 	for (reg = r->regtab; reg; reg = reg->next)
312661d06d6bSBaptiste Daroussin 		if (len == reg->key.sz &&
312761d06d6bSBaptiste Daroussin 		    0 == strncmp(name, reg->key.p, len))
312861d06d6bSBaptiste Daroussin 			return 1;
312961d06d6bSBaptiste Daroussin 
313061d06d6bSBaptiste Daroussin 	return 0;
313161d06d6bSBaptiste Daroussin }
313261d06d6bSBaptiste Daroussin 
313361d06d6bSBaptiste Daroussin static void
roff_freereg(struct roffreg * reg)313461d06d6bSBaptiste Daroussin roff_freereg(struct roffreg *reg)
313561d06d6bSBaptiste Daroussin {
313661d06d6bSBaptiste Daroussin 	struct roffreg	*old_reg;
313761d06d6bSBaptiste Daroussin 
313861d06d6bSBaptiste Daroussin 	while (NULL != reg) {
313961d06d6bSBaptiste Daroussin 		free(reg->key.p);
314061d06d6bSBaptiste Daroussin 		old_reg = reg;
314161d06d6bSBaptiste Daroussin 		reg = reg->next;
314261d06d6bSBaptiste Daroussin 		free(old_reg);
314361d06d6bSBaptiste Daroussin 	}
314461d06d6bSBaptiste Daroussin }
314561d06d6bSBaptiste Daroussin 
31467295610fSBaptiste Daroussin static int
roff_nr(ROFF_ARGS)314761d06d6bSBaptiste Daroussin roff_nr(ROFF_ARGS)
314861d06d6bSBaptiste Daroussin {
314961d06d6bSBaptiste Daroussin 	char		*key, *val, *step;
315061d06d6bSBaptiste Daroussin 	size_t		 keysz;
315161d06d6bSBaptiste Daroussin 	int		 iv, is, len;
315261d06d6bSBaptiste Daroussin 	char		 sign;
315361d06d6bSBaptiste Daroussin 
315461d06d6bSBaptiste Daroussin 	key = val = buf->buf + pos;
315561d06d6bSBaptiste Daroussin 	if (*key == '\0')
315661d06d6bSBaptiste Daroussin 		return ROFF_IGN;
315761d06d6bSBaptiste Daroussin 
315861d06d6bSBaptiste Daroussin 	keysz = roff_getname(r, &val, ln, pos);
31597295610fSBaptiste Daroussin 	if (key[keysz] == '\\' || key[keysz] == '\t')
316061d06d6bSBaptiste Daroussin 		return ROFF_IGN;
316161d06d6bSBaptiste Daroussin 
316261d06d6bSBaptiste Daroussin 	sign = *val;
316361d06d6bSBaptiste Daroussin 	if (sign == '+' || sign == '-')
316461d06d6bSBaptiste Daroussin 		val++;
316561d06d6bSBaptiste Daroussin 
316661d06d6bSBaptiste Daroussin 	len = 0;
316761d06d6bSBaptiste Daroussin 	if (roff_evalnum(r, ln, val, &len, &iv, ROFFNUM_SCALE) == 0)
316861d06d6bSBaptiste Daroussin 		return ROFF_IGN;
316961d06d6bSBaptiste Daroussin 
317061d06d6bSBaptiste Daroussin 	step = val + len;
317161d06d6bSBaptiste Daroussin 	while (isspace((unsigned char)*step))
317261d06d6bSBaptiste Daroussin 		step++;
317361d06d6bSBaptiste Daroussin 	if (roff_evalnum(r, ln, step, NULL, &is, 0) == 0)
317461d06d6bSBaptiste Daroussin 		is = INT_MIN;
317561d06d6bSBaptiste Daroussin 
317661d06d6bSBaptiste Daroussin 	roff_setregn(r, key, keysz, iv, sign, is);
317761d06d6bSBaptiste Daroussin 	return ROFF_IGN;
317861d06d6bSBaptiste Daroussin }
317961d06d6bSBaptiste Daroussin 
31807295610fSBaptiste Daroussin static int
roff_rr(ROFF_ARGS)318161d06d6bSBaptiste Daroussin roff_rr(ROFF_ARGS)
318261d06d6bSBaptiste Daroussin {
318361d06d6bSBaptiste Daroussin 	struct roffreg	*reg, **prev;
318461d06d6bSBaptiste Daroussin 	char		*name, *cp;
318561d06d6bSBaptiste Daroussin 	size_t		 namesz;
318661d06d6bSBaptiste Daroussin 
318761d06d6bSBaptiste Daroussin 	name = cp = buf->buf + pos;
318861d06d6bSBaptiste Daroussin 	if (*name == '\0')
318961d06d6bSBaptiste Daroussin 		return ROFF_IGN;
319061d06d6bSBaptiste Daroussin 	namesz = roff_getname(r, &cp, ln, pos);
319161d06d6bSBaptiste Daroussin 	name[namesz] = '\0';
319261d06d6bSBaptiste Daroussin 
319361d06d6bSBaptiste Daroussin 	prev = &r->regtab;
319461d06d6bSBaptiste Daroussin 	while (1) {
319561d06d6bSBaptiste Daroussin 		reg = *prev;
319661d06d6bSBaptiste Daroussin 		if (reg == NULL || !strcmp(name, reg->key.p))
319761d06d6bSBaptiste Daroussin 			break;
319861d06d6bSBaptiste Daroussin 		prev = &reg->next;
319961d06d6bSBaptiste Daroussin 	}
320061d06d6bSBaptiste Daroussin 	if (reg != NULL) {
320161d06d6bSBaptiste Daroussin 		*prev = reg->next;
320261d06d6bSBaptiste Daroussin 		free(reg->key.p);
320361d06d6bSBaptiste Daroussin 		free(reg);
320461d06d6bSBaptiste Daroussin 	}
320561d06d6bSBaptiste Daroussin 	return ROFF_IGN;
320661d06d6bSBaptiste Daroussin }
320761d06d6bSBaptiste Daroussin 
320861d06d6bSBaptiste Daroussin /* --- handler functions for roff requests -------------------------------- */
320961d06d6bSBaptiste Daroussin 
32107295610fSBaptiste Daroussin static int
roff_rm(ROFF_ARGS)321161d06d6bSBaptiste Daroussin roff_rm(ROFF_ARGS)
321261d06d6bSBaptiste Daroussin {
321361d06d6bSBaptiste Daroussin 	const char	 *name;
321461d06d6bSBaptiste Daroussin 	char		 *cp;
321561d06d6bSBaptiste Daroussin 	size_t		  namesz;
321661d06d6bSBaptiste Daroussin 
321761d06d6bSBaptiste Daroussin 	cp = buf->buf + pos;
321861d06d6bSBaptiste Daroussin 	while (*cp != '\0') {
321961d06d6bSBaptiste Daroussin 		name = cp;
322061d06d6bSBaptiste Daroussin 		namesz = roff_getname(r, &cp, ln, (int)(cp - buf->buf));
322161d06d6bSBaptiste Daroussin 		roff_setstrn(&r->strtab, name, namesz, NULL, 0, 0);
322261d06d6bSBaptiste Daroussin 		roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
32237295610fSBaptiste Daroussin 		if (name[namesz] == '\\' || name[namesz] == '\t')
322461d06d6bSBaptiste Daroussin 			break;
322561d06d6bSBaptiste Daroussin 	}
322661d06d6bSBaptiste Daroussin 	return ROFF_IGN;
322761d06d6bSBaptiste Daroussin }
322861d06d6bSBaptiste Daroussin 
32297295610fSBaptiste Daroussin static int
roff_it(ROFF_ARGS)323061d06d6bSBaptiste Daroussin roff_it(ROFF_ARGS)
323161d06d6bSBaptiste Daroussin {
323261d06d6bSBaptiste Daroussin 	int		 iv;
323361d06d6bSBaptiste Daroussin 
323461d06d6bSBaptiste Daroussin 	/* Parse the number of lines. */
323561d06d6bSBaptiste Daroussin 
323661d06d6bSBaptiste Daroussin 	if ( ! roff_evalnum(r, ln, buf->buf, &pos, &iv, 0)) {
32377295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_IT_NONUM,
32387295610fSBaptiste Daroussin 		    ln, ppos, "%s", buf->buf + 1);
323961d06d6bSBaptiste Daroussin 		return ROFF_IGN;
324061d06d6bSBaptiste Daroussin 	}
324161d06d6bSBaptiste Daroussin 
324261d06d6bSBaptiste Daroussin 	while (isspace((unsigned char)buf->buf[pos]))
324361d06d6bSBaptiste Daroussin 		pos++;
324461d06d6bSBaptiste Daroussin 
324561d06d6bSBaptiste Daroussin 	/*
324661d06d6bSBaptiste Daroussin 	 * Arm the input line trap.
324761d06d6bSBaptiste Daroussin 	 * Special-casing "an-trap" is an ugly workaround to cope
324861d06d6bSBaptiste Daroussin 	 * with DocBook stupidly fiddling with man(7) internals.
324961d06d6bSBaptiste Daroussin 	 */
325061d06d6bSBaptiste Daroussin 
325161d06d6bSBaptiste Daroussin 	roffit_lines = iv;
325261d06d6bSBaptiste Daroussin 	roffit_macro = mandoc_strdup(iv != 1 ||
325361d06d6bSBaptiste Daroussin 	    strcmp(buf->buf + pos, "an-trap") ?
325461d06d6bSBaptiste Daroussin 	    buf->buf + pos : "br");
325561d06d6bSBaptiste Daroussin 	return ROFF_IGN;
325661d06d6bSBaptiste Daroussin }
325761d06d6bSBaptiste Daroussin 
32587295610fSBaptiste Daroussin static int
roff_Dd(ROFF_ARGS)325961d06d6bSBaptiste Daroussin roff_Dd(ROFF_ARGS)
326061d06d6bSBaptiste Daroussin {
326161d06d6bSBaptiste Daroussin 	int		 mask;
326261d06d6bSBaptiste Daroussin 	enum roff_tok	 t, te;
326361d06d6bSBaptiste Daroussin 
326461d06d6bSBaptiste Daroussin 	switch (tok) {
326561d06d6bSBaptiste Daroussin 	case ROFF_Dd:
326661d06d6bSBaptiste Daroussin 		tok = MDOC_Dd;
326761d06d6bSBaptiste Daroussin 		te = MDOC_MAX;
326861d06d6bSBaptiste Daroussin 		if (r->format == 0)
326961d06d6bSBaptiste Daroussin 			r->format = MPARSE_MDOC;
327061d06d6bSBaptiste Daroussin 		mask = MPARSE_MDOC | MPARSE_QUICK;
327161d06d6bSBaptiste Daroussin 		break;
327261d06d6bSBaptiste Daroussin 	case ROFF_TH:
327361d06d6bSBaptiste Daroussin 		tok = MAN_TH;
327461d06d6bSBaptiste Daroussin 		te = MAN_MAX;
327561d06d6bSBaptiste Daroussin 		if (r->format == 0)
327661d06d6bSBaptiste Daroussin 			r->format = MPARSE_MAN;
327761d06d6bSBaptiste Daroussin 		mask = MPARSE_QUICK;
327861d06d6bSBaptiste Daroussin 		break;
327961d06d6bSBaptiste Daroussin 	default:
328061d06d6bSBaptiste Daroussin 		abort();
328161d06d6bSBaptiste Daroussin 	}
328261d06d6bSBaptiste Daroussin 	if ((r->options & mask) == 0)
328361d06d6bSBaptiste Daroussin 		for (t = tok; t < te; t++)
328461d06d6bSBaptiste Daroussin 			roff_setstr(r, roff_name[t], NULL, 0);
328561d06d6bSBaptiste Daroussin 	return ROFF_CONT;
328661d06d6bSBaptiste Daroussin }
328761d06d6bSBaptiste Daroussin 
32887295610fSBaptiste Daroussin static int
roff_TE(ROFF_ARGS)328961d06d6bSBaptiste Daroussin roff_TE(ROFF_ARGS)
329061d06d6bSBaptiste Daroussin {
32917295610fSBaptiste Daroussin 	r->man->flags &= ~ROFF_NONOFILL;
329261d06d6bSBaptiste Daroussin 	if (r->tbl == NULL) {
32937295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "TE");
329461d06d6bSBaptiste Daroussin 		return ROFF_IGN;
329561d06d6bSBaptiste Daroussin 	}
32967295610fSBaptiste Daroussin 	if (tbl_end(r->tbl, 0) == 0) {
329761d06d6bSBaptiste Daroussin 		r->tbl = NULL;
329861d06d6bSBaptiste Daroussin 		free(buf->buf);
329961d06d6bSBaptiste Daroussin 		buf->buf = mandoc_strdup(".sp");
330061d06d6bSBaptiste Daroussin 		buf->sz = 4;
330161d06d6bSBaptiste Daroussin 		*offs = 0;
330261d06d6bSBaptiste Daroussin 		return ROFF_REPARSE;
330361d06d6bSBaptiste Daroussin 	}
330461d06d6bSBaptiste Daroussin 	r->tbl = NULL;
330561d06d6bSBaptiste Daroussin 	return ROFF_IGN;
330661d06d6bSBaptiste Daroussin }
330761d06d6bSBaptiste Daroussin 
33087295610fSBaptiste Daroussin static int
roff_T_(ROFF_ARGS)330961d06d6bSBaptiste Daroussin roff_T_(ROFF_ARGS)
331061d06d6bSBaptiste Daroussin {
331161d06d6bSBaptiste Daroussin 
331261d06d6bSBaptiste Daroussin 	if (NULL == r->tbl)
33137295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "T&");
331461d06d6bSBaptiste Daroussin 	else
331561d06d6bSBaptiste Daroussin 		tbl_restart(ln, ppos, r->tbl);
331661d06d6bSBaptiste Daroussin 
331761d06d6bSBaptiste Daroussin 	return ROFF_IGN;
331861d06d6bSBaptiste Daroussin }
331961d06d6bSBaptiste Daroussin 
332061d06d6bSBaptiste Daroussin /*
332161d06d6bSBaptiste Daroussin  * Handle in-line equation delimiters.
332261d06d6bSBaptiste Daroussin  */
33237295610fSBaptiste Daroussin static int
roff_eqndelim(struct roff * r,struct buf * buf,int pos)332461d06d6bSBaptiste Daroussin roff_eqndelim(struct roff *r, struct buf *buf, int pos)
332561d06d6bSBaptiste Daroussin {
332661d06d6bSBaptiste Daroussin 	char		*cp1, *cp2;
332761d06d6bSBaptiste Daroussin 	const char	*bef_pr, *bef_nl, *mac, *aft_nl, *aft_pr;
332861d06d6bSBaptiste Daroussin 
332961d06d6bSBaptiste Daroussin 	/*
333061d06d6bSBaptiste Daroussin 	 * Outside equations, look for an opening delimiter.
333161d06d6bSBaptiste Daroussin 	 * If we are inside an equation, we already know it is
333261d06d6bSBaptiste Daroussin 	 * in-line, or this function wouldn't have been called;
333361d06d6bSBaptiste Daroussin 	 * so look for a closing delimiter.
333461d06d6bSBaptiste Daroussin 	 */
333561d06d6bSBaptiste Daroussin 
333661d06d6bSBaptiste Daroussin 	cp1 = buf->buf + pos;
333761d06d6bSBaptiste Daroussin 	cp2 = strchr(cp1, r->eqn == NULL ?
333861d06d6bSBaptiste Daroussin 	    r->last_eqn->odelim : r->last_eqn->cdelim);
333961d06d6bSBaptiste Daroussin 	if (cp2 == NULL)
334061d06d6bSBaptiste Daroussin 		return ROFF_CONT;
334161d06d6bSBaptiste Daroussin 
334261d06d6bSBaptiste Daroussin 	*cp2++ = '\0';
334361d06d6bSBaptiste Daroussin 	bef_pr = bef_nl = aft_nl = aft_pr = "";
334461d06d6bSBaptiste Daroussin 
334561d06d6bSBaptiste Daroussin 	/* Handle preceding text, protecting whitespace. */
334661d06d6bSBaptiste Daroussin 
334761d06d6bSBaptiste Daroussin 	if (*buf->buf != '\0') {
334861d06d6bSBaptiste Daroussin 		if (r->eqn == NULL)
334961d06d6bSBaptiste Daroussin 			bef_pr = "\\&";
335061d06d6bSBaptiste Daroussin 		bef_nl = "\n";
335161d06d6bSBaptiste Daroussin 	}
335261d06d6bSBaptiste Daroussin 
335361d06d6bSBaptiste Daroussin 	/*
335461d06d6bSBaptiste Daroussin 	 * Prepare replacing the delimiter with an equation macro
335561d06d6bSBaptiste Daroussin 	 * and drop leading white space from the equation.
335661d06d6bSBaptiste Daroussin 	 */
335761d06d6bSBaptiste Daroussin 
335861d06d6bSBaptiste Daroussin 	if (r->eqn == NULL) {
335961d06d6bSBaptiste Daroussin 		while (*cp2 == ' ')
336061d06d6bSBaptiste Daroussin 			cp2++;
336161d06d6bSBaptiste Daroussin 		mac = ".EQ";
336261d06d6bSBaptiste Daroussin 	} else
336361d06d6bSBaptiste Daroussin 		mac = ".EN";
336461d06d6bSBaptiste Daroussin 
336561d06d6bSBaptiste Daroussin 	/* Handle following text, protecting whitespace. */
336661d06d6bSBaptiste Daroussin 
336761d06d6bSBaptiste Daroussin 	if (*cp2 != '\0') {
336861d06d6bSBaptiste Daroussin 		aft_nl = "\n";
336961d06d6bSBaptiste Daroussin 		if (r->eqn != NULL)
337061d06d6bSBaptiste Daroussin 			aft_pr = "\\&";
337161d06d6bSBaptiste Daroussin 	}
337261d06d6bSBaptiste Daroussin 
337361d06d6bSBaptiste Daroussin 	/* Do the actual replacement. */
337461d06d6bSBaptiste Daroussin 
337561d06d6bSBaptiste Daroussin 	buf->sz = mandoc_asprintf(&cp1, "%s%s%s%s%s%s%s", buf->buf,
337661d06d6bSBaptiste Daroussin 	    bef_pr, bef_nl, mac, aft_nl, aft_pr, cp2) + 1;
337761d06d6bSBaptiste Daroussin 	free(buf->buf);
337861d06d6bSBaptiste Daroussin 	buf->buf = cp1;
337961d06d6bSBaptiste Daroussin 
338061d06d6bSBaptiste Daroussin 	/* Toggle the in-line state of the eqn subsystem. */
338161d06d6bSBaptiste Daroussin 
338261d06d6bSBaptiste Daroussin 	r->eqn_inline = r->eqn == NULL;
338361d06d6bSBaptiste Daroussin 	return ROFF_REPARSE;
338461d06d6bSBaptiste Daroussin }
338561d06d6bSBaptiste Daroussin 
33867295610fSBaptiste Daroussin static int
roff_EQ(ROFF_ARGS)338761d06d6bSBaptiste Daroussin roff_EQ(ROFF_ARGS)
338861d06d6bSBaptiste Daroussin {
338961d06d6bSBaptiste Daroussin 	struct roff_node	*n;
339061d06d6bSBaptiste Daroussin 
33917295610fSBaptiste Daroussin 	if (r->man->meta.macroset == MACROSET_MAN)
339261d06d6bSBaptiste Daroussin 		man_breakscope(r->man, ROFF_EQ);
339361d06d6bSBaptiste Daroussin 	n = roff_node_alloc(r->man, ln, ppos, ROFFT_EQN, TOKEN_NONE);
339461d06d6bSBaptiste Daroussin 	if (ln > r->man->last->line)
339561d06d6bSBaptiste Daroussin 		n->flags |= NODE_LINE;
33967295610fSBaptiste Daroussin 	n->eqn = eqn_box_new();
339761d06d6bSBaptiste Daroussin 	roff_node_append(r->man, n);
339861d06d6bSBaptiste Daroussin 	r->man->next = ROFF_NEXT_SIBLING;
339961d06d6bSBaptiste Daroussin 
340061d06d6bSBaptiste Daroussin 	assert(r->eqn == NULL);
340161d06d6bSBaptiste Daroussin 	if (r->last_eqn == NULL)
34027295610fSBaptiste Daroussin 		r->last_eqn = eqn_alloc();
340361d06d6bSBaptiste Daroussin 	else
340461d06d6bSBaptiste Daroussin 		eqn_reset(r->last_eqn);
340561d06d6bSBaptiste Daroussin 	r->eqn = r->last_eqn;
340661d06d6bSBaptiste Daroussin 	r->eqn->node = n;
340761d06d6bSBaptiste Daroussin 
340861d06d6bSBaptiste Daroussin 	if (buf->buf[pos] != '\0')
34097295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
341061d06d6bSBaptiste Daroussin 		    ".EQ %s", buf->buf + pos);
341161d06d6bSBaptiste Daroussin 
341261d06d6bSBaptiste Daroussin 	return ROFF_IGN;
341361d06d6bSBaptiste Daroussin }
341461d06d6bSBaptiste Daroussin 
34157295610fSBaptiste Daroussin static int
roff_EN(ROFF_ARGS)341661d06d6bSBaptiste Daroussin roff_EN(ROFF_ARGS)
341761d06d6bSBaptiste Daroussin {
341861d06d6bSBaptiste Daroussin 	if (r->eqn != NULL) {
341961d06d6bSBaptiste Daroussin 		eqn_parse(r->eqn);
342061d06d6bSBaptiste Daroussin 		r->eqn = NULL;
342161d06d6bSBaptiste Daroussin 	} else
34227295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "EN");
342361d06d6bSBaptiste Daroussin 	if (buf->buf[pos] != '\0')
34247295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
342561d06d6bSBaptiste Daroussin 		    "EN %s", buf->buf + pos);
342661d06d6bSBaptiste Daroussin 	return ROFF_IGN;
342761d06d6bSBaptiste Daroussin }
342861d06d6bSBaptiste Daroussin 
34297295610fSBaptiste Daroussin static int
roff_TS(ROFF_ARGS)343061d06d6bSBaptiste Daroussin roff_TS(ROFF_ARGS)
343161d06d6bSBaptiste Daroussin {
343261d06d6bSBaptiste Daroussin 	if (r->tbl != NULL) {
34337295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_BROKEN, ln, ppos, "TS breaks TS");
34347295610fSBaptiste Daroussin 		tbl_end(r->tbl, 0);
343561d06d6bSBaptiste Daroussin 	}
34367295610fSBaptiste Daroussin 	r->man->flags |= ROFF_NONOFILL;
34377295610fSBaptiste Daroussin 	r->tbl = tbl_alloc(ppos, ln, r->last_tbl);
34387295610fSBaptiste Daroussin 	if (r->last_tbl == NULL)
343961d06d6bSBaptiste Daroussin 		r->first_tbl = r->tbl;
344061d06d6bSBaptiste Daroussin 	r->last_tbl = r->tbl;
344161d06d6bSBaptiste Daroussin 	return ROFF_IGN;
344261d06d6bSBaptiste Daroussin }
344361d06d6bSBaptiste Daroussin 
34447295610fSBaptiste Daroussin static int
roff_noarg(ROFF_ARGS)34457295610fSBaptiste Daroussin roff_noarg(ROFF_ARGS)
34467295610fSBaptiste Daroussin {
34477295610fSBaptiste Daroussin 	if (r->man->flags & (MAN_BLINE | MAN_ELINE))
34487295610fSBaptiste Daroussin 		man_breakscope(r->man, tok);
34497295610fSBaptiste Daroussin 	if (tok == ROFF_brp)
34507295610fSBaptiste Daroussin 		tok = ROFF_br;
34517295610fSBaptiste Daroussin 	roff_elem_alloc(r->man, ln, ppos, tok);
34527295610fSBaptiste Daroussin 	if (buf->buf[pos] != '\0')
34537295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
34547295610fSBaptiste Daroussin 		   "%s %s", roff_name[tok], buf->buf + pos);
34557295610fSBaptiste Daroussin 	if (tok == ROFF_nf)
34567295610fSBaptiste Daroussin 		r->man->flags |= ROFF_NOFILL;
34577295610fSBaptiste Daroussin 	else if (tok == ROFF_fi)
34587295610fSBaptiste Daroussin 		r->man->flags &= ~ROFF_NOFILL;
34597295610fSBaptiste Daroussin 	r->man->last->flags |= NODE_LINE | NODE_VALID | NODE_ENDED;
34607295610fSBaptiste Daroussin 	r->man->next = ROFF_NEXT_SIBLING;
34617295610fSBaptiste Daroussin 	return ROFF_IGN;
34627295610fSBaptiste Daroussin }
34637295610fSBaptiste Daroussin 
34647295610fSBaptiste Daroussin static int
roff_onearg(ROFF_ARGS)346561d06d6bSBaptiste Daroussin roff_onearg(ROFF_ARGS)
346661d06d6bSBaptiste Daroussin {
346761d06d6bSBaptiste Daroussin 	struct roff_node	*n;
346861d06d6bSBaptiste Daroussin 	char			*cp;
346961d06d6bSBaptiste Daroussin 	int			 npos;
347061d06d6bSBaptiste Daroussin 
347161d06d6bSBaptiste Daroussin 	if (r->man->flags & (MAN_BLINE | MAN_ELINE) &&
347261d06d6bSBaptiste Daroussin 	    (tok == ROFF_ce || tok == ROFF_rj || tok == ROFF_sp ||
347361d06d6bSBaptiste Daroussin 	     tok == ROFF_ti))
347461d06d6bSBaptiste Daroussin 		man_breakscope(r->man, tok);
347561d06d6bSBaptiste Daroussin 
347661d06d6bSBaptiste Daroussin 	if (roffce_node != NULL && (tok == ROFF_ce || tok == ROFF_rj)) {
347761d06d6bSBaptiste Daroussin 		r->man->last = roffce_node;
347861d06d6bSBaptiste Daroussin 		r->man->next = ROFF_NEXT_SIBLING;
347961d06d6bSBaptiste Daroussin 	}
348061d06d6bSBaptiste Daroussin 
348161d06d6bSBaptiste Daroussin 	roff_elem_alloc(r->man, ln, ppos, tok);
348261d06d6bSBaptiste Daroussin 	n = r->man->last;
348361d06d6bSBaptiste Daroussin 
348461d06d6bSBaptiste Daroussin 	cp = buf->buf + pos;
348561d06d6bSBaptiste Daroussin 	if (*cp != '\0') {
348661d06d6bSBaptiste Daroussin 		while (*cp != '\0' && *cp != ' ')
348761d06d6bSBaptiste Daroussin 			cp++;
348861d06d6bSBaptiste Daroussin 		while (*cp == ' ')
348961d06d6bSBaptiste Daroussin 			*cp++ = '\0';
349061d06d6bSBaptiste Daroussin 		if (*cp != '\0')
34917295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_ARG_EXCESS,
34927295610fSBaptiste Daroussin 			    ln, (int)(cp - buf->buf),
349361d06d6bSBaptiste Daroussin 			    "%s ... %s", roff_name[tok], cp);
349461d06d6bSBaptiste Daroussin 		roff_word_alloc(r->man, ln, pos, buf->buf + pos);
349561d06d6bSBaptiste Daroussin 	}
349661d06d6bSBaptiste Daroussin 
349761d06d6bSBaptiste Daroussin 	if (tok == ROFF_ce || tok == ROFF_rj) {
349861d06d6bSBaptiste Daroussin 		if (r->man->last->type == ROFFT_ELEM) {
349961d06d6bSBaptiste Daroussin 			roff_word_alloc(r->man, ln, pos, "1");
350061d06d6bSBaptiste Daroussin 			r->man->last->flags |= NODE_NOSRC;
350161d06d6bSBaptiste Daroussin 		}
350261d06d6bSBaptiste Daroussin 		npos = 0;
350361d06d6bSBaptiste Daroussin 		if (roff_evalnum(r, ln, r->man->last->string, &npos,
350461d06d6bSBaptiste Daroussin 		    &roffce_lines, 0) == 0) {
35057295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_CE_NONUM,
35067295610fSBaptiste Daroussin 			    ln, pos, "ce %s", buf->buf + pos);
350761d06d6bSBaptiste Daroussin 			roffce_lines = 1;
350861d06d6bSBaptiste Daroussin 		}
350961d06d6bSBaptiste Daroussin 		if (roffce_lines < 1) {
351061d06d6bSBaptiste Daroussin 			r->man->last = r->man->last->parent;
351161d06d6bSBaptiste Daroussin 			roffce_node = NULL;
351261d06d6bSBaptiste Daroussin 			roffce_lines = 0;
351361d06d6bSBaptiste Daroussin 		} else
351461d06d6bSBaptiste Daroussin 			roffce_node = r->man->last->parent;
351561d06d6bSBaptiste Daroussin 	} else {
351661d06d6bSBaptiste Daroussin 		n->flags |= NODE_VALID | NODE_ENDED;
351761d06d6bSBaptiste Daroussin 		r->man->last = n;
351861d06d6bSBaptiste Daroussin 	}
351961d06d6bSBaptiste Daroussin 	n->flags |= NODE_LINE;
352061d06d6bSBaptiste Daroussin 	r->man->next = ROFF_NEXT_SIBLING;
352161d06d6bSBaptiste Daroussin 	return ROFF_IGN;
352261d06d6bSBaptiste Daroussin }
352361d06d6bSBaptiste Daroussin 
35247295610fSBaptiste Daroussin static int
roff_manyarg(ROFF_ARGS)352561d06d6bSBaptiste Daroussin roff_manyarg(ROFF_ARGS)
352661d06d6bSBaptiste Daroussin {
352761d06d6bSBaptiste Daroussin 	struct roff_node	*n;
352861d06d6bSBaptiste Daroussin 	char			*sp, *ep;
352961d06d6bSBaptiste Daroussin 
353061d06d6bSBaptiste Daroussin 	roff_elem_alloc(r->man, ln, ppos, tok);
353161d06d6bSBaptiste Daroussin 	n = r->man->last;
353261d06d6bSBaptiste Daroussin 
353361d06d6bSBaptiste Daroussin 	for (sp = ep = buf->buf + pos; *sp != '\0'; sp = ep) {
353461d06d6bSBaptiste Daroussin 		while (*ep != '\0' && *ep != ' ')
353561d06d6bSBaptiste Daroussin 			ep++;
353661d06d6bSBaptiste Daroussin 		while (*ep == ' ')
353761d06d6bSBaptiste Daroussin 			*ep++ = '\0';
353861d06d6bSBaptiste Daroussin 		roff_word_alloc(r->man, ln, sp - buf->buf, sp);
353961d06d6bSBaptiste Daroussin 	}
354061d06d6bSBaptiste Daroussin 
354161d06d6bSBaptiste Daroussin 	n->flags |= NODE_LINE | NODE_VALID | NODE_ENDED;
354261d06d6bSBaptiste Daroussin 	r->man->last = n;
354361d06d6bSBaptiste Daroussin 	r->man->next = ROFF_NEXT_SIBLING;
354461d06d6bSBaptiste Daroussin 	return ROFF_IGN;
354561d06d6bSBaptiste Daroussin }
354661d06d6bSBaptiste Daroussin 
35477295610fSBaptiste Daroussin static int
roff_als(ROFF_ARGS)354861d06d6bSBaptiste Daroussin roff_als(ROFF_ARGS)
354961d06d6bSBaptiste Daroussin {
355061d06d6bSBaptiste Daroussin 	char		*oldn, *newn, *end, *value;
355161d06d6bSBaptiste Daroussin 	size_t		 oldsz, newsz, valsz;
355261d06d6bSBaptiste Daroussin 
355361d06d6bSBaptiste Daroussin 	newn = oldn = buf->buf + pos;
355461d06d6bSBaptiste Daroussin 	if (*newn == '\0')
355561d06d6bSBaptiste Daroussin 		return ROFF_IGN;
355661d06d6bSBaptiste Daroussin 
355761d06d6bSBaptiste Daroussin 	newsz = roff_getname(r, &oldn, ln, pos);
35587295610fSBaptiste Daroussin 	if (newn[newsz] == '\\' || newn[newsz] == '\t' || *oldn == '\0')
355961d06d6bSBaptiste Daroussin 		return ROFF_IGN;
356061d06d6bSBaptiste Daroussin 
356161d06d6bSBaptiste Daroussin 	end = oldn;
356261d06d6bSBaptiste Daroussin 	oldsz = roff_getname(r, &end, ln, oldn - buf->buf);
356361d06d6bSBaptiste Daroussin 	if (oldsz == 0)
356461d06d6bSBaptiste Daroussin 		return ROFF_IGN;
356561d06d6bSBaptiste Daroussin 
35667295610fSBaptiste Daroussin 	valsz = mandoc_asprintf(&value, ".%.*s \\$@\\\"\n",
356761d06d6bSBaptiste Daroussin 	    (int)oldsz, oldn);
356861d06d6bSBaptiste Daroussin 	roff_setstrn(&r->strtab, newn, newsz, value, valsz, 0);
356961d06d6bSBaptiste Daroussin 	roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
357061d06d6bSBaptiste Daroussin 	free(value);
357161d06d6bSBaptiste Daroussin 	return ROFF_IGN;
357261d06d6bSBaptiste Daroussin }
357361d06d6bSBaptiste Daroussin 
357445a5aec3SBaptiste Daroussin /*
357545a5aec3SBaptiste Daroussin  * The .break request only makes sense inside conditionals,
357645a5aec3SBaptiste Daroussin  * and that case is already handled in roff_cond_sub().
357745a5aec3SBaptiste Daroussin  */
357845a5aec3SBaptiste Daroussin static int
roff_break(ROFF_ARGS)357945a5aec3SBaptiste Daroussin roff_break(ROFF_ARGS)
358045a5aec3SBaptiste Daroussin {
358145a5aec3SBaptiste Daroussin 	mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, pos, "break");
358245a5aec3SBaptiste Daroussin 	return ROFF_IGN;
358345a5aec3SBaptiste Daroussin }
358445a5aec3SBaptiste Daroussin 
35857295610fSBaptiste Daroussin static int
roff_cc(ROFF_ARGS)358661d06d6bSBaptiste Daroussin roff_cc(ROFF_ARGS)
358761d06d6bSBaptiste Daroussin {
358861d06d6bSBaptiste Daroussin 	const char	*p;
358961d06d6bSBaptiste Daroussin 
359061d06d6bSBaptiste Daroussin 	p = buf->buf + pos;
359161d06d6bSBaptiste Daroussin 
359261d06d6bSBaptiste Daroussin 	if (*p == '\0' || (r->control = *p++) == '.')
359361d06d6bSBaptiste Daroussin 		r->control = '\0';
359461d06d6bSBaptiste Daroussin 
359561d06d6bSBaptiste Daroussin 	if (*p != '\0')
35967295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_EXCESS,
359761d06d6bSBaptiste Daroussin 		    ln, p - buf->buf, "cc ... %s", p);
359861d06d6bSBaptiste Daroussin 
359961d06d6bSBaptiste Daroussin 	return ROFF_IGN;
360061d06d6bSBaptiste Daroussin }
360161d06d6bSBaptiste Daroussin 
36027295610fSBaptiste Daroussin static int
roff_char(ROFF_ARGS)36037295610fSBaptiste Daroussin roff_char(ROFF_ARGS)
36047295610fSBaptiste Daroussin {
36057295610fSBaptiste Daroussin 	const char	*p, *kp, *vp;
36067295610fSBaptiste Daroussin 	size_t		 ksz, vsz;
36077295610fSBaptiste Daroussin 	int		 font;
36087295610fSBaptiste Daroussin 
36097295610fSBaptiste Daroussin 	/* Parse the character to be replaced. */
36107295610fSBaptiste Daroussin 
36117295610fSBaptiste Daroussin 	kp = buf->buf + pos;
36127295610fSBaptiste Daroussin 	p = kp + 1;
36137295610fSBaptiste Daroussin 	if (*kp == '\0' || (*kp == '\\' &&
36147295610fSBaptiste Daroussin 	     mandoc_escape(&p, NULL, NULL) != ESCAPE_SPECIAL) ||
36157295610fSBaptiste Daroussin 	    (*p != ' ' && *p != '\0')) {
36167295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_CHAR_ARG, ln, pos, "char %s", kp);
36177295610fSBaptiste Daroussin 		return ROFF_IGN;
36187295610fSBaptiste Daroussin 	}
36197295610fSBaptiste Daroussin 	ksz = p - kp;
36207295610fSBaptiste Daroussin 	while (*p == ' ')
36217295610fSBaptiste Daroussin 		p++;
36227295610fSBaptiste Daroussin 
36237295610fSBaptiste Daroussin 	/*
36247295610fSBaptiste Daroussin 	 * If the replacement string contains a font escape sequence,
36257295610fSBaptiste Daroussin 	 * we have to restore the font at the end.
36267295610fSBaptiste Daroussin 	 */
36277295610fSBaptiste Daroussin 
36287295610fSBaptiste Daroussin 	vp = p;
36297295610fSBaptiste Daroussin 	vsz = strlen(p);
36307295610fSBaptiste Daroussin 	font = 0;
36317295610fSBaptiste Daroussin 	while (*p != '\0') {
36327295610fSBaptiste Daroussin 		if (*p++ != '\\')
36337295610fSBaptiste Daroussin 			continue;
36347295610fSBaptiste Daroussin 		switch (mandoc_escape(&p, NULL, NULL)) {
36357295610fSBaptiste Daroussin 		case ESCAPE_FONT:
36367295610fSBaptiste Daroussin 		case ESCAPE_FONTROMAN:
36377295610fSBaptiste Daroussin 		case ESCAPE_FONTITALIC:
36387295610fSBaptiste Daroussin 		case ESCAPE_FONTBOLD:
36397295610fSBaptiste Daroussin 		case ESCAPE_FONTBI:
36406d38604fSBaptiste Daroussin 		case ESCAPE_FONTCR:
36416d38604fSBaptiste Daroussin 		case ESCAPE_FONTCB:
36426d38604fSBaptiste Daroussin 		case ESCAPE_FONTCI:
36437295610fSBaptiste Daroussin 		case ESCAPE_FONTPREV:
36447295610fSBaptiste Daroussin 			font++;
36457295610fSBaptiste Daroussin 			break;
36467295610fSBaptiste Daroussin 		default:
36477295610fSBaptiste Daroussin 			break;
36487295610fSBaptiste Daroussin 		}
36497295610fSBaptiste Daroussin 	}
36507295610fSBaptiste Daroussin 	if (font > 1)
36517295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_CHAR_FONT,
36527295610fSBaptiste Daroussin 		    ln, (int)(vp - buf->buf), "%s", vp);
36537295610fSBaptiste Daroussin 
36547295610fSBaptiste Daroussin 	/*
36557295610fSBaptiste Daroussin 	 * Approximate the effect of .char using the .tr tables.
36567295610fSBaptiste Daroussin 	 * XXX In groff, .char and .tr interact differently.
36577295610fSBaptiste Daroussin 	 */
36587295610fSBaptiste Daroussin 
36597295610fSBaptiste Daroussin 	if (ksz == 1) {
36607295610fSBaptiste Daroussin 		if (r->xtab == NULL)
36617295610fSBaptiste Daroussin 			r->xtab = mandoc_calloc(128, sizeof(*r->xtab));
36627295610fSBaptiste Daroussin 		assert((unsigned int)*kp < 128);
36637295610fSBaptiste Daroussin 		free(r->xtab[(int)*kp].p);
36647295610fSBaptiste Daroussin 		r->xtab[(int)*kp].sz = mandoc_asprintf(&r->xtab[(int)*kp].p,
36657295610fSBaptiste Daroussin 		    "%s%s", vp, font ? "\fP" : "");
36667295610fSBaptiste Daroussin 	} else {
36677295610fSBaptiste Daroussin 		roff_setstrn(&r->xmbtab, kp, ksz, vp, vsz, 0);
36687295610fSBaptiste Daroussin 		if (font)
36697295610fSBaptiste Daroussin 			roff_setstrn(&r->xmbtab, kp, ksz, "\\fP", 3, 1);
36707295610fSBaptiste Daroussin 	}
36717295610fSBaptiste Daroussin 	return ROFF_IGN;
36727295610fSBaptiste Daroussin }
36737295610fSBaptiste Daroussin 
36747295610fSBaptiste Daroussin static int
roff_ec(ROFF_ARGS)367561d06d6bSBaptiste Daroussin roff_ec(ROFF_ARGS)
367661d06d6bSBaptiste Daroussin {
367761d06d6bSBaptiste Daroussin 	const char	*p;
367861d06d6bSBaptiste Daroussin 
367961d06d6bSBaptiste Daroussin 	p = buf->buf + pos;
368061d06d6bSBaptiste Daroussin 	if (*p == '\0')
368161d06d6bSBaptiste Daroussin 		r->escape = '\\';
368261d06d6bSBaptiste Daroussin 	else {
368361d06d6bSBaptiste Daroussin 		r->escape = *p;
368461d06d6bSBaptiste Daroussin 		if (*++p != '\0')
36857295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_ARG_EXCESS, ln,
36867295610fSBaptiste Daroussin 			    (int)(p - buf->buf), "ec ... %s", p);
368761d06d6bSBaptiste Daroussin 	}
368861d06d6bSBaptiste Daroussin 	return ROFF_IGN;
368961d06d6bSBaptiste Daroussin }
369061d06d6bSBaptiste Daroussin 
36917295610fSBaptiste Daroussin static int
roff_eo(ROFF_ARGS)369261d06d6bSBaptiste Daroussin roff_eo(ROFF_ARGS)
369361d06d6bSBaptiste Daroussin {
369461d06d6bSBaptiste Daroussin 	r->escape = '\0';
369561d06d6bSBaptiste Daroussin 	if (buf->buf[pos] != '\0')
36967295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_SKIP,
369761d06d6bSBaptiste Daroussin 		    ln, pos, "eo %s", buf->buf + pos);
369861d06d6bSBaptiste Daroussin 	return ROFF_IGN;
369961d06d6bSBaptiste Daroussin }
370061d06d6bSBaptiste Daroussin 
37017295610fSBaptiste Daroussin static int
roff_mc(ROFF_ARGS)3702*c1c95addSBrooks Davis roff_mc(ROFF_ARGS)
3703*c1c95addSBrooks Davis {
3704*c1c95addSBrooks Davis 	struct roff_node	*n;
3705*c1c95addSBrooks Davis 	char			*cp;
3706*c1c95addSBrooks Davis 
3707*c1c95addSBrooks Davis 	/* Parse the first argument. */
3708*c1c95addSBrooks Davis 
3709*c1c95addSBrooks Davis 	cp = buf->buf + pos;
3710*c1c95addSBrooks Davis 	if (*cp != '\0')
3711*c1c95addSBrooks Davis 		cp++;
3712*c1c95addSBrooks Davis 	if (buf->buf[pos] == '\\') {
3713*c1c95addSBrooks Davis 		switch (mandoc_escape((const char **)&cp, NULL, NULL)) {
3714*c1c95addSBrooks Davis 		case ESCAPE_SPECIAL:
3715*c1c95addSBrooks Davis 		case ESCAPE_UNICODE:
3716*c1c95addSBrooks Davis 		case ESCAPE_NUMBERED:
3717*c1c95addSBrooks Davis 			break;
3718*c1c95addSBrooks Davis 		default:
3719*c1c95addSBrooks Davis 			*cp = '\0';
3720*c1c95addSBrooks Davis 			mandoc_msg(MANDOCERR_MC_ESC, ln, pos,
3721*c1c95addSBrooks Davis 			    "mc %s", buf->buf + pos);
3722*c1c95addSBrooks Davis 			buf->buf[pos] = '\0';
3723*c1c95addSBrooks Davis 			break;
3724*c1c95addSBrooks Davis 		}
3725*c1c95addSBrooks Davis 	}
3726*c1c95addSBrooks Davis 
3727*c1c95addSBrooks Davis 	/* Ignore additional arguments. */
3728*c1c95addSBrooks Davis 
3729*c1c95addSBrooks Davis 	while (*cp == ' ')
3730*c1c95addSBrooks Davis 		*cp++ = '\0';
3731*c1c95addSBrooks Davis 	if (*cp != '\0') {
3732*c1c95addSBrooks Davis 		mandoc_msg(MANDOCERR_MC_DIST, ln, (int)(cp - buf->buf),
3733*c1c95addSBrooks Davis 		    "mc ... %s", cp);
3734*c1c95addSBrooks Davis 		*cp = '\0';
3735*c1c95addSBrooks Davis 	}
3736*c1c95addSBrooks Davis 
3737*c1c95addSBrooks Davis 	/* Create the .mc node. */
3738*c1c95addSBrooks Davis 
3739*c1c95addSBrooks Davis 	roff_elem_alloc(r->man, ln, ppos, tok);
3740*c1c95addSBrooks Davis 	n = r->man->last;
3741*c1c95addSBrooks Davis 	if (buf->buf[pos] != '\0')
3742*c1c95addSBrooks Davis 		roff_word_alloc(r->man, ln, pos, buf->buf + pos);
3743*c1c95addSBrooks Davis 	n->flags |= NODE_LINE | NODE_VALID | NODE_ENDED;
3744*c1c95addSBrooks Davis 	r->man->last = n;
3745*c1c95addSBrooks Davis 	r->man->next = ROFF_NEXT_SIBLING;
3746*c1c95addSBrooks Davis 	return ROFF_IGN;
3747*c1c95addSBrooks Davis }
3748*c1c95addSBrooks Davis 
3749*c1c95addSBrooks Davis static int
roff_nop(ROFF_ARGS)37507295610fSBaptiste Daroussin roff_nop(ROFF_ARGS)
37517295610fSBaptiste Daroussin {
37527295610fSBaptiste Daroussin 	while (buf->buf[pos] == ' ')
37537295610fSBaptiste Daroussin 		pos++;
37547295610fSBaptiste Daroussin 	*offs = pos;
37557295610fSBaptiste Daroussin 	return ROFF_RERUN;
37567295610fSBaptiste Daroussin }
37577295610fSBaptiste Daroussin 
37587295610fSBaptiste Daroussin static int
roff_tr(ROFF_ARGS)375961d06d6bSBaptiste Daroussin roff_tr(ROFF_ARGS)
376061d06d6bSBaptiste Daroussin {
376161d06d6bSBaptiste Daroussin 	const char	*p, *first, *second;
376261d06d6bSBaptiste Daroussin 	size_t		 fsz, ssz;
376361d06d6bSBaptiste Daroussin 
376461d06d6bSBaptiste Daroussin 	p = buf->buf + pos;
376561d06d6bSBaptiste Daroussin 
376661d06d6bSBaptiste Daroussin 	if (*p == '\0') {
37677295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_REQ_EMPTY, ln, ppos, "tr");
376861d06d6bSBaptiste Daroussin 		return ROFF_IGN;
376961d06d6bSBaptiste Daroussin 	}
377061d06d6bSBaptiste Daroussin 
377161d06d6bSBaptiste Daroussin 	while (*p != '\0') {
377261d06d6bSBaptiste Daroussin 		fsz = ssz = 1;
377361d06d6bSBaptiste Daroussin 
377461d06d6bSBaptiste Daroussin 		first = p++;
377561d06d6bSBaptiste Daroussin 		if (*first == '\\') {
3776*c1c95addSBrooks Davis 			if (mandoc_escape(&p, NULL, NULL) == ESCAPE_ERROR)
377761d06d6bSBaptiste Daroussin 				return ROFF_IGN;
377861d06d6bSBaptiste Daroussin 			fsz = (size_t)(p - first);
377961d06d6bSBaptiste Daroussin 		}
378061d06d6bSBaptiste Daroussin 
378161d06d6bSBaptiste Daroussin 		second = p++;
378261d06d6bSBaptiste Daroussin 		if (*second == '\\') {
3783*c1c95addSBrooks Davis 			if (mandoc_escape(&p, NULL, NULL) == ESCAPE_ERROR)
378461d06d6bSBaptiste Daroussin 				return ROFF_IGN;
378561d06d6bSBaptiste Daroussin 			ssz = (size_t)(p - second);
378661d06d6bSBaptiste Daroussin 		} else if (*second == '\0') {
37877295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_TR_ODD, ln,
37887295610fSBaptiste Daroussin 			    (int)(first - buf->buf), "tr %s", first);
378961d06d6bSBaptiste Daroussin 			second = " ";
379061d06d6bSBaptiste Daroussin 			p--;
379161d06d6bSBaptiste Daroussin 		}
379261d06d6bSBaptiste Daroussin 
379361d06d6bSBaptiste Daroussin 		if (fsz > 1) {
379461d06d6bSBaptiste Daroussin 			roff_setstrn(&r->xmbtab, first, fsz,
379561d06d6bSBaptiste Daroussin 			    second, ssz, 0);
379661d06d6bSBaptiste Daroussin 			continue;
379761d06d6bSBaptiste Daroussin 		}
379861d06d6bSBaptiste Daroussin 
379961d06d6bSBaptiste Daroussin 		if (r->xtab == NULL)
380061d06d6bSBaptiste Daroussin 			r->xtab = mandoc_calloc(128,
380161d06d6bSBaptiste Daroussin 			    sizeof(struct roffstr));
380261d06d6bSBaptiste Daroussin 
380361d06d6bSBaptiste Daroussin 		free(r->xtab[(int)*first].p);
380461d06d6bSBaptiste Daroussin 		r->xtab[(int)*first].p = mandoc_strndup(second, ssz);
380561d06d6bSBaptiste Daroussin 		r->xtab[(int)*first].sz = ssz;
380661d06d6bSBaptiste Daroussin 	}
380761d06d6bSBaptiste Daroussin 
380861d06d6bSBaptiste Daroussin 	return ROFF_IGN;
380961d06d6bSBaptiste Daroussin }
381061d06d6bSBaptiste Daroussin 
38117295610fSBaptiste Daroussin /*
38127295610fSBaptiste Daroussin  * Implementation of the .return request.
38137295610fSBaptiste Daroussin  * There is no need to call roff_userret() from here.
38147295610fSBaptiste Daroussin  * The read module will call that after rewinding the reader stack
38157295610fSBaptiste Daroussin  * to the place from where the current macro was called.
38167295610fSBaptiste Daroussin  */
38177295610fSBaptiste Daroussin static int
roff_return(ROFF_ARGS)38187295610fSBaptiste Daroussin roff_return(ROFF_ARGS)
38197295610fSBaptiste Daroussin {
38207295610fSBaptiste Daroussin 	if (r->mstackpos >= 0)
38217295610fSBaptiste Daroussin 		return ROFF_IGN | ROFF_USERRET;
38227295610fSBaptiste Daroussin 
38237295610fSBaptiste Daroussin 	mandoc_msg(MANDOCERR_REQ_NOMAC, ln, ppos, "return");
38247295610fSBaptiste Daroussin 	return ROFF_IGN;
38257295610fSBaptiste Daroussin }
38267295610fSBaptiste Daroussin 
38277295610fSBaptiste Daroussin static int
roff_rn(ROFF_ARGS)382861d06d6bSBaptiste Daroussin roff_rn(ROFF_ARGS)
382961d06d6bSBaptiste Daroussin {
383061d06d6bSBaptiste Daroussin 	const char	*value;
383161d06d6bSBaptiste Daroussin 	char		*oldn, *newn, *end;
383261d06d6bSBaptiste Daroussin 	size_t		 oldsz, newsz;
383361d06d6bSBaptiste Daroussin 	int		 deftype;
383461d06d6bSBaptiste Daroussin 
383561d06d6bSBaptiste Daroussin 	oldn = newn = buf->buf + pos;
383661d06d6bSBaptiste Daroussin 	if (*oldn == '\0')
383761d06d6bSBaptiste Daroussin 		return ROFF_IGN;
383861d06d6bSBaptiste Daroussin 
383961d06d6bSBaptiste Daroussin 	oldsz = roff_getname(r, &newn, ln, pos);
38407295610fSBaptiste Daroussin 	if (oldn[oldsz] == '\\' || oldn[oldsz] == '\t' || *newn == '\0')
384161d06d6bSBaptiste Daroussin 		return ROFF_IGN;
384261d06d6bSBaptiste Daroussin 
384361d06d6bSBaptiste Daroussin 	end = newn;
384461d06d6bSBaptiste Daroussin 	newsz = roff_getname(r, &end, ln, newn - buf->buf);
384561d06d6bSBaptiste Daroussin 	if (newsz == 0)
384661d06d6bSBaptiste Daroussin 		return ROFF_IGN;
384761d06d6bSBaptiste Daroussin 
384861d06d6bSBaptiste Daroussin 	deftype = ROFFDEF_ANY;
384961d06d6bSBaptiste Daroussin 	value = roff_getstrn(r, oldn, oldsz, &deftype);
385061d06d6bSBaptiste Daroussin 	switch (deftype) {
385161d06d6bSBaptiste Daroussin 	case ROFFDEF_USER:
385261d06d6bSBaptiste Daroussin 		roff_setstrn(&r->strtab, newn, newsz, value, strlen(value), 0);
385361d06d6bSBaptiste Daroussin 		roff_setstrn(&r->strtab, oldn, oldsz, NULL, 0, 0);
385461d06d6bSBaptiste Daroussin 		roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
385561d06d6bSBaptiste Daroussin 		break;
385661d06d6bSBaptiste Daroussin 	case ROFFDEF_PRE:
385761d06d6bSBaptiste Daroussin 		roff_setstrn(&r->strtab, newn, newsz, value, strlen(value), 0);
385861d06d6bSBaptiste Daroussin 		roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
385961d06d6bSBaptiste Daroussin 		break;
386061d06d6bSBaptiste Daroussin 	case ROFFDEF_REN:
386161d06d6bSBaptiste Daroussin 		roff_setstrn(&r->rentab, newn, newsz, value, strlen(value), 0);
386261d06d6bSBaptiste Daroussin 		roff_setstrn(&r->rentab, oldn, oldsz, NULL, 0, 0);
386361d06d6bSBaptiste Daroussin 		roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0);
386461d06d6bSBaptiste Daroussin 		break;
386561d06d6bSBaptiste Daroussin 	case ROFFDEF_STD:
386661d06d6bSBaptiste Daroussin 		roff_setstrn(&r->rentab, newn, newsz, oldn, oldsz, 0);
386761d06d6bSBaptiste Daroussin 		roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0);
386861d06d6bSBaptiste Daroussin 		break;
386961d06d6bSBaptiste Daroussin 	default:
387061d06d6bSBaptiste Daroussin 		roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0);
387161d06d6bSBaptiste Daroussin 		roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
387261d06d6bSBaptiste Daroussin 		break;
387361d06d6bSBaptiste Daroussin 	}
387461d06d6bSBaptiste Daroussin 	return ROFF_IGN;
387561d06d6bSBaptiste Daroussin }
387661d06d6bSBaptiste Daroussin 
38777295610fSBaptiste Daroussin static int
roff_shift(ROFF_ARGS)38787295610fSBaptiste Daroussin roff_shift(ROFF_ARGS)
38797295610fSBaptiste Daroussin {
38807295610fSBaptiste Daroussin 	struct mctx	*ctx;
3881*c1c95addSBrooks Davis 	int		 argpos, levels, i;
38827295610fSBaptiste Daroussin 
3883*c1c95addSBrooks Davis 	argpos = pos;
38847295610fSBaptiste Daroussin 	levels = 1;
38857295610fSBaptiste Daroussin 	if (buf->buf[pos] != '\0' &&
38867295610fSBaptiste Daroussin 	    roff_evalnum(r, ln, buf->buf, &pos, &levels, 0) == 0) {
38877295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_CE_NONUM,
38887295610fSBaptiste Daroussin 		    ln, pos, "shift %s", buf->buf + pos);
38897295610fSBaptiste Daroussin 		levels = 1;
38907295610fSBaptiste Daroussin 	}
38917295610fSBaptiste Daroussin 	if (r->mstackpos < 0) {
38927295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_REQ_NOMAC, ln, ppos, "shift");
38937295610fSBaptiste Daroussin 		return ROFF_IGN;
38947295610fSBaptiste Daroussin 	}
38957295610fSBaptiste Daroussin 	ctx = r->mstack + r->mstackpos;
38967295610fSBaptiste Daroussin 	if (levels > ctx->argc) {
38977295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_SHIFT,
3898*c1c95addSBrooks Davis 		    ln, argpos, "%d, but max is %d", levels, ctx->argc);
38997295610fSBaptiste Daroussin 		levels = ctx->argc;
39007295610fSBaptiste Daroussin 	}
3901*c1c95addSBrooks Davis 	if (levels < 0) {
3902*c1c95addSBrooks Davis 		mandoc_msg(MANDOCERR_ARG_NEG, ln, argpos, "shift %d", levels);
3903*c1c95addSBrooks Davis 		levels = 0;
3904*c1c95addSBrooks Davis 	}
39057295610fSBaptiste Daroussin 	if (levels == 0)
39067295610fSBaptiste Daroussin 		return ROFF_IGN;
39077295610fSBaptiste Daroussin 	for (i = 0; i < levels; i++)
39087295610fSBaptiste Daroussin 		free(ctx->argv[i]);
39097295610fSBaptiste Daroussin 	ctx->argc -= levels;
39107295610fSBaptiste Daroussin 	for (i = 0; i < ctx->argc; i++)
39117295610fSBaptiste Daroussin 		ctx->argv[i] = ctx->argv[i + levels];
39127295610fSBaptiste Daroussin 	return ROFF_IGN;
39137295610fSBaptiste Daroussin }
39147295610fSBaptiste Daroussin 
39157295610fSBaptiste Daroussin static int
roff_so(ROFF_ARGS)391661d06d6bSBaptiste Daroussin roff_so(ROFF_ARGS)
391761d06d6bSBaptiste Daroussin {
391861d06d6bSBaptiste Daroussin 	char *name, *cp;
391961d06d6bSBaptiste Daroussin 
392061d06d6bSBaptiste Daroussin 	name = buf->buf + pos;
39217295610fSBaptiste Daroussin 	mandoc_msg(MANDOCERR_SO, ln, ppos, "so %s", name);
392261d06d6bSBaptiste Daroussin 
392361d06d6bSBaptiste Daroussin 	/*
392461d06d6bSBaptiste Daroussin 	 * Handle `so'.  Be EXTREMELY careful, as we shouldn't be
392561d06d6bSBaptiste Daroussin 	 * opening anything that's not in our cwd or anything beneath
392661d06d6bSBaptiste Daroussin 	 * it.  Thus, explicitly disallow traversing up the file-system
392761d06d6bSBaptiste Daroussin 	 * or using absolute paths.
392861d06d6bSBaptiste Daroussin 	 */
392961d06d6bSBaptiste Daroussin 
393061d06d6bSBaptiste Daroussin 	if (*name == '/' || strstr(name, "../") || strstr(name, "/..")) {
39317295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_SO_PATH, ln, ppos, ".so %s", name);
393261d06d6bSBaptiste Daroussin 		buf->sz = mandoc_asprintf(&cp,
393361d06d6bSBaptiste Daroussin 		    ".sp\nSee the file %s.\n.sp", name) + 1;
393461d06d6bSBaptiste Daroussin 		free(buf->buf);
393561d06d6bSBaptiste Daroussin 		buf->buf = cp;
393661d06d6bSBaptiste Daroussin 		*offs = 0;
393761d06d6bSBaptiste Daroussin 		return ROFF_REPARSE;
393861d06d6bSBaptiste Daroussin 	}
393961d06d6bSBaptiste Daroussin 
394061d06d6bSBaptiste Daroussin 	*offs = pos;
394161d06d6bSBaptiste Daroussin 	return ROFF_SO;
394261d06d6bSBaptiste Daroussin }
394361d06d6bSBaptiste Daroussin 
394461d06d6bSBaptiste Daroussin /* --- user defined strings and macros ------------------------------------ */
394561d06d6bSBaptiste Daroussin 
39467295610fSBaptiste Daroussin static int
roff_userdef(ROFF_ARGS)394761d06d6bSBaptiste Daroussin roff_userdef(ROFF_ARGS)
394861d06d6bSBaptiste Daroussin {
39497295610fSBaptiste Daroussin 	struct mctx	 *ctx;
39507295610fSBaptiste Daroussin 	char		 *arg, *ap, *dst, *src;
39517295610fSBaptiste Daroussin 	size_t		  sz;
395261d06d6bSBaptiste Daroussin 
395345a5aec3SBaptiste Daroussin 	/* If the macro is empty, ignore it altogether. */
395445a5aec3SBaptiste Daroussin 
395545a5aec3SBaptiste Daroussin 	if (*r->current_string == '\0')
395645a5aec3SBaptiste Daroussin 		return ROFF_IGN;
395745a5aec3SBaptiste Daroussin 
39587295610fSBaptiste Daroussin 	/* Initialize a new macro stack context. */
395961d06d6bSBaptiste Daroussin 
39607295610fSBaptiste Daroussin 	if (++r->mstackpos == r->mstacksz) {
39617295610fSBaptiste Daroussin 		r->mstack = mandoc_recallocarray(r->mstack,
39627295610fSBaptiste Daroussin 		    r->mstacksz, r->mstacksz + 8, sizeof(*r->mstack));
39637295610fSBaptiste Daroussin 		r->mstacksz += 8;
396461d06d6bSBaptiste Daroussin 	}
39657295610fSBaptiste Daroussin 	ctx = r->mstack + r->mstackpos;
39667295610fSBaptiste Daroussin 	ctx->argc = 0;
39677295610fSBaptiste Daroussin 
39687295610fSBaptiste Daroussin 	/*
39697295610fSBaptiste Daroussin 	 * Collect pointers to macro argument strings,
39707295610fSBaptiste Daroussin 	 * NUL-terminating them and escaping quotes.
39717295610fSBaptiste Daroussin 	 */
39727295610fSBaptiste Daroussin 
39737295610fSBaptiste Daroussin 	src = buf->buf + pos;
39747295610fSBaptiste Daroussin 	while (*src != '\0') {
39757295610fSBaptiste Daroussin 		if (ctx->argc == ctx->argsz) {
39767295610fSBaptiste Daroussin 			ctx->argsz += 8;
39777295610fSBaptiste Daroussin 			ctx->argv = mandoc_reallocarray(ctx->argv,
39787295610fSBaptiste Daroussin 			    ctx->argsz, sizeof(*ctx->argv));
397961d06d6bSBaptiste Daroussin 		}
39807295610fSBaptiste Daroussin 		arg = roff_getarg(r, &src, ln, &pos);
39817295610fSBaptiste Daroussin 		sz = 1;  /* For the terminating NUL. */
39827295610fSBaptiste Daroussin 		for (ap = arg; *ap != '\0'; ap++)
39837295610fSBaptiste Daroussin 			sz += *ap == '"' ? 4 : 1;
39847295610fSBaptiste Daroussin 		ctx->argv[ctx->argc++] = dst = mandoc_malloc(sz);
39857295610fSBaptiste Daroussin 		for (ap = arg; *ap != '\0'; ap++) {
398661d06d6bSBaptiste Daroussin 			if (*ap == '"') {
39877295610fSBaptiste Daroussin 				memcpy(dst, "\\(dq", 4);
39887295610fSBaptiste Daroussin 				dst += 4;
398961d06d6bSBaptiste Daroussin 			} else
39907295610fSBaptiste Daroussin 				*dst++ = *ap;
399161d06d6bSBaptiste Daroussin 		}
39927295610fSBaptiste Daroussin 		*dst = '\0';
39937295610fSBaptiste Daroussin 		free(arg);
399461d06d6bSBaptiste Daroussin 	}
399561d06d6bSBaptiste Daroussin 
39967295610fSBaptiste Daroussin 	/* Replace the macro invocation by the macro definition. */
399761d06d6bSBaptiste Daroussin 
399861d06d6bSBaptiste Daroussin 	free(buf->buf);
39997295610fSBaptiste Daroussin 	buf->buf = mandoc_strdup(r->current_string);
40007295610fSBaptiste Daroussin 	buf->sz = strlen(buf->buf) + 1;
400161d06d6bSBaptiste Daroussin 	*offs = 0;
400261d06d6bSBaptiste Daroussin 
400345a5aec3SBaptiste Daroussin 	return buf->buf[buf->sz - 2] == '\n' ?
40047295610fSBaptiste Daroussin 	    ROFF_REPARSE | ROFF_USERCALL : ROFF_IGN | ROFF_APPEND;
400561d06d6bSBaptiste Daroussin }
400661d06d6bSBaptiste Daroussin 
400761d06d6bSBaptiste Daroussin /*
400861d06d6bSBaptiste Daroussin  * Calling a high-level macro that was renamed with .rn.
400961d06d6bSBaptiste Daroussin  * r->current_string has already been set up by roff_parse().
401061d06d6bSBaptiste Daroussin  */
40117295610fSBaptiste Daroussin static int
roff_renamed(ROFF_ARGS)401261d06d6bSBaptiste Daroussin roff_renamed(ROFF_ARGS)
401361d06d6bSBaptiste Daroussin {
401461d06d6bSBaptiste Daroussin 	char	*nbuf;
401561d06d6bSBaptiste Daroussin 
401661d06d6bSBaptiste Daroussin 	buf->sz = mandoc_asprintf(&nbuf, ".%s%s%s", r->current_string,
401761d06d6bSBaptiste Daroussin 	    buf->buf[pos] == '\0' ? "" : " ", buf->buf + pos) + 1;
401861d06d6bSBaptiste Daroussin 	free(buf->buf);
401961d06d6bSBaptiste Daroussin 	buf->buf = nbuf;
402061d06d6bSBaptiste Daroussin 	*offs = 0;
402161d06d6bSBaptiste Daroussin 	return ROFF_CONT;
402261d06d6bSBaptiste Daroussin }
402361d06d6bSBaptiste Daroussin 
40247295610fSBaptiste Daroussin /*
40257295610fSBaptiste Daroussin  * Measure the length in bytes of the roff identifier at *cpp
40267295610fSBaptiste Daroussin  * and advance the pointer to the next word.
40277295610fSBaptiste Daroussin  */
402861d06d6bSBaptiste Daroussin static size_t
roff_getname(struct roff * r,char ** cpp,int ln,int pos)402961d06d6bSBaptiste Daroussin roff_getname(struct roff *r, char **cpp, int ln, int pos)
403061d06d6bSBaptiste Daroussin {
403161d06d6bSBaptiste Daroussin 	char	 *name, *cp;
4032*c1c95addSBrooks Davis 	int	  namesz, inam, iend;
403361d06d6bSBaptiste Daroussin 
403461d06d6bSBaptiste Daroussin 	name = *cpp;
40357295610fSBaptiste Daroussin 	if (*name == '\0')
403661d06d6bSBaptiste Daroussin 		return 0;
403761d06d6bSBaptiste Daroussin 
40387295610fSBaptiste Daroussin 	/* Advance cp to the byte after the end of the name. */
40397295610fSBaptiste Daroussin 
4040*c1c95addSBrooks Davis 	cp = name;
4041*c1c95addSBrooks Davis 	namesz = 0;
4042*c1c95addSBrooks Davis 	for (;;) {
40437295610fSBaptiste Daroussin 		if (*cp == '\0')
40447295610fSBaptiste Daroussin 			break;
40457295610fSBaptiste Daroussin 		if (*cp == ' ' || *cp == '\t') {
40467295610fSBaptiste Daroussin 			cp++;
404761d06d6bSBaptiste Daroussin 			break;
404861d06d6bSBaptiste Daroussin 		}
4049*c1c95addSBrooks Davis 		if (*cp != '\\') {
4050*c1c95addSBrooks Davis 			if (name + namesz < cp) {
4051*c1c95addSBrooks Davis 				name[namesz] = *cp;
4052*c1c95addSBrooks Davis 				*cp = ' ';
4053*c1c95addSBrooks Davis 			}
4054*c1c95addSBrooks Davis 			namesz++;
4055*c1c95addSBrooks Davis 			cp++;
405661d06d6bSBaptiste Daroussin 			continue;
4057*c1c95addSBrooks Davis 		}
40587295610fSBaptiste Daroussin 		if (cp[1] == '{' || cp[1] == '}')
405961d06d6bSBaptiste Daroussin 			break;
4060*c1c95addSBrooks Davis 		if (roff_escape(cp, 0, 0, NULL, &inam,
4061*c1c95addSBrooks Davis 		    NULL, NULL, &iend) != ESCAPE_UNDEF) {
40627295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_NAMESC, ln, pos,
4063*c1c95addSBrooks Davis 			    "%.*s%.*s", namesz, name, iend, cp);
4064*c1c95addSBrooks Davis 			cp += iend;
406561d06d6bSBaptiste Daroussin 			break;
406661d06d6bSBaptiste Daroussin 		}
406761d06d6bSBaptiste Daroussin 
4068*c1c95addSBrooks Davis 		/*
4069*c1c95addSBrooks Davis 		 * In an identifier, \\, \., \G and so on
4070*c1c95addSBrooks Davis 		 * are reduced to \, ., G and so on,
4071*c1c95addSBrooks Davis 		 * vaguely similar to copy mode.
4072*c1c95addSBrooks Davis 		 */
4073*c1c95addSBrooks Davis 
4074*c1c95addSBrooks Davis 		name[namesz++] = cp[inam];
4075*c1c95addSBrooks Davis 		while (iend--) {
4076*c1c95addSBrooks Davis 			if (cp >= name + namesz)
4077*c1c95addSBrooks Davis 				*cp = ' ';
4078*c1c95addSBrooks Davis 			cp++;
4079*c1c95addSBrooks Davis 		}
4080*c1c95addSBrooks Davis 	}
4081*c1c95addSBrooks Davis 
408261d06d6bSBaptiste Daroussin 	/* Read past spaces. */
40837295610fSBaptiste Daroussin 
40847295610fSBaptiste Daroussin 	while (*cp == ' ')
408561d06d6bSBaptiste Daroussin 		cp++;
408661d06d6bSBaptiste Daroussin 
408761d06d6bSBaptiste Daroussin 	*cpp = cp;
408861d06d6bSBaptiste Daroussin 	return namesz;
408961d06d6bSBaptiste Daroussin }
409061d06d6bSBaptiste Daroussin 
409161d06d6bSBaptiste Daroussin /*
409261d06d6bSBaptiste Daroussin  * Store *string into the user-defined string called *name.
409361d06d6bSBaptiste Daroussin  * To clear an existing entry, call with (*r, *name, NULL, 0).
409461d06d6bSBaptiste Daroussin  * append == 0: replace mode
409561d06d6bSBaptiste Daroussin  * append == 1: single-line append mode
409661d06d6bSBaptiste Daroussin  * append == 2: multiline append mode, append '\n' after each call
409761d06d6bSBaptiste Daroussin  */
409861d06d6bSBaptiste Daroussin static void
roff_setstr(struct roff * r,const char * name,const char * string,int append)409961d06d6bSBaptiste Daroussin roff_setstr(struct roff *r, const char *name, const char *string,
410061d06d6bSBaptiste Daroussin 	int append)
410161d06d6bSBaptiste Daroussin {
410261d06d6bSBaptiste Daroussin 	size_t	 namesz;
410361d06d6bSBaptiste Daroussin 
410461d06d6bSBaptiste Daroussin 	namesz = strlen(name);
410561d06d6bSBaptiste Daroussin 	roff_setstrn(&r->strtab, name, namesz, string,
410661d06d6bSBaptiste Daroussin 	    string ? strlen(string) : 0, append);
410761d06d6bSBaptiste Daroussin 	roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
410861d06d6bSBaptiste Daroussin }
410961d06d6bSBaptiste Daroussin 
411061d06d6bSBaptiste Daroussin static void
roff_setstrn(struct roffkv ** r,const char * name,size_t namesz,const char * string,size_t stringsz,int append)411161d06d6bSBaptiste Daroussin roff_setstrn(struct roffkv **r, const char *name, size_t namesz,
411261d06d6bSBaptiste Daroussin 		const char *string, size_t stringsz, int append)
411361d06d6bSBaptiste Daroussin {
411461d06d6bSBaptiste Daroussin 	struct roffkv	*n;
411561d06d6bSBaptiste Daroussin 	char		*c;
411661d06d6bSBaptiste Daroussin 	int		 i;
411761d06d6bSBaptiste Daroussin 	size_t		 oldch, newch;
411861d06d6bSBaptiste Daroussin 
411961d06d6bSBaptiste Daroussin 	/* Search for an existing string with the same name. */
412061d06d6bSBaptiste Daroussin 	n = *r;
412161d06d6bSBaptiste Daroussin 
412261d06d6bSBaptiste Daroussin 	while (n && (namesz != n->key.sz ||
412361d06d6bSBaptiste Daroussin 			strncmp(n->key.p, name, namesz)))
412461d06d6bSBaptiste Daroussin 		n = n->next;
412561d06d6bSBaptiste Daroussin 
412661d06d6bSBaptiste Daroussin 	if (NULL == n) {
412761d06d6bSBaptiste Daroussin 		/* Create a new string table entry. */
412861d06d6bSBaptiste Daroussin 		n = mandoc_malloc(sizeof(struct roffkv));
412961d06d6bSBaptiste Daroussin 		n->key.p = mandoc_strndup(name, namesz);
413061d06d6bSBaptiste Daroussin 		n->key.sz = namesz;
413161d06d6bSBaptiste Daroussin 		n->val.p = NULL;
413261d06d6bSBaptiste Daroussin 		n->val.sz = 0;
413361d06d6bSBaptiste Daroussin 		n->next = *r;
413461d06d6bSBaptiste Daroussin 		*r = n;
413561d06d6bSBaptiste Daroussin 	} else if (0 == append) {
413661d06d6bSBaptiste Daroussin 		free(n->val.p);
413761d06d6bSBaptiste Daroussin 		n->val.p = NULL;
413861d06d6bSBaptiste Daroussin 		n->val.sz = 0;
413961d06d6bSBaptiste Daroussin 	}
414061d06d6bSBaptiste Daroussin 
414161d06d6bSBaptiste Daroussin 	if (NULL == string)
414261d06d6bSBaptiste Daroussin 		return;
414361d06d6bSBaptiste Daroussin 
414461d06d6bSBaptiste Daroussin 	/*
414561d06d6bSBaptiste Daroussin 	 * One additional byte for the '\n' in multiline mode,
414661d06d6bSBaptiste Daroussin 	 * and one for the terminating '\0'.
414761d06d6bSBaptiste Daroussin 	 */
414861d06d6bSBaptiste Daroussin 	newch = stringsz + (1 < append ? 2u : 1u);
414961d06d6bSBaptiste Daroussin 
415061d06d6bSBaptiste Daroussin 	if (NULL == n->val.p) {
415161d06d6bSBaptiste Daroussin 		n->val.p = mandoc_malloc(newch);
415261d06d6bSBaptiste Daroussin 		*n->val.p = '\0';
415361d06d6bSBaptiste Daroussin 		oldch = 0;
415461d06d6bSBaptiste Daroussin 	} else {
415561d06d6bSBaptiste Daroussin 		oldch = n->val.sz;
415661d06d6bSBaptiste Daroussin 		n->val.p = mandoc_realloc(n->val.p, oldch + newch);
415761d06d6bSBaptiste Daroussin 	}
415861d06d6bSBaptiste Daroussin 
415961d06d6bSBaptiste Daroussin 	/* Skip existing content in the destination buffer. */
416061d06d6bSBaptiste Daroussin 	c = n->val.p + (int)oldch;
416161d06d6bSBaptiste Daroussin 
416261d06d6bSBaptiste Daroussin 	/* Append new content to the destination buffer. */
416361d06d6bSBaptiste Daroussin 	i = 0;
416461d06d6bSBaptiste Daroussin 	while (i < (int)stringsz) {
416561d06d6bSBaptiste Daroussin 		/*
416661d06d6bSBaptiste Daroussin 		 * Rudimentary roff copy mode:
416761d06d6bSBaptiste Daroussin 		 * Handle escaped backslashes.
416861d06d6bSBaptiste Daroussin 		 */
416961d06d6bSBaptiste Daroussin 		if ('\\' == string[i] && '\\' == string[i + 1])
417061d06d6bSBaptiste Daroussin 			i++;
417161d06d6bSBaptiste Daroussin 		*c++ = string[i++];
417261d06d6bSBaptiste Daroussin 	}
417361d06d6bSBaptiste Daroussin 
417461d06d6bSBaptiste Daroussin 	/* Append terminating bytes. */
417561d06d6bSBaptiste Daroussin 	if (1 < append)
417661d06d6bSBaptiste Daroussin 		*c++ = '\n';
417761d06d6bSBaptiste Daroussin 
417861d06d6bSBaptiste Daroussin 	*c = '\0';
417961d06d6bSBaptiste Daroussin 	n->val.sz = (int)(c - n->val.p);
418061d06d6bSBaptiste Daroussin }
418161d06d6bSBaptiste Daroussin 
418261d06d6bSBaptiste Daroussin static const char *
roff_getstrn(struct roff * r,const char * name,size_t len,int * deftype)418361d06d6bSBaptiste Daroussin roff_getstrn(struct roff *r, const char *name, size_t len,
418461d06d6bSBaptiste Daroussin     int *deftype)
418561d06d6bSBaptiste Daroussin {
418661d06d6bSBaptiste Daroussin 	const struct roffkv	*n;
418761d06d6bSBaptiste Daroussin 	int			 found, i;
418861d06d6bSBaptiste Daroussin 	enum roff_tok		 tok;
418961d06d6bSBaptiste Daroussin 
419061d06d6bSBaptiste Daroussin 	found = 0;
419161d06d6bSBaptiste Daroussin 	for (n = r->strtab; n != NULL; n = n->next) {
419261d06d6bSBaptiste Daroussin 		if (strncmp(name, n->key.p, len) != 0 ||
419361d06d6bSBaptiste Daroussin 		    n->key.p[len] != '\0' || n->val.p == NULL)
419461d06d6bSBaptiste Daroussin 			continue;
419561d06d6bSBaptiste Daroussin 		if (*deftype & ROFFDEF_USER) {
419661d06d6bSBaptiste Daroussin 			*deftype = ROFFDEF_USER;
419761d06d6bSBaptiste Daroussin 			return n->val.p;
419861d06d6bSBaptiste Daroussin 		} else {
419961d06d6bSBaptiste Daroussin 			found = 1;
420061d06d6bSBaptiste Daroussin 			break;
420161d06d6bSBaptiste Daroussin 		}
420261d06d6bSBaptiste Daroussin 	}
420361d06d6bSBaptiste Daroussin 	for (n = r->rentab; n != NULL; n = n->next) {
420461d06d6bSBaptiste Daroussin 		if (strncmp(name, n->key.p, len) != 0 ||
420561d06d6bSBaptiste Daroussin 		    n->key.p[len] != '\0' || n->val.p == NULL)
420661d06d6bSBaptiste Daroussin 			continue;
420761d06d6bSBaptiste Daroussin 		if (*deftype & ROFFDEF_REN) {
420861d06d6bSBaptiste Daroussin 			*deftype = ROFFDEF_REN;
420961d06d6bSBaptiste Daroussin 			return n->val.p;
421061d06d6bSBaptiste Daroussin 		} else {
421161d06d6bSBaptiste Daroussin 			found = 1;
421261d06d6bSBaptiste Daroussin 			break;
421361d06d6bSBaptiste Daroussin 		}
421461d06d6bSBaptiste Daroussin 	}
421561d06d6bSBaptiste Daroussin 	for (i = 0; i < PREDEFS_MAX; i++) {
421661d06d6bSBaptiste Daroussin 		if (strncmp(name, predefs[i].name, len) != 0 ||
421761d06d6bSBaptiste Daroussin 		    predefs[i].name[len] != '\0')
421861d06d6bSBaptiste Daroussin 			continue;
421961d06d6bSBaptiste Daroussin 		if (*deftype & ROFFDEF_PRE) {
422061d06d6bSBaptiste Daroussin 			*deftype = ROFFDEF_PRE;
422161d06d6bSBaptiste Daroussin 			return predefs[i].str;
422261d06d6bSBaptiste Daroussin 		} else {
422361d06d6bSBaptiste Daroussin 			found = 1;
422461d06d6bSBaptiste Daroussin 			break;
422561d06d6bSBaptiste Daroussin 		}
422661d06d6bSBaptiste Daroussin 	}
42277295610fSBaptiste Daroussin 	if (r->man->meta.macroset != MACROSET_MAN) {
422861d06d6bSBaptiste Daroussin 		for (tok = MDOC_Dd; tok < MDOC_MAX; tok++) {
422961d06d6bSBaptiste Daroussin 			if (strncmp(name, roff_name[tok], len) != 0 ||
423061d06d6bSBaptiste Daroussin 			    roff_name[tok][len] != '\0')
423161d06d6bSBaptiste Daroussin 				continue;
423261d06d6bSBaptiste Daroussin 			if (*deftype & ROFFDEF_STD) {
423361d06d6bSBaptiste Daroussin 				*deftype = ROFFDEF_STD;
423461d06d6bSBaptiste Daroussin 				return NULL;
423561d06d6bSBaptiste Daroussin 			} else {
423661d06d6bSBaptiste Daroussin 				found = 1;
423761d06d6bSBaptiste Daroussin 				break;
423861d06d6bSBaptiste Daroussin 			}
423961d06d6bSBaptiste Daroussin 		}
424061d06d6bSBaptiste Daroussin 	}
42417295610fSBaptiste Daroussin 	if (r->man->meta.macroset != MACROSET_MDOC) {
424261d06d6bSBaptiste Daroussin 		for (tok = MAN_TH; tok < MAN_MAX; tok++) {
424361d06d6bSBaptiste Daroussin 			if (strncmp(name, roff_name[tok], len) != 0 ||
424461d06d6bSBaptiste Daroussin 			    roff_name[tok][len] != '\0')
424561d06d6bSBaptiste Daroussin 				continue;
424661d06d6bSBaptiste Daroussin 			if (*deftype & ROFFDEF_STD) {
424761d06d6bSBaptiste Daroussin 				*deftype = ROFFDEF_STD;
424861d06d6bSBaptiste Daroussin 				return NULL;
424961d06d6bSBaptiste Daroussin 			} else {
425061d06d6bSBaptiste Daroussin 				found = 1;
425161d06d6bSBaptiste Daroussin 				break;
425261d06d6bSBaptiste Daroussin 			}
425361d06d6bSBaptiste Daroussin 		}
425461d06d6bSBaptiste Daroussin 	}
425561d06d6bSBaptiste Daroussin 
425661d06d6bSBaptiste Daroussin 	if (found == 0 && *deftype != ROFFDEF_ANY) {
425761d06d6bSBaptiste Daroussin 		if (*deftype & ROFFDEF_REN) {
425861d06d6bSBaptiste Daroussin 			/*
425961d06d6bSBaptiste Daroussin 			 * This might still be a request,
426061d06d6bSBaptiste Daroussin 			 * so do not treat it as undefined yet.
426161d06d6bSBaptiste Daroussin 			 */
426261d06d6bSBaptiste Daroussin 			*deftype = ROFFDEF_UNDEF;
426361d06d6bSBaptiste Daroussin 			return NULL;
426461d06d6bSBaptiste Daroussin 		}
426561d06d6bSBaptiste Daroussin 
426661d06d6bSBaptiste Daroussin 		/* Using an undefined string defines it to be empty. */
426761d06d6bSBaptiste Daroussin 
426861d06d6bSBaptiste Daroussin 		roff_setstrn(&r->strtab, name, len, "", 0, 0);
426961d06d6bSBaptiste Daroussin 		roff_setstrn(&r->rentab, name, len, NULL, 0, 0);
427061d06d6bSBaptiste Daroussin 	}
427161d06d6bSBaptiste Daroussin 
427261d06d6bSBaptiste Daroussin 	*deftype = 0;
427361d06d6bSBaptiste Daroussin 	return NULL;
427461d06d6bSBaptiste Daroussin }
427561d06d6bSBaptiste Daroussin 
427661d06d6bSBaptiste Daroussin static void
roff_freestr(struct roffkv * r)427761d06d6bSBaptiste Daroussin roff_freestr(struct roffkv *r)
427861d06d6bSBaptiste Daroussin {
427961d06d6bSBaptiste Daroussin 	struct roffkv	 *n, *nn;
428061d06d6bSBaptiste Daroussin 
428161d06d6bSBaptiste Daroussin 	for (n = r; n; n = nn) {
428261d06d6bSBaptiste Daroussin 		free(n->key.p);
428361d06d6bSBaptiste Daroussin 		free(n->val.p);
428461d06d6bSBaptiste Daroussin 		nn = n->next;
428561d06d6bSBaptiste Daroussin 		free(n);
428661d06d6bSBaptiste Daroussin 	}
428761d06d6bSBaptiste Daroussin }
428861d06d6bSBaptiste Daroussin 
428961d06d6bSBaptiste Daroussin /* --- accessors and utility functions ------------------------------------ */
429061d06d6bSBaptiste Daroussin 
429161d06d6bSBaptiste Daroussin /*
429261d06d6bSBaptiste Daroussin  * Duplicate an input string, making the appropriate character
429361d06d6bSBaptiste Daroussin  * conversations (as stipulated by `tr') along the way.
429461d06d6bSBaptiste Daroussin  * Returns a heap-allocated string with all the replacements made.
429561d06d6bSBaptiste Daroussin  */
429661d06d6bSBaptiste Daroussin char *
roff_strdup(const struct roff * r,const char * p)429761d06d6bSBaptiste Daroussin roff_strdup(const struct roff *r, const char *p)
429861d06d6bSBaptiste Daroussin {
429961d06d6bSBaptiste Daroussin 	const struct roffkv *cp;
430061d06d6bSBaptiste Daroussin 	char		*res;
430161d06d6bSBaptiste Daroussin 	const char	*pp;
430261d06d6bSBaptiste Daroussin 	size_t		 ssz, sz;
430361d06d6bSBaptiste Daroussin 	enum mandoc_esc	 esc;
430461d06d6bSBaptiste Daroussin 
430561d06d6bSBaptiste Daroussin 	if (NULL == r->xmbtab && NULL == r->xtab)
430661d06d6bSBaptiste Daroussin 		return mandoc_strdup(p);
430761d06d6bSBaptiste Daroussin 	else if ('\0' == *p)
430861d06d6bSBaptiste Daroussin 		return mandoc_strdup("");
430961d06d6bSBaptiste Daroussin 
431061d06d6bSBaptiste Daroussin 	/*
431161d06d6bSBaptiste Daroussin 	 * Step through each character looking for term matches
431261d06d6bSBaptiste Daroussin 	 * (remember that a `tr' can be invoked with an escape, which is
431361d06d6bSBaptiste Daroussin 	 * a glyph but the escape is multi-character).
431461d06d6bSBaptiste Daroussin 	 * We only do this if the character hash has been initialised
431561d06d6bSBaptiste Daroussin 	 * and the string is >0 length.
431661d06d6bSBaptiste Daroussin 	 */
431761d06d6bSBaptiste Daroussin 
431861d06d6bSBaptiste Daroussin 	res = NULL;
431961d06d6bSBaptiste Daroussin 	ssz = 0;
432061d06d6bSBaptiste Daroussin 
432161d06d6bSBaptiste Daroussin 	while ('\0' != *p) {
432261d06d6bSBaptiste Daroussin 		assert((unsigned int)*p < 128);
432361d06d6bSBaptiste Daroussin 		if ('\\' != *p && r->xtab && r->xtab[(unsigned int)*p].p) {
432461d06d6bSBaptiste Daroussin 			sz = r->xtab[(int)*p].sz;
432561d06d6bSBaptiste Daroussin 			res = mandoc_realloc(res, ssz + sz + 1);
432661d06d6bSBaptiste Daroussin 			memcpy(res + ssz, r->xtab[(int)*p].p, sz);
432761d06d6bSBaptiste Daroussin 			ssz += sz;
432861d06d6bSBaptiste Daroussin 			p++;
432961d06d6bSBaptiste Daroussin 			continue;
433061d06d6bSBaptiste Daroussin 		} else if ('\\' != *p) {
433161d06d6bSBaptiste Daroussin 			res = mandoc_realloc(res, ssz + 2);
433261d06d6bSBaptiste Daroussin 			res[ssz++] = *p++;
433361d06d6bSBaptiste Daroussin 			continue;
433461d06d6bSBaptiste Daroussin 		}
433561d06d6bSBaptiste Daroussin 
433661d06d6bSBaptiste Daroussin 		/* Search for term matches. */
433761d06d6bSBaptiste Daroussin 		for (cp = r->xmbtab; cp; cp = cp->next)
433861d06d6bSBaptiste Daroussin 			if (0 == strncmp(p, cp->key.p, cp->key.sz))
433961d06d6bSBaptiste Daroussin 				break;
434061d06d6bSBaptiste Daroussin 
434161d06d6bSBaptiste Daroussin 		if (NULL != cp) {
434261d06d6bSBaptiste Daroussin 			/*
434361d06d6bSBaptiste Daroussin 			 * A match has been found.
434461d06d6bSBaptiste Daroussin 			 * Append the match to the array and move
434561d06d6bSBaptiste Daroussin 			 * forward by its keysize.
434661d06d6bSBaptiste Daroussin 			 */
434761d06d6bSBaptiste Daroussin 			res = mandoc_realloc(res,
434861d06d6bSBaptiste Daroussin 			    ssz + cp->val.sz + 1);
434961d06d6bSBaptiste Daroussin 			memcpy(res + ssz, cp->val.p, cp->val.sz);
435061d06d6bSBaptiste Daroussin 			ssz += cp->val.sz;
435161d06d6bSBaptiste Daroussin 			p += (int)cp->key.sz;
435261d06d6bSBaptiste Daroussin 			continue;
435361d06d6bSBaptiste Daroussin 		}
435461d06d6bSBaptiste Daroussin 
435561d06d6bSBaptiste Daroussin 		/*
435661d06d6bSBaptiste Daroussin 		 * Handle escapes carefully: we need to copy
435761d06d6bSBaptiste Daroussin 		 * over just the escape itself, or else we might
435861d06d6bSBaptiste Daroussin 		 * do replacements within the escape itself.
435961d06d6bSBaptiste Daroussin 		 * Make sure to pass along the bogus string.
436061d06d6bSBaptiste Daroussin 		 */
436161d06d6bSBaptiste Daroussin 		pp = p++;
436261d06d6bSBaptiste Daroussin 		esc = mandoc_escape(&p, NULL, NULL);
436361d06d6bSBaptiste Daroussin 		if (ESCAPE_ERROR == esc) {
436461d06d6bSBaptiste Daroussin 			sz = strlen(pp);
436561d06d6bSBaptiste Daroussin 			res = mandoc_realloc(res, ssz + sz + 1);
436661d06d6bSBaptiste Daroussin 			memcpy(res + ssz, pp, sz);
436761d06d6bSBaptiste Daroussin 			break;
436861d06d6bSBaptiste Daroussin 		}
436961d06d6bSBaptiste Daroussin 		/*
437061d06d6bSBaptiste Daroussin 		 * We bail out on bad escapes.
437161d06d6bSBaptiste Daroussin 		 * No need to warn: we already did so when
43727295610fSBaptiste Daroussin 		 * roff_expand() was called.
437361d06d6bSBaptiste Daroussin 		 */
437461d06d6bSBaptiste Daroussin 		sz = (int)(p - pp);
437561d06d6bSBaptiste Daroussin 		res = mandoc_realloc(res, ssz + sz + 1);
437661d06d6bSBaptiste Daroussin 		memcpy(res + ssz, pp, sz);
437761d06d6bSBaptiste Daroussin 		ssz += sz;
437861d06d6bSBaptiste Daroussin 	}
437961d06d6bSBaptiste Daroussin 
438061d06d6bSBaptiste Daroussin 	res[(int)ssz] = '\0';
438161d06d6bSBaptiste Daroussin 	return res;
438261d06d6bSBaptiste Daroussin }
438361d06d6bSBaptiste Daroussin 
438461d06d6bSBaptiste Daroussin int
roff_getformat(const struct roff * r)438561d06d6bSBaptiste Daroussin roff_getformat(const struct roff *r)
438661d06d6bSBaptiste Daroussin {
438761d06d6bSBaptiste Daroussin 
438861d06d6bSBaptiste Daroussin 	return r->format;
438961d06d6bSBaptiste Daroussin }
439061d06d6bSBaptiste Daroussin 
439161d06d6bSBaptiste Daroussin /*
439261d06d6bSBaptiste Daroussin  * Find out whether a line is a macro line or not.
439361d06d6bSBaptiste Daroussin  * If it is, adjust the current position and return one; if it isn't,
439461d06d6bSBaptiste Daroussin  * return zero and don't change the current position.
439561d06d6bSBaptiste Daroussin  * If the control character has been set with `.cc', then let that grain
439661d06d6bSBaptiste Daroussin  * precedence.
4397*c1c95addSBrooks Davis  * This is slightly contrary to groff, where using the non-breaking
439861d06d6bSBaptiste Daroussin  * control character when `cc' has been invoked will cause the
439961d06d6bSBaptiste Daroussin  * non-breaking macro contents to be printed verbatim.
440061d06d6bSBaptiste Daroussin  */
440161d06d6bSBaptiste Daroussin int
roff_getcontrol(const struct roff * r,const char * cp,int * ppos)440261d06d6bSBaptiste Daroussin roff_getcontrol(const struct roff *r, const char *cp, int *ppos)
440361d06d6bSBaptiste Daroussin {
440461d06d6bSBaptiste Daroussin 	int		pos;
440561d06d6bSBaptiste Daroussin 
440661d06d6bSBaptiste Daroussin 	pos = *ppos;
440761d06d6bSBaptiste Daroussin 
440861d06d6bSBaptiste Daroussin 	if (r->control != '\0' && cp[pos] == r->control)
440961d06d6bSBaptiste Daroussin 		pos++;
441061d06d6bSBaptiste Daroussin 	else if (r->control != '\0')
441161d06d6bSBaptiste Daroussin 		return 0;
441261d06d6bSBaptiste Daroussin 	else if ('\\' == cp[pos] && '.' == cp[pos + 1])
441361d06d6bSBaptiste Daroussin 		pos += 2;
441461d06d6bSBaptiste Daroussin 	else if ('.' == cp[pos] || '\'' == cp[pos])
441561d06d6bSBaptiste Daroussin 		pos++;
441661d06d6bSBaptiste Daroussin 	else
441761d06d6bSBaptiste Daroussin 		return 0;
441861d06d6bSBaptiste Daroussin 
441961d06d6bSBaptiste Daroussin 	while (' ' == cp[pos] || '\t' == cp[pos])
442061d06d6bSBaptiste Daroussin 		pos++;
442161d06d6bSBaptiste Daroussin 
442261d06d6bSBaptiste Daroussin 	*ppos = pos;
442361d06d6bSBaptiste Daroussin 	return 1;
442461d06d6bSBaptiste Daroussin }
4425