xref: /freebsd/contrib/mandoc/roff.c (revision 6d38604f)
1*6d38604fSBaptiste Daroussin /* $Id: roff.c,v 1.378 2021/08/10 12:55:04 schwarze Exp $ */
261d06d6bSBaptiste Daroussin /*
3*6d38604fSBaptiste Daroussin  * Copyright (c) 2010-2015, 2017-2020 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.
17*6d38604fSBaptiste Daroussin  *
18*6d38604fSBaptiste 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 
437295610fSBaptiste Daroussin /*
447295610fSBaptiste Daroussin  * ASCII_ESC is used to signal from roff_getarg() to roff_expand()
457295610fSBaptiste Daroussin  * that an escape sequence resulted from copy-in processing and
467295610fSBaptiste Daroussin  * needs to be checked or interpolated.  As it is used nowhere
477295610fSBaptiste Daroussin  * else, it is defined here rather than in a header file.
487295610fSBaptiste Daroussin  */
497295610fSBaptiste Daroussin #define	ASCII_ESC	27
5061d06d6bSBaptiste Daroussin 
5161d06d6bSBaptiste Daroussin /* Maximum number of string expansions per line, to break infinite loops. */
5261d06d6bSBaptiste Daroussin #define	EXPAND_LIMIT	1000
5361d06d6bSBaptiste Daroussin 
5461d06d6bSBaptiste Daroussin /* Types of definitions of macros and strings. */
5561d06d6bSBaptiste Daroussin #define	ROFFDEF_USER	(1 << 1)  /* User-defined. */
5661d06d6bSBaptiste Daroussin #define	ROFFDEF_PRE	(1 << 2)  /* Predefined. */
5761d06d6bSBaptiste Daroussin #define	ROFFDEF_REN	(1 << 3)  /* Renamed standard macro. */
5861d06d6bSBaptiste Daroussin #define	ROFFDEF_STD	(1 << 4)  /* mdoc(7) or man(7) macro. */
5961d06d6bSBaptiste Daroussin #define	ROFFDEF_ANY	(ROFFDEF_USER | ROFFDEF_PRE | \
6061d06d6bSBaptiste Daroussin 			 ROFFDEF_REN | ROFFDEF_STD)
6161d06d6bSBaptiste Daroussin #define	ROFFDEF_UNDEF	(1 << 5)  /* Completely undefined. */
6261d06d6bSBaptiste Daroussin 
6361d06d6bSBaptiste Daroussin /* --- data types --------------------------------------------------------- */
6461d06d6bSBaptiste Daroussin 
6561d06d6bSBaptiste Daroussin /*
6661d06d6bSBaptiste Daroussin  * An incredibly-simple string buffer.
6761d06d6bSBaptiste Daroussin  */
6861d06d6bSBaptiste Daroussin struct	roffstr {
6961d06d6bSBaptiste Daroussin 	char		*p; /* nil-terminated buffer */
7061d06d6bSBaptiste Daroussin 	size_t		 sz; /* saved strlen(p) */
7161d06d6bSBaptiste Daroussin };
7261d06d6bSBaptiste Daroussin 
7361d06d6bSBaptiste Daroussin /*
7461d06d6bSBaptiste Daroussin  * A key-value roffstr pair as part of a singly-linked list.
7561d06d6bSBaptiste Daroussin  */
7661d06d6bSBaptiste Daroussin struct	roffkv {
7761d06d6bSBaptiste Daroussin 	struct roffstr	 key;
7861d06d6bSBaptiste Daroussin 	struct roffstr	 val;
7961d06d6bSBaptiste Daroussin 	struct roffkv	*next; /* next in list */
8061d06d6bSBaptiste Daroussin };
8161d06d6bSBaptiste Daroussin 
8261d06d6bSBaptiste Daroussin /*
8361d06d6bSBaptiste Daroussin  * A single number register as part of a singly-linked list.
8461d06d6bSBaptiste Daroussin  */
8561d06d6bSBaptiste Daroussin struct	roffreg {
8661d06d6bSBaptiste Daroussin 	struct roffstr	 key;
8761d06d6bSBaptiste Daroussin 	int		 val;
8861d06d6bSBaptiste Daroussin 	int		 step;
8961d06d6bSBaptiste Daroussin 	struct roffreg	*next;
9061d06d6bSBaptiste Daroussin };
9161d06d6bSBaptiste Daroussin 
9261d06d6bSBaptiste Daroussin /*
9361d06d6bSBaptiste Daroussin  * Association of request and macro names with token IDs.
9461d06d6bSBaptiste Daroussin  */
9561d06d6bSBaptiste Daroussin struct	roffreq {
9661d06d6bSBaptiste Daroussin 	enum roff_tok	 tok;
9761d06d6bSBaptiste Daroussin 	char		 name[];
9861d06d6bSBaptiste Daroussin };
9961d06d6bSBaptiste Daroussin 
1007295610fSBaptiste Daroussin /*
1017295610fSBaptiste Daroussin  * A macro processing context.
1027295610fSBaptiste Daroussin  * More than one is needed when macro calls are nested.
1037295610fSBaptiste Daroussin  */
1047295610fSBaptiste Daroussin struct	mctx {
1057295610fSBaptiste Daroussin 	char		**argv;
1067295610fSBaptiste Daroussin 	int		 argc;
1077295610fSBaptiste Daroussin 	int		 argsz;
1087295610fSBaptiste Daroussin };
1097295610fSBaptiste Daroussin 
11061d06d6bSBaptiste Daroussin struct	roff {
11161d06d6bSBaptiste Daroussin 	struct roff_man	*man; /* mdoc or man parser */
11261d06d6bSBaptiste Daroussin 	struct roffnode	*last; /* leaf of stack */
1137295610fSBaptiste Daroussin 	struct mctx	*mstack; /* stack of macro contexts */
11461d06d6bSBaptiste Daroussin 	int		*rstack; /* stack of inverted `ie' values */
11561d06d6bSBaptiste Daroussin 	struct ohash	*reqtab; /* request lookup table */
11661d06d6bSBaptiste Daroussin 	struct roffreg	*regtab; /* number registers */
11761d06d6bSBaptiste Daroussin 	struct roffkv	*strtab; /* user-defined strings & macros */
11861d06d6bSBaptiste Daroussin 	struct roffkv	*rentab; /* renamed strings & macros */
11961d06d6bSBaptiste Daroussin 	struct roffkv	*xmbtab; /* multi-byte trans table (`tr') */
12061d06d6bSBaptiste Daroussin 	struct roffstr	*xtab; /* single-byte trans table (`tr') */
12161d06d6bSBaptiste Daroussin 	const char	*current_string; /* value of last called user macro */
12261d06d6bSBaptiste Daroussin 	struct tbl_node	*first_tbl; /* first table parsed */
12361d06d6bSBaptiste Daroussin 	struct tbl_node	*last_tbl; /* last table parsed */
12461d06d6bSBaptiste Daroussin 	struct tbl_node	*tbl; /* current table being parsed */
12561d06d6bSBaptiste Daroussin 	struct eqn_node	*last_eqn; /* equation parser */
12661d06d6bSBaptiste Daroussin 	struct eqn_node	*eqn; /* active equation parser */
12761d06d6bSBaptiste Daroussin 	int		 eqn_inline; /* current equation is inline */
12861d06d6bSBaptiste Daroussin 	int		 options; /* parse options */
1297295610fSBaptiste Daroussin 	int		 mstacksz; /* current size of mstack */
1307295610fSBaptiste Daroussin 	int		 mstackpos; /* position in mstack */
13161d06d6bSBaptiste Daroussin 	int		 rstacksz; /* current size limit of rstack */
13261d06d6bSBaptiste Daroussin 	int		 rstackpos; /* position in rstack */
13361d06d6bSBaptiste Daroussin 	int		 format; /* current file in mdoc or man format */
13461d06d6bSBaptiste Daroussin 	char		 control; /* control character */
13561d06d6bSBaptiste Daroussin 	char		 escape; /* escape character */
13661d06d6bSBaptiste Daroussin };
13761d06d6bSBaptiste Daroussin 
13845a5aec3SBaptiste Daroussin /*
13945a5aec3SBaptiste Daroussin  * A macro definition, condition, or ignored block.
14045a5aec3SBaptiste Daroussin  */
14161d06d6bSBaptiste Daroussin struct	roffnode {
14261d06d6bSBaptiste Daroussin 	enum roff_tok	 tok; /* type of node */
14361d06d6bSBaptiste Daroussin 	struct roffnode	*parent; /* up one in stack */
14461d06d6bSBaptiste Daroussin 	int		 line; /* parse line */
14561d06d6bSBaptiste Daroussin 	int		 col; /* parse col */
14661d06d6bSBaptiste Daroussin 	char		*name; /* node name, e.g. macro name */
14745a5aec3SBaptiste Daroussin 	char		*end; /* custom end macro of the block */
14845a5aec3SBaptiste Daroussin 	int		 endspan; /* scope to: 1=eol 2=next line -1=\} */
14945a5aec3SBaptiste Daroussin 	int		 rule; /* content is: 1=evaluated 0=skipped */
15061d06d6bSBaptiste Daroussin };
15161d06d6bSBaptiste Daroussin 
15261d06d6bSBaptiste Daroussin #define	ROFF_ARGS	 struct roff *r, /* parse ctx */ \
15361d06d6bSBaptiste Daroussin 			 enum roff_tok tok, /* tok of macro */ \
15461d06d6bSBaptiste Daroussin 			 struct buf *buf, /* input buffer */ \
15561d06d6bSBaptiste Daroussin 			 int ln, /* parse line */ \
15661d06d6bSBaptiste Daroussin 			 int ppos, /* original pos in buffer */ \
15761d06d6bSBaptiste Daroussin 			 int pos, /* current pos in buffer */ \
15861d06d6bSBaptiste Daroussin 			 int *offs /* reset offset of buffer data */
15961d06d6bSBaptiste Daroussin 
1607295610fSBaptiste Daroussin typedef	int (*roffproc)(ROFF_ARGS);
16161d06d6bSBaptiste Daroussin 
16261d06d6bSBaptiste Daroussin struct	roffmac {
16361d06d6bSBaptiste Daroussin 	roffproc	 proc; /* process new macro */
16461d06d6bSBaptiste Daroussin 	roffproc	 text; /* process as child text of macro */
16561d06d6bSBaptiste Daroussin 	roffproc	 sub; /* process as child of macro */
16661d06d6bSBaptiste Daroussin 	int		 flags;
16761d06d6bSBaptiste Daroussin #define	ROFFMAC_STRUCT	(1 << 0) /* always interpret */
16861d06d6bSBaptiste Daroussin };
16961d06d6bSBaptiste Daroussin 
17061d06d6bSBaptiste Daroussin struct	predef {
17161d06d6bSBaptiste Daroussin 	const char	*name; /* predefined input name */
17261d06d6bSBaptiste Daroussin 	const char	*str; /* replacement symbol */
17361d06d6bSBaptiste Daroussin };
17461d06d6bSBaptiste Daroussin 
17561d06d6bSBaptiste Daroussin #define	PREDEF(__name, __str) \
17661d06d6bSBaptiste Daroussin 	{ (__name), (__str) },
17761d06d6bSBaptiste Daroussin 
17861d06d6bSBaptiste Daroussin /* --- function prototypes ------------------------------------------------ */
17961d06d6bSBaptiste Daroussin 
1807295610fSBaptiste Daroussin static	int		 roffnode_cleanscope(struct roff *);
1817295610fSBaptiste Daroussin static	int		 roffnode_pop(struct roff *);
18261d06d6bSBaptiste Daroussin static	void		 roffnode_push(struct roff *, enum roff_tok,
18361d06d6bSBaptiste Daroussin 				const char *, int, int);
1847295610fSBaptiste Daroussin static	void		 roff_addtbl(struct roff_man *, int, struct tbl_node *);
1857295610fSBaptiste Daroussin static	int		 roff_als(ROFF_ARGS);
1867295610fSBaptiste Daroussin static	int		 roff_block(ROFF_ARGS);
1877295610fSBaptiste Daroussin static	int		 roff_block_text(ROFF_ARGS);
1887295610fSBaptiste Daroussin static	int		 roff_block_sub(ROFF_ARGS);
18945a5aec3SBaptiste Daroussin static	int		 roff_break(ROFF_ARGS);
1907295610fSBaptiste Daroussin static	int		 roff_cblock(ROFF_ARGS);
1917295610fSBaptiste Daroussin static	int		 roff_cc(ROFF_ARGS);
1927295610fSBaptiste Daroussin static	int		 roff_ccond(struct roff *, int, int);
1937295610fSBaptiste Daroussin static	int		 roff_char(ROFF_ARGS);
1947295610fSBaptiste Daroussin static	int		 roff_cond(ROFF_ARGS);
195*6d38604fSBaptiste Daroussin static	int		 roff_cond_checkend(ROFF_ARGS);
1967295610fSBaptiste Daroussin static	int		 roff_cond_text(ROFF_ARGS);
1977295610fSBaptiste Daroussin static	int		 roff_cond_sub(ROFF_ARGS);
1987295610fSBaptiste Daroussin static	int		 roff_ds(ROFF_ARGS);
1997295610fSBaptiste Daroussin static	int		 roff_ec(ROFF_ARGS);
2007295610fSBaptiste Daroussin static	int		 roff_eo(ROFF_ARGS);
2017295610fSBaptiste Daroussin static	int		 roff_eqndelim(struct roff *, struct buf *, int);
202*6d38604fSBaptiste Daroussin static	int		 roff_evalcond(struct roff *, int, char *, int *);
20361d06d6bSBaptiste Daroussin static	int		 roff_evalnum(struct roff *, int,
20461d06d6bSBaptiste Daroussin 				const char *, int *, int *, int);
20561d06d6bSBaptiste Daroussin static	int		 roff_evalpar(struct roff *, int,
20661d06d6bSBaptiste Daroussin 				const char *, int *, int *, int);
20761d06d6bSBaptiste Daroussin static	int		 roff_evalstrcond(const char *, int *);
2087295610fSBaptiste Daroussin static	int		 roff_expand(struct roff *, struct buf *,
2097295610fSBaptiste Daroussin 				int, int, char);
21061d06d6bSBaptiste Daroussin static	void		 roff_free1(struct roff *);
21161d06d6bSBaptiste Daroussin static	void		 roff_freereg(struct roffreg *);
21261d06d6bSBaptiste Daroussin static	void		 roff_freestr(struct roffkv *);
21361d06d6bSBaptiste Daroussin static	size_t		 roff_getname(struct roff *, char **, int, int);
21461d06d6bSBaptiste Daroussin static	int		 roff_getnum(const char *, int *, int *, int);
21561d06d6bSBaptiste Daroussin static	int		 roff_getop(const char *, int *, char *);
21661d06d6bSBaptiste Daroussin static	int		 roff_getregn(struct roff *,
21761d06d6bSBaptiste Daroussin 				const char *, size_t, char);
21861d06d6bSBaptiste Daroussin static	int		 roff_getregro(const struct roff *,
21961d06d6bSBaptiste Daroussin 				const char *name);
22061d06d6bSBaptiste Daroussin static	const char	*roff_getstrn(struct roff *,
22161d06d6bSBaptiste Daroussin 				const char *, size_t, int *);
22261d06d6bSBaptiste Daroussin static	int		 roff_hasregn(const struct roff *,
22361d06d6bSBaptiste Daroussin 				const char *, size_t);
2247295610fSBaptiste Daroussin static	int		 roff_insec(ROFF_ARGS);
2257295610fSBaptiste Daroussin static	int		 roff_it(ROFF_ARGS);
2267295610fSBaptiste Daroussin static	int		 roff_line_ignore(ROFF_ARGS);
22761d06d6bSBaptiste Daroussin static	void		 roff_man_alloc1(struct roff_man *);
22861d06d6bSBaptiste Daroussin static	void		 roff_man_free1(struct roff_man *);
2297295610fSBaptiste Daroussin static	int		 roff_manyarg(ROFF_ARGS);
2307295610fSBaptiste Daroussin static	int		 roff_noarg(ROFF_ARGS);
2317295610fSBaptiste Daroussin static	int		 roff_nop(ROFF_ARGS);
2327295610fSBaptiste Daroussin static	int		 roff_nr(ROFF_ARGS);
2337295610fSBaptiste Daroussin static	int		 roff_onearg(ROFF_ARGS);
23461d06d6bSBaptiste Daroussin static	enum roff_tok	 roff_parse(struct roff *, char *, int *,
23561d06d6bSBaptiste Daroussin 				int, int);
2367295610fSBaptiste Daroussin static	int		 roff_parsetext(struct roff *, struct buf *,
23761d06d6bSBaptiste Daroussin 				int, int *);
2387295610fSBaptiste Daroussin static	int		 roff_renamed(ROFF_ARGS);
2397295610fSBaptiste Daroussin static	int		 roff_return(ROFF_ARGS);
2407295610fSBaptiste Daroussin static	int		 roff_rm(ROFF_ARGS);
2417295610fSBaptiste Daroussin static	int		 roff_rn(ROFF_ARGS);
2427295610fSBaptiste Daroussin static	int		 roff_rr(ROFF_ARGS);
24361d06d6bSBaptiste Daroussin static	void		 roff_setregn(struct roff *, const char *,
24461d06d6bSBaptiste Daroussin 				size_t, int, char, int);
24561d06d6bSBaptiste Daroussin static	void		 roff_setstr(struct roff *,
24661d06d6bSBaptiste Daroussin 				const char *, const char *, int);
24761d06d6bSBaptiste Daroussin static	void		 roff_setstrn(struct roffkv **, const char *,
24861d06d6bSBaptiste Daroussin 				size_t, const char *, size_t, int);
2497295610fSBaptiste Daroussin static	int		 roff_shift(ROFF_ARGS);
2507295610fSBaptiste Daroussin static	int		 roff_so(ROFF_ARGS);
2517295610fSBaptiste Daroussin static	int		 roff_tr(ROFF_ARGS);
2527295610fSBaptiste Daroussin static	int		 roff_Dd(ROFF_ARGS);
2537295610fSBaptiste Daroussin static	int		 roff_TE(ROFF_ARGS);
2547295610fSBaptiste Daroussin static	int		 roff_TS(ROFF_ARGS);
2557295610fSBaptiste Daroussin static	int		 roff_EQ(ROFF_ARGS);
2567295610fSBaptiste Daroussin static	int		 roff_EN(ROFF_ARGS);
2577295610fSBaptiste Daroussin static	int		 roff_T_(ROFF_ARGS);
2587295610fSBaptiste Daroussin static	int		 roff_unsupp(ROFF_ARGS);
2597295610fSBaptiste Daroussin static	int		 roff_userdef(ROFF_ARGS);
26061d06d6bSBaptiste Daroussin 
26161d06d6bSBaptiste Daroussin /* --- constant data ------------------------------------------------------ */
26261d06d6bSBaptiste Daroussin 
26361d06d6bSBaptiste Daroussin #define	ROFFNUM_SCALE	(1 << 0)  /* Honour scaling in roff_getnum(). */
26461d06d6bSBaptiste Daroussin #define	ROFFNUM_WHITE	(1 << 1)  /* Skip whitespace in roff_evalnum(). */
26561d06d6bSBaptiste Daroussin 
26661d06d6bSBaptiste Daroussin const char *__roff_name[MAN_MAX + 1] = {
2677295610fSBaptiste Daroussin 	"br",		"ce",		"fi",		"ft",
2687295610fSBaptiste Daroussin 	"ll",		"mc",		"nf",
2697295610fSBaptiste Daroussin 	"po",		"rj",		"sp",
27061d06d6bSBaptiste Daroussin 	"ta",		"ti",		NULL,
27161d06d6bSBaptiste Daroussin 	"ab",		"ad",		"af",		"aln",
27261d06d6bSBaptiste Daroussin 	"als",		"am",		"am1",		"ami",
27361d06d6bSBaptiste Daroussin 	"ami1",		"as",		"as1",		"asciify",
27461d06d6bSBaptiste Daroussin 	"backtrace",	"bd",		"bleedat",	"blm",
27561d06d6bSBaptiste Daroussin         "box",		"boxa",		"bp",		"BP",
27661d06d6bSBaptiste Daroussin 	"break",	"breakchar",	"brnl",		"brp",
27761d06d6bSBaptiste Daroussin 	"brpnl",	"c2",		"cc",
27861d06d6bSBaptiste Daroussin 	"cf",		"cflags",	"ch",		"char",
27961d06d6bSBaptiste Daroussin 	"chop",		"class",	"close",	"CL",
28061d06d6bSBaptiste Daroussin 	"color",	"composite",	"continue",	"cp",
28161d06d6bSBaptiste Daroussin 	"cropat",	"cs",		"cu",		"da",
28261d06d6bSBaptiste Daroussin 	"dch",		"Dd",		"de",		"de1",
28361d06d6bSBaptiste Daroussin 	"defcolor",	"dei",		"dei1",		"device",
28461d06d6bSBaptiste Daroussin 	"devicem",	"di",		"do",		"ds",
28561d06d6bSBaptiste Daroussin 	"ds1",		"dwh",		"dt",		"ec",
28661d06d6bSBaptiste Daroussin 	"ecr",		"ecs",		"el",		"em",
28761d06d6bSBaptiste Daroussin 	"EN",		"eo",		"EP",		"EQ",
28861d06d6bSBaptiste Daroussin 	"errprint",	"ev",		"evc",		"ex",
28961d06d6bSBaptiste Daroussin 	"fallback",	"fam",		"fc",		"fchar",
29061d06d6bSBaptiste Daroussin 	"fcolor",	"fdeferlig",	"feature",	"fkern",
29161d06d6bSBaptiste Daroussin 	"fl",		"flig",		"fp",		"fps",
29261d06d6bSBaptiste Daroussin 	"fschar",	"fspacewidth",	"fspecial",	"ftr",
29361d06d6bSBaptiste Daroussin 	"fzoom",	"gcolor",	"hc",		"hcode",
29461d06d6bSBaptiste Daroussin 	"hidechar",	"hla",		"hlm",		"hpf",
29561d06d6bSBaptiste Daroussin 	"hpfa",		"hpfcode",	"hw",		"hy",
29661d06d6bSBaptiste Daroussin 	"hylang",	"hylen",	"hym",		"hypp",
29761d06d6bSBaptiste Daroussin 	"hys",		"ie",		"if",		"ig",
29861d06d6bSBaptiste Daroussin 	"index",	"it",		"itc",		"IX",
29961d06d6bSBaptiste Daroussin 	"kern",		"kernafter",	"kernbefore",	"kernpair",
30061d06d6bSBaptiste Daroussin 	"lc",		"lc_ctype",	"lds",		"length",
30161d06d6bSBaptiste Daroussin 	"letadj",	"lf",		"lg",		"lhang",
30261d06d6bSBaptiste Daroussin 	"linetabs",	"lnr",		"lnrf",		"lpfx",
30361d06d6bSBaptiste Daroussin 	"ls",		"lsm",		"lt",
30461d06d6bSBaptiste Daroussin 	"mediasize",	"minss",	"mk",		"mso",
30561d06d6bSBaptiste Daroussin 	"na",		"ne",		"nh",		"nhychar",
30661d06d6bSBaptiste Daroussin 	"nm",		"nn",		"nop",		"nr",
30761d06d6bSBaptiste Daroussin 	"nrf",		"nroff",	"ns",		"nx",
30861d06d6bSBaptiste Daroussin 	"open",		"opena",	"os",		"output",
30961d06d6bSBaptiste Daroussin 	"padj",		"papersize",	"pc",		"pev",
31061d06d6bSBaptiste Daroussin 	"pi",		"PI",		"pl",		"pm",
31161d06d6bSBaptiste Daroussin 	"pn",		"pnr",		"ps",
31261d06d6bSBaptiste Daroussin 	"psbb",		"pshape",	"pso",		"ptr",
31361d06d6bSBaptiste Daroussin 	"pvs",		"rchar",	"rd",		"recursionlimit",
31461d06d6bSBaptiste Daroussin 	"return",	"rfschar",	"rhang",
31561d06d6bSBaptiste Daroussin 	"rm",		"rn",		"rnn",		"rr",
31661d06d6bSBaptiste Daroussin 	"rs",		"rt",		"schar",	"sentchar",
31761d06d6bSBaptiste Daroussin 	"shc",		"shift",	"sizes",	"so",
31861d06d6bSBaptiste Daroussin 	"spacewidth",	"special",	"spreadwarn",	"ss",
31961d06d6bSBaptiste Daroussin 	"sty",		"substring",	"sv",		"sy",
32061d06d6bSBaptiste Daroussin 	"T&",		"tc",		"TE",
32161d06d6bSBaptiste Daroussin 	"TH",		"tkf",		"tl",
32261d06d6bSBaptiste Daroussin 	"tm",		"tm1",		"tmc",		"tr",
32361d06d6bSBaptiste Daroussin 	"track",	"transchar",	"trf",		"trimat",
32461d06d6bSBaptiste Daroussin 	"trin",		"trnt",		"troff",	"TS",
32561d06d6bSBaptiste Daroussin 	"uf",		"ul",		"unformat",	"unwatch",
32661d06d6bSBaptiste Daroussin 	"unwatchn",	"vpt",		"vs",		"warn",
32761d06d6bSBaptiste Daroussin 	"warnscale",	"watch",	"watchlength",	"watchn",
32861d06d6bSBaptiste Daroussin 	"wh",		"while",	"write",	"writec",
32961d06d6bSBaptiste Daroussin 	"writem",	"xflag",	".",		NULL,
33061d06d6bSBaptiste Daroussin 	NULL,		"text",
33161d06d6bSBaptiste Daroussin 	"Dd",		"Dt",		"Os",		"Sh",
33261d06d6bSBaptiste Daroussin 	"Ss",		"Pp",		"D1",		"Dl",
33361d06d6bSBaptiste Daroussin 	"Bd",		"Ed",		"Bl",		"El",
33461d06d6bSBaptiste Daroussin 	"It",		"Ad",		"An",		"Ap",
33561d06d6bSBaptiste Daroussin 	"Ar",		"Cd",		"Cm",		"Dv",
33661d06d6bSBaptiste Daroussin 	"Er",		"Ev",		"Ex",		"Fa",
33761d06d6bSBaptiste Daroussin 	"Fd",		"Fl",		"Fn",		"Ft",
33861d06d6bSBaptiste Daroussin 	"Ic",		"In",		"Li",		"Nd",
33961d06d6bSBaptiste Daroussin 	"Nm",		"Op",		"Ot",		"Pa",
34061d06d6bSBaptiste Daroussin 	"Rv",		"St",		"Va",		"Vt",
34161d06d6bSBaptiste Daroussin 	"Xr",		"%A",		"%B",		"%D",
34261d06d6bSBaptiste Daroussin 	"%I",		"%J",		"%N",		"%O",
34361d06d6bSBaptiste Daroussin 	"%P",		"%R",		"%T",		"%V",
34461d06d6bSBaptiste Daroussin 	"Ac",		"Ao",		"Aq",		"At",
34561d06d6bSBaptiste Daroussin 	"Bc",		"Bf",		"Bo",		"Bq",
34661d06d6bSBaptiste Daroussin 	"Bsx",		"Bx",		"Db",		"Dc",
34761d06d6bSBaptiste Daroussin 	"Do",		"Dq",		"Ec",		"Ef",
34861d06d6bSBaptiste Daroussin 	"Em",		"Eo",		"Fx",		"Ms",
34961d06d6bSBaptiste Daroussin 	"No",		"Ns",		"Nx",		"Ox",
35061d06d6bSBaptiste Daroussin 	"Pc",		"Pf",		"Po",		"Pq",
35161d06d6bSBaptiste Daroussin 	"Qc",		"Ql",		"Qo",		"Qq",
35261d06d6bSBaptiste Daroussin 	"Re",		"Rs",		"Sc",		"So",
35361d06d6bSBaptiste Daroussin 	"Sq",		"Sm",		"Sx",		"Sy",
35461d06d6bSBaptiste Daroussin 	"Tn",		"Ux",		"Xc",		"Xo",
35561d06d6bSBaptiste Daroussin 	"Fo",		"Fc",		"Oo",		"Oc",
35661d06d6bSBaptiste Daroussin 	"Bk",		"Ek",		"Bt",		"Hf",
35761d06d6bSBaptiste Daroussin 	"Fr",		"Ud",		"Lb",		"Lp",
35861d06d6bSBaptiste Daroussin 	"Lk",		"Mt",		"Brq",		"Bro",
35961d06d6bSBaptiste Daroussin 	"Brc",		"%C",		"Es",		"En",
36061d06d6bSBaptiste Daroussin 	"Dx",		"%Q",		"%U",		"Ta",
361*6d38604fSBaptiste Daroussin 	"Tg",		NULL,
36261d06d6bSBaptiste Daroussin 	"TH",		"SH",		"SS",		"TP",
3637295610fSBaptiste Daroussin 	"TQ",
36461d06d6bSBaptiste Daroussin 	"LP",		"PP",		"P",		"IP",
36561d06d6bSBaptiste Daroussin 	"HP",		"SM",		"SB",		"BI",
36661d06d6bSBaptiste Daroussin 	"IB",		"BR",		"RB",		"R",
36761d06d6bSBaptiste Daroussin 	"B",		"I",		"IR",		"RI",
36861d06d6bSBaptiste Daroussin 	"RE",		"RS",		"DT",		"UC",
36961d06d6bSBaptiste Daroussin 	"PD",		"AT",		"in",
3707295610fSBaptiste Daroussin 	"SY",		"YS",		"OP",
3717295610fSBaptiste Daroussin 	"EX",		"EE",		"UR",
37261d06d6bSBaptiste Daroussin 	"UE",		"MT",		"ME",		NULL
37361d06d6bSBaptiste Daroussin };
37461d06d6bSBaptiste Daroussin const	char *const *roff_name = __roff_name;
37561d06d6bSBaptiste Daroussin 
37661d06d6bSBaptiste Daroussin static	struct roffmac	 roffs[TOKEN_NONE] = {
3777295610fSBaptiste Daroussin 	{ roff_noarg, NULL, NULL, 0 },  /* br */
37861d06d6bSBaptiste Daroussin 	{ roff_onearg, NULL, NULL, 0 },  /* ce */
3797295610fSBaptiste Daroussin 	{ roff_noarg, NULL, NULL, 0 },  /* fi */
38061d06d6bSBaptiste Daroussin 	{ roff_onearg, NULL, NULL, 0 },  /* ft */
38161d06d6bSBaptiste Daroussin 	{ roff_onearg, NULL, NULL, 0 },  /* ll */
38261d06d6bSBaptiste Daroussin 	{ roff_onearg, NULL, NULL, 0 },  /* mc */
3837295610fSBaptiste Daroussin 	{ roff_noarg, NULL, NULL, 0 },  /* nf */
38461d06d6bSBaptiste Daroussin 	{ roff_onearg, NULL, NULL, 0 },  /* po */
38561d06d6bSBaptiste Daroussin 	{ roff_onearg, NULL, NULL, 0 },  /* rj */
38661d06d6bSBaptiste Daroussin 	{ roff_onearg, NULL, NULL, 0 },  /* sp */
38761d06d6bSBaptiste Daroussin 	{ roff_manyarg, NULL, NULL, 0 },  /* ta */
38861d06d6bSBaptiste Daroussin 	{ roff_onearg, NULL, NULL, 0 },  /* ti */
38961d06d6bSBaptiste Daroussin 	{ NULL, NULL, NULL, 0 },  /* ROFF_MAX */
39061d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* ab */
39161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* ad */
39261d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* af */
39361d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* aln */
39461d06d6bSBaptiste Daroussin 	{ roff_als, NULL, NULL, 0 },  /* als */
39561d06d6bSBaptiste Daroussin 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* am */
39661d06d6bSBaptiste Daroussin 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* am1 */
39761d06d6bSBaptiste Daroussin 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* ami */
39861d06d6bSBaptiste Daroussin 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* ami1 */
39961d06d6bSBaptiste Daroussin 	{ roff_ds, NULL, NULL, 0 },  /* as */
40061d06d6bSBaptiste Daroussin 	{ roff_ds, NULL, NULL, 0 },  /* as1 */
40161d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* asciify */
40261d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* backtrace */
40361d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* bd */
40461d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* bleedat */
40561d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* blm */
40661d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* box */
40761d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* boxa */
40861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* bp */
40961d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* BP */
41045a5aec3SBaptiste Daroussin 	{ roff_break, NULL, NULL, 0 },  /* break */
41161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* breakchar */
41261d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* brnl */
4137295610fSBaptiste Daroussin 	{ roff_noarg, NULL, NULL, 0 },  /* brp */
41461d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* brpnl */
41561d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* c2 */
41661d06d6bSBaptiste Daroussin 	{ roff_cc, NULL, NULL, 0 },  /* cc */
41761d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* cf */
41861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* cflags */
41961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* ch */
4207295610fSBaptiste Daroussin 	{ roff_char, NULL, NULL, 0 },  /* char */
42161d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* chop */
42261d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* class */
42361d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* close */
42461d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* CL */
42561d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* color */
42661d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* composite */
42761d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* continue */
42861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* cp */
42961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* cropat */
43061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* cs */
43161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* cu */
43261d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* da */
43361d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* dch */
43461d06d6bSBaptiste Daroussin 	{ roff_Dd, NULL, NULL, 0 },  /* Dd */
43561d06d6bSBaptiste Daroussin 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* de */
43661d06d6bSBaptiste Daroussin 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* de1 */
43761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* defcolor */
43861d06d6bSBaptiste Daroussin 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* dei */
43961d06d6bSBaptiste Daroussin 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* dei1 */
44061d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* device */
44161d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* devicem */
44261d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* di */
44361d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* do */
44461d06d6bSBaptiste Daroussin 	{ roff_ds, NULL, NULL, 0 },  /* ds */
44561d06d6bSBaptiste Daroussin 	{ roff_ds, NULL, NULL, 0 },  /* ds1 */
44661d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* dwh */
44761d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* dt */
44861d06d6bSBaptiste Daroussin 	{ roff_ec, NULL, NULL, 0 },  /* ec */
44961d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* ecr */
45061d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* ecs */
45161d06d6bSBaptiste Daroussin 	{ roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },  /* el */
45261d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* em */
45361d06d6bSBaptiste Daroussin 	{ roff_EN, NULL, NULL, 0 },  /* EN */
45461d06d6bSBaptiste Daroussin 	{ roff_eo, NULL, NULL, 0 },  /* eo */
45561d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* EP */
45661d06d6bSBaptiste Daroussin 	{ roff_EQ, NULL, NULL, 0 },  /* EQ */
45761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* errprint */
45861d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* ev */
45961d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* evc */
46061d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* ex */
46161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* fallback */
46261d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* fam */
46361d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* fc */
46461d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* fchar */
46561d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* fcolor */
46661d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* fdeferlig */
46761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* feature */
46861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* fkern */
46961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* fl */
47061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* flig */
47161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* fp */
47261d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* fps */
47361d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* fschar */
47461d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* fspacewidth */
47561d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* fspecial */
47661d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* ftr */
47761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* fzoom */
47861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* gcolor */
47961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hc */
48061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hcode */
48161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hidechar */
48261d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hla */
48361d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hlm */
48461d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hpf */
48561d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hpfa */
48661d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hpfcode */
48761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hw */
48861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hy */
48961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hylang */
49061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hylen */
49161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hym */
49261d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hypp */
49361d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* hys */
49461d06d6bSBaptiste Daroussin 	{ roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },  /* ie */
49561d06d6bSBaptiste Daroussin 	{ roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },  /* if */
49661d06d6bSBaptiste Daroussin 	{ roff_block, roff_block_text, roff_block_sub, 0 },  /* ig */
49761d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* index */
49861d06d6bSBaptiste Daroussin 	{ roff_it, NULL, NULL, 0 },  /* it */
49961d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* itc */
50061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* IX */
50161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* kern */
50261d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* kernafter */
50361d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* kernbefore */
50461d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* kernpair */
50561d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* lc */
50661d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* lc_ctype */
50761d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* lds */
50861d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* length */
50961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* letadj */
51061d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* lf */
51161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* lg */
51261d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* lhang */
51361d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* linetabs */
51461d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* lnr */
51561d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* lnrf */
51661d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* lpfx */
51761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* ls */
51861d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* lsm */
51961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* lt */
52061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* mediasize */
52161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* minss */
52261d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* mk */
52361d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* mso */
52461d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* na */
52561d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* ne */
52661d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* nh */
52761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* nhychar */
52861d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* nm */
52961d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* nn */
5307295610fSBaptiste Daroussin 	{ roff_nop, NULL, NULL, 0 },  /* nop */
53161d06d6bSBaptiste Daroussin 	{ roff_nr, NULL, NULL, 0 },  /* nr */
53261d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* nrf */
53361d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* nroff */
53461d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* ns */
53561d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* nx */
53661d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* open */
53761d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* opena */
53861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* os */
53961d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* output */
54061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* padj */
54161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* papersize */
54261d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* pc */
54361d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* pev */
54461d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* pi */
54561d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* PI */
54661d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* pl */
54761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* pm */
54861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* pn */
54961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* pnr */
55061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* ps */
55161d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* psbb */
55261d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* pshape */
55361d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* pso */
55461d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* ptr */
55561d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* pvs */
55661d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* rchar */
55761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* rd */
55861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* recursionlimit */
5597295610fSBaptiste Daroussin 	{ roff_return, NULL, NULL, 0 },  /* return */
56061d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* rfschar */
56161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* rhang */
56261d06d6bSBaptiste Daroussin 	{ roff_rm, NULL, NULL, 0 },  /* rm */
56361d06d6bSBaptiste Daroussin 	{ roff_rn, NULL, NULL, 0 },  /* rn */
56461d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* rnn */
56561d06d6bSBaptiste Daroussin 	{ roff_rr, NULL, NULL, 0 },  /* rr */
56661d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* rs */
56761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* rt */
56861d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* schar */
56961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* sentchar */
57061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* shc */
5717295610fSBaptiste Daroussin 	{ roff_shift, NULL, NULL, 0 },  /* shift */
57261d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* sizes */
57361d06d6bSBaptiste Daroussin 	{ roff_so, NULL, NULL, 0 },  /* so */
57461d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* spacewidth */
57561d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* special */
57661d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* spreadwarn */
57761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* ss */
57861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* sty */
57961d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* substring */
58061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* sv */
58161d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* sy */
58261d06d6bSBaptiste Daroussin 	{ roff_T_, NULL, NULL, 0 },  /* T& */
58361d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* tc */
58461d06d6bSBaptiste Daroussin 	{ roff_TE, NULL, NULL, 0 },  /* TE */
58561d06d6bSBaptiste Daroussin 	{ roff_Dd, NULL, NULL, 0 },  /* TH */
58661d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* tkf */
58761d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* tl */
58861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* tm */
58961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* tm1 */
59061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* tmc */
59161d06d6bSBaptiste Daroussin 	{ roff_tr, NULL, NULL, 0 },  /* tr */
59261d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* track */
59361d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* transchar */
59461d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* trf */
59561d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* trimat */
59661d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* trin */
59761d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* trnt */
59861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* troff */
59961d06d6bSBaptiste Daroussin 	{ roff_TS, NULL, NULL, 0 },  /* TS */
60061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* uf */
60161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* ul */
60261d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* unformat */
60361d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* unwatch */
60461d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* unwatchn */
60561d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* vpt */
60661d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* vs */
60761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* warn */
60861d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* warnscale */
60961d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* watch */
61061d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* watchlength */
61161d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* watchn */
61261d06d6bSBaptiste Daroussin 	{ roff_unsupp, NULL, NULL, 0 },  /* wh */
6137295610fSBaptiste Daroussin 	{ roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /*while*/
61461d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* write */
61561d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* writec */
61661d06d6bSBaptiste Daroussin 	{ roff_insec, NULL, NULL, 0 },  /* writem */
61761d06d6bSBaptiste Daroussin 	{ roff_line_ignore, NULL, NULL, 0 },  /* xflag */
61861d06d6bSBaptiste Daroussin 	{ roff_cblock, NULL, NULL, 0 },  /* . */
61961d06d6bSBaptiste Daroussin 	{ roff_renamed, NULL, NULL, 0 },
62061d06d6bSBaptiste Daroussin 	{ roff_userdef, NULL, NULL, 0 }
62161d06d6bSBaptiste Daroussin };
62261d06d6bSBaptiste Daroussin 
62361d06d6bSBaptiste Daroussin /* Array of injected predefined strings. */
62461d06d6bSBaptiste Daroussin #define	PREDEFS_MAX	 38
62561d06d6bSBaptiste Daroussin static	const struct predef predefs[PREDEFS_MAX] = {
62661d06d6bSBaptiste Daroussin #include "predefs.in"
62761d06d6bSBaptiste Daroussin };
62861d06d6bSBaptiste Daroussin 
62961d06d6bSBaptiste Daroussin static	int	 roffce_lines;	/* number of input lines to center */
63061d06d6bSBaptiste Daroussin static	struct roff_node *roffce_node;  /* active request */
63161d06d6bSBaptiste Daroussin static	int	 roffit_lines;  /* number of lines to delay */
63261d06d6bSBaptiste Daroussin static	char	*roffit_macro;  /* nil-terminated macro line */
63361d06d6bSBaptiste Daroussin 
63461d06d6bSBaptiste Daroussin 
63561d06d6bSBaptiste Daroussin /* --- request table ------------------------------------------------------ */
63661d06d6bSBaptiste Daroussin 
63761d06d6bSBaptiste Daroussin struct ohash *
roffhash_alloc(enum roff_tok mintok,enum roff_tok maxtok)63861d06d6bSBaptiste Daroussin roffhash_alloc(enum roff_tok mintok, enum roff_tok maxtok)
63961d06d6bSBaptiste Daroussin {
64061d06d6bSBaptiste Daroussin 	struct ohash	*htab;
64161d06d6bSBaptiste Daroussin 	struct roffreq	*req;
64261d06d6bSBaptiste Daroussin 	enum roff_tok	 tok;
64361d06d6bSBaptiste Daroussin 	size_t		 sz;
64461d06d6bSBaptiste Daroussin 	unsigned int	 slot;
64561d06d6bSBaptiste Daroussin 
64661d06d6bSBaptiste Daroussin 	htab = mandoc_malloc(sizeof(*htab));
64761d06d6bSBaptiste Daroussin 	mandoc_ohash_init(htab, 8, offsetof(struct roffreq, name));
64861d06d6bSBaptiste Daroussin 
64961d06d6bSBaptiste Daroussin 	for (tok = mintok; tok < maxtok; tok++) {
65061d06d6bSBaptiste Daroussin 		if (roff_name[tok] == NULL)
65161d06d6bSBaptiste Daroussin 			continue;
65261d06d6bSBaptiste Daroussin 		sz = strlen(roff_name[tok]);
65361d06d6bSBaptiste Daroussin 		req = mandoc_malloc(sizeof(*req) + sz + 1);
65461d06d6bSBaptiste Daroussin 		req->tok = tok;
65561d06d6bSBaptiste Daroussin 		memcpy(req->name, roff_name[tok], sz + 1);
65661d06d6bSBaptiste Daroussin 		slot = ohash_qlookup(htab, req->name);
65761d06d6bSBaptiste Daroussin 		ohash_insert(htab, slot, req);
65861d06d6bSBaptiste Daroussin 	}
65961d06d6bSBaptiste Daroussin 	return htab;
66061d06d6bSBaptiste Daroussin }
66161d06d6bSBaptiste Daroussin 
66261d06d6bSBaptiste Daroussin void
roffhash_free(struct ohash * htab)66361d06d6bSBaptiste Daroussin roffhash_free(struct ohash *htab)
66461d06d6bSBaptiste Daroussin {
66561d06d6bSBaptiste Daroussin 	struct roffreq	*req;
66661d06d6bSBaptiste Daroussin 	unsigned int	 slot;
66761d06d6bSBaptiste Daroussin 
66861d06d6bSBaptiste Daroussin 	if (htab == NULL)
66961d06d6bSBaptiste Daroussin 		return;
67061d06d6bSBaptiste Daroussin 	for (req = ohash_first(htab, &slot); req != NULL;
67161d06d6bSBaptiste Daroussin 	     req = ohash_next(htab, &slot))
67261d06d6bSBaptiste Daroussin 		free(req);
67361d06d6bSBaptiste Daroussin 	ohash_delete(htab);
67461d06d6bSBaptiste Daroussin 	free(htab);
67561d06d6bSBaptiste Daroussin }
67661d06d6bSBaptiste Daroussin 
67761d06d6bSBaptiste Daroussin enum roff_tok
roffhash_find(struct ohash * htab,const char * name,size_t sz)67861d06d6bSBaptiste Daroussin roffhash_find(struct ohash *htab, const char *name, size_t sz)
67961d06d6bSBaptiste Daroussin {
68061d06d6bSBaptiste Daroussin 	struct roffreq	*req;
68161d06d6bSBaptiste Daroussin 	const char	*end;
68261d06d6bSBaptiste Daroussin 
68361d06d6bSBaptiste Daroussin 	if (sz) {
68461d06d6bSBaptiste Daroussin 		end = name + sz;
68561d06d6bSBaptiste Daroussin 		req = ohash_find(htab, ohash_qlookupi(htab, name, &end));
68661d06d6bSBaptiste Daroussin 	} else
68761d06d6bSBaptiste Daroussin 		req = ohash_find(htab, ohash_qlookup(htab, name));
68861d06d6bSBaptiste Daroussin 	return req == NULL ? TOKEN_NONE : req->tok;
68961d06d6bSBaptiste Daroussin }
69061d06d6bSBaptiste Daroussin 
69161d06d6bSBaptiste Daroussin /* --- stack of request blocks -------------------------------------------- */
69261d06d6bSBaptiste Daroussin 
69361d06d6bSBaptiste Daroussin /*
69461d06d6bSBaptiste Daroussin  * Pop the current node off of the stack of roff instructions currently
69545a5aec3SBaptiste Daroussin  * pending.  Return 1 if it is a loop or 0 otherwise.
69661d06d6bSBaptiste Daroussin  */
6977295610fSBaptiste Daroussin static int
roffnode_pop(struct roff * r)69861d06d6bSBaptiste Daroussin roffnode_pop(struct roff *r)
69961d06d6bSBaptiste Daroussin {
70061d06d6bSBaptiste Daroussin 	struct roffnode	*p;
7017295610fSBaptiste Daroussin 	int		 inloop;
70261d06d6bSBaptiste Daroussin 
70361d06d6bSBaptiste Daroussin 	p = r->last;
7047295610fSBaptiste Daroussin 	inloop = p->tok == ROFF_while;
7057295610fSBaptiste Daroussin 	r->last = p->parent;
70661d06d6bSBaptiste Daroussin 	free(p->name);
70761d06d6bSBaptiste Daroussin 	free(p->end);
70861d06d6bSBaptiste Daroussin 	free(p);
7097295610fSBaptiste Daroussin 	return inloop;
71061d06d6bSBaptiste Daroussin }
71161d06d6bSBaptiste Daroussin 
71261d06d6bSBaptiste Daroussin /*
71361d06d6bSBaptiste Daroussin  * Push a roff node onto the instruction stack.  This must later be
71461d06d6bSBaptiste Daroussin  * removed with roffnode_pop().
71561d06d6bSBaptiste Daroussin  */
71661d06d6bSBaptiste Daroussin static void
roffnode_push(struct roff * r,enum roff_tok tok,const char * name,int line,int col)71761d06d6bSBaptiste Daroussin roffnode_push(struct roff *r, enum roff_tok tok, const char *name,
71861d06d6bSBaptiste Daroussin 		int line, int col)
71961d06d6bSBaptiste Daroussin {
72061d06d6bSBaptiste Daroussin 	struct roffnode	*p;
72161d06d6bSBaptiste Daroussin 
72261d06d6bSBaptiste Daroussin 	p = mandoc_calloc(1, sizeof(struct roffnode));
72361d06d6bSBaptiste Daroussin 	p->tok = tok;
72461d06d6bSBaptiste Daroussin 	if (name)
72561d06d6bSBaptiste Daroussin 		p->name = mandoc_strdup(name);
72661d06d6bSBaptiste Daroussin 	p->parent = r->last;
72761d06d6bSBaptiste Daroussin 	p->line = line;
72861d06d6bSBaptiste Daroussin 	p->col = col;
72961d06d6bSBaptiste Daroussin 	p->rule = p->parent ? p->parent->rule : 0;
73061d06d6bSBaptiste Daroussin 
73161d06d6bSBaptiste Daroussin 	r->last = p;
73261d06d6bSBaptiste Daroussin }
73361d06d6bSBaptiste Daroussin 
73461d06d6bSBaptiste Daroussin /* --- roff parser state data management ---------------------------------- */
73561d06d6bSBaptiste Daroussin 
73661d06d6bSBaptiste Daroussin static void
roff_free1(struct roff * r)73761d06d6bSBaptiste Daroussin roff_free1(struct roff *r)
73861d06d6bSBaptiste Daroussin {
73961d06d6bSBaptiste Daroussin 	int		 i;
74061d06d6bSBaptiste Daroussin 
7417295610fSBaptiste Daroussin 	tbl_free(r->first_tbl);
74261d06d6bSBaptiste Daroussin 	r->first_tbl = r->last_tbl = r->tbl = NULL;
74361d06d6bSBaptiste Daroussin 
74461d06d6bSBaptiste Daroussin 	eqn_free(r->last_eqn);
74561d06d6bSBaptiste Daroussin 	r->last_eqn = r->eqn = NULL;
74661d06d6bSBaptiste Daroussin 
7477295610fSBaptiste Daroussin 	while (r->mstackpos >= 0)
7487295610fSBaptiste Daroussin 		roff_userret(r);
7497295610fSBaptiste Daroussin 
75061d06d6bSBaptiste Daroussin 	while (r->last)
75161d06d6bSBaptiste Daroussin 		roffnode_pop(r);
75261d06d6bSBaptiste Daroussin 
75361d06d6bSBaptiste Daroussin 	free (r->rstack);
75461d06d6bSBaptiste Daroussin 	r->rstack = NULL;
75561d06d6bSBaptiste Daroussin 	r->rstacksz = 0;
75661d06d6bSBaptiste Daroussin 	r->rstackpos = -1;
75761d06d6bSBaptiste Daroussin 
75861d06d6bSBaptiste Daroussin 	roff_freereg(r->regtab);
75961d06d6bSBaptiste Daroussin 	r->regtab = NULL;
76061d06d6bSBaptiste Daroussin 
76161d06d6bSBaptiste Daroussin 	roff_freestr(r->strtab);
76261d06d6bSBaptiste Daroussin 	roff_freestr(r->rentab);
76361d06d6bSBaptiste Daroussin 	roff_freestr(r->xmbtab);
76461d06d6bSBaptiste Daroussin 	r->strtab = r->rentab = r->xmbtab = NULL;
76561d06d6bSBaptiste Daroussin 
76661d06d6bSBaptiste Daroussin 	if (r->xtab)
76761d06d6bSBaptiste Daroussin 		for (i = 0; i < 128; i++)
76861d06d6bSBaptiste Daroussin 			free(r->xtab[i].p);
76961d06d6bSBaptiste Daroussin 	free(r->xtab);
77061d06d6bSBaptiste Daroussin 	r->xtab = NULL;
77161d06d6bSBaptiste Daroussin }
77261d06d6bSBaptiste Daroussin 
77361d06d6bSBaptiste Daroussin void
roff_reset(struct roff * r)77461d06d6bSBaptiste Daroussin roff_reset(struct roff *r)
77561d06d6bSBaptiste Daroussin {
77661d06d6bSBaptiste Daroussin 	roff_free1(r);
777*6d38604fSBaptiste Daroussin 	r->options |= MPARSE_COMMENT;
77861d06d6bSBaptiste Daroussin 	r->format = r->options & (MPARSE_MDOC | MPARSE_MAN);
77961d06d6bSBaptiste Daroussin 	r->control = '\0';
78061d06d6bSBaptiste Daroussin 	r->escape = '\\';
78161d06d6bSBaptiste Daroussin 	roffce_lines = 0;
78261d06d6bSBaptiste Daroussin 	roffce_node = NULL;
78361d06d6bSBaptiste Daroussin 	roffit_lines = 0;
78461d06d6bSBaptiste Daroussin 	roffit_macro = NULL;
78561d06d6bSBaptiste Daroussin }
78661d06d6bSBaptiste Daroussin 
78761d06d6bSBaptiste Daroussin void
roff_free(struct roff * r)78861d06d6bSBaptiste Daroussin roff_free(struct roff *r)
78961d06d6bSBaptiste Daroussin {
7907295610fSBaptiste Daroussin 	int		 i;
7917295610fSBaptiste Daroussin 
79261d06d6bSBaptiste Daroussin 	roff_free1(r);
7937295610fSBaptiste Daroussin 	for (i = 0; i < r->mstacksz; i++)
7947295610fSBaptiste Daroussin 		free(r->mstack[i].argv);
7957295610fSBaptiste Daroussin 	free(r->mstack);
79661d06d6bSBaptiste Daroussin 	roffhash_free(r->reqtab);
79761d06d6bSBaptiste Daroussin 	free(r);
79861d06d6bSBaptiste Daroussin }
79961d06d6bSBaptiste Daroussin 
80061d06d6bSBaptiste Daroussin struct roff *
roff_alloc(int options)8017295610fSBaptiste Daroussin roff_alloc(int options)
80261d06d6bSBaptiste Daroussin {
80361d06d6bSBaptiste Daroussin 	struct roff	*r;
80461d06d6bSBaptiste Daroussin 
80561d06d6bSBaptiste Daroussin 	r = mandoc_calloc(1, sizeof(struct roff));
80661d06d6bSBaptiste Daroussin 	r->reqtab = roffhash_alloc(0, ROFF_RENAMED);
807*6d38604fSBaptiste Daroussin 	r->options = options | MPARSE_COMMENT;
80861d06d6bSBaptiste Daroussin 	r->format = options & (MPARSE_MDOC | MPARSE_MAN);
8097295610fSBaptiste Daroussin 	r->mstackpos = -1;
81061d06d6bSBaptiste Daroussin 	r->rstackpos = -1;
81161d06d6bSBaptiste Daroussin 	r->escape = '\\';
81261d06d6bSBaptiste Daroussin 	return r;
81361d06d6bSBaptiste Daroussin }
81461d06d6bSBaptiste Daroussin 
81561d06d6bSBaptiste Daroussin /* --- syntax tree state data management ---------------------------------- */
81661d06d6bSBaptiste Daroussin 
81761d06d6bSBaptiste Daroussin static void
roff_man_free1(struct roff_man * man)81861d06d6bSBaptiste Daroussin roff_man_free1(struct roff_man *man)
81961d06d6bSBaptiste Daroussin {
8207295610fSBaptiste Daroussin 	if (man->meta.first != NULL)
8217295610fSBaptiste Daroussin 		roff_node_delete(man, man->meta.first);
82261d06d6bSBaptiste Daroussin 	free(man->meta.msec);
82361d06d6bSBaptiste Daroussin 	free(man->meta.vol);
82461d06d6bSBaptiste Daroussin 	free(man->meta.os);
82561d06d6bSBaptiste Daroussin 	free(man->meta.arch);
82661d06d6bSBaptiste Daroussin 	free(man->meta.title);
82761d06d6bSBaptiste Daroussin 	free(man->meta.name);
82861d06d6bSBaptiste Daroussin 	free(man->meta.date);
8297295610fSBaptiste Daroussin 	free(man->meta.sodest);
8307295610fSBaptiste Daroussin }
8317295610fSBaptiste Daroussin 
8327295610fSBaptiste Daroussin void
roff_state_reset(struct roff_man * man)8337295610fSBaptiste Daroussin roff_state_reset(struct roff_man *man)
8347295610fSBaptiste Daroussin {
8357295610fSBaptiste Daroussin 	man->last = man->meta.first;
8367295610fSBaptiste Daroussin 	man->last_es = NULL;
8377295610fSBaptiste Daroussin 	man->flags = 0;
8387295610fSBaptiste Daroussin 	man->lastsec = man->lastnamed = SEC_NONE;
8397295610fSBaptiste Daroussin 	man->next = ROFF_NEXT_CHILD;
8407295610fSBaptiste Daroussin 	roff_setreg(man->roff, "nS", 0, '=');
84161d06d6bSBaptiste Daroussin }
84261d06d6bSBaptiste Daroussin 
84361d06d6bSBaptiste Daroussin static void
roff_man_alloc1(struct roff_man * man)84461d06d6bSBaptiste Daroussin roff_man_alloc1(struct roff_man *man)
84561d06d6bSBaptiste Daroussin {
84661d06d6bSBaptiste Daroussin 	memset(&man->meta, 0, sizeof(man->meta));
8477295610fSBaptiste Daroussin 	man->meta.first = mandoc_calloc(1, sizeof(*man->meta.first));
8487295610fSBaptiste Daroussin 	man->meta.first->type = ROFFT_ROOT;
8497295610fSBaptiste Daroussin 	man->meta.macroset = MACROSET_NONE;
8507295610fSBaptiste Daroussin 	roff_state_reset(man);
85161d06d6bSBaptiste Daroussin }
85261d06d6bSBaptiste Daroussin 
85361d06d6bSBaptiste Daroussin void
roff_man_reset(struct roff_man * man)85461d06d6bSBaptiste Daroussin roff_man_reset(struct roff_man *man)
85561d06d6bSBaptiste Daroussin {
85661d06d6bSBaptiste Daroussin 	roff_man_free1(man);
85761d06d6bSBaptiste Daroussin 	roff_man_alloc1(man);
85861d06d6bSBaptiste Daroussin }
85961d06d6bSBaptiste Daroussin 
86061d06d6bSBaptiste Daroussin void
roff_man_free(struct roff_man * man)86161d06d6bSBaptiste Daroussin roff_man_free(struct roff_man *man)
86261d06d6bSBaptiste Daroussin {
86361d06d6bSBaptiste Daroussin 	roff_man_free1(man);
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);
1107*6d38604fSBaptiste 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 
1121*6d38604fSBaptiste Daroussin int
roff_node_transparent(struct roff_node * n)1122*6d38604fSBaptiste Daroussin roff_node_transparent(struct roff_node *n)
1123*6d38604fSBaptiste Daroussin {
1124*6d38604fSBaptiste Daroussin 	if (n == NULL)
1125*6d38604fSBaptiste Daroussin 		return 0;
1126*6d38604fSBaptiste Daroussin 	if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
1127*6d38604fSBaptiste Daroussin 		return 1;
1128*6d38604fSBaptiste Daroussin 	return roff_tok_transparent(n->tok);
1129*6d38604fSBaptiste Daroussin }
1130*6d38604fSBaptiste Daroussin 
1131*6d38604fSBaptiste Daroussin int
roff_tok_transparent(enum roff_tok tok)1132*6d38604fSBaptiste Daroussin roff_tok_transparent(enum roff_tok tok)
1133*6d38604fSBaptiste Daroussin {
1134*6d38604fSBaptiste Daroussin 	switch (tok) {
1135*6d38604fSBaptiste Daroussin 	case ROFF_ft:
1136*6d38604fSBaptiste Daroussin 	case ROFF_ll:
1137*6d38604fSBaptiste Daroussin 	case ROFF_mc:
1138*6d38604fSBaptiste Daroussin 	case ROFF_po:
1139*6d38604fSBaptiste Daroussin 	case ROFF_ta:
1140*6d38604fSBaptiste Daroussin 	case MDOC_Db:
1141*6d38604fSBaptiste Daroussin 	case MDOC_Es:
1142*6d38604fSBaptiste Daroussin 	case MDOC_Sm:
1143*6d38604fSBaptiste Daroussin 	case MDOC_Tg:
1144*6d38604fSBaptiste Daroussin 	case MAN_DT:
1145*6d38604fSBaptiste Daroussin 	case MAN_UC:
1146*6d38604fSBaptiste Daroussin 	case MAN_PD:
1147*6d38604fSBaptiste Daroussin 	case MAN_AT:
1148*6d38604fSBaptiste Daroussin 		return 1;
1149*6d38604fSBaptiste Daroussin 	default:
1150*6d38604fSBaptiste Daroussin 		return 0;
1151*6d38604fSBaptiste Daroussin 	}
1152*6d38604fSBaptiste Daroussin }
1153*6d38604fSBaptiste Daroussin 
1154*6d38604fSBaptiste Daroussin struct roff_node *
roff_node_child(struct roff_node * n)1155*6d38604fSBaptiste Daroussin roff_node_child(struct roff_node *n)
1156*6d38604fSBaptiste Daroussin {
1157*6d38604fSBaptiste Daroussin 	for (n = n->child; roff_node_transparent(n); n = n->next)
1158*6d38604fSBaptiste Daroussin 		continue;
1159*6d38604fSBaptiste Daroussin 	return n;
1160*6d38604fSBaptiste Daroussin }
1161*6d38604fSBaptiste Daroussin 
1162*6d38604fSBaptiste Daroussin struct roff_node *
roff_node_prev(struct roff_node * n)1163*6d38604fSBaptiste Daroussin roff_node_prev(struct roff_node *n)
1164*6d38604fSBaptiste Daroussin {
1165*6d38604fSBaptiste Daroussin 	do {
1166*6d38604fSBaptiste Daroussin 		n = n->prev;
1167*6d38604fSBaptiste Daroussin 	} while (roff_node_transparent(n));
1168*6d38604fSBaptiste Daroussin 	return n;
1169*6d38604fSBaptiste Daroussin }
1170*6d38604fSBaptiste Daroussin 
1171*6d38604fSBaptiste Daroussin struct roff_node *
roff_node_next(struct roff_node * n)1172*6d38604fSBaptiste Daroussin roff_node_next(struct roff_node *n)
1173*6d38604fSBaptiste Daroussin {
1174*6d38604fSBaptiste Daroussin 	do {
1175*6d38604fSBaptiste Daroussin 		n = n->next;
1176*6d38604fSBaptiste Daroussin 	} while (roff_node_transparent(n));
1177*6d38604fSBaptiste Daroussin 	return n;
1178*6d38604fSBaptiste Daroussin }
1179*6d38604fSBaptiste 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 
1186*6d38604fSBaptiste 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 /*
12327295610fSBaptiste Daroussin  * In the current line, expand escape sequences that produce parsable
12337295610fSBaptiste Daroussin  * input text.  Also check the syntax of the remaining escape sequences,
12347295610fSBaptiste Daroussin  * which typically produce output glyphs or change formatter state.
123561d06d6bSBaptiste Daroussin  */
12367295610fSBaptiste Daroussin static int
roff_expand(struct roff * r,struct buf * buf,int ln,int pos,char newesc)12377295610fSBaptiste Daroussin roff_expand(struct roff *r, struct buf *buf, int ln, int pos, char newesc)
123861d06d6bSBaptiste Daroussin {
12397295610fSBaptiste Daroussin 	struct mctx	*ctx;	/* current macro call context */
124061d06d6bSBaptiste Daroussin 	char		 ubuf[24]; /* buffer to print the number */
124161d06d6bSBaptiste Daroussin 	struct roff_node *n;	/* used for header comments */
124261d06d6bSBaptiste Daroussin 	const char	*start;	/* start of the string to process */
124361d06d6bSBaptiste Daroussin 	char		*stesc;	/* start of an escape sequence ('\\') */
12447295610fSBaptiste Daroussin 	const char	*esct;	/* type of esccape sequence */
124561d06d6bSBaptiste Daroussin 	char		*ep;	/* end of comment string */
124661d06d6bSBaptiste Daroussin 	const char	*stnam;	/* start of the name, after "[(*" */
124761d06d6bSBaptiste Daroussin 	const char	*cp;	/* end of the name, e.g. before ']' */
124861d06d6bSBaptiste Daroussin 	const char	*res;	/* the string to be substituted */
124961d06d6bSBaptiste Daroussin 	char		*nbuf;	/* new buffer to copy buf->buf to */
125061d06d6bSBaptiste Daroussin 	size_t		 maxl;  /* expected length of the escape name */
125161d06d6bSBaptiste Daroussin 	size_t		 naml;	/* actual length of the escape name */
12527295610fSBaptiste Daroussin 	size_t		 asz;	/* length of the replacement */
12537295610fSBaptiste Daroussin 	size_t		 rsz;	/* length of the rest of the string */
125461d06d6bSBaptiste Daroussin 	int		 inaml;	/* length returned from mandoc_escape() */
125561d06d6bSBaptiste Daroussin 	int		 expand_count;	/* to avoid infinite loops */
125661d06d6bSBaptiste Daroussin 	int		 npos;	/* position in numeric expression */
125761d06d6bSBaptiste Daroussin 	int		 arg_complete; /* argument not interrupted by eol */
12587295610fSBaptiste Daroussin 	int		 quote_args; /* true for \\$@, false for \\$* */
125961d06d6bSBaptiste Daroussin 	int		 done;	/* no more input available */
126061d06d6bSBaptiste Daroussin 	int		 deftype; /* type of definition to paste */
126161d06d6bSBaptiste Daroussin 	int		 rcsid;	/* kind of RCS id seen */
12627295610fSBaptiste Daroussin 	enum mandocerr	 err;	/* for escape sequence problems */
126361d06d6bSBaptiste Daroussin 	char		 sign;	/* increment number register */
126461d06d6bSBaptiste Daroussin 	char		 term;	/* character terminating the escape */
126561d06d6bSBaptiste Daroussin 
126661d06d6bSBaptiste Daroussin 	/* Search forward for comments. */
126761d06d6bSBaptiste Daroussin 
126861d06d6bSBaptiste Daroussin 	done = 0;
126961d06d6bSBaptiste Daroussin 	start = buf->buf + pos;
127061d06d6bSBaptiste Daroussin 	for (stesc = buf->buf + pos; *stesc != '\0'; stesc++) {
12717295610fSBaptiste Daroussin 		if (stesc[0] != newesc || stesc[1] == '\0')
127261d06d6bSBaptiste Daroussin 			continue;
127361d06d6bSBaptiste Daroussin 		stesc++;
127461d06d6bSBaptiste Daroussin 		if (*stesc != '"' && *stesc != '#')
127561d06d6bSBaptiste Daroussin 			continue;
127661d06d6bSBaptiste Daroussin 
127761d06d6bSBaptiste Daroussin 		/* Comment found, look for RCS id. */
127861d06d6bSBaptiste Daroussin 
127961d06d6bSBaptiste Daroussin 		rcsid = 0;
128061d06d6bSBaptiste Daroussin 		if ((cp = strstr(stesc, "$" "OpenBSD")) != NULL) {
128161d06d6bSBaptiste Daroussin 			rcsid = 1 << MANDOC_OS_OPENBSD;
128261d06d6bSBaptiste Daroussin 			cp += 8;
128361d06d6bSBaptiste Daroussin 		} else if ((cp = strstr(stesc, "$" "NetBSD")) != NULL) {
128461d06d6bSBaptiste Daroussin 			rcsid = 1 << MANDOC_OS_NETBSD;
128561d06d6bSBaptiste Daroussin 			cp += 7;
128661d06d6bSBaptiste Daroussin 		}
128761d06d6bSBaptiste Daroussin 		if (cp != NULL &&
128861d06d6bSBaptiste Daroussin 		    isalnum((unsigned char)*cp) == 0 &&
128961d06d6bSBaptiste Daroussin 		    strchr(cp, '$') != NULL) {
129061d06d6bSBaptiste Daroussin 			if (r->man->meta.rcsids & rcsid)
12917295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_RCS_REP, ln,
12927295610fSBaptiste Daroussin 				    (int)(stesc - buf->buf) + 1,
12937295610fSBaptiste Daroussin 				    "%s", stesc + 1);
129461d06d6bSBaptiste Daroussin 			r->man->meta.rcsids |= rcsid;
129561d06d6bSBaptiste Daroussin 		}
129661d06d6bSBaptiste Daroussin 
129761d06d6bSBaptiste Daroussin 		/* Handle trailing whitespace. */
129861d06d6bSBaptiste Daroussin 
129961d06d6bSBaptiste Daroussin 		ep = strchr(stesc--, '\0') - 1;
130061d06d6bSBaptiste Daroussin 		if (*ep == '\n') {
130161d06d6bSBaptiste Daroussin 			done = 1;
130261d06d6bSBaptiste Daroussin 			ep--;
130361d06d6bSBaptiste Daroussin 		}
130461d06d6bSBaptiste Daroussin 		if (*ep == ' ' || *ep == '\t')
13057295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_SPACE_EOL,
13067295610fSBaptiste Daroussin 			    ln, (int)(ep - buf->buf), NULL);
130761d06d6bSBaptiste Daroussin 
130861d06d6bSBaptiste Daroussin 		/*
130961d06d6bSBaptiste Daroussin 		 * Save comments preceding the title macro
131061d06d6bSBaptiste Daroussin 		 * in the syntax tree.
131161d06d6bSBaptiste Daroussin 		 */
131261d06d6bSBaptiste Daroussin 
1313*6d38604fSBaptiste Daroussin 		if (newesc != ASCII_ESC && r->options & MPARSE_COMMENT) {
131461d06d6bSBaptiste Daroussin 			while (*ep == ' ' || *ep == '\t')
131561d06d6bSBaptiste Daroussin 				ep--;
131661d06d6bSBaptiste Daroussin 			ep[1] = '\0';
131761d06d6bSBaptiste Daroussin 			n = roff_node_alloc(r->man,
131861d06d6bSBaptiste Daroussin 			    ln, stesc + 1 - buf->buf,
131961d06d6bSBaptiste Daroussin 			    ROFFT_COMMENT, TOKEN_NONE);
132061d06d6bSBaptiste Daroussin 			n->string = mandoc_strdup(stesc + 2);
132161d06d6bSBaptiste Daroussin 			roff_node_append(r->man, n);
132261d06d6bSBaptiste Daroussin 			n->flags |= NODE_VALID | NODE_ENDED;
132361d06d6bSBaptiste Daroussin 			r->man->next = ROFF_NEXT_SIBLING;
132461d06d6bSBaptiste Daroussin 		}
132561d06d6bSBaptiste Daroussin 
13267295610fSBaptiste Daroussin 		/* Line continuation with comment. */
132761d06d6bSBaptiste Daroussin 
13287295610fSBaptiste Daroussin 		if (stesc[1] == '#') {
13297295610fSBaptiste Daroussin 			*stesc = '\0';
13307295610fSBaptiste Daroussin 			return ROFF_IGN | ROFF_APPEND;
13317295610fSBaptiste Daroussin 		}
13327295610fSBaptiste Daroussin 
13337295610fSBaptiste Daroussin 		/* Discard normal comments. */
13347295610fSBaptiste Daroussin 
13357295610fSBaptiste Daroussin 		while (stesc > start && stesc[-1] == ' ' &&
13367295610fSBaptiste Daroussin 		    (stesc == start + 1 || stesc[-2] != '\\'))
133761d06d6bSBaptiste Daroussin 			stesc--;
133861d06d6bSBaptiste Daroussin 		*stesc = '\0';
133961d06d6bSBaptiste Daroussin 		break;
134061d06d6bSBaptiste Daroussin 	}
134161d06d6bSBaptiste Daroussin 	if (stesc == start)
134261d06d6bSBaptiste Daroussin 		return ROFF_CONT;
134361d06d6bSBaptiste Daroussin 	stesc--;
134461d06d6bSBaptiste Daroussin 
134561d06d6bSBaptiste Daroussin 	/* Notice the end of the input. */
134661d06d6bSBaptiste Daroussin 
134761d06d6bSBaptiste Daroussin 	if (*stesc == '\n') {
134861d06d6bSBaptiste Daroussin 		*stesc-- = '\0';
134961d06d6bSBaptiste Daroussin 		done = 1;
135061d06d6bSBaptiste Daroussin 	}
135161d06d6bSBaptiste Daroussin 
135261d06d6bSBaptiste Daroussin 	expand_count = 0;
135361d06d6bSBaptiste Daroussin 	while (stesc >= start) {
13547295610fSBaptiste Daroussin 		if (*stesc != newesc) {
135561d06d6bSBaptiste Daroussin 
13567295610fSBaptiste Daroussin 			/*
13577295610fSBaptiste Daroussin 			 * If we have a non-standard escape character,
13587295610fSBaptiste Daroussin 			 * escape literal backslashes because all
13597295610fSBaptiste Daroussin 			 * processing in subsequent functions uses
13607295610fSBaptiste Daroussin 			 * the standard escaping rules.
13617295610fSBaptiste Daroussin 			 */
136261d06d6bSBaptiste Daroussin 
13637295610fSBaptiste Daroussin 			if (newesc != ASCII_ESC && *stesc == '\\') {
136461d06d6bSBaptiste Daroussin 				*stesc = '\0';
136561d06d6bSBaptiste Daroussin 				buf->sz = mandoc_asprintf(&nbuf, "%s\\e%s",
136661d06d6bSBaptiste Daroussin 				    buf->buf, stesc + 1) + 1;
136761d06d6bSBaptiste Daroussin 				start = nbuf + pos;
136861d06d6bSBaptiste Daroussin 				stesc = nbuf + (stesc - buf->buf);
136961d06d6bSBaptiste Daroussin 				free(buf->buf);
137061d06d6bSBaptiste Daroussin 				buf->buf = nbuf;
137161d06d6bSBaptiste Daroussin 			}
13727295610fSBaptiste Daroussin 
13737295610fSBaptiste Daroussin 			/* Search backwards for the next escape. */
13747295610fSBaptiste Daroussin 
137561d06d6bSBaptiste Daroussin 			stesc--;
137661d06d6bSBaptiste Daroussin 			continue;
137761d06d6bSBaptiste Daroussin 		}
137861d06d6bSBaptiste Daroussin 
137961d06d6bSBaptiste Daroussin 		/* If it is escaped, skip it. */
138061d06d6bSBaptiste Daroussin 
138161d06d6bSBaptiste Daroussin 		for (cp = stesc - 1; cp >= start; cp--)
138261d06d6bSBaptiste Daroussin 			if (*cp != r->escape)
138361d06d6bSBaptiste Daroussin 				break;
138461d06d6bSBaptiste Daroussin 
138561d06d6bSBaptiste Daroussin 		if ((stesc - cp) % 2 == 0) {
138661d06d6bSBaptiste Daroussin 			while (stesc > cp)
138761d06d6bSBaptiste Daroussin 				*stesc-- = '\\';
138861d06d6bSBaptiste Daroussin 			continue;
138961d06d6bSBaptiste Daroussin 		} else if (stesc[1] != '\0') {
139061d06d6bSBaptiste Daroussin 			*stesc = '\\';
139161d06d6bSBaptiste Daroussin 		} else {
139261d06d6bSBaptiste Daroussin 			*stesc-- = '\0';
139361d06d6bSBaptiste Daroussin 			if (done)
139461d06d6bSBaptiste Daroussin 				continue;
139561d06d6bSBaptiste Daroussin 			else
13967295610fSBaptiste Daroussin 				return ROFF_IGN | ROFF_APPEND;
139761d06d6bSBaptiste Daroussin 		}
139861d06d6bSBaptiste Daroussin 
139961d06d6bSBaptiste Daroussin 		/* Decide whether to expand or to check only. */
140061d06d6bSBaptiste Daroussin 
140161d06d6bSBaptiste Daroussin 		term = '\0';
140261d06d6bSBaptiste Daroussin 		cp = stesc + 1;
14037295610fSBaptiste Daroussin 		if (*cp == 'E')
14047295610fSBaptiste Daroussin 			cp++;
14057295610fSBaptiste Daroussin 		esct = cp;
14067295610fSBaptiste Daroussin 		switch (*esct) {
140761d06d6bSBaptiste Daroussin 		case '*':
14087295610fSBaptiste Daroussin 		case '$':
140961d06d6bSBaptiste Daroussin 			res = NULL;
141061d06d6bSBaptiste Daroussin 			break;
141161d06d6bSBaptiste Daroussin 		case 'B':
141261d06d6bSBaptiste Daroussin 		case 'w':
141361d06d6bSBaptiste Daroussin 			term = cp[1];
141461d06d6bSBaptiste Daroussin 			/* FALLTHROUGH */
141561d06d6bSBaptiste Daroussin 		case 'n':
141661d06d6bSBaptiste Daroussin 			sign = cp[1];
141761d06d6bSBaptiste Daroussin 			if (sign == '+' || sign == '-')
141861d06d6bSBaptiste Daroussin 				cp++;
141961d06d6bSBaptiste Daroussin 			res = ubuf;
142061d06d6bSBaptiste Daroussin 			break;
142161d06d6bSBaptiste Daroussin 		default:
14227295610fSBaptiste Daroussin 			err = MANDOCERR_OK;
14237295610fSBaptiste Daroussin 			switch(mandoc_escape(&cp, &stnam, &inaml)) {
14247295610fSBaptiste Daroussin 			case ESCAPE_SPECIAL:
14257295610fSBaptiste Daroussin 				if (mchars_spec2cp(stnam, inaml) >= 0)
14267295610fSBaptiste Daroussin 					break;
14277295610fSBaptiste Daroussin 				/* FALLTHROUGH */
14287295610fSBaptiste Daroussin 			case ESCAPE_ERROR:
14297295610fSBaptiste Daroussin 				err = MANDOCERR_ESC_BAD;
14307295610fSBaptiste Daroussin 				break;
14317295610fSBaptiste Daroussin 			case ESCAPE_UNDEF:
14327295610fSBaptiste Daroussin 				err = MANDOCERR_ESC_UNDEF;
14337295610fSBaptiste Daroussin 				break;
14347295610fSBaptiste Daroussin 			case ESCAPE_UNSUPP:
14357295610fSBaptiste Daroussin 				err = MANDOCERR_ESC_UNSUPP;
14367295610fSBaptiste Daroussin 				break;
14377295610fSBaptiste Daroussin 			default:
14387295610fSBaptiste Daroussin 				break;
14397295610fSBaptiste Daroussin 			}
14407295610fSBaptiste Daroussin 			if (err != MANDOCERR_OK)
14417295610fSBaptiste Daroussin 				mandoc_msg(err, ln, (int)(stesc - buf->buf),
144261d06d6bSBaptiste Daroussin 				    "%.*s", (int)(cp - stesc), stesc);
144361d06d6bSBaptiste Daroussin 			stesc--;
144461d06d6bSBaptiste Daroussin 			continue;
144561d06d6bSBaptiste Daroussin 		}
144661d06d6bSBaptiste Daroussin 
144761d06d6bSBaptiste Daroussin 		if (EXPAND_LIMIT < ++expand_count) {
14487295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_ROFFLOOP,
144961d06d6bSBaptiste Daroussin 			    ln, (int)(stesc - buf->buf), NULL);
145061d06d6bSBaptiste Daroussin 			return ROFF_IGN;
145161d06d6bSBaptiste Daroussin 		}
145261d06d6bSBaptiste Daroussin 
145361d06d6bSBaptiste Daroussin 		/*
145461d06d6bSBaptiste Daroussin 		 * The third character decides the length
145561d06d6bSBaptiste Daroussin 		 * of the name of the string or register.
145661d06d6bSBaptiste Daroussin 		 * Save a pointer to the name.
145761d06d6bSBaptiste Daroussin 		 */
145861d06d6bSBaptiste Daroussin 
145961d06d6bSBaptiste Daroussin 		if (term == '\0') {
146061d06d6bSBaptiste Daroussin 			switch (*++cp) {
146161d06d6bSBaptiste Daroussin 			case '\0':
146261d06d6bSBaptiste Daroussin 				maxl = 0;
146361d06d6bSBaptiste Daroussin 				break;
146461d06d6bSBaptiste Daroussin 			case '(':
146561d06d6bSBaptiste Daroussin 				cp++;
146661d06d6bSBaptiste Daroussin 				maxl = 2;
146761d06d6bSBaptiste Daroussin 				break;
146861d06d6bSBaptiste Daroussin 			case '[':
146961d06d6bSBaptiste Daroussin 				cp++;
147061d06d6bSBaptiste Daroussin 				term = ']';
147161d06d6bSBaptiste Daroussin 				maxl = 0;
147261d06d6bSBaptiste Daroussin 				break;
147361d06d6bSBaptiste Daroussin 			default:
147461d06d6bSBaptiste Daroussin 				maxl = 1;
147561d06d6bSBaptiste Daroussin 				break;
147661d06d6bSBaptiste Daroussin 			}
147761d06d6bSBaptiste Daroussin 		} else {
147861d06d6bSBaptiste Daroussin 			cp += 2;
147961d06d6bSBaptiste Daroussin 			maxl = 0;
148061d06d6bSBaptiste Daroussin 		}
148161d06d6bSBaptiste Daroussin 		stnam = cp;
148261d06d6bSBaptiste Daroussin 
148361d06d6bSBaptiste Daroussin 		/* Advance to the end of the name. */
148461d06d6bSBaptiste Daroussin 
148561d06d6bSBaptiste Daroussin 		naml = 0;
148661d06d6bSBaptiste Daroussin 		arg_complete = 1;
148761d06d6bSBaptiste Daroussin 		while (maxl == 0 || naml < maxl) {
148861d06d6bSBaptiste Daroussin 			if (*cp == '\0') {
14897295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_ESC_BAD, ln,
14907295610fSBaptiste Daroussin 				    (int)(stesc - buf->buf), "%s", stesc);
149161d06d6bSBaptiste Daroussin 				arg_complete = 0;
149261d06d6bSBaptiste Daroussin 				break;
149361d06d6bSBaptiste Daroussin 			}
149461d06d6bSBaptiste Daroussin 			if (maxl == 0 && *cp == term) {
149561d06d6bSBaptiste Daroussin 				cp++;
149661d06d6bSBaptiste Daroussin 				break;
149761d06d6bSBaptiste Daroussin 			}
14987295610fSBaptiste Daroussin 			if (*cp++ != '\\' || *esct != 'w') {
149961d06d6bSBaptiste Daroussin 				naml++;
150061d06d6bSBaptiste Daroussin 				continue;
150161d06d6bSBaptiste Daroussin 			}
150261d06d6bSBaptiste Daroussin 			switch (mandoc_escape(&cp, NULL, NULL)) {
150361d06d6bSBaptiste Daroussin 			case ESCAPE_SPECIAL:
150461d06d6bSBaptiste Daroussin 			case ESCAPE_UNICODE:
150561d06d6bSBaptiste Daroussin 			case ESCAPE_NUMBERED:
15067295610fSBaptiste Daroussin 			case ESCAPE_UNDEF:
150761d06d6bSBaptiste Daroussin 			case ESCAPE_OVERSTRIKE:
150861d06d6bSBaptiste Daroussin 				naml++;
150961d06d6bSBaptiste Daroussin 				break;
151061d06d6bSBaptiste Daroussin 			default:
151161d06d6bSBaptiste Daroussin 				break;
151261d06d6bSBaptiste Daroussin 			}
151361d06d6bSBaptiste Daroussin 		}
151461d06d6bSBaptiste Daroussin 
151561d06d6bSBaptiste Daroussin 		/*
151661d06d6bSBaptiste Daroussin 		 * Retrieve the replacement string; if it is
151761d06d6bSBaptiste Daroussin 		 * undefined, resume searching for escapes.
151861d06d6bSBaptiste Daroussin 		 */
151961d06d6bSBaptiste Daroussin 
15207295610fSBaptiste Daroussin 		switch (*esct) {
152161d06d6bSBaptiste Daroussin 		case '*':
152261d06d6bSBaptiste Daroussin 			if (arg_complete) {
152361d06d6bSBaptiste Daroussin 				deftype = ROFFDEF_USER | ROFFDEF_PRE;
152461d06d6bSBaptiste Daroussin 				res = roff_getstrn(r, stnam, naml, &deftype);
15257295610fSBaptiste Daroussin 
15267295610fSBaptiste Daroussin 				/*
15277295610fSBaptiste Daroussin 				 * If not overriden, let \*(.T
15287295610fSBaptiste Daroussin 				 * through to the formatters.
15297295610fSBaptiste Daroussin 				 */
15307295610fSBaptiste Daroussin 
15317295610fSBaptiste Daroussin 				if (res == NULL && naml == 2 &&
15327295610fSBaptiste Daroussin 				    stnam[0] == '.' && stnam[1] == 'T') {
15337295610fSBaptiste Daroussin 					roff_setstrn(&r->strtab,
15347295610fSBaptiste Daroussin 					    ".T", 2, NULL, 0, 0);
15357295610fSBaptiste Daroussin 					stesc--;
15367295610fSBaptiste Daroussin 					continue;
15377295610fSBaptiste Daroussin 				}
153861d06d6bSBaptiste Daroussin 			}
153961d06d6bSBaptiste Daroussin 			break;
15407295610fSBaptiste Daroussin 		case '$':
15417295610fSBaptiste Daroussin 			if (r->mstackpos < 0) {
15427295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_ARG_UNDEF, ln,
15437295610fSBaptiste Daroussin 				    (int)(stesc - buf->buf), "%.3s", stesc);
15447295610fSBaptiste Daroussin 				break;
15457295610fSBaptiste Daroussin 			}
15467295610fSBaptiste Daroussin 			ctx = r->mstack + r->mstackpos;
15477295610fSBaptiste Daroussin 			npos = esct[1] - '1';
15487295610fSBaptiste Daroussin 			if (npos >= 0 && npos <= 8) {
15497295610fSBaptiste Daroussin 				res = npos < ctx->argc ?
15507295610fSBaptiste Daroussin 				    ctx->argv[npos] : "";
15517295610fSBaptiste Daroussin 				break;
15527295610fSBaptiste Daroussin 			}
15537295610fSBaptiste Daroussin 			if (esct[1] == '*')
15547295610fSBaptiste Daroussin 				quote_args = 0;
15557295610fSBaptiste Daroussin 			else if (esct[1] == '@')
15567295610fSBaptiste Daroussin 				quote_args = 1;
15577295610fSBaptiste Daroussin 			else {
15587295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_ARG_NONUM, ln,
15597295610fSBaptiste Daroussin 				    (int)(stesc - buf->buf), "%.3s", stesc);
15607295610fSBaptiste Daroussin 				break;
15617295610fSBaptiste Daroussin 			}
15627295610fSBaptiste Daroussin 			asz = 0;
15637295610fSBaptiste Daroussin 			for (npos = 0; npos < ctx->argc; npos++) {
15647295610fSBaptiste Daroussin 				if (npos)
15657295610fSBaptiste Daroussin 					asz++;  /* blank */
15667295610fSBaptiste Daroussin 				if (quote_args)
15677295610fSBaptiste Daroussin 					asz += 2;  /* quotes */
15687295610fSBaptiste Daroussin 				asz += strlen(ctx->argv[npos]);
15697295610fSBaptiste Daroussin 			}
15707295610fSBaptiste Daroussin 			if (asz != 3) {
15717295610fSBaptiste Daroussin 				rsz = buf->sz - (stesc - buf->buf) - 3;
15727295610fSBaptiste Daroussin 				if (asz < 3)
15737295610fSBaptiste Daroussin 					memmove(stesc + asz, stesc + 3, rsz);
15747295610fSBaptiste Daroussin 				buf->sz += asz - 3;
15757295610fSBaptiste Daroussin 				nbuf = mandoc_realloc(buf->buf, buf->sz);
15767295610fSBaptiste Daroussin 				start = nbuf + pos;
15777295610fSBaptiste Daroussin 				stesc = nbuf + (stesc - buf->buf);
15787295610fSBaptiste Daroussin 				buf->buf = nbuf;
15797295610fSBaptiste Daroussin 				if (asz > 3)
15807295610fSBaptiste Daroussin 					memmove(stesc + asz, stesc + 3, rsz);
15817295610fSBaptiste Daroussin 			}
15827295610fSBaptiste Daroussin 			for (npos = 0; npos < ctx->argc; npos++) {
15837295610fSBaptiste Daroussin 				if (npos)
15847295610fSBaptiste Daroussin 					*stesc++ = ' ';
15857295610fSBaptiste Daroussin 				if (quote_args)
15867295610fSBaptiste Daroussin 					*stesc++ = '"';
15877295610fSBaptiste Daroussin 				cp = ctx->argv[npos];
15887295610fSBaptiste Daroussin 				while (*cp != '\0')
15897295610fSBaptiste Daroussin 					*stesc++ = *cp++;
15907295610fSBaptiste Daroussin 				if (quote_args)
15917295610fSBaptiste Daroussin 					*stesc++ = '"';
15927295610fSBaptiste Daroussin 			}
15937295610fSBaptiste Daroussin 			continue;
159461d06d6bSBaptiste Daroussin 		case 'B':
159561d06d6bSBaptiste Daroussin 			npos = 0;
159661d06d6bSBaptiste Daroussin 			ubuf[0] = arg_complete &&
159761d06d6bSBaptiste Daroussin 			    roff_evalnum(r, ln, stnam, &npos,
159861d06d6bSBaptiste Daroussin 			      NULL, ROFFNUM_SCALE) &&
159961d06d6bSBaptiste Daroussin 			    stnam + npos + 1 == cp ? '1' : '0';
160061d06d6bSBaptiste Daroussin 			ubuf[1] = '\0';
160161d06d6bSBaptiste Daroussin 			break;
160261d06d6bSBaptiste Daroussin 		case 'n':
160361d06d6bSBaptiste Daroussin 			if (arg_complete)
160461d06d6bSBaptiste Daroussin 				(void)snprintf(ubuf, sizeof(ubuf), "%d",
160561d06d6bSBaptiste Daroussin 				    roff_getregn(r, stnam, naml, sign));
160661d06d6bSBaptiste Daroussin 			else
160761d06d6bSBaptiste Daroussin 				ubuf[0] = '\0';
160861d06d6bSBaptiste Daroussin 			break;
160961d06d6bSBaptiste Daroussin 		case 'w':
161061d06d6bSBaptiste Daroussin 			/* use even incomplete args */
161161d06d6bSBaptiste Daroussin 			(void)snprintf(ubuf, sizeof(ubuf), "%d",
161261d06d6bSBaptiste Daroussin 			    24 * (int)naml);
161361d06d6bSBaptiste Daroussin 			break;
161461d06d6bSBaptiste Daroussin 		}
161561d06d6bSBaptiste Daroussin 
161661d06d6bSBaptiste Daroussin 		if (res == NULL) {
16177295610fSBaptiste Daroussin 			if (*esct == '*')
16187295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_STR_UNDEF,
16197295610fSBaptiste Daroussin 				    ln, (int)(stesc - buf->buf),
162061d06d6bSBaptiste Daroussin 				    "%.*s", (int)naml, stnam);
162161d06d6bSBaptiste Daroussin 			res = "";
162261d06d6bSBaptiste Daroussin 		} else if (buf->sz + strlen(res) > SHRT_MAX) {
16237295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_ROFFLOOP,
162461d06d6bSBaptiste Daroussin 			    ln, (int)(stesc - buf->buf), NULL);
162561d06d6bSBaptiste Daroussin 			return ROFF_IGN;
162661d06d6bSBaptiste Daroussin 		}
162761d06d6bSBaptiste Daroussin 
162861d06d6bSBaptiste Daroussin 		/* Replace the escape sequence by the string. */
162961d06d6bSBaptiste Daroussin 
163061d06d6bSBaptiste Daroussin 		*stesc = '\0';
163161d06d6bSBaptiste Daroussin 		buf->sz = mandoc_asprintf(&nbuf, "%s%s%s",
163261d06d6bSBaptiste Daroussin 		    buf->buf, res, cp) + 1;
163361d06d6bSBaptiste Daroussin 
163461d06d6bSBaptiste Daroussin 		/* Prepare for the next replacement. */
163561d06d6bSBaptiste Daroussin 
163661d06d6bSBaptiste Daroussin 		start = nbuf + pos;
163761d06d6bSBaptiste Daroussin 		stesc = nbuf + (stesc - buf->buf) + strlen(res);
163861d06d6bSBaptiste Daroussin 		free(buf->buf);
163961d06d6bSBaptiste Daroussin 		buf->buf = nbuf;
164061d06d6bSBaptiste Daroussin 	}
164161d06d6bSBaptiste Daroussin 	return ROFF_CONT;
164261d06d6bSBaptiste Daroussin }
164361d06d6bSBaptiste Daroussin 
164461d06d6bSBaptiste Daroussin /*
16457295610fSBaptiste Daroussin  * Parse a quoted or unquoted roff-style request or macro argument.
16467295610fSBaptiste Daroussin  * Return a pointer to the parsed argument, which is either the original
16477295610fSBaptiste Daroussin  * pointer or advanced by one byte in case the argument is quoted.
16487295610fSBaptiste Daroussin  * NUL-terminate the argument in place.
16497295610fSBaptiste Daroussin  * Collapse pairs of quotes inside quoted arguments.
16507295610fSBaptiste Daroussin  * Advance the argument pointer to the next argument,
16517295610fSBaptiste Daroussin  * or to the NUL byte terminating the argument line.
16527295610fSBaptiste Daroussin  */
16537295610fSBaptiste Daroussin char *
roff_getarg(struct roff * r,char ** cpp,int ln,int * pos)16547295610fSBaptiste Daroussin roff_getarg(struct roff *r, char **cpp, int ln, int *pos)
16557295610fSBaptiste Daroussin {
16567295610fSBaptiste Daroussin 	struct buf	 buf;
16577295610fSBaptiste Daroussin 	char		*cp, *start;
16587295610fSBaptiste Daroussin 	int		 newesc, pairs, quoted, white;
16597295610fSBaptiste Daroussin 
16607295610fSBaptiste Daroussin 	/* Quoting can only start with a new word. */
16617295610fSBaptiste Daroussin 	start = *cpp;
16627295610fSBaptiste Daroussin 	quoted = 0;
16637295610fSBaptiste Daroussin 	if ('"' == *start) {
16647295610fSBaptiste Daroussin 		quoted = 1;
16657295610fSBaptiste Daroussin 		start++;
16667295610fSBaptiste Daroussin 	}
16677295610fSBaptiste Daroussin 
16687295610fSBaptiste Daroussin 	newesc = pairs = white = 0;
16697295610fSBaptiste Daroussin 	for (cp = start; '\0' != *cp; cp++) {
16707295610fSBaptiste Daroussin 
16717295610fSBaptiste Daroussin 		/*
16727295610fSBaptiste Daroussin 		 * Move the following text left
16737295610fSBaptiste Daroussin 		 * after quoted quotes and after "\\" and "\t".
16747295610fSBaptiste Daroussin 		 */
16757295610fSBaptiste Daroussin 		if (pairs)
16767295610fSBaptiste Daroussin 			cp[-pairs] = cp[0];
16777295610fSBaptiste Daroussin 
16787295610fSBaptiste Daroussin 		if ('\\' == cp[0]) {
16797295610fSBaptiste Daroussin 			/*
16807295610fSBaptiste Daroussin 			 * In copy mode, translate double to single
16817295610fSBaptiste Daroussin 			 * backslashes and backslash-t to literal tabs.
16827295610fSBaptiste Daroussin 			 */
16837295610fSBaptiste Daroussin 			switch (cp[1]) {
16847295610fSBaptiste Daroussin 			case 'a':
16857295610fSBaptiste Daroussin 			case 't':
16867295610fSBaptiste Daroussin 				cp[-pairs] = '\t';
16877295610fSBaptiste Daroussin 				pairs++;
16887295610fSBaptiste Daroussin 				cp++;
16897295610fSBaptiste Daroussin 				break;
16907295610fSBaptiste Daroussin 			case '\\':
16917295610fSBaptiste Daroussin 				newesc = 1;
16927295610fSBaptiste Daroussin 				cp[-pairs] = ASCII_ESC;
16937295610fSBaptiste Daroussin 				pairs++;
16947295610fSBaptiste Daroussin 				cp++;
16957295610fSBaptiste Daroussin 				break;
16967295610fSBaptiste Daroussin 			case ' ':
16977295610fSBaptiste Daroussin 				/* Skip escaped blanks. */
16987295610fSBaptiste Daroussin 				if (0 == quoted)
16997295610fSBaptiste Daroussin 					cp++;
17007295610fSBaptiste Daroussin 				break;
17017295610fSBaptiste Daroussin 			default:
17027295610fSBaptiste Daroussin 				break;
17037295610fSBaptiste Daroussin 			}
17047295610fSBaptiste Daroussin 		} else if (0 == quoted) {
17057295610fSBaptiste Daroussin 			if (' ' == cp[0]) {
17067295610fSBaptiste Daroussin 				/* Unescaped blanks end unquoted args. */
17077295610fSBaptiste Daroussin 				white = 1;
17087295610fSBaptiste Daroussin 				break;
17097295610fSBaptiste Daroussin 			}
17107295610fSBaptiste Daroussin 		} else if ('"' == cp[0]) {
17117295610fSBaptiste Daroussin 			if ('"' == cp[1]) {
17127295610fSBaptiste Daroussin 				/* Quoted quotes collapse. */
17137295610fSBaptiste Daroussin 				pairs++;
17147295610fSBaptiste Daroussin 				cp++;
17157295610fSBaptiste Daroussin 			} else {
17167295610fSBaptiste Daroussin 				/* Unquoted quotes end quoted args. */
17177295610fSBaptiste Daroussin 				quoted = 2;
17187295610fSBaptiste Daroussin 				break;
17197295610fSBaptiste Daroussin 			}
17207295610fSBaptiste Daroussin 		}
17217295610fSBaptiste Daroussin 	}
17227295610fSBaptiste Daroussin 
17237295610fSBaptiste Daroussin 	/* Quoted argument without a closing quote. */
17247295610fSBaptiste Daroussin 	if (1 == quoted)
17257295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_QUOTE, ln, *pos, NULL);
17267295610fSBaptiste Daroussin 
17277295610fSBaptiste Daroussin 	/* NUL-terminate this argument and move to the next one. */
17287295610fSBaptiste Daroussin 	if (pairs)
17297295610fSBaptiste Daroussin 		cp[-pairs] = '\0';
17307295610fSBaptiste Daroussin 	if ('\0' != *cp) {
17317295610fSBaptiste Daroussin 		*cp++ = '\0';
17327295610fSBaptiste Daroussin 		while (' ' == *cp)
17337295610fSBaptiste Daroussin 			cp++;
17347295610fSBaptiste Daroussin 	}
17357295610fSBaptiste Daroussin 	*pos += (int)(cp - start) + (quoted ? 1 : 0);
17367295610fSBaptiste Daroussin 	*cpp = cp;
17377295610fSBaptiste Daroussin 
17387295610fSBaptiste Daroussin 	if ('\0' == *cp && (white || ' ' == cp[-1]))
17397295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_SPACE_EOL, ln, *pos, NULL);
17407295610fSBaptiste Daroussin 
17417295610fSBaptiste Daroussin 	start = mandoc_strdup(start);
17427295610fSBaptiste Daroussin 	if (newesc == 0)
17437295610fSBaptiste Daroussin 		return start;
17447295610fSBaptiste Daroussin 
17457295610fSBaptiste Daroussin 	buf.buf = start;
17467295610fSBaptiste Daroussin 	buf.sz = strlen(start) + 1;
17477295610fSBaptiste Daroussin 	buf.next = NULL;
17487295610fSBaptiste Daroussin 	if (roff_expand(r, &buf, ln, 0, ASCII_ESC) & ROFF_IGN) {
17497295610fSBaptiste Daroussin 		free(buf.buf);
17507295610fSBaptiste Daroussin 		buf.buf = mandoc_strdup("");
17517295610fSBaptiste Daroussin 	}
17527295610fSBaptiste Daroussin 	return buf.buf;
17537295610fSBaptiste Daroussin }
17547295610fSBaptiste Daroussin 
17557295610fSBaptiste Daroussin 
17567295610fSBaptiste Daroussin /*
175761d06d6bSBaptiste Daroussin  * Process text streams.
175861d06d6bSBaptiste Daroussin  */
17597295610fSBaptiste Daroussin static int
roff_parsetext(struct roff * r,struct buf * buf,int pos,int * offs)176061d06d6bSBaptiste Daroussin roff_parsetext(struct roff *r, struct buf *buf, int pos, int *offs)
176161d06d6bSBaptiste Daroussin {
176261d06d6bSBaptiste Daroussin 	size_t		 sz;
176361d06d6bSBaptiste Daroussin 	const char	*start;
176461d06d6bSBaptiste Daroussin 	char		*p;
176561d06d6bSBaptiste Daroussin 	int		 isz;
176661d06d6bSBaptiste Daroussin 	enum mandoc_esc	 esc;
176761d06d6bSBaptiste Daroussin 
176861d06d6bSBaptiste Daroussin 	/* Spring the input line trap. */
176961d06d6bSBaptiste Daroussin 
177061d06d6bSBaptiste Daroussin 	if (roffit_lines == 1) {
177161d06d6bSBaptiste Daroussin 		isz = mandoc_asprintf(&p, "%s\n.%s", buf->buf, roffit_macro);
177261d06d6bSBaptiste Daroussin 		free(buf->buf);
177361d06d6bSBaptiste Daroussin 		buf->buf = p;
177461d06d6bSBaptiste Daroussin 		buf->sz = isz + 1;
177561d06d6bSBaptiste Daroussin 		*offs = 0;
177661d06d6bSBaptiste Daroussin 		free(roffit_macro);
177761d06d6bSBaptiste Daroussin 		roffit_lines = 0;
177861d06d6bSBaptiste Daroussin 		return ROFF_REPARSE;
177961d06d6bSBaptiste Daroussin 	} else if (roffit_lines > 1)
178061d06d6bSBaptiste Daroussin 		--roffit_lines;
178161d06d6bSBaptiste Daroussin 
178261d06d6bSBaptiste Daroussin 	if (roffce_node != NULL && buf->buf[pos] != '\0') {
178361d06d6bSBaptiste Daroussin 		if (roffce_lines < 1) {
178461d06d6bSBaptiste Daroussin 			r->man->last = roffce_node;
178561d06d6bSBaptiste Daroussin 			r->man->next = ROFF_NEXT_SIBLING;
178661d06d6bSBaptiste Daroussin 			roffce_lines = 0;
178761d06d6bSBaptiste Daroussin 			roffce_node = NULL;
178861d06d6bSBaptiste Daroussin 		} else
178961d06d6bSBaptiste Daroussin 			roffce_lines--;
179061d06d6bSBaptiste Daroussin 	}
179161d06d6bSBaptiste Daroussin 
179261d06d6bSBaptiste Daroussin 	/* Convert all breakable hyphens into ASCII_HYPH. */
179361d06d6bSBaptiste Daroussin 
179461d06d6bSBaptiste Daroussin 	start = p = buf->buf + pos;
179561d06d6bSBaptiste Daroussin 
179661d06d6bSBaptiste Daroussin 	while (*p != '\0') {
179761d06d6bSBaptiste Daroussin 		sz = strcspn(p, "-\\");
179861d06d6bSBaptiste Daroussin 		p += sz;
179961d06d6bSBaptiste Daroussin 
180061d06d6bSBaptiste Daroussin 		if (*p == '\0')
180161d06d6bSBaptiste Daroussin 			break;
180261d06d6bSBaptiste Daroussin 
180361d06d6bSBaptiste Daroussin 		if (*p == '\\') {
180461d06d6bSBaptiste Daroussin 			/* Skip over escapes. */
180561d06d6bSBaptiste Daroussin 			p++;
180661d06d6bSBaptiste Daroussin 			esc = mandoc_escape((const char **)&p, NULL, NULL);
180761d06d6bSBaptiste Daroussin 			if (esc == ESCAPE_ERROR)
180861d06d6bSBaptiste Daroussin 				break;
180961d06d6bSBaptiste Daroussin 			while (*p == '-')
181061d06d6bSBaptiste Daroussin 				p++;
181161d06d6bSBaptiste Daroussin 			continue;
181261d06d6bSBaptiste Daroussin 		} else if (p == start) {
181361d06d6bSBaptiste Daroussin 			p++;
181461d06d6bSBaptiste Daroussin 			continue;
181561d06d6bSBaptiste Daroussin 		}
181661d06d6bSBaptiste Daroussin 
181761d06d6bSBaptiste Daroussin 		if (isalpha((unsigned char)p[-1]) &&
181861d06d6bSBaptiste Daroussin 		    isalpha((unsigned char)p[1]))
181961d06d6bSBaptiste Daroussin 			*p = ASCII_HYPH;
182061d06d6bSBaptiste Daroussin 		p++;
182161d06d6bSBaptiste Daroussin 	}
182261d06d6bSBaptiste Daroussin 	return ROFF_CONT;
182361d06d6bSBaptiste Daroussin }
182461d06d6bSBaptiste Daroussin 
18257295610fSBaptiste Daroussin int
roff_parseln(struct roff * r,int ln,struct buf * buf,int * offs,size_t len)1826*6d38604fSBaptiste Daroussin roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs, size_t len)
182761d06d6bSBaptiste Daroussin {
182861d06d6bSBaptiste Daroussin 	enum roff_tok	 t;
18297295610fSBaptiste Daroussin 	int		 e;
183061d06d6bSBaptiste Daroussin 	int		 pos;	/* parse point */
183161d06d6bSBaptiste Daroussin 	int		 spos;	/* saved parse point for messages */
183261d06d6bSBaptiste Daroussin 	int		 ppos;	/* original offset in buf->buf */
183361d06d6bSBaptiste Daroussin 	int		 ctl;	/* macro line (boolean) */
183461d06d6bSBaptiste Daroussin 
183561d06d6bSBaptiste Daroussin 	ppos = pos = *offs;
183661d06d6bSBaptiste Daroussin 
1837*6d38604fSBaptiste Daroussin 	if (len > 80 && r->tbl == NULL && r->eqn == NULL &&
1838*6d38604fSBaptiste Daroussin 	    (r->man->flags & ROFF_NOFILL) == 0 &&
1839*6d38604fSBaptiste Daroussin 	    strchr(" .\\", buf->buf[pos]) == NULL &&
1840*6d38604fSBaptiste Daroussin 	    buf->buf[pos] != r->control &&
1841*6d38604fSBaptiste Daroussin 	    strcspn(buf->buf, " ") < 80)
1842*6d38604fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_TEXT_LONG, ln, (int)len - 1,
1843*6d38604fSBaptiste Daroussin 		    "%.20s...", buf->buf + pos);
1844*6d38604fSBaptiste Daroussin 
184561d06d6bSBaptiste Daroussin 	/* Handle in-line equation delimiters. */
184661d06d6bSBaptiste Daroussin 
184761d06d6bSBaptiste Daroussin 	if (r->tbl == NULL &&
184861d06d6bSBaptiste Daroussin 	    r->last_eqn != NULL && r->last_eqn->delim &&
184961d06d6bSBaptiste Daroussin 	    (r->eqn == NULL || r->eqn_inline)) {
185061d06d6bSBaptiste Daroussin 		e = roff_eqndelim(r, buf, pos);
185161d06d6bSBaptiste Daroussin 		if (e == ROFF_REPARSE)
185261d06d6bSBaptiste Daroussin 			return e;
185361d06d6bSBaptiste Daroussin 		assert(e == ROFF_CONT);
185461d06d6bSBaptiste Daroussin 	}
185561d06d6bSBaptiste Daroussin 
185661d06d6bSBaptiste Daroussin 	/* Expand some escape sequences. */
185761d06d6bSBaptiste Daroussin 
18587295610fSBaptiste Daroussin 	e = roff_expand(r, buf, ln, pos, r->escape);
18597295610fSBaptiste Daroussin 	if ((e & ROFF_MASK) == ROFF_IGN)
186061d06d6bSBaptiste Daroussin 		return e;
186161d06d6bSBaptiste Daroussin 	assert(e == ROFF_CONT);
186261d06d6bSBaptiste Daroussin 
186361d06d6bSBaptiste Daroussin 	ctl = roff_getcontrol(r, buf->buf, &pos);
186461d06d6bSBaptiste Daroussin 
186561d06d6bSBaptiste Daroussin 	/*
186661d06d6bSBaptiste Daroussin 	 * First, if a scope is open and we're not a macro, pass the
186761d06d6bSBaptiste Daroussin 	 * text through the macro's filter.
186861d06d6bSBaptiste Daroussin 	 * Equations process all content themselves.
186961d06d6bSBaptiste Daroussin 	 * Tables process almost all content themselves, but we want
187061d06d6bSBaptiste Daroussin 	 * to warn about macros before passing it there.
187161d06d6bSBaptiste Daroussin 	 */
187261d06d6bSBaptiste Daroussin 
187361d06d6bSBaptiste Daroussin 	if (r->last != NULL && ! ctl) {
187461d06d6bSBaptiste Daroussin 		t = r->last->tok;
187561d06d6bSBaptiste Daroussin 		e = (*roffs[t].text)(r, t, buf, ln, pos, pos, offs);
18767295610fSBaptiste Daroussin 		if ((e & ROFF_MASK) == ROFF_IGN)
187761d06d6bSBaptiste Daroussin 			return e;
18787295610fSBaptiste Daroussin 		e &= ~ROFF_MASK;
18797295610fSBaptiste Daroussin 	} else
18807295610fSBaptiste Daroussin 		e = ROFF_IGN;
188161d06d6bSBaptiste Daroussin 	if (r->eqn != NULL && strncmp(buf->buf + ppos, ".EN", 3)) {
188261d06d6bSBaptiste Daroussin 		eqn_read(r->eqn, buf->buf + ppos);
18837295610fSBaptiste Daroussin 		return e;
188461d06d6bSBaptiste Daroussin 	}
188561d06d6bSBaptiste Daroussin 	if (r->tbl != NULL && (ctl == 0 || buf->buf[pos] == '\0')) {
188661d06d6bSBaptiste Daroussin 		tbl_read(r->tbl, ln, buf->buf, ppos);
18877295610fSBaptiste Daroussin 		roff_addtbl(r->man, ln, r->tbl);
18887295610fSBaptiste Daroussin 		return e;
188961d06d6bSBaptiste Daroussin 	}
1890*6d38604fSBaptiste Daroussin 	if ( ! ctl) {
1891*6d38604fSBaptiste Daroussin 		r->options &= ~MPARSE_COMMENT;
18927295610fSBaptiste Daroussin 		return roff_parsetext(r, buf, pos, offs) | e;
1893*6d38604fSBaptiste Daroussin 	}
189461d06d6bSBaptiste Daroussin 
189561d06d6bSBaptiste Daroussin 	/* Skip empty request lines. */
189661d06d6bSBaptiste Daroussin 
189761d06d6bSBaptiste Daroussin 	if (buf->buf[pos] == '"') {
18987295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_COMMENT_BAD, ln, pos, NULL);
189961d06d6bSBaptiste Daroussin 		return ROFF_IGN;
190061d06d6bSBaptiste Daroussin 	} else if (buf->buf[pos] == '\0')
190161d06d6bSBaptiste Daroussin 		return ROFF_IGN;
190261d06d6bSBaptiste Daroussin 
190361d06d6bSBaptiste Daroussin 	/*
190461d06d6bSBaptiste Daroussin 	 * If a scope is open, go to the child handler for that macro,
190561d06d6bSBaptiste Daroussin 	 * as it may want to preprocess before doing anything with it.
190661d06d6bSBaptiste Daroussin 	 * Don't do so if an equation is open.
190761d06d6bSBaptiste Daroussin 	 */
190861d06d6bSBaptiste Daroussin 
190961d06d6bSBaptiste Daroussin 	if (r->last) {
191061d06d6bSBaptiste Daroussin 		t = r->last->tok;
191161d06d6bSBaptiste Daroussin 		return (*roffs[t].sub)(r, t, buf, ln, ppos, pos, offs);
191261d06d6bSBaptiste Daroussin 	}
191361d06d6bSBaptiste Daroussin 
191461d06d6bSBaptiste Daroussin 	/* No scope is open.  This is a new request or macro. */
191561d06d6bSBaptiste Daroussin 
1916*6d38604fSBaptiste Daroussin 	r->options &= ~MPARSE_COMMENT;
191761d06d6bSBaptiste Daroussin 	spos = pos;
191861d06d6bSBaptiste Daroussin 	t = roff_parse(r, buf->buf, &pos, ln, ppos);
191961d06d6bSBaptiste Daroussin 
192061d06d6bSBaptiste Daroussin 	/* Tables ignore most macros. */
192161d06d6bSBaptiste Daroussin 
192261d06d6bSBaptiste Daroussin 	if (r->tbl != NULL && (t == TOKEN_NONE || t == ROFF_TS ||
192361d06d6bSBaptiste Daroussin 	    t == ROFF_br || t == ROFF_ce || t == ROFF_rj || t == ROFF_sp)) {
19247295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_TBLMACRO,
19257295610fSBaptiste Daroussin 		    ln, pos, "%s", buf->buf + spos);
192661d06d6bSBaptiste Daroussin 		if (t != TOKEN_NONE)
192761d06d6bSBaptiste Daroussin 			return ROFF_IGN;
192861d06d6bSBaptiste Daroussin 		while (buf->buf[pos] != '\0' && buf->buf[pos] != ' ')
192961d06d6bSBaptiste Daroussin 			pos++;
193061d06d6bSBaptiste Daroussin 		while (buf->buf[pos] == ' ')
193161d06d6bSBaptiste Daroussin 			pos++;
193261d06d6bSBaptiste Daroussin 		tbl_read(r->tbl, ln, buf->buf, pos);
19337295610fSBaptiste Daroussin 		roff_addtbl(r->man, ln, r->tbl);
193461d06d6bSBaptiste Daroussin 		return ROFF_IGN;
193561d06d6bSBaptiste Daroussin 	}
193661d06d6bSBaptiste Daroussin 
193761d06d6bSBaptiste Daroussin 	/* For now, let high level macros abort .ce mode. */
193861d06d6bSBaptiste Daroussin 
193961d06d6bSBaptiste Daroussin 	if (ctl && roffce_node != NULL &&
194061d06d6bSBaptiste Daroussin 	    (t == TOKEN_NONE || t == ROFF_Dd || t == ROFF_EQ ||
194161d06d6bSBaptiste Daroussin 	     t == ROFF_TH || t == ROFF_TS)) {
194261d06d6bSBaptiste Daroussin 		r->man->last = roffce_node;
194361d06d6bSBaptiste Daroussin 		r->man->next = ROFF_NEXT_SIBLING;
194461d06d6bSBaptiste Daroussin 		roffce_lines = 0;
194561d06d6bSBaptiste Daroussin 		roffce_node = NULL;
194661d06d6bSBaptiste Daroussin 	}
194761d06d6bSBaptiste Daroussin 
194861d06d6bSBaptiste Daroussin 	/*
194961d06d6bSBaptiste Daroussin 	 * This is neither a roff request nor a user-defined macro.
195061d06d6bSBaptiste Daroussin 	 * Let the standard macro set parsers handle it.
195161d06d6bSBaptiste Daroussin 	 */
195261d06d6bSBaptiste Daroussin 
195361d06d6bSBaptiste Daroussin 	if (t == TOKEN_NONE)
195461d06d6bSBaptiste Daroussin 		return ROFF_CONT;
195561d06d6bSBaptiste Daroussin 
195661d06d6bSBaptiste Daroussin 	/* Execute a roff request or a user defined macro. */
195761d06d6bSBaptiste Daroussin 
195861d06d6bSBaptiste Daroussin 	return (*roffs[t].proc)(r, t, buf, ln, spos, pos, offs);
195961d06d6bSBaptiste Daroussin }
196061d06d6bSBaptiste Daroussin 
19617295610fSBaptiste Daroussin /*
19627295610fSBaptiste Daroussin  * Internal interface function to tell the roff parser that execution
19637295610fSBaptiste Daroussin  * of the current macro ended.  This is required because macro
19647295610fSBaptiste Daroussin  * definitions usually do not end with a .return request.
19657295610fSBaptiste Daroussin  */
19667295610fSBaptiste Daroussin void
roff_userret(struct roff * r)19677295610fSBaptiste Daroussin roff_userret(struct roff *r)
19687295610fSBaptiste Daroussin {
19697295610fSBaptiste Daroussin 	struct mctx	*ctx;
19707295610fSBaptiste Daroussin 	int		 i;
19717295610fSBaptiste Daroussin 
19727295610fSBaptiste Daroussin 	assert(r->mstackpos >= 0);
19737295610fSBaptiste Daroussin 	ctx = r->mstack + r->mstackpos;
19747295610fSBaptiste Daroussin 	for (i = 0; i < ctx->argc; i++)
19757295610fSBaptiste Daroussin 		free(ctx->argv[i]);
19767295610fSBaptiste Daroussin 	ctx->argc = 0;
19777295610fSBaptiste Daroussin 	r->mstackpos--;
19787295610fSBaptiste Daroussin }
19797295610fSBaptiste Daroussin 
198061d06d6bSBaptiste Daroussin void
roff_endparse(struct roff * r)198161d06d6bSBaptiste Daroussin roff_endparse(struct roff *r)
198261d06d6bSBaptiste Daroussin {
198361d06d6bSBaptiste Daroussin 	if (r->last != NULL)
19847295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_NOEND, r->last->line,
19857295610fSBaptiste Daroussin 		    r->last->col, "%s", roff_name[r->last->tok]);
198661d06d6bSBaptiste Daroussin 
198761d06d6bSBaptiste Daroussin 	if (r->eqn != NULL) {
19887295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_NOEND,
198961d06d6bSBaptiste Daroussin 		    r->eqn->node->line, r->eqn->node->pos, "EQ");
199061d06d6bSBaptiste Daroussin 		eqn_parse(r->eqn);
199161d06d6bSBaptiste Daroussin 		r->eqn = NULL;
199261d06d6bSBaptiste Daroussin 	}
199361d06d6bSBaptiste Daroussin 
199461d06d6bSBaptiste Daroussin 	if (r->tbl != NULL) {
19957295610fSBaptiste Daroussin 		tbl_end(r->tbl, 1);
199661d06d6bSBaptiste Daroussin 		r->tbl = NULL;
199761d06d6bSBaptiste Daroussin 	}
199861d06d6bSBaptiste Daroussin }
199961d06d6bSBaptiste Daroussin 
200061d06d6bSBaptiste Daroussin /*
200161d06d6bSBaptiste Daroussin  * Parse a roff node's type from the input buffer.  This must be in the
200261d06d6bSBaptiste Daroussin  * form of ".foo xxx" in the usual way.
200361d06d6bSBaptiste Daroussin  */
200461d06d6bSBaptiste Daroussin static enum roff_tok
roff_parse(struct roff * r,char * buf,int * pos,int ln,int ppos)200561d06d6bSBaptiste Daroussin roff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos)
200661d06d6bSBaptiste Daroussin {
200761d06d6bSBaptiste Daroussin 	char		*cp;
200861d06d6bSBaptiste Daroussin 	const char	*mac;
200961d06d6bSBaptiste Daroussin 	size_t		 maclen;
201061d06d6bSBaptiste Daroussin 	int		 deftype;
201161d06d6bSBaptiste Daroussin 	enum roff_tok	 t;
201261d06d6bSBaptiste Daroussin 
201361d06d6bSBaptiste Daroussin 	cp = buf + *pos;
201461d06d6bSBaptiste Daroussin 
201561d06d6bSBaptiste Daroussin 	if ('\0' == *cp || '"' == *cp || '\t' == *cp || ' ' == *cp)
201661d06d6bSBaptiste Daroussin 		return TOKEN_NONE;
201761d06d6bSBaptiste Daroussin 
201861d06d6bSBaptiste Daroussin 	mac = cp;
201961d06d6bSBaptiste Daroussin 	maclen = roff_getname(r, &cp, ln, ppos);
202061d06d6bSBaptiste Daroussin 
202161d06d6bSBaptiste Daroussin 	deftype = ROFFDEF_USER | ROFFDEF_REN;
202261d06d6bSBaptiste Daroussin 	r->current_string = roff_getstrn(r, mac, maclen, &deftype);
202361d06d6bSBaptiste Daroussin 	switch (deftype) {
202461d06d6bSBaptiste Daroussin 	case ROFFDEF_USER:
202561d06d6bSBaptiste Daroussin 		t = ROFF_USERDEF;
202661d06d6bSBaptiste Daroussin 		break;
202761d06d6bSBaptiste Daroussin 	case ROFFDEF_REN:
202861d06d6bSBaptiste Daroussin 		t = ROFF_RENAMED;
202961d06d6bSBaptiste Daroussin 		break;
203061d06d6bSBaptiste Daroussin 	default:
203161d06d6bSBaptiste Daroussin 		t = roffhash_find(r->reqtab, mac, maclen);
203261d06d6bSBaptiste Daroussin 		break;
203361d06d6bSBaptiste Daroussin 	}
203461d06d6bSBaptiste Daroussin 	if (t != TOKEN_NONE)
203561d06d6bSBaptiste Daroussin 		*pos = cp - buf;
203661d06d6bSBaptiste Daroussin 	else if (deftype == ROFFDEF_UNDEF) {
203761d06d6bSBaptiste Daroussin 		/* Using an undefined macro defines it to be empty. */
203861d06d6bSBaptiste Daroussin 		roff_setstrn(&r->strtab, mac, maclen, "", 0, 0);
203961d06d6bSBaptiste Daroussin 		roff_setstrn(&r->rentab, mac, maclen, NULL, 0, 0);
204061d06d6bSBaptiste Daroussin 	}
204161d06d6bSBaptiste Daroussin 	return t;
204261d06d6bSBaptiste Daroussin }
204361d06d6bSBaptiste Daroussin 
204461d06d6bSBaptiste Daroussin /* --- handling of request blocks ----------------------------------------- */
204561d06d6bSBaptiste Daroussin 
2046*6d38604fSBaptiste Daroussin /*
2047*6d38604fSBaptiste Daroussin  * Close a macro definition block or an "ignore" block.
2048*6d38604fSBaptiste Daroussin  */
20497295610fSBaptiste Daroussin static int
roff_cblock(ROFF_ARGS)205061d06d6bSBaptiste Daroussin roff_cblock(ROFF_ARGS)
205161d06d6bSBaptiste Daroussin {
2052*6d38604fSBaptiste Daroussin 	int	 rr;
205361d06d6bSBaptiste Daroussin 
205461d06d6bSBaptiste Daroussin 	if (r->last == NULL) {
20557295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "..");
205661d06d6bSBaptiste Daroussin 		return ROFF_IGN;
205761d06d6bSBaptiste Daroussin 	}
205861d06d6bSBaptiste Daroussin 
205961d06d6bSBaptiste Daroussin 	switch (r->last->tok) {
206061d06d6bSBaptiste Daroussin 	case ROFF_am:
206161d06d6bSBaptiste Daroussin 	case ROFF_ami:
206261d06d6bSBaptiste Daroussin 	case ROFF_de:
206361d06d6bSBaptiste Daroussin 	case ROFF_dei:
206461d06d6bSBaptiste Daroussin 	case ROFF_ig:
206561d06d6bSBaptiste Daroussin 		break;
2066*6d38604fSBaptiste Daroussin 	case ROFF_am1:
2067*6d38604fSBaptiste Daroussin 	case ROFF_de1:
2068*6d38604fSBaptiste Daroussin 		/* Remapped in roff_block(). */
2069*6d38604fSBaptiste Daroussin 		abort();
207061d06d6bSBaptiste Daroussin 	default:
20717295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "..");
207261d06d6bSBaptiste Daroussin 		return ROFF_IGN;
207361d06d6bSBaptiste Daroussin 	}
207461d06d6bSBaptiste Daroussin 
2075*6d38604fSBaptiste Daroussin 	roffnode_pop(r);
2076*6d38604fSBaptiste Daroussin 	roffnode_cleanscope(r);
2077*6d38604fSBaptiste Daroussin 
2078*6d38604fSBaptiste Daroussin 	/*
2079*6d38604fSBaptiste Daroussin 	 * If a conditional block with braces is still open,
2080*6d38604fSBaptiste Daroussin 	 * check for "\}" block end markers.
2081*6d38604fSBaptiste Daroussin 	 */
2082*6d38604fSBaptiste Daroussin 
2083*6d38604fSBaptiste Daroussin 	if (r->last != NULL && r->last->endspan < 0) {
2084*6d38604fSBaptiste Daroussin 		rr = 1;  /* If arguments follow "\}", warn about them. */
2085*6d38604fSBaptiste Daroussin 		roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr);
2086*6d38604fSBaptiste Daroussin 	}
2087*6d38604fSBaptiste Daroussin 
208861d06d6bSBaptiste Daroussin 	if (buf->buf[pos] != '\0')
20897295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
209061d06d6bSBaptiste Daroussin 		    ".. %s", buf->buf + pos);
209161d06d6bSBaptiste Daroussin 
209261d06d6bSBaptiste Daroussin 	return ROFF_IGN;
209361d06d6bSBaptiste Daroussin }
209461d06d6bSBaptiste Daroussin 
209545a5aec3SBaptiste Daroussin /*
209645a5aec3SBaptiste Daroussin  * Pop all nodes ending at the end of the current input line.
209745a5aec3SBaptiste Daroussin  * Return the number of loops ended.
209845a5aec3SBaptiste Daroussin  */
20997295610fSBaptiste Daroussin static int
roffnode_cleanscope(struct roff * r)210061d06d6bSBaptiste Daroussin roffnode_cleanscope(struct roff *r)
210161d06d6bSBaptiste Daroussin {
21027295610fSBaptiste Daroussin 	int inloop;
210361d06d6bSBaptiste Daroussin 
21047295610fSBaptiste Daroussin 	inloop = 0;
2105*6d38604fSBaptiste Daroussin 	while (r->last != NULL && r->last->endspan > 0) {
210661d06d6bSBaptiste Daroussin 		if (--r->last->endspan != 0)
210761d06d6bSBaptiste Daroussin 			break;
21087295610fSBaptiste Daroussin 		inloop += roffnode_pop(r);
210961d06d6bSBaptiste Daroussin 	}
21107295610fSBaptiste Daroussin 	return inloop;
211161d06d6bSBaptiste Daroussin }
211261d06d6bSBaptiste Daroussin 
211345a5aec3SBaptiste Daroussin /*
2114*6d38604fSBaptiste Daroussin  * Handle the closing "\}" of a conditional block.
211545a5aec3SBaptiste Daroussin  * Apart from generating warnings, this only pops nodes.
211645a5aec3SBaptiste Daroussin  * Return the number of loops ended.
211745a5aec3SBaptiste Daroussin  */
21187295610fSBaptiste Daroussin static int
roff_ccond(struct roff * r,int ln,int ppos)211961d06d6bSBaptiste Daroussin roff_ccond(struct roff *r, int ln, int ppos)
212061d06d6bSBaptiste Daroussin {
212161d06d6bSBaptiste Daroussin 	if (NULL == r->last) {
21227295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}");
21237295610fSBaptiste Daroussin 		return 0;
212461d06d6bSBaptiste Daroussin 	}
212561d06d6bSBaptiste Daroussin 
212661d06d6bSBaptiste Daroussin 	switch (r->last->tok) {
212761d06d6bSBaptiste Daroussin 	case ROFF_el:
212861d06d6bSBaptiste Daroussin 	case ROFF_ie:
212961d06d6bSBaptiste Daroussin 	case ROFF_if:
21307295610fSBaptiste Daroussin 	case ROFF_while:
213161d06d6bSBaptiste Daroussin 		break;
213261d06d6bSBaptiste Daroussin 	default:
21337295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}");
21347295610fSBaptiste Daroussin 		return 0;
213561d06d6bSBaptiste Daroussin 	}
213661d06d6bSBaptiste Daroussin 
213761d06d6bSBaptiste Daroussin 	if (r->last->endspan > -1) {
21387295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}");
21397295610fSBaptiste Daroussin 		return 0;
214061d06d6bSBaptiste Daroussin 	}
214161d06d6bSBaptiste Daroussin 
21427295610fSBaptiste Daroussin 	return roffnode_pop(r) + roffnode_cleanscope(r);
214361d06d6bSBaptiste Daroussin }
214461d06d6bSBaptiste Daroussin 
21457295610fSBaptiste Daroussin static int
roff_block(ROFF_ARGS)214661d06d6bSBaptiste Daroussin roff_block(ROFF_ARGS)
214761d06d6bSBaptiste Daroussin {
214861d06d6bSBaptiste Daroussin 	const char	*name, *value;
214961d06d6bSBaptiste Daroussin 	char		*call, *cp, *iname, *rname;
215061d06d6bSBaptiste Daroussin 	size_t		 csz, namesz, rsz;
215161d06d6bSBaptiste Daroussin 	int		 deftype;
215261d06d6bSBaptiste Daroussin 
215361d06d6bSBaptiste Daroussin 	/* Ignore groff compatibility mode for now. */
215461d06d6bSBaptiste Daroussin 
215561d06d6bSBaptiste Daroussin 	if (tok == ROFF_de1)
215661d06d6bSBaptiste Daroussin 		tok = ROFF_de;
215761d06d6bSBaptiste Daroussin 	else if (tok == ROFF_dei1)
215861d06d6bSBaptiste Daroussin 		tok = ROFF_dei;
215961d06d6bSBaptiste Daroussin 	else if (tok == ROFF_am1)
216061d06d6bSBaptiste Daroussin 		tok = ROFF_am;
216161d06d6bSBaptiste Daroussin 	else if (tok == ROFF_ami1)
216261d06d6bSBaptiste Daroussin 		tok = ROFF_ami;
216361d06d6bSBaptiste Daroussin 
216461d06d6bSBaptiste Daroussin 	/* Parse the macro name argument. */
216561d06d6bSBaptiste Daroussin 
216661d06d6bSBaptiste Daroussin 	cp = buf->buf + pos;
216761d06d6bSBaptiste Daroussin 	if (tok == ROFF_ig) {
216861d06d6bSBaptiste Daroussin 		iname = NULL;
216961d06d6bSBaptiste Daroussin 		namesz = 0;
217061d06d6bSBaptiste Daroussin 	} else {
217161d06d6bSBaptiste Daroussin 		iname = cp;
217261d06d6bSBaptiste Daroussin 		namesz = roff_getname(r, &cp, ln, ppos);
217361d06d6bSBaptiste Daroussin 		iname[namesz] = '\0';
217461d06d6bSBaptiste Daroussin 	}
217561d06d6bSBaptiste Daroussin 
217661d06d6bSBaptiste Daroussin 	/* Resolve the macro name argument if it is indirect. */
217761d06d6bSBaptiste Daroussin 
217861d06d6bSBaptiste Daroussin 	if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
217961d06d6bSBaptiste Daroussin 		deftype = ROFFDEF_USER;
218061d06d6bSBaptiste Daroussin 		name = roff_getstrn(r, iname, namesz, &deftype);
218161d06d6bSBaptiste Daroussin 		if (name == NULL) {
21827295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_STR_UNDEF,
21837295610fSBaptiste Daroussin 			    ln, (int)(iname - buf->buf),
218461d06d6bSBaptiste Daroussin 			    "%.*s", (int)namesz, iname);
218561d06d6bSBaptiste Daroussin 			namesz = 0;
218661d06d6bSBaptiste Daroussin 		} else
218761d06d6bSBaptiste Daroussin 			namesz = strlen(name);
218861d06d6bSBaptiste Daroussin 	} else
218961d06d6bSBaptiste Daroussin 		name = iname;
219061d06d6bSBaptiste Daroussin 
219161d06d6bSBaptiste Daroussin 	if (namesz == 0 && tok != ROFF_ig) {
21927295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_REQ_EMPTY,
21937295610fSBaptiste Daroussin 		    ln, ppos, "%s", roff_name[tok]);
219461d06d6bSBaptiste Daroussin 		return ROFF_IGN;
219561d06d6bSBaptiste Daroussin 	}
219661d06d6bSBaptiste Daroussin 
219761d06d6bSBaptiste Daroussin 	roffnode_push(r, tok, name, ln, ppos);
219861d06d6bSBaptiste Daroussin 
219961d06d6bSBaptiste Daroussin 	/*
220061d06d6bSBaptiste Daroussin 	 * At the beginning of a `de' macro, clear the existing string
220161d06d6bSBaptiste Daroussin 	 * with the same name, if there is one.  New content will be
220261d06d6bSBaptiste Daroussin 	 * appended from roff_block_text() in multiline mode.
220361d06d6bSBaptiste Daroussin 	 */
220461d06d6bSBaptiste Daroussin 
220561d06d6bSBaptiste Daroussin 	if (tok == ROFF_de || tok == ROFF_dei) {
220661d06d6bSBaptiste Daroussin 		roff_setstrn(&r->strtab, name, namesz, "", 0, 0);
220761d06d6bSBaptiste Daroussin 		roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
220861d06d6bSBaptiste Daroussin 	} else if (tok == ROFF_am || tok == ROFF_ami) {
220961d06d6bSBaptiste Daroussin 		deftype = ROFFDEF_ANY;
221061d06d6bSBaptiste Daroussin 		value = roff_getstrn(r, iname, namesz, &deftype);
221161d06d6bSBaptiste Daroussin 		switch (deftype) {  /* Before appending, ... */
221261d06d6bSBaptiste Daroussin 		case ROFFDEF_PRE: /* copy predefined to user-defined. */
221361d06d6bSBaptiste Daroussin 			roff_setstrn(&r->strtab, name, namesz,
221461d06d6bSBaptiste Daroussin 			    value, strlen(value), 0);
221561d06d6bSBaptiste Daroussin 			break;
221661d06d6bSBaptiste Daroussin 		case ROFFDEF_REN: /* call original standard macro. */
221761d06d6bSBaptiste Daroussin 			csz = mandoc_asprintf(&call, ".%.*s \\$* \\\"\n",
221861d06d6bSBaptiste Daroussin 			    (int)strlen(value), value);
221961d06d6bSBaptiste Daroussin 			roff_setstrn(&r->strtab, name, namesz, call, csz, 0);
222061d06d6bSBaptiste Daroussin 			roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
222161d06d6bSBaptiste Daroussin 			free(call);
222261d06d6bSBaptiste Daroussin 			break;
222361d06d6bSBaptiste Daroussin 		case ROFFDEF_STD:  /* rename and call standard macro. */
222461d06d6bSBaptiste Daroussin 			rsz = mandoc_asprintf(&rname, "__%s_renamed", name);
222561d06d6bSBaptiste Daroussin 			roff_setstrn(&r->rentab, rname, rsz, name, namesz, 0);
222661d06d6bSBaptiste Daroussin 			csz = mandoc_asprintf(&call, ".%.*s \\$* \\\"\n",
222761d06d6bSBaptiste Daroussin 			    (int)rsz, rname);
222861d06d6bSBaptiste Daroussin 			roff_setstrn(&r->strtab, name, namesz, call, csz, 0);
222961d06d6bSBaptiste Daroussin 			free(call);
223061d06d6bSBaptiste Daroussin 			free(rname);
223161d06d6bSBaptiste Daroussin 			break;
223261d06d6bSBaptiste Daroussin 		default:
223361d06d6bSBaptiste Daroussin 			break;
223461d06d6bSBaptiste Daroussin 		}
223561d06d6bSBaptiste Daroussin 	}
223661d06d6bSBaptiste Daroussin 
223761d06d6bSBaptiste Daroussin 	if (*cp == '\0')
223861d06d6bSBaptiste Daroussin 		return ROFF_IGN;
223961d06d6bSBaptiste Daroussin 
224061d06d6bSBaptiste Daroussin 	/* Get the custom end marker. */
224161d06d6bSBaptiste Daroussin 
224261d06d6bSBaptiste Daroussin 	iname = cp;
224361d06d6bSBaptiste Daroussin 	namesz = roff_getname(r, &cp, ln, ppos);
224461d06d6bSBaptiste Daroussin 
224561d06d6bSBaptiste Daroussin 	/* Resolve the end marker if it is indirect. */
224661d06d6bSBaptiste Daroussin 
224761d06d6bSBaptiste Daroussin 	if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
224861d06d6bSBaptiste Daroussin 		deftype = ROFFDEF_USER;
224961d06d6bSBaptiste Daroussin 		name = roff_getstrn(r, iname, namesz, &deftype);
225061d06d6bSBaptiste Daroussin 		if (name == NULL) {
22517295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_STR_UNDEF,
22527295610fSBaptiste Daroussin 			    ln, (int)(iname - buf->buf),
225361d06d6bSBaptiste Daroussin 			    "%.*s", (int)namesz, iname);
225461d06d6bSBaptiste Daroussin 			namesz = 0;
225561d06d6bSBaptiste Daroussin 		} else
225661d06d6bSBaptiste Daroussin 			namesz = strlen(name);
225761d06d6bSBaptiste Daroussin 	} else
225861d06d6bSBaptiste Daroussin 		name = iname;
225961d06d6bSBaptiste Daroussin 
226061d06d6bSBaptiste Daroussin 	if (namesz)
226161d06d6bSBaptiste Daroussin 		r->last->end = mandoc_strndup(name, namesz);
226261d06d6bSBaptiste Daroussin 
226361d06d6bSBaptiste Daroussin 	if (*cp != '\0')
22647295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_EXCESS,
226561d06d6bSBaptiste Daroussin 		    ln, pos, ".%s ... %s", roff_name[tok], cp);
226661d06d6bSBaptiste Daroussin 
226761d06d6bSBaptiste Daroussin 	return ROFF_IGN;
226861d06d6bSBaptiste Daroussin }
226961d06d6bSBaptiste Daroussin 
22707295610fSBaptiste Daroussin static int
roff_block_sub(ROFF_ARGS)227161d06d6bSBaptiste Daroussin roff_block_sub(ROFF_ARGS)
227261d06d6bSBaptiste Daroussin {
227361d06d6bSBaptiste Daroussin 	enum roff_tok	t;
227461d06d6bSBaptiste Daroussin 	int		i, j;
227561d06d6bSBaptiste Daroussin 
227661d06d6bSBaptiste Daroussin 	/*
227761d06d6bSBaptiste Daroussin 	 * First check whether a custom macro exists at this level.  If
227861d06d6bSBaptiste Daroussin 	 * it does, then check against it.  This is some of groff's
227961d06d6bSBaptiste Daroussin 	 * stranger behaviours.  If we encountered a custom end-scope
228061d06d6bSBaptiste Daroussin 	 * tag and that tag also happens to be a "real" macro, then we
228161d06d6bSBaptiste Daroussin 	 * need to try interpreting it again as a real macro.  If it's
228261d06d6bSBaptiste Daroussin 	 * not, then return ignore.  Else continue.
228361d06d6bSBaptiste Daroussin 	 */
228461d06d6bSBaptiste Daroussin 
228561d06d6bSBaptiste Daroussin 	if (r->last->end) {
228661d06d6bSBaptiste Daroussin 		for (i = pos, j = 0; r->last->end[j]; j++, i++)
228761d06d6bSBaptiste Daroussin 			if (buf->buf[i] != r->last->end[j])
228861d06d6bSBaptiste Daroussin 				break;
228961d06d6bSBaptiste Daroussin 
229061d06d6bSBaptiste Daroussin 		if (r->last->end[j] == '\0' &&
229161d06d6bSBaptiste Daroussin 		    (buf->buf[i] == '\0' ||
229261d06d6bSBaptiste Daroussin 		     buf->buf[i] == ' ' ||
229361d06d6bSBaptiste Daroussin 		     buf->buf[i] == '\t')) {
229461d06d6bSBaptiste Daroussin 			roffnode_pop(r);
229561d06d6bSBaptiste Daroussin 			roffnode_cleanscope(r);
229661d06d6bSBaptiste Daroussin 
229761d06d6bSBaptiste Daroussin 			while (buf->buf[i] == ' ' || buf->buf[i] == '\t')
229861d06d6bSBaptiste Daroussin 				i++;
229961d06d6bSBaptiste Daroussin 
230061d06d6bSBaptiste Daroussin 			pos = i;
230161d06d6bSBaptiste Daroussin 			if (roff_parse(r, buf->buf, &pos, ln, ppos) !=
230261d06d6bSBaptiste Daroussin 			    TOKEN_NONE)
230361d06d6bSBaptiste Daroussin 				return ROFF_RERUN;
230461d06d6bSBaptiste Daroussin 			return ROFF_IGN;
230561d06d6bSBaptiste Daroussin 		}
230661d06d6bSBaptiste Daroussin 	}
230761d06d6bSBaptiste Daroussin 
230861d06d6bSBaptiste Daroussin 	/*
230961d06d6bSBaptiste Daroussin 	 * If we have no custom end-query or lookup failed, then try
231061d06d6bSBaptiste Daroussin 	 * pulling it out of the hashtable.
231161d06d6bSBaptiste Daroussin 	 */
231261d06d6bSBaptiste Daroussin 
231361d06d6bSBaptiste Daroussin 	t = roff_parse(r, buf->buf, &pos, ln, ppos);
231461d06d6bSBaptiste Daroussin 
231561d06d6bSBaptiste Daroussin 	if (t != ROFF_cblock) {
231661d06d6bSBaptiste Daroussin 		if (tok != ROFF_ig)
231761d06d6bSBaptiste Daroussin 			roff_setstr(r, r->last->name, buf->buf + ppos, 2);
231861d06d6bSBaptiste Daroussin 		return ROFF_IGN;
231961d06d6bSBaptiste Daroussin 	}
232061d06d6bSBaptiste Daroussin 
232161d06d6bSBaptiste Daroussin 	return (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs);
232261d06d6bSBaptiste Daroussin }
232361d06d6bSBaptiste Daroussin 
23247295610fSBaptiste Daroussin static int
roff_block_text(ROFF_ARGS)232561d06d6bSBaptiste Daroussin roff_block_text(ROFF_ARGS)
232661d06d6bSBaptiste Daroussin {
232761d06d6bSBaptiste Daroussin 
232861d06d6bSBaptiste Daroussin 	if (tok != ROFF_ig)
232961d06d6bSBaptiste Daroussin 		roff_setstr(r, r->last->name, buf->buf + pos, 2);
233061d06d6bSBaptiste Daroussin 
233161d06d6bSBaptiste Daroussin 	return ROFF_IGN;
233261d06d6bSBaptiste Daroussin }
233361d06d6bSBaptiste Daroussin 
2334*6d38604fSBaptiste Daroussin /*
2335*6d38604fSBaptiste Daroussin  * Check for a closing "\}" and handle it.
2336*6d38604fSBaptiste Daroussin  * In this function, the final "int *offs" argument is used for
2337*6d38604fSBaptiste Daroussin  * different purposes than elsewhere:
2338*6d38604fSBaptiste Daroussin  * Input: *offs == 0: caller wants to discard arguments following \}
2339*6d38604fSBaptiste Daroussin  *        *offs == 1: caller wants to preserve text following \}
2340*6d38604fSBaptiste Daroussin  * Output: *offs = 0: tell caller to discard input line
2341*6d38604fSBaptiste Daroussin  *         *offs = 1: tell caller to use input line
2342*6d38604fSBaptiste Daroussin  */
23437295610fSBaptiste Daroussin static int
roff_cond_checkend(ROFF_ARGS)2344*6d38604fSBaptiste Daroussin roff_cond_checkend(ROFF_ARGS)
234561d06d6bSBaptiste Daroussin {
234661d06d6bSBaptiste Daroussin 	char		*ep;
23477295610fSBaptiste Daroussin 	int		 endloop, irc, rr;
234861d06d6bSBaptiste Daroussin 
23497295610fSBaptiste Daroussin 	irc = ROFF_IGN;
235061d06d6bSBaptiste Daroussin 	rr = r->last->rule;
23517295610fSBaptiste Daroussin 	endloop = tok != ROFF_while ? ROFF_IGN :
23527295610fSBaptiste Daroussin 	    rr ? ROFF_LOOPCONT : ROFF_LOOPEXIT;
23537295610fSBaptiste Daroussin 	if (roffnode_cleanscope(r))
23547295610fSBaptiste Daroussin 		irc |= endloop;
235561d06d6bSBaptiste Daroussin 
235661d06d6bSBaptiste Daroussin 	/*
2357*6d38604fSBaptiste Daroussin 	 * If "\}" occurs on a macro line without a preceding macro or
2358*6d38604fSBaptiste Daroussin 	 * a text line contains nothing else, drop the line completely.
235961d06d6bSBaptiste Daroussin 	 */
236061d06d6bSBaptiste Daroussin 
236161d06d6bSBaptiste Daroussin 	ep = buf->buf + pos;
2362*6d38604fSBaptiste Daroussin 	if (ep[0] == '\\' && ep[1] == '}' && (ep[2] == '\0' || *offs == 0))
236361d06d6bSBaptiste Daroussin 		rr = 0;
236461d06d6bSBaptiste Daroussin 
23657295610fSBaptiste Daroussin 	/*
2366*6d38604fSBaptiste Daroussin 	 * The closing delimiter "\}" rewinds the conditional scope
23677295610fSBaptiste Daroussin 	 * but is otherwise ignored when interpreting the line.
23687295610fSBaptiste Daroussin 	 */
236961d06d6bSBaptiste Daroussin 
237061d06d6bSBaptiste Daroussin 	while ((ep = strchr(ep, '\\')) != NULL) {
237161d06d6bSBaptiste Daroussin 		switch (ep[1]) {
237261d06d6bSBaptiste Daroussin 		case '}':
2373*6d38604fSBaptiste Daroussin 			if (ep[2] == '\0')
2374*6d38604fSBaptiste Daroussin 				ep[0] = '\0';
2375*6d38604fSBaptiste Daroussin 			else if (rr)
2376*6d38604fSBaptiste Daroussin 				ep[1] = '&';
2377*6d38604fSBaptiste Daroussin 			else
237861d06d6bSBaptiste Daroussin 				memmove(ep, ep + 2, strlen(ep + 2) + 1);
23797295610fSBaptiste Daroussin 			if (roff_ccond(r, ln, ep - buf->buf))
23807295610fSBaptiste Daroussin 				irc |= endloop;
238161d06d6bSBaptiste Daroussin 			break;
238261d06d6bSBaptiste Daroussin 		case '\0':
238361d06d6bSBaptiste Daroussin 			++ep;
238461d06d6bSBaptiste Daroussin 			break;
238561d06d6bSBaptiste Daroussin 		default:
238661d06d6bSBaptiste Daroussin 			ep += 2;
238761d06d6bSBaptiste Daroussin 			break;
238861d06d6bSBaptiste Daroussin 		}
238961d06d6bSBaptiste Daroussin 	}
2390*6d38604fSBaptiste Daroussin 	*offs = rr;
2391*6d38604fSBaptiste Daroussin 	return irc;
2392*6d38604fSBaptiste Daroussin }
2393*6d38604fSBaptiste Daroussin 
2394*6d38604fSBaptiste Daroussin /*
2395*6d38604fSBaptiste Daroussin  * Parse and process a request or macro line in conditional scope.
2396*6d38604fSBaptiste Daroussin  */
2397*6d38604fSBaptiste Daroussin static int
roff_cond_sub(ROFF_ARGS)2398*6d38604fSBaptiste Daroussin roff_cond_sub(ROFF_ARGS)
2399*6d38604fSBaptiste Daroussin {
2400*6d38604fSBaptiste Daroussin 	struct roffnode	*bl;
2401*6d38604fSBaptiste Daroussin 	int		 irc, rr;
2402*6d38604fSBaptiste Daroussin 	enum roff_tok	 t;
2403*6d38604fSBaptiste Daroussin 
2404*6d38604fSBaptiste Daroussin 	rr = 0;  /* If arguments follow "\}", skip them. */
2405*6d38604fSBaptiste Daroussin 	irc = roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr);
2406*6d38604fSBaptiste Daroussin 	t = roff_parse(r, buf->buf, &pos, ln, ppos);
2407*6d38604fSBaptiste Daroussin 
2408*6d38604fSBaptiste Daroussin 	/* For now, let high level macros abort .ce mode. */
2409*6d38604fSBaptiste Daroussin 
2410*6d38604fSBaptiste Daroussin 	if (roffce_node != NULL &&
2411*6d38604fSBaptiste Daroussin 	    (t == TOKEN_NONE || t == ROFF_Dd || t == ROFF_EQ ||
2412*6d38604fSBaptiste Daroussin              t == ROFF_TH || t == ROFF_TS)) {
2413*6d38604fSBaptiste Daroussin 		r->man->last = roffce_node;
2414*6d38604fSBaptiste Daroussin 		r->man->next = ROFF_NEXT_SIBLING;
2415*6d38604fSBaptiste Daroussin 		roffce_lines = 0;
2416*6d38604fSBaptiste Daroussin 		roffce_node = NULL;
2417*6d38604fSBaptiste Daroussin 	}
241861d06d6bSBaptiste Daroussin 
241961d06d6bSBaptiste Daroussin 	/*
242061d06d6bSBaptiste Daroussin 	 * Fully handle known macros when they are structurally
242161d06d6bSBaptiste Daroussin 	 * required or when the conditional evaluated to true.
242261d06d6bSBaptiste Daroussin 	 */
242361d06d6bSBaptiste Daroussin 
242445a5aec3SBaptiste Daroussin 	if (t == ROFF_break) {
242545a5aec3SBaptiste Daroussin 		if (irc & ROFF_LOOPMASK)
242645a5aec3SBaptiste Daroussin 			irc = ROFF_IGN | ROFF_LOOPEXIT;
242745a5aec3SBaptiste Daroussin 		else if (rr) {
242845a5aec3SBaptiste Daroussin 			for (bl = r->last; bl != NULL; bl = bl->parent) {
242945a5aec3SBaptiste Daroussin 				bl->rule = 0;
243045a5aec3SBaptiste Daroussin 				if (bl->tok == ROFF_while)
243145a5aec3SBaptiste Daroussin 					break;
243245a5aec3SBaptiste Daroussin 			}
243345a5aec3SBaptiste Daroussin 		}
243445a5aec3SBaptiste Daroussin 	} else if (t != TOKEN_NONE &&
243545a5aec3SBaptiste Daroussin 	    (rr || roffs[t].flags & ROFFMAC_STRUCT))
243645a5aec3SBaptiste Daroussin 		irc |= (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs);
243745a5aec3SBaptiste Daroussin 	else
243845a5aec3SBaptiste Daroussin 		irc |= rr ? ROFF_CONT : ROFF_IGN;
24397295610fSBaptiste Daroussin 	return irc;
244061d06d6bSBaptiste Daroussin }
244161d06d6bSBaptiste Daroussin 
2442*6d38604fSBaptiste Daroussin /*
2443*6d38604fSBaptiste Daroussin  * Parse and process a text line in conditional scope.
2444*6d38604fSBaptiste Daroussin  */
24457295610fSBaptiste Daroussin static int
roff_cond_text(ROFF_ARGS)244661d06d6bSBaptiste Daroussin roff_cond_text(ROFF_ARGS)
244761d06d6bSBaptiste Daroussin {
2448*6d38604fSBaptiste Daroussin 	int	 irc, rr;
244961d06d6bSBaptiste Daroussin 
2450*6d38604fSBaptiste Daroussin 	rr = 1;  /* If arguments follow "\}", preserve them. */
2451*6d38604fSBaptiste Daroussin 	irc = roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr);
24527295610fSBaptiste Daroussin 	if (rr)
24537295610fSBaptiste Daroussin 		irc |= ROFF_CONT;
24547295610fSBaptiste Daroussin 	return irc;
245561d06d6bSBaptiste Daroussin }
245661d06d6bSBaptiste Daroussin 
245761d06d6bSBaptiste Daroussin /* --- handling of numeric and conditional expressions -------------------- */
245861d06d6bSBaptiste Daroussin 
245961d06d6bSBaptiste Daroussin /*
246061d06d6bSBaptiste Daroussin  * Parse a single signed integer number.  Stop at the first non-digit.
246161d06d6bSBaptiste Daroussin  * If there is at least one digit, return success and advance the
246261d06d6bSBaptiste Daroussin  * parse point, else return failure and let the parse point unchanged.
246361d06d6bSBaptiste Daroussin  * Ignore overflows, treat them just like the C language.
246461d06d6bSBaptiste Daroussin  */
246561d06d6bSBaptiste Daroussin static int
roff_getnum(const char * v,int * pos,int * res,int flags)246661d06d6bSBaptiste Daroussin roff_getnum(const char *v, int *pos, int *res, int flags)
246761d06d6bSBaptiste Daroussin {
246861d06d6bSBaptiste Daroussin 	int	 myres, scaled, n, p;
246961d06d6bSBaptiste Daroussin 
247061d06d6bSBaptiste Daroussin 	if (NULL == res)
247161d06d6bSBaptiste Daroussin 		res = &myres;
247261d06d6bSBaptiste Daroussin 
247361d06d6bSBaptiste Daroussin 	p = *pos;
247461d06d6bSBaptiste Daroussin 	n = v[p] == '-';
247561d06d6bSBaptiste Daroussin 	if (n || v[p] == '+')
247661d06d6bSBaptiste Daroussin 		p++;
247761d06d6bSBaptiste Daroussin 
247861d06d6bSBaptiste Daroussin 	if (flags & ROFFNUM_WHITE)
247961d06d6bSBaptiste Daroussin 		while (isspace((unsigned char)v[p]))
248061d06d6bSBaptiste Daroussin 			p++;
248161d06d6bSBaptiste Daroussin 
248261d06d6bSBaptiste Daroussin 	for (*res = 0; isdigit((unsigned char)v[p]); p++)
248361d06d6bSBaptiste Daroussin 		*res = 10 * *res + v[p] - '0';
248461d06d6bSBaptiste Daroussin 	if (p == *pos + n)
248561d06d6bSBaptiste Daroussin 		return 0;
248661d06d6bSBaptiste Daroussin 
248761d06d6bSBaptiste Daroussin 	if (n)
248861d06d6bSBaptiste Daroussin 		*res = -*res;
248961d06d6bSBaptiste Daroussin 
249061d06d6bSBaptiste Daroussin 	/* Each number may be followed by one optional scaling unit. */
249161d06d6bSBaptiste Daroussin 
249261d06d6bSBaptiste Daroussin 	switch (v[p]) {
249361d06d6bSBaptiste Daroussin 	case 'f':
249461d06d6bSBaptiste Daroussin 		scaled = *res * 65536;
249561d06d6bSBaptiste Daroussin 		break;
249661d06d6bSBaptiste Daroussin 	case 'i':
249761d06d6bSBaptiste Daroussin 		scaled = *res * 240;
249861d06d6bSBaptiste Daroussin 		break;
249961d06d6bSBaptiste Daroussin 	case 'c':
250061d06d6bSBaptiste Daroussin 		scaled = *res * 240 / 2.54;
250161d06d6bSBaptiste Daroussin 		break;
250261d06d6bSBaptiste Daroussin 	case 'v':
250361d06d6bSBaptiste Daroussin 	case 'P':
250461d06d6bSBaptiste Daroussin 		scaled = *res * 40;
250561d06d6bSBaptiste Daroussin 		break;
250661d06d6bSBaptiste Daroussin 	case 'm':
250761d06d6bSBaptiste Daroussin 	case 'n':
250861d06d6bSBaptiste Daroussin 		scaled = *res * 24;
250961d06d6bSBaptiste Daroussin 		break;
251061d06d6bSBaptiste Daroussin 	case 'p':
251161d06d6bSBaptiste Daroussin 		scaled = *res * 10 / 3;
251261d06d6bSBaptiste Daroussin 		break;
251361d06d6bSBaptiste Daroussin 	case 'u':
251461d06d6bSBaptiste Daroussin 		scaled = *res;
251561d06d6bSBaptiste Daroussin 		break;
251661d06d6bSBaptiste Daroussin 	case 'M':
251761d06d6bSBaptiste Daroussin 		scaled = *res * 6 / 25;
251861d06d6bSBaptiste Daroussin 		break;
251961d06d6bSBaptiste Daroussin 	default:
252061d06d6bSBaptiste Daroussin 		scaled = *res;
252161d06d6bSBaptiste Daroussin 		p--;
252261d06d6bSBaptiste Daroussin 		break;
252361d06d6bSBaptiste Daroussin 	}
252461d06d6bSBaptiste Daroussin 	if (flags & ROFFNUM_SCALE)
252561d06d6bSBaptiste Daroussin 		*res = scaled;
252661d06d6bSBaptiste Daroussin 
252761d06d6bSBaptiste Daroussin 	*pos = p + 1;
252861d06d6bSBaptiste Daroussin 	return 1;
252961d06d6bSBaptiste Daroussin }
253061d06d6bSBaptiste Daroussin 
253161d06d6bSBaptiste Daroussin /*
253261d06d6bSBaptiste Daroussin  * Evaluate a string comparison condition.
253361d06d6bSBaptiste Daroussin  * The first character is the delimiter.
253461d06d6bSBaptiste Daroussin  * Succeed if the string up to its second occurrence
253561d06d6bSBaptiste Daroussin  * matches the string up to its third occurence.
253661d06d6bSBaptiste Daroussin  * Advance the cursor after the third occurrence
253761d06d6bSBaptiste Daroussin  * or lacking that, to the end of the line.
253861d06d6bSBaptiste Daroussin  */
253961d06d6bSBaptiste Daroussin static int
roff_evalstrcond(const char * v,int * pos)254061d06d6bSBaptiste Daroussin roff_evalstrcond(const char *v, int *pos)
254161d06d6bSBaptiste Daroussin {
254261d06d6bSBaptiste Daroussin 	const char	*s1, *s2, *s3;
254361d06d6bSBaptiste Daroussin 	int		 match;
254461d06d6bSBaptiste Daroussin 
254561d06d6bSBaptiste Daroussin 	match = 0;
254661d06d6bSBaptiste Daroussin 	s1 = v + *pos;		/* initial delimiter */
254761d06d6bSBaptiste Daroussin 	s2 = s1 + 1;		/* for scanning the first string */
254861d06d6bSBaptiste Daroussin 	s3 = strchr(s2, *s1);	/* for scanning the second string */
254961d06d6bSBaptiste Daroussin 
255061d06d6bSBaptiste Daroussin 	if (NULL == s3)		/* found no middle delimiter */
255161d06d6bSBaptiste Daroussin 		goto out;
255261d06d6bSBaptiste Daroussin 
255361d06d6bSBaptiste Daroussin 	while ('\0' != *++s3) {
255461d06d6bSBaptiste Daroussin 		if (*s2 != *s3) {  /* mismatch */
255561d06d6bSBaptiste Daroussin 			s3 = strchr(s3, *s1);
255661d06d6bSBaptiste Daroussin 			break;
255761d06d6bSBaptiste Daroussin 		}
255861d06d6bSBaptiste Daroussin 		if (*s3 == *s1) {  /* found the final delimiter */
255961d06d6bSBaptiste Daroussin 			match = 1;
256061d06d6bSBaptiste Daroussin 			break;
256161d06d6bSBaptiste Daroussin 		}
256261d06d6bSBaptiste Daroussin 		s2++;
256361d06d6bSBaptiste Daroussin 	}
256461d06d6bSBaptiste Daroussin 
256561d06d6bSBaptiste Daroussin out:
256661d06d6bSBaptiste Daroussin 	if (NULL == s3)
256761d06d6bSBaptiste Daroussin 		s3 = strchr(s2, '\0');
256861d06d6bSBaptiste Daroussin 	else if (*s3 != '\0')
256961d06d6bSBaptiste Daroussin 		s3++;
257061d06d6bSBaptiste Daroussin 	*pos = s3 - v;
257161d06d6bSBaptiste Daroussin 	return match;
257261d06d6bSBaptiste Daroussin }
257361d06d6bSBaptiste Daroussin 
257461d06d6bSBaptiste Daroussin /*
257561d06d6bSBaptiste Daroussin  * Evaluate an optionally negated single character, numerical,
257661d06d6bSBaptiste Daroussin  * or string condition.
257761d06d6bSBaptiste Daroussin  */
257861d06d6bSBaptiste Daroussin static int
roff_evalcond(struct roff * r,int ln,char * v,int * pos)257961d06d6bSBaptiste Daroussin roff_evalcond(struct roff *r, int ln, char *v, int *pos)
258061d06d6bSBaptiste Daroussin {
25817295610fSBaptiste Daroussin 	const char	*start, *end;
258261d06d6bSBaptiste Daroussin 	char		*cp, *name;
258361d06d6bSBaptiste Daroussin 	size_t		 sz;
25847295610fSBaptiste Daroussin 	int		 deftype, len, number, savepos, istrue, wanttrue;
258561d06d6bSBaptiste Daroussin 
258661d06d6bSBaptiste Daroussin 	if ('!' == v[*pos]) {
258761d06d6bSBaptiste Daroussin 		wanttrue = 0;
258861d06d6bSBaptiste Daroussin 		(*pos)++;
258961d06d6bSBaptiste Daroussin 	} else
259061d06d6bSBaptiste Daroussin 		wanttrue = 1;
259161d06d6bSBaptiste Daroussin 
259261d06d6bSBaptiste Daroussin 	switch (v[*pos]) {
259361d06d6bSBaptiste Daroussin 	case '\0':
259461d06d6bSBaptiste Daroussin 		return 0;
259561d06d6bSBaptiste Daroussin 	case 'n':
259661d06d6bSBaptiste Daroussin 	case 'o':
259761d06d6bSBaptiste Daroussin 		(*pos)++;
259861d06d6bSBaptiste Daroussin 		return wanttrue;
259961d06d6bSBaptiste Daroussin 	case 'e':
260061d06d6bSBaptiste Daroussin 	case 't':
260161d06d6bSBaptiste Daroussin 	case 'v':
260261d06d6bSBaptiste Daroussin 		(*pos)++;
260361d06d6bSBaptiste Daroussin 		return !wanttrue;
26047295610fSBaptiste Daroussin 	case 'c':
26057295610fSBaptiste Daroussin 		do {
26067295610fSBaptiste Daroussin 			(*pos)++;
26077295610fSBaptiste Daroussin 		} while (v[*pos] == ' ');
26087295610fSBaptiste Daroussin 
26097295610fSBaptiste Daroussin 		/*
26107295610fSBaptiste Daroussin 		 * Quirk for groff compatibility:
26117295610fSBaptiste Daroussin 		 * The horizontal tab is neither available nor unavailable.
26127295610fSBaptiste Daroussin 		 */
26137295610fSBaptiste Daroussin 
26147295610fSBaptiste Daroussin 		if (v[*pos] == '\t') {
26157295610fSBaptiste Daroussin 			(*pos)++;
26167295610fSBaptiste Daroussin 			return 0;
26177295610fSBaptiste Daroussin 		}
26187295610fSBaptiste Daroussin 
26197295610fSBaptiste Daroussin 		/* Printable ASCII characters are available. */
26207295610fSBaptiste Daroussin 
26217295610fSBaptiste Daroussin 		if (v[*pos] != '\\') {
26227295610fSBaptiste Daroussin 			(*pos)++;
26237295610fSBaptiste Daroussin 			return wanttrue;
26247295610fSBaptiste Daroussin 		}
26257295610fSBaptiste Daroussin 
26267295610fSBaptiste Daroussin 		end = v + ++*pos;
26277295610fSBaptiste Daroussin 		switch (mandoc_escape(&end, &start, &len)) {
26287295610fSBaptiste Daroussin 		case ESCAPE_SPECIAL:
26297295610fSBaptiste Daroussin 			istrue = mchars_spec2cp(start, len) != -1;
26307295610fSBaptiste Daroussin 			break;
26317295610fSBaptiste Daroussin 		case ESCAPE_UNICODE:
26327295610fSBaptiste Daroussin 			istrue = 1;
26337295610fSBaptiste Daroussin 			break;
26347295610fSBaptiste Daroussin 		case ESCAPE_NUMBERED:
26357295610fSBaptiste Daroussin 			istrue = mchars_num2char(start, len) != -1;
26367295610fSBaptiste Daroussin 			break;
26377295610fSBaptiste Daroussin 		default:
26387295610fSBaptiste Daroussin 			istrue = !wanttrue;
26397295610fSBaptiste Daroussin 			break;
26407295610fSBaptiste Daroussin 		}
26417295610fSBaptiste Daroussin 		*pos = end - v;
26427295610fSBaptiste Daroussin 		return istrue == wanttrue;
264361d06d6bSBaptiste Daroussin 	case 'd':
264461d06d6bSBaptiste Daroussin 	case 'r':
264561d06d6bSBaptiste Daroussin 		cp = v + *pos + 1;
264661d06d6bSBaptiste Daroussin 		while (*cp == ' ')
264761d06d6bSBaptiste Daroussin 			cp++;
264861d06d6bSBaptiste Daroussin 		name = cp;
264961d06d6bSBaptiste Daroussin 		sz = roff_getname(r, &cp, ln, cp - v);
265061d06d6bSBaptiste Daroussin 		if (sz == 0)
265161d06d6bSBaptiste Daroussin 			istrue = 0;
265261d06d6bSBaptiste Daroussin 		else if (v[*pos] == 'r')
265361d06d6bSBaptiste Daroussin 			istrue = roff_hasregn(r, name, sz);
265461d06d6bSBaptiste Daroussin 		else {
265561d06d6bSBaptiste Daroussin 			deftype = ROFFDEF_ANY;
265661d06d6bSBaptiste Daroussin 		        roff_getstrn(r, name, sz, &deftype);
265761d06d6bSBaptiste Daroussin 			istrue = !!deftype;
265861d06d6bSBaptiste Daroussin 		}
26597295610fSBaptiste Daroussin 		*pos = (name + sz) - v;
266061d06d6bSBaptiste Daroussin 		return istrue == wanttrue;
266161d06d6bSBaptiste Daroussin 	default:
266261d06d6bSBaptiste Daroussin 		break;
266361d06d6bSBaptiste Daroussin 	}
266461d06d6bSBaptiste Daroussin 
266561d06d6bSBaptiste Daroussin 	savepos = *pos;
266661d06d6bSBaptiste Daroussin 	if (roff_evalnum(r, ln, v, pos, &number, ROFFNUM_SCALE))
266761d06d6bSBaptiste Daroussin 		return (number > 0) == wanttrue;
266861d06d6bSBaptiste Daroussin 	else if (*pos == savepos)
266961d06d6bSBaptiste Daroussin 		return roff_evalstrcond(v, pos) == wanttrue;
267061d06d6bSBaptiste Daroussin 	else
267161d06d6bSBaptiste Daroussin 		return 0;
267261d06d6bSBaptiste Daroussin }
267361d06d6bSBaptiste Daroussin 
26747295610fSBaptiste Daroussin static int
roff_line_ignore(ROFF_ARGS)267561d06d6bSBaptiste Daroussin roff_line_ignore(ROFF_ARGS)
267661d06d6bSBaptiste Daroussin {
267761d06d6bSBaptiste Daroussin 
267861d06d6bSBaptiste Daroussin 	return ROFF_IGN;
267961d06d6bSBaptiste Daroussin }
268061d06d6bSBaptiste Daroussin 
26817295610fSBaptiste Daroussin static int
roff_insec(ROFF_ARGS)268261d06d6bSBaptiste Daroussin roff_insec(ROFF_ARGS)
268361d06d6bSBaptiste Daroussin {
268461d06d6bSBaptiste Daroussin 
26857295610fSBaptiste Daroussin 	mandoc_msg(MANDOCERR_REQ_INSEC, ln, ppos, "%s", roff_name[tok]);
268661d06d6bSBaptiste Daroussin 	return ROFF_IGN;
268761d06d6bSBaptiste Daroussin }
268861d06d6bSBaptiste Daroussin 
26897295610fSBaptiste Daroussin static int
roff_unsupp(ROFF_ARGS)269061d06d6bSBaptiste Daroussin roff_unsupp(ROFF_ARGS)
269161d06d6bSBaptiste Daroussin {
269261d06d6bSBaptiste Daroussin 
26937295610fSBaptiste Daroussin 	mandoc_msg(MANDOCERR_REQ_UNSUPP, ln, ppos, "%s", roff_name[tok]);
269461d06d6bSBaptiste Daroussin 	return ROFF_IGN;
269561d06d6bSBaptiste Daroussin }
269661d06d6bSBaptiste Daroussin 
26977295610fSBaptiste Daroussin static int
roff_cond(ROFF_ARGS)269861d06d6bSBaptiste Daroussin roff_cond(ROFF_ARGS)
269961d06d6bSBaptiste Daroussin {
27007295610fSBaptiste Daroussin 	int	 irc;
270161d06d6bSBaptiste Daroussin 
270261d06d6bSBaptiste Daroussin 	roffnode_push(r, tok, NULL, ln, ppos);
270361d06d6bSBaptiste Daroussin 
270461d06d6bSBaptiste Daroussin 	/*
270561d06d6bSBaptiste Daroussin 	 * An `.el' has no conditional body: it will consume the value
270661d06d6bSBaptiste Daroussin 	 * of the current rstack entry set in prior `ie' calls or
270761d06d6bSBaptiste Daroussin 	 * defaults to DENY.
270861d06d6bSBaptiste Daroussin 	 *
270961d06d6bSBaptiste Daroussin 	 * If we're not an `el', however, then evaluate the conditional.
271061d06d6bSBaptiste Daroussin 	 */
271161d06d6bSBaptiste Daroussin 
271261d06d6bSBaptiste Daroussin 	r->last->rule = tok == ROFF_el ?
271361d06d6bSBaptiste Daroussin 	    (r->rstackpos < 0 ? 0 : r->rstack[r->rstackpos--]) :
271461d06d6bSBaptiste Daroussin 	    roff_evalcond(r, ln, buf->buf, &pos);
271561d06d6bSBaptiste Daroussin 
271661d06d6bSBaptiste Daroussin 	/*
271761d06d6bSBaptiste Daroussin 	 * An if-else will put the NEGATION of the current evaluated
271861d06d6bSBaptiste Daroussin 	 * conditional into the stack of rules.
271961d06d6bSBaptiste Daroussin 	 */
272061d06d6bSBaptiste Daroussin 
272161d06d6bSBaptiste Daroussin 	if (tok == ROFF_ie) {
272261d06d6bSBaptiste Daroussin 		if (r->rstackpos + 1 == r->rstacksz) {
272361d06d6bSBaptiste Daroussin 			r->rstacksz += 16;
272461d06d6bSBaptiste Daroussin 			r->rstack = mandoc_reallocarray(r->rstack,
272561d06d6bSBaptiste Daroussin 			    r->rstacksz, sizeof(int));
272661d06d6bSBaptiste Daroussin 		}
272761d06d6bSBaptiste Daroussin 		r->rstack[++r->rstackpos] = !r->last->rule;
272861d06d6bSBaptiste Daroussin 	}
272961d06d6bSBaptiste Daroussin 
273061d06d6bSBaptiste Daroussin 	/* If the parent has false as its rule, then so do we. */
273161d06d6bSBaptiste Daroussin 
273261d06d6bSBaptiste Daroussin 	if (r->last->parent && !r->last->parent->rule)
273361d06d6bSBaptiste Daroussin 		r->last->rule = 0;
273461d06d6bSBaptiste Daroussin 
273561d06d6bSBaptiste Daroussin 	/*
273661d06d6bSBaptiste Daroussin 	 * Determine scope.
273761d06d6bSBaptiste Daroussin 	 * If there is nothing on the line after the conditional,
273861d06d6bSBaptiste Daroussin 	 * not even whitespace, use next-line scope.
27397295610fSBaptiste Daroussin 	 * Except that .while does not support next-line scope.
274061d06d6bSBaptiste Daroussin 	 */
274161d06d6bSBaptiste Daroussin 
27427295610fSBaptiste Daroussin 	if (buf->buf[pos] == '\0' && tok != ROFF_while) {
274361d06d6bSBaptiste Daroussin 		r->last->endspan = 2;
274461d06d6bSBaptiste Daroussin 		goto out;
274561d06d6bSBaptiste Daroussin 	}
274661d06d6bSBaptiste Daroussin 
274761d06d6bSBaptiste Daroussin 	while (buf->buf[pos] == ' ')
274861d06d6bSBaptiste Daroussin 		pos++;
274961d06d6bSBaptiste Daroussin 
275061d06d6bSBaptiste Daroussin 	/* An opening brace requests multiline scope. */
275161d06d6bSBaptiste Daroussin 
275261d06d6bSBaptiste Daroussin 	if (buf->buf[pos] == '\\' && buf->buf[pos + 1] == '{') {
275361d06d6bSBaptiste Daroussin 		r->last->endspan = -1;
275461d06d6bSBaptiste Daroussin 		pos += 2;
275561d06d6bSBaptiste Daroussin 		while (buf->buf[pos] == ' ')
275661d06d6bSBaptiste Daroussin 			pos++;
275761d06d6bSBaptiste Daroussin 		goto out;
275861d06d6bSBaptiste Daroussin 	}
275961d06d6bSBaptiste Daroussin 
276061d06d6bSBaptiste Daroussin 	/*
276161d06d6bSBaptiste Daroussin 	 * Anything else following the conditional causes
276261d06d6bSBaptiste Daroussin 	 * single-line scope.  Warn if the scope contains
276361d06d6bSBaptiste Daroussin 	 * nothing but trailing whitespace.
276461d06d6bSBaptiste Daroussin 	 */
276561d06d6bSBaptiste Daroussin 
276661d06d6bSBaptiste Daroussin 	if (buf->buf[pos] == '\0')
27677295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_COND_EMPTY,
27687295610fSBaptiste Daroussin 		    ln, ppos, "%s", roff_name[tok]);
276961d06d6bSBaptiste Daroussin 
277061d06d6bSBaptiste Daroussin 	r->last->endspan = 1;
277161d06d6bSBaptiste Daroussin 
277261d06d6bSBaptiste Daroussin out:
277361d06d6bSBaptiste Daroussin 	*offs = pos;
27747295610fSBaptiste Daroussin 	irc = ROFF_RERUN;
27757295610fSBaptiste Daroussin 	if (tok == ROFF_while)
27767295610fSBaptiste Daroussin 		irc |= ROFF_WHILE;
27777295610fSBaptiste Daroussin 	return irc;
277861d06d6bSBaptiste Daroussin }
277961d06d6bSBaptiste Daroussin 
27807295610fSBaptiste Daroussin static int
roff_ds(ROFF_ARGS)278161d06d6bSBaptiste Daroussin roff_ds(ROFF_ARGS)
278261d06d6bSBaptiste Daroussin {
278361d06d6bSBaptiste Daroussin 	char		*string;
278461d06d6bSBaptiste Daroussin 	const char	*name;
278561d06d6bSBaptiste Daroussin 	size_t		 namesz;
278661d06d6bSBaptiste Daroussin 
278761d06d6bSBaptiste Daroussin 	/* Ignore groff compatibility mode for now. */
278861d06d6bSBaptiste Daroussin 
278961d06d6bSBaptiste Daroussin 	if (tok == ROFF_ds1)
279061d06d6bSBaptiste Daroussin 		tok = ROFF_ds;
279161d06d6bSBaptiste Daroussin 	else if (tok == ROFF_as1)
279261d06d6bSBaptiste Daroussin 		tok = ROFF_as;
279361d06d6bSBaptiste Daroussin 
279461d06d6bSBaptiste Daroussin 	/*
279561d06d6bSBaptiste Daroussin 	 * The first word is the name of the string.
279661d06d6bSBaptiste Daroussin 	 * If it is empty or terminated by an escape sequence,
279761d06d6bSBaptiste Daroussin 	 * abort the `ds' request without defining anything.
279861d06d6bSBaptiste Daroussin 	 */
279961d06d6bSBaptiste Daroussin 
280061d06d6bSBaptiste Daroussin 	name = string = buf->buf + pos;
280161d06d6bSBaptiste Daroussin 	if (*name == '\0')
280261d06d6bSBaptiste Daroussin 		return ROFF_IGN;
280361d06d6bSBaptiste Daroussin 
280461d06d6bSBaptiste Daroussin 	namesz = roff_getname(r, &string, ln, pos);
28057295610fSBaptiste Daroussin 	switch (name[namesz]) {
28067295610fSBaptiste Daroussin 	case '\\':
280761d06d6bSBaptiste Daroussin 		return ROFF_IGN;
28087295610fSBaptiste Daroussin 	case '\t':
28097295610fSBaptiste Daroussin 		string = buf->buf + pos + namesz;
28107295610fSBaptiste Daroussin 		break;
28117295610fSBaptiste Daroussin 	default:
28127295610fSBaptiste Daroussin 		break;
28137295610fSBaptiste Daroussin 	}
281461d06d6bSBaptiste Daroussin 
281561d06d6bSBaptiste Daroussin 	/* Read past the initial double-quote, if any. */
281661d06d6bSBaptiste Daroussin 	if (*string == '"')
281761d06d6bSBaptiste Daroussin 		string++;
281861d06d6bSBaptiste Daroussin 
281961d06d6bSBaptiste Daroussin 	/* The rest is the value. */
282061d06d6bSBaptiste Daroussin 	roff_setstrn(&r->strtab, name, namesz, string, strlen(string),
282161d06d6bSBaptiste Daroussin 	    ROFF_as == tok);
282261d06d6bSBaptiste Daroussin 	roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
282361d06d6bSBaptiste Daroussin 	return ROFF_IGN;
282461d06d6bSBaptiste Daroussin }
282561d06d6bSBaptiste Daroussin 
282661d06d6bSBaptiste Daroussin /*
282761d06d6bSBaptiste Daroussin  * Parse a single operator, one or two characters long.
282861d06d6bSBaptiste Daroussin  * If the operator is recognized, return success and advance the
282961d06d6bSBaptiste Daroussin  * parse point, else return failure and let the parse point unchanged.
283061d06d6bSBaptiste Daroussin  */
283161d06d6bSBaptiste Daroussin static int
roff_getop(const char * v,int * pos,char * res)283261d06d6bSBaptiste Daroussin roff_getop(const char *v, int *pos, char *res)
283361d06d6bSBaptiste Daroussin {
283461d06d6bSBaptiste Daroussin 
283561d06d6bSBaptiste Daroussin 	*res = v[*pos];
283661d06d6bSBaptiste Daroussin 
283761d06d6bSBaptiste Daroussin 	switch (*res) {
283861d06d6bSBaptiste Daroussin 	case '+':
283961d06d6bSBaptiste Daroussin 	case '-':
284061d06d6bSBaptiste Daroussin 	case '*':
284161d06d6bSBaptiste Daroussin 	case '/':
284261d06d6bSBaptiste Daroussin 	case '%':
284361d06d6bSBaptiste Daroussin 	case '&':
284461d06d6bSBaptiste Daroussin 	case ':':
284561d06d6bSBaptiste Daroussin 		break;
284661d06d6bSBaptiste Daroussin 	case '<':
284761d06d6bSBaptiste Daroussin 		switch (v[*pos + 1]) {
284861d06d6bSBaptiste Daroussin 		case '=':
284961d06d6bSBaptiste Daroussin 			*res = 'l';
285061d06d6bSBaptiste Daroussin 			(*pos)++;
285161d06d6bSBaptiste Daroussin 			break;
285261d06d6bSBaptiste Daroussin 		case '>':
285361d06d6bSBaptiste Daroussin 			*res = '!';
285461d06d6bSBaptiste Daroussin 			(*pos)++;
285561d06d6bSBaptiste Daroussin 			break;
285661d06d6bSBaptiste Daroussin 		case '?':
285761d06d6bSBaptiste Daroussin 			*res = 'i';
285861d06d6bSBaptiste Daroussin 			(*pos)++;
285961d06d6bSBaptiste Daroussin 			break;
286061d06d6bSBaptiste Daroussin 		default:
286161d06d6bSBaptiste Daroussin 			break;
286261d06d6bSBaptiste Daroussin 		}
286361d06d6bSBaptiste Daroussin 		break;
286461d06d6bSBaptiste Daroussin 	case '>':
286561d06d6bSBaptiste Daroussin 		switch (v[*pos + 1]) {
286661d06d6bSBaptiste Daroussin 		case '=':
286761d06d6bSBaptiste Daroussin 			*res = 'g';
286861d06d6bSBaptiste Daroussin 			(*pos)++;
286961d06d6bSBaptiste Daroussin 			break;
287061d06d6bSBaptiste Daroussin 		case '?':
287161d06d6bSBaptiste Daroussin 			*res = 'a';
287261d06d6bSBaptiste Daroussin 			(*pos)++;
287361d06d6bSBaptiste Daroussin 			break;
287461d06d6bSBaptiste Daroussin 		default:
287561d06d6bSBaptiste Daroussin 			break;
287661d06d6bSBaptiste Daroussin 		}
287761d06d6bSBaptiste Daroussin 		break;
287861d06d6bSBaptiste Daroussin 	case '=':
287961d06d6bSBaptiste Daroussin 		if ('=' == v[*pos + 1])
288061d06d6bSBaptiste Daroussin 			(*pos)++;
288161d06d6bSBaptiste Daroussin 		break;
288261d06d6bSBaptiste Daroussin 	default:
288361d06d6bSBaptiste Daroussin 		return 0;
288461d06d6bSBaptiste Daroussin 	}
288561d06d6bSBaptiste Daroussin 	(*pos)++;
288661d06d6bSBaptiste Daroussin 
288761d06d6bSBaptiste Daroussin 	return *res;
288861d06d6bSBaptiste Daroussin }
288961d06d6bSBaptiste Daroussin 
289061d06d6bSBaptiste Daroussin /*
289161d06d6bSBaptiste Daroussin  * Evaluate either a parenthesized numeric expression
289261d06d6bSBaptiste Daroussin  * or a single signed integer number.
289361d06d6bSBaptiste Daroussin  */
289461d06d6bSBaptiste Daroussin static int
roff_evalpar(struct roff * r,int ln,const char * v,int * pos,int * res,int flags)289561d06d6bSBaptiste Daroussin roff_evalpar(struct roff *r, int ln,
289661d06d6bSBaptiste Daroussin 	const char *v, int *pos, int *res, int flags)
289761d06d6bSBaptiste Daroussin {
289861d06d6bSBaptiste Daroussin 
289961d06d6bSBaptiste Daroussin 	if ('(' != v[*pos])
290061d06d6bSBaptiste Daroussin 		return roff_getnum(v, pos, res, flags);
290161d06d6bSBaptiste Daroussin 
290261d06d6bSBaptiste Daroussin 	(*pos)++;
290361d06d6bSBaptiste Daroussin 	if ( ! roff_evalnum(r, ln, v, pos, res, flags | ROFFNUM_WHITE))
290461d06d6bSBaptiste Daroussin 		return 0;
290561d06d6bSBaptiste Daroussin 
290661d06d6bSBaptiste Daroussin 	/*
290761d06d6bSBaptiste Daroussin 	 * Omission of the closing parenthesis
290861d06d6bSBaptiste Daroussin 	 * is an error in validation mode,
290961d06d6bSBaptiste Daroussin 	 * but ignored in evaluation mode.
291061d06d6bSBaptiste Daroussin 	 */
291161d06d6bSBaptiste Daroussin 
291261d06d6bSBaptiste Daroussin 	if (')' == v[*pos])
291361d06d6bSBaptiste Daroussin 		(*pos)++;
291461d06d6bSBaptiste Daroussin 	else if (NULL == res)
291561d06d6bSBaptiste Daroussin 		return 0;
291661d06d6bSBaptiste Daroussin 
291761d06d6bSBaptiste Daroussin 	return 1;
291861d06d6bSBaptiste Daroussin }
291961d06d6bSBaptiste Daroussin 
292061d06d6bSBaptiste Daroussin /*
292161d06d6bSBaptiste Daroussin  * Evaluate a complete numeric expression.
292261d06d6bSBaptiste Daroussin  * Proceed left to right, there is no concept of precedence.
292361d06d6bSBaptiste Daroussin  */
292461d06d6bSBaptiste Daroussin static int
roff_evalnum(struct roff * r,int ln,const char * v,int * pos,int * res,int flags)292561d06d6bSBaptiste Daroussin roff_evalnum(struct roff *r, int ln, const char *v,
292661d06d6bSBaptiste Daroussin 	int *pos, int *res, int flags)
292761d06d6bSBaptiste Daroussin {
292861d06d6bSBaptiste Daroussin 	int		 mypos, operand2;
292961d06d6bSBaptiste Daroussin 	char		 operator;
293061d06d6bSBaptiste Daroussin 
293161d06d6bSBaptiste Daroussin 	if (NULL == pos) {
293261d06d6bSBaptiste Daroussin 		mypos = 0;
293361d06d6bSBaptiste Daroussin 		pos = &mypos;
293461d06d6bSBaptiste Daroussin 	}
293561d06d6bSBaptiste Daroussin 
293661d06d6bSBaptiste Daroussin 	if (flags & ROFFNUM_WHITE)
293761d06d6bSBaptiste Daroussin 		while (isspace((unsigned char)v[*pos]))
293861d06d6bSBaptiste Daroussin 			(*pos)++;
293961d06d6bSBaptiste Daroussin 
294061d06d6bSBaptiste Daroussin 	if ( ! roff_evalpar(r, ln, v, pos, res, flags))
294161d06d6bSBaptiste Daroussin 		return 0;
294261d06d6bSBaptiste Daroussin 
294361d06d6bSBaptiste Daroussin 	while (1) {
294461d06d6bSBaptiste Daroussin 		if (flags & ROFFNUM_WHITE)
294561d06d6bSBaptiste Daroussin 			while (isspace((unsigned char)v[*pos]))
294661d06d6bSBaptiste Daroussin 				(*pos)++;
294761d06d6bSBaptiste Daroussin 
294861d06d6bSBaptiste Daroussin 		if ( ! roff_getop(v, pos, &operator))
294961d06d6bSBaptiste Daroussin 			break;
295061d06d6bSBaptiste Daroussin 
295161d06d6bSBaptiste Daroussin 		if (flags & ROFFNUM_WHITE)
295261d06d6bSBaptiste Daroussin 			while (isspace((unsigned char)v[*pos]))
295361d06d6bSBaptiste Daroussin 				(*pos)++;
295461d06d6bSBaptiste Daroussin 
295561d06d6bSBaptiste Daroussin 		if ( ! roff_evalpar(r, ln, v, pos, &operand2, flags))
295661d06d6bSBaptiste Daroussin 			return 0;
295761d06d6bSBaptiste Daroussin 
295861d06d6bSBaptiste Daroussin 		if (flags & ROFFNUM_WHITE)
295961d06d6bSBaptiste Daroussin 			while (isspace((unsigned char)v[*pos]))
296061d06d6bSBaptiste Daroussin 				(*pos)++;
296161d06d6bSBaptiste Daroussin 
296261d06d6bSBaptiste Daroussin 		if (NULL == res)
296361d06d6bSBaptiste Daroussin 			continue;
296461d06d6bSBaptiste Daroussin 
296561d06d6bSBaptiste Daroussin 		switch (operator) {
296661d06d6bSBaptiste Daroussin 		case '+':
296761d06d6bSBaptiste Daroussin 			*res += operand2;
296861d06d6bSBaptiste Daroussin 			break;
296961d06d6bSBaptiste Daroussin 		case '-':
297061d06d6bSBaptiste Daroussin 			*res -= operand2;
297161d06d6bSBaptiste Daroussin 			break;
297261d06d6bSBaptiste Daroussin 		case '*':
297361d06d6bSBaptiste Daroussin 			*res *= operand2;
297461d06d6bSBaptiste Daroussin 			break;
297561d06d6bSBaptiste Daroussin 		case '/':
297661d06d6bSBaptiste Daroussin 			if (operand2 == 0) {
297761d06d6bSBaptiste Daroussin 				mandoc_msg(MANDOCERR_DIVZERO,
29787295610fSBaptiste Daroussin 					ln, *pos, "%s", v);
297961d06d6bSBaptiste Daroussin 				*res = 0;
298061d06d6bSBaptiste Daroussin 				break;
298161d06d6bSBaptiste Daroussin 			}
298261d06d6bSBaptiste Daroussin 			*res /= operand2;
298361d06d6bSBaptiste Daroussin 			break;
298461d06d6bSBaptiste Daroussin 		case '%':
298561d06d6bSBaptiste Daroussin 			if (operand2 == 0) {
298661d06d6bSBaptiste Daroussin 				mandoc_msg(MANDOCERR_DIVZERO,
29877295610fSBaptiste Daroussin 					ln, *pos, "%s", v);
298861d06d6bSBaptiste Daroussin 				*res = 0;
298961d06d6bSBaptiste Daroussin 				break;
299061d06d6bSBaptiste Daroussin 			}
299161d06d6bSBaptiste Daroussin 			*res %= operand2;
299261d06d6bSBaptiste Daroussin 			break;
299361d06d6bSBaptiste Daroussin 		case '<':
299461d06d6bSBaptiste Daroussin 			*res = *res < operand2;
299561d06d6bSBaptiste Daroussin 			break;
299661d06d6bSBaptiste Daroussin 		case '>':
299761d06d6bSBaptiste Daroussin 			*res = *res > operand2;
299861d06d6bSBaptiste Daroussin 			break;
299961d06d6bSBaptiste Daroussin 		case 'l':
300061d06d6bSBaptiste Daroussin 			*res = *res <= operand2;
300161d06d6bSBaptiste Daroussin 			break;
300261d06d6bSBaptiste Daroussin 		case 'g':
300361d06d6bSBaptiste Daroussin 			*res = *res >= operand2;
300461d06d6bSBaptiste Daroussin 			break;
300561d06d6bSBaptiste Daroussin 		case '=':
300661d06d6bSBaptiste Daroussin 			*res = *res == operand2;
300761d06d6bSBaptiste Daroussin 			break;
300861d06d6bSBaptiste Daroussin 		case '!':
300961d06d6bSBaptiste Daroussin 			*res = *res != operand2;
301061d06d6bSBaptiste Daroussin 			break;
301161d06d6bSBaptiste Daroussin 		case '&':
301261d06d6bSBaptiste Daroussin 			*res = *res && operand2;
301361d06d6bSBaptiste Daroussin 			break;
301461d06d6bSBaptiste Daroussin 		case ':':
301561d06d6bSBaptiste Daroussin 			*res = *res || operand2;
301661d06d6bSBaptiste Daroussin 			break;
301761d06d6bSBaptiste Daroussin 		case 'i':
301861d06d6bSBaptiste Daroussin 			if (operand2 < *res)
301961d06d6bSBaptiste Daroussin 				*res = operand2;
302061d06d6bSBaptiste Daroussin 			break;
302161d06d6bSBaptiste Daroussin 		case 'a':
302261d06d6bSBaptiste Daroussin 			if (operand2 > *res)
302361d06d6bSBaptiste Daroussin 				*res = operand2;
302461d06d6bSBaptiste Daroussin 			break;
302561d06d6bSBaptiste Daroussin 		default:
302661d06d6bSBaptiste Daroussin 			abort();
302761d06d6bSBaptiste Daroussin 		}
302861d06d6bSBaptiste Daroussin 	}
302961d06d6bSBaptiste Daroussin 	return 1;
303061d06d6bSBaptiste Daroussin }
303161d06d6bSBaptiste Daroussin 
303261d06d6bSBaptiste Daroussin /* --- register management ------------------------------------------------ */
303361d06d6bSBaptiste Daroussin 
303461d06d6bSBaptiste Daroussin void
roff_setreg(struct roff * r,const char * name,int val,char sign)303561d06d6bSBaptiste Daroussin roff_setreg(struct roff *r, const char *name, int val, char sign)
303661d06d6bSBaptiste Daroussin {
303761d06d6bSBaptiste Daroussin 	roff_setregn(r, name, strlen(name), val, sign, INT_MIN);
303861d06d6bSBaptiste Daroussin }
303961d06d6bSBaptiste Daroussin 
304061d06d6bSBaptiste Daroussin static void
roff_setregn(struct roff * r,const char * name,size_t len,int val,char sign,int step)304161d06d6bSBaptiste Daroussin roff_setregn(struct roff *r, const char *name, size_t len,
304261d06d6bSBaptiste Daroussin     int val, char sign, int step)
304361d06d6bSBaptiste Daroussin {
304461d06d6bSBaptiste Daroussin 	struct roffreg	*reg;
304561d06d6bSBaptiste Daroussin 
304661d06d6bSBaptiste Daroussin 	/* Search for an existing register with the same name. */
304761d06d6bSBaptiste Daroussin 	reg = r->regtab;
304861d06d6bSBaptiste Daroussin 
304961d06d6bSBaptiste Daroussin 	while (reg != NULL && (reg->key.sz != len ||
305061d06d6bSBaptiste Daroussin 	    strncmp(reg->key.p, name, len) != 0))
305161d06d6bSBaptiste Daroussin 		reg = reg->next;
305261d06d6bSBaptiste Daroussin 
305361d06d6bSBaptiste Daroussin 	if (NULL == reg) {
305461d06d6bSBaptiste Daroussin 		/* Create a new register. */
305561d06d6bSBaptiste Daroussin 		reg = mandoc_malloc(sizeof(struct roffreg));
305661d06d6bSBaptiste Daroussin 		reg->key.p = mandoc_strndup(name, len);
305761d06d6bSBaptiste Daroussin 		reg->key.sz = len;
305861d06d6bSBaptiste Daroussin 		reg->val = 0;
305961d06d6bSBaptiste Daroussin 		reg->step = 0;
306061d06d6bSBaptiste Daroussin 		reg->next = r->regtab;
306161d06d6bSBaptiste Daroussin 		r->regtab = reg;
306261d06d6bSBaptiste Daroussin 	}
306361d06d6bSBaptiste Daroussin 
306461d06d6bSBaptiste Daroussin 	if ('+' == sign)
306561d06d6bSBaptiste Daroussin 		reg->val += val;
306661d06d6bSBaptiste Daroussin 	else if ('-' == sign)
306761d06d6bSBaptiste Daroussin 		reg->val -= val;
306861d06d6bSBaptiste Daroussin 	else
306961d06d6bSBaptiste Daroussin 		reg->val = val;
307061d06d6bSBaptiste Daroussin 	if (step != INT_MIN)
307161d06d6bSBaptiste Daroussin 		reg->step = step;
307261d06d6bSBaptiste Daroussin }
307361d06d6bSBaptiste Daroussin 
307461d06d6bSBaptiste Daroussin /*
307561d06d6bSBaptiste Daroussin  * Handle some predefined read-only number registers.
307661d06d6bSBaptiste Daroussin  * For now, return -1 if the requested register is not predefined;
307761d06d6bSBaptiste Daroussin  * in case a predefined read-only register having the value -1
307861d06d6bSBaptiste Daroussin  * were to turn up, another special value would have to be chosen.
307961d06d6bSBaptiste Daroussin  */
308061d06d6bSBaptiste Daroussin static int
roff_getregro(const struct roff * r,const char * name)308161d06d6bSBaptiste Daroussin roff_getregro(const struct roff *r, const char *name)
308261d06d6bSBaptiste Daroussin {
308361d06d6bSBaptiste Daroussin 
308461d06d6bSBaptiste Daroussin 	switch (*name) {
308561d06d6bSBaptiste Daroussin 	case '$':  /* Number of arguments of the last macro evaluated. */
30867295610fSBaptiste Daroussin 		return r->mstackpos < 0 ? 0 : r->mstack[r->mstackpos].argc;
308761d06d6bSBaptiste Daroussin 	case 'A':  /* ASCII approximation mode is always off. */
308861d06d6bSBaptiste Daroussin 		return 0;
308961d06d6bSBaptiste Daroussin 	case 'g':  /* Groff compatibility mode is always on. */
309061d06d6bSBaptiste Daroussin 		return 1;
309161d06d6bSBaptiste Daroussin 	case 'H':  /* Fixed horizontal resolution. */
309261d06d6bSBaptiste Daroussin 		return 24;
309361d06d6bSBaptiste Daroussin 	case 'j':  /* Always adjust left margin only. */
309461d06d6bSBaptiste Daroussin 		return 0;
309561d06d6bSBaptiste Daroussin 	case 'T':  /* Some output device is always defined. */
309661d06d6bSBaptiste Daroussin 		return 1;
309761d06d6bSBaptiste Daroussin 	case 'V':  /* Fixed vertical resolution. */
309861d06d6bSBaptiste Daroussin 		return 40;
309961d06d6bSBaptiste Daroussin 	default:
310061d06d6bSBaptiste Daroussin 		return -1;
310161d06d6bSBaptiste Daroussin 	}
310261d06d6bSBaptiste Daroussin }
310361d06d6bSBaptiste Daroussin 
310461d06d6bSBaptiste Daroussin int
roff_getreg(struct roff * r,const char * name)310561d06d6bSBaptiste Daroussin roff_getreg(struct roff *r, const char *name)
310661d06d6bSBaptiste Daroussin {
310761d06d6bSBaptiste Daroussin 	return roff_getregn(r, name, strlen(name), '\0');
310861d06d6bSBaptiste Daroussin }
310961d06d6bSBaptiste Daroussin 
311061d06d6bSBaptiste Daroussin static int
roff_getregn(struct roff * r,const char * name,size_t len,char sign)311161d06d6bSBaptiste Daroussin roff_getregn(struct roff *r, const char *name, size_t len, char sign)
311261d06d6bSBaptiste Daroussin {
311361d06d6bSBaptiste Daroussin 	struct roffreg	*reg;
311461d06d6bSBaptiste Daroussin 	int		 val;
311561d06d6bSBaptiste Daroussin 
311661d06d6bSBaptiste Daroussin 	if ('.' == name[0] && 2 == len) {
311761d06d6bSBaptiste Daroussin 		val = roff_getregro(r, name + 1);
311861d06d6bSBaptiste Daroussin 		if (-1 != val)
311961d06d6bSBaptiste Daroussin 			return val;
312061d06d6bSBaptiste Daroussin 	}
312161d06d6bSBaptiste Daroussin 
312261d06d6bSBaptiste Daroussin 	for (reg = r->regtab; reg; reg = reg->next) {
312361d06d6bSBaptiste Daroussin 		if (len == reg->key.sz &&
312461d06d6bSBaptiste Daroussin 		    0 == strncmp(name, reg->key.p, len)) {
312561d06d6bSBaptiste Daroussin 			switch (sign) {
312661d06d6bSBaptiste Daroussin 			case '+':
312761d06d6bSBaptiste Daroussin 				reg->val += reg->step;
312861d06d6bSBaptiste Daroussin 				break;
312961d06d6bSBaptiste Daroussin 			case '-':
313061d06d6bSBaptiste Daroussin 				reg->val -= reg->step;
313161d06d6bSBaptiste Daroussin 				break;
313261d06d6bSBaptiste Daroussin 			default:
313361d06d6bSBaptiste Daroussin 				break;
313461d06d6bSBaptiste Daroussin 			}
313561d06d6bSBaptiste Daroussin 			return reg->val;
313661d06d6bSBaptiste Daroussin 		}
313761d06d6bSBaptiste Daroussin 	}
313861d06d6bSBaptiste Daroussin 
313961d06d6bSBaptiste Daroussin 	roff_setregn(r, name, len, 0, '\0', INT_MIN);
314061d06d6bSBaptiste Daroussin 	return 0;
314161d06d6bSBaptiste Daroussin }
314261d06d6bSBaptiste Daroussin 
314361d06d6bSBaptiste Daroussin static int
roff_hasregn(const struct roff * r,const char * name,size_t len)314461d06d6bSBaptiste Daroussin roff_hasregn(const struct roff *r, const char *name, size_t len)
314561d06d6bSBaptiste Daroussin {
314661d06d6bSBaptiste Daroussin 	struct roffreg	*reg;
314761d06d6bSBaptiste Daroussin 	int		 val;
314861d06d6bSBaptiste Daroussin 
314961d06d6bSBaptiste Daroussin 	if ('.' == name[0] && 2 == len) {
315061d06d6bSBaptiste Daroussin 		val = roff_getregro(r, name + 1);
315161d06d6bSBaptiste Daroussin 		if (-1 != val)
315261d06d6bSBaptiste Daroussin 			return 1;
315361d06d6bSBaptiste Daroussin 	}
315461d06d6bSBaptiste Daroussin 
315561d06d6bSBaptiste Daroussin 	for (reg = r->regtab; reg; reg = reg->next)
315661d06d6bSBaptiste Daroussin 		if (len == reg->key.sz &&
315761d06d6bSBaptiste Daroussin 		    0 == strncmp(name, reg->key.p, len))
315861d06d6bSBaptiste Daroussin 			return 1;
315961d06d6bSBaptiste Daroussin 
316061d06d6bSBaptiste Daroussin 	return 0;
316161d06d6bSBaptiste Daroussin }
316261d06d6bSBaptiste Daroussin 
316361d06d6bSBaptiste Daroussin static void
roff_freereg(struct roffreg * reg)316461d06d6bSBaptiste Daroussin roff_freereg(struct roffreg *reg)
316561d06d6bSBaptiste Daroussin {
316661d06d6bSBaptiste Daroussin 	struct roffreg	*old_reg;
316761d06d6bSBaptiste Daroussin 
316861d06d6bSBaptiste Daroussin 	while (NULL != reg) {
316961d06d6bSBaptiste Daroussin 		free(reg->key.p);
317061d06d6bSBaptiste Daroussin 		old_reg = reg;
317161d06d6bSBaptiste Daroussin 		reg = reg->next;
317261d06d6bSBaptiste Daroussin 		free(old_reg);
317361d06d6bSBaptiste Daroussin 	}
317461d06d6bSBaptiste Daroussin }
317561d06d6bSBaptiste Daroussin 
31767295610fSBaptiste Daroussin static int
roff_nr(ROFF_ARGS)317761d06d6bSBaptiste Daroussin roff_nr(ROFF_ARGS)
317861d06d6bSBaptiste Daroussin {
317961d06d6bSBaptiste Daroussin 	char		*key, *val, *step;
318061d06d6bSBaptiste Daroussin 	size_t		 keysz;
318161d06d6bSBaptiste Daroussin 	int		 iv, is, len;
318261d06d6bSBaptiste Daroussin 	char		 sign;
318361d06d6bSBaptiste Daroussin 
318461d06d6bSBaptiste Daroussin 	key = val = buf->buf + pos;
318561d06d6bSBaptiste Daroussin 	if (*key == '\0')
318661d06d6bSBaptiste Daroussin 		return ROFF_IGN;
318761d06d6bSBaptiste Daroussin 
318861d06d6bSBaptiste Daroussin 	keysz = roff_getname(r, &val, ln, pos);
31897295610fSBaptiste Daroussin 	if (key[keysz] == '\\' || key[keysz] == '\t')
319061d06d6bSBaptiste Daroussin 		return ROFF_IGN;
319161d06d6bSBaptiste Daroussin 
319261d06d6bSBaptiste Daroussin 	sign = *val;
319361d06d6bSBaptiste Daroussin 	if (sign == '+' || sign == '-')
319461d06d6bSBaptiste Daroussin 		val++;
319561d06d6bSBaptiste Daroussin 
319661d06d6bSBaptiste Daroussin 	len = 0;
319761d06d6bSBaptiste Daroussin 	if (roff_evalnum(r, ln, val, &len, &iv, ROFFNUM_SCALE) == 0)
319861d06d6bSBaptiste Daroussin 		return ROFF_IGN;
319961d06d6bSBaptiste Daroussin 
320061d06d6bSBaptiste Daroussin 	step = val + len;
320161d06d6bSBaptiste Daroussin 	while (isspace((unsigned char)*step))
320261d06d6bSBaptiste Daroussin 		step++;
320361d06d6bSBaptiste Daroussin 	if (roff_evalnum(r, ln, step, NULL, &is, 0) == 0)
320461d06d6bSBaptiste Daroussin 		is = INT_MIN;
320561d06d6bSBaptiste Daroussin 
320661d06d6bSBaptiste Daroussin 	roff_setregn(r, key, keysz, iv, sign, is);
320761d06d6bSBaptiste Daroussin 	return ROFF_IGN;
320861d06d6bSBaptiste Daroussin }
320961d06d6bSBaptiste Daroussin 
32107295610fSBaptiste Daroussin static int
roff_rr(ROFF_ARGS)321161d06d6bSBaptiste Daroussin roff_rr(ROFF_ARGS)
321261d06d6bSBaptiste Daroussin {
321361d06d6bSBaptiste Daroussin 	struct roffreg	*reg, **prev;
321461d06d6bSBaptiste Daroussin 	char		*name, *cp;
321561d06d6bSBaptiste Daroussin 	size_t		 namesz;
321661d06d6bSBaptiste Daroussin 
321761d06d6bSBaptiste Daroussin 	name = cp = buf->buf + pos;
321861d06d6bSBaptiste Daroussin 	if (*name == '\0')
321961d06d6bSBaptiste Daroussin 		return ROFF_IGN;
322061d06d6bSBaptiste Daroussin 	namesz = roff_getname(r, &cp, ln, pos);
322161d06d6bSBaptiste Daroussin 	name[namesz] = '\0';
322261d06d6bSBaptiste Daroussin 
322361d06d6bSBaptiste Daroussin 	prev = &r->regtab;
322461d06d6bSBaptiste Daroussin 	while (1) {
322561d06d6bSBaptiste Daroussin 		reg = *prev;
322661d06d6bSBaptiste Daroussin 		if (reg == NULL || !strcmp(name, reg->key.p))
322761d06d6bSBaptiste Daroussin 			break;
322861d06d6bSBaptiste Daroussin 		prev = &reg->next;
322961d06d6bSBaptiste Daroussin 	}
323061d06d6bSBaptiste Daroussin 	if (reg != NULL) {
323161d06d6bSBaptiste Daroussin 		*prev = reg->next;
323261d06d6bSBaptiste Daroussin 		free(reg->key.p);
323361d06d6bSBaptiste Daroussin 		free(reg);
323461d06d6bSBaptiste Daroussin 	}
323561d06d6bSBaptiste Daroussin 	return ROFF_IGN;
323661d06d6bSBaptiste Daroussin }
323761d06d6bSBaptiste Daroussin 
323861d06d6bSBaptiste Daroussin /* --- handler functions for roff requests -------------------------------- */
323961d06d6bSBaptiste Daroussin 
32407295610fSBaptiste Daroussin static int
roff_rm(ROFF_ARGS)324161d06d6bSBaptiste Daroussin roff_rm(ROFF_ARGS)
324261d06d6bSBaptiste Daroussin {
324361d06d6bSBaptiste Daroussin 	const char	 *name;
324461d06d6bSBaptiste Daroussin 	char		 *cp;
324561d06d6bSBaptiste Daroussin 	size_t		  namesz;
324661d06d6bSBaptiste Daroussin 
324761d06d6bSBaptiste Daroussin 	cp = buf->buf + pos;
324861d06d6bSBaptiste Daroussin 	while (*cp != '\0') {
324961d06d6bSBaptiste Daroussin 		name = cp;
325061d06d6bSBaptiste Daroussin 		namesz = roff_getname(r, &cp, ln, (int)(cp - buf->buf));
325161d06d6bSBaptiste Daroussin 		roff_setstrn(&r->strtab, name, namesz, NULL, 0, 0);
325261d06d6bSBaptiste Daroussin 		roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
32537295610fSBaptiste Daroussin 		if (name[namesz] == '\\' || name[namesz] == '\t')
325461d06d6bSBaptiste Daroussin 			break;
325561d06d6bSBaptiste Daroussin 	}
325661d06d6bSBaptiste Daroussin 	return ROFF_IGN;
325761d06d6bSBaptiste Daroussin }
325861d06d6bSBaptiste Daroussin 
32597295610fSBaptiste Daroussin static int
roff_it(ROFF_ARGS)326061d06d6bSBaptiste Daroussin roff_it(ROFF_ARGS)
326161d06d6bSBaptiste Daroussin {
326261d06d6bSBaptiste Daroussin 	int		 iv;
326361d06d6bSBaptiste Daroussin 
326461d06d6bSBaptiste Daroussin 	/* Parse the number of lines. */
326561d06d6bSBaptiste Daroussin 
326661d06d6bSBaptiste Daroussin 	if ( ! roff_evalnum(r, ln, buf->buf, &pos, &iv, 0)) {
32677295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_IT_NONUM,
32687295610fSBaptiste Daroussin 		    ln, ppos, "%s", buf->buf + 1);
326961d06d6bSBaptiste Daroussin 		return ROFF_IGN;
327061d06d6bSBaptiste Daroussin 	}
327161d06d6bSBaptiste Daroussin 
327261d06d6bSBaptiste Daroussin 	while (isspace((unsigned char)buf->buf[pos]))
327361d06d6bSBaptiste Daroussin 		pos++;
327461d06d6bSBaptiste Daroussin 
327561d06d6bSBaptiste Daroussin 	/*
327661d06d6bSBaptiste Daroussin 	 * Arm the input line trap.
327761d06d6bSBaptiste Daroussin 	 * Special-casing "an-trap" is an ugly workaround to cope
327861d06d6bSBaptiste Daroussin 	 * with DocBook stupidly fiddling with man(7) internals.
327961d06d6bSBaptiste Daroussin 	 */
328061d06d6bSBaptiste Daroussin 
328161d06d6bSBaptiste Daroussin 	roffit_lines = iv;
328261d06d6bSBaptiste Daroussin 	roffit_macro = mandoc_strdup(iv != 1 ||
328361d06d6bSBaptiste Daroussin 	    strcmp(buf->buf + pos, "an-trap") ?
328461d06d6bSBaptiste Daroussin 	    buf->buf + pos : "br");
328561d06d6bSBaptiste Daroussin 	return ROFF_IGN;
328661d06d6bSBaptiste Daroussin }
328761d06d6bSBaptiste Daroussin 
32887295610fSBaptiste Daroussin static int
roff_Dd(ROFF_ARGS)328961d06d6bSBaptiste Daroussin roff_Dd(ROFF_ARGS)
329061d06d6bSBaptiste Daroussin {
329161d06d6bSBaptiste Daroussin 	int		 mask;
329261d06d6bSBaptiste Daroussin 	enum roff_tok	 t, te;
329361d06d6bSBaptiste Daroussin 
329461d06d6bSBaptiste Daroussin 	switch (tok) {
329561d06d6bSBaptiste Daroussin 	case ROFF_Dd:
329661d06d6bSBaptiste Daroussin 		tok = MDOC_Dd;
329761d06d6bSBaptiste Daroussin 		te = MDOC_MAX;
329861d06d6bSBaptiste Daroussin 		if (r->format == 0)
329961d06d6bSBaptiste Daroussin 			r->format = MPARSE_MDOC;
330061d06d6bSBaptiste Daroussin 		mask = MPARSE_MDOC | MPARSE_QUICK;
330161d06d6bSBaptiste Daroussin 		break;
330261d06d6bSBaptiste Daroussin 	case ROFF_TH:
330361d06d6bSBaptiste Daroussin 		tok = MAN_TH;
330461d06d6bSBaptiste Daroussin 		te = MAN_MAX;
330561d06d6bSBaptiste Daroussin 		if (r->format == 0)
330661d06d6bSBaptiste Daroussin 			r->format = MPARSE_MAN;
330761d06d6bSBaptiste Daroussin 		mask = MPARSE_QUICK;
330861d06d6bSBaptiste Daroussin 		break;
330961d06d6bSBaptiste Daroussin 	default:
331061d06d6bSBaptiste Daroussin 		abort();
331161d06d6bSBaptiste Daroussin 	}
331261d06d6bSBaptiste Daroussin 	if ((r->options & mask) == 0)
331361d06d6bSBaptiste Daroussin 		for (t = tok; t < te; t++)
331461d06d6bSBaptiste Daroussin 			roff_setstr(r, roff_name[t], NULL, 0);
331561d06d6bSBaptiste Daroussin 	return ROFF_CONT;
331661d06d6bSBaptiste Daroussin }
331761d06d6bSBaptiste Daroussin 
33187295610fSBaptiste Daroussin static int
roff_TE(ROFF_ARGS)331961d06d6bSBaptiste Daroussin roff_TE(ROFF_ARGS)
332061d06d6bSBaptiste Daroussin {
33217295610fSBaptiste Daroussin 	r->man->flags &= ~ROFF_NONOFILL;
332261d06d6bSBaptiste Daroussin 	if (r->tbl == NULL) {
33237295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "TE");
332461d06d6bSBaptiste Daroussin 		return ROFF_IGN;
332561d06d6bSBaptiste Daroussin 	}
33267295610fSBaptiste Daroussin 	if (tbl_end(r->tbl, 0) == 0) {
332761d06d6bSBaptiste Daroussin 		r->tbl = NULL;
332861d06d6bSBaptiste Daroussin 		free(buf->buf);
332961d06d6bSBaptiste Daroussin 		buf->buf = mandoc_strdup(".sp");
333061d06d6bSBaptiste Daroussin 		buf->sz = 4;
333161d06d6bSBaptiste Daroussin 		*offs = 0;
333261d06d6bSBaptiste Daroussin 		return ROFF_REPARSE;
333361d06d6bSBaptiste Daroussin 	}
333461d06d6bSBaptiste Daroussin 	r->tbl = NULL;
333561d06d6bSBaptiste Daroussin 	return ROFF_IGN;
333661d06d6bSBaptiste Daroussin }
333761d06d6bSBaptiste Daroussin 
33387295610fSBaptiste Daroussin static int
roff_T_(ROFF_ARGS)333961d06d6bSBaptiste Daroussin roff_T_(ROFF_ARGS)
334061d06d6bSBaptiste Daroussin {
334161d06d6bSBaptiste Daroussin 
334261d06d6bSBaptiste Daroussin 	if (NULL == r->tbl)
33437295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "T&");
334461d06d6bSBaptiste Daroussin 	else
334561d06d6bSBaptiste Daroussin 		tbl_restart(ln, ppos, r->tbl);
334661d06d6bSBaptiste Daroussin 
334761d06d6bSBaptiste Daroussin 	return ROFF_IGN;
334861d06d6bSBaptiste Daroussin }
334961d06d6bSBaptiste Daroussin 
335061d06d6bSBaptiste Daroussin /*
335161d06d6bSBaptiste Daroussin  * Handle in-line equation delimiters.
335261d06d6bSBaptiste Daroussin  */
33537295610fSBaptiste Daroussin static int
roff_eqndelim(struct roff * r,struct buf * buf,int pos)335461d06d6bSBaptiste Daroussin roff_eqndelim(struct roff *r, struct buf *buf, int pos)
335561d06d6bSBaptiste Daroussin {
335661d06d6bSBaptiste Daroussin 	char		*cp1, *cp2;
335761d06d6bSBaptiste Daroussin 	const char	*bef_pr, *bef_nl, *mac, *aft_nl, *aft_pr;
335861d06d6bSBaptiste Daroussin 
335961d06d6bSBaptiste Daroussin 	/*
336061d06d6bSBaptiste Daroussin 	 * Outside equations, look for an opening delimiter.
336161d06d6bSBaptiste Daroussin 	 * If we are inside an equation, we already know it is
336261d06d6bSBaptiste Daroussin 	 * in-line, or this function wouldn't have been called;
336361d06d6bSBaptiste Daroussin 	 * so look for a closing delimiter.
336461d06d6bSBaptiste Daroussin 	 */
336561d06d6bSBaptiste Daroussin 
336661d06d6bSBaptiste Daroussin 	cp1 = buf->buf + pos;
336761d06d6bSBaptiste Daroussin 	cp2 = strchr(cp1, r->eqn == NULL ?
336861d06d6bSBaptiste Daroussin 	    r->last_eqn->odelim : r->last_eqn->cdelim);
336961d06d6bSBaptiste Daroussin 	if (cp2 == NULL)
337061d06d6bSBaptiste Daroussin 		return ROFF_CONT;
337161d06d6bSBaptiste Daroussin 
337261d06d6bSBaptiste Daroussin 	*cp2++ = '\0';
337361d06d6bSBaptiste Daroussin 	bef_pr = bef_nl = aft_nl = aft_pr = "";
337461d06d6bSBaptiste Daroussin 
337561d06d6bSBaptiste Daroussin 	/* Handle preceding text, protecting whitespace. */
337661d06d6bSBaptiste Daroussin 
337761d06d6bSBaptiste Daroussin 	if (*buf->buf != '\0') {
337861d06d6bSBaptiste Daroussin 		if (r->eqn == NULL)
337961d06d6bSBaptiste Daroussin 			bef_pr = "\\&";
338061d06d6bSBaptiste Daroussin 		bef_nl = "\n";
338161d06d6bSBaptiste Daroussin 	}
338261d06d6bSBaptiste Daroussin 
338361d06d6bSBaptiste Daroussin 	/*
338461d06d6bSBaptiste Daroussin 	 * Prepare replacing the delimiter with an equation macro
338561d06d6bSBaptiste Daroussin 	 * and drop leading white space from the equation.
338661d06d6bSBaptiste Daroussin 	 */
338761d06d6bSBaptiste Daroussin 
338861d06d6bSBaptiste Daroussin 	if (r->eqn == NULL) {
338961d06d6bSBaptiste Daroussin 		while (*cp2 == ' ')
339061d06d6bSBaptiste Daroussin 			cp2++;
339161d06d6bSBaptiste Daroussin 		mac = ".EQ";
339261d06d6bSBaptiste Daroussin 	} else
339361d06d6bSBaptiste Daroussin 		mac = ".EN";
339461d06d6bSBaptiste Daroussin 
339561d06d6bSBaptiste Daroussin 	/* Handle following text, protecting whitespace. */
339661d06d6bSBaptiste Daroussin 
339761d06d6bSBaptiste Daroussin 	if (*cp2 != '\0') {
339861d06d6bSBaptiste Daroussin 		aft_nl = "\n";
339961d06d6bSBaptiste Daroussin 		if (r->eqn != NULL)
340061d06d6bSBaptiste Daroussin 			aft_pr = "\\&";
340161d06d6bSBaptiste Daroussin 	}
340261d06d6bSBaptiste Daroussin 
340361d06d6bSBaptiste Daroussin 	/* Do the actual replacement. */
340461d06d6bSBaptiste Daroussin 
340561d06d6bSBaptiste Daroussin 	buf->sz = mandoc_asprintf(&cp1, "%s%s%s%s%s%s%s", buf->buf,
340661d06d6bSBaptiste Daroussin 	    bef_pr, bef_nl, mac, aft_nl, aft_pr, cp2) + 1;
340761d06d6bSBaptiste Daroussin 	free(buf->buf);
340861d06d6bSBaptiste Daroussin 	buf->buf = cp1;
340961d06d6bSBaptiste Daroussin 
341061d06d6bSBaptiste Daroussin 	/* Toggle the in-line state of the eqn subsystem. */
341161d06d6bSBaptiste Daroussin 
341261d06d6bSBaptiste Daroussin 	r->eqn_inline = r->eqn == NULL;
341361d06d6bSBaptiste Daroussin 	return ROFF_REPARSE;
341461d06d6bSBaptiste Daroussin }
341561d06d6bSBaptiste Daroussin 
34167295610fSBaptiste Daroussin static int
roff_EQ(ROFF_ARGS)341761d06d6bSBaptiste Daroussin roff_EQ(ROFF_ARGS)
341861d06d6bSBaptiste Daroussin {
341961d06d6bSBaptiste Daroussin 	struct roff_node	*n;
342061d06d6bSBaptiste Daroussin 
34217295610fSBaptiste Daroussin 	if (r->man->meta.macroset == MACROSET_MAN)
342261d06d6bSBaptiste Daroussin 		man_breakscope(r->man, ROFF_EQ);
342361d06d6bSBaptiste Daroussin 	n = roff_node_alloc(r->man, ln, ppos, ROFFT_EQN, TOKEN_NONE);
342461d06d6bSBaptiste Daroussin 	if (ln > r->man->last->line)
342561d06d6bSBaptiste Daroussin 		n->flags |= NODE_LINE;
34267295610fSBaptiste Daroussin 	n->eqn = eqn_box_new();
342761d06d6bSBaptiste Daroussin 	roff_node_append(r->man, n);
342861d06d6bSBaptiste Daroussin 	r->man->next = ROFF_NEXT_SIBLING;
342961d06d6bSBaptiste Daroussin 
343061d06d6bSBaptiste Daroussin 	assert(r->eqn == NULL);
343161d06d6bSBaptiste Daroussin 	if (r->last_eqn == NULL)
34327295610fSBaptiste Daroussin 		r->last_eqn = eqn_alloc();
343361d06d6bSBaptiste Daroussin 	else
343461d06d6bSBaptiste Daroussin 		eqn_reset(r->last_eqn);
343561d06d6bSBaptiste Daroussin 	r->eqn = r->last_eqn;
343661d06d6bSBaptiste Daroussin 	r->eqn->node = n;
343761d06d6bSBaptiste Daroussin 
343861d06d6bSBaptiste Daroussin 	if (buf->buf[pos] != '\0')
34397295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
344061d06d6bSBaptiste Daroussin 		    ".EQ %s", buf->buf + pos);
344161d06d6bSBaptiste Daroussin 
344261d06d6bSBaptiste Daroussin 	return ROFF_IGN;
344361d06d6bSBaptiste Daroussin }
344461d06d6bSBaptiste Daroussin 
34457295610fSBaptiste Daroussin static int
roff_EN(ROFF_ARGS)344661d06d6bSBaptiste Daroussin roff_EN(ROFF_ARGS)
344761d06d6bSBaptiste Daroussin {
344861d06d6bSBaptiste Daroussin 	if (r->eqn != NULL) {
344961d06d6bSBaptiste Daroussin 		eqn_parse(r->eqn);
345061d06d6bSBaptiste Daroussin 		r->eqn = NULL;
345161d06d6bSBaptiste Daroussin 	} else
34527295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "EN");
345361d06d6bSBaptiste Daroussin 	if (buf->buf[pos] != '\0')
34547295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
345561d06d6bSBaptiste Daroussin 		    "EN %s", buf->buf + pos);
345661d06d6bSBaptiste Daroussin 	return ROFF_IGN;
345761d06d6bSBaptiste Daroussin }
345861d06d6bSBaptiste Daroussin 
34597295610fSBaptiste Daroussin static int
roff_TS(ROFF_ARGS)346061d06d6bSBaptiste Daroussin roff_TS(ROFF_ARGS)
346161d06d6bSBaptiste Daroussin {
346261d06d6bSBaptiste Daroussin 	if (r->tbl != NULL) {
34637295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_BROKEN, ln, ppos, "TS breaks TS");
34647295610fSBaptiste Daroussin 		tbl_end(r->tbl, 0);
346561d06d6bSBaptiste Daroussin 	}
34667295610fSBaptiste Daroussin 	r->man->flags |= ROFF_NONOFILL;
34677295610fSBaptiste Daroussin 	r->tbl = tbl_alloc(ppos, ln, r->last_tbl);
34687295610fSBaptiste Daroussin 	if (r->last_tbl == NULL)
346961d06d6bSBaptiste Daroussin 		r->first_tbl = r->tbl;
347061d06d6bSBaptiste Daroussin 	r->last_tbl = r->tbl;
347161d06d6bSBaptiste Daroussin 	return ROFF_IGN;
347261d06d6bSBaptiste Daroussin }
347361d06d6bSBaptiste Daroussin 
34747295610fSBaptiste Daroussin static int
roff_noarg(ROFF_ARGS)34757295610fSBaptiste Daroussin roff_noarg(ROFF_ARGS)
34767295610fSBaptiste Daroussin {
34777295610fSBaptiste Daroussin 	if (r->man->flags & (MAN_BLINE | MAN_ELINE))
34787295610fSBaptiste Daroussin 		man_breakscope(r->man, tok);
34797295610fSBaptiste Daroussin 	if (tok == ROFF_brp)
34807295610fSBaptiste Daroussin 		tok = ROFF_br;
34817295610fSBaptiste Daroussin 	roff_elem_alloc(r->man, ln, ppos, tok);
34827295610fSBaptiste Daroussin 	if (buf->buf[pos] != '\0')
34837295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
34847295610fSBaptiste Daroussin 		   "%s %s", roff_name[tok], buf->buf + pos);
34857295610fSBaptiste Daroussin 	if (tok == ROFF_nf)
34867295610fSBaptiste Daroussin 		r->man->flags |= ROFF_NOFILL;
34877295610fSBaptiste Daroussin 	else if (tok == ROFF_fi)
34887295610fSBaptiste Daroussin 		r->man->flags &= ~ROFF_NOFILL;
34897295610fSBaptiste Daroussin 	r->man->last->flags |= NODE_LINE | NODE_VALID | NODE_ENDED;
34907295610fSBaptiste Daroussin 	r->man->next = ROFF_NEXT_SIBLING;
34917295610fSBaptiste Daroussin 	return ROFF_IGN;
34927295610fSBaptiste Daroussin }
34937295610fSBaptiste Daroussin 
34947295610fSBaptiste Daroussin static int
roff_onearg(ROFF_ARGS)349561d06d6bSBaptiste Daroussin roff_onearg(ROFF_ARGS)
349661d06d6bSBaptiste Daroussin {
349761d06d6bSBaptiste Daroussin 	struct roff_node	*n;
349861d06d6bSBaptiste Daroussin 	char			*cp;
349961d06d6bSBaptiste Daroussin 	int			 npos;
350061d06d6bSBaptiste Daroussin 
350161d06d6bSBaptiste Daroussin 	if (r->man->flags & (MAN_BLINE | MAN_ELINE) &&
350261d06d6bSBaptiste Daroussin 	    (tok == ROFF_ce || tok == ROFF_rj || tok == ROFF_sp ||
350361d06d6bSBaptiste Daroussin 	     tok == ROFF_ti))
350461d06d6bSBaptiste Daroussin 		man_breakscope(r->man, tok);
350561d06d6bSBaptiste Daroussin 
350661d06d6bSBaptiste Daroussin 	if (roffce_node != NULL && (tok == ROFF_ce || tok == ROFF_rj)) {
350761d06d6bSBaptiste Daroussin 		r->man->last = roffce_node;
350861d06d6bSBaptiste Daroussin 		r->man->next = ROFF_NEXT_SIBLING;
350961d06d6bSBaptiste Daroussin 	}
351061d06d6bSBaptiste Daroussin 
351161d06d6bSBaptiste Daroussin 	roff_elem_alloc(r->man, ln, ppos, tok);
351261d06d6bSBaptiste Daroussin 	n = r->man->last;
351361d06d6bSBaptiste Daroussin 
351461d06d6bSBaptiste Daroussin 	cp = buf->buf + pos;
351561d06d6bSBaptiste Daroussin 	if (*cp != '\0') {
351661d06d6bSBaptiste Daroussin 		while (*cp != '\0' && *cp != ' ')
351761d06d6bSBaptiste Daroussin 			cp++;
351861d06d6bSBaptiste Daroussin 		while (*cp == ' ')
351961d06d6bSBaptiste Daroussin 			*cp++ = '\0';
352061d06d6bSBaptiste Daroussin 		if (*cp != '\0')
35217295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_ARG_EXCESS,
35227295610fSBaptiste Daroussin 			    ln, (int)(cp - buf->buf),
352361d06d6bSBaptiste Daroussin 			    "%s ... %s", roff_name[tok], cp);
352461d06d6bSBaptiste Daroussin 		roff_word_alloc(r->man, ln, pos, buf->buf + pos);
352561d06d6bSBaptiste Daroussin 	}
352661d06d6bSBaptiste Daroussin 
352761d06d6bSBaptiste Daroussin 	if (tok == ROFF_ce || tok == ROFF_rj) {
352861d06d6bSBaptiste Daroussin 		if (r->man->last->type == ROFFT_ELEM) {
352961d06d6bSBaptiste Daroussin 			roff_word_alloc(r->man, ln, pos, "1");
353061d06d6bSBaptiste Daroussin 			r->man->last->flags |= NODE_NOSRC;
353161d06d6bSBaptiste Daroussin 		}
353261d06d6bSBaptiste Daroussin 		npos = 0;
353361d06d6bSBaptiste Daroussin 		if (roff_evalnum(r, ln, r->man->last->string, &npos,
353461d06d6bSBaptiste Daroussin 		    &roffce_lines, 0) == 0) {
35357295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_CE_NONUM,
35367295610fSBaptiste Daroussin 			    ln, pos, "ce %s", buf->buf + pos);
353761d06d6bSBaptiste Daroussin 			roffce_lines = 1;
353861d06d6bSBaptiste Daroussin 		}
353961d06d6bSBaptiste Daroussin 		if (roffce_lines < 1) {
354061d06d6bSBaptiste Daroussin 			r->man->last = r->man->last->parent;
354161d06d6bSBaptiste Daroussin 			roffce_node = NULL;
354261d06d6bSBaptiste Daroussin 			roffce_lines = 0;
354361d06d6bSBaptiste Daroussin 		} else
354461d06d6bSBaptiste Daroussin 			roffce_node = r->man->last->parent;
354561d06d6bSBaptiste Daroussin 	} else {
354661d06d6bSBaptiste Daroussin 		n->flags |= NODE_VALID | NODE_ENDED;
354761d06d6bSBaptiste Daroussin 		r->man->last = n;
354861d06d6bSBaptiste Daroussin 	}
354961d06d6bSBaptiste Daroussin 	n->flags |= NODE_LINE;
355061d06d6bSBaptiste Daroussin 	r->man->next = ROFF_NEXT_SIBLING;
355161d06d6bSBaptiste Daroussin 	return ROFF_IGN;
355261d06d6bSBaptiste Daroussin }
355361d06d6bSBaptiste Daroussin 
35547295610fSBaptiste Daroussin static int
roff_manyarg(ROFF_ARGS)355561d06d6bSBaptiste Daroussin roff_manyarg(ROFF_ARGS)
355661d06d6bSBaptiste Daroussin {
355761d06d6bSBaptiste Daroussin 	struct roff_node	*n;
355861d06d6bSBaptiste Daroussin 	char			*sp, *ep;
355961d06d6bSBaptiste Daroussin 
356061d06d6bSBaptiste Daroussin 	roff_elem_alloc(r->man, ln, ppos, tok);
356161d06d6bSBaptiste Daroussin 	n = r->man->last;
356261d06d6bSBaptiste Daroussin 
356361d06d6bSBaptiste Daroussin 	for (sp = ep = buf->buf + pos; *sp != '\0'; sp = ep) {
356461d06d6bSBaptiste Daroussin 		while (*ep != '\0' && *ep != ' ')
356561d06d6bSBaptiste Daroussin 			ep++;
356661d06d6bSBaptiste Daroussin 		while (*ep == ' ')
356761d06d6bSBaptiste Daroussin 			*ep++ = '\0';
356861d06d6bSBaptiste Daroussin 		roff_word_alloc(r->man, ln, sp - buf->buf, sp);
356961d06d6bSBaptiste Daroussin 	}
357061d06d6bSBaptiste Daroussin 
357161d06d6bSBaptiste Daroussin 	n->flags |= NODE_LINE | NODE_VALID | NODE_ENDED;
357261d06d6bSBaptiste Daroussin 	r->man->last = n;
357361d06d6bSBaptiste Daroussin 	r->man->next = ROFF_NEXT_SIBLING;
357461d06d6bSBaptiste Daroussin 	return ROFF_IGN;
357561d06d6bSBaptiste Daroussin }
357661d06d6bSBaptiste Daroussin 
35777295610fSBaptiste Daroussin static int
roff_als(ROFF_ARGS)357861d06d6bSBaptiste Daroussin roff_als(ROFF_ARGS)
357961d06d6bSBaptiste Daroussin {
358061d06d6bSBaptiste Daroussin 	char		*oldn, *newn, *end, *value;
358161d06d6bSBaptiste Daroussin 	size_t		 oldsz, newsz, valsz;
358261d06d6bSBaptiste Daroussin 
358361d06d6bSBaptiste Daroussin 	newn = oldn = buf->buf + pos;
358461d06d6bSBaptiste Daroussin 	if (*newn == '\0')
358561d06d6bSBaptiste Daroussin 		return ROFF_IGN;
358661d06d6bSBaptiste Daroussin 
358761d06d6bSBaptiste Daroussin 	newsz = roff_getname(r, &oldn, ln, pos);
35887295610fSBaptiste Daroussin 	if (newn[newsz] == '\\' || newn[newsz] == '\t' || *oldn == '\0')
358961d06d6bSBaptiste Daroussin 		return ROFF_IGN;
359061d06d6bSBaptiste Daroussin 
359161d06d6bSBaptiste Daroussin 	end = oldn;
359261d06d6bSBaptiste Daroussin 	oldsz = roff_getname(r, &end, ln, oldn - buf->buf);
359361d06d6bSBaptiste Daroussin 	if (oldsz == 0)
359461d06d6bSBaptiste Daroussin 		return ROFF_IGN;
359561d06d6bSBaptiste Daroussin 
35967295610fSBaptiste Daroussin 	valsz = mandoc_asprintf(&value, ".%.*s \\$@\\\"\n",
359761d06d6bSBaptiste Daroussin 	    (int)oldsz, oldn);
359861d06d6bSBaptiste Daroussin 	roff_setstrn(&r->strtab, newn, newsz, value, valsz, 0);
359961d06d6bSBaptiste Daroussin 	roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
360061d06d6bSBaptiste Daroussin 	free(value);
360161d06d6bSBaptiste Daroussin 	return ROFF_IGN;
360261d06d6bSBaptiste Daroussin }
360361d06d6bSBaptiste Daroussin 
360445a5aec3SBaptiste Daroussin /*
360545a5aec3SBaptiste Daroussin  * The .break request only makes sense inside conditionals,
360645a5aec3SBaptiste Daroussin  * and that case is already handled in roff_cond_sub().
360745a5aec3SBaptiste Daroussin  */
360845a5aec3SBaptiste Daroussin static int
roff_break(ROFF_ARGS)360945a5aec3SBaptiste Daroussin roff_break(ROFF_ARGS)
361045a5aec3SBaptiste Daroussin {
361145a5aec3SBaptiste Daroussin 	mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, pos, "break");
361245a5aec3SBaptiste Daroussin 	return ROFF_IGN;
361345a5aec3SBaptiste Daroussin }
361445a5aec3SBaptiste Daroussin 
36157295610fSBaptiste Daroussin static int
roff_cc(ROFF_ARGS)361661d06d6bSBaptiste Daroussin roff_cc(ROFF_ARGS)
361761d06d6bSBaptiste Daroussin {
361861d06d6bSBaptiste Daroussin 	const char	*p;
361961d06d6bSBaptiste Daroussin 
362061d06d6bSBaptiste Daroussin 	p = buf->buf + pos;
362161d06d6bSBaptiste Daroussin 
362261d06d6bSBaptiste Daroussin 	if (*p == '\0' || (r->control = *p++) == '.')
362361d06d6bSBaptiste Daroussin 		r->control = '\0';
362461d06d6bSBaptiste Daroussin 
362561d06d6bSBaptiste Daroussin 	if (*p != '\0')
36267295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_EXCESS,
362761d06d6bSBaptiste Daroussin 		    ln, p - buf->buf, "cc ... %s", p);
362861d06d6bSBaptiste Daroussin 
362961d06d6bSBaptiste Daroussin 	return ROFF_IGN;
363061d06d6bSBaptiste Daroussin }
363161d06d6bSBaptiste Daroussin 
36327295610fSBaptiste Daroussin static int
roff_char(ROFF_ARGS)36337295610fSBaptiste Daroussin roff_char(ROFF_ARGS)
36347295610fSBaptiste Daroussin {
36357295610fSBaptiste Daroussin 	const char	*p, *kp, *vp;
36367295610fSBaptiste Daroussin 	size_t		 ksz, vsz;
36377295610fSBaptiste Daroussin 	int		 font;
36387295610fSBaptiste Daroussin 
36397295610fSBaptiste Daroussin 	/* Parse the character to be replaced. */
36407295610fSBaptiste Daroussin 
36417295610fSBaptiste Daroussin 	kp = buf->buf + pos;
36427295610fSBaptiste Daroussin 	p = kp + 1;
36437295610fSBaptiste Daroussin 	if (*kp == '\0' || (*kp == '\\' &&
36447295610fSBaptiste Daroussin 	     mandoc_escape(&p, NULL, NULL) != ESCAPE_SPECIAL) ||
36457295610fSBaptiste Daroussin 	    (*p != ' ' && *p != '\0')) {
36467295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_CHAR_ARG, ln, pos, "char %s", kp);
36477295610fSBaptiste Daroussin 		return ROFF_IGN;
36487295610fSBaptiste Daroussin 	}
36497295610fSBaptiste Daroussin 	ksz = p - kp;
36507295610fSBaptiste Daroussin 	while (*p == ' ')
36517295610fSBaptiste Daroussin 		p++;
36527295610fSBaptiste Daroussin 
36537295610fSBaptiste Daroussin 	/*
36547295610fSBaptiste Daroussin 	 * If the replacement string contains a font escape sequence,
36557295610fSBaptiste Daroussin 	 * we have to restore the font at the end.
36567295610fSBaptiste Daroussin 	 */
36577295610fSBaptiste Daroussin 
36587295610fSBaptiste Daroussin 	vp = p;
36597295610fSBaptiste Daroussin 	vsz = strlen(p);
36607295610fSBaptiste Daroussin 	font = 0;
36617295610fSBaptiste Daroussin 	while (*p != '\0') {
36627295610fSBaptiste Daroussin 		if (*p++ != '\\')
36637295610fSBaptiste Daroussin 			continue;
36647295610fSBaptiste Daroussin 		switch (mandoc_escape(&p, NULL, NULL)) {
36657295610fSBaptiste Daroussin 		case ESCAPE_FONT:
36667295610fSBaptiste Daroussin 		case ESCAPE_FONTROMAN:
36677295610fSBaptiste Daroussin 		case ESCAPE_FONTITALIC:
36687295610fSBaptiste Daroussin 		case ESCAPE_FONTBOLD:
36697295610fSBaptiste Daroussin 		case ESCAPE_FONTBI:
3670*6d38604fSBaptiste Daroussin 		case ESCAPE_FONTCR:
3671*6d38604fSBaptiste Daroussin 		case ESCAPE_FONTCB:
3672*6d38604fSBaptiste Daroussin 		case ESCAPE_FONTCI:
36737295610fSBaptiste Daroussin 		case ESCAPE_FONTPREV:
36747295610fSBaptiste Daroussin 			font++;
36757295610fSBaptiste Daroussin 			break;
36767295610fSBaptiste Daroussin 		default:
36777295610fSBaptiste Daroussin 			break;
36787295610fSBaptiste Daroussin 		}
36797295610fSBaptiste Daroussin 	}
36807295610fSBaptiste Daroussin 	if (font > 1)
36817295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_CHAR_FONT,
36827295610fSBaptiste Daroussin 		    ln, (int)(vp - buf->buf), "%s", vp);
36837295610fSBaptiste Daroussin 
36847295610fSBaptiste Daroussin 	/*
36857295610fSBaptiste Daroussin 	 * Approximate the effect of .char using the .tr tables.
36867295610fSBaptiste Daroussin 	 * XXX In groff, .char and .tr interact differently.
36877295610fSBaptiste Daroussin 	 */
36887295610fSBaptiste Daroussin 
36897295610fSBaptiste Daroussin 	if (ksz == 1) {
36907295610fSBaptiste Daroussin 		if (r->xtab == NULL)
36917295610fSBaptiste Daroussin 			r->xtab = mandoc_calloc(128, sizeof(*r->xtab));
36927295610fSBaptiste Daroussin 		assert((unsigned int)*kp < 128);
36937295610fSBaptiste Daroussin 		free(r->xtab[(int)*kp].p);
36947295610fSBaptiste Daroussin 		r->xtab[(int)*kp].sz = mandoc_asprintf(&r->xtab[(int)*kp].p,
36957295610fSBaptiste Daroussin 		    "%s%s", vp, font ? "\fP" : "");
36967295610fSBaptiste Daroussin 	} else {
36977295610fSBaptiste Daroussin 		roff_setstrn(&r->xmbtab, kp, ksz, vp, vsz, 0);
36987295610fSBaptiste Daroussin 		if (font)
36997295610fSBaptiste Daroussin 			roff_setstrn(&r->xmbtab, kp, ksz, "\\fP", 3, 1);
37007295610fSBaptiste Daroussin 	}
37017295610fSBaptiste Daroussin 	return ROFF_IGN;
37027295610fSBaptiste Daroussin }
37037295610fSBaptiste Daroussin 
37047295610fSBaptiste Daroussin static int
roff_ec(ROFF_ARGS)370561d06d6bSBaptiste Daroussin roff_ec(ROFF_ARGS)
370661d06d6bSBaptiste Daroussin {
370761d06d6bSBaptiste Daroussin 	const char	*p;
370861d06d6bSBaptiste Daroussin 
370961d06d6bSBaptiste Daroussin 	p = buf->buf + pos;
371061d06d6bSBaptiste Daroussin 	if (*p == '\0')
371161d06d6bSBaptiste Daroussin 		r->escape = '\\';
371261d06d6bSBaptiste Daroussin 	else {
371361d06d6bSBaptiste Daroussin 		r->escape = *p;
371461d06d6bSBaptiste Daroussin 		if (*++p != '\0')
37157295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_ARG_EXCESS, ln,
37167295610fSBaptiste Daroussin 			    (int)(p - buf->buf), "ec ... %s", p);
371761d06d6bSBaptiste Daroussin 	}
371861d06d6bSBaptiste Daroussin 	return ROFF_IGN;
371961d06d6bSBaptiste Daroussin }
372061d06d6bSBaptiste Daroussin 
37217295610fSBaptiste Daroussin static int
roff_eo(ROFF_ARGS)372261d06d6bSBaptiste Daroussin roff_eo(ROFF_ARGS)
372361d06d6bSBaptiste Daroussin {
372461d06d6bSBaptiste Daroussin 	r->escape = '\0';
372561d06d6bSBaptiste Daroussin 	if (buf->buf[pos] != '\0')
37267295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_SKIP,
372761d06d6bSBaptiste Daroussin 		    ln, pos, "eo %s", buf->buf + pos);
372861d06d6bSBaptiste Daroussin 	return ROFF_IGN;
372961d06d6bSBaptiste Daroussin }
373061d06d6bSBaptiste Daroussin 
37317295610fSBaptiste Daroussin static int
roff_nop(ROFF_ARGS)37327295610fSBaptiste Daroussin roff_nop(ROFF_ARGS)
37337295610fSBaptiste Daroussin {
37347295610fSBaptiste Daroussin 	while (buf->buf[pos] == ' ')
37357295610fSBaptiste Daroussin 		pos++;
37367295610fSBaptiste Daroussin 	*offs = pos;
37377295610fSBaptiste Daroussin 	return ROFF_RERUN;
37387295610fSBaptiste Daroussin }
37397295610fSBaptiste Daroussin 
37407295610fSBaptiste Daroussin static int
roff_tr(ROFF_ARGS)374161d06d6bSBaptiste Daroussin roff_tr(ROFF_ARGS)
374261d06d6bSBaptiste Daroussin {
374361d06d6bSBaptiste Daroussin 	const char	*p, *first, *second;
374461d06d6bSBaptiste Daroussin 	size_t		 fsz, ssz;
374561d06d6bSBaptiste Daroussin 	enum mandoc_esc	 esc;
374661d06d6bSBaptiste Daroussin 
374761d06d6bSBaptiste Daroussin 	p = buf->buf + pos;
374861d06d6bSBaptiste Daroussin 
374961d06d6bSBaptiste Daroussin 	if (*p == '\0') {
37507295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_REQ_EMPTY, ln, ppos, "tr");
375161d06d6bSBaptiste Daroussin 		return ROFF_IGN;
375261d06d6bSBaptiste Daroussin 	}
375361d06d6bSBaptiste Daroussin 
375461d06d6bSBaptiste Daroussin 	while (*p != '\0') {
375561d06d6bSBaptiste Daroussin 		fsz = ssz = 1;
375661d06d6bSBaptiste Daroussin 
375761d06d6bSBaptiste Daroussin 		first = p++;
375861d06d6bSBaptiste Daroussin 		if (*first == '\\') {
375961d06d6bSBaptiste Daroussin 			esc = mandoc_escape(&p, NULL, NULL);
376061d06d6bSBaptiste Daroussin 			if (esc == ESCAPE_ERROR) {
37617295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_ESC_BAD, ln,
37627295610fSBaptiste Daroussin 				    (int)(p - buf->buf), "%s", first);
376361d06d6bSBaptiste Daroussin 				return ROFF_IGN;
376461d06d6bSBaptiste Daroussin 			}
376561d06d6bSBaptiste Daroussin 			fsz = (size_t)(p - first);
376661d06d6bSBaptiste Daroussin 		}
376761d06d6bSBaptiste Daroussin 
376861d06d6bSBaptiste Daroussin 		second = p++;
376961d06d6bSBaptiste Daroussin 		if (*second == '\\') {
377061d06d6bSBaptiste Daroussin 			esc = mandoc_escape(&p, NULL, NULL);
377161d06d6bSBaptiste Daroussin 			if (esc == ESCAPE_ERROR) {
37727295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_ESC_BAD, ln,
37737295610fSBaptiste Daroussin 				    (int)(p - buf->buf), "%s", second);
377461d06d6bSBaptiste Daroussin 				return ROFF_IGN;
377561d06d6bSBaptiste Daroussin 			}
377661d06d6bSBaptiste Daroussin 			ssz = (size_t)(p - second);
377761d06d6bSBaptiste Daroussin 		} else if (*second == '\0') {
37787295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_TR_ODD, ln,
37797295610fSBaptiste Daroussin 			    (int)(first - buf->buf), "tr %s", first);
378061d06d6bSBaptiste Daroussin 			second = " ";
378161d06d6bSBaptiste Daroussin 			p--;
378261d06d6bSBaptiste Daroussin 		}
378361d06d6bSBaptiste Daroussin 
378461d06d6bSBaptiste Daroussin 		if (fsz > 1) {
378561d06d6bSBaptiste Daroussin 			roff_setstrn(&r->xmbtab, first, fsz,
378661d06d6bSBaptiste Daroussin 			    second, ssz, 0);
378761d06d6bSBaptiste Daroussin 			continue;
378861d06d6bSBaptiste Daroussin 		}
378961d06d6bSBaptiste Daroussin 
379061d06d6bSBaptiste Daroussin 		if (r->xtab == NULL)
379161d06d6bSBaptiste Daroussin 			r->xtab = mandoc_calloc(128,
379261d06d6bSBaptiste Daroussin 			    sizeof(struct roffstr));
379361d06d6bSBaptiste Daroussin 
379461d06d6bSBaptiste Daroussin 		free(r->xtab[(int)*first].p);
379561d06d6bSBaptiste Daroussin 		r->xtab[(int)*first].p = mandoc_strndup(second, ssz);
379661d06d6bSBaptiste Daroussin 		r->xtab[(int)*first].sz = ssz;
379761d06d6bSBaptiste Daroussin 	}
379861d06d6bSBaptiste Daroussin 
379961d06d6bSBaptiste Daroussin 	return ROFF_IGN;
380061d06d6bSBaptiste Daroussin }
380161d06d6bSBaptiste Daroussin 
38027295610fSBaptiste Daroussin /*
38037295610fSBaptiste Daroussin  * Implementation of the .return request.
38047295610fSBaptiste Daroussin  * There is no need to call roff_userret() from here.
38057295610fSBaptiste Daroussin  * The read module will call that after rewinding the reader stack
38067295610fSBaptiste Daroussin  * to the place from where the current macro was called.
38077295610fSBaptiste Daroussin  */
38087295610fSBaptiste Daroussin static int
roff_return(ROFF_ARGS)38097295610fSBaptiste Daroussin roff_return(ROFF_ARGS)
38107295610fSBaptiste Daroussin {
38117295610fSBaptiste Daroussin 	if (r->mstackpos >= 0)
38127295610fSBaptiste Daroussin 		return ROFF_IGN | ROFF_USERRET;
38137295610fSBaptiste Daroussin 
38147295610fSBaptiste Daroussin 	mandoc_msg(MANDOCERR_REQ_NOMAC, ln, ppos, "return");
38157295610fSBaptiste Daroussin 	return ROFF_IGN;
38167295610fSBaptiste Daroussin }
38177295610fSBaptiste Daroussin 
38187295610fSBaptiste Daroussin static int
roff_rn(ROFF_ARGS)381961d06d6bSBaptiste Daroussin roff_rn(ROFF_ARGS)
382061d06d6bSBaptiste Daroussin {
382161d06d6bSBaptiste Daroussin 	const char	*value;
382261d06d6bSBaptiste Daroussin 	char		*oldn, *newn, *end;
382361d06d6bSBaptiste Daroussin 	size_t		 oldsz, newsz;
382461d06d6bSBaptiste Daroussin 	int		 deftype;
382561d06d6bSBaptiste Daroussin 
382661d06d6bSBaptiste Daroussin 	oldn = newn = buf->buf + pos;
382761d06d6bSBaptiste Daroussin 	if (*oldn == '\0')
382861d06d6bSBaptiste Daroussin 		return ROFF_IGN;
382961d06d6bSBaptiste Daroussin 
383061d06d6bSBaptiste Daroussin 	oldsz = roff_getname(r, &newn, ln, pos);
38317295610fSBaptiste Daroussin 	if (oldn[oldsz] == '\\' || oldn[oldsz] == '\t' || *newn == '\0')
383261d06d6bSBaptiste Daroussin 		return ROFF_IGN;
383361d06d6bSBaptiste Daroussin 
383461d06d6bSBaptiste Daroussin 	end = newn;
383561d06d6bSBaptiste Daroussin 	newsz = roff_getname(r, &end, ln, newn - buf->buf);
383661d06d6bSBaptiste Daroussin 	if (newsz == 0)
383761d06d6bSBaptiste Daroussin 		return ROFF_IGN;
383861d06d6bSBaptiste Daroussin 
383961d06d6bSBaptiste Daroussin 	deftype = ROFFDEF_ANY;
384061d06d6bSBaptiste Daroussin 	value = roff_getstrn(r, oldn, oldsz, &deftype);
384161d06d6bSBaptiste Daroussin 	switch (deftype) {
384261d06d6bSBaptiste Daroussin 	case ROFFDEF_USER:
384361d06d6bSBaptiste Daroussin 		roff_setstrn(&r->strtab, newn, newsz, value, strlen(value), 0);
384461d06d6bSBaptiste Daroussin 		roff_setstrn(&r->strtab, oldn, oldsz, NULL, 0, 0);
384561d06d6bSBaptiste Daroussin 		roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
384661d06d6bSBaptiste Daroussin 		break;
384761d06d6bSBaptiste Daroussin 	case ROFFDEF_PRE:
384861d06d6bSBaptiste Daroussin 		roff_setstrn(&r->strtab, newn, newsz, value, strlen(value), 0);
384961d06d6bSBaptiste Daroussin 		roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
385061d06d6bSBaptiste Daroussin 		break;
385161d06d6bSBaptiste Daroussin 	case ROFFDEF_REN:
385261d06d6bSBaptiste Daroussin 		roff_setstrn(&r->rentab, newn, newsz, value, strlen(value), 0);
385361d06d6bSBaptiste Daroussin 		roff_setstrn(&r->rentab, oldn, oldsz, NULL, 0, 0);
385461d06d6bSBaptiste Daroussin 		roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0);
385561d06d6bSBaptiste Daroussin 		break;
385661d06d6bSBaptiste Daroussin 	case ROFFDEF_STD:
385761d06d6bSBaptiste Daroussin 		roff_setstrn(&r->rentab, newn, newsz, oldn, oldsz, 0);
385861d06d6bSBaptiste Daroussin 		roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0);
385961d06d6bSBaptiste Daroussin 		break;
386061d06d6bSBaptiste Daroussin 	default:
386161d06d6bSBaptiste Daroussin 		roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0);
386261d06d6bSBaptiste Daroussin 		roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
386361d06d6bSBaptiste Daroussin 		break;
386461d06d6bSBaptiste Daroussin 	}
386561d06d6bSBaptiste Daroussin 	return ROFF_IGN;
386661d06d6bSBaptiste Daroussin }
386761d06d6bSBaptiste Daroussin 
38687295610fSBaptiste Daroussin static int
roff_shift(ROFF_ARGS)38697295610fSBaptiste Daroussin roff_shift(ROFF_ARGS)
38707295610fSBaptiste Daroussin {
38717295610fSBaptiste Daroussin 	struct mctx	*ctx;
38727295610fSBaptiste Daroussin 	int		 levels, i;
38737295610fSBaptiste Daroussin 
38747295610fSBaptiste Daroussin 	levels = 1;
38757295610fSBaptiste Daroussin 	if (buf->buf[pos] != '\0' &&
38767295610fSBaptiste Daroussin 	    roff_evalnum(r, ln, buf->buf, &pos, &levels, 0) == 0) {
38777295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_CE_NONUM,
38787295610fSBaptiste Daroussin 		    ln, pos, "shift %s", buf->buf + pos);
38797295610fSBaptiste Daroussin 		levels = 1;
38807295610fSBaptiste Daroussin 	}
38817295610fSBaptiste Daroussin 	if (r->mstackpos < 0) {
38827295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_REQ_NOMAC, ln, ppos, "shift");
38837295610fSBaptiste Daroussin 		return ROFF_IGN;
38847295610fSBaptiste Daroussin 	}
38857295610fSBaptiste Daroussin 	ctx = r->mstack + r->mstackpos;
38867295610fSBaptiste Daroussin 	if (levels > ctx->argc) {
38877295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_SHIFT,
38887295610fSBaptiste Daroussin 		    ln, pos, "%d, but max is %d", levels, ctx->argc);
38897295610fSBaptiste Daroussin 		levels = ctx->argc;
38907295610fSBaptiste Daroussin 	}
38917295610fSBaptiste Daroussin 	if (levels == 0)
38927295610fSBaptiste Daroussin 		return ROFF_IGN;
38937295610fSBaptiste Daroussin 	for (i = 0; i < levels; i++)
38947295610fSBaptiste Daroussin 		free(ctx->argv[i]);
38957295610fSBaptiste Daroussin 	ctx->argc -= levels;
38967295610fSBaptiste Daroussin 	for (i = 0; i < ctx->argc; i++)
38977295610fSBaptiste Daroussin 		ctx->argv[i] = ctx->argv[i + levels];
38987295610fSBaptiste Daroussin 	return ROFF_IGN;
38997295610fSBaptiste Daroussin }
39007295610fSBaptiste Daroussin 
39017295610fSBaptiste Daroussin static int
roff_so(ROFF_ARGS)390261d06d6bSBaptiste Daroussin roff_so(ROFF_ARGS)
390361d06d6bSBaptiste Daroussin {
390461d06d6bSBaptiste Daroussin 	char *name, *cp;
390561d06d6bSBaptiste Daroussin 
390661d06d6bSBaptiste Daroussin 	name = buf->buf + pos;
39077295610fSBaptiste Daroussin 	mandoc_msg(MANDOCERR_SO, ln, ppos, "so %s", name);
390861d06d6bSBaptiste Daroussin 
390961d06d6bSBaptiste Daroussin 	/*
391061d06d6bSBaptiste Daroussin 	 * Handle `so'.  Be EXTREMELY careful, as we shouldn't be
391161d06d6bSBaptiste Daroussin 	 * opening anything that's not in our cwd or anything beneath
391261d06d6bSBaptiste Daroussin 	 * it.  Thus, explicitly disallow traversing up the file-system
391361d06d6bSBaptiste Daroussin 	 * or using absolute paths.
391461d06d6bSBaptiste Daroussin 	 */
391561d06d6bSBaptiste Daroussin 
391661d06d6bSBaptiste Daroussin 	if (*name == '/' || strstr(name, "../") || strstr(name, "/..")) {
39177295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_SO_PATH, ln, ppos, ".so %s", name);
391861d06d6bSBaptiste Daroussin 		buf->sz = mandoc_asprintf(&cp,
391961d06d6bSBaptiste Daroussin 		    ".sp\nSee the file %s.\n.sp", name) + 1;
392061d06d6bSBaptiste Daroussin 		free(buf->buf);
392161d06d6bSBaptiste Daroussin 		buf->buf = cp;
392261d06d6bSBaptiste Daroussin 		*offs = 0;
392361d06d6bSBaptiste Daroussin 		return ROFF_REPARSE;
392461d06d6bSBaptiste Daroussin 	}
392561d06d6bSBaptiste Daroussin 
392661d06d6bSBaptiste Daroussin 	*offs = pos;
392761d06d6bSBaptiste Daroussin 	return ROFF_SO;
392861d06d6bSBaptiste Daroussin }
392961d06d6bSBaptiste Daroussin 
393061d06d6bSBaptiste Daroussin /* --- user defined strings and macros ------------------------------------ */
393161d06d6bSBaptiste Daroussin 
39327295610fSBaptiste Daroussin static int
roff_userdef(ROFF_ARGS)393361d06d6bSBaptiste Daroussin roff_userdef(ROFF_ARGS)
393461d06d6bSBaptiste Daroussin {
39357295610fSBaptiste Daroussin 	struct mctx	 *ctx;
39367295610fSBaptiste Daroussin 	char		 *arg, *ap, *dst, *src;
39377295610fSBaptiste Daroussin 	size_t		  sz;
393861d06d6bSBaptiste Daroussin 
393945a5aec3SBaptiste Daroussin 	/* If the macro is empty, ignore it altogether. */
394045a5aec3SBaptiste Daroussin 
394145a5aec3SBaptiste Daroussin 	if (*r->current_string == '\0')
394245a5aec3SBaptiste Daroussin 		return ROFF_IGN;
394345a5aec3SBaptiste Daroussin 
39447295610fSBaptiste Daroussin 	/* Initialize a new macro stack context. */
394561d06d6bSBaptiste Daroussin 
39467295610fSBaptiste Daroussin 	if (++r->mstackpos == r->mstacksz) {
39477295610fSBaptiste Daroussin 		r->mstack = mandoc_recallocarray(r->mstack,
39487295610fSBaptiste Daroussin 		    r->mstacksz, r->mstacksz + 8, sizeof(*r->mstack));
39497295610fSBaptiste Daroussin 		r->mstacksz += 8;
395061d06d6bSBaptiste Daroussin 	}
39517295610fSBaptiste Daroussin 	ctx = r->mstack + r->mstackpos;
39527295610fSBaptiste Daroussin 	ctx->argsz = 0;
39537295610fSBaptiste Daroussin 	ctx->argc = 0;
39547295610fSBaptiste Daroussin 	ctx->argv = NULL;
39557295610fSBaptiste Daroussin 
39567295610fSBaptiste Daroussin 	/*
39577295610fSBaptiste Daroussin 	 * Collect pointers to macro argument strings,
39587295610fSBaptiste Daroussin 	 * NUL-terminating them and escaping quotes.
39597295610fSBaptiste Daroussin 	 */
39607295610fSBaptiste Daroussin 
39617295610fSBaptiste Daroussin 	src = buf->buf + pos;
39627295610fSBaptiste Daroussin 	while (*src != '\0') {
39637295610fSBaptiste Daroussin 		if (ctx->argc == ctx->argsz) {
39647295610fSBaptiste Daroussin 			ctx->argsz += 8;
39657295610fSBaptiste Daroussin 			ctx->argv = mandoc_reallocarray(ctx->argv,
39667295610fSBaptiste Daroussin 			    ctx->argsz, sizeof(*ctx->argv));
396761d06d6bSBaptiste Daroussin 		}
39687295610fSBaptiste Daroussin 		arg = roff_getarg(r, &src, ln, &pos);
39697295610fSBaptiste Daroussin 		sz = 1;  /* For the terminating NUL. */
39707295610fSBaptiste Daroussin 		for (ap = arg; *ap != '\0'; ap++)
39717295610fSBaptiste Daroussin 			sz += *ap == '"' ? 4 : 1;
39727295610fSBaptiste Daroussin 		ctx->argv[ctx->argc++] = dst = mandoc_malloc(sz);
39737295610fSBaptiste Daroussin 		for (ap = arg; *ap != '\0'; ap++) {
397461d06d6bSBaptiste Daroussin 			if (*ap == '"') {
39757295610fSBaptiste Daroussin 				memcpy(dst, "\\(dq", 4);
39767295610fSBaptiste Daroussin 				dst += 4;
397761d06d6bSBaptiste Daroussin 			} else
39787295610fSBaptiste Daroussin 				*dst++ = *ap;
397961d06d6bSBaptiste Daroussin 		}
39807295610fSBaptiste Daroussin 		*dst = '\0';
39817295610fSBaptiste Daroussin 		free(arg);
398261d06d6bSBaptiste Daroussin 	}
398361d06d6bSBaptiste Daroussin 
39847295610fSBaptiste Daroussin 	/* Replace the macro invocation by the macro definition. */
398561d06d6bSBaptiste Daroussin 
398661d06d6bSBaptiste Daroussin 	free(buf->buf);
39877295610fSBaptiste Daroussin 	buf->buf = mandoc_strdup(r->current_string);
39887295610fSBaptiste Daroussin 	buf->sz = strlen(buf->buf) + 1;
398961d06d6bSBaptiste Daroussin 	*offs = 0;
399061d06d6bSBaptiste Daroussin 
399145a5aec3SBaptiste Daroussin 	return buf->buf[buf->sz - 2] == '\n' ?
39927295610fSBaptiste Daroussin 	    ROFF_REPARSE | ROFF_USERCALL : ROFF_IGN | ROFF_APPEND;
399361d06d6bSBaptiste Daroussin }
399461d06d6bSBaptiste Daroussin 
399561d06d6bSBaptiste Daroussin /*
399661d06d6bSBaptiste Daroussin  * Calling a high-level macro that was renamed with .rn.
399761d06d6bSBaptiste Daroussin  * r->current_string has already been set up by roff_parse().
399861d06d6bSBaptiste Daroussin  */
39997295610fSBaptiste Daroussin static int
roff_renamed(ROFF_ARGS)400061d06d6bSBaptiste Daroussin roff_renamed(ROFF_ARGS)
400161d06d6bSBaptiste Daroussin {
400261d06d6bSBaptiste Daroussin 	char	*nbuf;
400361d06d6bSBaptiste Daroussin 
400461d06d6bSBaptiste Daroussin 	buf->sz = mandoc_asprintf(&nbuf, ".%s%s%s", r->current_string,
400561d06d6bSBaptiste Daroussin 	    buf->buf[pos] == '\0' ? "" : " ", buf->buf + pos) + 1;
400661d06d6bSBaptiste Daroussin 	free(buf->buf);
400761d06d6bSBaptiste Daroussin 	buf->buf = nbuf;
400861d06d6bSBaptiste Daroussin 	*offs = 0;
400961d06d6bSBaptiste Daroussin 	return ROFF_CONT;
401061d06d6bSBaptiste Daroussin }
401161d06d6bSBaptiste Daroussin 
40127295610fSBaptiste Daroussin /*
40137295610fSBaptiste Daroussin  * Measure the length in bytes of the roff identifier at *cpp
40147295610fSBaptiste Daroussin  * and advance the pointer to the next word.
40157295610fSBaptiste Daroussin  */
401661d06d6bSBaptiste Daroussin static size_t
roff_getname(struct roff * r,char ** cpp,int ln,int pos)401761d06d6bSBaptiste Daroussin roff_getname(struct roff *r, char **cpp, int ln, int pos)
401861d06d6bSBaptiste Daroussin {
401961d06d6bSBaptiste Daroussin 	char	 *name, *cp;
402061d06d6bSBaptiste Daroussin 	size_t	  namesz;
402161d06d6bSBaptiste Daroussin 
402261d06d6bSBaptiste Daroussin 	name = *cpp;
40237295610fSBaptiste Daroussin 	if (*name == '\0')
402461d06d6bSBaptiste Daroussin 		return 0;
402561d06d6bSBaptiste Daroussin 
40267295610fSBaptiste Daroussin 	/* Advance cp to the byte after the end of the name. */
40277295610fSBaptiste Daroussin 
402861d06d6bSBaptiste Daroussin 	for (cp = name; 1; cp++) {
402961d06d6bSBaptiste Daroussin 		namesz = cp - name;
40307295610fSBaptiste Daroussin 		if (*cp == '\0')
40317295610fSBaptiste Daroussin 			break;
40327295610fSBaptiste Daroussin 		if (*cp == ' ' || *cp == '\t') {
40337295610fSBaptiste Daroussin 			cp++;
403461d06d6bSBaptiste Daroussin 			break;
403561d06d6bSBaptiste Daroussin 		}
40367295610fSBaptiste Daroussin 		if (*cp != '\\')
403761d06d6bSBaptiste Daroussin 			continue;
40387295610fSBaptiste Daroussin 		if (cp[1] == '{' || cp[1] == '}')
403961d06d6bSBaptiste Daroussin 			break;
40407295610fSBaptiste Daroussin 		if (*++cp == '\\')
404161d06d6bSBaptiste Daroussin 			continue;
40427295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_NAMESC, ln, pos,
404361d06d6bSBaptiste Daroussin 		    "%.*s", (int)(cp - name + 1), name);
404461d06d6bSBaptiste Daroussin 		mandoc_escape((const char **)&cp, NULL, NULL);
404561d06d6bSBaptiste Daroussin 		break;
404661d06d6bSBaptiste Daroussin 	}
404761d06d6bSBaptiste Daroussin 
404861d06d6bSBaptiste Daroussin 	/* Read past spaces. */
40497295610fSBaptiste Daroussin 
40507295610fSBaptiste Daroussin 	while (*cp == ' ')
405161d06d6bSBaptiste Daroussin 		cp++;
405261d06d6bSBaptiste Daroussin 
405361d06d6bSBaptiste Daroussin 	*cpp = cp;
405461d06d6bSBaptiste Daroussin 	return namesz;
405561d06d6bSBaptiste Daroussin }
405661d06d6bSBaptiste Daroussin 
405761d06d6bSBaptiste Daroussin /*
405861d06d6bSBaptiste Daroussin  * Store *string into the user-defined string called *name.
405961d06d6bSBaptiste Daroussin  * To clear an existing entry, call with (*r, *name, NULL, 0).
406061d06d6bSBaptiste Daroussin  * append == 0: replace mode
406161d06d6bSBaptiste Daroussin  * append == 1: single-line append mode
406261d06d6bSBaptiste Daroussin  * append == 2: multiline append mode, append '\n' after each call
406361d06d6bSBaptiste Daroussin  */
406461d06d6bSBaptiste Daroussin static void
roff_setstr(struct roff * r,const char * name,const char * string,int append)406561d06d6bSBaptiste Daroussin roff_setstr(struct roff *r, const char *name, const char *string,
406661d06d6bSBaptiste Daroussin 	int append)
406761d06d6bSBaptiste Daroussin {
406861d06d6bSBaptiste Daroussin 	size_t	 namesz;
406961d06d6bSBaptiste Daroussin 
407061d06d6bSBaptiste Daroussin 	namesz = strlen(name);
407161d06d6bSBaptiste Daroussin 	roff_setstrn(&r->strtab, name, namesz, string,
407261d06d6bSBaptiste Daroussin 	    string ? strlen(string) : 0, append);
407361d06d6bSBaptiste Daroussin 	roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
407461d06d6bSBaptiste Daroussin }
407561d06d6bSBaptiste Daroussin 
407661d06d6bSBaptiste Daroussin static void
roff_setstrn(struct roffkv ** r,const char * name,size_t namesz,const char * string,size_t stringsz,int append)407761d06d6bSBaptiste Daroussin roff_setstrn(struct roffkv **r, const char *name, size_t namesz,
407861d06d6bSBaptiste Daroussin 		const char *string, size_t stringsz, int append)
407961d06d6bSBaptiste Daroussin {
408061d06d6bSBaptiste Daroussin 	struct roffkv	*n;
408161d06d6bSBaptiste Daroussin 	char		*c;
408261d06d6bSBaptiste Daroussin 	int		 i;
408361d06d6bSBaptiste Daroussin 	size_t		 oldch, newch;
408461d06d6bSBaptiste Daroussin 
408561d06d6bSBaptiste Daroussin 	/* Search for an existing string with the same name. */
408661d06d6bSBaptiste Daroussin 	n = *r;
408761d06d6bSBaptiste Daroussin 
408861d06d6bSBaptiste Daroussin 	while (n && (namesz != n->key.sz ||
408961d06d6bSBaptiste Daroussin 			strncmp(n->key.p, name, namesz)))
409061d06d6bSBaptiste Daroussin 		n = n->next;
409161d06d6bSBaptiste Daroussin 
409261d06d6bSBaptiste Daroussin 	if (NULL == n) {
409361d06d6bSBaptiste Daroussin 		/* Create a new string table entry. */
409461d06d6bSBaptiste Daroussin 		n = mandoc_malloc(sizeof(struct roffkv));
409561d06d6bSBaptiste Daroussin 		n->key.p = mandoc_strndup(name, namesz);
409661d06d6bSBaptiste Daroussin 		n->key.sz = namesz;
409761d06d6bSBaptiste Daroussin 		n->val.p = NULL;
409861d06d6bSBaptiste Daroussin 		n->val.sz = 0;
409961d06d6bSBaptiste Daroussin 		n->next = *r;
410061d06d6bSBaptiste Daroussin 		*r = n;
410161d06d6bSBaptiste Daroussin 	} else if (0 == append) {
410261d06d6bSBaptiste Daroussin 		free(n->val.p);
410361d06d6bSBaptiste Daroussin 		n->val.p = NULL;
410461d06d6bSBaptiste Daroussin 		n->val.sz = 0;
410561d06d6bSBaptiste Daroussin 	}
410661d06d6bSBaptiste Daroussin 
410761d06d6bSBaptiste Daroussin 	if (NULL == string)
410861d06d6bSBaptiste Daroussin 		return;
410961d06d6bSBaptiste Daroussin 
411061d06d6bSBaptiste Daroussin 	/*
411161d06d6bSBaptiste Daroussin 	 * One additional byte for the '\n' in multiline mode,
411261d06d6bSBaptiste Daroussin 	 * and one for the terminating '\0'.
411361d06d6bSBaptiste Daroussin 	 */
411461d06d6bSBaptiste Daroussin 	newch = stringsz + (1 < append ? 2u : 1u);
411561d06d6bSBaptiste Daroussin 
411661d06d6bSBaptiste Daroussin 	if (NULL == n->val.p) {
411761d06d6bSBaptiste Daroussin 		n->val.p = mandoc_malloc(newch);
411861d06d6bSBaptiste Daroussin 		*n->val.p = '\0';
411961d06d6bSBaptiste Daroussin 		oldch = 0;
412061d06d6bSBaptiste Daroussin 	} else {
412161d06d6bSBaptiste Daroussin 		oldch = n->val.sz;
412261d06d6bSBaptiste Daroussin 		n->val.p = mandoc_realloc(n->val.p, oldch + newch);
412361d06d6bSBaptiste Daroussin 	}
412461d06d6bSBaptiste Daroussin 
412561d06d6bSBaptiste Daroussin 	/* Skip existing content in the destination buffer. */
412661d06d6bSBaptiste Daroussin 	c = n->val.p + (int)oldch;
412761d06d6bSBaptiste Daroussin 
412861d06d6bSBaptiste Daroussin 	/* Append new content to the destination buffer. */
412961d06d6bSBaptiste Daroussin 	i = 0;
413061d06d6bSBaptiste Daroussin 	while (i < (int)stringsz) {
413161d06d6bSBaptiste Daroussin 		/*
413261d06d6bSBaptiste Daroussin 		 * Rudimentary roff copy mode:
413361d06d6bSBaptiste Daroussin 		 * Handle escaped backslashes.
413461d06d6bSBaptiste Daroussin 		 */
413561d06d6bSBaptiste Daroussin 		if ('\\' == string[i] && '\\' == string[i + 1])
413661d06d6bSBaptiste Daroussin 			i++;
413761d06d6bSBaptiste Daroussin 		*c++ = string[i++];
413861d06d6bSBaptiste Daroussin 	}
413961d06d6bSBaptiste Daroussin 
414061d06d6bSBaptiste Daroussin 	/* Append terminating bytes. */
414161d06d6bSBaptiste Daroussin 	if (1 < append)
414261d06d6bSBaptiste Daroussin 		*c++ = '\n';
414361d06d6bSBaptiste Daroussin 
414461d06d6bSBaptiste Daroussin 	*c = '\0';
414561d06d6bSBaptiste Daroussin 	n->val.sz = (int)(c - n->val.p);
414661d06d6bSBaptiste Daroussin }
414761d06d6bSBaptiste Daroussin 
414861d06d6bSBaptiste Daroussin static const char *
roff_getstrn(struct roff * r,const char * name,size_t len,int * deftype)414961d06d6bSBaptiste Daroussin roff_getstrn(struct roff *r, const char *name, size_t len,
415061d06d6bSBaptiste Daroussin     int *deftype)
415161d06d6bSBaptiste Daroussin {
415261d06d6bSBaptiste Daroussin 	const struct roffkv	*n;
415361d06d6bSBaptiste Daroussin 	int			 found, i;
415461d06d6bSBaptiste Daroussin 	enum roff_tok		 tok;
415561d06d6bSBaptiste Daroussin 
415661d06d6bSBaptiste Daroussin 	found = 0;
415761d06d6bSBaptiste Daroussin 	for (n = r->strtab; n != NULL; n = n->next) {
415861d06d6bSBaptiste Daroussin 		if (strncmp(name, n->key.p, len) != 0 ||
415961d06d6bSBaptiste Daroussin 		    n->key.p[len] != '\0' || n->val.p == NULL)
416061d06d6bSBaptiste Daroussin 			continue;
416161d06d6bSBaptiste Daroussin 		if (*deftype & ROFFDEF_USER) {
416261d06d6bSBaptiste Daroussin 			*deftype = ROFFDEF_USER;
416361d06d6bSBaptiste Daroussin 			return n->val.p;
416461d06d6bSBaptiste Daroussin 		} else {
416561d06d6bSBaptiste Daroussin 			found = 1;
416661d06d6bSBaptiste Daroussin 			break;
416761d06d6bSBaptiste Daroussin 		}
416861d06d6bSBaptiste Daroussin 	}
416961d06d6bSBaptiste Daroussin 	for (n = r->rentab; n != NULL; n = n->next) {
417061d06d6bSBaptiste Daroussin 		if (strncmp(name, n->key.p, len) != 0 ||
417161d06d6bSBaptiste Daroussin 		    n->key.p[len] != '\0' || n->val.p == NULL)
417261d06d6bSBaptiste Daroussin 			continue;
417361d06d6bSBaptiste Daroussin 		if (*deftype & ROFFDEF_REN) {
417461d06d6bSBaptiste Daroussin 			*deftype = ROFFDEF_REN;
417561d06d6bSBaptiste Daroussin 			return n->val.p;
417661d06d6bSBaptiste Daroussin 		} else {
417761d06d6bSBaptiste Daroussin 			found = 1;
417861d06d6bSBaptiste Daroussin 			break;
417961d06d6bSBaptiste Daroussin 		}
418061d06d6bSBaptiste Daroussin 	}
418161d06d6bSBaptiste Daroussin 	for (i = 0; i < PREDEFS_MAX; i++) {
418261d06d6bSBaptiste Daroussin 		if (strncmp(name, predefs[i].name, len) != 0 ||
418361d06d6bSBaptiste Daroussin 		    predefs[i].name[len] != '\0')
418461d06d6bSBaptiste Daroussin 			continue;
418561d06d6bSBaptiste Daroussin 		if (*deftype & ROFFDEF_PRE) {
418661d06d6bSBaptiste Daroussin 			*deftype = ROFFDEF_PRE;
418761d06d6bSBaptiste Daroussin 			return predefs[i].str;
418861d06d6bSBaptiste Daroussin 		} else {
418961d06d6bSBaptiste Daroussin 			found = 1;
419061d06d6bSBaptiste Daroussin 			break;
419161d06d6bSBaptiste Daroussin 		}
419261d06d6bSBaptiste Daroussin 	}
41937295610fSBaptiste Daroussin 	if (r->man->meta.macroset != MACROSET_MAN) {
419461d06d6bSBaptiste Daroussin 		for (tok = MDOC_Dd; tok < MDOC_MAX; tok++) {
419561d06d6bSBaptiste Daroussin 			if (strncmp(name, roff_name[tok], len) != 0 ||
419661d06d6bSBaptiste Daroussin 			    roff_name[tok][len] != '\0')
419761d06d6bSBaptiste Daroussin 				continue;
419861d06d6bSBaptiste Daroussin 			if (*deftype & ROFFDEF_STD) {
419961d06d6bSBaptiste Daroussin 				*deftype = ROFFDEF_STD;
420061d06d6bSBaptiste Daroussin 				return NULL;
420161d06d6bSBaptiste Daroussin 			} else {
420261d06d6bSBaptiste Daroussin 				found = 1;
420361d06d6bSBaptiste Daroussin 				break;
420461d06d6bSBaptiste Daroussin 			}
420561d06d6bSBaptiste Daroussin 		}
420661d06d6bSBaptiste Daroussin 	}
42077295610fSBaptiste Daroussin 	if (r->man->meta.macroset != MACROSET_MDOC) {
420861d06d6bSBaptiste Daroussin 		for (tok = MAN_TH; tok < MAN_MAX; tok++) {
420961d06d6bSBaptiste Daroussin 			if (strncmp(name, roff_name[tok], len) != 0 ||
421061d06d6bSBaptiste Daroussin 			    roff_name[tok][len] != '\0')
421161d06d6bSBaptiste Daroussin 				continue;
421261d06d6bSBaptiste Daroussin 			if (*deftype & ROFFDEF_STD) {
421361d06d6bSBaptiste Daroussin 				*deftype = ROFFDEF_STD;
421461d06d6bSBaptiste Daroussin 				return NULL;
421561d06d6bSBaptiste Daroussin 			} else {
421661d06d6bSBaptiste Daroussin 				found = 1;
421761d06d6bSBaptiste Daroussin 				break;
421861d06d6bSBaptiste Daroussin 			}
421961d06d6bSBaptiste Daroussin 		}
422061d06d6bSBaptiste Daroussin 	}
422161d06d6bSBaptiste Daroussin 
422261d06d6bSBaptiste Daroussin 	if (found == 0 && *deftype != ROFFDEF_ANY) {
422361d06d6bSBaptiste Daroussin 		if (*deftype & ROFFDEF_REN) {
422461d06d6bSBaptiste Daroussin 			/*
422561d06d6bSBaptiste Daroussin 			 * This might still be a request,
422661d06d6bSBaptiste Daroussin 			 * so do not treat it as undefined yet.
422761d06d6bSBaptiste Daroussin 			 */
422861d06d6bSBaptiste Daroussin 			*deftype = ROFFDEF_UNDEF;
422961d06d6bSBaptiste Daroussin 			return NULL;
423061d06d6bSBaptiste Daroussin 		}
423161d06d6bSBaptiste Daroussin 
423261d06d6bSBaptiste Daroussin 		/* Using an undefined string defines it to be empty. */
423361d06d6bSBaptiste Daroussin 
423461d06d6bSBaptiste Daroussin 		roff_setstrn(&r->strtab, name, len, "", 0, 0);
423561d06d6bSBaptiste Daroussin 		roff_setstrn(&r->rentab, name, len, NULL, 0, 0);
423661d06d6bSBaptiste Daroussin 	}
423761d06d6bSBaptiste Daroussin 
423861d06d6bSBaptiste Daroussin 	*deftype = 0;
423961d06d6bSBaptiste Daroussin 	return NULL;
424061d06d6bSBaptiste Daroussin }
424161d06d6bSBaptiste Daroussin 
424261d06d6bSBaptiste Daroussin static void
roff_freestr(struct roffkv * r)424361d06d6bSBaptiste Daroussin roff_freestr(struct roffkv *r)
424461d06d6bSBaptiste Daroussin {
424561d06d6bSBaptiste Daroussin 	struct roffkv	 *n, *nn;
424661d06d6bSBaptiste Daroussin 
424761d06d6bSBaptiste Daroussin 	for (n = r; n; n = nn) {
424861d06d6bSBaptiste Daroussin 		free(n->key.p);
424961d06d6bSBaptiste Daroussin 		free(n->val.p);
425061d06d6bSBaptiste Daroussin 		nn = n->next;
425161d06d6bSBaptiste Daroussin 		free(n);
425261d06d6bSBaptiste Daroussin 	}
425361d06d6bSBaptiste Daroussin }
425461d06d6bSBaptiste Daroussin 
425561d06d6bSBaptiste Daroussin /* --- accessors and utility functions ------------------------------------ */
425661d06d6bSBaptiste Daroussin 
425761d06d6bSBaptiste Daroussin /*
425861d06d6bSBaptiste Daroussin  * Duplicate an input string, making the appropriate character
425961d06d6bSBaptiste Daroussin  * conversations (as stipulated by `tr') along the way.
426061d06d6bSBaptiste Daroussin  * Returns a heap-allocated string with all the replacements made.
426161d06d6bSBaptiste Daroussin  */
426261d06d6bSBaptiste Daroussin char *
roff_strdup(const struct roff * r,const char * p)426361d06d6bSBaptiste Daroussin roff_strdup(const struct roff *r, const char *p)
426461d06d6bSBaptiste Daroussin {
426561d06d6bSBaptiste Daroussin 	const struct roffkv *cp;
426661d06d6bSBaptiste Daroussin 	char		*res;
426761d06d6bSBaptiste Daroussin 	const char	*pp;
426861d06d6bSBaptiste Daroussin 	size_t		 ssz, sz;
426961d06d6bSBaptiste Daroussin 	enum mandoc_esc	 esc;
427061d06d6bSBaptiste Daroussin 
427161d06d6bSBaptiste Daroussin 	if (NULL == r->xmbtab && NULL == r->xtab)
427261d06d6bSBaptiste Daroussin 		return mandoc_strdup(p);
427361d06d6bSBaptiste Daroussin 	else if ('\0' == *p)
427461d06d6bSBaptiste Daroussin 		return mandoc_strdup("");
427561d06d6bSBaptiste Daroussin 
427661d06d6bSBaptiste Daroussin 	/*
427761d06d6bSBaptiste Daroussin 	 * Step through each character looking for term matches
427861d06d6bSBaptiste Daroussin 	 * (remember that a `tr' can be invoked with an escape, which is
427961d06d6bSBaptiste Daroussin 	 * a glyph but the escape is multi-character).
428061d06d6bSBaptiste Daroussin 	 * We only do this if the character hash has been initialised
428161d06d6bSBaptiste Daroussin 	 * and the string is >0 length.
428261d06d6bSBaptiste Daroussin 	 */
428361d06d6bSBaptiste Daroussin 
428461d06d6bSBaptiste Daroussin 	res = NULL;
428561d06d6bSBaptiste Daroussin 	ssz = 0;
428661d06d6bSBaptiste Daroussin 
428761d06d6bSBaptiste Daroussin 	while ('\0' != *p) {
428861d06d6bSBaptiste Daroussin 		assert((unsigned int)*p < 128);
428961d06d6bSBaptiste Daroussin 		if ('\\' != *p && r->xtab && r->xtab[(unsigned int)*p].p) {
429061d06d6bSBaptiste Daroussin 			sz = r->xtab[(int)*p].sz;
429161d06d6bSBaptiste Daroussin 			res = mandoc_realloc(res, ssz + sz + 1);
429261d06d6bSBaptiste Daroussin 			memcpy(res + ssz, r->xtab[(int)*p].p, sz);
429361d06d6bSBaptiste Daroussin 			ssz += sz;
429461d06d6bSBaptiste Daroussin 			p++;
429561d06d6bSBaptiste Daroussin 			continue;
429661d06d6bSBaptiste Daroussin 		} else if ('\\' != *p) {
429761d06d6bSBaptiste Daroussin 			res = mandoc_realloc(res, ssz + 2);
429861d06d6bSBaptiste Daroussin 			res[ssz++] = *p++;
429961d06d6bSBaptiste Daroussin 			continue;
430061d06d6bSBaptiste Daroussin 		}
430161d06d6bSBaptiste Daroussin 
430261d06d6bSBaptiste Daroussin 		/* Search for term matches. */
430361d06d6bSBaptiste Daroussin 		for (cp = r->xmbtab; cp; cp = cp->next)
430461d06d6bSBaptiste Daroussin 			if (0 == strncmp(p, cp->key.p, cp->key.sz))
430561d06d6bSBaptiste Daroussin 				break;
430661d06d6bSBaptiste Daroussin 
430761d06d6bSBaptiste Daroussin 		if (NULL != cp) {
430861d06d6bSBaptiste Daroussin 			/*
430961d06d6bSBaptiste Daroussin 			 * A match has been found.
431061d06d6bSBaptiste Daroussin 			 * Append the match to the array and move
431161d06d6bSBaptiste Daroussin 			 * forward by its keysize.
431261d06d6bSBaptiste Daroussin 			 */
431361d06d6bSBaptiste Daroussin 			res = mandoc_realloc(res,
431461d06d6bSBaptiste Daroussin 			    ssz + cp->val.sz + 1);
431561d06d6bSBaptiste Daroussin 			memcpy(res + ssz, cp->val.p, cp->val.sz);
431661d06d6bSBaptiste Daroussin 			ssz += cp->val.sz;
431761d06d6bSBaptiste Daroussin 			p += (int)cp->key.sz;
431861d06d6bSBaptiste Daroussin 			continue;
431961d06d6bSBaptiste Daroussin 		}
432061d06d6bSBaptiste Daroussin 
432161d06d6bSBaptiste Daroussin 		/*
432261d06d6bSBaptiste Daroussin 		 * Handle escapes carefully: we need to copy
432361d06d6bSBaptiste Daroussin 		 * over just the escape itself, or else we might
432461d06d6bSBaptiste Daroussin 		 * do replacements within the escape itself.
432561d06d6bSBaptiste Daroussin 		 * Make sure to pass along the bogus string.
432661d06d6bSBaptiste Daroussin 		 */
432761d06d6bSBaptiste Daroussin 		pp = p++;
432861d06d6bSBaptiste Daroussin 		esc = mandoc_escape(&p, NULL, NULL);
432961d06d6bSBaptiste Daroussin 		if (ESCAPE_ERROR == esc) {
433061d06d6bSBaptiste Daroussin 			sz = strlen(pp);
433161d06d6bSBaptiste Daroussin 			res = mandoc_realloc(res, ssz + sz + 1);
433261d06d6bSBaptiste Daroussin 			memcpy(res + ssz, pp, sz);
433361d06d6bSBaptiste Daroussin 			break;
433461d06d6bSBaptiste Daroussin 		}
433561d06d6bSBaptiste Daroussin 		/*
433661d06d6bSBaptiste Daroussin 		 * We bail out on bad escapes.
433761d06d6bSBaptiste Daroussin 		 * No need to warn: we already did so when
43387295610fSBaptiste Daroussin 		 * roff_expand() was called.
433961d06d6bSBaptiste Daroussin 		 */
434061d06d6bSBaptiste Daroussin 		sz = (int)(p - pp);
434161d06d6bSBaptiste Daroussin 		res = mandoc_realloc(res, ssz + sz + 1);
434261d06d6bSBaptiste Daroussin 		memcpy(res + ssz, pp, sz);
434361d06d6bSBaptiste Daroussin 		ssz += sz;
434461d06d6bSBaptiste Daroussin 	}
434561d06d6bSBaptiste Daroussin 
434661d06d6bSBaptiste Daroussin 	res[(int)ssz] = '\0';
434761d06d6bSBaptiste Daroussin 	return res;
434861d06d6bSBaptiste Daroussin }
434961d06d6bSBaptiste Daroussin 
435061d06d6bSBaptiste Daroussin int
roff_getformat(const struct roff * r)435161d06d6bSBaptiste Daroussin roff_getformat(const struct roff *r)
435261d06d6bSBaptiste Daroussin {
435361d06d6bSBaptiste Daroussin 
435461d06d6bSBaptiste Daroussin 	return r->format;
435561d06d6bSBaptiste Daroussin }
435661d06d6bSBaptiste Daroussin 
435761d06d6bSBaptiste Daroussin /*
435861d06d6bSBaptiste Daroussin  * Find out whether a line is a macro line or not.
435961d06d6bSBaptiste Daroussin  * If it is, adjust the current position and return one; if it isn't,
436061d06d6bSBaptiste Daroussin  * return zero and don't change the current position.
436161d06d6bSBaptiste Daroussin  * If the control character has been set with `.cc', then let that grain
436261d06d6bSBaptiste Daroussin  * precedence.
436361d06d6bSBaptiste Daroussin  * This is slighly contrary to groff, where using the non-breaking
436461d06d6bSBaptiste Daroussin  * control character when `cc' has been invoked will cause the
436561d06d6bSBaptiste Daroussin  * non-breaking macro contents to be printed verbatim.
436661d06d6bSBaptiste Daroussin  */
436761d06d6bSBaptiste Daroussin int
roff_getcontrol(const struct roff * r,const char * cp,int * ppos)436861d06d6bSBaptiste Daroussin roff_getcontrol(const struct roff *r, const char *cp, int *ppos)
436961d06d6bSBaptiste Daroussin {
437061d06d6bSBaptiste Daroussin 	int		pos;
437161d06d6bSBaptiste Daroussin 
437261d06d6bSBaptiste Daroussin 	pos = *ppos;
437361d06d6bSBaptiste Daroussin 
437461d06d6bSBaptiste Daroussin 	if (r->control != '\0' && cp[pos] == r->control)
437561d06d6bSBaptiste Daroussin 		pos++;
437661d06d6bSBaptiste Daroussin 	else if (r->control != '\0')
437761d06d6bSBaptiste Daroussin 		return 0;
437861d06d6bSBaptiste Daroussin 	else if ('\\' == cp[pos] && '.' == cp[pos + 1])
437961d06d6bSBaptiste Daroussin 		pos += 2;
438061d06d6bSBaptiste Daroussin 	else if ('.' == cp[pos] || '\'' == cp[pos])
438161d06d6bSBaptiste Daroussin 		pos++;
438261d06d6bSBaptiste Daroussin 	else
438361d06d6bSBaptiste Daroussin 		return 0;
438461d06d6bSBaptiste Daroussin 
438561d06d6bSBaptiste Daroussin 	while (' ' == cp[pos] || '\t' == cp[pos])
438661d06d6bSBaptiste Daroussin 		pos++;
438761d06d6bSBaptiste Daroussin 
438861d06d6bSBaptiste Daroussin 	*ppos = pos;
438961d06d6bSBaptiste Daroussin 	return 1;
439061d06d6bSBaptiste Daroussin }
4391