xref: /titanic_51/usr/src/cmd/mandoc/mdoc.c (revision 260e9a87725c090ba5835b1f9f0b62fa2f96036f)
1*260e9a87SYuri Pankov /*	$Id: mdoc.c,v 1.238 2015/02/12 13:00:52 schwarze Exp $ */
295c635efSGarrett D'Amore /*
395c635efSGarrett D'Amore  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4*260e9a87SYuri Pankov  * Copyright (c) 2010, 2012-2015 Ingo Schwarze <schwarze@openbsd.org>
595c635efSGarrett D'Amore  *
695c635efSGarrett D'Amore  * Permission to use, copy, modify, and distribute this software for any
795c635efSGarrett D'Amore  * purpose with or without fee is hereby granted, provided that the above
895c635efSGarrett D'Amore  * copyright notice and this permission notice appear in all copies.
995c635efSGarrett D'Amore  *
1095c635efSGarrett D'Amore  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1195c635efSGarrett D'Amore  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1295c635efSGarrett D'Amore  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1395c635efSGarrett D'Amore  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1495c635efSGarrett D'Amore  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1595c635efSGarrett D'Amore  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1695c635efSGarrett D'Amore  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1795c635efSGarrett D'Amore  */
1895c635efSGarrett D'Amore #include "config.h"
1995c635efSGarrett D'Amore 
2095c635efSGarrett D'Amore #include <sys/types.h>
2195c635efSGarrett D'Amore 
2295c635efSGarrett D'Amore #include <assert.h>
23*260e9a87SYuri Pankov #include <ctype.h>
2495c635efSGarrett D'Amore #include <stdarg.h>
2595c635efSGarrett D'Amore #include <stdio.h>
2695c635efSGarrett D'Amore #include <stdlib.h>
2795c635efSGarrett D'Amore #include <string.h>
2895c635efSGarrett D'Amore #include <time.h>
2995c635efSGarrett D'Amore 
3095c635efSGarrett D'Amore #include "mdoc.h"
3195c635efSGarrett D'Amore #include "mandoc.h"
32*260e9a87SYuri Pankov #include "mandoc_aux.h"
3395c635efSGarrett D'Amore #include "libmdoc.h"
3495c635efSGarrett D'Amore #include "libmandoc.h"
3595c635efSGarrett D'Amore 
36*260e9a87SYuri Pankov const	char *const __mdoc_macronames[MDOC_MAX + 1] = {
3795c635efSGarrett D'Amore 	"Ap",		"Dd",		"Dt",		"Os",
3895c635efSGarrett D'Amore 	"Sh",		"Ss",		"Pp",		"D1",
3995c635efSGarrett D'Amore 	"Dl",		"Bd",		"Ed",		"Bl",
4095c635efSGarrett D'Amore 	"El",		"It",		"Ad",		"An",
4195c635efSGarrett D'Amore 	"Ar",		"Cd",		"Cm",		"Dv",
4295c635efSGarrett D'Amore 	"Er",		"Ev",		"Ex",		"Fa",
4395c635efSGarrett D'Amore 	"Fd",		"Fl",		"Fn",		"Ft",
4495c635efSGarrett D'Amore 	"Ic",		"In",		"Li",		"Nd",
4595c635efSGarrett D'Amore 	"Nm",		"Op",		"Ot",		"Pa",
4695c635efSGarrett D'Amore 	"Rv",		"St",		"Va",		"Vt",
4795c635efSGarrett D'Amore 	"Xr",		"%A",		"%B",		"%D",
4895c635efSGarrett D'Amore 	"%I",		"%J",		"%N",		"%O",
4995c635efSGarrett D'Amore 	"%P",		"%R",		"%T",		"%V",
5095c635efSGarrett D'Amore 	"Ac",		"Ao",		"Aq",		"At",
5195c635efSGarrett D'Amore 	"Bc",		"Bf",		"Bo",		"Bq",
5295c635efSGarrett D'Amore 	"Bsx",		"Bx",		"Db",		"Dc",
5395c635efSGarrett D'Amore 	"Do",		"Dq",		"Ec",		"Ef",
5495c635efSGarrett D'Amore 	"Em",		"Eo",		"Fx",		"Ms",
5595c635efSGarrett D'Amore 	"No",		"Ns",		"Nx",		"Ox",
5695c635efSGarrett D'Amore 	"Pc",		"Pf",		"Po",		"Pq",
5795c635efSGarrett D'Amore 	"Qc",		"Ql",		"Qo",		"Qq",
5895c635efSGarrett D'Amore 	"Re",		"Rs",		"Sc",		"So",
5995c635efSGarrett D'Amore 	"Sq",		"Sm",		"Sx",		"Sy",
6095c635efSGarrett D'Amore 	"Tn",		"Ux",		"Xc",		"Xo",
6195c635efSGarrett D'Amore 	"Fo",		"Fc",		"Oo",		"Oc",
6295c635efSGarrett D'Amore 	"Bk",		"Ek",		"Bt",		"Hf",
6395c635efSGarrett D'Amore 	"Fr",		"Ud",		"Lb",		"Lp",
6495c635efSGarrett D'Amore 	"Lk",		"Mt",		"Brq",		"Bro",
6595c635efSGarrett D'Amore 	"Brc",		"%C",		"Es",		"En",
6695c635efSGarrett D'Amore 	"Dx",		"%Q",		"br",		"sp",
67*260e9a87SYuri Pankov 	"%U",		"Ta",		"ll",		"text",
6895c635efSGarrett D'Amore 	};
6995c635efSGarrett D'Amore 
7095c635efSGarrett D'Amore const	char *const __mdoc_argnames[MDOC_ARG_MAX] = {
7195c635efSGarrett D'Amore 	"split",		"nosplit",		"ragged",
7295c635efSGarrett D'Amore 	"unfilled",		"literal",		"file",
7395c635efSGarrett D'Amore 	"offset",		"bullet",		"dash",
7495c635efSGarrett D'Amore 	"hyphen",		"item",			"enum",
7595c635efSGarrett D'Amore 	"tag",			"diag",			"hang",
7695c635efSGarrett D'Amore 	"ohang",		"inset",		"column",
7795c635efSGarrett D'Amore 	"width",		"compact",		"std",
7895c635efSGarrett D'Amore 	"filled",		"words",		"emphasis",
7995c635efSGarrett D'Amore 	"symbolic",		"nested",		"centered"
8095c635efSGarrett D'Amore 	};
8195c635efSGarrett D'Amore 
8295c635efSGarrett D'Amore const	char * const *mdoc_macronames = __mdoc_macronames;
8395c635efSGarrett D'Amore const	char * const *mdoc_argnames = __mdoc_argnames;
8495c635efSGarrett D'Amore 
8595c635efSGarrett D'Amore static	void		  mdoc_node_free(struct mdoc_node *);
8695c635efSGarrett D'Amore static	void		  mdoc_node_unlink(struct mdoc *,
8795c635efSGarrett D'Amore 				struct mdoc_node *);
8895c635efSGarrett D'Amore static	void		  mdoc_free1(struct mdoc *);
8995c635efSGarrett D'Amore static	void		  mdoc_alloc1(struct mdoc *);
9095c635efSGarrett D'Amore static	struct mdoc_node *node_alloc(struct mdoc *, int, int,
9195c635efSGarrett D'Amore 				enum mdoct, enum mdoc_type);
92*260e9a87SYuri Pankov static	void		  node_append(struct mdoc *, struct mdoc_node *);
9395c635efSGarrett D'Amore static	int		  mdoc_ptext(struct mdoc *, int, char *, int);
9495c635efSGarrett D'Amore static	int		  mdoc_pmacro(struct mdoc *, int, char *, int);
9595c635efSGarrett D'Amore 
96*260e9a87SYuri Pankov 
9795c635efSGarrett D'Amore const struct mdoc_node *
98698f87a4SGarrett D'Amore mdoc_node(const struct mdoc *mdoc)
9995c635efSGarrett D'Amore {
10095c635efSGarrett D'Amore 
101698f87a4SGarrett D'Amore 	return(mdoc->first);
10295c635efSGarrett D'Amore }
10395c635efSGarrett D'Amore 
10495c635efSGarrett D'Amore const struct mdoc_meta *
105698f87a4SGarrett D'Amore mdoc_meta(const struct mdoc *mdoc)
10695c635efSGarrett D'Amore {
10795c635efSGarrett D'Amore 
108698f87a4SGarrett D'Amore 	return(&mdoc->meta);
10995c635efSGarrett D'Amore }
11095c635efSGarrett D'Amore 
11195c635efSGarrett D'Amore /*
11295c635efSGarrett D'Amore  * Frees volatile resources (parse tree, meta-data, fields).
11395c635efSGarrett D'Amore  */
11495c635efSGarrett D'Amore static void
11595c635efSGarrett D'Amore mdoc_free1(struct mdoc *mdoc)
11695c635efSGarrett D'Amore {
11795c635efSGarrett D'Amore 
11895c635efSGarrett D'Amore 	if (mdoc->first)
11995c635efSGarrett D'Amore 		mdoc_node_delete(mdoc, mdoc->first);
12095c635efSGarrett D'Amore 	free(mdoc->meta.msec);
121*260e9a87SYuri Pankov 	free(mdoc->meta.vol);
122*260e9a87SYuri Pankov 	free(mdoc->meta.arch);
12395c635efSGarrett D'Amore 	free(mdoc->meta.date);
124*260e9a87SYuri Pankov 	free(mdoc->meta.title);
125*260e9a87SYuri Pankov 	free(mdoc->meta.os);
126*260e9a87SYuri Pankov 	free(mdoc->meta.name);
12795c635efSGarrett D'Amore }
12895c635efSGarrett D'Amore 
12995c635efSGarrett D'Amore /*
13095c635efSGarrett D'Amore  * Allocate all volatile resources (parse tree, meta-data, fields).
13195c635efSGarrett D'Amore  */
13295c635efSGarrett D'Amore static void
13395c635efSGarrett D'Amore mdoc_alloc1(struct mdoc *mdoc)
13495c635efSGarrett D'Amore {
13595c635efSGarrett D'Amore 
13695c635efSGarrett D'Amore 	memset(&mdoc->meta, 0, sizeof(struct mdoc_meta));
13795c635efSGarrett D'Amore 	mdoc->flags = 0;
13895c635efSGarrett D'Amore 	mdoc->lastnamed = mdoc->lastsec = SEC_NONE;
13995c635efSGarrett D'Amore 	mdoc->last = mandoc_calloc(1, sizeof(struct mdoc_node));
14095c635efSGarrett D'Amore 	mdoc->first = mdoc->last;
14195c635efSGarrett D'Amore 	mdoc->last->type = MDOC_ROOT;
14295c635efSGarrett D'Amore 	mdoc->last->tok = MDOC_MAX;
14395c635efSGarrett D'Amore 	mdoc->next = MDOC_NEXT_CHILD;
14495c635efSGarrett D'Amore }
14595c635efSGarrett D'Amore 
14695c635efSGarrett D'Amore /*
14795c635efSGarrett D'Amore  * Free up volatile resources (see mdoc_free1()) then re-initialises the
14895c635efSGarrett D'Amore  * data with mdoc_alloc1().  After invocation, parse data has been reset
14995c635efSGarrett D'Amore  * and the parser is ready for re-invocation on a new tree; however,
15095c635efSGarrett D'Amore  * cross-parse non-volatile data is kept intact.
15195c635efSGarrett D'Amore  */
15295c635efSGarrett D'Amore void
15395c635efSGarrett D'Amore mdoc_reset(struct mdoc *mdoc)
15495c635efSGarrett D'Amore {
15595c635efSGarrett D'Amore 
15695c635efSGarrett D'Amore 	mdoc_free1(mdoc);
15795c635efSGarrett D'Amore 	mdoc_alloc1(mdoc);
15895c635efSGarrett D'Amore }
15995c635efSGarrett D'Amore 
16095c635efSGarrett D'Amore /*
16195c635efSGarrett D'Amore  * Completely free up all volatile and non-volatile parse resources.
16295c635efSGarrett D'Amore  * After invocation, the pointer is no longer usable.
16395c635efSGarrett D'Amore  */
16495c635efSGarrett D'Amore void
16595c635efSGarrett D'Amore mdoc_free(struct mdoc *mdoc)
16695c635efSGarrett D'Amore {
16795c635efSGarrett D'Amore 
16895c635efSGarrett D'Amore 	mdoc_free1(mdoc);
16995c635efSGarrett D'Amore 	free(mdoc);
17095c635efSGarrett D'Amore }
17195c635efSGarrett D'Amore 
17295c635efSGarrett D'Amore /*
17395c635efSGarrett D'Amore  * Allocate volatile and non-volatile parse resources.
17495c635efSGarrett D'Amore  */
17595c635efSGarrett D'Amore struct mdoc *
176*260e9a87SYuri Pankov mdoc_alloc(struct roff *roff, struct mparse *parse,
177*260e9a87SYuri Pankov 	const char *defos, int quick)
17895c635efSGarrett D'Amore {
17995c635efSGarrett D'Amore 	struct mdoc	*p;
18095c635efSGarrett D'Amore 
18195c635efSGarrett D'Amore 	p = mandoc_calloc(1, sizeof(struct mdoc));
18295c635efSGarrett D'Amore 
18395c635efSGarrett D'Amore 	p->parse = parse;
184698f87a4SGarrett D'Amore 	p->defos = defos;
185*260e9a87SYuri Pankov 	p->quick = quick;
18695c635efSGarrett D'Amore 	p->roff = roff;
18795c635efSGarrett D'Amore 
18895c635efSGarrett D'Amore 	mdoc_hash_init();
18995c635efSGarrett D'Amore 	mdoc_alloc1(p);
19095c635efSGarrett D'Amore 	return(p);
19195c635efSGarrett D'Amore }
19295c635efSGarrett D'Amore 
193*260e9a87SYuri Pankov void
194698f87a4SGarrett D'Amore mdoc_endparse(struct mdoc *mdoc)
19595c635efSGarrett D'Amore {
19695c635efSGarrett D'Amore 
197*260e9a87SYuri Pankov 	mdoc_macroend(mdoc);
19895c635efSGarrett D'Amore }
19995c635efSGarrett D'Amore 
200*260e9a87SYuri Pankov void
201698f87a4SGarrett D'Amore mdoc_addeqn(struct mdoc *mdoc, const struct eqn *ep)
20295c635efSGarrett D'Amore {
20395c635efSGarrett D'Amore 	struct mdoc_node *n;
20495c635efSGarrett D'Amore 
205698f87a4SGarrett D'Amore 	n = node_alloc(mdoc, ep->ln, ep->pos, MDOC_MAX, MDOC_EQN);
20695c635efSGarrett D'Amore 	n->eqn = ep;
207*260e9a87SYuri Pankov 	if (ep->ln > mdoc->last->line)
208*260e9a87SYuri Pankov 		n->flags |= MDOC_LINE;
209*260e9a87SYuri Pankov 	node_append(mdoc, n);
210698f87a4SGarrett D'Amore 	mdoc->next = MDOC_NEXT_SIBLING;
21195c635efSGarrett D'Amore }
21295c635efSGarrett D'Amore 
213*260e9a87SYuri Pankov void
214698f87a4SGarrett D'Amore mdoc_addspan(struct mdoc *mdoc, const struct tbl_span *sp)
21595c635efSGarrett D'Amore {
21695c635efSGarrett D'Amore 	struct mdoc_node *n;
21795c635efSGarrett D'Amore 
218698f87a4SGarrett D'Amore 	n = node_alloc(mdoc, sp->line, 0, MDOC_MAX, MDOC_TBL);
21995c635efSGarrett D'Amore 	n->span = sp;
220*260e9a87SYuri Pankov 	node_append(mdoc, n);
221698f87a4SGarrett D'Amore 	mdoc->next = MDOC_NEXT_SIBLING;
22295c635efSGarrett D'Amore }
22395c635efSGarrett D'Amore 
22495c635efSGarrett D'Amore /*
22595c635efSGarrett D'Amore  * Main parse routine.  Parses a single line -- really just hands off to
22695c635efSGarrett D'Amore  * the macro (mdoc_pmacro()) or text parser (mdoc_ptext()).
22795c635efSGarrett D'Amore  */
22895c635efSGarrett D'Amore int
229698f87a4SGarrett D'Amore mdoc_parseln(struct mdoc *mdoc, int ln, char *buf, int offs)
23095c635efSGarrett D'Amore {
23195c635efSGarrett D'Amore 
232*260e9a87SYuri Pankov 	if (mdoc->last->type != MDOC_EQN || ln > mdoc->last->line)
233698f87a4SGarrett D'Amore 		mdoc->flags |= MDOC_NEWLINE;
23495c635efSGarrett D'Amore 
23595c635efSGarrett D'Amore 	/*
23695c635efSGarrett D'Amore 	 * Let the roff nS register switch SYNOPSIS mode early,
23795c635efSGarrett D'Amore 	 * such that the parser knows at all times
23895c635efSGarrett D'Amore 	 * whether this mode is on or off.
23995c635efSGarrett D'Amore 	 * Note that this mode is also switched by the Sh macro.
24095c635efSGarrett D'Amore 	 */
241698f87a4SGarrett D'Amore 	if (roff_getreg(mdoc->roff, "nS"))
242698f87a4SGarrett D'Amore 		mdoc->flags |= MDOC_SYNOPSIS;
24395c635efSGarrett D'Amore 	else
244698f87a4SGarrett D'Amore 		mdoc->flags &= ~MDOC_SYNOPSIS;
24595c635efSGarrett D'Amore 
246698f87a4SGarrett D'Amore 	return(roff_getcontrol(mdoc->roff, buf, &offs) ?
247698f87a4SGarrett D'Amore 	    mdoc_pmacro(mdoc, ln, buf, offs) :
248698f87a4SGarrett D'Amore 	    mdoc_ptext(mdoc, ln, buf, offs));
24995c635efSGarrett D'Amore }
25095c635efSGarrett D'Amore 
251*260e9a87SYuri Pankov void
25295c635efSGarrett D'Amore mdoc_macro(MACRO_PROT_ARGS)
25395c635efSGarrett D'Amore {
25495c635efSGarrett D'Amore 	assert(tok < MDOC_MAX);
25595c635efSGarrett D'Amore 
256*260e9a87SYuri Pankov 	if (mdoc->flags & MDOC_PBODY) {
257*260e9a87SYuri Pankov 		if (tok == MDOC_Dt) {
258*260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_DT_LATE,
259*260e9a87SYuri Pankov 			    mdoc->parse, line, ppos,
260*260e9a87SYuri Pankov 			    "Dt %s", buf + *pos);
261*260e9a87SYuri Pankov 			return;
26295c635efSGarrett D'Amore 		}
263*260e9a87SYuri Pankov 	} else if ( ! (mdoc_macros[tok].flags & MDOC_PROLOGUE)) {
264*260e9a87SYuri Pankov 		if (mdoc->meta.title == NULL) {
265*260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_DT_NOTITLE,
266*260e9a87SYuri Pankov 			    mdoc->parse, line, ppos, "%s %s",
267*260e9a87SYuri Pankov 			    mdoc_macronames[tok], buf + *pos);
268*260e9a87SYuri Pankov 			mdoc->meta.title = mandoc_strdup("UNTITLED");
269*260e9a87SYuri Pankov 		}
270698f87a4SGarrett D'Amore 		if (NULL == mdoc->meta.vol)
271698f87a4SGarrett D'Amore 			mdoc->meta.vol = mandoc_strdup("LOCAL");
272698f87a4SGarrett D'Amore 		mdoc->flags |= MDOC_PBODY;
27395c635efSGarrett D'Amore 	}
274*260e9a87SYuri Pankov 	(*mdoc_macros[tok].fp)(mdoc, tok, line, ppos, pos, buf);
27595c635efSGarrett D'Amore }
27695c635efSGarrett D'Amore 
27795c635efSGarrett D'Amore 
278*260e9a87SYuri Pankov static void
27995c635efSGarrett D'Amore node_append(struct mdoc *mdoc, struct mdoc_node *p)
28095c635efSGarrett D'Amore {
28195c635efSGarrett D'Amore 
28295c635efSGarrett D'Amore 	assert(mdoc->last);
28395c635efSGarrett D'Amore 	assert(mdoc->first);
28495c635efSGarrett D'Amore 	assert(MDOC_ROOT != p->type);
28595c635efSGarrett D'Amore 
28695c635efSGarrett D'Amore 	switch (mdoc->next) {
287*260e9a87SYuri Pankov 	case MDOC_NEXT_SIBLING:
28895c635efSGarrett D'Amore 		mdoc->last->next = p;
28995c635efSGarrett D'Amore 		p->prev = mdoc->last;
29095c635efSGarrett D'Amore 		p->parent = mdoc->last->parent;
29195c635efSGarrett D'Amore 		break;
292*260e9a87SYuri Pankov 	case MDOC_NEXT_CHILD:
29395c635efSGarrett D'Amore 		mdoc->last->child = p;
29495c635efSGarrett D'Amore 		p->parent = mdoc->last;
29595c635efSGarrett D'Amore 		break;
29695c635efSGarrett D'Amore 	default:
29795c635efSGarrett D'Amore 		abort();
29895c635efSGarrett D'Amore 		/* NOTREACHED */
29995c635efSGarrett D'Amore 	}
30095c635efSGarrett D'Amore 
30195c635efSGarrett D'Amore 	p->parent->nchild++;
30295c635efSGarrett D'Amore 
30395c635efSGarrett D'Amore 	/*
30495c635efSGarrett D'Amore 	 * Copy over the normalised-data pointer of our parent.  Not
30595c635efSGarrett D'Amore 	 * everybody has one, but copying a null pointer is fine.
30695c635efSGarrett D'Amore 	 */
30795c635efSGarrett D'Amore 
30895c635efSGarrett D'Amore 	switch (p->type) {
309*260e9a87SYuri Pankov 	case MDOC_BODY:
310698f87a4SGarrett D'Amore 		if (ENDBODY_NOT != p->end)
311698f87a4SGarrett D'Amore 			break;
31295c635efSGarrett D'Amore 		/* FALLTHROUGH */
313*260e9a87SYuri Pankov 	case MDOC_TAIL:
31495c635efSGarrett D'Amore 		/* FALLTHROUGH */
315*260e9a87SYuri Pankov 	case MDOC_HEAD:
31695c635efSGarrett D'Amore 		p->norm = p->parent->norm;
31795c635efSGarrett D'Amore 		break;
31895c635efSGarrett D'Amore 	default:
31995c635efSGarrett D'Amore 		break;
32095c635efSGarrett D'Amore 	}
32195c635efSGarrett D'Amore 
322*260e9a87SYuri Pankov 	mdoc_valid_pre(mdoc, p);
32395c635efSGarrett D'Amore 
32495c635efSGarrett D'Amore 	switch (p->type) {
325*260e9a87SYuri Pankov 	case MDOC_HEAD:
32695c635efSGarrett D'Amore 		assert(MDOC_BLOCK == p->parent->type);
32795c635efSGarrett D'Amore 		p->parent->head = p;
32895c635efSGarrett D'Amore 		break;
329*260e9a87SYuri Pankov 	case MDOC_TAIL:
33095c635efSGarrett D'Amore 		assert(MDOC_BLOCK == p->parent->type);
33195c635efSGarrett D'Amore 		p->parent->tail = p;
33295c635efSGarrett D'Amore 		break;
333*260e9a87SYuri Pankov 	case MDOC_BODY:
33495c635efSGarrett D'Amore 		if (p->end)
33595c635efSGarrett D'Amore 			break;
33695c635efSGarrett D'Amore 		assert(MDOC_BLOCK == p->parent->type);
33795c635efSGarrett D'Amore 		p->parent->body = p;
33895c635efSGarrett D'Amore 		break;
33995c635efSGarrett D'Amore 	default:
34095c635efSGarrett D'Amore 		break;
34195c635efSGarrett D'Amore 	}
34295c635efSGarrett D'Amore 
34395c635efSGarrett D'Amore 	mdoc->last = p;
34495c635efSGarrett D'Amore 
34595c635efSGarrett D'Amore 	switch (p->type) {
346*260e9a87SYuri Pankov 	case MDOC_TBL:
34795c635efSGarrett D'Amore 		/* FALLTHROUGH */
348*260e9a87SYuri Pankov 	case MDOC_TEXT:
349*260e9a87SYuri Pankov 		mdoc_valid_post(mdoc);
35095c635efSGarrett D'Amore 		break;
35195c635efSGarrett D'Amore 	default:
35295c635efSGarrett D'Amore 		break;
35395c635efSGarrett D'Amore 	}
35495c635efSGarrett D'Amore }
35595c635efSGarrett D'Amore 
35695c635efSGarrett D'Amore static struct mdoc_node *
357698f87a4SGarrett D'Amore node_alloc(struct mdoc *mdoc, int line, int pos,
35895c635efSGarrett D'Amore 		enum mdoct tok, enum mdoc_type type)
35995c635efSGarrett D'Amore {
36095c635efSGarrett D'Amore 	struct mdoc_node *p;
36195c635efSGarrett D'Amore 
36295c635efSGarrett D'Amore 	p = mandoc_calloc(1, sizeof(struct mdoc_node));
363698f87a4SGarrett D'Amore 	p->sec = mdoc->lastsec;
36495c635efSGarrett D'Amore 	p->line = line;
36595c635efSGarrett D'Amore 	p->pos = pos;
36695c635efSGarrett D'Amore 	p->tok = tok;
36795c635efSGarrett D'Amore 	p->type = type;
36895c635efSGarrett D'Amore 
36995c635efSGarrett D'Amore 	/* Flag analysis. */
37095c635efSGarrett D'Amore 
371698f87a4SGarrett D'Amore 	if (MDOC_SYNOPSIS & mdoc->flags)
37295c635efSGarrett D'Amore 		p->flags |= MDOC_SYNPRETTY;
37395c635efSGarrett D'Amore 	else
37495c635efSGarrett D'Amore 		p->flags &= ~MDOC_SYNPRETTY;
375698f87a4SGarrett D'Amore 	if (MDOC_NEWLINE & mdoc->flags)
37695c635efSGarrett D'Amore 		p->flags |= MDOC_LINE;
377698f87a4SGarrett D'Amore 	mdoc->flags &= ~MDOC_NEWLINE;
37895c635efSGarrett D'Amore 
37995c635efSGarrett D'Amore 	return(p);
38095c635efSGarrett D'Amore }
38195c635efSGarrett D'Amore 
382*260e9a87SYuri Pankov void
383698f87a4SGarrett D'Amore mdoc_tail_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok)
38495c635efSGarrett D'Amore {
38595c635efSGarrett D'Amore 	struct mdoc_node *p;
38695c635efSGarrett D'Amore 
387698f87a4SGarrett D'Amore 	p = node_alloc(mdoc, line, pos, tok, MDOC_TAIL);
388*260e9a87SYuri Pankov 	node_append(mdoc, p);
389698f87a4SGarrett D'Amore 	mdoc->next = MDOC_NEXT_CHILD;
39095c635efSGarrett D'Amore }
39195c635efSGarrett D'Amore 
392*260e9a87SYuri Pankov struct mdoc_node *
393698f87a4SGarrett D'Amore mdoc_head_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok)
39495c635efSGarrett D'Amore {
39595c635efSGarrett D'Amore 	struct mdoc_node *p;
39695c635efSGarrett D'Amore 
397698f87a4SGarrett D'Amore 	assert(mdoc->first);
398698f87a4SGarrett D'Amore 	assert(mdoc->last);
399698f87a4SGarrett D'Amore 	p = node_alloc(mdoc, line, pos, tok, MDOC_HEAD);
400*260e9a87SYuri Pankov 	node_append(mdoc, p);
401698f87a4SGarrett D'Amore 	mdoc->next = MDOC_NEXT_CHILD;
402*260e9a87SYuri Pankov 	return(p);
40395c635efSGarrett D'Amore }
40495c635efSGarrett D'Amore 
405*260e9a87SYuri Pankov struct mdoc_node *
406698f87a4SGarrett D'Amore mdoc_body_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok)
40795c635efSGarrett D'Amore {
40895c635efSGarrett D'Amore 	struct mdoc_node *p;
40995c635efSGarrett D'Amore 
410698f87a4SGarrett D'Amore 	p = node_alloc(mdoc, line, pos, tok, MDOC_BODY);
411*260e9a87SYuri Pankov 	node_append(mdoc, p);
412698f87a4SGarrett D'Amore 	mdoc->next = MDOC_NEXT_CHILD;
413*260e9a87SYuri Pankov 	return(p);
41495c635efSGarrett D'Amore }
41595c635efSGarrett D'Amore 
416*260e9a87SYuri Pankov struct mdoc_node *
417698f87a4SGarrett D'Amore mdoc_endbody_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok,
41895c635efSGarrett D'Amore 		struct mdoc_node *body, enum mdoc_endbody end)
41995c635efSGarrett D'Amore {
42095c635efSGarrett D'Amore 	struct mdoc_node *p;
42195c635efSGarrett D'Amore 
422*260e9a87SYuri Pankov 	body->flags |= MDOC_ENDED;
423*260e9a87SYuri Pankov 	body->parent->flags |= MDOC_ENDED;
424698f87a4SGarrett D'Amore 	p = node_alloc(mdoc, line, pos, tok, MDOC_BODY);
425*260e9a87SYuri Pankov 	p->body = body;
426698f87a4SGarrett D'Amore 	p->norm = body->norm;
42795c635efSGarrett D'Amore 	p->end = end;
428*260e9a87SYuri Pankov 	node_append(mdoc, p);
429698f87a4SGarrett D'Amore 	mdoc->next = MDOC_NEXT_SIBLING;
430*260e9a87SYuri Pankov 	return(p);
43195c635efSGarrett D'Amore }
43295c635efSGarrett D'Amore 
433*260e9a87SYuri Pankov struct mdoc_node *
434698f87a4SGarrett D'Amore mdoc_block_alloc(struct mdoc *mdoc, int line, int pos,
43595c635efSGarrett D'Amore 		enum mdoct tok, struct mdoc_arg *args)
43695c635efSGarrett D'Amore {
43795c635efSGarrett D'Amore 	struct mdoc_node *p;
43895c635efSGarrett D'Amore 
439698f87a4SGarrett D'Amore 	p = node_alloc(mdoc, line, pos, tok, MDOC_BLOCK);
44095c635efSGarrett D'Amore 	p->args = args;
44195c635efSGarrett D'Amore 	if (p->args)
44295c635efSGarrett D'Amore 		(args->refcnt)++;
44395c635efSGarrett D'Amore 
44495c635efSGarrett D'Amore 	switch (tok) {
445*260e9a87SYuri Pankov 	case MDOC_Bd:
44695c635efSGarrett D'Amore 		/* FALLTHROUGH */
447*260e9a87SYuri Pankov 	case MDOC_Bf:
44895c635efSGarrett D'Amore 		/* FALLTHROUGH */
449*260e9a87SYuri Pankov 	case MDOC_Bl:
45095c635efSGarrett D'Amore 		/* FALLTHROUGH */
451*260e9a87SYuri Pankov 	case MDOC_En:
452*260e9a87SYuri Pankov 		/* FALLTHROUGH */
453*260e9a87SYuri Pankov 	case MDOC_Rs:
45495c635efSGarrett D'Amore 		p->norm = mandoc_calloc(1, sizeof(union mdoc_data));
45595c635efSGarrett D'Amore 		break;
45695c635efSGarrett D'Amore 	default:
45795c635efSGarrett D'Amore 		break;
45895c635efSGarrett D'Amore 	}
459*260e9a87SYuri Pankov 	node_append(mdoc, p);
460698f87a4SGarrett D'Amore 	mdoc->next = MDOC_NEXT_CHILD;
461*260e9a87SYuri Pankov 	return(p);
46295c635efSGarrett D'Amore }
46395c635efSGarrett D'Amore 
464*260e9a87SYuri Pankov void
465698f87a4SGarrett D'Amore mdoc_elem_alloc(struct mdoc *mdoc, int line, int pos,
46695c635efSGarrett D'Amore 		enum mdoct tok, struct mdoc_arg *args)
46795c635efSGarrett D'Amore {
46895c635efSGarrett D'Amore 	struct mdoc_node *p;
46995c635efSGarrett D'Amore 
470698f87a4SGarrett D'Amore 	p = node_alloc(mdoc, line, pos, tok, MDOC_ELEM);
47195c635efSGarrett D'Amore 	p->args = args;
47295c635efSGarrett D'Amore 	if (p->args)
47395c635efSGarrett D'Amore 		(args->refcnt)++;
47495c635efSGarrett D'Amore 
47595c635efSGarrett D'Amore 	switch (tok) {
476*260e9a87SYuri Pankov 	case MDOC_An:
47795c635efSGarrett D'Amore 		p->norm = mandoc_calloc(1, sizeof(union mdoc_data));
47895c635efSGarrett D'Amore 		break;
47995c635efSGarrett D'Amore 	default:
48095c635efSGarrett D'Amore 		break;
48195c635efSGarrett D'Amore 	}
482*260e9a87SYuri Pankov 	node_append(mdoc, p);
483698f87a4SGarrett D'Amore 	mdoc->next = MDOC_NEXT_CHILD;
48495c635efSGarrett D'Amore }
48595c635efSGarrett D'Amore 
486*260e9a87SYuri Pankov void
487698f87a4SGarrett D'Amore mdoc_word_alloc(struct mdoc *mdoc, int line, int pos, const char *p)
48895c635efSGarrett D'Amore {
48995c635efSGarrett D'Amore 	struct mdoc_node *n;
49095c635efSGarrett D'Amore 
491698f87a4SGarrett D'Amore 	n = node_alloc(mdoc, line, pos, MDOC_MAX, MDOC_TEXT);
492698f87a4SGarrett D'Amore 	n->string = roff_strdup(mdoc->roff, p);
493*260e9a87SYuri Pankov 	node_append(mdoc, n);
494698f87a4SGarrett D'Amore 	mdoc->next = MDOC_NEXT_SIBLING;
49595c635efSGarrett D'Amore }
49695c635efSGarrett D'Amore 
497698f87a4SGarrett D'Amore void
498698f87a4SGarrett D'Amore mdoc_word_append(struct mdoc *mdoc, const char *p)
499698f87a4SGarrett D'Amore {
500698f87a4SGarrett D'Amore 	struct mdoc_node	*n;
501698f87a4SGarrett D'Amore 	char			*addstr, *newstr;
502698f87a4SGarrett D'Amore 
503698f87a4SGarrett D'Amore 	n = mdoc->last;
504698f87a4SGarrett D'Amore 	addstr = roff_strdup(mdoc->roff, p);
505*260e9a87SYuri Pankov 	mandoc_asprintf(&newstr, "%s %s", n->string, addstr);
506698f87a4SGarrett D'Amore 	free(addstr);
507698f87a4SGarrett D'Amore 	free(n->string);
508698f87a4SGarrett D'Amore 	n->string = newstr;
509698f87a4SGarrett D'Amore 	mdoc->next = MDOC_NEXT_SIBLING;
510698f87a4SGarrett D'Amore }
51195c635efSGarrett D'Amore 
51295c635efSGarrett D'Amore static void
51395c635efSGarrett D'Amore mdoc_node_free(struct mdoc_node *p)
51495c635efSGarrett D'Amore {
51595c635efSGarrett D'Amore 
51695c635efSGarrett D'Amore 	if (MDOC_BLOCK == p->type || MDOC_ELEM == p->type)
51795c635efSGarrett D'Amore 		free(p->norm);
51895c635efSGarrett D'Amore 	if (p->string)
51995c635efSGarrett D'Amore 		free(p->string);
52095c635efSGarrett D'Amore 	if (p->args)
52195c635efSGarrett D'Amore 		mdoc_argv_free(p->args);
52295c635efSGarrett D'Amore 	free(p);
52395c635efSGarrett D'Amore }
52495c635efSGarrett D'Amore 
52595c635efSGarrett D'Amore static void
526698f87a4SGarrett D'Amore mdoc_node_unlink(struct mdoc *mdoc, struct mdoc_node *n)
52795c635efSGarrett D'Amore {
52895c635efSGarrett D'Amore 
52995c635efSGarrett D'Amore 	/* Adjust siblings. */
53095c635efSGarrett D'Amore 
53195c635efSGarrett D'Amore 	if (n->prev)
53295c635efSGarrett D'Amore 		n->prev->next = n->next;
53395c635efSGarrett D'Amore 	if (n->next)
53495c635efSGarrett D'Amore 		n->next->prev = n->prev;
53595c635efSGarrett D'Amore 
53695c635efSGarrett D'Amore 	/* Adjust parent. */
53795c635efSGarrett D'Amore 
53895c635efSGarrett D'Amore 	if (n->parent) {
53995c635efSGarrett D'Amore 		n->parent->nchild--;
54095c635efSGarrett D'Amore 		if (n->parent->child == n)
54195c635efSGarrett D'Amore 			n->parent->child = n->prev ? n->prev : n->next;
54295c635efSGarrett D'Amore 		if (n->parent->last == n)
54395c635efSGarrett D'Amore 			n->parent->last = n->prev ? n->prev : NULL;
54495c635efSGarrett D'Amore 	}
54595c635efSGarrett D'Amore 
54695c635efSGarrett D'Amore 	/* Adjust parse point, if applicable. */
54795c635efSGarrett D'Amore 
548698f87a4SGarrett D'Amore 	if (mdoc && mdoc->last == n) {
54995c635efSGarrett D'Amore 		if (n->prev) {
550698f87a4SGarrett D'Amore 			mdoc->last = n->prev;
551698f87a4SGarrett D'Amore 			mdoc->next = MDOC_NEXT_SIBLING;
55295c635efSGarrett D'Amore 		} else {
553698f87a4SGarrett D'Amore 			mdoc->last = n->parent;
554698f87a4SGarrett D'Amore 			mdoc->next = MDOC_NEXT_CHILD;
55595c635efSGarrett D'Amore 		}
55695c635efSGarrett D'Amore 	}
55795c635efSGarrett D'Amore 
558698f87a4SGarrett D'Amore 	if (mdoc && mdoc->first == n)
559698f87a4SGarrett D'Amore 		mdoc->first = NULL;
56095c635efSGarrett D'Amore }
56195c635efSGarrett D'Amore 
56295c635efSGarrett D'Amore void
563698f87a4SGarrett D'Amore mdoc_node_delete(struct mdoc *mdoc, struct mdoc_node *p)
56495c635efSGarrett D'Amore {
56595c635efSGarrett D'Amore 
56695c635efSGarrett D'Amore 	while (p->child) {
56795c635efSGarrett D'Amore 		assert(p->nchild);
568698f87a4SGarrett D'Amore 		mdoc_node_delete(mdoc, p->child);
56995c635efSGarrett D'Amore 	}
57095c635efSGarrett D'Amore 	assert(0 == p->nchild);
57195c635efSGarrett D'Amore 
572698f87a4SGarrett D'Amore 	mdoc_node_unlink(mdoc, p);
57395c635efSGarrett D'Amore 	mdoc_node_free(p);
57495c635efSGarrett D'Amore }
57595c635efSGarrett D'Amore 
576*260e9a87SYuri Pankov void
577698f87a4SGarrett D'Amore mdoc_node_relink(struct mdoc *mdoc, struct mdoc_node *p)
578698f87a4SGarrett D'Amore {
579698f87a4SGarrett D'Amore 
580698f87a4SGarrett D'Amore 	mdoc_node_unlink(mdoc, p);
581*260e9a87SYuri Pankov 	node_append(mdoc, p);
582698f87a4SGarrett D'Amore }
583698f87a4SGarrett D'Amore 
58495c635efSGarrett D'Amore /*
58595c635efSGarrett D'Amore  * Parse free-form text, that is, a line that does not begin with the
58695c635efSGarrett D'Amore  * control character.
58795c635efSGarrett D'Amore  */
58895c635efSGarrett D'Amore static int
589698f87a4SGarrett D'Amore mdoc_ptext(struct mdoc *mdoc, int line, char *buf, int offs)
59095c635efSGarrett D'Amore {
59195c635efSGarrett D'Amore 	char		 *c, *ws, *end;
59295c635efSGarrett D'Amore 	struct mdoc_node *n;
59395c635efSGarrett D'Amore 
594698f87a4SGarrett D'Amore 	assert(mdoc->last);
595698f87a4SGarrett D'Amore 	n = mdoc->last;
59695c635efSGarrett D'Amore 
59795c635efSGarrett D'Amore 	/*
59895c635efSGarrett D'Amore 	 * Divert directly to list processing if we're encountering a
59995c635efSGarrett D'Amore 	 * columnar MDOC_BLOCK with or without a prior MDOC_BLOCK entry
60095c635efSGarrett D'Amore 	 * (a MDOC_BODY means it's already open, in which case we should
60195c635efSGarrett D'Amore 	 * process within its context in the normal way).
60295c635efSGarrett D'Amore 	 */
60395c635efSGarrett D'Amore 
604*260e9a87SYuri Pankov 	if (n->tok == MDOC_Bl && n->type == MDOC_BODY &&
605*260e9a87SYuri Pankov 	    n->end == ENDBODY_NOT && n->norm->Bl.type == LIST_column) {
60695c635efSGarrett D'Amore 		/* `Bl' is open without any children. */
607698f87a4SGarrett D'Amore 		mdoc->flags |= MDOC_FREECOL;
608*260e9a87SYuri Pankov 		mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf);
609*260e9a87SYuri Pankov 		return(1);
61095c635efSGarrett D'Amore 	}
61195c635efSGarrett D'Amore 
61295c635efSGarrett D'Amore 	if (MDOC_It == n->tok && MDOC_BLOCK == n->type &&
61395c635efSGarrett D'Amore 	    NULL != n->parent &&
61495c635efSGarrett D'Amore 	    MDOC_Bl == n->parent->tok &&
61595c635efSGarrett D'Amore 	    LIST_column == n->parent->norm->Bl.type) {
61695c635efSGarrett D'Amore 		/* `Bl' has block-level `It' children. */
617698f87a4SGarrett D'Amore 		mdoc->flags |= MDOC_FREECOL;
618*260e9a87SYuri Pankov 		mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf);
619*260e9a87SYuri Pankov 		return(1);
62095c635efSGarrett D'Amore 	}
62195c635efSGarrett D'Amore 
62295c635efSGarrett D'Amore 	/*
62395c635efSGarrett D'Amore 	 * Search for the beginning of unescaped trailing whitespace (ws)
62495c635efSGarrett D'Amore 	 * and for the first character not to be output (end).
62595c635efSGarrett D'Amore 	 */
62695c635efSGarrett D'Amore 
62795c635efSGarrett D'Amore 	/* FIXME: replace with strcspn(). */
62895c635efSGarrett D'Amore 	ws = NULL;
62995c635efSGarrett D'Amore 	for (c = end = buf + offs; *c; c++) {
63095c635efSGarrett D'Amore 		switch (*c) {
63195c635efSGarrett D'Amore 		case ' ':
63295c635efSGarrett D'Amore 			if (NULL == ws)
63395c635efSGarrett D'Amore 				ws = c;
63495c635efSGarrett D'Amore 			continue;
63595c635efSGarrett D'Amore 		case '\t':
63695c635efSGarrett D'Amore 			/*
63795c635efSGarrett D'Amore 			 * Always warn about trailing tabs,
63895c635efSGarrett D'Amore 			 * even outside literal context,
63995c635efSGarrett D'Amore 			 * where they should be put on the next line.
64095c635efSGarrett D'Amore 			 */
64195c635efSGarrett D'Amore 			if (NULL == ws)
64295c635efSGarrett D'Amore 				ws = c;
64395c635efSGarrett D'Amore 			/*
64495c635efSGarrett D'Amore 			 * Strip trailing tabs in literal context only;
64595c635efSGarrett D'Amore 			 * outside, they affect the next line.
64695c635efSGarrett D'Amore 			 */
647698f87a4SGarrett D'Amore 			if (MDOC_LITERAL & mdoc->flags)
64895c635efSGarrett D'Amore 				continue;
64995c635efSGarrett D'Amore 			break;
65095c635efSGarrett D'Amore 		case '\\':
65195c635efSGarrett D'Amore 			/* Skip the escaped character, too, if any. */
65295c635efSGarrett D'Amore 			if (c[1])
65395c635efSGarrett D'Amore 				c++;
65495c635efSGarrett D'Amore 			/* FALLTHROUGH */
65595c635efSGarrett D'Amore 		default:
65695c635efSGarrett D'Amore 			ws = NULL;
65795c635efSGarrett D'Amore 			break;
65895c635efSGarrett D'Amore 		}
65995c635efSGarrett D'Amore 		end = c + 1;
66095c635efSGarrett D'Amore 	}
66195c635efSGarrett D'Amore 	*end = '\0';
66295c635efSGarrett D'Amore 
66395c635efSGarrett D'Amore 	if (ws)
664*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse,
665*260e9a87SYuri Pankov 		    line, (int)(ws-buf), NULL);
66695c635efSGarrett D'Amore 
667*260e9a87SYuri Pankov 	if (buf[offs] == '\0' && ! (mdoc->flags & MDOC_LITERAL)) {
668*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_FI_BLANK, mdoc->parse,
669*260e9a87SYuri Pankov 		    line, (int)(c - buf), NULL);
67095c635efSGarrett D'Amore 
67195c635efSGarrett D'Amore 		/*
67295c635efSGarrett D'Amore 		 * Insert a `sp' in the case of a blank line.  Technically,
67395c635efSGarrett D'Amore 		 * blank lines aren't allowed, but enough manuals assume this
67495c635efSGarrett D'Amore 		 * behaviour that we want to work around it.
67595c635efSGarrett D'Amore 		 */
676*260e9a87SYuri Pankov 		mdoc_elem_alloc(mdoc, line, offs, MDOC_sp, NULL);
677698f87a4SGarrett D'Amore 		mdoc->next = MDOC_NEXT_SIBLING;
678*260e9a87SYuri Pankov 		mdoc_valid_post(mdoc);
679*260e9a87SYuri Pankov 		return(1);
68095c635efSGarrett D'Amore 	}
68195c635efSGarrett D'Amore 
682*260e9a87SYuri Pankov 	mdoc_word_alloc(mdoc, line, offs, buf+offs);
68395c635efSGarrett D'Amore 
684*260e9a87SYuri Pankov 	if (mdoc->flags & MDOC_LITERAL)
68595c635efSGarrett D'Amore 		return(1);
68695c635efSGarrett D'Amore 
68795c635efSGarrett D'Amore 	/*
68895c635efSGarrett D'Amore 	 * End-of-sentence check.  If the last character is an unescaped
68995c635efSGarrett D'Amore 	 * EOS character, then flag the node as being the end of a
69095c635efSGarrett D'Amore 	 * sentence.  The front-end will know how to interpret this.
69195c635efSGarrett D'Amore 	 */
69295c635efSGarrett D'Amore 
69395c635efSGarrett D'Amore 	assert(buf < end);
69495c635efSGarrett D'Amore 
695*260e9a87SYuri Pankov 	if (mandoc_eos(buf+offs, (size_t)(end-buf-offs)))
696698f87a4SGarrett D'Amore 		mdoc->last->flags |= MDOC_EOS;
69795c635efSGarrett D'Amore 	return(1);
69895c635efSGarrett D'Amore }
69995c635efSGarrett D'Amore 
70095c635efSGarrett D'Amore /*
70195c635efSGarrett D'Amore  * Parse a macro line, that is, a line beginning with the control
70295c635efSGarrett D'Amore  * character.
70395c635efSGarrett D'Amore  */
70495c635efSGarrett D'Amore static int
705698f87a4SGarrett D'Amore mdoc_pmacro(struct mdoc *mdoc, int ln, char *buf, int offs)
70695c635efSGarrett D'Amore {
707*260e9a87SYuri Pankov 	struct mdoc_node *n;
708*260e9a87SYuri Pankov 	const char	 *cp;
70995c635efSGarrett D'Amore 	enum mdoct	  tok;
71095c635efSGarrett D'Amore 	int		  i, sv;
71195c635efSGarrett D'Amore 	char		  mac[5];
71295c635efSGarrett D'Amore 
71395c635efSGarrett D'Amore 	sv = offs;
71495c635efSGarrett D'Amore 
71595c635efSGarrett D'Amore 	/*
71695c635efSGarrett D'Amore 	 * Copy the first word into a nil-terminated buffer.
717*260e9a87SYuri Pankov 	 * Stop when a space, tab, escape, or eoln is encountered.
71895c635efSGarrett D'Amore 	 */
71995c635efSGarrett D'Amore 
72095c635efSGarrett D'Amore 	i = 0;
721*260e9a87SYuri Pankov 	while (i < 4 && strchr(" \t\\", buf[offs]) == NULL)
72295c635efSGarrett D'Amore 		mac[i++] = buf[offs++];
72395c635efSGarrett D'Amore 
72495c635efSGarrett D'Amore 	mac[i] = '\0';
72595c635efSGarrett D'Amore 
726*260e9a87SYuri Pankov 	tok = (i > 1 && i < 4) ? mdoc_hash_find(mac) : MDOC_MAX;
72795c635efSGarrett D'Amore 
728*260e9a87SYuri Pankov 	if (tok == MDOC_MAX) {
729*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_MACRO, mdoc->parse,
730*260e9a87SYuri Pankov 		    ln, sv, buf + sv - 1);
73195c635efSGarrett D'Amore 		return(1);
73295c635efSGarrett D'Amore 	}
73395c635efSGarrett D'Amore 
734*260e9a87SYuri Pankov 	/* Skip a leading escape sequence or tab. */
73595c635efSGarrett D'Amore 
736*260e9a87SYuri Pankov 	switch (buf[offs]) {
737*260e9a87SYuri Pankov 	case '\\':
738*260e9a87SYuri Pankov 		cp = buf + offs + 1;
739*260e9a87SYuri Pankov 		mandoc_escape(&cp, NULL, NULL);
740*260e9a87SYuri Pankov 		offs = cp - buf;
741*260e9a87SYuri Pankov 		break;
742*260e9a87SYuri Pankov 	case '\t':
74395c635efSGarrett D'Amore 		offs++;
744*260e9a87SYuri Pankov 		break;
745*260e9a87SYuri Pankov 	default:
746*260e9a87SYuri Pankov 		break;
747*260e9a87SYuri Pankov 	}
74895c635efSGarrett D'Amore 
74995c635efSGarrett D'Amore 	/* Jump to the next non-whitespace word. */
75095c635efSGarrett D'Amore 
75195c635efSGarrett D'Amore 	while (buf[offs] && ' ' == buf[offs])
75295c635efSGarrett D'Amore 		offs++;
75395c635efSGarrett D'Amore 
75495c635efSGarrett D'Amore 	/*
75595c635efSGarrett D'Amore 	 * Trailing whitespace.  Note that tabs are allowed to be passed
75695c635efSGarrett D'Amore 	 * into the parser as "text", so we only warn about spaces here.
75795c635efSGarrett D'Amore 	 */
75895c635efSGarrett D'Amore 
75995c635efSGarrett D'Amore 	if ('\0' == buf[offs] && ' ' == buf[offs - 1])
760*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse,
761*260e9a87SYuri Pankov 		    ln, offs - 1, NULL);
76295c635efSGarrett D'Amore 
76395c635efSGarrett D'Amore 	/*
76495c635efSGarrett D'Amore 	 * If an initial macro or a list invocation, divert directly
76595c635efSGarrett D'Amore 	 * into macro processing.
76695c635efSGarrett D'Amore 	 */
76795c635efSGarrett D'Amore 
768698f87a4SGarrett D'Amore 	if (NULL == mdoc->last || MDOC_It == tok || MDOC_El == tok) {
769*260e9a87SYuri Pankov 		mdoc_macro(mdoc, tok, ln, sv, &offs, buf);
77095c635efSGarrett D'Amore 		return(1);
77195c635efSGarrett D'Amore 	}
77295c635efSGarrett D'Amore 
773698f87a4SGarrett D'Amore 	n = mdoc->last;
774698f87a4SGarrett D'Amore 	assert(mdoc->last);
77595c635efSGarrett D'Amore 
77695c635efSGarrett D'Amore 	/*
77795c635efSGarrett D'Amore 	 * If the first macro of a `Bl -column', open an `It' block
77895c635efSGarrett D'Amore 	 * context around the parsed macro.
77995c635efSGarrett D'Amore 	 */
78095c635efSGarrett D'Amore 
781*260e9a87SYuri Pankov 	if (n->tok == MDOC_Bl && n->type == MDOC_BODY &&
782*260e9a87SYuri Pankov 	    n->end == ENDBODY_NOT && n->norm->Bl.type == LIST_column) {
783698f87a4SGarrett D'Amore 		mdoc->flags |= MDOC_FREECOL;
784*260e9a87SYuri Pankov 		mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf);
78595c635efSGarrett D'Amore 		return(1);
78695c635efSGarrett D'Amore 	}
78795c635efSGarrett D'Amore 
78895c635efSGarrett D'Amore 	/*
78995c635efSGarrett D'Amore 	 * If we're following a block-level `It' within a `Bl -column'
79095c635efSGarrett D'Amore 	 * context (perhaps opened in the above block or in ptext()),
79195c635efSGarrett D'Amore 	 * then open an `It' block context around the parsed macro.
79295c635efSGarrett D'Amore 	 */
79395c635efSGarrett D'Amore 
79495c635efSGarrett D'Amore 	if (MDOC_It == n->tok && MDOC_BLOCK == n->type &&
79595c635efSGarrett D'Amore 	    NULL != n->parent &&
79695c635efSGarrett D'Amore 	    MDOC_Bl == n->parent->tok &&
79795c635efSGarrett D'Amore 	    LIST_column == n->parent->norm->Bl.type) {
798698f87a4SGarrett D'Amore 		mdoc->flags |= MDOC_FREECOL;
799*260e9a87SYuri Pankov 		mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf);
80095c635efSGarrett D'Amore 		return(1);
80195c635efSGarrett D'Amore 	}
80295c635efSGarrett D'Amore 
80395c635efSGarrett D'Amore 	/* Normal processing of a macro. */
80495c635efSGarrett D'Amore 
805*260e9a87SYuri Pankov 	mdoc_macro(mdoc, tok, ln, sv, &offs, buf);
806*260e9a87SYuri Pankov 
807*260e9a87SYuri Pankov 	/* In quick mode (for mandocdb), abort after the NAME section. */
808*260e9a87SYuri Pankov 
809*260e9a87SYuri Pankov 	if (mdoc->quick && MDOC_Sh == tok &&
810*260e9a87SYuri Pankov 	    SEC_NAME != mdoc->last->sec)
811*260e9a87SYuri Pankov 		return(2);
81295c635efSGarrett D'Amore 
81395c635efSGarrett D'Amore 	return(1);
81495c635efSGarrett D'Amore }
81595c635efSGarrett D'Amore 
81695c635efSGarrett D'Amore enum mdelim
81795c635efSGarrett D'Amore mdoc_isdelim(const char *p)
81895c635efSGarrett D'Amore {
81995c635efSGarrett D'Amore 
82095c635efSGarrett D'Amore 	if ('\0' == p[0])
82195c635efSGarrett D'Amore 		return(DELIM_NONE);
82295c635efSGarrett D'Amore 
82395c635efSGarrett D'Amore 	if ('\0' == p[1])
82495c635efSGarrett D'Amore 		switch (p[0]) {
825*260e9a87SYuri Pankov 		case '(':
82695c635efSGarrett D'Amore 			/* FALLTHROUGH */
827*260e9a87SYuri Pankov 		case '[':
82895c635efSGarrett D'Amore 			return(DELIM_OPEN);
829*260e9a87SYuri Pankov 		case '|':
83095c635efSGarrett D'Amore 			return(DELIM_MIDDLE);
831*260e9a87SYuri Pankov 		case '.':
83295c635efSGarrett D'Amore 			/* FALLTHROUGH */
833*260e9a87SYuri Pankov 		case ',':
83495c635efSGarrett D'Amore 			/* FALLTHROUGH */
835*260e9a87SYuri Pankov 		case ';':
83695c635efSGarrett D'Amore 			/* FALLTHROUGH */
837*260e9a87SYuri Pankov 		case ':':
83895c635efSGarrett D'Amore 			/* FALLTHROUGH */
839*260e9a87SYuri Pankov 		case '?':
84095c635efSGarrett D'Amore 			/* FALLTHROUGH */
841*260e9a87SYuri Pankov 		case '!':
84295c635efSGarrett D'Amore 			/* FALLTHROUGH */
843*260e9a87SYuri Pankov 		case ')':
84495c635efSGarrett D'Amore 			/* FALLTHROUGH */
845*260e9a87SYuri Pankov 		case ']':
84695c635efSGarrett D'Amore 			return(DELIM_CLOSE);
84795c635efSGarrett D'Amore 		default:
84895c635efSGarrett D'Amore 			return(DELIM_NONE);
84995c635efSGarrett D'Amore 		}
85095c635efSGarrett D'Amore 
85195c635efSGarrett D'Amore 	if ('\\' != p[0])
85295c635efSGarrett D'Amore 		return(DELIM_NONE);
85395c635efSGarrett D'Amore 
85495c635efSGarrett D'Amore 	if (0 == strcmp(p + 1, "."))
85595c635efSGarrett D'Amore 		return(DELIM_CLOSE);
856698f87a4SGarrett D'Amore 	if (0 == strcmp(p + 1, "fR|\\fP"))
85795c635efSGarrett D'Amore 		return(DELIM_MIDDLE);
85895c635efSGarrett D'Amore 
85995c635efSGarrett D'Amore 	return(DELIM_NONE);
86095c635efSGarrett D'Amore }
861*260e9a87SYuri Pankov 
862*260e9a87SYuri Pankov void
863*260e9a87SYuri Pankov mdoc_deroff(char **dest, const struct mdoc_node *n)
864*260e9a87SYuri Pankov {
865*260e9a87SYuri Pankov 	char	*cp;
866*260e9a87SYuri Pankov 	size_t	 sz;
867*260e9a87SYuri Pankov 
868*260e9a87SYuri Pankov 	if (MDOC_TEXT != n->type) {
869*260e9a87SYuri Pankov 		for (n = n->child; n; n = n->next)
870*260e9a87SYuri Pankov 			mdoc_deroff(dest, n);
871*260e9a87SYuri Pankov 		return;
872*260e9a87SYuri Pankov 	}
873*260e9a87SYuri Pankov 
874*260e9a87SYuri Pankov 	/* Skip leading whitespace. */
875*260e9a87SYuri Pankov 
876*260e9a87SYuri Pankov 	for (cp = n->string; '\0' != *cp; cp++)
877*260e9a87SYuri Pankov 		if (0 == isspace((unsigned char)*cp))
878*260e9a87SYuri Pankov 			break;
879*260e9a87SYuri Pankov 
880*260e9a87SYuri Pankov 	/* Skip trailing whitespace. */
881*260e9a87SYuri Pankov 
882*260e9a87SYuri Pankov 	for (sz = strlen(cp); sz; sz--)
883*260e9a87SYuri Pankov 		if (0 == isspace((unsigned char)cp[sz-1]))
884*260e9a87SYuri Pankov 			break;
885*260e9a87SYuri Pankov 
886*260e9a87SYuri Pankov 	/* Skip empty strings. */
887*260e9a87SYuri Pankov 
888*260e9a87SYuri Pankov 	if (0 == sz)
889*260e9a87SYuri Pankov 		return;
890*260e9a87SYuri Pankov 
891*260e9a87SYuri Pankov 	if (NULL == *dest) {
892*260e9a87SYuri Pankov 		*dest = mandoc_strndup(cp, sz);
893*260e9a87SYuri Pankov 		return;
894*260e9a87SYuri Pankov 	}
895*260e9a87SYuri Pankov 
896*260e9a87SYuri Pankov 	mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp);
897*260e9a87SYuri Pankov 	free(*dest);
898*260e9a87SYuri Pankov 	*dest = cp;
899*260e9a87SYuri Pankov }
900