1241675Suqs/*	$Id: roff.c,v 1.172 2011/10/24 21:41:45 schwarze Exp $ */
2241675Suqs/*
3241675Suqs * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4241675Suqs * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
5241675Suqs *
6241675Suqs * Permission to use, copy, modify, and distribute this software for any
7241675Suqs * purpose with or without fee is hereby granted, provided that the above
8241675Suqs * copyright notice and this permission notice appear in all copies.
9241675Suqs *
10241675Suqs * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11241675Suqs * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12241675Suqs * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13241675Suqs * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14241675Suqs * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15241675Suqs * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16241675Suqs * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17241675Suqs */
18241675Suqs#ifdef HAVE_CONFIG_H
19241675Suqs#include "config.h"
20241675Suqs#endif
21241675Suqs
22241675Suqs#include <assert.h>
23241675Suqs#include <ctype.h>
24241675Suqs#include <stdlib.h>
25241675Suqs#include <string.h>
26241675Suqs
27241675Suqs#include "mandoc.h"
28241675Suqs#include "libroff.h"
29241675Suqs#include "libmandoc.h"
30241675Suqs
31241675Suqs/* Maximum number of nested if-else conditionals. */
32241675Suqs#define	RSTACK_MAX	128
33241675Suqs
34241675Suqs/* Maximum number of string expansions per line, to break infinite loops. */
35241675Suqs#define	EXPAND_LIMIT	1000
36241675Suqs
37241675Suqsenum	rofft {
38241675Suqs	ROFF_ad,
39241675Suqs	ROFF_am,
40241675Suqs	ROFF_ami,
41241675Suqs	ROFF_am1,
42241675Suqs	ROFF_de,
43241675Suqs	ROFF_dei,
44241675Suqs	ROFF_de1,
45241675Suqs	ROFF_ds,
46241675Suqs	ROFF_el,
47241675Suqs	ROFF_hy,
48241675Suqs	ROFF_ie,
49241675Suqs	ROFF_if,
50241675Suqs	ROFF_ig,
51241675Suqs	ROFF_it,
52241675Suqs	ROFF_ne,
53241675Suqs	ROFF_nh,
54241675Suqs	ROFF_nr,
55241675Suqs	ROFF_ns,
56241675Suqs	ROFF_ps,
57241675Suqs	ROFF_rm,
58241675Suqs	ROFF_so,
59241675Suqs	ROFF_ta,
60241675Suqs	ROFF_tr,
61241675Suqs	ROFF_TS,
62241675Suqs	ROFF_TE,
63241675Suqs	ROFF_T_,
64241675Suqs	ROFF_EQ,
65241675Suqs	ROFF_EN,
66241675Suqs	ROFF_cblock,
67241675Suqs	ROFF_ccond,
68241675Suqs	ROFF_USERDEF,
69241675Suqs	ROFF_MAX
70241675Suqs};
71241675Suqs
72241675Suqsenum	roffrule {
73241675Suqs	ROFFRULE_ALLOW,
74241675Suqs	ROFFRULE_DENY
75241675Suqs};
76241675Suqs
77241675Suqs/*
78241675Suqs * A single register entity.  If "set" is zero, the value of the
79241675Suqs * register should be the default one, which is per-register.
80241675Suqs * Registers are assumed to be unsigned ints for now.
81241675Suqs */
82241675Suqsstruct	reg {
83241675Suqs	int		 set; /* whether set or not */
84241675Suqs	unsigned int	 u; /* unsigned integer */
85241675Suqs};
86241675Suqs
87241675Suqs/*
88241675Suqs * An incredibly-simple string buffer.
89241675Suqs */
90241675Suqsstruct	roffstr {
91241675Suqs	char		*p; /* nil-terminated buffer */
92241675Suqs	size_t		 sz; /* saved strlen(p) */
93241675Suqs};
94241675Suqs
95241675Suqs/*
96241675Suqs * A key-value roffstr pair as part of a singly-linked list.
97241675Suqs */
98241675Suqsstruct	roffkv {
99241675Suqs	struct roffstr	 key;
100241675Suqs	struct roffstr	 val;
101241675Suqs	struct roffkv	*next; /* next in list */
102241675Suqs};
103241675Suqs
104241675Suqsstruct	roff {
105241675Suqs	struct mparse	*parse; /* parse point */
106241675Suqs	struct roffnode	*last; /* leaf of stack */
107241675Suqs	enum roffrule	 rstack[RSTACK_MAX]; /* stack of !`ie' rules */
108241675Suqs	int		 rstackpos; /* position in rstack */
109241675Suqs	struct reg	 regs[REG__MAX];
110241675Suqs	struct roffkv	*strtab; /* user-defined strings & macros */
111241675Suqs	struct roffkv	*xmbtab; /* multi-byte trans table (`tr') */
112241675Suqs	struct roffstr	*xtab; /* single-byte trans table (`tr') */
113241675Suqs	const char	*current_string; /* value of last called user macro */
114241675Suqs	struct tbl_node	*first_tbl; /* first table parsed */
115241675Suqs	struct tbl_node	*last_tbl; /* last table parsed */
116241675Suqs	struct tbl_node	*tbl; /* current table being parsed */
117241675Suqs	struct eqn_node	*last_eqn; /* last equation parsed */
118241675Suqs	struct eqn_node	*first_eqn; /* first equation parsed */
119241675Suqs	struct eqn_node	*eqn; /* current equation being parsed */
120241675Suqs};
121241675Suqs
122241675Suqsstruct	roffnode {
123241675Suqs	enum rofft	 tok; /* type of node */
124241675Suqs	struct roffnode	*parent; /* up one in stack */
125241675Suqs	int		 line; /* parse line */
126241675Suqs	int		 col; /* parse col */
127241675Suqs	char		*name; /* node name, e.g. macro name */
128241675Suqs	char		*end; /* end-rules: custom token */
129241675Suqs	int		 endspan; /* end-rules: next-line or infty */
130241675Suqs	enum roffrule	 rule; /* current evaluation rule */
131241675Suqs};
132241675Suqs
133241675Suqs#define	ROFF_ARGS	 struct roff *r, /* parse ctx */ \
134241675Suqs			 enum rofft tok, /* tok of macro */ \
135241675Suqs		 	 char **bufp, /* input buffer */ \
136241675Suqs			 size_t *szp, /* size of input buffer */ \
137241675Suqs			 int ln, /* parse line */ \
138241675Suqs			 int ppos, /* original pos in buffer */ \
139241675Suqs			 int pos, /* current pos in buffer */ \
140241675Suqs			 int *offs /* reset offset of buffer data */
141241675Suqs
142241675Suqstypedef	enum rofferr (*roffproc)(ROFF_ARGS);
143241675Suqs
144241675Suqsstruct	roffmac {
145241675Suqs	const char	*name; /* macro name */
146241675Suqs	roffproc	 proc; /* process new macro */
147241675Suqs	roffproc	 text; /* process as child text of macro */
148241675Suqs	roffproc	 sub; /* process as child of macro */
149241675Suqs	int		 flags;
150241675Suqs#define	ROFFMAC_STRUCT	(1 << 0) /* always interpret */
151241675Suqs	struct roffmac	*next;
152241675Suqs};
153241675Suqs
154241675Suqsstruct	predef {
155241675Suqs	const char	*name; /* predefined input name */
156241675Suqs	const char	*str; /* replacement symbol */
157241675Suqs};
158241675Suqs
159241675Suqs#define	PREDEF(__name, __str) \
160241675Suqs	{ (__name), (__str) },
161241675Suqs
162241675Suqsstatic	enum rofft	 roffhash_find(const char *, size_t);
163241675Suqsstatic	void		 roffhash_init(void);
164241675Suqsstatic	void		 roffnode_cleanscope(struct roff *);
165241675Suqsstatic	void		 roffnode_pop(struct roff *);
166241675Suqsstatic	void		 roffnode_push(struct roff *, enum rofft,
167241675Suqs				const char *, int, int);
168241675Suqsstatic	enum rofferr	 roff_block(ROFF_ARGS);
169241675Suqsstatic	enum rofferr	 roff_block_text(ROFF_ARGS);
170241675Suqsstatic	enum rofferr	 roff_block_sub(ROFF_ARGS);
171241675Suqsstatic	enum rofferr	 roff_cblock(ROFF_ARGS);
172241675Suqsstatic	enum rofferr	 roff_ccond(ROFF_ARGS);
173241675Suqsstatic	enum rofferr	 roff_cond(ROFF_ARGS);
174241675Suqsstatic	enum rofferr	 roff_cond_text(ROFF_ARGS);
175241675Suqsstatic	enum rofferr	 roff_cond_sub(ROFF_ARGS);
176241675Suqsstatic	enum rofferr	 roff_ds(ROFF_ARGS);
177241675Suqsstatic	enum roffrule	 roff_evalcond(const char *, int *);
178241675Suqsstatic	void		 roff_free1(struct roff *);
179241675Suqsstatic	void		 roff_freestr(struct roffkv *);
180241675Suqsstatic	char		*roff_getname(struct roff *, char **, int, int);
181241675Suqsstatic	const char	*roff_getstrn(const struct roff *,
182241675Suqs				const char *, size_t);
183241675Suqsstatic	enum rofferr	 roff_line_ignore(ROFF_ARGS);
184241675Suqsstatic	enum rofferr	 roff_nr(ROFF_ARGS);
185241675Suqsstatic	void		 roff_openeqn(struct roff *, const char *,
186241675Suqs				int, int, const char *);
187241675Suqsstatic	enum rofft	 roff_parse(struct roff *, const char *, int *);
188241675Suqsstatic	enum rofferr	 roff_parsetext(char *);
189241675Suqsstatic	enum rofferr	 roff_res(struct roff *,
190241675Suqs				char **, size_t *, int, int);
191241675Suqsstatic	enum rofferr	 roff_rm(ROFF_ARGS);
192241675Suqsstatic	void		 roff_setstr(struct roff *,
193241675Suqs				const char *, const char *, int);
194241675Suqsstatic	void		 roff_setstrn(struct roffkv **, const char *,
195241675Suqs				size_t, const char *, size_t, int);
196241675Suqsstatic	enum rofferr	 roff_so(ROFF_ARGS);
197241675Suqsstatic	enum rofferr	 roff_tr(ROFF_ARGS);
198241675Suqsstatic	enum rofferr	 roff_TE(ROFF_ARGS);
199241675Suqsstatic	enum rofferr	 roff_TS(ROFF_ARGS);
200241675Suqsstatic	enum rofferr	 roff_EQ(ROFF_ARGS);
201241675Suqsstatic	enum rofferr	 roff_EN(ROFF_ARGS);
202241675Suqsstatic	enum rofferr	 roff_T_(ROFF_ARGS);
203241675Suqsstatic	enum rofferr	 roff_userdef(ROFF_ARGS);
204241675Suqs
205241675Suqs/* See roffhash_find() */
206241675Suqs
207241675Suqs#define	ASCII_HI	 126
208241675Suqs#define	ASCII_LO	 33
209241675Suqs#define	HASHWIDTH	(ASCII_HI - ASCII_LO + 1)
210241675Suqs
211241675Suqsstatic	struct roffmac	*hash[HASHWIDTH];
212241675Suqs
213241675Suqsstatic	struct roffmac	 roffs[ROFF_MAX] = {
214241675Suqs	{ "ad", roff_line_ignore, NULL, NULL, 0, NULL },
215241675Suqs	{ "am", roff_block, roff_block_text, roff_block_sub, 0, NULL },
216241675Suqs	{ "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL },
217241675Suqs	{ "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
218241675Suqs	{ "de", roff_block, roff_block_text, roff_block_sub, 0, NULL },
219241675Suqs	{ "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL },
220241675Suqs	{ "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
221241675Suqs	{ "ds", roff_ds, NULL, NULL, 0, NULL },
222241675Suqs	{ "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
223241675Suqs	{ "hy", roff_line_ignore, NULL, NULL, 0, NULL },
224241675Suqs	{ "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
225241675Suqs	{ "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
226241675Suqs	{ "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL },
227241675Suqs	{ "it", roff_line_ignore, NULL, NULL, 0, NULL },
228241675Suqs	{ "ne", roff_line_ignore, NULL, NULL, 0, NULL },
229241675Suqs	{ "nh", roff_line_ignore, NULL, NULL, 0, NULL },
230241675Suqs	{ "nr", roff_nr, NULL, NULL, 0, NULL },
231241675Suqs	{ "ns", roff_line_ignore, NULL, NULL, 0, NULL },
232241675Suqs	{ "ps", roff_line_ignore, NULL, NULL, 0, NULL },
233241675Suqs	{ "rm", roff_rm, NULL, NULL, 0, NULL },
234241675Suqs	{ "so", roff_so, NULL, NULL, 0, NULL },
235241675Suqs	{ "ta", roff_line_ignore, NULL, NULL, 0, NULL },
236241675Suqs	{ "tr", roff_tr, NULL, NULL, 0, NULL },
237241675Suqs	{ "TS", roff_TS, NULL, NULL, 0, NULL },
238241675Suqs	{ "TE", roff_TE, NULL, NULL, 0, NULL },
239241675Suqs	{ "T&", roff_T_, NULL, NULL, 0, NULL },
240241675Suqs	{ "EQ", roff_EQ, NULL, NULL, 0, NULL },
241241675Suqs	{ "EN", roff_EN, NULL, NULL, 0, NULL },
242241675Suqs	{ ".", roff_cblock, NULL, NULL, 0, NULL },
243241675Suqs	{ "\\}", roff_ccond, NULL, NULL, 0, NULL },
244241675Suqs	{ NULL, roff_userdef, NULL, NULL, 0, NULL },
245241675Suqs};
246241675Suqs
247241675Suqs/* Array of injected predefined strings. */
248241675Suqs#define	PREDEFS_MAX	 38
249241675Suqsstatic	const struct predef predefs[PREDEFS_MAX] = {
250241675Suqs#include "predefs.in"
251241675Suqs};
252241675Suqs
253241675Suqs/* See roffhash_find() */
254241675Suqs#define	ROFF_HASH(p)	(p[0] - ASCII_LO)
255241675Suqs
256241675Suqsstatic void
257241675Suqsroffhash_init(void)
258241675Suqs{
259241675Suqs	struct roffmac	 *n;
260241675Suqs	int		  buc, i;
261241675Suqs
262241675Suqs	for (i = 0; i < (int)ROFF_USERDEF; i++) {
263241675Suqs		assert(roffs[i].name[0] >= ASCII_LO);
264241675Suqs		assert(roffs[i].name[0] <= ASCII_HI);
265241675Suqs
266241675Suqs		buc = ROFF_HASH(roffs[i].name);
267241675Suqs
268241675Suqs		if (NULL != (n = hash[buc])) {
269241675Suqs			for ( ; n->next; n = n->next)
270241675Suqs				/* Do nothing. */ ;
271241675Suqs			n->next = &roffs[i];
272241675Suqs		} else
273241675Suqs			hash[buc] = &roffs[i];
274241675Suqs	}
275241675Suqs}
276241675Suqs
277241675Suqs/*
278241675Suqs * Look up a roff token by its name.  Returns ROFF_MAX if no macro by
279241675Suqs * the nil-terminated string name could be found.
280241675Suqs */
281241675Suqsstatic enum rofft
282241675Suqsroffhash_find(const char *p, size_t s)
283241675Suqs{
284241675Suqs	int		 buc;
285241675Suqs	struct roffmac	*n;
286241675Suqs
287241675Suqs	/*
288241675Suqs	 * libroff has an extremely simple hashtable, for the time
289241675Suqs	 * being, which simply keys on the first character, which must
290241675Suqs	 * be printable, then walks a chain.  It works well enough until
291241675Suqs	 * optimised.
292241675Suqs	 */
293241675Suqs
294241675Suqs	if (p[0] < ASCII_LO || p[0] > ASCII_HI)
295241675Suqs		return(ROFF_MAX);
296241675Suqs
297241675Suqs	buc = ROFF_HASH(p);
298241675Suqs
299241675Suqs	if (NULL == (n = hash[buc]))
300241675Suqs		return(ROFF_MAX);
301241675Suqs	for ( ; n; n = n->next)
302241675Suqs		if (0 == strncmp(n->name, p, s) && '\0' == n->name[(int)s])
303241675Suqs			return((enum rofft)(n - roffs));
304241675Suqs
305241675Suqs	return(ROFF_MAX);
306241675Suqs}
307241675Suqs
308241675Suqs
309241675Suqs/*
310241675Suqs * Pop the current node off of the stack of roff instructions currently
311241675Suqs * pending.
312241675Suqs */
313241675Suqsstatic void
314241675Suqsroffnode_pop(struct roff *r)
315241675Suqs{
316241675Suqs	struct roffnode	*p;
317241675Suqs
318241675Suqs	assert(r->last);
319241675Suqs	p = r->last;
320241675Suqs
321241675Suqs	r->last = r->last->parent;
322241675Suqs	free(p->name);
323241675Suqs	free(p->end);
324241675Suqs	free(p);
325241675Suqs}
326241675Suqs
327241675Suqs
328241675Suqs/*
329241675Suqs * Push a roff node onto the instruction stack.  This must later be
330241675Suqs * removed with roffnode_pop().
331241675Suqs */
332241675Suqsstatic void
333241675Suqsroffnode_push(struct roff *r, enum rofft tok, const char *name,
334241675Suqs		int line, int col)
335241675Suqs{
336241675Suqs	struct roffnode	*p;
337241675Suqs
338241675Suqs	p = mandoc_calloc(1, sizeof(struct roffnode));
339241675Suqs	p->tok = tok;
340241675Suqs	if (name)
341241675Suqs		p->name = mandoc_strdup(name);
342241675Suqs	p->parent = r->last;
343241675Suqs	p->line = line;
344241675Suqs	p->col = col;
345241675Suqs	p->rule = p->parent ? p->parent->rule : ROFFRULE_DENY;
346241675Suqs
347241675Suqs	r->last = p;
348241675Suqs}
349241675Suqs
350241675Suqs
351241675Suqsstatic void
352241675Suqsroff_free1(struct roff *r)
353241675Suqs{
354241675Suqs	struct tbl_node	*t;
355241675Suqs	struct eqn_node	*e;
356241675Suqs	int		 i;
357241675Suqs
358241675Suqs	while (NULL != (t = r->first_tbl)) {
359241675Suqs		r->first_tbl = t->next;
360241675Suqs		tbl_free(t);
361241675Suqs	}
362241675Suqs
363241675Suqs	r->first_tbl = r->last_tbl = r->tbl = NULL;
364241675Suqs
365241675Suqs	while (NULL != (e = r->first_eqn)) {
366241675Suqs		r->first_eqn = e->next;
367241675Suqs		eqn_free(e);
368241675Suqs	}
369241675Suqs
370241675Suqs	r->first_eqn = r->last_eqn = r->eqn = NULL;
371241675Suqs
372241675Suqs	while (r->last)
373241675Suqs		roffnode_pop(r);
374241675Suqs
375241675Suqs	roff_freestr(r->strtab);
376241675Suqs	roff_freestr(r->xmbtab);
377241675Suqs
378241675Suqs	r->strtab = r->xmbtab = NULL;
379241675Suqs
380241675Suqs	if (r->xtab)
381241675Suqs		for (i = 0; i < 128; i++)
382241675Suqs			free(r->xtab[i].p);
383241675Suqs
384241675Suqs	free(r->xtab);
385241675Suqs	r->xtab = NULL;
386241675Suqs}
387241675Suqs
388241675Suqsvoid
389241675Suqsroff_reset(struct roff *r)
390241675Suqs{
391241675Suqs	int		 i;
392241675Suqs
393241675Suqs	roff_free1(r);
394241675Suqs
395241675Suqs	memset(&r->regs, 0, sizeof(struct reg) * REG__MAX);
396241675Suqs
397241675Suqs	for (i = 0; i < PREDEFS_MAX; i++)
398241675Suqs		roff_setstr(r, predefs[i].name, predefs[i].str, 0);
399241675Suqs}
400241675Suqs
401241675Suqs
402241675Suqsvoid
403241675Suqsroff_free(struct roff *r)
404241675Suqs{
405241675Suqs
406241675Suqs	roff_free1(r);
407241675Suqs	free(r);
408241675Suqs}
409241675Suqs
410241675Suqs
411241675Suqsstruct roff *
412241675Suqsroff_alloc(struct mparse *parse)
413241675Suqs{
414241675Suqs	struct roff	*r;
415241675Suqs	int		 i;
416241675Suqs
417241675Suqs	r = mandoc_calloc(1, sizeof(struct roff));
418241675Suqs	r->parse = parse;
419241675Suqs	r->rstackpos = -1;
420241675Suqs
421241675Suqs	roffhash_init();
422241675Suqs
423241675Suqs	for (i = 0; i < PREDEFS_MAX; i++)
424241675Suqs		roff_setstr(r, predefs[i].name, predefs[i].str, 0);
425241675Suqs
426241675Suqs	return(r);
427241675Suqs}
428241675Suqs
429241675Suqs/*
430241675Suqs * Pre-filter each and every line for reserved words (one beginning with
431241675Suqs * `\*', e.g., `\*(ab').  These must be handled before the actual line
432241675Suqs * is processed.
433241675Suqs * This also checks the syntax of regular escapes.
434241675Suqs */
435241675Suqsstatic enum rofferr
436241675Suqsroff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos)
437241675Suqs{
438241675Suqs	enum mandoc_esc	 esc;
439241675Suqs	const char	*stesc;	/* start of an escape sequence ('\\') */
440241675Suqs	const char	*stnam;	/* start of the name, after "[(*" */
441241675Suqs	const char	*cp;	/* end of the name, e.g. before ']' */
442241675Suqs	const char	*res;	/* the string to be substituted */
443241675Suqs	int		 i, maxl, expand_count;
444241675Suqs	size_t		 nsz;
445241675Suqs	char		*n;
446241675Suqs
447241675Suqs	expand_count = 0;
448241675Suqs
449241675Suqsagain:
450241675Suqs	cp = *bufp + pos;
451241675Suqs	while (NULL != (cp = strchr(cp, '\\'))) {
452241675Suqs		stesc = cp++;
453241675Suqs
454241675Suqs		/*
455241675Suqs		 * The second character must be an asterisk.
456241675Suqs		 * If it isn't, skip it anyway:  It is escaped,
457241675Suqs		 * so it can't start another escape sequence.
458241675Suqs		 */
459241675Suqs
460241675Suqs		if ('\0' == *cp)
461241675Suqs			return(ROFF_CONT);
462241675Suqs
463241675Suqs		if ('*' != *cp) {
464241675Suqs			res = cp;
465241675Suqs			esc = mandoc_escape(&cp, NULL, NULL);
466241675Suqs			if (ESCAPE_ERROR != esc)
467241675Suqs				continue;
468241675Suqs			cp = res;
469241675Suqs			mandoc_msg
470241675Suqs				(MANDOCERR_BADESCAPE, r->parse,
471241675Suqs				 ln, (int)(stesc - *bufp), NULL);
472241675Suqs			return(ROFF_CONT);
473241675Suqs		}
474241675Suqs
475241675Suqs		cp++;
476241675Suqs
477241675Suqs		/*
478241675Suqs		 * The third character decides the length
479241675Suqs		 * of the name of the string.
480241675Suqs		 * Save a pointer to the name.
481241675Suqs		 */
482241675Suqs
483241675Suqs		switch (*cp) {
484241675Suqs		case ('\0'):
485241675Suqs			return(ROFF_CONT);
486241675Suqs		case ('('):
487241675Suqs			cp++;
488241675Suqs			maxl = 2;
489241675Suqs			break;
490241675Suqs		case ('['):
491241675Suqs			cp++;
492241675Suqs			maxl = 0;
493241675Suqs			break;
494241675Suqs		default:
495241675Suqs			maxl = 1;
496241675Suqs			break;
497241675Suqs		}
498241675Suqs		stnam = cp;
499241675Suqs
500241675Suqs		/* Advance to the end of the name. */
501241675Suqs
502241675Suqs		for (i = 0; 0 == maxl || i < maxl; i++, cp++) {
503241675Suqs			if ('\0' == *cp) {
504241675Suqs				mandoc_msg
505241675Suqs					(MANDOCERR_BADESCAPE,
506241675Suqs					 r->parse, ln,
507241675Suqs					 (int)(stesc - *bufp), NULL);
508241675Suqs				return(ROFF_CONT);
509241675Suqs			}
510241675Suqs			if (0 == maxl && ']' == *cp)
511241675Suqs				break;
512241675Suqs		}
513241675Suqs
514241675Suqs		/*
515241675Suqs		 * Retrieve the replacement string; if it is
516241675Suqs		 * undefined, resume searching for escapes.
517241675Suqs		 */
518241675Suqs
519241675Suqs		res = roff_getstrn(r, stnam, (size_t)i);
520241675Suqs
521241675Suqs		if (NULL == res) {
522241675Suqs			mandoc_msg
523241675Suqs				(MANDOCERR_BADESCAPE, r->parse,
524241675Suqs				 ln, (int)(stesc - *bufp), NULL);
525241675Suqs			res = "";
526241675Suqs		}
527241675Suqs
528241675Suqs		/* Replace the escape sequence by the string. */
529241675Suqs
530241675Suqs		pos = stesc - *bufp;
531241675Suqs
532241675Suqs		nsz = *szp + strlen(res) + 1;
533241675Suqs		n = mandoc_malloc(nsz);
534241675Suqs
535241675Suqs		strlcpy(n, *bufp, (size_t)(stesc - *bufp + 1));
536241675Suqs		strlcat(n, res, nsz);
537241675Suqs		strlcat(n, cp + (maxl ? 0 : 1), nsz);
538241675Suqs
539241675Suqs		free(*bufp);
540241675Suqs
541241675Suqs		*bufp = n;
542241675Suqs		*szp = nsz;
543241675Suqs
544241675Suqs		if (EXPAND_LIMIT >= ++expand_count)
545241675Suqs			goto again;
546241675Suqs
547241675Suqs		/* Just leave the string unexpanded. */
548241675Suqs		mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, ln, pos, NULL);
549241675Suqs		return(ROFF_IGN);
550241675Suqs	}
551241675Suqs	return(ROFF_CONT);
552241675Suqs}
553241675Suqs
554241675Suqs/*
555241675Suqs * Process text streams: convert all breakable hyphens into ASCII_HYPH.
556241675Suqs */
557241675Suqsstatic enum rofferr
558241675Suqsroff_parsetext(char *p)
559241675Suqs{
560241675Suqs	size_t		 sz;
561241675Suqs	const char	*start;
562241675Suqs	enum mandoc_esc	 esc;
563241675Suqs
564241675Suqs	start = p;
565241675Suqs
566241675Suqs	while ('\0' != *p) {
567241675Suqs		sz = strcspn(p, "-\\");
568241675Suqs		p += sz;
569241675Suqs
570241675Suqs		if ('\0' == *p)
571241675Suqs			break;
572241675Suqs
573241675Suqs		if ('\\' == *p) {
574241675Suqs			/* Skip over escapes. */
575241675Suqs			p++;
576241675Suqs			esc = mandoc_escape
577241675Suqs				((const char **)&p, NULL, NULL);
578241675Suqs			if (ESCAPE_ERROR == esc)
579241675Suqs				break;
580241675Suqs			continue;
581241675Suqs		} else if (p == start) {
582241675Suqs			p++;
583241675Suqs			continue;
584241675Suqs		}
585241675Suqs
586241675Suqs		if (isalpha((unsigned char)p[-1]) &&
587241675Suqs		    isalpha((unsigned char)p[1]))
588241675Suqs			*p = ASCII_HYPH;
589241675Suqs		p++;
590241675Suqs	}
591241675Suqs
592241675Suqs	return(ROFF_CONT);
593241675Suqs}
594241675Suqs
595241675Suqsenum rofferr
596241675Suqsroff_parseln(struct roff *r, int ln, char **bufp,
597241675Suqs		size_t *szp, int pos, int *offs)
598241675Suqs{
599241675Suqs	enum rofft	 t;
600241675Suqs	enum rofferr	 e;
601241675Suqs	int		 ppos, ctl;
602241675Suqs
603241675Suqs	/*
604241675Suqs	 * Run the reserved-word filter only if we have some reserved
605241675Suqs	 * words to fill in.
606241675Suqs	 */
607241675Suqs
608241675Suqs	e = roff_res(r, bufp, szp, ln, pos);
609241675Suqs	if (ROFF_IGN == e)
610241675Suqs		return(e);
611241675Suqs	assert(ROFF_CONT == e);
612241675Suqs
613241675Suqs	ppos = pos;
614241675Suqs	ctl = mandoc_getcontrol(*bufp, &pos);
615241675Suqs
616241675Suqs	/*
617241675Suqs	 * First, if a scope is open and we're not a macro, pass the
618241675Suqs	 * text through the macro's filter.  If a scope isn't open and
619241675Suqs	 * we're not a macro, just let it through.
620241675Suqs	 * Finally, if there's an equation scope open, divert it into it
621241675Suqs	 * no matter our state.
622241675Suqs	 */
623241675Suqs
624241675Suqs	if (r->last && ! ctl) {
625241675Suqs		t = r->last->tok;
626241675Suqs		assert(roffs[t].text);
627241675Suqs		e = (*roffs[t].text)
628241675Suqs			(r, t, bufp, szp, ln, pos, pos, offs);
629241675Suqs		assert(ROFF_IGN == e || ROFF_CONT == e);
630241675Suqs		if (ROFF_CONT != e)
631241675Suqs			return(e);
632241675Suqs		if (r->eqn)
633241675Suqs			return(eqn_read(&r->eqn, ln, *bufp, pos, offs));
634241675Suqs		if (r->tbl)
635241675Suqs			return(tbl_read(r->tbl, ln, *bufp, pos));
636241675Suqs		return(roff_parsetext(*bufp + pos));
637241675Suqs	} else if ( ! ctl) {
638241675Suqs		if (r->eqn)
639241675Suqs			return(eqn_read(&r->eqn, ln, *bufp, pos, offs));
640241675Suqs		if (r->tbl)
641241675Suqs			return(tbl_read(r->tbl, ln, *bufp, pos));
642241675Suqs		return(roff_parsetext(*bufp + pos));
643241675Suqs	} else if (r->eqn)
644241675Suqs		return(eqn_read(&r->eqn, ln, *bufp, ppos, offs));
645241675Suqs
646241675Suqs	/*
647241675Suqs	 * If a scope is open, go to the child handler for that macro,
648241675Suqs	 * as it may want to preprocess before doing anything with it.
649241675Suqs	 * Don't do so if an equation is open.
650241675Suqs	 */
651241675Suqs
652241675Suqs	if (r->last) {
653241675Suqs		t = r->last->tok;
654241675Suqs		assert(roffs[t].sub);
655241675Suqs		return((*roffs[t].sub)
656241675Suqs				(r, t, bufp, szp,
657241675Suqs				 ln, ppos, pos, offs));
658241675Suqs	}
659241675Suqs
660241675Suqs	/*
661241675Suqs	 * Lastly, as we've no scope open, try to look up and execute
662241675Suqs	 * the new macro.  If no macro is found, simply return and let
663241675Suqs	 * the compilers handle it.
664241675Suqs	 */
665241675Suqs
666241675Suqs	if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos)))
667241675Suqs		return(ROFF_CONT);
668241675Suqs
669241675Suqs	assert(roffs[t].proc);
670241675Suqs	return((*roffs[t].proc)
671241675Suqs			(r, t, bufp, szp,
672241675Suqs			 ln, ppos, pos, offs));
673241675Suqs}
674241675Suqs
675241675Suqs
676241675Suqsvoid
677241675Suqsroff_endparse(struct roff *r)
678241675Suqs{
679241675Suqs
680241675Suqs	if (r->last)
681241675Suqs		mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse,
682241675Suqs				r->last->line, r->last->col, NULL);
683241675Suqs
684241675Suqs	if (r->eqn) {
685241675Suqs		mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse,
686241675Suqs				r->eqn->eqn.ln, r->eqn->eqn.pos, NULL);
687241675Suqs		eqn_end(&r->eqn);
688241675Suqs	}
689241675Suqs
690241675Suqs	if (r->tbl) {
691241675Suqs		mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse,
692241675Suqs				r->tbl->line, r->tbl->pos, NULL);
693241675Suqs		tbl_end(&r->tbl);
694241675Suqs	}
695241675Suqs}
696241675Suqs
697241675Suqs/*
698241675Suqs * Parse a roff node's type from the input buffer.  This must be in the
699241675Suqs * form of ".foo xxx" in the usual way.
700241675Suqs */
701241675Suqsstatic enum rofft
702241675Suqsroff_parse(struct roff *r, const char *buf, int *pos)
703241675Suqs{
704241675Suqs	const char	*mac;
705241675Suqs	size_t		 maclen;
706241675Suqs	enum rofft	 t;
707241675Suqs
708241675Suqs	if ('\0' == buf[*pos] || '"' == buf[*pos] ||
709241675Suqs			'\t' == buf[*pos] || ' ' == buf[*pos])
710241675Suqs		return(ROFF_MAX);
711241675Suqs
712241675Suqs	/*
713241675Suqs	 * We stop the macro parse at an escape, tab, space, or nil.
714241675Suqs	 * However, `\}' is also a valid macro, so make sure we don't
715241675Suqs	 * clobber it by seeing the `\' as the end of token.
716241675Suqs	 */
717241675Suqs
718241675Suqs	mac = buf + *pos;
719241675Suqs	maclen = strcspn(mac + 1, " \\\t\0") + 1;
720241675Suqs
721241675Suqs	t = (r->current_string = roff_getstrn(r, mac, maclen))
722241675Suqs	    ? ROFF_USERDEF : roffhash_find(mac, maclen);
723241675Suqs
724241675Suqs	*pos += (int)maclen;
725241675Suqs
726241675Suqs	while (buf[*pos] && ' ' == buf[*pos])
727241675Suqs		(*pos)++;
728241675Suqs
729241675Suqs	return(t);
730241675Suqs}
731241675Suqs
732241675Suqs/* ARGSUSED */
733241675Suqsstatic enum rofferr
734241675Suqsroff_cblock(ROFF_ARGS)
735241675Suqs{
736241675Suqs
737241675Suqs	/*
738241675Suqs	 * A block-close `..' should only be invoked as a child of an
739241675Suqs	 * ignore macro, otherwise raise a warning and just ignore it.
740241675Suqs	 */
741241675Suqs
742241675Suqs	if (NULL == r->last) {
743241675Suqs		mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
744241675Suqs		return(ROFF_IGN);
745241675Suqs	}
746241675Suqs
747241675Suqs	switch (r->last->tok) {
748241675Suqs	case (ROFF_am):
749241675Suqs		/* FALLTHROUGH */
750241675Suqs	case (ROFF_ami):
751241675Suqs		/* FALLTHROUGH */
752241675Suqs	case (ROFF_am1):
753241675Suqs		/* FALLTHROUGH */
754241675Suqs	case (ROFF_de):
755241675Suqs		/* ROFF_de1 is remapped to ROFF_de in roff_block(). */
756241675Suqs		/* FALLTHROUGH */
757241675Suqs	case (ROFF_dei):
758241675Suqs		/* FALLTHROUGH */
759241675Suqs	case (ROFF_ig):
760241675Suqs		break;
761241675Suqs	default:
762241675Suqs		mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
763241675Suqs		return(ROFF_IGN);
764241675Suqs	}
765241675Suqs
766241675Suqs	if ((*bufp)[pos])
767241675Suqs		mandoc_msg(MANDOCERR_ARGSLOST, r->parse, ln, pos, NULL);
768241675Suqs
769241675Suqs	roffnode_pop(r);
770241675Suqs	roffnode_cleanscope(r);
771241675Suqs	return(ROFF_IGN);
772241675Suqs
773241675Suqs}
774241675Suqs
775241675Suqs
776241675Suqsstatic void
777241675Suqsroffnode_cleanscope(struct roff *r)
778241675Suqs{
779241675Suqs
780241675Suqs	while (r->last) {
781241675Suqs		if (--r->last->endspan < 0)
782241675Suqs			break;
783241675Suqs		roffnode_pop(r);
784241675Suqs	}
785241675Suqs}
786241675Suqs
787241675Suqs
788241675Suqs/* ARGSUSED */
789241675Suqsstatic enum rofferr
790241675Suqsroff_ccond(ROFF_ARGS)
791241675Suqs{
792241675Suqs
793241675Suqs	if (NULL == r->last) {
794241675Suqs		mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
795241675Suqs		return(ROFF_IGN);
796241675Suqs	}
797241675Suqs
798241675Suqs	switch (r->last->tok) {
799241675Suqs	case (ROFF_el):
800241675Suqs		/* FALLTHROUGH */
801241675Suqs	case (ROFF_ie):
802241675Suqs		/* FALLTHROUGH */
803241675Suqs	case (ROFF_if):
804241675Suqs		break;
805241675Suqs	default:
806241675Suqs		mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
807241675Suqs		return(ROFF_IGN);
808241675Suqs	}
809241675Suqs
810241675Suqs	if (r->last->endspan > -1) {
811241675Suqs		mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
812241675Suqs		return(ROFF_IGN);
813241675Suqs	}
814241675Suqs
815241675Suqs	if ((*bufp)[pos])
816241675Suqs		mandoc_msg(MANDOCERR_ARGSLOST, r->parse, ln, pos, NULL);
817241675Suqs
818241675Suqs	roffnode_pop(r);
819241675Suqs	roffnode_cleanscope(r);
820241675Suqs	return(ROFF_IGN);
821241675Suqs}
822241675Suqs
823241675Suqs
824241675Suqs/* ARGSUSED */
825241675Suqsstatic enum rofferr
826241675Suqsroff_block(ROFF_ARGS)
827241675Suqs{
828241675Suqs	int		sv;
829241675Suqs	size_t		sz;
830241675Suqs	char		*name;
831241675Suqs
832241675Suqs	name = NULL;
833241675Suqs
834241675Suqs	if (ROFF_ig != tok) {
835241675Suqs		if ('\0' == (*bufp)[pos]) {
836241675Suqs			mandoc_msg(MANDOCERR_NOARGS, r->parse, ln, ppos, NULL);
837241675Suqs			return(ROFF_IGN);
838241675Suqs		}
839241675Suqs
840241675Suqs		/*
841241675Suqs		 * Re-write `de1', since we don't really care about
842241675Suqs		 * groff's strange compatibility mode, into `de'.
843241675Suqs		 */
844241675Suqs
845241675Suqs		if (ROFF_de1 == tok)
846241675Suqs			tok = ROFF_de;
847241675Suqs		if (ROFF_de == tok)
848241675Suqs			name = *bufp + pos;
849241675Suqs		else
850241675Suqs			mandoc_msg(MANDOCERR_REQUEST, r->parse, ln, ppos,
851241675Suqs			    roffs[tok].name);
852241675Suqs
853241675Suqs		while ((*bufp)[pos] && ! isspace((unsigned char)(*bufp)[pos]))
854241675Suqs			pos++;
855241675Suqs
856241675Suqs		while (isspace((unsigned char)(*bufp)[pos]))
857241675Suqs			(*bufp)[pos++] = '\0';
858241675Suqs	}
859241675Suqs
860241675Suqs	roffnode_push(r, tok, name, ln, ppos);
861241675Suqs
862241675Suqs	/*
863241675Suqs	 * At the beginning of a `de' macro, clear the existing string
864241675Suqs	 * with the same name, if there is one.  New content will be
865241675Suqs	 * added from roff_block_text() in multiline mode.
866241675Suqs	 */
867241675Suqs
868241675Suqs	if (ROFF_de == tok)
869241675Suqs		roff_setstr(r, name, "", 0);
870241675Suqs
871241675Suqs	if ('\0' == (*bufp)[pos])
872241675Suqs		return(ROFF_IGN);
873241675Suqs
874241675Suqs	/* If present, process the custom end-of-line marker. */
875241675Suqs
876241675Suqs	sv = pos;
877241675Suqs	while ((*bufp)[pos] && ! isspace((unsigned char)(*bufp)[pos]))
878241675Suqs		pos++;
879241675Suqs
880241675Suqs	/*
881241675Suqs	 * Note: groff does NOT like escape characters in the input.
882241675Suqs	 * Instead of detecting this, we're just going to let it fly and
883241675Suqs	 * to hell with it.
884241675Suqs	 */
885241675Suqs
886241675Suqs	assert(pos > sv);
887241675Suqs	sz = (size_t)(pos - sv);
888241675Suqs
889241675Suqs	if (1 == sz && '.' == (*bufp)[sv])
890241675Suqs		return(ROFF_IGN);
891241675Suqs
892241675Suqs	r->last->end = mandoc_malloc(sz + 1);
893241675Suqs
894241675Suqs	memcpy(r->last->end, *bufp + sv, sz);
895241675Suqs	r->last->end[(int)sz] = '\0';
896241675Suqs
897241675Suqs	if ((*bufp)[pos])
898241675Suqs		mandoc_msg(MANDOCERR_ARGSLOST, r->parse, ln, pos, NULL);
899241675Suqs
900241675Suqs	return(ROFF_IGN);
901241675Suqs}
902241675Suqs
903241675Suqs
904241675Suqs/* ARGSUSED */
905241675Suqsstatic enum rofferr
906241675Suqsroff_block_sub(ROFF_ARGS)
907241675Suqs{
908241675Suqs	enum rofft	t;
909241675Suqs	int		i, j;
910241675Suqs
911241675Suqs	/*
912241675Suqs	 * First check whether a custom macro exists at this level.  If
913241675Suqs	 * it does, then check against it.  This is some of groff's
914241675Suqs	 * stranger behaviours.  If we encountered a custom end-scope
915241675Suqs	 * tag and that tag also happens to be a "real" macro, then we
916241675Suqs	 * need to try interpreting it again as a real macro.  If it's
917241675Suqs	 * not, then return ignore.  Else continue.
918241675Suqs	 */
919241675Suqs
920241675Suqs	if (r->last->end) {
921241675Suqs		for (i = pos, j = 0; r->last->end[j]; j++, i++)
922241675Suqs			if ((*bufp)[i] != r->last->end[j])
923241675Suqs				break;
924241675Suqs
925241675Suqs		if ('\0' == r->last->end[j] &&
926241675Suqs				('\0' == (*bufp)[i] ||
927241675Suqs				 ' ' == (*bufp)[i] ||
928241675Suqs				 '\t' == (*bufp)[i])) {
929241675Suqs			roffnode_pop(r);
930241675Suqs			roffnode_cleanscope(r);
931241675Suqs
932241675Suqs			while (' ' == (*bufp)[i] || '\t' == (*bufp)[i])
933241675Suqs				i++;
934241675Suqs
935241675Suqs			pos = i;
936241675Suqs			if (ROFF_MAX != roff_parse(r, *bufp, &pos))
937241675Suqs				return(ROFF_RERUN);
938241675Suqs			return(ROFF_IGN);
939241675Suqs		}
940241675Suqs	}
941241675Suqs
942241675Suqs	/*
943241675Suqs	 * If we have no custom end-query or lookup failed, then try
944241675Suqs	 * pulling it out of the hashtable.
945241675Suqs	 */
946241675Suqs
947241675Suqs	t = roff_parse(r, *bufp, &pos);
948241675Suqs
949241675Suqs	/*
950241675Suqs	 * Macros other than block-end are only significant
951241675Suqs	 * in `de' blocks; elsewhere, simply throw them away.
952241675Suqs	 */
953241675Suqs	if (ROFF_cblock != t) {
954241675Suqs		if (ROFF_de == tok)
955241675Suqs			roff_setstr(r, r->last->name, *bufp + ppos, 1);
956241675Suqs		return(ROFF_IGN);
957241675Suqs	}
958241675Suqs
959241675Suqs	assert(roffs[t].proc);
960241675Suqs	return((*roffs[t].proc)(r, t, bufp, szp,
961241675Suqs				ln, ppos, pos, offs));
962241675Suqs}
963241675Suqs
964241675Suqs
965241675Suqs/* ARGSUSED */
966241675Suqsstatic enum rofferr
967241675Suqsroff_block_text(ROFF_ARGS)
968241675Suqs{
969241675Suqs
970241675Suqs	if (ROFF_de == tok)
971241675Suqs		roff_setstr(r, r->last->name, *bufp + pos, 1);
972241675Suqs
973241675Suqs	return(ROFF_IGN);
974241675Suqs}
975241675Suqs
976241675Suqs
977241675Suqs/* ARGSUSED */
978241675Suqsstatic enum rofferr
979241675Suqsroff_cond_sub(ROFF_ARGS)
980241675Suqs{
981241675Suqs	enum rofft	 t;
982241675Suqs	enum roffrule	 rr;
983241675Suqs	char		*ep;
984241675Suqs
985241675Suqs	rr = r->last->rule;
986241675Suqs	roffnode_cleanscope(r);
987241675Suqs
988241675Suqs	/*
989241675Suqs	 * If the macro is unknown, first check if it contains a closing
990241675Suqs	 * delimiter `\}'.  If it does, close out our scope and return
991241675Suqs	 * the currently-scoped rule (ignore or continue).  Else, drop
992241675Suqs	 * into the currently-scoped rule.
993241675Suqs	 */
994241675Suqs
995241675Suqs	if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos))) {
996241675Suqs		ep = &(*bufp)[pos];
997241675Suqs		for ( ; NULL != (ep = strchr(ep, '\\')); ep++) {
998241675Suqs			ep++;
999241675Suqs			if ('}' != *ep)
1000241675Suqs				continue;
1001241675Suqs
1002241675Suqs			/*
1003241675Suqs			 * Make the \} go away.
1004241675Suqs			 * This is a little haphazard, as it's not quite
1005241675Suqs			 * clear how nroff does this.
1006241675Suqs			 * If we're at the end of line, then just chop
1007241675Suqs			 * off the \} and resize the buffer.
1008241675Suqs			 * If we aren't, then conver it to spaces.
1009241675Suqs			 */
1010241675Suqs
1011241675Suqs			if ('\0' == *(ep + 1)) {
1012241675Suqs				*--ep = '\0';
1013241675Suqs				*szp -= 2;
1014241675Suqs			} else
1015241675Suqs				*(ep - 1) = *ep = ' ';
1016241675Suqs
1017241675Suqs			roff_ccond(r, ROFF_ccond, bufp, szp,
1018241675Suqs					ln, pos, pos + 2, offs);
1019241675Suqs			break;
1020241675Suqs		}
1021241675Suqs		return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
1022241675Suqs	}
1023241675Suqs
1024241675Suqs	/*
1025241675Suqs	 * A denied conditional must evaluate its children if and only
1026241675Suqs	 * if they're either structurally required (such as loops and
1027241675Suqs	 * conditionals) or a closing macro.
1028241675Suqs	 */
1029241675Suqs
1030241675Suqs	if (ROFFRULE_DENY == rr)
1031241675Suqs		if ( ! (ROFFMAC_STRUCT & roffs[t].flags))
1032241675Suqs			if (ROFF_ccond != t)
1033241675Suqs				return(ROFF_IGN);
1034241675Suqs
1035241675Suqs	assert(roffs[t].proc);
1036241675Suqs	return((*roffs[t].proc)(r, t, bufp, szp,
1037241675Suqs				ln, ppos, pos, offs));
1038241675Suqs}
1039241675Suqs
1040241675Suqs/* ARGSUSED */
1041241675Suqsstatic enum rofferr
1042241675Suqsroff_cond_text(ROFF_ARGS)
1043241675Suqs{
1044241675Suqs	char		*ep;
1045241675Suqs	enum roffrule	 rr;
1046241675Suqs
1047241675Suqs	rr = r->last->rule;
1048241675Suqs	roffnode_cleanscope(r);
1049241675Suqs
1050241675Suqs	ep = &(*bufp)[pos];
1051241675Suqs	for ( ; NULL != (ep = strchr(ep, '\\')); ep++) {
1052241675Suqs		ep++;
1053241675Suqs		if ('}' != *ep)
1054241675Suqs			continue;
1055241675Suqs		*ep = '&';
1056241675Suqs		roff_ccond(r, ROFF_ccond, bufp, szp,
1057241675Suqs				ln, pos, pos + 2, offs);
1058241675Suqs	}
1059241675Suqs	return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
1060241675Suqs}
1061241675Suqs
1062241675Suqsstatic enum roffrule
1063241675Suqsroff_evalcond(const char *v, int *pos)
1064241675Suqs{
1065241675Suqs
1066241675Suqs	switch (v[*pos]) {
1067241675Suqs	case ('n'):
1068241675Suqs		(*pos)++;
1069241675Suqs		return(ROFFRULE_ALLOW);
1070241675Suqs	case ('e'):
1071241675Suqs		/* FALLTHROUGH */
1072241675Suqs	case ('o'):
1073241675Suqs		/* FALLTHROUGH */
1074241675Suqs	case ('t'):
1075241675Suqs		(*pos)++;
1076241675Suqs		return(ROFFRULE_DENY);
1077241675Suqs	default:
1078241675Suqs		break;
1079241675Suqs	}
1080241675Suqs
1081241675Suqs	while (v[*pos] && ' ' != v[*pos])
1082241675Suqs		(*pos)++;
1083241675Suqs	return(ROFFRULE_DENY);
1084241675Suqs}
1085241675Suqs
1086241675Suqs/* ARGSUSED */
1087241675Suqsstatic enum rofferr
1088241675Suqsroff_line_ignore(ROFF_ARGS)
1089241675Suqs{
1090241675Suqs
1091241675Suqs	if (ROFF_it == tok)
1092241675Suqs		mandoc_msg(MANDOCERR_REQUEST, r->parse, ln, ppos, "it");
1093241675Suqs
1094241675Suqs	return(ROFF_IGN);
1095241675Suqs}
1096241675Suqs
1097241675Suqs/* ARGSUSED */
1098241675Suqsstatic enum rofferr
1099241675Suqsroff_cond(ROFF_ARGS)
1100241675Suqs{
1101241675Suqs	int		 sv;
1102241675Suqs	enum roffrule	 rule;
1103241675Suqs
1104241675Suqs	/*
1105241675Suqs	 * An `.el' has no conditional body: it will consume the value
1106241675Suqs	 * of the current rstack entry set in prior `ie' calls or
1107241675Suqs	 * defaults to DENY.
1108241675Suqs	 *
1109241675Suqs	 * If we're not an `el', however, then evaluate the conditional.
1110241675Suqs	 */
1111241675Suqs
1112241675Suqs	rule = ROFF_el == tok ?
1113241675Suqs		(r->rstackpos < 0 ?
1114241675Suqs		 ROFFRULE_DENY : r->rstack[r->rstackpos--]) :
1115241675Suqs		roff_evalcond(*bufp, &pos);
1116241675Suqs
1117241675Suqs	sv = pos;
1118241675Suqs	while (' ' == (*bufp)[pos])
1119241675Suqs		pos++;
1120241675Suqs
1121241675Suqs	/*
1122241675Suqs	 * Roff is weird.  If we have just white-space after the
1123241675Suqs	 * conditional, it's considered the BODY and we exit without
1124241675Suqs	 * really doing anything.  Warn about this.  It's probably
1125241675Suqs	 * wrong.
1126241675Suqs	 */
1127241675Suqs
1128241675Suqs	if ('\0' == (*bufp)[pos] && sv != pos) {
1129241675Suqs		mandoc_msg(MANDOCERR_NOARGS, r->parse, ln, ppos, NULL);
1130241675Suqs		return(ROFF_IGN);
1131241675Suqs	}
1132241675Suqs
1133241675Suqs	roffnode_push(r, tok, NULL, ln, ppos);
1134241675Suqs
1135241675Suqs	r->last->rule = rule;
1136241675Suqs
1137241675Suqs	/*
1138241675Suqs	 * An if-else will put the NEGATION of the current evaluated
1139241675Suqs	 * conditional into the stack of rules.
1140241675Suqs	 */
1141241675Suqs
1142241675Suqs	if (ROFF_ie == tok) {
1143241675Suqs		if (r->rstackpos == RSTACK_MAX - 1) {
1144241675Suqs			mandoc_msg(MANDOCERR_MEM,
1145241675Suqs				r->parse, ln, ppos, NULL);
1146241675Suqs			return(ROFF_ERR);
1147241675Suqs		}
1148241675Suqs		r->rstack[++r->rstackpos] =
1149241675Suqs			ROFFRULE_DENY == r->last->rule ?
1150241675Suqs			ROFFRULE_ALLOW : ROFFRULE_DENY;
1151241675Suqs	}
1152241675Suqs
1153241675Suqs	/* If the parent has false as its rule, then so do we. */
1154241675Suqs
1155241675Suqs	if (r->last->parent && ROFFRULE_DENY == r->last->parent->rule)
1156241675Suqs		r->last->rule = ROFFRULE_DENY;
1157241675Suqs
1158241675Suqs	/*
1159241675Suqs	 * Determine scope.  If we're invoked with "\{" trailing the
1160241675Suqs	 * conditional, then we're in a multiline scope.  Else our scope
1161241675Suqs	 * expires on the next line.
1162241675Suqs	 */
1163241675Suqs
1164241675Suqs	r->last->endspan = 1;
1165241675Suqs
1166241675Suqs	if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
1167241675Suqs		r->last->endspan = -1;
1168241675Suqs		pos += 2;
1169241675Suqs	}
1170241675Suqs
1171241675Suqs	/*
1172241675Suqs	 * If there are no arguments on the line, the next-line scope is
1173241675Suqs	 * assumed.
1174241675Suqs	 */
1175241675Suqs
1176241675Suqs	if ('\0' == (*bufp)[pos])
1177241675Suqs		return(ROFF_IGN);
1178241675Suqs
1179241675Suqs	/* Otherwise re-run the roff parser after recalculating. */
1180241675Suqs
1181241675Suqs	*offs = pos;
1182241675Suqs	return(ROFF_RERUN);
1183241675Suqs}
1184241675Suqs
1185241675Suqs
1186241675Suqs/* ARGSUSED */
1187241675Suqsstatic enum rofferr
1188241675Suqsroff_ds(ROFF_ARGS)
1189241675Suqs{
1190241675Suqs	char		*name, *string;
1191241675Suqs
1192241675Suqs	/*
1193241675Suqs	 * A symbol is named by the first word following the macro
1194241675Suqs	 * invocation up to a space.  Its value is anything after the
1195241675Suqs	 * name's trailing whitespace and optional double-quote.  Thus,
1196241675Suqs	 *
1197241675Suqs	 *  [.ds foo "bar  "     ]
1198241675Suqs	 *
1199241675Suqs	 * will have `bar  "     ' as its value.
1200241675Suqs	 */
1201241675Suqs
1202241675Suqs	string = *bufp + pos;
1203241675Suqs	name = roff_getname(r, &string, ln, pos);
1204241675Suqs	if ('\0' == *name)
1205241675Suqs		return(ROFF_IGN);
1206241675Suqs
1207241675Suqs	/* Read past initial double-quote. */
1208241675Suqs	if ('"' == *string)
1209241675Suqs		string++;
1210241675Suqs
1211241675Suqs	/* The rest is the value. */
1212241675Suqs	roff_setstr(r, name, string, 0);
1213241675Suqs	return(ROFF_IGN);
1214241675Suqs}
1215241675Suqs
1216241675Suqsint
1217241675Suqsroff_regisset(const struct roff *r, enum regs reg)
1218241675Suqs{
1219241675Suqs
1220241675Suqs	return(r->regs[(int)reg].set);
1221241675Suqs}
1222241675Suqs
1223241675Suqsunsigned int
1224241675Suqsroff_regget(const struct roff *r, enum regs reg)
1225241675Suqs{
1226241675Suqs
1227241675Suqs	return(r->regs[(int)reg].u);
1228241675Suqs}
1229241675Suqs
1230241675Suqsvoid
1231241675Suqsroff_regunset(struct roff *r, enum regs reg)
1232241675Suqs{
1233241675Suqs
1234241675Suqs	r->regs[(int)reg].set = 0;
1235241675Suqs}
1236241675Suqs
1237241675Suqs/* ARGSUSED */
1238241675Suqsstatic enum rofferr
1239241675Suqsroff_nr(ROFF_ARGS)
1240241675Suqs{
1241241675Suqs	const char	*key;
1242241675Suqs	char		*val;
1243241675Suqs	int		 iv;
1244241675Suqs
1245241675Suqs	val = *bufp + pos;
1246241675Suqs	key = roff_getname(r, &val, ln, pos);
1247241675Suqs
1248241675Suqs	if (0 == strcmp(key, "nS")) {
1249241675Suqs		r->regs[(int)REG_nS].set = 1;
1250241675Suqs		if ((iv = mandoc_strntoi(val, strlen(val), 10)) >= 0)
1251241675Suqs			r->regs[(int)REG_nS].u = (unsigned)iv;
1252241675Suqs		else
1253241675Suqs			r->regs[(int)REG_nS].u = 0u;
1254241675Suqs	}
1255241675Suqs
1256241675Suqs	return(ROFF_IGN);
1257241675Suqs}
1258241675Suqs
1259241675Suqs/* ARGSUSED */
1260241675Suqsstatic enum rofferr
1261241675Suqsroff_rm(ROFF_ARGS)
1262241675Suqs{
1263241675Suqs	const char	 *name;
1264241675Suqs	char		 *cp;
1265241675Suqs
1266241675Suqs	cp = *bufp + pos;
1267241675Suqs	while ('\0' != *cp) {
1268241675Suqs		name = roff_getname(r, &cp, ln, (int)(cp - *bufp));
1269241675Suqs		if ('\0' != *name)
1270241675Suqs			roff_setstr(r, name, NULL, 0);
1271241675Suqs	}
1272241675Suqs	return(ROFF_IGN);
1273241675Suqs}
1274241675Suqs
1275241675Suqs/* ARGSUSED */
1276241675Suqsstatic enum rofferr
1277241675Suqsroff_TE(ROFF_ARGS)
1278241675Suqs{
1279241675Suqs
1280241675Suqs	if (NULL == r->tbl)
1281241675Suqs		mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
1282241675Suqs	else
1283241675Suqs		tbl_end(&r->tbl);
1284241675Suqs
1285241675Suqs	return(ROFF_IGN);
1286241675Suqs}
1287241675Suqs
1288241675Suqs/* ARGSUSED */
1289241675Suqsstatic enum rofferr
1290241675Suqsroff_T_(ROFF_ARGS)
1291241675Suqs{
1292241675Suqs
1293241675Suqs	if (NULL == r->tbl)
1294241675Suqs		mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
1295241675Suqs	else
1296241675Suqs		tbl_restart(ppos, ln, r->tbl);
1297241675Suqs
1298241675Suqs	return(ROFF_IGN);
1299241675Suqs}
1300241675Suqs
1301241675Suqs#if 0
1302241675Suqsstatic int
1303241675Suqsroff_closeeqn(struct roff *r)
1304241675Suqs{
1305241675Suqs
1306241675Suqs	return(r->eqn && ROFF_EQN == eqn_end(&r->eqn) ? 1 : 0);
1307241675Suqs}
1308241675Suqs#endif
1309241675Suqs
1310241675Suqsstatic void
1311241675Suqsroff_openeqn(struct roff *r, const char *name, int line,
1312241675Suqs		int offs, const char *buf)
1313241675Suqs{
1314241675Suqs	struct eqn_node *e;
1315241675Suqs	int		 poff;
1316241675Suqs
1317241675Suqs	assert(NULL == r->eqn);
1318241675Suqs	e = eqn_alloc(name, offs, line, r->parse);
1319241675Suqs
1320241675Suqs	if (r->last_eqn)
1321241675Suqs		r->last_eqn->next = e;
1322241675Suqs	else
1323241675Suqs		r->first_eqn = r->last_eqn = e;
1324241675Suqs
1325241675Suqs	r->eqn = r->last_eqn = e;
1326241675Suqs
1327241675Suqs	if (buf) {
1328241675Suqs		poff = 0;
1329241675Suqs		eqn_read(&r->eqn, line, buf, offs, &poff);
1330241675Suqs	}
1331241675Suqs}
1332241675Suqs
1333241675Suqs/* ARGSUSED */
1334241675Suqsstatic enum rofferr
1335241675Suqsroff_EQ(ROFF_ARGS)
1336241675Suqs{
1337241675Suqs
1338241675Suqs	roff_openeqn(r, *bufp + pos, ln, ppos, NULL);
1339241675Suqs	return(ROFF_IGN);
1340241675Suqs}
1341241675Suqs
1342241675Suqs/* ARGSUSED */
1343241675Suqsstatic enum rofferr
1344241675Suqsroff_EN(ROFF_ARGS)
1345241675Suqs{
1346241675Suqs
1347241675Suqs	mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
1348241675Suqs	return(ROFF_IGN);
1349241675Suqs}
1350241675Suqs
1351241675Suqs/* ARGSUSED */
1352241675Suqsstatic enum rofferr
1353241675Suqsroff_TS(ROFF_ARGS)
1354241675Suqs{
1355241675Suqs	struct tbl_node	*t;
1356241675Suqs
1357241675Suqs	if (r->tbl) {
1358241675Suqs		mandoc_msg(MANDOCERR_SCOPEBROKEN, r->parse, ln, ppos, NULL);
1359241675Suqs		tbl_end(&r->tbl);
1360241675Suqs	}
1361241675Suqs
1362241675Suqs	t = tbl_alloc(ppos, ln, r->parse);
1363241675Suqs
1364241675Suqs	if (r->last_tbl)
1365241675Suqs		r->last_tbl->next = t;
1366241675Suqs	else
1367241675Suqs		r->first_tbl = r->last_tbl = t;
1368241675Suqs
1369241675Suqs	r->tbl = r->last_tbl = t;
1370241675Suqs	return(ROFF_IGN);
1371241675Suqs}
1372241675Suqs
1373241675Suqs/* ARGSUSED */
1374241675Suqsstatic enum rofferr
1375241675Suqsroff_tr(ROFF_ARGS)
1376241675Suqs{
1377241675Suqs	const char	*p, *first, *second;
1378241675Suqs	size_t		 fsz, ssz;
1379241675Suqs	enum mandoc_esc	 esc;
1380241675Suqs
1381241675Suqs	p = *bufp + pos;
1382241675Suqs
1383241675Suqs	if ('\0' == *p) {
1384241675Suqs		mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL);
1385241675Suqs		return(ROFF_IGN);
1386241675Suqs	}
1387241675Suqs
1388241675Suqs	while ('\0' != *p) {
1389241675Suqs		fsz = ssz = 1;
1390241675Suqs
1391241675Suqs		first = p++;
1392241675Suqs		if ('\\' == *first) {
1393241675Suqs			esc = mandoc_escape(&p, NULL, NULL);
1394241675Suqs			if (ESCAPE_ERROR == esc) {
1395241675Suqs				mandoc_msg
1396241675Suqs					(MANDOCERR_BADESCAPE, r->parse,
1397241675Suqs					 ln, (int)(p - *bufp), NULL);
1398241675Suqs				return(ROFF_IGN);
1399241675Suqs			}
1400241675Suqs			fsz = (size_t)(p - first);
1401241675Suqs		}
1402241675Suqs
1403241675Suqs		second = p++;
1404241675Suqs		if ('\\' == *second) {
1405241675Suqs			esc = mandoc_escape(&p, NULL, NULL);
1406241675Suqs			if (ESCAPE_ERROR == esc) {
1407241675Suqs				mandoc_msg
1408241675Suqs					(MANDOCERR_BADESCAPE, r->parse,
1409241675Suqs					 ln, (int)(p - *bufp), NULL);
1410241675Suqs				return(ROFF_IGN);
1411241675Suqs			}
1412241675Suqs			ssz = (size_t)(p - second);
1413241675Suqs		} else if ('\0' == *second) {
1414241675Suqs			mandoc_msg(MANDOCERR_ARGCOUNT, r->parse,
1415241675Suqs					ln, (int)(p - *bufp), NULL);
1416241675Suqs			second = " ";
1417241675Suqs			p--;
1418241675Suqs		}
1419241675Suqs
1420241675Suqs		if (fsz > 1) {
1421241675Suqs			roff_setstrn(&r->xmbtab, first,
1422241675Suqs					fsz, second, ssz, 0);
1423241675Suqs			continue;
1424241675Suqs		}
1425241675Suqs
1426241675Suqs		if (NULL == r->xtab)
1427241675Suqs			r->xtab = mandoc_calloc
1428241675Suqs				(128, sizeof(struct roffstr));
1429241675Suqs
1430241675Suqs		free(r->xtab[(int)*first].p);
1431241675Suqs		r->xtab[(int)*first].p = mandoc_strndup(second, ssz);
1432241675Suqs		r->xtab[(int)*first].sz = ssz;
1433241675Suqs	}
1434241675Suqs
1435241675Suqs	return(ROFF_IGN);
1436241675Suqs}
1437241675Suqs
1438241675Suqs/* ARGSUSED */
1439241675Suqsstatic enum rofferr
1440241675Suqsroff_so(ROFF_ARGS)
1441241675Suqs{
1442241675Suqs	char *name;
1443241675Suqs
1444241675Suqs	mandoc_msg(MANDOCERR_SO, r->parse, ln, ppos, NULL);
1445241675Suqs
1446241675Suqs	/*
1447241675Suqs	 * Handle `so'.  Be EXTREMELY careful, as we shouldn't be
1448241675Suqs	 * opening anything that's not in our cwd or anything beneath
1449241675Suqs	 * it.  Thus, explicitly disallow traversing up the file-system
1450241675Suqs	 * or using absolute paths.
1451241675Suqs	 */
1452241675Suqs
1453241675Suqs	name = *bufp + pos;
1454241675Suqs	if ('/' == *name || strstr(name, "../") || strstr(name, "/..")) {
1455241675Suqs		mandoc_msg(MANDOCERR_SOPATH, r->parse, ln, pos, NULL);
1456241675Suqs		return(ROFF_ERR);
1457241675Suqs	}
1458241675Suqs
1459241675Suqs	*offs = pos;
1460241675Suqs	return(ROFF_SO);
1461241675Suqs}
1462241675Suqs
1463241675Suqs/* ARGSUSED */
1464241675Suqsstatic enum rofferr
1465241675Suqsroff_userdef(ROFF_ARGS)
1466241675Suqs{
1467241675Suqs	const char	 *arg[9];
1468241675Suqs	char		 *cp, *n1, *n2;
1469241675Suqs	int		  i;
1470241675Suqs
1471241675Suqs	/*
1472241675Suqs	 * Collect pointers to macro argument strings
1473241675Suqs	 * and null-terminate them.
1474241675Suqs	 */
1475241675Suqs	cp = *bufp + pos;
1476241675Suqs	for (i = 0; i < 9; i++)
1477241675Suqs		arg[i] = '\0' == *cp ? "" :
1478241675Suqs		    mandoc_getarg(r->parse, &cp, ln, &pos);
1479241675Suqs
1480241675Suqs	/*
1481241675Suqs	 * Expand macro arguments.
1482241675Suqs	 */
1483241675Suqs	*szp = 0;
1484241675Suqs	n1 = cp = mandoc_strdup(r->current_string);
1485241675Suqs	while (NULL != (cp = strstr(cp, "\\$"))) {
1486241675Suqs		i = cp[2] - '1';
1487241675Suqs		if (0 > i || 8 < i) {
1488241675Suqs			/* Not an argument invocation. */
1489241675Suqs			cp += 2;
1490241675Suqs			continue;
1491241675Suqs		}
1492241675Suqs
1493241675Suqs		*szp = strlen(n1) - 3 + strlen(arg[i]) + 1;
1494241675Suqs		n2 = mandoc_malloc(*szp);
1495241675Suqs
1496241675Suqs		strlcpy(n2, n1, (size_t)(cp - n1 + 1));
1497241675Suqs		strlcat(n2, arg[i], *szp);
1498241675Suqs		strlcat(n2, cp + 3, *szp);
1499241675Suqs
1500241675Suqs		cp = n2 + (cp - n1);
1501241675Suqs		free(n1);
1502241675Suqs		n1 = n2;
1503241675Suqs	}
1504241675Suqs
1505241675Suqs	/*
1506241675Suqs	 * Replace the macro invocation
1507241675Suqs	 * by the expanded macro.
1508241675Suqs	 */
1509241675Suqs	free(*bufp);
1510241675Suqs	*bufp = n1;
1511241675Suqs	if (0 == *szp)
1512241675Suqs		*szp = strlen(*bufp) + 1;
1513241675Suqs
1514241675Suqs	return(*szp > 1 && '\n' == (*bufp)[(int)*szp - 2] ?
1515241675Suqs	   ROFF_REPARSE : ROFF_APPEND);
1516241675Suqs}
1517241675Suqs
1518241675Suqsstatic char *
1519241675Suqsroff_getname(struct roff *r, char **cpp, int ln, int pos)
1520241675Suqs{
1521241675Suqs	char	 *name, *cp;
1522241675Suqs
1523241675Suqs	name = *cpp;
1524241675Suqs	if ('\0' == *name)
1525241675Suqs		return(name);
1526241675Suqs
1527241675Suqs	/* Read until end of name. */
1528241675Suqs	for (cp = name; '\0' != *cp && ' ' != *cp; cp++) {
1529241675Suqs		if ('\\' != *cp)
1530241675Suqs			continue;
1531241675Suqs		cp++;
1532241675Suqs		if ('\\' == *cp)
1533241675Suqs			continue;
1534241675Suqs		mandoc_msg(MANDOCERR_NAMESC, r->parse, ln, pos, NULL);
1535241675Suqs		*cp = '\0';
1536241675Suqs		name = cp;
1537241675Suqs	}
1538241675Suqs
1539241675Suqs	/* Nil-terminate name. */
1540241675Suqs	if ('\0' != *cp)
1541241675Suqs		*(cp++) = '\0';
1542241675Suqs
1543241675Suqs	/* Read past spaces. */
1544241675Suqs	while (' ' == *cp)
1545241675Suqs		cp++;
1546241675Suqs
1547241675Suqs	*cpp = cp;
1548241675Suqs	return(name);
1549241675Suqs}
1550241675Suqs
1551241675Suqs/*
1552241675Suqs * Store *string into the user-defined string called *name.
1553241675Suqs * In multiline mode, append to an existing entry and append '\n';
1554241675Suqs * else replace the existing entry, if there is one.
1555241675Suqs * To clear an existing entry, call with (*r, *name, NULL, 0).
1556241675Suqs */
1557241675Suqsstatic void
1558241675Suqsroff_setstr(struct roff *r, const char *name, const char *string,
1559241675Suqs	int multiline)
1560241675Suqs{
1561241675Suqs
1562241675Suqs	roff_setstrn(&r->strtab, name, strlen(name), string,
1563241675Suqs			string ? strlen(string) : 0, multiline);
1564241675Suqs}
1565241675Suqs
1566241675Suqsstatic void
1567241675Suqsroff_setstrn(struct roffkv **r, const char *name, size_t namesz,
1568241675Suqs		const char *string, size_t stringsz, int multiline)
1569241675Suqs{
1570241675Suqs	struct roffkv	*n;
1571241675Suqs	char		*c;
1572241675Suqs	int		 i;
1573241675Suqs	size_t		 oldch, newch;
1574241675Suqs
1575241675Suqs	/* Search for an existing string with the same name. */
1576241675Suqs	n = *r;
1577241675Suqs
1578241675Suqs	while (n && strcmp(name, n->key.p))
1579241675Suqs		n = n->next;
1580241675Suqs
1581241675Suqs	if (NULL == n) {
1582241675Suqs		/* Create a new string table entry. */
1583241675Suqs		n = mandoc_malloc(sizeof(struct roffkv));
1584241675Suqs		n->key.p = mandoc_strndup(name, namesz);
1585241675Suqs		n->key.sz = namesz;
1586241675Suqs		n->val.p = NULL;
1587241675Suqs		n->val.sz = 0;
1588241675Suqs		n->next = *r;
1589241675Suqs		*r = n;
1590241675Suqs	} else if (0 == multiline) {
1591241675Suqs		/* In multiline mode, append; else replace. */
1592241675Suqs		free(n->val.p);
1593241675Suqs		n->val.p = NULL;
1594241675Suqs		n->val.sz = 0;
1595241675Suqs	}
1596241675Suqs
1597241675Suqs	if (NULL == string)
1598241675Suqs		return;
1599241675Suqs
1600241675Suqs	/*
1601241675Suqs	 * One additional byte for the '\n' in multiline mode,
1602241675Suqs	 * and one for the terminating '\0'.
1603241675Suqs	 */
1604241675Suqs	newch = stringsz + (multiline ? 2u : 1u);
1605241675Suqs
1606241675Suqs	if (NULL == n->val.p) {
1607241675Suqs		n->val.p = mandoc_malloc(newch);
1608241675Suqs		*n->val.p = '\0';
1609241675Suqs		oldch = 0;
1610241675Suqs	} else {
1611241675Suqs		oldch = n->val.sz;
1612241675Suqs		n->val.p = mandoc_realloc(n->val.p, oldch + newch);
1613241675Suqs	}
1614241675Suqs
1615241675Suqs	/* Skip existing content in the destination buffer. */
1616241675Suqs	c = n->val.p + (int)oldch;
1617241675Suqs
1618241675Suqs	/* Append new content to the destination buffer. */
1619241675Suqs	i = 0;
1620241675Suqs	while (i < (int)stringsz) {
1621241675Suqs		/*
1622241675Suqs		 * Rudimentary roff copy mode:
1623241675Suqs		 * Handle escaped backslashes.
1624241675Suqs		 */
1625241675Suqs		if ('\\' == string[i] && '\\' == string[i + 1])
1626241675Suqs			i++;
1627241675Suqs		*c++ = string[i++];
1628241675Suqs	}
1629241675Suqs
1630241675Suqs	/* Append terminating bytes. */
1631241675Suqs	if (multiline)
1632241675Suqs		*c++ = '\n';
1633241675Suqs
1634241675Suqs	*c = '\0';
1635241675Suqs	n->val.sz = (int)(c - n->val.p);
1636241675Suqs}
1637241675Suqs
1638241675Suqsstatic const char *
1639241675Suqsroff_getstrn(const struct roff *r, const char *name, size_t len)
1640241675Suqs{
1641241675Suqs	const struct roffkv *n;
1642241675Suqs
1643241675Suqs	for (n = r->strtab; n; n = n->next)
1644241675Suqs		if (0 == strncmp(name, n->key.p, len) &&
1645241675Suqs				'\0' == n->key.p[(int)len])
1646241675Suqs			return(n->val.p);
1647241675Suqs
1648241675Suqs	return(NULL);
1649241675Suqs}
1650241675Suqs
1651241675Suqsstatic void
1652241675Suqsroff_freestr(struct roffkv *r)
1653241675Suqs{
1654241675Suqs	struct roffkv	 *n, *nn;
1655241675Suqs
1656241675Suqs	for (n = r; n; n = nn) {
1657241675Suqs		free(n->key.p);
1658241675Suqs		free(n->val.p);
1659241675Suqs		nn = n->next;
1660241675Suqs		free(n);
1661241675Suqs	}
1662241675Suqs}
1663241675Suqs
1664241675Suqsconst struct tbl_span *
1665241675Suqsroff_span(const struct roff *r)
1666241675Suqs{
1667241675Suqs
1668241675Suqs	return(r->tbl ? tbl_span(r->tbl) : NULL);
1669241675Suqs}
1670241675Suqs
1671241675Suqsconst struct eqn *
1672241675Suqsroff_eqn(const struct roff *r)
1673241675Suqs{
1674241675Suqs
1675241675Suqs	return(r->last_eqn ? &r->last_eqn->eqn : NULL);
1676241675Suqs}
1677241675Suqs
1678241675Suqs/*
1679241675Suqs * Duplicate an input string, making the appropriate character
1680241675Suqs * conversations (as stipulated by `tr') along the way.
1681241675Suqs * Returns a heap-allocated string with all the replacements made.
1682241675Suqs */
1683241675Suqschar *
1684241675Suqsroff_strdup(const struct roff *r, const char *p)
1685241675Suqs{
1686241675Suqs	const struct roffkv *cp;
1687241675Suqs	char		*res;
1688241675Suqs	const char	*pp;
1689241675Suqs	size_t		 ssz, sz;
1690241675Suqs	enum mandoc_esc	 esc;
1691241675Suqs
1692241675Suqs	if (NULL == r->xmbtab && NULL == r->xtab)
1693241675Suqs		return(mandoc_strdup(p));
1694241675Suqs	else if ('\0' == *p)
1695241675Suqs		return(mandoc_strdup(""));
1696241675Suqs
1697241675Suqs	/*
1698241675Suqs	 * Step through each character looking for term matches
1699241675Suqs	 * (remember that a `tr' can be invoked with an escape, which is
1700241675Suqs	 * a glyph but the escape is multi-character).
1701241675Suqs	 * We only do this if the character hash has been initialised
1702241675Suqs	 * and the string is >0 length.
1703241675Suqs	 */
1704241675Suqs
1705241675Suqs	res = NULL;
1706241675Suqs	ssz = 0;
1707241675Suqs
1708241675Suqs	while ('\0' != *p) {
1709241675Suqs		if ('\\' != *p && r->xtab && r->xtab[(int)*p].p) {
1710241675Suqs			sz = r->xtab[(int)*p].sz;
1711241675Suqs			res = mandoc_realloc(res, ssz + sz + 1);
1712241675Suqs			memcpy(res + ssz, r->xtab[(int)*p].p, sz);
1713241675Suqs			ssz += sz;
1714241675Suqs			p++;
1715241675Suqs			continue;
1716241675Suqs		} else if ('\\' != *p) {
1717241675Suqs			res = mandoc_realloc(res, ssz + 2);
1718241675Suqs			res[ssz++] = *p++;
1719241675Suqs			continue;
1720241675Suqs		}
1721241675Suqs
1722241675Suqs		/* Search for term matches. */
1723241675Suqs		for (cp = r->xmbtab; cp; cp = cp->next)
1724241675Suqs			if (0 == strncmp(p, cp->key.p, cp->key.sz))
1725241675Suqs				break;
1726241675Suqs
1727241675Suqs		if (NULL != cp) {
1728241675Suqs			/*
1729241675Suqs			 * A match has been found.
1730241675Suqs			 * Append the match to the array and move
1731241675Suqs			 * forward by its keysize.
1732241675Suqs			 */
1733241675Suqs			res = mandoc_realloc
1734241675Suqs				(res, ssz + cp->val.sz + 1);
1735241675Suqs			memcpy(res + ssz, cp->val.p, cp->val.sz);
1736241675Suqs			ssz += cp->val.sz;
1737241675Suqs			p += (int)cp->key.sz;
1738241675Suqs			continue;
1739241675Suqs		}
1740241675Suqs
1741241675Suqs		/*
1742241675Suqs		 * Handle escapes carefully: we need to copy
1743241675Suqs		 * over just the escape itself, or else we might
1744241675Suqs		 * do replacements within the escape itself.
1745241675Suqs		 * Make sure to pass along the bogus string.
1746241675Suqs		 */
1747241675Suqs		pp = p++;
1748241675Suqs		esc = mandoc_escape(&p, NULL, NULL);
1749241675Suqs		if (ESCAPE_ERROR == esc) {
1750241675Suqs			sz = strlen(pp);
1751241675Suqs			res = mandoc_realloc(res, ssz + sz + 1);
1752241675Suqs			memcpy(res + ssz, pp, sz);
1753241675Suqs			break;
1754241675Suqs		}
1755241675Suqs		/*
1756241675Suqs		 * We bail out on bad escapes.
1757241675Suqs		 * No need to warn: we already did so when
1758241675Suqs		 * roff_res() was called.
1759241675Suqs		 */
1760241675Suqs		sz = (int)(p - pp);
1761241675Suqs		res = mandoc_realloc(res, ssz + sz + 1);
1762241675Suqs		memcpy(res + ssz, pp, sz);
1763241675Suqs		ssz += sz;
1764241675Suqs	}
1765241675Suqs
1766241675Suqs	res[(int)ssz] = '\0';
1767241675Suqs	return(res);
1768241675Suqs}
1769