xref: /minix3/external/bsd/mdocml/dist/mdoc.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /*	Id: mdoc.c,v 1.207 2013/12/31 23:23:11 schwarze Exp  */
2d65f6f70SBen Gras /*
392395e9cSLionel Sambuc  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4*0a6a1f1dSLionel Sambuc  * Copyright (c) 2010, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
5d65f6f70SBen Gras  *
6d65f6f70SBen Gras  * Permission to use, copy, modify, and distribute this software for any
7d65f6f70SBen Gras  * purpose with or without fee is hereby granted, provided that the above
8d65f6f70SBen Gras  * copyright notice and this permission notice appear in all copies.
9d65f6f70SBen Gras  *
10d65f6f70SBen Gras  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11d65f6f70SBen Gras  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12d65f6f70SBen Gras  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13d65f6f70SBen Gras  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14d65f6f70SBen Gras  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15d65f6f70SBen Gras  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16d65f6f70SBen Gras  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17d65f6f70SBen Gras  */
18d65f6f70SBen Gras #ifdef HAVE_CONFIG_H
19d65f6f70SBen Gras #include "config.h"
20d65f6f70SBen Gras #endif
21d65f6f70SBen Gras 
22d65f6f70SBen Gras #include <sys/types.h>
23d65f6f70SBen Gras 
24d65f6f70SBen Gras #include <assert.h>
25d65f6f70SBen Gras #include <stdarg.h>
26d65f6f70SBen Gras #include <stdio.h>
27d65f6f70SBen Gras #include <stdlib.h>
28d65f6f70SBen Gras #include <string.h>
29d65f6f70SBen Gras #include <time.h>
30d65f6f70SBen Gras 
3192395e9cSLionel Sambuc #include "mdoc.h"
32d65f6f70SBen Gras #include "mandoc.h"
33d65f6f70SBen Gras #include "libmdoc.h"
34d65f6f70SBen Gras #include "libmandoc.h"
35d65f6f70SBen Gras 
36d65f6f70SBen Gras const	char *const __mdoc_macronames[MDOC_MAX] = {
37d65f6f70SBen Gras 	"Ap",		"Dd",		"Dt",		"Os",
38d65f6f70SBen Gras 	"Sh",		"Ss",		"Pp",		"D1",
39d65f6f70SBen Gras 	"Dl",		"Bd",		"Ed",		"Bl",
40d65f6f70SBen Gras 	"El",		"It",		"Ad",		"An",
41d65f6f70SBen Gras 	"Ar",		"Cd",		"Cm",		"Dv",
42d65f6f70SBen Gras 	"Er",		"Ev",		"Ex",		"Fa",
43d65f6f70SBen Gras 	"Fd",		"Fl",		"Fn",		"Ft",
44d65f6f70SBen Gras 	"Ic",		"In",		"Li",		"Nd",
45d65f6f70SBen Gras 	"Nm",		"Op",		"Ot",		"Pa",
46d65f6f70SBen Gras 	"Rv",		"St",		"Va",		"Vt",
47d65f6f70SBen Gras 	/* LINTED */
48d65f6f70SBen Gras 	"Xr",		"%A",		"%B",		"%D",
49d65f6f70SBen Gras 	/* LINTED */
50d65f6f70SBen Gras 	"%I",		"%J",		"%N",		"%O",
51d65f6f70SBen Gras 	/* LINTED */
52d65f6f70SBen Gras 	"%P",		"%R",		"%T",		"%V",
53d65f6f70SBen Gras 	"Ac",		"Ao",		"Aq",		"At",
54d65f6f70SBen Gras 	"Bc",		"Bf",		"Bo",		"Bq",
55d65f6f70SBen Gras 	"Bsx",		"Bx",		"Db",		"Dc",
56d65f6f70SBen Gras 	"Do",		"Dq",		"Ec",		"Ef",
57d65f6f70SBen Gras 	"Em",		"Eo",		"Fx",		"Ms",
58d65f6f70SBen Gras 	"No",		"Ns",		"Nx",		"Ox",
59d65f6f70SBen Gras 	"Pc",		"Pf",		"Po",		"Pq",
60d65f6f70SBen Gras 	"Qc",		"Ql",		"Qo",		"Qq",
61d65f6f70SBen Gras 	"Re",		"Rs",		"Sc",		"So",
62d65f6f70SBen Gras 	"Sq",		"Sm",		"Sx",		"Sy",
63d65f6f70SBen Gras 	"Tn",		"Ux",		"Xc",		"Xo",
64d65f6f70SBen Gras 	"Fo",		"Fc",		"Oo",		"Oc",
65d65f6f70SBen Gras 	"Bk",		"Ek",		"Bt",		"Hf",
66d65f6f70SBen Gras 	"Fr",		"Ud",		"Lb",		"Lp",
67d65f6f70SBen Gras 	"Lk",		"Mt",		"Brq",		"Bro",
68d65f6f70SBen Gras 	/* LINTED */
69d65f6f70SBen Gras 	"Brc",		"%C",		"Es",		"En",
70d65f6f70SBen Gras 	/* LINTED */
71d65f6f70SBen Gras 	"Dx",		"%Q",		"br",		"sp",
72d65f6f70SBen Gras 	/* LINTED */
73d65f6f70SBen Gras 	"%U",		"Ta"
74d65f6f70SBen Gras 	};
75d65f6f70SBen Gras 
76d65f6f70SBen Gras const	char *const __mdoc_argnames[MDOC_ARG_MAX] = {
77d65f6f70SBen Gras 	"split",		"nosplit",		"ragged",
78d65f6f70SBen Gras 	"unfilled",		"literal",		"file",
79d65f6f70SBen Gras 	"offset",		"bullet",		"dash",
80d65f6f70SBen Gras 	"hyphen",		"item",			"enum",
81d65f6f70SBen Gras 	"tag",			"diag",			"hang",
82d65f6f70SBen Gras 	"ohang",		"inset",		"column",
83d65f6f70SBen Gras 	"width",		"compact",		"std",
84d65f6f70SBen Gras 	"filled",		"words",		"emphasis",
85d65f6f70SBen Gras 	"symbolic",		"nested",		"centered"
86d65f6f70SBen Gras 	};
87d65f6f70SBen Gras 
88d65f6f70SBen Gras const	char * const *mdoc_macronames = __mdoc_macronames;
89d65f6f70SBen Gras const	char * const *mdoc_argnames = __mdoc_argnames;
90d65f6f70SBen Gras 
91d65f6f70SBen Gras static	void		  mdoc_node_free(struct mdoc_node *);
92d65f6f70SBen Gras static	void		  mdoc_node_unlink(struct mdoc *,
93d65f6f70SBen Gras 				struct mdoc_node *);
94d65f6f70SBen Gras static	void		  mdoc_free1(struct mdoc *);
95d65f6f70SBen Gras static	void		  mdoc_alloc1(struct mdoc *);
96d65f6f70SBen Gras static	struct mdoc_node *node_alloc(struct mdoc *, int, int,
97d65f6f70SBen Gras 				enum mdoct, enum mdoc_type);
98d65f6f70SBen Gras static	int		  node_append(struct mdoc *,
99d65f6f70SBen Gras 				struct mdoc_node *);
10092395e9cSLionel Sambuc #if 0
10192395e9cSLionel Sambuc static	int		  mdoc_preptext(struct mdoc *, int, char *, int);
10292395e9cSLionel Sambuc #endif
103d65f6f70SBen Gras static	int		  mdoc_ptext(struct mdoc *, int, char *, int);
104d65f6f70SBen Gras static	int		  mdoc_pmacro(struct mdoc *, int, char *, int);
105d65f6f70SBen Gras 
106d65f6f70SBen Gras const struct mdoc_node *
mdoc_node(const struct mdoc * mdoc)107*0a6a1f1dSLionel Sambuc mdoc_node(const struct mdoc *mdoc)
108d65f6f70SBen Gras {
109d65f6f70SBen Gras 
110*0a6a1f1dSLionel Sambuc 	assert( ! (MDOC_HALT & mdoc->flags));
111*0a6a1f1dSLionel Sambuc 	return(mdoc->first);
112d65f6f70SBen Gras }
113d65f6f70SBen Gras 
114d65f6f70SBen Gras 
115d65f6f70SBen Gras const struct mdoc_meta *
mdoc_meta(const struct mdoc * mdoc)116*0a6a1f1dSLionel Sambuc mdoc_meta(const struct mdoc *mdoc)
117d65f6f70SBen Gras {
118d65f6f70SBen Gras 
119*0a6a1f1dSLionel Sambuc 	assert( ! (MDOC_HALT & mdoc->flags));
120*0a6a1f1dSLionel Sambuc 	return(&mdoc->meta);
121d65f6f70SBen Gras }
122d65f6f70SBen Gras 
123d65f6f70SBen Gras 
124d65f6f70SBen Gras /*
125d65f6f70SBen Gras  * Frees volatile resources (parse tree, meta-data, fields).
126d65f6f70SBen Gras  */
127d65f6f70SBen Gras static void
mdoc_free1(struct mdoc * mdoc)128d65f6f70SBen Gras mdoc_free1(struct mdoc *mdoc)
129d65f6f70SBen Gras {
130d65f6f70SBen Gras 
131d65f6f70SBen Gras 	if (mdoc->first)
132d65f6f70SBen Gras 		mdoc_node_delete(mdoc, mdoc->first);
133d65f6f70SBen Gras 	if (mdoc->meta.title)
134d65f6f70SBen Gras 		free(mdoc->meta.title);
135d65f6f70SBen Gras 	if (mdoc->meta.os)
136d65f6f70SBen Gras 		free(mdoc->meta.os);
137d65f6f70SBen Gras 	if (mdoc->meta.name)
138d65f6f70SBen Gras 		free(mdoc->meta.name);
139d65f6f70SBen Gras 	if (mdoc->meta.arch)
140d65f6f70SBen Gras 		free(mdoc->meta.arch);
141d65f6f70SBen Gras 	if (mdoc->meta.vol)
142d65f6f70SBen Gras 		free(mdoc->meta.vol);
143d65f6f70SBen Gras 	if (mdoc->meta.msec)
144d65f6f70SBen Gras 		free(mdoc->meta.msec);
14592395e9cSLionel Sambuc 	if (mdoc->meta.date)
14692395e9cSLionel Sambuc 		free(mdoc->meta.date);
147d65f6f70SBen Gras }
148d65f6f70SBen Gras 
149d65f6f70SBen Gras 
150d65f6f70SBen Gras /*
151d65f6f70SBen Gras  * Allocate all volatile resources (parse tree, meta-data, fields).
152d65f6f70SBen Gras  */
153d65f6f70SBen Gras static void
mdoc_alloc1(struct mdoc * mdoc)154d65f6f70SBen Gras mdoc_alloc1(struct mdoc *mdoc)
155d65f6f70SBen Gras {
156d65f6f70SBen Gras 
157d65f6f70SBen Gras 	memset(&mdoc->meta, 0, sizeof(struct mdoc_meta));
158d65f6f70SBen Gras 	mdoc->flags = 0;
159d65f6f70SBen Gras 	mdoc->lastnamed = mdoc->lastsec = SEC_NONE;
160d65f6f70SBen Gras 	mdoc->last = mandoc_calloc(1, sizeof(struct mdoc_node));
161d65f6f70SBen Gras 	mdoc->first = mdoc->last;
162d65f6f70SBen Gras 	mdoc->last->type = MDOC_ROOT;
16392395e9cSLionel Sambuc 	mdoc->last->tok = MDOC_MAX;
164d65f6f70SBen Gras 	mdoc->next = MDOC_NEXT_CHILD;
165d65f6f70SBen Gras }
166d65f6f70SBen Gras 
167d65f6f70SBen Gras 
168d65f6f70SBen Gras /*
169d65f6f70SBen Gras  * Free up volatile resources (see mdoc_free1()) then re-initialises the
170d65f6f70SBen Gras  * data with mdoc_alloc1().  After invocation, parse data has been reset
171d65f6f70SBen Gras  * and the parser is ready for re-invocation on a new tree; however,
172d65f6f70SBen Gras  * cross-parse non-volatile data is kept intact.
173d65f6f70SBen Gras  */
174d65f6f70SBen Gras void
mdoc_reset(struct mdoc * mdoc)175d65f6f70SBen Gras mdoc_reset(struct mdoc *mdoc)
176d65f6f70SBen Gras {
177d65f6f70SBen Gras 
178d65f6f70SBen Gras 	mdoc_free1(mdoc);
179d65f6f70SBen Gras 	mdoc_alloc1(mdoc);
180d65f6f70SBen Gras }
181d65f6f70SBen Gras 
182d65f6f70SBen Gras 
183d65f6f70SBen Gras /*
184d65f6f70SBen Gras  * Completely free up all volatile and non-volatile parse resources.
185d65f6f70SBen Gras  * After invocation, the pointer is no longer usable.
186d65f6f70SBen Gras  */
187d65f6f70SBen Gras void
mdoc_free(struct mdoc * mdoc)188d65f6f70SBen Gras mdoc_free(struct mdoc *mdoc)
189d65f6f70SBen Gras {
190d65f6f70SBen Gras 
191d65f6f70SBen Gras 	mdoc_free1(mdoc);
192d65f6f70SBen Gras 	free(mdoc);
193d65f6f70SBen Gras }
194d65f6f70SBen Gras 
195d65f6f70SBen Gras 
196d65f6f70SBen Gras /*
197d65f6f70SBen Gras  * Allocate volatile and non-volatile parse resources.
198d65f6f70SBen Gras  */
199d65f6f70SBen Gras struct mdoc *
mdoc_alloc(struct roff * roff,struct mparse * parse,char * defos)200*0a6a1f1dSLionel Sambuc mdoc_alloc(struct roff *roff, struct mparse *parse, char *defos)
201d65f6f70SBen Gras {
202d65f6f70SBen Gras 	struct mdoc	*p;
203d65f6f70SBen Gras 
204d65f6f70SBen Gras 	p = mandoc_calloc(1, sizeof(struct mdoc));
205d65f6f70SBen Gras 
20692395e9cSLionel Sambuc 	p->parse = parse;
207*0a6a1f1dSLionel Sambuc 	p->defos = defos;
20892395e9cSLionel Sambuc 	p->roff = roff;
209d65f6f70SBen Gras 
210d65f6f70SBen Gras 	mdoc_hash_init();
211d65f6f70SBen Gras 	mdoc_alloc1(p);
212d65f6f70SBen Gras 	return(p);
213d65f6f70SBen Gras }
214d65f6f70SBen Gras 
215d65f6f70SBen Gras 
216d65f6f70SBen Gras /*
217d65f6f70SBen Gras  * Climb back up the parse tree, validating open scopes.  Mostly calls
218d65f6f70SBen Gras  * through to macro_end() in macro.c.
219d65f6f70SBen Gras  */
220d65f6f70SBen Gras int
mdoc_endparse(struct mdoc * mdoc)221*0a6a1f1dSLionel Sambuc mdoc_endparse(struct mdoc *mdoc)
222d65f6f70SBen Gras {
223d65f6f70SBen Gras 
224*0a6a1f1dSLionel Sambuc 	assert( ! (MDOC_HALT & mdoc->flags));
225*0a6a1f1dSLionel Sambuc 	if (mdoc_macroend(mdoc))
226d65f6f70SBen Gras 		return(1);
227*0a6a1f1dSLionel Sambuc 	mdoc->flags |= MDOC_HALT;
228d65f6f70SBen Gras 	return(0);
229d65f6f70SBen Gras }
230d65f6f70SBen Gras 
231d65f6f70SBen Gras int
mdoc_addeqn(struct mdoc * mdoc,const struct eqn * ep)232*0a6a1f1dSLionel Sambuc mdoc_addeqn(struct mdoc *mdoc, const struct eqn *ep)
233d65f6f70SBen Gras {
23492395e9cSLionel Sambuc 	struct mdoc_node *n;
235d65f6f70SBen Gras 
236*0a6a1f1dSLionel Sambuc 	assert( ! (MDOC_HALT & mdoc->flags));
237d65f6f70SBen Gras 
238d65f6f70SBen Gras 	/* No text before an initial macro. */
239d65f6f70SBen Gras 
240*0a6a1f1dSLionel Sambuc 	if (SEC_NONE == mdoc->lastnamed) {
241*0a6a1f1dSLionel Sambuc 		mdoc_pmsg(mdoc, ep->ln, ep->pos, MANDOCERR_NOTEXT);
242d65f6f70SBen Gras 		return(1);
243d65f6f70SBen Gras 	}
244d65f6f70SBen Gras 
245*0a6a1f1dSLionel Sambuc 	n = node_alloc(mdoc, ep->ln, ep->pos, MDOC_MAX, MDOC_EQN);
24692395e9cSLionel Sambuc 	n->eqn = ep;
24792395e9cSLionel Sambuc 
248*0a6a1f1dSLionel Sambuc 	if ( ! node_append(mdoc, n))
24992395e9cSLionel Sambuc 		return(0);
25092395e9cSLionel Sambuc 
251*0a6a1f1dSLionel Sambuc 	mdoc->next = MDOC_NEXT_SIBLING;
25292395e9cSLionel Sambuc 	return(1);
25392395e9cSLionel Sambuc }
25492395e9cSLionel Sambuc 
25592395e9cSLionel Sambuc int
mdoc_addspan(struct mdoc * mdoc,const struct tbl_span * sp)256*0a6a1f1dSLionel Sambuc mdoc_addspan(struct mdoc *mdoc, const struct tbl_span *sp)
25792395e9cSLionel Sambuc {
25892395e9cSLionel Sambuc 	struct mdoc_node *n;
25992395e9cSLionel Sambuc 
260*0a6a1f1dSLionel Sambuc 	assert( ! (MDOC_HALT & mdoc->flags));
26192395e9cSLionel Sambuc 
26292395e9cSLionel Sambuc 	/* No text before an initial macro. */
26392395e9cSLionel Sambuc 
264*0a6a1f1dSLionel Sambuc 	if (SEC_NONE == mdoc->lastnamed) {
265*0a6a1f1dSLionel Sambuc 		mdoc_pmsg(mdoc, sp->line, 0, MANDOCERR_NOTEXT);
26692395e9cSLionel Sambuc 		return(1);
26792395e9cSLionel Sambuc 	}
26892395e9cSLionel Sambuc 
269*0a6a1f1dSLionel Sambuc 	n = node_alloc(mdoc, sp->line, 0, MDOC_MAX, MDOC_TBL);
27092395e9cSLionel Sambuc 	n->span = sp;
27192395e9cSLionel Sambuc 
272*0a6a1f1dSLionel Sambuc 	if ( ! node_append(mdoc, n))
27392395e9cSLionel Sambuc 		return(0);
27492395e9cSLionel Sambuc 
275*0a6a1f1dSLionel Sambuc 	mdoc->next = MDOC_NEXT_SIBLING;
27692395e9cSLionel Sambuc 	return(1);
277d65f6f70SBen Gras }
278d65f6f70SBen Gras 
279d65f6f70SBen Gras 
280d65f6f70SBen Gras /*
281d65f6f70SBen Gras  * Main parse routine.  Parses a single line -- really just hands off to
282d65f6f70SBen Gras  * the macro (mdoc_pmacro()) or text parser (mdoc_ptext()).
283d65f6f70SBen Gras  */
284d65f6f70SBen Gras int
mdoc_parseln(struct mdoc * mdoc,int ln,char * buf,int offs)285*0a6a1f1dSLionel Sambuc mdoc_parseln(struct mdoc *mdoc, int ln, char *buf, int offs)
286d65f6f70SBen Gras {
287d65f6f70SBen Gras 
288*0a6a1f1dSLionel Sambuc 	assert( ! (MDOC_HALT & mdoc->flags));
289d65f6f70SBen Gras 
290*0a6a1f1dSLionel Sambuc 	mdoc->flags |= MDOC_NEWLINE;
291d65f6f70SBen Gras 
292d65f6f70SBen Gras 	/*
293d65f6f70SBen Gras 	 * Let the roff nS register switch SYNOPSIS mode early,
294d65f6f70SBen Gras 	 * such that the parser knows at all times
295d65f6f70SBen Gras 	 * whether this mode is on or off.
296d65f6f70SBen Gras 	 * Note that this mode is also switched by the Sh macro.
297d65f6f70SBen Gras 	 */
298*0a6a1f1dSLionel Sambuc 	if (roff_getreg(mdoc->roff, "nS"))
299*0a6a1f1dSLionel Sambuc 		mdoc->flags |= MDOC_SYNOPSIS;
300d65f6f70SBen Gras 	else
301*0a6a1f1dSLionel Sambuc 		mdoc->flags &= ~MDOC_SYNOPSIS;
302d65f6f70SBen Gras 
303*0a6a1f1dSLionel Sambuc 	return(roff_getcontrol(mdoc->roff, buf, &offs) ?
304*0a6a1f1dSLionel Sambuc 			mdoc_pmacro(mdoc, ln, buf, offs) :
305*0a6a1f1dSLionel Sambuc 			mdoc_ptext(mdoc, ln, buf, offs));
306d65f6f70SBen Gras }
307d65f6f70SBen Gras 
308d65f6f70SBen Gras int
mdoc_macro(MACRO_PROT_ARGS)309d65f6f70SBen Gras mdoc_macro(MACRO_PROT_ARGS)
310d65f6f70SBen Gras {
311d65f6f70SBen Gras 	assert(tok < MDOC_MAX);
312d65f6f70SBen Gras 
313d65f6f70SBen Gras 	/* If we're in the body, deny prologue calls. */
314d65f6f70SBen Gras 
315d65f6f70SBen Gras 	if (MDOC_PROLOGUE & mdoc_macros[tok].flags &&
316*0a6a1f1dSLionel Sambuc 			MDOC_PBODY & mdoc->flags) {
317*0a6a1f1dSLionel Sambuc 		mdoc_pmsg(mdoc, line, ppos, MANDOCERR_BADBODY);
318d65f6f70SBen Gras 		return(1);
319d65f6f70SBen Gras 	}
320d65f6f70SBen Gras 
321d65f6f70SBen Gras 	/* If we're in the prologue, deny "body" macros.  */
322d65f6f70SBen Gras 
323d65f6f70SBen Gras 	if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) &&
324*0a6a1f1dSLionel Sambuc 			! (MDOC_PBODY & mdoc->flags)) {
325*0a6a1f1dSLionel Sambuc 		mdoc_pmsg(mdoc, line, ppos, MANDOCERR_BADPROLOG);
326*0a6a1f1dSLionel Sambuc 		if (NULL == mdoc->meta.msec)
327*0a6a1f1dSLionel Sambuc 			mdoc->meta.msec = mandoc_strdup("1");
328*0a6a1f1dSLionel Sambuc 		if (NULL == mdoc->meta.title)
329*0a6a1f1dSLionel Sambuc 			mdoc->meta.title = mandoc_strdup("UNKNOWN");
330*0a6a1f1dSLionel Sambuc 		if (NULL == mdoc->meta.vol)
331*0a6a1f1dSLionel Sambuc 			mdoc->meta.vol = mandoc_strdup("LOCAL");
332*0a6a1f1dSLionel Sambuc 		if (NULL == mdoc->meta.os)
333*0a6a1f1dSLionel Sambuc 			mdoc->meta.os = mandoc_strdup("LOCAL");
334*0a6a1f1dSLionel Sambuc 		if (NULL == mdoc->meta.date)
335*0a6a1f1dSLionel Sambuc 			mdoc->meta.date = mandoc_normdate
336*0a6a1f1dSLionel Sambuc 				(mdoc->parse, NULL, line, ppos);
337*0a6a1f1dSLionel Sambuc 		mdoc->flags |= MDOC_PBODY;
338d65f6f70SBen Gras 	}
339d65f6f70SBen Gras 
340*0a6a1f1dSLionel Sambuc 	return((*mdoc_macros[tok].fp)(mdoc, tok, line, ppos, pos, buf));
341d65f6f70SBen Gras }
342d65f6f70SBen Gras 
343d65f6f70SBen Gras 
344d65f6f70SBen Gras static int
node_append(struct mdoc * mdoc,struct mdoc_node * p)345d65f6f70SBen Gras node_append(struct mdoc *mdoc, struct mdoc_node *p)
346d65f6f70SBen Gras {
347d65f6f70SBen Gras 
348d65f6f70SBen Gras 	assert(mdoc->last);
349d65f6f70SBen Gras 	assert(mdoc->first);
350d65f6f70SBen Gras 	assert(MDOC_ROOT != p->type);
351d65f6f70SBen Gras 
352d65f6f70SBen Gras 	switch (mdoc->next) {
353d65f6f70SBen Gras 	case (MDOC_NEXT_SIBLING):
354d65f6f70SBen Gras 		mdoc->last->next = p;
355d65f6f70SBen Gras 		p->prev = mdoc->last;
356d65f6f70SBen Gras 		p->parent = mdoc->last->parent;
357d65f6f70SBen Gras 		break;
358d65f6f70SBen Gras 	case (MDOC_NEXT_CHILD):
359d65f6f70SBen Gras 		mdoc->last->child = p;
360d65f6f70SBen Gras 		p->parent = mdoc->last;
361d65f6f70SBen Gras 		break;
362d65f6f70SBen Gras 	default:
363d65f6f70SBen Gras 		abort();
364d65f6f70SBen Gras 		/* NOTREACHED */
365d65f6f70SBen Gras 	}
366d65f6f70SBen Gras 
367d65f6f70SBen Gras 	p->parent->nchild++;
368d65f6f70SBen Gras 
369d65f6f70SBen Gras 	/*
370d65f6f70SBen Gras 	 * Copy over the normalised-data pointer of our parent.  Not
371d65f6f70SBen Gras 	 * everybody has one, but copying a null pointer is fine.
372d65f6f70SBen Gras 	 */
373d65f6f70SBen Gras 
374d65f6f70SBen Gras 	switch (p->type) {
375d65f6f70SBen Gras 	case (MDOC_BODY):
376*0a6a1f1dSLionel Sambuc 		if (ENDBODY_NOT != p->end)
377*0a6a1f1dSLionel Sambuc 			break;
378d65f6f70SBen Gras 		/* FALLTHROUGH */
379d65f6f70SBen Gras 	case (MDOC_TAIL):
380d65f6f70SBen Gras 		/* FALLTHROUGH */
381d65f6f70SBen Gras 	case (MDOC_HEAD):
382d65f6f70SBen Gras 		p->norm = p->parent->norm;
383d65f6f70SBen Gras 		break;
384d65f6f70SBen Gras 	default:
385d65f6f70SBen Gras 		break;
386d65f6f70SBen Gras 	}
387d65f6f70SBen Gras 
388d65f6f70SBen Gras 	if ( ! mdoc_valid_pre(mdoc, p))
389d65f6f70SBen Gras 		return(0);
390d65f6f70SBen Gras 
391d65f6f70SBen Gras 	switch (p->type) {
392d65f6f70SBen Gras 	case (MDOC_HEAD):
393d65f6f70SBen Gras 		assert(MDOC_BLOCK == p->parent->type);
394d65f6f70SBen Gras 		p->parent->head = p;
395d65f6f70SBen Gras 		break;
396d65f6f70SBen Gras 	case (MDOC_TAIL):
397d65f6f70SBen Gras 		assert(MDOC_BLOCK == p->parent->type);
398d65f6f70SBen Gras 		p->parent->tail = p;
399d65f6f70SBen Gras 		break;
400d65f6f70SBen Gras 	case (MDOC_BODY):
401d65f6f70SBen Gras 		if (p->end)
402d65f6f70SBen Gras 			break;
403d65f6f70SBen Gras 		assert(MDOC_BLOCK == p->parent->type);
404d65f6f70SBen Gras 		p->parent->body = p;
405d65f6f70SBen Gras 		break;
406d65f6f70SBen Gras 	default:
407d65f6f70SBen Gras 		break;
408d65f6f70SBen Gras 	}
409d65f6f70SBen Gras 
410d65f6f70SBen Gras 	mdoc->last = p;
411d65f6f70SBen Gras 
412d65f6f70SBen Gras 	switch (p->type) {
413d65f6f70SBen Gras 	case (MDOC_TBL):
414d65f6f70SBen Gras 		/* FALLTHROUGH */
415d65f6f70SBen Gras 	case (MDOC_TEXT):
416d65f6f70SBen Gras 		if ( ! mdoc_valid_post(mdoc))
417d65f6f70SBen Gras 			return(0);
418d65f6f70SBen Gras 		break;
419d65f6f70SBen Gras 	default:
420d65f6f70SBen Gras 		break;
421d65f6f70SBen Gras 	}
422d65f6f70SBen Gras 
423d65f6f70SBen Gras 	return(1);
424d65f6f70SBen Gras }
425d65f6f70SBen Gras 
426d65f6f70SBen Gras 
427d65f6f70SBen Gras static struct mdoc_node *
node_alloc(struct mdoc * mdoc,int line,int pos,enum mdoct tok,enum mdoc_type type)428*0a6a1f1dSLionel Sambuc node_alloc(struct mdoc *mdoc, int line, int pos,
429d65f6f70SBen Gras 		enum mdoct tok, enum mdoc_type type)
430d65f6f70SBen Gras {
431d65f6f70SBen Gras 	struct mdoc_node *p;
432d65f6f70SBen Gras 
433d65f6f70SBen Gras 	p = mandoc_calloc(1, sizeof(struct mdoc_node));
434*0a6a1f1dSLionel Sambuc 	p->sec = mdoc->lastsec;
435d65f6f70SBen Gras 	p->line = line;
436d65f6f70SBen Gras 	p->pos = pos;
437*0a6a1f1dSLionel Sambuc 	p->lastline = line;
438d65f6f70SBen Gras 	p->tok = tok;
439d65f6f70SBen Gras 	p->type = type;
440d65f6f70SBen Gras 
441d65f6f70SBen Gras 	/* Flag analysis. */
442d65f6f70SBen Gras 
443*0a6a1f1dSLionel Sambuc 	if (MDOC_SYNOPSIS & mdoc->flags)
444d65f6f70SBen Gras 		p->flags |= MDOC_SYNPRETTY;
445d65f6f70SBen Gras 	else
446d65f6f70SBen Gras 		p->flags &= ~MDOC_SYNPRETTY;
447*0a6a1f1dSLionel Sambuc 	if (MDOC_NEWLINE & mdoc->flags)
448d65f6f70SBen Gras 		p->flags |= MDOC_LINE;
449*0a6a1f1dSLionel Sambuc 	mdoc->flags &= ~MDOC_NEWLINE;
450d65f6f70SBen Gras 
451d65f6f70SBen Gras 	return(p);
452d65f6f70SBen Gras }
453d65f6f70SBen Gras 
454d65f6f70SBen Gras 
455d65f6f70SBen Gras int
mdoc_tail_alloc(struct mdoc * mdoc,int line,int pos,enum mdoct tok)456*0a6a1f1dSLionel Sambuc mdoc_tail_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok)
457d65f6f70SBen Gras {
458d65f6f70SBen Gras 	struct mdoc_node *p;
459d65f6f70SBen Gras 
460*0a6a1f1dSLionel Sambuc 	p = node_alloc(mdoc, line, pos, tok, MDOC_TAIL);
461*0a6a1f1dSLionel Sambuc 	if ( ! node_append(mdoc, p))
462d65f6f70SBen Gras 		return(0);
463*0a6a1f1dSLionel Sambuc 	mdoc->next = MDOC_NEXT_CHILD;
464d65f6f70SBen Gras 	return(1);
465d65f6f70SBen Gras }
466d65f6f70SBen Gras 
467d65f6f70SBen Gras 
468d65f6f70SBen Gras int
mdoc_head_alloc(struct mdoc * mdoc,int line,int pos,enum mdoct tok)469*0a6a1f1dSLionel Sambuc mdoc_head_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok)
470d65f6f70SBen Gras {
471d65f6f70SBen Gras 	struct mdoc_node *p;
472d65f6f70SBen Gras 
473*0a6a1f1dSLionel Sambuc 	assert(mdoc->first);
474*0a6a1f1dSLionel Sambuc 	assert(mdoc->last);
475d65f6f70SBen Gras 
476*0a6a1f1dSLionel Sambuc 	p = node_alloc(mdoc, line, pos, tok, MDOC_HEAD);
477*0a6a1f1dSLionel Sambuc 	if ( ! node_append(mdoc, p))
478d65f6f70SBen Gras 		return(0);
479*0a6a1f1dSLionel Sambuc 	mdoc->next = MDOC_NEXT_CHILD;
480d65f6f70SBen Gras 	return(1);
481d65f6f70SBen Gras }
482d65f6f70SBen Gras 
483d65f6f70SBen Gras 
484d65f6f70SBen Gras int
mdoc_body_alloc(struct mdoc * mdoc,int line,int pos,enum mdoct tok)485*0a6a1f1dSLionel Sambuc mdoc_body_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok)
486d65f6f70SBen Gras {
487d65f6f70SBen Gras 	struct mdoc_node *p;
488d65f6f70SBen Gras 
489*0a6a1f1dSLionel Sambuc 	p = node_alloc(mdoc, line, pos, tok, MDOC_BODY);
490*0a6a1f1dSLionel Sambuc 	if ( ! node_append(mdoc, p))
491d65f6f70SBen Gras 		return(0);
492*0a6a1f1dSLionel Sambuc 	mdoc->next = MDOC_NEXT_CHILD;
493d65f6f70SBen Gras 	return(1);
494d65f6f70SBen Gras }
495d65f6f70SBen Gras 
496d65f6f70SBen Gras 
497d65f6f70SBen Gras int
mdoc_endbody_alloc(struct mdoc * mdoc,int line,int pos,enum mdoct tok,struct mdoc_node * body,enum mdoc_endbody end)498*0a6a1f1dSLionel Sambuc mdoc_endbody_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok,
499d65f6f70SBen Gras 		struct mdoc_node *body, enum mdoc_endbody end)
500d65f6f70SBen Gras {
501d65f6f70SBen Gras 	struct mdoc_node *p;
502d65f6f70SBen Gras 
503*0a6a1f1dSLionel Sambuc 	p = node_alloc(mdoc, line, pos, tok, MDOC_BODY);
504d65f6f70SBen Gras 	p->pending = body;
505*0a6a1f1dSLionel Sambuc 	p->norm = body->norm;
506d65f6f70SBen Gras 	p->end = end;
507*0a6a1f1dSLionel Sambuc 	if ( ! node_append(mdoc, p))
508d65f6f70SBen Gras 		return(0);
509*0a6a1f1dSLionel Sambuc 	mdoc->next = MDOC_NEXT_SIBLING;
510d65f6f70SBen Gras 	return(1);
511d65f6f70SBen Gras }
512d65f6f70SBen Gras 
513d65f6f70SBen Gras 
514d65f6f70SBen Gras int
mdoc_block_alloc(struct mdoc * mdoc,int line,int pos,enum mdoct tok,struct mdoc_arg * args)515*0a6a1f1dSLionel Sambuc mdoc_block_alloc(struct mdoc *mdoc, int line, int pos,
516d65f6f70SBen Gras 		enum mdoct tok, struct mdoc_arg *args)
517d65f6f70SBen Gras {
518d65f6f70SBen Gras 	struct mdoc_node *p;
519d65f6f70SBen Gras 
520*0a6a1f1dSLionel Sambuc 	p = node_alloc(mdoc, line, pos, tok, MDOC_BLOCK);
521d65f6f70SBen Gras 	p->args = args;
522d65f6f70SBen Gras 	if (p->args)
523d65f6f70SBen Gras 		(args->refcnt)++;
524d65f6f70SBen Gras 
525d65f6f70SBen Gras 	switch (tok) {
526d65f6f70SBen Gras 	case (MDOC_Bd):
527d65f6f70SBen Gras 		/* FALLTHROUGH */
528d65f6f70SBen Gras 	case (MDOC_Bf):
529d65f6f70SBen Gras 		/* FALLTHROUGH */
530d65f6f70SBen Gras 	case (MDOC_Bl):
531d65f6f70SBen Gras 		/* FALLTHROUGH */
532d65f6f70SBen Gras 	case (MDOC_Rs):
533d65f6f70SBen Gras 		p->norm = mandoc_calloc(1, sizeof(union mdoc_data));
534d65f6f70SBen Gras 		break;
535d65f6f70SBen Gras 	default:
536d65f6f70SBen Gras 		break;
537d65f6f70SBen Gras 	}
538d65f6f70SBen Gras 
539*0a6a1f1dSLionel Sambuc 	if ( ! node_append(mdoc, p))
540d65f6f70SBen Gras 		return(0);
541*0a6a1f1dSLionel Sambuc 	mdoc->next = MDOC_NEXT_CHILD;
542d65f6f70SBen Gras 	return(1);
543d65f6f70SBen Gras }
544d65f6f70SBen Gras 
545d65f6f70SBen Gras 
546d65f6f70SBen Gras int
mdoc_elem_alloc(struct mdoc * mdoc,int line,int pos,enum mdoct tok,struct mdoc_arg * args)547*0a6a1f1dSLionel Sambuc mdoc_elem_alloc(struct mdoc *mdoc, int line, int pos,
548d65f6f70SBen Gras 		enum mdoct tok, struct mdoc_arg *args)
549d65f6f70SBen Gras {
550d65f6f70SBen Gras 	struct mdoc_node *p;
551d65f6f70SBen Gras 
552*0a6a1f1dSLionel Sambuc 	p = node_alloc(mdoc, line, pos, tok, MDOC_ELEM);
553d65f6f70SBen Gras 	p->args = args;
554d65f6f70SBen Gras 	if (p->args)
555d65f6f70SBen Gras 		(args->refcnt)++;
556d65f6f70SBen Gras 
557d65f6f70SBen Gras 	switch (tok) {
558d65f6f70SBen Gras 	case (MDOC_An):
559d65f6f70SBen Gras 		p->norm = mandoc_calloc(1, sizeof(union mdoc_data));
560d65f6f70SBen Gras 		break;
561d65f6f70SBen Gras 	default:
562d65f6f70SBen Gras 		break;
563d65f6f70SBen Gras 	}
564d65f6f70SBen Gras 
565*0a6a1f1dSLionel Sambuc 	if ( ! node_append(mdoc, p))
566d65f6f70SBen Gras 		return(0);
567*0a6a1f1dSLionel Sambuc 	mdoc->next = MDOC_NEXT_CHILD;
568d65f6f70SBen Gras 	return(1);
569d65f6f70SBen Gras }
570d65f6f70SBen Gras 
571d65f6f70SBen Gras int
mdoc_word_alloc(struct mdoc * mdoc,int line,int pos,const char * p)572*0a6a1f1dSLionel Sambuc mdoc_word_alloc(struct mdoc *mdoc, int line, int pos, const char *p)
573d65f6f70SBen Gras {
574d65f6f70SBen Gras 	struct mdoc_node *n;
575d65f6f70SBen Gras 
576*0a6a1f1dSLionel Sambuc 	n = node_alloc(mdoc, line, pos, MDOC_MAX, MDOC_TEXT);
577*0a6a1f1dSLionel Sambuc 	n->string = roff_strdup(mdoc->roff, p);
578d65f6f70SBen Gras 
579*0a6a1f1dSLionel Sambuc 	if ( ! node_append(mdoc, n))
580d65f6f70SBen Gras 		return(0);
581d65f6f70SBen Gras 
582*0a6a1f1dSLionel Sambuc 	mdoc->next = MDOC_NEXT_SIBLING;
583d65f6f70SBen Gras 	return(1);
584d65f6f70SBen Gras }
585d65f6f70SBen Gras 
586*0a6a1f1dSLionel Sambuc void
mdoc_word_append(struct mdoc * mdoc,const char * p)587*0a6a1f1dSLionel Sambuc mdoc_word_append(struct mdoc *mdoc, const char *p)
588*0a6a1f1dSLionel Sambuc {
589*0a6a1f1dSLionel Sambuc 	struct mdoc_node	*n;
590*0a6a1f1dSLionel Sambuc 	char			*addstr, *newstr;
591*0a6a1f1dSLionel Sambuc 
592*0a6a1f1dSLionel Sambuc 	n = mdoc->last;
593*0a6a1f1dSLionel Sambuc 	addstr = roff_strdup(mdoc->roff, p);
594*0a6a1f1dSLionel Sambuc 	if (-1 == asprintf(&newstr, "%s %s", n->string, addstr)) {
595*0a6a1f1dSLionel Sambuc 		perror(NULL);
596*0a6a1f1dSLionel Sambuc 		exit((int)MANDOCLEVEL_SYSERR);
597*0a6a1f1dSLionel Sambuc 	}
598*0a6a1f1dSLionel Sambuc 	free(addstr);
599*0a6a1f1dSLionel Sambuc 	free(n->string);
600*0a6a1f1dSLionel Sambuc 	n->string = newstr;
601*0a6a1f1dSLionel Sambuc 	mdoc->next = MDOC_NEXT_SIBLING;
602*0a6a1f1dSLionel Sambuc }
603d65f6f70SBen Gras 
604d65f6f70SBen Gras static void
mdoc_node_free(struct mdoc_node * p)605d65f6f70SBen Gras mdoc_node_free(struct mdoc_node *p)
606d65f6f70SBen Gras {
607d65f6f70SBen Gras 
608d65f6f70SBen Gras 	if (MDOC_BLOCK == p->type || MDOC_ELEM == p->type)
609d65f6f70SBen Gras 		free(p->norm);
610d65f6f70SBen Gras 	if (p->string)
611d65f6f70SBen Gras 		free(p->string);
612d65f6f70SBen Gras 	if (p->args)
613d65f6f70SBen Gras 		mdoc_argv_free(p->args);
614d65f6f70SBen Gras 	free(p);
615d65f6f70SBen Gras }
616d65f6f70SBen Gras 
617d65f6f70SBen Gras 
618d65f6f70SBen Gras static void
mdoc_node_unlink(struct mdoc * mdoc,struct mdoc_node * n)619*0a6a1f1dSLionel Sambuc mdoc_node_unlink(struct mdoc *mdoc, struct mdoc_node *n)
620d65f6f70SBen Gras {
621d65f6f70SBen Gras 
622d65f6f70SBen Gras 	/* Adjust siblings. */
623d65f6f70SBen Gras 
624d65f6f70SBen Gras 	if (n->prev)
625d65f6f70SBen Gras 		n->prev->next = n->next;
626d65f6f70SBen Gras 	if (n->next)
627d65f6f70SBen Gras 		n->next->prev = n->prev;
628d65f6f70SBen Gras 
629d65f6f70SBen Gras 	/* Adjust parent. */
630d65f6f70SBen Gras 
631d65f6f70SBen Gras 	if (n->parent) {
632d65f6f70SBen Gras 		n->parent->nchild--;
633d65f6f70SBen Gras 		if (n->parent->child == n)
634d65f6f70SBen Gras 			n->parent->child = n->prev ? n->prev : n->next;
635d65f6f70SBen Gras 		if (n->parent->last == n)
636d65f6f70SBen Gras 			n->parent->last = n->prev ? n->prev : NULL;
637d65f6f70SBen Gras 	}
638d65f6f70SBen Gras 
639d65f6f70SBen Gras 	/* Adjust parse point, if applicable. */
640d65f6f70SBen Gras 
641*0a6a1f1dSLionel Sambuc 	if (mdoc && mdoc->last == n) {
642d65f6f70SBen Gras 		if (n->prev) {
643*0a6a1f1dSLionel Sambuc 			mdoc->last = n->prev;
644*0a6a1f1dSLionel Sambuc 			mdoc->next = MDOC_NEXT_SIBLING;
645d65f6f70SBen Gras 		} else {
646*0a6a1f1dSLionel Sambuc 			mdoc->last = n->parent;
647*0a6a1f1dSLionel Sambuc 			mdoc->next = MDOC_NEXT_CHILD;
648d65f6f70SBen Gras 		}
649d65f6f70SBen Gras 	}
650d65f6f70SBen Gras 
651*0a6a1f1dSLionel Sambuc 	if (mdoc && mdoc->first == n)
652*0a6a1f1dSLionel Sambuc 		mdoc->first = NULL;
653d65f6f70SBen Gras }
654d65f6f70SBen Gras 
655d65f6f70SBen Gras 
656d65f6f70SBen Gras void
mdoc_node_delete(struct mdoc * mdoc,struct mdoc_node * p)657*0a6a1f1dSLionel Sambuc mdoc_node_delete(struct mdoc *mdoc, struct mdoc_node *p)
658d65f6f70SBen Gras {
659d65f6f70SBen Gras 
660d65f6f70SBen Gras 	while (p->child) {
661d65f6f70SBen Gras 		assert(p->nchild);
662*0a6a1f1dSLionel Sambuc 		mdoc_node_delete(mdoc, p->child);
663d65f6f70SBen Gras 	}
664d65f6f70SBen Gras 	assert(0 == p->nchild);
665d65f6f70SBen Gras 
666*0a6a1f1dSLionel Sambuc 	mdoc_node_unlink(mdoc, p);
667d65f6f70SBen Gras 	mdoc_node_free(p);
668d65f6f70SBen Gras }
669d65f6f70SBen Gras 
670*0a6a1f1dSLionel Sambuc int
mdoc_node_relink(struct mdoc * mdoc,struct mdoc_node * p)671*0a6a1f1dSLionel Sambuc mdoc_node_relink(struct mdoc *mdoc, struct mdoc_node *p)
672*0a6a1f1dSLionel Sambuc {
673*0a6a1f1dSLionel Sambuc 
674*0a6a1f1dSLionel Sambuc 	mdoc_node_unlink(mdoc, p);
675*0a6a1f1dSLionel Sambuc 	return(node_append(mdoc, p));
676*0a6a1f1dSLionel Sambuc }
677*0a6a1f1dSLionel Sambuc 
67892395e9cSLionel Sambuc #if 0
67992395e9cSLionel Sambuc /*
68092395e9cSLionel Sambuc  * Pre-treat a text line.
68192395e9cSLionel Sambuc  * Text lines can consist of equations, which must be handled apart from
68292395e9cSLionel Sambuc  * the regular text.
68392395e9cSLionel Sambuc  * Thus, use this function to step through a line checking if it has any
68492395e9cSLionel Sambuc  * equations embedded in it.
68592395e9cSLionel Sambuc  * This must handle multiple equations AND equations that do not end at
68692395e9cSLionel Sambuc  * the end-of-line, i.e., will re-enter in the next roff parse.
68792395e9cSLionel Sambuc  */
68892395e9cSLionel Sambuc static int
689*0a6a1f1dSLionel Sambuc mdoc_preptext(struct mdoc *mdoc, int line, char *buf, int offs)
69092395e9cSLionel Sambuc {
69192395e9cSLionel Sambuc 	char		*start, *end;
69292395e9cSLionel Sambuc 	char		 delim;
69392395e9cSLionel Sambuc 
69492395e9cSLionel Sambuc 	while ('\0' != buf[offs]) {
69592395e9cSLionel Sambuc 		/* Mark starting position if eqn is set. */
69692395e9cSLionel Sambuc 		start = NULL;
697*0a6a1f1dSLionel Sambuc 		if ('\0' != (delim = roff_eqndelim(mdoc->roff)))
69892395e9cSLionel Sambuc 			if (NULL != (start = strchr(buf + offs, delim)))
69992395e9cSLionel Sambuc 				*start++ = '\0';
70092395e9cSLionel Sambuc 
70192395e9cSLionel Sambuc 		/* Parse text as normal. */
702*0a6a1f1dSLionel Sambuc 		if ( ! mdoc_ptext(mdoc, line, buf, offs))
70392395e9cSLionel Sambuc 			return(0);
70492395e9cSLionel Sambuc 
70592395e9cSLionel Sambuc 		/* Continue only if an equation exists. */
70692395e9cSLionel Sambuc 		if (NULL == start)
70792395e9cSLionel Sambuc 			break;
70892395e9cSLionel Sambuc 
70992395e9cSLionel Sambuc 		/* Read past the end of the equation. */
71092395e9cSLionel Sambuc 		offs += start - (buf + offs);
71192395e9cSLionel Sambuc 		assert(start == &buf[offs]);
71292395e9cSLionel Sambuc 		if (NULL != (end = strchr(buf + offs, delim))) {
71392395e9cSLionel Sambuc 			*end++ = '\0';
71492395e9cSLionel Sambuc 			while (' ' == *end)
71592395e9cSLionel Sambuc 				end++;
71692395e9cSLionel Sambuc 		}
71792395e9cSLionel Sambuc 
71892395e9cSLionel Sambuc 		/* Parse the equation itself. */
719*0a6a1f1dSLionel Sambuc 		roff_openeqn(mdoc->roff, NULL, line, offs, buf);
72092395e9cSLionel Sambuc 
72192395e9cSLionel Sambuc 		/* Process a finished equation? */
722*0a6a1f1dSLionel Sambuc 		if (roff_closeeqn(mdoc->roff))
723*0a6a1f1dSLionel Sambuc 			if ( ! mdoc_addeqn(mdoc, roff_eqn(mdoc->roff)))
72492395e9cSLionel Sambuc 				return(0);
72592395e9cSLionel Sambuc 		offs += (end - (buf + offs));
72692395e9cSLionel Sambuc 	}
72792395e9cSLionel Sambuc 
72892395e9cSLionel Sambuc 	return(1);
72992395e9cSLionel Sambuc }
73092395e9cSLionel Sambuc #endif
731d65f6f70SBen Gras 
732d65f6f70SBen Gras /*
733d65f6f70SBen Gras  * Parse free-form text, that is, a line that does not begin with the
734d65f6f70SBen Gras  * control character.
735d65f6f70SBen Gras  */
736d65f6f70SBen Gras static int
mdoc_ptext(struct mdoc * mdoc,int line,char * buf,int offs)737*0a6a1f1dSLionel Sambuc mdoc_ptext(struct mdoc *mdoc, int line, char *buf, int offs)
738d65f6f70SBen Gras {
739d65f6f70SBen Gras 	char		 *c, *ws, *end;
740d65f6f70SBen Gras 	struct mdoc_node *n;
741d65f6f70SBen Gras 
742d65f6f70SBen Gras 	/* No text before an initial macro. */
743d65f6f70SBen Gras 
744*0a6a1f1dSLionel Sambuc 	if (SEC_NONE == mdoc->lastnamed) {
745*0a6a1f1dSLionel Sambuc 		mdoc_pmsg(mdoc, line, offs, MANDOCERR_NOTEXT);
746d65f6f70SBen Gras 		return(1);
747d65f6f70SBen Gras 	}
748d65f6f70SBen Gras 
749*0a6a1f1dSLionel Sambuc 	assert(mdoc->last);
750*0a6a1f1dSLionel Sambuc 	n = mdoc->last;
751d65f6f70SBen Gras 
752d65f6f70SBen Gras 	/*
753d65f6f70SBen Gras 	 * Divert directly to list processing if we're encountering a
754d65f6f70SBen Gras 	 * columnar MDOC_BLOCK with or without a prior MDOC_BLOCK entry
755d65f6f70SBen Gras 	 * (a MDOC_BODY means it's already open, in which case we should
756d65f6f70SBen Gras 	 * process within its context in the normal way).
757d65f6f70SBen Gras 	 */
758d65f6f70SBen Gras 
759d65f6f70SBen Gras 	if (MDOC_Bl == n->tok && MDOC_BODY == n->type &&
760d65f6f70SBen Gras 			LIST_column == n->norm->Bl.type) {
761d65f6f70SBen Gras 		/* `Bl' is open without any children. */
762*0a6a1f1dSLionel Sambuc 		mdoc->flags |= MDOC_FREECOL;
763*0a6a1f1dSLionel Sambuc 		return(mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf));
764d65f6f70SBen Gras 	}
765d65f6f70SBen Gras 
766d65f6f70SBen Gras 	if (MDOC_It == n->tok && MDOC_BLOCK == n->type &&
767d65f6f70SBen Gras 			NULL != n->parent &&
768d65f6f70SBen Gras 			MDOC_Bl == n->parent->tok &&
769d65f6f70SBen Gras 			LIST_column == n->parent->norm->Bl.type) {
770d65f6f70SBen Gras 		/* `Bl' has block-level `It' children. */
771*0a6a1f1dSLionel Sambuc 		mdoc->flags |= MDOC_FREECOL;
772*0a6a1f1dSLionel Sambuc 		return(mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf));
773d65f6f70SBen Gras 	}
774d65f6f70SBen Gras 
775d65f6f70SBen Gras 	/*
776d65f6f70SBen Gras 	 * Search for the beginning of unescaped trailing whitespace (ws)
777d65f6f70SBen Gras 	 * and for the first character not to be output (end).
778d65f6f70SBen Gras 	 */
779d65f6f70SBen Gras 
780d65f6f70SBen Gras 	/* FIXME: replace with strcspn(). */
781d65f6f70SBen Gras 	ws = NULL;
782d65f6f70SBen Gras 	for (c = end = buf + offs; *c; c++) {
783d65f6f70SBen Gras 		switch (*c) {
784d65f6f70SBen Gras 		case ' ':
785d65f6f70SBen Gras 			if (NULL == ws)
786d65f6f70SBen Gras 				ws = c;
787d65f6f70SBen Gras 			continue;
788d65f6f70SBen Gras 		case '\t':
789d65f6f70SBen Gras 			/*
790d65f6f70SBen Gras 			 * Always warn about trailing tabs,
791d65f6f70SBen Gras 			 * even outside literal context,
792d65f6f70SBen Gras 			 * where they should be put on the next line.
793d65f6f70SBen Gras 			 */
794d65f6f70SBen Gras 			if (NULL == ws)
795d65f6f70SBen Gras 				ws = c;
796d65f6f70SBen Gras 			/*
797d65f6f70SBen Gras 			 * Strip trailing tabs in literal context only;
798d65f6f70SBen Gras 			 * outside, they affect the next line.
799d65f6f70SBen Gras 			 */
800*0a6a1f1dSLionel Sambuc 			if (MDOC_LITERAL & mdoc->flags)
801d65f6f70SBen Gras 				continue;
802d65f6f70SBen Gras 			break;
803d65f6f70SBen Gras 		case '\\':
804d65f6f70SBen Gras 			/* Skip the escaped character, too, if any. */
805d65f6f70SBen Gras 			if (c[1])
806d65f6f70SBen Gras 				c++;
807d65f6f70SBen Gras 			/* FALLTHROUGH */
808d65f6f70SBen Gras 		default:
809d65f6f70SBen Gras 			ws = NULL;
810d65f6f70SBen Gras 			break;
811d65f6f70SBen Gras 		}
812d65f6f70SBen Gras 		end = c + 1;
813d65f6f70SBen Gras 	}
814d65f6f70SBen Gras 	*end = '\0';
815d65f6f70SBen Gras 
816d65f6f70SBen Gras 	if (ws)
817*0a6a1f1dSLionel Sambuc 		mdoc_pmsg(mdoc, line, (int)(ws-buf), MANDOCERR_EOLNSPACE);
818d65f6f70SBen Gras 
819*0a6a1f1dSLionel Sambuc 	if ('\0' == buf[offs] && ! (MDOC_LITERAL & mdoc->flags)) {
820*0a6a1f1dSLionel Sambuc 		mdoc_pmsg(mdoc, line, (int)(c-buf), MANDOCERR_NOBLANKLN);
821d65f6f70SBen Gras 
822d65f6f70SBen Gras 		/*
823d65f6f70SBen Gras 		 * Insert a `sp' in the case of a blank line.  Technically,
824d65f6f70SBen Gras 		 * blank lines aren't allowed, but enough manuals assume this
825d65f6f70SBen Gras 		 * behaviour that we want to work around it.
826d65f6f70SBen Gras 		 */
827*0a6a1f1dSLionel Sambuc 		if ( ! mdoc_elem_alloc(mdoc, line, offs, MDOC_sp, NULL))
828d65f6f70SBen Gras 			return(0);
829d65f6f70SBen Gras 
830*0a6a1f1dSLionel Sambuc 		mdoc->next = MDOC_NEXT_SIBLING;
831*0a6a1f1dSLionel Sambuc 
832*0a6a1f1dSLionel Sambuc 		return(mdoc_valid_post(mdoc));
833d65f6f70SBen Gras 	}
834d65f6f70SBen Gras 
835*0a6a1f1dSLionel Sambuc 	if ( ! mdoc_word_alloc(mdoc, line, offs, buf+offs))
836d65f6f70SBen Gras 		return(0);
837d65f6f70SBen Gras 
838*0a6a1f1dSLionel Sambuc 	if (MDOC_LITERAL & mdoc->flags)
839d65f6f70SBen Gras 		return(1);
840d65f6f70SBen Gras 
841d65f6f70SBen Gras 	/*
842d65f6f70SBen Gras 	 * End-of-sentence check.  If the last character is an unescaped
843d65f6f70SBen Gras 	 * EOS character, then flag the node as being the end of a
844d65f6f70SBen Gras 	 * sentence.  The front-end will know how to interpret this.
845d65f6f70SBen Gras 	 */
846d65f6f70SBen Gras 
847d65f6f70SBen Gras 	assert(buf < end);
848d65f6f70SBen Gras 
849*0a6a1f1dSLionel Sambuc 	if (mandoc_eos(buf+offs, (size_t)(end-buf-offs)))
850*0a6a1f1dSLionel Sambuc 		mdoc->last->flags |= MDOC_EOS;
851d65f6f70SBen Gras 
852d65f6f70SBen Gras 	return(1);
853d65f6f70SBen Gras }
854d65f6f70SBen Gras 
855d65f6f70SBen Gras 
856d65f6f70SBen Gras /*
857d65f6f70SBen Gras  * Parse a macro line, that is, a line beginning with the control
858d65f6f70SBen Gras  * character.
859d65f6f70SBen Gras  */
860d65f6f70SBen Gras static int
mdoc_pmacro(struct mdoc * mdoc,int ln,char * buf,int offs)861*0a6a1f1dSLionel Sambuc mdoc_pmacro(struct mdoc *mdoc, int ln, char *buf, int offs)
862d65f6f70SBen Gras {
863d65f6f70SBen Gras 	enum mdoct	  tok;
86492395e9cSLionel Sambuc 	int		  i, sv;
865d65f6f70SBen Gras 	char		  mac[5];
866d65f6f70SBen Gras 	struct mdoc_node *n;
867d65f6f70SBen Gras 
86892395e9cSLionel Sambuc 	/* Empty post-control lines are ignored. */
869d65f6f70SBen Gras 
87092395e9cSLionel Sambuc 	if ('"' == buf[offs]) {
871*0a6a1f1dSLionel Sambuc 		mdoc_pmsg(mdoc, ln, offs, MANDOCERR_BADCOMMENT);
87292395e9cSLionel Sambuc 		return(1);
87392395e9cSLionel Sambuc 	} else if ('\0' == buf[offs])
874d65f6f70SBen Gras 		return(1);
875d65f6f70SBen Gras 
87692395e9cSLionel Sambuc 	sv = offs;
877d65f6f70SBen Gras 
878d65f6f70SBen Gras 	/*
879d65f6f70SBen Gras 	 * Copy the first word into a nil-terminated buffer.
880d65f6f70SBen Gras 	 * Stop copying when a tab, space, or eoln is encountered.
881d65f6f70SBen Gras 	 */
882d65f6f70SBen Gras 
88392395e9cSLionel Sambuc 	i = 0;
88492395e9cSLionel Sambuc 	while (i < 4 && '\0' != buf[offs] &&
88592395e9cSLionel Sambuc 			' ' != buf[offs] && '\t' != buf[offs])
88692395e9cSLionel Sambuc 		mac[i++] = buf[offs++];
887d65f6f70SBen Gras 
88892395e9cSLionel Sambuc 	mac[i] = '\0';
88992395e9cSLionel Sambuc 
89092395e9cSLionel Sambuc 	tok = (i > 1 || i < 4) ? mdoc_hash_find(mac) : MDOC_MAX;
89192395e9cSLionel Sambuc 
892d65f6f70SBen Gras 	if (MDOC_MAX == tok) {
893*0a6a1f1dSLionel Sambuc 		mandoc_vmsg(MANDOCERR_MACRO, mdoc->parse,
89492395e9cSLionel Sambuc 				ln, sv, "%s", buf + sv - 1);
895d65f6f70SBen Gras 		return(1);
896d65f6f70SBen Gras 	}
897d65f6f70SBen Gras 
898d65f6f70SBen Gras 	/* Disregard the first trailing tab, if applicable. */
899d65f6f70SBen Gras 
90092395e9cSLionel Sambuc 	if ('\t' == buf[offs])
90192395e9cSLionel Sambuc 		offs++;
902d65f6f70SBen Gras 
903d65f6f70SBen Gras 	/* Jump to the next non-whitespace word. */
904d65f6f70SBen Gras 
90592395e9cSLionel Sambuc 	while (buf[offs] && ' ' == buf[offs])
90692395e9cSLionel Sambuc 		offs++;
907d65f6f70SBen Gras 
908d65f6f70SBen Gras 	/*
909d65f6f70SBen Gras 	 * Trailing whitespace.  Note that tabs are allowed to be passed
910d65f6f70SBen Gras 	 * into the parser as "text", so we only warn about spaces here.
911d65f6f70SBen Gras 	 */
912d65f6f70SBen Gras 
91392395e9cSLionel Sambuc 	if ('\0' == buf[offs] && ' ' == buf[offs - 1])
914*0a6a1f1dSLionel Sambuc 		mdoc_pmsg(mdoc, ln, offs - 1, MANDOCERR_EOLNSPACE);
915d65f6f70SBen Gras 
916d65f6f70SBen Gras 	/*
917d65f6f70SBen Gras 	 * If an initial macro or a list invocation, divert directly
918d65f6f70SBen Gras 	 * into macro processing.
919d65f6f70SBen Gras 	 */
920d65f6f70SBen Gras 
921*0a6a1f1dSLionel Sambuc 	if (NULL == mdoc->last || MDOC_It == tok || MDOC_El == tok) {
922*0a6a1f1dSLionel Sambuc 		if ( ! mdoc_macro(mdoc, tok, ln, sv, &offs, buf))
923d65f6f70SBen Gras 			goto err;
924d65f6f70SBen Gras 		return(1);
925d65f6f70SBen Gras 	}
926d65f6f70SBen Gras 
927*0a6a1f1dSLionel Sambuc 	n = mdoc->last;
928*0a6a1f1dSLionel Sambuc 	assert(mdoc->last);
929d65f6f70SBen Gras 
930d65f6f70SBen Gras 	/*
931d65f6f70SBen Gras 	 * If the first macro of a `Bl -column', open an `It' block
932d65f6f70SBen Gras 	 * context around the parsed macro.
933d65f6f70SBen Gras 	 */
934d65f6f70SBen Gras 
935d65f6f70SBen Gras 	if (MDOC_Bl == n->tok && MDOC_BODY == n->type &&
936d65f6f70SBen Gras 			LIST_column == n->norm->Bl.type) {
937*0a6a1f1dSLionel Sambuc 		mdoc->flags |= MDOC_FREECOL;
938*0a6a1f1dSLionel Sambuc 		if ( ! mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf))
939d65f6f70SBen Gras 			goto err;
940d65f6f70SBen Gras 		return(1);
941d65f6f70SBen Gras 	}
942d65f6f70SBen Gras 
943d65f6f70SBen Gras 	/*
944d65f6f70SBen Gras 	 * If we're following a block-level `It' within a `Bl -column'
945d65f6f70SBen Gras 	 * context (perhaps opened in the above block or in ptext()),
946d65f6f70SBen Gras 	 * then open an `It' block context around the parsed macro.
947d65f6f70SBen Gras 	 */
948d65f6f70SBen Gras 
949d65f6f70SBen Gras 	if (MDOC_It == n->tok && MDOC_BLOCK == n->type &&
950d65f6f70SBen Gras 			NULL != n->parent &&
951d65f6f70SBen Gras 			MDOC_Bl == n->parent->tok &&
952d65f6f70SBen Gras 			LIST_column == n->parent->norm->Bl.type) {
953*0a6a1f1dSLionel Sambuc 		mdoc->flags |= MDOC_FREECOL;
954*0a6a1f1dSLionel Sambuc 		if ( ! mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf))
955d65f6f70SBen Gras 			goto err;
956d65f6f70SBen Gras 		return(1);
957d65f6f70SBen Gras 	}
958d65f6f70SBen Gras 
959d65f6f70SBen Gras 	/* Normal processing of a macro. */
960d65f6f70SBen Gras 
961*0a6a1f1dSLionel Sambuc 	if ( ! mdoc_macro(mdoc, tok, ln, sv, &offs, buf))
962d65f6f70SBen Gras 		goto err;
963d65f6f70SBen Gras 
964d65f6f70SBen Gras 	return(1);
965d65f6f70SBen Gras 
966d65f6f70SBen Gras err:	/* Error out. */
967d65f6f70SBen Gras 
968*0a6a1f1dSLionel Sambuc 	mdoc->flags |= MDOC_HALT;
969d65f6f70SBen Gras 	return(0);
970d65f6f70SBen Gras }
971d65f6f70SBen Gras 
97292395e9cSLionel Sambuc enum mdelim
mdoc_isdelim(const char * p)97392395e9cSLionel Sambuc mdoc_isdelim(const char *p)
97492395e9cSLionel Sambuc {
975d65f6f70SBen Gras 
97692395e9cSLionel Sambuc 	if ('\0' == p[0])
97792395e9cSLionel Sambuc 		return(DELIM_NONE);
97892395e9cSLionel Sambuc 
97992395e9cSLionel Sambuc 	if ('\0' == p[1])
98092395e9cSLionel Sambuc 		switch (p[0]) {
98192395e9cSLionel Sambuc 		case('('):
98292395e9cSLionel Sambuc 			/* FALLTHROUGH */
98392395e9cSLionel Sambuc 		case('['):
98492395e9cSLionel Sambuc 			return(DELIM_OPEN);
98592395e9cSLionel Sambuc 		case('|'):
98692395e9cSLionel Sambuc 			return(DELIM_MIDDLE);
98792395e9cSLionel Sambuc 		case('.'):
98892395e9cSLionel Sambuc 			/* FALLTHROUGH */
98992395e9cSLionel Sambuc 		case(','):
99092395e9cSLionel Sambuc 			/* FALLTHROUGH */
99192395e9cSLionel Sambuc 		case(';'):
99292395e9cSLionel Sambuc 			/* FALLTHROUGH */
99392395e9cSLionel Sambuc 		case(':'):
99492395e9cSLionel Sambuc 			/* FALLTHROUGH */
99592395e9cSLionel Sambuc 		case('?'):
99692395e9cSLionel Sambuc 			/* FALLTHROUGH */
99792395e9cSLionel Sambuc 		case('!'):
99892395e9cSLionel Sambuc 			/* FALLTHROUGH */
99992395e9cSLionel Sambuc 		case(')'):
100092395e9cSLionel Sambuc 			/* FALLTHROUGH */
100192395e9cSLionel Sambuc 		case(']'):
100292395e9cSLionel Sambuc 			return(DELIM_CLOSE);
100392395e9cSLionel Sambuc 		default:
100492395e9cSLionel Sambuc 			return(DELIM_NONE);
100592395e9cSLionel Sambuc 		}
100692395e9cSLionel Sambuc 
100792395e9cSLionel Sambuc 	if ('\\' != p[0])
100892395e9cSLionel Sambuc 		return(DELIM_NONE);
100992395e9cSLionel Sambuc 
101092395e9cSLionel Sambuc 	if (0 == strcmp(p + 1, "."))
101192395e9cSLionel Sambuc 		return(DELIM_CLOSE);
1012*0a6a1f1dSLionel Sambuc 	if (0 == strcmp(p + 1, "fR|\\fP"))
101392395e9cSLionel Sambuc 		return(DELIM_MIDDLE);
101492395e9cSLionel Sambuc 
101592395e9cSLionel Sambuc 	return(DELIM_NONE);
101692395e9cSLionel Sambuc }
1017