xref: /titanic_51/usr/src/cmd/mandoc/mdoc_macro.c (revision 698f87a48e2e945bfe5493ce168e0d0ae1cedd5c)
1*698f87a4SGarrett D'Amore /*	$Id: mdoc_macro.c,v 1.125 2013/12/24 20:45:27 schwarze Exp $ */
295c635efSGarrett D'Amore /*
3*698f87a4SGarrett D'Amore  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4*698f87a4SGarrett D'Amore  * Copyright (c) 2010, 2012, 2013 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 #ifdef HAVE_CONFIG_H
1995c635efSGarrett D'Amore #include "config.h"
2095c635efSGarrett D'Amore #endif
2195c635efSGarrett D'Amore 
2295c635efSGarrett D'Amore #include <assert.h>
2395c635efSGarrett D'Amore #include <ctype.h>
2495c635efSGarrett D'Amore #include <stdlib.h>
2595c635efSGarrett D'Amore #include <stdio.h>
2695c635efSGarrett D'Amore #include <string.h>
2795c635efSGarrett D'Amore #include <time.h>
2895c635efSGarrett D'Amore 
2995c635efSGarrett D'Amore #include "mdoc.h"
3095c635efSGarrett D'Amore #include "mandoc.h"
3195c635efSGarrett D'Amore #include "libmdoc.h"
3295c635efSGarrett D'Amore #include "libmandoc.h"
3395c635efSGarrett D'Amore 
3495c635efSGarrett D'Amore enum	rew {	/* see rew_dohalt() */
3595c635efSGarrett D'Amore 	REWIND_NONE,
3695c635efSGarrett D'Amore 	REWIND_THIS,
3795c635efSGarrett D'Amore 	REWIND_MORE,
3895c635efSGarrett D'Amore 	REWIND_FORCE,
3995c635efSGarrett D'Amore 	REWIND_LATER,
4095c635efSGarrett D'Amore 	REWIND_ERROR
4195c635efSGarrett D'Amore };
4295c635efSGarrett D'Amore 
4395c635efSGarrett D'Amore static	int	  	blk_full(MACRO_PROT_ARGS);
4495c635efSGarrett D'Amore static	int	  	blk_exp_close(MACRO_PROT_ARGS);
4595c635efSGarrett D'Amore static	int	  	blk_part_exp(MACRO_PROT_ARGS);
4695c635efSGarrett D'Amore static	int	  	blk_part_imp(MACRO_PROT_ARGS);
4795c635efSGarrett D'Amore static	int	  	ctx_synopsis(MACRO_PROT_ARGS);
4895c635efSGarrett D'Amore static	int	  	in_line_eoln(MACRO_PROT_ARGS);
4995c635efSGarrett D'Amore static	int	  	in_line_argn(MACRO_PROT_ARGS);
5095c635efSGarrett D'Amore static	int	  	in_line(MACRO_PROT_ARGS);
5195c635efSGarrett D'Amore static	int	  	obsolete(MACRO_PROT_ARGS);
5295c635efSGarrett D'Amore static	int	  	phrase_ta(MACRO_PROT_ARGS);
5395c635efSGarrett D'Amore 
54*698f87a4SGarrett D'Amore static	int		dword(struct mdoc *, int, int, const char *,
55*698f87a4SGarrett D'Amore 				 enum mdelim, int);
5695c635efSGarrett D'Amore static	int	  	append_delims(struct mdoc *,
5795c635efSGarrett D'Amore 				int, int *, char *);
5895c635efSGarrett D'Amore static	enum mdoct	lookup(enum mdoct, const char *);
5995c635efSGarrett D'Amore static	enum mdoct	lookup_raw(const char *);
6095c635efSGarrett D'Amore static	int		make_pending(struct mdoc_node *, enum mdoct,
6195c635efSGarrett D'Amore 				struct mdoc *, int, int);
6295c635efSGarrett D'Amore static	int	  	phrase(struct mdoc *, int, int, char *);
6395c635efSGarrett D'Amore static	enum mdoct 	rew_alt(enum mdoct);
6495c635efSGarrett D'Amore static	enum rew  	rew_dohalt(enum mdoct, enum mdoc_type,
6595c635efSGarrett D'Amore 				const struct mdoc_node *);
6695c635efSGarrett D'Amore static	int	  	rew_elem(struct mdoc *, enum mdoct);
6795c635efSGarrett D'Amore static	int	  	rew_last(struct mdoc *,
6895c635efSGarrett D'Amore 				const struct mdoc_node *);
6995c635efSGarrett D'Amore static	int	  	rew_sub(enum mdoc_type, struct mdoc *,
7095c635efSGarrett D'Amore 				enum mdoct, int, int);
7195c635efSGarrett D'Amore 
7295c635efSGarrett D'Amore const	struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
73*698f87a4SGarrett D'Amore 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ap */
7495c635efSGarrett D'Amore 	{ in_line_eoln, MDOC_PROLOGUE }, /* Dd */
7595c635efSGarrett D'Amore 	{ in_line_eoln, MDOC_PROLOGUE }, /* Dt */
7695c635efSGarrett D'Amore 	{ in_line_eoln, MDOC_PROLOGUE }, /* Os */
77*698f87a4SGarrett D'Amore 	{ blk_full, MDOC_PARSED | MDOC_JOIN }, /* Sh */
78*698f87a4SGarrett D'Amore 	{ blk_full, MDOC_PARSED | MDOC_JOIN }, /* Ss */
7995c635efSGarrett D'Amore 	{ in_line_eoln, 0 }, /* Pp */
80*698f87a4SGarrett D'Amore 	{ blk_part_imp, MDOC_PARSED | MDOC_JOIN }, /* D1 */
81*698f87a4SGarrett D'Amore 	{ blk_part_imp, MDOC_PARSED | MDOC_JOIN }, /* Dl */
8295c635efSGarrett D'Amore 	{ blk_full, MDOC_EXPLICIT }, /* Bd */
83*698f87a4SGarrett D'Amore 	{ blk_exp_close, MDOC_EXPLICIT | MDOC_JOIN }, /* Ed */
8495c635efSGarrett D'Amore 	{ blk_full, MDOC_EXPLICIT }, /* Bl */
85*698f87a4SGarrett D'Amore 	{ blk_exp_close, MDOC_EXPLICIT | MDOC_JOIN }, /* El */
86*698f87a4SGarrett D'Amore 	{ blk_full, MDOC_PARSED | MDOC_JOIN }, /* It */
8795c635efSGarrett D'Amore 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ad */
88*698f87a4SGarrett D'Amore 	{ in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* An */
8995c635efSGarrett D'Amore 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ar */
9095c635efSGarrett D'Amore 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Cd */
9195c635efSGarrett D'Amore 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Cm */
9295c635efSGarrett D'Amore 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Dv */
9395c635efSGarrett D'Amore 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Er */
9495c635efSGarrett D'Amore 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ev */
9595c635efSGarrett D'Amore 	{ in_line_eoln, 0 }, /* Ex */
9695c635efSGarrett D'Amore 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fa */
9795c635efSGarrett D'Amore 	{ in_line_eoln, 0 }, /* Fd */
9895c635efSGarrett D'Amore 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fl */
9995c635efSGarrett D'Amore 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fn */
10095c635efSGarrett D'Amore 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ft */
10195c635efSGarrett D'Amore 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ic */
10295c635efSGarrett D'Amore 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* In */
103*698f87a4SGarrett D'Amore 	{ in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Li */
104*698f87a4SGarrett D'Amore 	{ blk_full, MDOC_JOIN }, /* Nd */
10595c635efSGarrett D'Amore 	{ ctx_synopsis, MDOC_CALLABLE | MDOC_PARSED }, /* Nm */
10695c635efSGarrett D'Amore 	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Op */
10795c635efSGarrett D'Amore 	{ obsolete, 0 }, /* Ot */
10895c635efSGarrett D'Amore 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Pa */
10995c635efSGarrett D'Amore 	{ in_line_eoln, 0 }, /* Rv */
11095c635efSGarrett D'Amore 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* St */
11195c635efSGarrett D'Amore 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Va */
11295c635efSGarrett D'Amore 	{ ctx_synopsis, MDOC_CALLABLE | MDOC_PARSED }, /* Vt */
11395c635efSGarrett D'Amore 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Xr */
114*698f87a4SGarrett D'Amore 	{ in_line_eoln, MDOC_JOIN }, /* %A */
115*698f87a4SGarrett D'Amore 	{ in_line_eoln, MDOC_JOIN }, /* %B */
116*698f87a4SGarrett D'Amore 	{ in_line_eoln, MDOC_JOIN }, /* %D */
117*698f87a4SGarrett D'Amore 	{ in_line_eoln, MDOC_JOIN }, /* %I */
118*698f87a4SGarrett D'Amore 	{ in_line_eoln, MDOC_JOIN }, /* %J */
11995c635efSGarrett D'Amore 	{ in_line_eoln, 0 }, /* %N */
120*698f87a4SGarrett D'Amore 	{ in_line_eoln, MDOC_JOIN }, /* %O */
12195c635efSGarrett D'Amore 	{ in_line_eoln, 0 }, /* %P */
122*698f87a4SGarrett D'Amore 	{ in_line_eoln, MDOC_JOIN }, /* %R */
123*698f87a4SGarrett D'Amore 	{ in_line_eoln, MDOC_JOIN }, /* %T */
12495c635efSGarrett D'Amore 	{ in_line_eoln, 0 }, /* %V */
125*698f87a4SGarrett D'Amore 	{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
126*698f87a4SGarrett D'Amore 			 MDOC_EXPLICIT | MDOC_JOIN }, /* Ac */
127*698f87a4SGarrett D'Amore 	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
128*698f87a4SGarrett D'Amore 			MDOC_EXPLICIT | MDOC_JOIN }, /* Ao */
129*698f87a4SGarrett D'Amore 	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Aq */
13095c635efSGarrett D'Amore 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* At */
131*698f87a4SGarrett D'Amore 	{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
132*698f87a4SGarrett D'Amore 			 MDOC_EXPLICIT | MDOC_JOIN }, /* Bc */
13395c635efSGarrett D'Amore 	{ blk_full, MDOC_EXPLICIT }, /* Bf */
134*698f87a4SGarrett D'Amore 	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
135*698f87a4SGarrett D'Amore 			MDOC_EXPLICIT | MDOC_JOIN }, /* Bo */
136*698f87a4SGarrett D'Amore 	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Bq */
13795c635efSGarrett D'Amore 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Bsx */
13895c635efSGarrett D'Amore 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Bx */
13995c635efSGarrett D'Amore 	{ in_line_eoln, 0 }, /* Db */
140*698f87a4SGarrett D'Amore 	{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
141*698f87a4SGarrett D'Amore 			 MDOC_EXPLICIT | MDOC_JOIN }, /* Dc */
142*698f87a4SGarrett D'Amore 	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
143*698f87a4SGarrett D'Amore 			MDOC_EXPLICIT | MDOC_JOIN }, /* Do */
144*698f87a4SGarrett D'Amore 	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Dq */
145*698f87a4SGarrett D'Amore 	{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Ec */
146*698f87a4SGarrett D'Amore 	{ blk_exp_close, MDOC_EXPLICIT | MDOC_JOIN }, /* Ef */
147*698f87a4SGarrett D'Amore 	{ in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Em */
14895c635efSGarrett D'Amore 	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Eo */
14995c635efSGarrett D'Amore 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Fx */
15095c635efSGarrett D'Amore 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ms */
151*698f87a4SGarrett D'Amore 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED |
152*698f87a4SGarrett D'Amore 			MDOC_IGNDELIM | MDOC_JOIN }, /* No */
153*698f87a4SGarrett D'Amore 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED |
154*698f87a4SGarrett D'Amore 			MDOC_IGNDELIM | MDOC_JOIN }, /* Ns */
15595c635efSGarrett D'Amore 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Nx */
15695c635efSGarrett D'Amore 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ox */
157*698f87a4SGarrett D'Amore 	{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
158*698f87a4SGarrett D'Amore 			 MDOC_EXPLICIT | MDOC_JOIN }, /* Pc */
15995c635efSGarrett D'Amore 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED | MDOC_IGNDELIM }, /* Pf */
160*698f87a4SGarrett D'Amore 	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
161*698f87a4SGarrett D'Amore 			MDOC_EXPLICIT | MDOC_JOIN }, /* Po */
162*698f87a4SGarrett D'Amore 	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Pq */
163*698f87a4SGarrett D'Amore 	{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
164*698f87a4SGarrett D'Amore 			 MDOC_EXPLICIT | MDOC_JOIN }, /* Qc */
165*698f87a4SGarrett D'Amore 	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ql */
166*698f87a4SGarrett D'Amore 	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
167*698f87a4SGarrett D'Amore 			MDOC_EXPLICIT | MDOC_JOIN }, /* Qo */
168*698f87a4SGarrett D'Amore 	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Qq */
169*698f87a4SGarrett D'Amore 	{ blk_exp_close, MDOC_EXPLICIT | MDOC_JOIN }, /* Re */
17095c635efSGarrett D'Amore 	{ blk_full, MDOC_EXPLICIT }, /* Rs */
171*698f87a4SGarrett D'Amore 	{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
172*698f87a4SGarrett D'Amore 			 MDOC_EXPLICIT | MDOC_JOIN }, /* Sc */
173*698f87a4SGarrett D'Amore 	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
174*698f87a4SGarrett D'Amore 			MDOC_EXPLICIT | MDOC_JOIN }, /* So */
175*698f87a4SGarrett D'Amore 	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Sq */
17695c635efSGarrett D'Amore 	{ in_line_eoln, 0 }, /* Sm */
177*698f87a4SGarrett D'Amore 	{ in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Sx */
178*698f87a4SGarrett D'Amore 	{ in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Sy */
17995c635efSGarrett D'Amore 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Tn */
180*698f87a4SGarrett D'Amore 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ux */
18195c635efSGarrett D'Amore 	{ blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Xc */
18295c635efSGarrett D'Amore 	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Xo */
18395c635efSGarrett D'Amore 	{ blk_full, MDOC_EXPLICIT | MDOC_CALLABLE }, /* Fo */
184*698f87a4SGarrett D'Amore 	{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
185*698f87a4SGarrett D'Amore 			 MDOC_EXPLICIT | MDOC_JOIN }, /* Fc */
186*698f87a4SGarrett D'Amore 	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
187*698f87a4SGarrett D'Amore 			MDOC_EXPLICIT | MDOC_JOIN }, /* Oo */
188*698f87a4SGarrett D'Amore 	{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
189*698f87a4SGarrett D'Amore 			 MDOC_EXPLICIT | MDOC_JOIN }, /* Oc */
19095c635efSGarrett D'Amore 	{ blk_full, MDOC_EXPLICIT }, /* Bk */
191*698f87a4SGarrett D'Amore 	{ blk_exp_close, MDOC_EXPLICIT | MDOC_JOIN }, /* Ek */
19295c635efSGarrett D'Amore 	{ in_line_eoln, 0 }, /* Bt */
19395c635efSGarrett D'Amore 	{ in_line_eoln, 0 }, /* Hf */
19495c635efSGarrett D'Amore 	{ obsolete, 0 }, /* Fr */
19595c635efSGarrett D'Amore 	{ in_line_eoln, 0 }, /* Ud */
19695c635efSGarrett D'Amore 	{ in_line, 0 }, /* Lb */
19795c635efSGarrett D'Amore 	{ in_line_eoln, 0 }, /* Lp */
19895c635efSGarrett D'Amore 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Lk */
19995c635efSGarrett D'Amore 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Mt */
200*698f87a4SGarrett D'Amore 	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Brq */
201*698f87a4SGarrett D'Amore 	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
202*698f87a4SGarrett D'Amore 			MDOC_EXPLICIT | MDOC_JOIN }, /* Bro */
203*698f87a4SGarrett D'Amore 	{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
204*698f87a4SGarrett D'Amore 			 MDOC_EXPLICIT | MDOC_JOIN }, /* Brc */
205*698f87a4SGarrett D'Amore 	{ in_line_eoln, MDOC_JOIN }, /* %C */
20695c635efSGarrett D'Amore 	{ obsolete, 0 }, /* Es */
20795c635efSGarrett D'Amore 	{ obsolete, 0 }, /* En */
20895c635efSGarrett D'Amore 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Dx */
209*698f87a4SGarrett D'Amore 	{ in_line_eoln, MDOC_JOIN }, /* %Q */
21095c635efSGarrett D'Amore 	{ in_line_eoln, 0 }, /* br */
21195c635efSGarrett D'Amore 	{ in_line_eoln, 0 }, /* sp */
21295c635efSGarrett D'Amore 	{ in_line_eoln, 0 }, /* %U */
213*698f87a4SGarrett D'Amore 	{ phrase_ta, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ta */
21495c635efSGarrett D'Amore };
21595c635efSGarrett D'Amore 
21695c635efSGarrett D'Amore const	struct mdoc_macro * const mdoc_macros = __mdoc_macros;
21795c635efSGarrett D'Amore 
21895c635efSGarrett D'Amore 
21995c635efSGarrett D'Amore /*
22095c635efSGarrett D'Amore  * This is called at the end of parsing.  It must traverse up the tree,
22195c635efSGarrett D'Amore  * closing out open [implicit] scopes.  Obviously, open explicit scopes
22295c635efSGarrett D'Amore  * are errors.
22395c635efSGarrett D'Amore  */
22495c635efSGarrett D'Amore int
225*698f87a4SGarrett D'Amore mdoc_macroend(struct mdoc *mdoc)
22695c635efSGarrett D'Amore {
22795c635efSGarrett D'Amore 	struct mdoc_node *n;
22895c635efSGarrett D'Amore 
22995c635efSGarrett D'Amore 	/* Scan for open explicit scopes. */
23095c635efSGarrett D'Amore 
231*698f87a4SGarrett D'Amore 	n = MDOC_VALID & mdoc->last->flags ?
232*698f87a4SGarrett D'Amore 			mdoc->last->parent : mdoc->last;
23395c635efSGarrett D'Amore 
23495c635efSGarrett D'Amore 	for ( ; n; n = n->parent)
23595c635efSGarrett D'Amore 		if (MDOC_BLOCK == n->type &&
23695c635efSGarrett D'Amore 		    MDOC_EXPLICIT & mdoc_macros[n->tok].flags)
237*698f87a4SGarrett D'Amore 			mdoc_nmsg(mdoc, n, MANDOCERR_SCOPEEXIT);
23895c635efSGarrett D'Amore 
23995c635efSGarrett D'Amore 	/* Rewind to the first. */
24095c635efSGarrett D'Amore 
241*698f87a4SGarrett D'Amore 	return(rew_last(mdoc, mdoc->first));
24295c635efSGarrett D'Amore }
24395c635efSGarrett D'Amore 
24495c635efSGarrett D'Amore 
24595c635efSGarrett D'Amore /*
24695c635efSGarrett D'Amore  * Look up a macro from within a subsequent context.
24795c635efSGarrett D'Amore  */
24895c635efSGarrett D'Amore static enum mdoct
24995c635efSGarrett D'Amore lookup(enum mdoct from, const char *p)
25095c635efSGarrett D'Amore {
25195c635efSGarrett D'Amore 
25295c635efSGarrett D'Amore 	if ( ! (MDOC_PARSED & mdoc_macros[from].flags))
25395c635efSGarrett D'Amore 		return(MDOC_MAX);
25495c635efSGarrett D'Amore 	return(lookup_raw(p));
25595c635efSGarrett D'Amore }
25695c635efSGarrett D'Amore 
25795c635efSGarrett D'Amore 
25895c635efSGarrett D'Amore /*
25995c635efSGarrett D'Amore  * Lookup a macro following the initial line macro.
26095c635efSGarrett D'Amore  */
26195c635efSGarrett D'Amore static enum mdoct
26295c635efSGarrett D'Amore lookup_raw(const char *p)
26395c635efSGarrett D'Amore {
26495c635efSGarrett D'Amore 	enum mdoct	 res;
26595c635efSGarrett D'Amore 
26695c635efSGarrett D'Amore 	if (MDOC_MAX == (res = mdoc_hash_find(p)))
26795c635efSGarrett D'Amore 		return(MDOC_MAX);
26895c635efSGarrett D'Amore 	if (MDOC_CALLABLE & mdoc_macros[res].flags)
26995c635efSGarrett D'Amore 		return(res);
27095c635efSGarrett D'Amore 	return(MDOC_MAX);
27195c635efSGarrett D'Amore }
27295c635efSGarrett D'Amore 
27395c635efSGarrett D'Amore 
27495c635efSGarrett D'Amore static int
27595c635efSGarrett D'Amore rew_last(struct mdoc *mdoc, const struct mdoc_node *to)
27695c635efSGarrett D'Amore {
27795c635efSGarrett D'Amore 	struct mdoc_node *n, *np;
27895c635efSGarrett D'Amore 
27995c635efSGarrett D'Amore 	assert(to);
28095c635efSGarrett D'Amore 	mdoc->next = MDOC_NEXT_SIBLING;
28195c635efSGarrett D'Amore 
28295c635efSGarrett D'Amore 	/* LINTED */
28395c635efSGarrett D'Amore 	while (mdoc->last != to) {
28495c635efSGarrett D'Amore 		/*
28595c635efSGarrett D'Amore 		 * Save the parent here, because we may delete the
286*698f87a4SGarrett D'Amore 		 * mdoc->last node in the post-validation phase and reset
287*698f87a4SGarrett D'Amore 		 * it to mdoc->last->parent, causing a step in the closing
28895c635efSGarrett D'Amore 		 * out to be lost.
28995c635efSGarrett D'Amore 		 */
29095c635efSGarrett D'Amore 		np = mdoc->last->parent;
29195c635efSGarrett D'Amore 		if ( ! mdoc_valid_post(mdoc))
29295c635efSGarrett D'Amore 			return(0);
29395c635efSGarrett D'Amore 		n = mdoc->last;
29495c635efSGarrett D'Amore 		mdoc->last = np;
29595c635efSGarrett D'Amore 		assert(mdoc->last);
29695c635efSGarrett D'Amore 		mdoc->last->last = n;
29795c635efSGarrett D'Amore 	}
29895c635efSGarrett D'Amore 
29995c635efSGarrett D'Amore 	return(mdoc_valid_post(mdoc));
30095c635efSGarrett D'Amore }
30195c635efSGarrett D'Amore 
30295c635efSGarrett D'Amore 
30395c635efSGarrett D'Amore /*
30495c635efSGarrett D'Amore  * For a block closing macro, return the corresponding opening one.
30595c635efSGarrett D'Amore  * Otherwise, return the macro itself.
30695c635efSGarrett D'Amore  */
30795c635efSGarrett D'Amore static enum mdoct
30895c635efSGarrett D'Amore rew_alt(enum mdoct tok)
30995c635efSGarrett D'Amore {
31095c635efSGarrett D'Amore 	switch (tok) {
31195c635efSGarrett D'Amore 	case (MDOC_Ac):
31295c635efSGarrett D'Amore 		return(MDOC_Ao);
31395c635efSGarrett D'Amore 	case (MDOC_Bc):
31495c635efSGarrett D'Amore 		return(MDOC_Bo);
31595c635efSGarrett D'Amore 	case (MDOC_Brc):
31695c635efSGarrett D'Amore 		return(MDOC_Bro);
31795c635efSGarrett D'Amore 	case (MDOC_Dc):
31895c635efSGarrett D'Amore 		return(MDOC_Do);
31995c635efSGarrett D'Amore 	case (MDOC_Ec):
32095c635efSGarrett D'Amore 		return(MDOC_Eo);
32195c635efSGarrett D'Amore 	case (MDOC_Ed):
32295c635efSGarrett D'Amore 		return(MDOC_Bd);
32395c635efSGarrett D'Amore 	case (MDOC_Ef):
32495c635efSGarrett D'Amore 		return(MDOC_Bf);
32595c635efSGarrett D'Amore 	case (MDOC_Ek):
32695c635efSGarrett D'Amore 		return(MDOC_Bk);
32795c635efSGarrett D'Amore 	case (MDOC_El):
32895c635efSGarrett D'Amore 		return(MDOC_Bl);
32995c635efSGarrett D'Amore 	case (MDOC_Fc):
33095c635efSGarrett D'Amore 		return(MDOC_Fo);
33195c635efSGarrett D'Amore 	case (MDOC_Oc):
33295c635efSGarrett D'Amore 		return(MDOC_Oo);
33395c635efSGarrett D'Amore 	case (MDOC_Pc):
33495c635efSGarrett D'Amore 		return(MDOC_Po);
33595c635efSGarrett D'Amore 	case (MDOC_Qc):
33695c635efSGarrett D'Amore 		return(MDOC_Qo);
33795c635efSGarrett D'Amore 	case (MDOC_Re):
33895c635efSGarrett D'Amore 		return(MDOC_Rs);
33995c635efSGarrett D'Amore 	case (MDOC_Sc):
34095c635efSGarrett D'Amore 		return(MDOC_So);
34195c635efSGarrett D'Amore 	case (MDOC_Xc):
34295c635efSGarrett D'Amore 		return(MDOC_Xo);
34395c635efSGarrett D'Amore 	default:
34495c635efSGarrett D'Amore 		return(tok);
34595c635efSGarrett D'Amore 	}
34695c635efSGarrett D'Amore 	/* NOTREACHED */
34795c635efSGarrett D'Amore }
34895c635efSGarrett D'Amore 
34995c635efSGarrett D'Amore 
35095c635efSGarrett D'Amore /*
35195c635efSGarrett D'Amore  * Rewinding to tok, how do we have to handle *p?
35295c635efSGarrett D'Amore  * REWIND_NONE: *p would delimit tok, but no tok scope is open
35395c635efSGarrett D'Amore  *   inside *p, so there is no need to rewind anything at all.
35495c635efSGarrett D'Amore  * REWIND_THIS: *p matches tok, so rewind *p and nothing else.
35595c635efSGarrett D'Amore  * REWIND_MORE: *p is implicit, rewind it and keep searching for tok.
35695c635efSGarrett D'Amore  * REWIND_FORCE: *p is explicit, but tok is full, force rewinding *p.
35795c635efSGarrett D'Amore  * REWIND_LATER: *p is explicit and still open, postpone rewinding.
35895c635efSGarrett D'Amore  * REWIND_ERROR: No tok block is open at all.
35995c635efSGarrett D'Amore  */
36095c635efSGarrett D'Amore static enum rew
36195c635efSGarrett D'Amore rew_dohalt(enum mdoct tok, enum mdoc_type type,
36295c635efSGarrett D'Amore 		const struct mdoc_node *p)
36395c635efSGarrett D'Amore {
36495c635efSGarrett D'Amore 
36595c635efSGarrett D'Amore 	/*
36695c635efSGarrett D'Amore 	 * No matching token, no delimiting block, no broken block.
36795c635efSGarrett D'Amore 	 * This can happen when full implicit macros are called for
36895c635efSGarrett D'Amore 	 * the first time but try to rewind their previous
36995c635efSGarrett D'Amore 	 * instance anyway.
37095c635efSGarrett D'Amore 	 */
37195c635efSGarrett D'Amore 	if (MDOC_ROOT == p->type)
37295c635efSGarrett D'Amore 		return(MDOC_BLOCK == type &&
37395c635efSGarrett D'Amore 		    MDOC_EXPLICIT & mdoc_macros[tok].flags ?
37495c635efSGarrett D'Amore 		    REWIND_ERROR : REWIND_NONE);
37595c635efSGarrett D'Amore 
37695c635efSGarrett D'Amore 	/*
37795c635efSGarrett D'Amore 	 * When starting to rewind, skip plain text
37895c635efSGarrett D'Amore 	 * and nodes that have already been rewound.
37995c635efSGarrett D'Amore 	 */
38095c635efSGarrett D'Amore 	if (MDOC_TEXT == p->type || MDOC_VALID & p->flags)
38195c635efSGarrett D'Amore 		return(REWIND_MORE);
38295c635efSGarrett D'Amore 
38395c635efSGarrett D'Amore 	/*
38495c635efSGarrett D'Amore 	 * The easiest case:  Found a matching token.
38595c635efSGarrett D'Amore 	 * This applies to both blocks and elements.
38695c635efSGarrett D'Amore 	 */
38795c635efSGarrett D'Amore 	tok = rew_alt(tok);
38895c635efSGarrett D'Amore 	if (tok == p->tok)
38995c635efSGarrett D'Amore 		return(p->end ? REWIND_NONE :
39095c635efSGarrett D'Amore 		    type == p->type ? REWIND_THIS : REWIND_MORE);
39195c635efSGarrett D'Amore 
39295c635efSGarrett D'Amore 	/*
39395c635efSGarrett D'Amore 	 * While elements do require rewinding for themselves,
39495c635efSGarrett D'Amore 	 * they never affect rewinding of other nodes.
39595c635efSGarrett D'Amore 	 */
39695c635efSGarrett D'Amore 	if (MDOC_ELEM == p->type)
39795c635efSGarrett D'Amore 		return(REWIND_MORE);
39895c635efSGarrett D'Amore 
39995c635efSGarrett D'Amore 	/*
40095c635efSGarrett D'Amore 	 * Blocks delimited by our target token get REWIND_MORE.
40195c635efSGarrett D'Amore 	 * Blocks delimiting our target token get REWIND_NONE.
40295c635efSGarrett D'Amore 	 */
40395c635efSGarrett D'Amore 	switch (tok) {
40495c635efSGarrett D'Amore 	case (MDOC_Bl):
40595c635efSGarrett D'Amore 		if (MDOC_It == p->tok)
40695c635efSGarrett D'Amore 			return(REWIND_MORE);
40795c635efSGarrett D'Amore 		break;
40895c635efSGarrett D'Amore 	case (MDOC_It):
40995c635efSGarrett D'Amore 		if (MDOC_BODY == p->type && MDOC_Bl == p->tok)
41095c635efSGarrett D'Amore 			return(REWIND_NONE);
41195c635efSGarrett D'Amore 		break;
41295c635efSGarrett D'Amore 	/*
41395c635efSGarrett D'Amore 	 * XXX Badly nested block handling still fails badly
41495c635efSGarrett D'Amore 	 * when one block is breaking two blocks of the same type.
41595c635efSGarrett D'Amore 	 * This is an incomplete and extremely ugly workaround,
41695c635efSGarrett D'Amore 	 * required to let the OpenBSD tree build.
41795c635efSGarrett D'Amore 	 */
41895c635efSGarrett D'Amore 	case (MDOC_Oo):
41995c635efSGarrett D'Amore 		if (MDOC_Op == p->tok)
42095c635efSGarrett D'Amore 			return(REWIND_MORE);
42195c635efSGarrett D'Amore 		break;
42295c635efSGarrett D'Amore 	case (MDOC_Nm):
42395c635efSGarrett D'Amore 		return(REWIND_NONE);
42495c635efSGarrett D'Amore 	case (MDOC_Nd):
42595c635efSGarrett D'Amore 		/* FALLTHROUGH */
42695c635efSGarrett D'Amore 	case (MDOC_Ss):
42795c635efSGarrett D'Amore 		if (MDOC_BODY == p->type && MDOC_Sh == p->tok)
42895c635efSGarrett D'Amore 			return(REWIND_NONE);
42995c635efSGarrett D'Amore 		/* FALLTHROUGH */
43095c635efSGarrett D'Amore 	case (MDOC_Sh):
43195c635efSGarrett D'Amore 		if (MDOC_Nd == p->tok || MDOC_Ss == p->tok ||
43295c635efSGarrett D'Amore 		    MDOC_Sh == p->tok)
43395c635efSGarrett D'Amore 			return(REWIND_MORE);
43495c635efSGarrett D'Amore 		break;
43595c635efSGarrett D'Amore 	default:
43695c635efSGarrett D'Amore 		break;
43795c635efSGarrett D'Amore 	}
43895c635efSGarrett D'Amore 
43995c635efSGarrett D'Amore 	/*
44095c635efSGarrett D'Amore 	 * Default block rewinding rules.
44195c635efSGarrett D'Amore 	 * In particular, always skip block end markers,
44295c635efSGarrett D'Amore 	 * and let all blocks rewind Nm children.
44395c635efSGarrett D'Amore 	 */
44495c635efSGarrett D'Amore 	if (ENDBODY_NOT != p->end || MDOC_Nm == p->tok ||
44595c635efSGarrett D'Amore 	    (MDOC_BLOCK == p->type &&
44695c635efSGarrett D'Amore 	    ! (MDOC_EXPLICIT & mdoc_macros[tok].flags)))
44795c635efSGarrett D'Amore 		return(REWIND_MORE);
44895c635efSGarrett D'Amore 
44995c635efSGarrett D'Amore 	/*
45095c635efSGarrett D'Amore 	 * By default, closing out full blocks
45195c635efSGarrett D'Amore 	 * forces closing of broken explicit blocks,
45295c635efSGarrett D'Amore 	 * while closing out partial blocks
45395c635efSGarrett D'Amore 	 * allows delayed rewinding by default.
45495c635efSGarrett D'Amore 	 */
45595c635efSGarrett D'Amore 	return (&blk_full == mdoc_macros[tok].fp ?
45695c635efSGarrett D'Amore 	    REWIND_FORCE : REWIND_LATER);
45795c635efSGarrett D'Amore }
45895c635efSGarrett D'Amore 
45995c635efSGarrett D'Amore 
46095c635efSGarrett D'Amore static int
46195c635efSGarrett D'Amore rew_elem(struct mdoc *mdoc, enum mdoct tok)
46295c635efSGarrett D'Amore {
46395c635efSGarrett D'Amore 	struct mdoc_node *n;
46495c635efSGarrett D'Amore 
46595c635efSGarrett D'Amore 	n = mdoc->last;
46695c635efSGarrett D'Amore 	if (MDOC_ELEM != n->type)
46795c635efSGarrett D'Amore 		n = n->parent;
46895c635efSGarrett D'Amore 	assert(MDOC_ELEM == n->type);
46995c635efSGarrett D'Amore 	assert(tok == n->tok);
47095c635efSGarrett D'Amore 
47195c635efSGarrett D'Amore 	return(rew_last(mdoc, n));
47295c635efSGarrett D'Amore }
47395c635efSGarrett D'Amore 
47495c635efSGarrett D'Amore 
47595c635efSGarrett D'Amore /*
47695c635efSGarrett D'Amore  * We are trying to close a block identified by tok,
47795c635efSGarrett D'Amore  * but the child block *broken is still open.
47895c635efSGarrett D'Amore  * Thus, postpone closing the tok block
47995c635efSGarrett D'Amore  * until the rew_sub call closing *broken.
48095c635efSGarrett D'Amore  */
48195c635efSGarrett D'Amore static int
48295c635efSGarrett D'Amore make_pending(struct mdoc_node *broken, enum mdoct tok,
483*698f87a4SGarrett D'Amore 		struct mdoc *mdoc, int line, int ppos)
48495c635efSGarrett D'Amore {
48595c635efSGarrett D'Amore 	struct mdoc_node *breaker;
48695c635efSGarrett D'Amore 
48795c635efSGarrett D'Amore 	/*
48895c635efSGarrett D'Amore 	 * Iterate backwards, searching for the block matching tok,
48995c635efSGarrett D'Amore 	 * that is, the block breaking the *broken block.
49095c635efSGarrett D'Amore 	 */
49195c635efSGarrett D'Amore 	for (breaker = broken->parent; breaker; breaker = breaker->parent) {
49295c635efSGarrett D'Amore 
49395c635efSGarrett D'Amore 		/*
49495c635efSGarrett D'Amore 		 * If the *broken block had already been broken before
49595c635efSGarrett D'Amore 		 * and we encounter its breaker, make the tok block
49695c635efSGarrett D'Amore 		 * pending on the inner breaker.
49795c635efSGarrett D'Amore 		 * Graphically, "[A breaker=[B broken=[C->B B] tok=A] C]"
49895c635efSGarrett D'Amore 		 * becomes "[A broken=[B [C->B B] tok=A] C]"
49995c635efSGarrett D'Amore 		 * and finally "[A [B->A [C->B B] A] C]".
50095c635efSGarrett D'Amore 		 */
50195c635efSGarrett D'Amore 		if (breaker == broken->pending) {
50295c635efSGarrett D'Amore 			broken = breaker;
50395c635efSGarrett D'Amore 			continue;
50495c635efSGarrett D'Amore 		}
50595c635efSGarrett D'Amore 
50695c635efSGarrett D'Amore 		if (REWIND_THIS != rew_dohalt(tok, MDOC_BLOCK, breaker))
50795c635efSGarrett D'Amore 			continue;
50895c635efSGarrett D'Amore 		if (MDOC_BODY == broken->type)
50995c635efSGarrett D'Amore 			broken = broken->parent;
51095c635efSGarrett D'Amore 
51195c635efSGarrett D'Amore 		/*
51295c635efSGarrett D'Amore 		 * Found the breaker.
51395c635efSGarrett D'Amore 		 * If another, outer breaker is already pending on
51495c635efSGarrett D'Amore 		 * the *broken block, we must not clobber the link
51595c635efSGarrett D'Amore 		 * to the outer breaker, but make it pending on the
51695c635efSGarrett D'Amore 		 * new, now inner breaker.
51795c635efSGarrett D'Amore 		 * Graphically, "[A breaker=[B broken=[C->A A] tok=B] C]"
51895c635efSGarrett D'Amore 		 * becomes "[A breaker=[B->A broken=[C A] tok=B] C]"
51995c635efSGarrett D'Amore 		 * and finally "[A [B->A [C->B A] B] C]".
52095c635efSGarrett D'Amore 		 */
52195c635efSGarrett D'Amore 		if (broken->pending) {
52295c635efSGarrett D'Amore 			struct mdoc_node *taker;
52395c635efSGarrett D'Amore 
52495c635efSGarrett D'Amore 			/*
52595c635efSGarrett D'Amore 			 * If the breaker had also been broken before,
52695c635efSGarrett D'Amore 			 * it cannot take on the outer breaker itself,
52795c635efSGarrett D'Amore 			 * but must hand it on to its own breakers.
52895c635efSGarrett D'Amore 			 * Graphically, this is the following situation:
52995c635efSGarrett D'Amore 			 * "[A [B breaker=[C->B B] broken=[D->A A] tok=C] D]"
53095c635efSGarrett D'Amore 			 * "[A taker=[B->A breaker=[C->B B] [D->C A] C] D]"
53195c635efSGarrett D'Amore 			 */
53295c635efSGarrett D'Amore 			taker = breaker;
53395c635efSGarrett D'Amore 			while (taker->pending)
53495c635efSGarrett D'Amore 				taker = taker->pending;
53595c635efSGarrett D'Amore 			taker->pending = broken->pending;
53695c635efSGarrett D'Amore 		}
53795c635efSGarrett D'Amore 		broken->pending = breaker;
538*698f87a4SGarrett D'Amore 		mandoc_vmsg(MANDOCERR_SCOPENEST, mdoc->parse, line, ppos,
53995c635efSGarrett D'Amore 				"%s breaks %s", mdoc_macronames[tok],
54095c635efSGarrett D'Amore 				mdoc_macronames[broken->tok]);
54195c635efSGarrett D'Amore 		return(1);
54295c635efSGarrett D'Amore 	}
54395c635efSGarrett D'Amore 
54495c635efSGarrett D'Amore 	/*
54595c635efSGarrett D'Amore 	 * Found no matching block for tok.
54695c635efSGarrett D'Amore 	 * Are you trying to close a block that is not open?
54795c635efSGarrett D'Amore 	 */
54895c635efSGarrett D'Amore 	return(0);
54995c635efSGarrett D'Amore }
55095c635efSGarrett D'Amore 
55195c635efSGarrett D'Amore 
55295c635efSGarrett D'Amore static int
553*698f87a4SGarrett D'Amore rew_sub(enum mdoc_type t, struct mdoc *mdoc,
55495c635efSGarrett D'Amore 		enum mdoct tok, int line, int ppos)
55595c635efSGarrett D'Amore {
55695c635efSGarrett D'Amore 	struct mdoc_node *n;
55795c635efSGarrett D'Amore 
558*698f87a4SGarrett D'Amore 	n = mdoc->last;
55995c635efSGarrett D'Amore 	while (n) {
56095c635efSGarrett D'Amore 		switch (rew_dohalt(tok, t, n)) {
56195c635efSGarrett D'Amore 		case (REWIND_NONE):
56295c635efSGarrett D'Amore 			return(1);
56395c635efSGarrett D'Amore 		case (REWIND_THIS):
564*698f87a4SGarrett D'Amore 			n->lastline = line -
565*698f87a4SGarrett D'Amore 			    (MDOC_NEWLINE & mdoc->flags &&
566*698f87a4SGarrett D'Amore 			     ! (MDOC_EXPLICIT & mdoc_macros[tok].flags));
56795c635efSGarrett D'Amore 			break;
56895c635efSGarrett D'Amore 		case (REWIND_FORCE):
569*698f87a4SGarrett D'Amore 			mandoc_vmsg(MANDOCERR_SCOPEBROKEN, mdoc->parse,
57095c635efSGarrett D'Amore 					line, ppos, "%s breaks %s",
57195c635efSGarrett D'Amore 					mdoc_macronames[tok],
57295c635efSGarrett D'Amore 					mdoc_macronames[n->tok]);
57395c635efSGarrett D'Amore 			/* FALLTHROUGH */
57495c635efSGarrett D'Amore 		case (REWIND_MORE):
575*698f87a4SGarrett D'Amore 			n->lastline = line -
576*698f87a4SGarrett D'Amore 			    (MDOC_NEWLINE & mdoc->flags ? 1 : 0);
57795c635efSGarrett D'Amore 			n = n->parent;
57895c635efSGarrett D'Amore 			continue;
57995c635efSGarrett D'Amore 		case (REWIND_LATER):
580*698f87a4SGarrett D'Amore 			if (make_pending(n, tok, mdoc, line, ppos) ||
58195c635efSGarrett D'Amore 			    MDOC_BLOCK != t)
58295c635efSGarrett D'Amore 				return(1);
58395c635efSGarrett D'Amore 			/* FALLTHROUGH */
58495c635efSGarrett D'Amore 		case (REWIND_ERROR):
585*698f87a4SGarrett D'Amore 			mdoc_pmsg(mdoc, line, ppos, MANDOCERR_NOSCOPE);
58695c635efSGarrett D'Amore 			return(1);
58795c635efSGarrett D'Amore 		}
58895c635efSGarrett D'Amore 		break;
58995c635efSGarrett D'Amore 	}
59095c635efSGarrett D'Amore 
59195c635efSGarrett D'Amore 	assert(n);
592*698f87a4SGarrett D'Amore 	if ( ! rew_last(mdoc, n))
59395c635efSGarrett D'Amore 		return(0);
59495c635efSGarrett D'Amore 
59595c635efSGarrett D'Amore 	/*
59695c635efSGarrett D'Amore 	 * The current block extends an enclosing block.
59795c635efSGarrett D'Amore 	 * Now that the current block ends, close the enclosing block, too.
59895c635efSGarrett D'Amore 	 */
59995c635efSGarrett D'Amore 	while (NULL != (n = n->pending)) {
600*698f87a4SGarrett D'Amore 		if ( ! rew_last(mdoc, n))
60195c635efSGarrett D'Amore 			return(0);
60295c635efSGarrett D'Amore 		if (MDOC_HEAD == n->type &&
603*698f87a4SGarrett D'Amore 		    ! mdoc_body_alloc(mdoc, n->line, n->pos, n->tok))
60495c635efSGarrett D'Amore 			return(0);
60595c635efSGarrett D'Amore 	}
60695c635efSGarrett D'Amore 
60795c635efSGarrett D'Amore 	return(1);
60895c635efSGarrett D'Amore }
60995c635efSGarrett D'Amore 
61095c635efSGarrett D'Amore /*
61195c635efSGarrett D'Amore  * Allocate a word and check whether it's punctuation or not.
61295c635efSGarrett D'Amore  * Punctuation consists of those tokens found in mdoc_isdelim().
61395c635efSGarrett D'Amore  */
61495c635efSGarrett D'Amore static int
615*698f87a4SGarrett D'Amore dword(struct mdoc *mdoc, int line, int col, const char *p,
616*698f87a4SGarrett D'Amore 		enum mdelim d, int may_append)
61795c635efSGarrett D'Amore {
61895c635efSGarrett D'Amore 
61995c635efSGarrett D'Amore 	if (DELIM_MAX == d)
62095c635efSGarrett D'Amore 		d = mdoc_isdelim(p);
62195c635efSGarrett D'Amore 
622*698f87a4SGarrett D'Amore 	if (may_append &&
623*698f87a4SGarrett D'Amore 	    ! ((MDOC_SYNOPSIS | MDOC_KEEP | MDOC_SMOFF) & mdoc->flags) &&
624*698f87a4SGarrett D'Amore 	    DELIM_NONE == d && MDOC_TEXT == mdoc->last->type &&
625*698f87a4SGarrett D'Amore 	    DELIM_NONE == mdoc_isdelim(mdoc->last->string)) {
626*698f87a4SGarrett D'Amore 		mdoc_word_append(mdoc, p);
627*698f87a4SGarrett D'Amore 		return(1);
628*698f87a4SGarrett D'Amore 	}
629*698f87a4SGarrett D'Amore 
630*698f87a4SGarrett D'Amore 	if ( ! mdoc_word_alloc(mdoc, line, col, p))
63195c635efSGarrett D'Amore 		return(0);
63295c635efSGarrett D'Amore 
63395c635efSGarrett D'Amore 	if (DELIM_OPEN == d)
634*698f87a4SGarrett D'Amore 		mdoc->last->flags |= MDOC_DELIMO;
63595c635efSGarrett D'Amore 
63695c635efSGarrett D'Amore 	/*
63795c635efSGarrett D'Amore 	 * Closing delimiters only suppress the preceding space
63895c635efSGarrett D'Amore 	 * when they follow something, not when they start a new
63995c635efSGarrett D'Amore 	 * block or element, and not when they follow `No'.
64095c635efSGarrett D'Amore 	 *
64195c635efSGarrett D'Amore 	 * XXX	Explicitly special-casing MDOC_No here feels
64295c635efSGarrett D'Amore 	 *	like a layering violation.  Find a better way
64395c635efSGarrett D'Amore 	 *	and solve this in the code related to `No'!
64495c635efSGarrett D'Amore 	 */
64595c635efSGarrett D'Amore 
646*698f87a4SGarrett D'Amore 	else if (DELIM_CLOSE == d && mdoc->last->prev &&
647*698f87a4SGarrett D'Amore 			mdoc->last->prev->tok != MDOC_No &&
648*698f87a4SGarrett D'Amore 			mdoc->last->parent->tok != MDOC_Fd)
649*698f87a4SGarrett D'Amore 		mdoc->last->flags |= MDOC_DELIMC;
65095c635efSGarrett D'Amore 
65195c635efSGarrett D'Amore 	return(1);
65295c635efSGarrett D'Amore }
65395c635efSGarrett D'Amore 
65495c635efSGarrett D'Amore static int
655*698f87a4SGarrett D'Amore append_delims(struct mdoc *mdoc, int line, int *pos, char *buf)
65695c635efSGarrett D'Amore {
65795c635efSGarrett D'Amore 	int		 la;
65895c635efSGarrett D'Amore 	enum margserr	 ac;
65995c635efSGarrett D'Amore 	char		*p;
66095c635efSGarrett D'Amore 
66195c635efSGarrett D'Amore 	if ('\0' == buf[*pos])
66295c635efSGarrett D'Amore 		return(1);
66395c635efSGarrett D'Amore 
66495c635efSGarrett D'Amore 	for (;;) {
66595c635efSGarrett D'Amore 		la = *pos;
666*698f87a4SGarrett D'Amore 		ac = mdoc_zargs(mdoc, line, pos, buf, &p);
66795c635efSGarrett D'Amore 
66895c635efSGarrett D'Amore 		if (ARGS_ERROR == ac)
66995c635efSGarrett D'Amore 			return(0);
67095c635efSGarrett D'Amore 		else if (ARGS_EOLN == ac)
67195c635efSGarrett D'Amore 			break;
67295c635efSGarrett D'Amore 
673*698f87a4SGarrett D'Amore 		dword(mdoc, line, la, p, DELIM_MAX, 1);
67495c635efSGarrett D'Amore 
67595c635efSGarrett D'Amore 		/*
67695c635efSGarrett D'Amore 		 * If we encounter end-of-sentence symbols, then trigger
67795c635efSGarrett D'Amore 		 * the double-space.
67895c635efSGarrett D'Amore 		 *
67995c635efSGarrett D'Amore 		 * XXX: it's easy to allow this to propagate outward to
68095c635efSGarrett D'Amore 		 * the last symbol, such that `. )' will cause the
68195c635efSGarrett D'Amore 		 * correct double-spacing.  However, (1) groff isn't
68295c635efSGarrett D'Amore 		 * smart enough to do this and (2) it would require
68395c635efSGarrett D'Amore 		 * knowing which symbols break this behaviour, for
68495c635efSGarrett D'Amore 		 * example, `.  ;' shouldn't propagate the double-space.
68595c635efSGarrett D'Amore 		 */
68695c635efSGarrett D'Amore 		if (mandoc_eos(p, strlen(p), 0))
687*698f87a4SGarrett D'Amore 			mdoc->last->flags |= MDOC_EOS;
68895c635efSGarrett D'Amore 	}
68995c635efSGarrett D'Amore 
69095c635efSGarrett D'Amore 	return(1);
69195c635efSGarrett D'Amore }
69295c635efSGarrett D'Amore 
69395c635efSGarrett D'Amore 
69495c635efSGarrett D'Amore /*
69595c635efSGarrett D'Amore  * Close out block partial/full explicit.
69695c635efSGarrett D'Amore  */
69795c635efSGarrett D'Amore static int
69895c635efSGarrett D'Amore blk_exp_close(MACRO_PROT_ARGS)
69995c635efSGarrett D'Amore {
70095c635efSGarrett D'Amore 	struct mdoc_node *body;		/* Our own body. */
70195c635efSGarrett D'Amore 	struct mdoc_node *later;	/* A sub-block starting later. */
70295c635efSGarrett D'Amore 	struct mdoc_node *n;		/* For searching backwards. */
70395c635efSGarrett D'Amore 
70495c635efSGarrett D'Amore 	int	 	 j, lastarg, maxargs, flushed, nl;
70595c635efSGarrett D'Amore 	enum margserr	 ac;
70695c635efSGarrett D'Amore 	enum mdoct	 atok, ntok;
70795c635efSGarrett D'Amore 	char		*p;
70895c635efSGarrett D'Amore 
709*698f87a4SGarrett D'Amore 	nl = MDOC_NEWLINE & mdoc->flags;
71095c635efSGarrett D'Amore 
71195c635efSGarrett D'Amore 	switch (tok) {
71295c635efSGarrett D'Amore 	case (MDOC_Ec):
71395c635efSGarrett D'Amore 		maxargs = 1;
71495c635efSGarrett D'Amore 		break;
715*698f87a4SGarrett D'Amore 	case (MDOC_Ek):
716*698f87a4SGarrett D'Amore 		mdoc->flags &= ~MDOC_KEEP;
71795c635efSGarrett D'Amore 	default:
71895c635efSGarrett D'Amore 		maxargs = 0;
71995c635efSGarrett D'Amore 		break;
72095c635efSGarrett D'Amore 	}
72195c635efSGarrett D'Amore 
72295c635efSGarrett D'Amore 	/*
72395c635efSGarrett D'Amore 	 * Search backwards for beginnings of blocks,
72495c635efSGarrett D'Amore 	 * both of our own and of pending sub-blocks.
72595c635efSGarrett D'Amore 	 */
72695c635efSGarrett D'Amore 	atok = rew_alt(tok);
72795c635efSGarrett D'Amore 	body = later = NULL;
728*698f87a4SGarrett D'Amore 	for (n = mdoc->last; n; n = n->parent) {
72995c635efSGarrett D'Amore 		if (MDOC_VALID & n->flags)
73095c635efSGarrett D'Amore 			continue;
73195c635efSGarrett D'Amore 
73295c635efSGarrett D'Amore 		/* Remember the start of our own body. */
73395c635efSGarrett D'Amore 		if (MDOC_BODY == n->type && atok == n->tok) {
73495c635efSGarrett D'Amore 			if (ENDBODY_NOT == n->end)
73595c635efSGarrett D'Amore 				body = n;
73695c635efSGarrett D'Amore 			continue;
73795c635efSGarrett D'Amore 		}
73895c635efSGarrett D'Amore 
73995c635efSGarrett D'Amore 		if (MDOC_BLOCK != n->type || MDOC_Nm == n->tok)
74095c635efSGarrett D'Amore 			continue;
74195c635efSGarrett D'Amore 		if (atok == n->tok) {
74295c635efSGarrett D'Amore 			assert(body);
74395c635efSGarrett D'Amore 
74495c635efSGarrett D'Amore 			/*
74595c635efSGarrett D'Amore 			 * Found the start of our own block.
74695c635efSGarrett D'Amore 			 * When there is no pending sub block,
74795c635efSGarrett D'Amore 			 * just proceed to closing out.
74895c635efSGarrett D'Amore 			 */
74995c635efSGarrett D'Amore 			if (NULL == later)
75095c635efSGarrett D'Amore 				break;
75195c635efSGarrett D'Amore 
75295c635efSGarrett D'Amore 			/*
75395c635efSGarrett D'Amore 			 * When there is a pending sub block,
75495c635efSGarrett D'Amore 			 * postpone closing out the current block
75595c635efSGarrett D'Amore 			 * until the rew_sub() closing out the sub-block.
75695c635efSGarrett D'Amore 			 */
757*698f87a4SGarrett D'Amore 			make_pending(later, tok, mdoc, line, ppos);
75895c635efSGarrett D'Amore 
75995c635efSGarrett D'Amore 			/*
76095c635efSGarrett D'Amore 			 * Mark the place where the formatting - but not
76195c635efSGarrett D'Amore 			 * the scope - of the current block ends.
76295c635efSGarrett D'Amore 			 */
763*698f87a4SGarrett D'Amore 			if ( ! mdoc_endbody_alloc(mdoc, line, ppos,
76495c635efSGarrett D'Amore 			    atok, body, ENDBODY_SPACE))
76595c635efSGarrett D'Amore 				return(0);
76695c635efSGarrett D'Amore 			break;
76795c635efSGarrett D'Amore 		}
76895c635efSGarrett D'Amore 
76995c635efSGarrett D'Amore 		/*
77095c635efSGarrett D'Amore 		 * When finding an open sub block, remember the last
77195c635efSGarrett D'Amore 		 * open explicit block, or, in case there are only
77295c635efSGarrett D'Amore 		 * implicit ones, the first open implicit block.
77395c635efSGarrett D'Amore 		 */
77495c635efSGarrett D'Amore 		if (later &&
77595c635efSGarrett D'Amore 		    MDOC_EXPLICIT & mdoc_macros[later->tok].flags)
77695c635efSGarrett D'Amore 			continue;
777*698f87a4SGarrett D'Amore 		if (MDOC_It != n->tok)
77895c635efSGarrett D'Amore 			later = n;
77995c635efSGarrett D'Amore 	}
78095c635efSGarrett D'Amore 
78195c635efSGarrett D'Amore 	if ( ! (MDOC_CALLABLE & mdoc_macros[tok].flags)) {
78295c635efSGarrett D'Amore 		/* FIXME: do this in validate */
78395c635efSGarrett D'Amore 		if (buf[*pos])
784*698f87a4SGarrett D'Amore 			mdoc_pmsg(mdoc, line, ppos, MANDOCERR_ARGSLOST);
78595c635efSGarrett D'Amore 
786*698f87a4SGarrett D'Amore 		if ( ! rew_sub(MDOC_BODY, mdoc, tok, line, ppos))
78795c635efSGarrett D'Amore 			return(0);
788*698f87a4SGarrett D'Amore 		return(rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos));
78995c635efSGarrett D'Amore 	}
79095c635efSGarrett D'Amore 
791*698f87a4SGarrett D'Amore 	if ( ! rew_sub(MDOC_BODY, mdoc, tok, line, ppos))
79295c635efSGarrett D'Amore 		return(0);
79395c635efSGarrett D'Amore 
79495c635efSGarrett D'Amore 	if (NULL == later && maxargs > 0)
795*698f87a4SGarrett D'Amore 		if ( ! mdoc_tail_alloc(mdoc, line, ppos, rew_alt(tok)))
79695c635efSGarrett D'Amore 			return(0);
79795c635efSGarrett D'Amore 
79895c635efSGarrett D'Amore 	for (flushed = j = 0; ; j++) {
79995c635efSGarrett D'Amore 		lastarg = *pos;
80095c635efSGarrett D'Amore 
80195c635efSGarrett D'Amore 		if (j == maxargs && ! flushed) {
802*698f87a4SGarrett D'Amore 			if ( ! rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos))
80395c635efSGarrett D'Amore 				return(0);
80495c635efSGarrett D'Amore 			flushed = 1;
80595c635efSGarrett D'Amore 		}
80695c635efSGarrett D'Amore 
807*698f87a4SGarrett D'Amore 		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
80895c635efSGarrett D'Amore 
80995c635efSGarrett D'Amore 		if (ARGS_ERROR == ac)
81095c635efSGarrett D'Amore 			return(0);
81195c635efSGarrett D'Amore 		if (ARGS_PUNCT == ac)
81295c635efSGarrett D'Amore 			break;
81395c635efSGarrett D'Amore 		if (ARGS_EOLN == ac)
81495c635efSGarrett D'Amore 			break;
81595c635efSGarrett D'Amore 
81695c635efSGarrett D'Amore 		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
81795c635efSGarrett D'Amore 
81895c635efSGarrett D'Amore 		if (MDOC_MAX == ntok) {
819*698f87a4SGarrett D'Amore 			if ( ! dword(mdoc, line, lastarg, p, DELIM_MAX,
820*698f87a4SGarrett D'Amore 			    MDOC_JOIN & mdoc_macros[tok].flags))
82195c635efSGarrett D'Amore 				return(0);
82295c635efSGarrett D'Amore 			continue;
82395c635efSGarrett D'Amore 		}
82495c635efSGarrett D'Amore 
82595c635efSGarrett D'Amore 		if ( ! flushed) {
826*698f87a4SGarrett D'Amore 			if ( ! rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos))
82795c635efSGarrett D'Amore 				return(0);
82895c635efSGarrett D'Amore 			flushed = 1;
82995c635efSGarrett D'Amore 		}
830*698f87a4SGarrett D'Amore 
831*698f87a4SGarrett D'Amore 		mdoc->flags &= ~MDOC_NEWLINE;
832*698f87a4SGarrett D'Amore 
833*698f87a4SGarrett D'Amore 		if ( ! mdoc_macro(mdoc, ntok, line, lastarg, pos, buf))
83495c635efSGarrett D'Amore 			return(0);
83595c635efSGarrett D'Amore 		break;
83695c635efSGarrett D'Amore 	}
83795c635efSGarrett D'Amore 
838*698f87a4SGarrett D'Amore 	if ( ! flushed && ! rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos))
83995c635efSGarrett D'Amore 		return(0);
84095c635efSGarrett D'Amore 
84195c635efSGarrett D'Amore 	if ( ! nl)
84295c635efSGarrett D'Amore 		return(1);
843*698f87a4SGarrett D'Amore 	return(append_delims(mdoc, line, pos, buf));
84495c635efSGarrett D'Amore }
84595c635efSGarrett D'Amore 
84695c635efSGarrett D'Amore 
84795c635efSGarrett D'Amore static int
84895c635efSGarrett D'Amore in_line(MACRO_PROT_ARGS)
84995c635efSGarrett D'Amore {
85095c635efSGarrett D'Amore 	int		 la, scope, cnt, nc, nl;
85195c635efSGarrett D'Amore 	enum margverr	 av;
85295c635efSGarrett D'Amore 	enum mdoct	 ntok;
85395c635efSGarrett D'Amore 	enum margserr	 ac;
85495c635efSGarrett D'Amore 	enum mdelim	 d;
85595c635efSGarrett D'Amore 	struct mdoc_arg	*arg;
85695c635efSGarrett D'Amore 	char		*p;
85795c635efSGarrett D'Amore 
858*698f87a4SGarrett D'Amore 	nl = MDOC_NEWLINE & mdoc->flags;
85995c635efSGarrett D'Amore 
86095c635efSGarrett D'Amore 	/*
86195c635efSGarrett D'Amore 	 * Whether we allow ignored elements (those without content,
86295c635efSGarrett D'Amore 	 * usually because of reserved words) to squeak by.
86395c635efSGarrett D'Amore 	 */
86495c635efSGarrett D'Amore 
86595c635efSGarrett D'Amore 	switch (tok) {
86695c635efSGarrett D'Amore 	case (MDOC_An):
86795c635efSGarrett D'Amore 		/* FALLTHROUGH */
86895c635efSGarrett D'Amore 	case (MDOC_Ar):
86995c635efSGarrett D'Amore 		/* FALLTHROUGH */
87095c635efSGarrett D'Amore 	case (MDOC_Fl):
87195c635efSGarrett D'Amore 		/* FALLTHROUGH */
87295c635efSGarrett D'Amore 	case (MDOC_Mt):
87395c635efSGarrett D'Amore 		/* FALLTHROUGH */
87495c635efSGarrett D'Amore 	case (MDOC_Nm):
87595c635efSGarrett D'Amore 		/* FALLTHROUGH */
87695c635efSGarrett D'Amore 	case (MDOC_Pa):
87795c635efSGarrett D'Amore 		nc = 1;
87895c635efSGarrett D'Amore 		break;
87995c635efSGarrett D'Amore 	default:
88095c635efSGarrett D'Amore 		nc = 0;
88195c635efSGarrett D'Amore 		break;
88295c635efSGarrett D'Amore 	}
88395c635efSGarrett D'Amore 
88495c635efSGarrett D'Amore 	for (arg = NULL;; ) {
88595c635efSGarrett D'Amore 		la = *pos;
886*698f87a4SGarrett D'Amore 		av = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
88795c635efSGarrett D'Amore 
88895c635efSGarrett D'Amore 		if (ARGV_WORD == av) {
88995c635efSGarrett D'Amore 			*pos = la;
89095c635efSGarrett D'Amore 			break;
89195c635efSGarrett D'Amore 		}
89295c635efSGarrett D'Amore 		if (ARGV_EOLN == av)
89395c635efSGarrett D'Amore 			break;
89495c635efSGarrett D'Amore 		if (ARGV_ARG == av)
89595c635efSGarrett D'Amore 			continue;
89695c635efSGarrett D'Amore 
89795c635efSGarrett D'Amore 		mdoc_argv_free(arg);
89895c635efSGarrett D'Amore 		return(0);
89995c635efSGarrett D'Amore 	}
90095c635efSGarrett D'Amore 
90195c635efSGarrett D'Amore 	for (cnt = scope = 0;; ) {
90295c635efSGarrett D'Amore 		la = *pos;
903*698f87a4SGarrett D'Amore 		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
90495c635efSGarrett D'Amore 
90595c635efSGarrett D'Amore 		if (ARGS_ERROR == ac)
90695c635efSGarrett D'Amore 			return(0);
90795c635efSGarrett D'Amore 		if (ARGS_EOLN == ac)
90895c635efSGarrett D'Amore 			break;
90995c635efSGarrett D'Amore 		if (ARGS_PUNCT == ac)
91095c635efSGarrett D'Amore 			break;
91195c635efSGarrett D'Amore 
91295c635efSGarrett D'Amore 		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
91395c635efSGarrett D'Amore 
91495c635efSGarrett D'Amore 		/*
91595c635efSGarrett D'Amore 		 * In this case, we've located a submacro and must
91695c635efSGarrett D'Amore 		 * execute it.  Close out scope, if open.  If no
91795c635efSGarrett D'Amore 		 * elements have been generated, either create one (nc)
91895c635efSGarrett D'Amore 		 * or raise a warning.
91995c635efSGarrett D'Amore 		 */
92095c635efSGarrett D'Amore 
92195c635efSGarrett D'Amore 		if (MDOC_MAX != ntok) {
922*698f87a4SGarrett D'Amore 			if (scope && ! rew_elem(mdoc, tok))
92395c635efSGarrett D'Amore 				return(0);
92495c635efSGarrett D'Amore 			if (nc && 0 == cnt) {
925*698f87a4SGarrett D'Amore 				if ( ! mdoc_elem_alloc(mdoc, line,
926*698f87a4SGarrett D'Amore 						ppos, tok, arg))
92795c635efSGarrett D'Amore 					return(0);
928*698f87a4SGarrett D'Amore 				if ( ! rew_last(mdoc, mdoc->last))
92995c635efSGarrett D'Amore 					return(0);
93095c635efSGarrett D'Amore 			} else if ( ! nc && 0 == cnt) {
93195c635efSGarrett D'Amore 				mdoc_argv_free(arg);
932*698f87a4SGarrett D'Amore 				mdoc_pmsg(mdoc, line, ppos,
933*698f87a4SGarrett D'Amore 					MANDOCERR_MACROEMPTY);
93495c635efSGarrett D'Amore 			}
93595c635efSGarrett D'Amore 
936*698f87a4SGarrett D'Amore 			if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf))
93795c635efSGarrett D'Amore 				return(0);
93895c635efSGarrett D'Amore 			if ( ! nl)
93995c635efSGarrett D'Amore 				return(1);
940*698f87a4SGarrett D'Amore 			return(append_delims(mdoc, line, pos, buf));
94195c635efSGarrett D'Amore 		}
94295c635efSGarrett D'Amore 
94395c635efSGarrett D'Amore 		/*
94495c635efSGarrett D'Amore 		 * Non-quote-enclosed punctuation.  Set up our scope, if
94595c635efSGarrett D'Amore 		 * a word; rewind the scope, if a delimiter; then append
94695c635efSGarrett D'Amore 		 * the word.
94795c635efSGarrett D'Amore 		 */
94895c635efSGarrett D'Amore 
94995c635efSGarrett D'Amore 		d = ARGS_QWORD == ac ? DELIM_NONE : mdoc_isdelim(p);
95095c635efSGarrett D'Amore 
95195c635efSGarrett D'Amore 		if (DELIM_NONE != d) {
95295c635efSGarrett D'Amore 			/*
95395c635efSGarrett D'Amore 			 * If we encounter closing punctuation, no word
95495c635efSGarrett D'Amore 			 * has been omitted, no scope is open, and we're
95595c635efSGarrett D'Amore 			 * allowed to have an empty element, then start
95695c635efSGarrett D'Amore 			 * a new scope.  `Ar', `Fl', and `Li', only do
95795c635efSGarrett D'Amore 			 * this once per invocation.  There may be more
95895c635efSGarrett D'Amore 			 * of these (all of them?).
95995c635efSGarrett D'Amore 			 */
96095c635efSGarrett D'Amore 			if (0 == cnt && (nc || MDOC_Li == tok) &&
96195c635efSGarrett D'Amore 					DELIM_CLOSE == d && ! scope) {
962*698f87a4SGarrett D'Amore 				if ( ! mdoc_elem_alloc(mdoc, line,
963*698f87a4SGarrett D'Amore 						ppos, tok, arg))
96495c635efSGarrett D'Amore 					return(0);
96595c635efSGarrett D'Amore 				if (MDOC_Ar == tok || MDOC_Li == tok ||
96695c635efSGarrett D'Amore 						MDOC_Fl == tok)
96795c635efSGarrett D'Amore 					cnt++;
96895c635efSGarrett D'Amore 				scope = 1;
96995c635efSGarrett D'Amore 			}
97095c635efSGarrett D'Amore 			/*
97195c635efSGarrett D'Amore 			 * Close out our scope, if one is open, before
97295c635efSGarrett D'Amore 			 * any punctuation.
97395c635efSGarrett D'Amore 			 */
974*698f87a4SGarrett D'Amore 			if (scope && ! rew_elem(mdoc, tok))
97595c635efSGarrett D'Amore 				return(0);
97695c635efSGarrett D'Amore 			scope = 0;
97795c635efSGarrett D'Amore 		} else if ( ! scope) {
978*698f87a4SGarrett D'Amore 			if ( ! mdoc_elem_alloc(mdoc, line, ppos, tok, arg))
97995c635efSGarrett D'Amore 				return(0);
98095c635efSGarrett D'Amore 			scope = 1;
98195c635efSGarrett D'Amore 		}
98295c635efSGarrett D'Amore 
98395c635efSGarrett D'Amore 		if (DELIM_NONE == d)
98495c635efSGarrett D'Amore 			cnt++;
98595c635efSGarrett D'Amore 
986*698f87a4SGarrett D'Amore 		if ( ! dword(mdoc, line, la, p, d,
987*698f87a4SGarrett D'Amore 		    MDOC_JOIN & mdoc_macros[tok].flags))
98895c635efSGarrett D'Amore 			return(0);
98995c635efSGarrett D'Amore 
99095c635efSGarrett D'Amore 		/*
99195c635efSGarrett D'Amore 		 * `Fl' macros have their scope re-opened with each new
99295c635efSGarrett D'Amore 		 * word so that the `-' can be added to each one without
99395c635efSGarrett D'Amore 		 * having to parse out spaces.
99495c635efSGarrett D'Amore 		 */
99595c635efSGarrett D'Amore 		if (scope && MDOC_Fl == tok) {
996*698f87a4SGarrett D'Amore 			if ( ! rew_elem(mdoc, tok))
99795c635efSGarrett D'Amore 				return(0);
99895c635efSGarrett D'Amore 			scope = 0;
99995c635efSGarrett D'Amore 		}
100095c635efSGarrett D'Amore 	}
100195c635efSGarrett D'Amore 
1002*698f87a4SGarrett D'Amore 	if (scope && ! rew_elem(mdoc, tok))
100395c635efSGarrett D'Amore 		return(0);
100495c635efSGarrett D'Amore 
100595c635efSGarrett D'Amore 	/*
100695c635efSGarrett D'Amore 	 * If no elements have been collected and we're allowed to have
100795c635efSGarrett D'Amore 	 * empties (nc), open a scope and close it out.  Otherwise,
100895c635efSGarrett D'Amore 	 * raise a warning.
100995c635efSGarrett D'Amore 	 */
101095c635efSGarrett D'Amore 
101195c635efSGarrett D'Amore 	if (nc && 0 == cnt) {
1012*698f87a4SGarrett D'Amore 		if ( ! mdoc_elem_alloc(mdoc, line, ppos, tok, arg))
101395c635efSGarrett D'Amore 			return(0);
1014*698f87a4SGarrett D'Amore 		if ( ! rew_last(mdoc, mdoc->last))
101595c635efSGarrett D'Amore 			return(0);
101695c635efSGarrett D'Amore 	} else if ( ! nc && 0 == cnt) {
101795c635efSGarrett D'Amore 		mdoc_argv_free(arg);
1018*698f87a4SGarrett D'Amore 		mdoc_pmsg(mdoc, line, ppos, MANDOCERR_MACROEMPTY);
101995c635efSGarrett D'Amore 	}
102095c635efSGarrett D'Amore 
102195c635efSGarrett D'Amore 	if ( ! nl)
102295c635efSGarrett D'Amore 		return(1);
1023*698f87a4SGarrett D'Amore 	return(append_delims(mdoc, line, pos, buf));
102495c635efSGarrett D'Amore }
102595c635efSGarrett D'Amore 
102695c635efSGarrett D'Amore 
102795c635efSGarrett D'Amore static int
102895c635efSGarrett D'Amore blk_full(MACRO_PROT_ARGS)
102995c635efSGarrett D'Amore {
103095c635efSGarrett D'Amore 	int		  la, nl, nparsed;
103195c635efSGarrett D'Amore 	struct mdoc_arg	 *arg;
103295c635efSGarrett D'Amore 	struct mdoc_node *head; /* save of head macro */
103395c635efSGarrett D'Amore 	struct mdoc_node *body; /* save of body macro */
103495c635efSGarrett D'Amore 	struct mdoc_node *n;
103595c635efSGarrett D'Amore 	enum mdoc_type	  mtt;
103695c635efSGarrett D'Amore 	enum mdoct	  ntok;
103795c635efSGarrett D'Amore 	enum margserr	  ac, lac;
103895c635efSGarrett D'Amore 	enum margverr	  av;
103995c635efSGarrett D'Amore 	char		 *p;
104095c635efSGarrett D'Amore 
1041*698f87a4SGarrett D'Amore 	nl = MDOC_NEWLINE & mdoc->flags;
104295c635efSGarrett D'Amore 
104395c635efSGarrett D'Amore 	/* Close out prior implicit scope. */
104495c635efSGarrett D'Amore 
104595c635efSGarrett D'Amore 	if ( ! (MDOC_EXPLICIT & mdoc_macros[tok].flags)) {
1046*698f87a4SGarrett D'Amore 		if ( ! rew_sub(MDOC_BODY, mdoc, tok, line, ppos))
104795c635efSGarrett D'Amore 			return(0);
1048*698f87a4SGarrett D'Amore 		if ( ! rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos))
104995c635efSGarrett D'Amore 			return(0);
105095c635efSGarrett D'Amore 	}
105195c635efSGarrett D'Amore 
105295c635efSGarrett D'Amore 	/*
105395c635efSGarrett D'Amore 	 * This routine accommodates implicitly- and explicitly-scoped
105495c635efSGarrett D'Amore 	 * macro openings.  Implicit ones first close out prior scope
105595c635efSGarrett D'Amore 	 * (seen above).  Delay opening the head until necessary to
105695c635efSGarrett D'Amore 	 * allow leading punctuation to print.  Special consideration
105795c635efSGarrett D'Amore 	 * for `It -column', which has phrase-part syntax instead of
105895c635efSGarrett D'Amore 	 * regular child nodes.
105995c635efSGarrett D'Amore 	 */
106095c635efSGarrett D'Amore 
106195c635efSGarrett D'Amore 	for (arg = NULL;; ) {
106295c635efSGarrett D'Amore 		la = *pos;
1063*698f87a4SGarrett D'Amore 		av = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
106495c635efSGarrett D'Amore 
106595c635efSGarrett D'Amore 		if (ARGV_WORD == av) {
106695c635efSGarrett D'Amore 			*pos = la;
106795c635efSGarrett D'Amore 			break;
106895c635efSGarrett D'Amore 		}
106995c635efSGarrett D'Amore 
107095c635efSGarrett D'Amore 		if (ARGV_EOLN == av)
107195c635efSGarrett D'Amore 			break;
107295c635efSGarrett D'Amore 		if (ARGV_ARG == av)
107395c635efSGarrett D'Amore 			continue;
107495c635efSGarrett D'Amore 
107595c635efSGarrett D'Amore 		mdoc_argv_free(arg);
107695c635efSGarrett D'Amore 		return(0);
107795c635efSGarrett D'Amore 	}
107895c635efSGarrett D'Amore 
1079*698f87a4SGarrett D'Amore 	if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, arg))
108095c635efSGarrett D'Amore 		return(0);
108195c635efSGarrett D'Amore 
108295c635efSGarrett D'Amore 	head = body = NULL;
108395c635efSGarrett D'Amore 
108495c635efSGarrett D'Amore 	/*
108595c635efSGarrett D'Amore 	 * Exception: Heads of `It' macros in `-diag' lists are not
108695c635efSGarrett D'Amore 	 * parsed, even though `It' macros in general are parsed.
108795c635efSGarrett D'Amore 	 */
108895c635efSGarrett D'Amore 	nparsed = MDOC_It == tok &&
1089*698f87a4SGarrett D'Amore 		MDOC_Bl == mdoc->last->parent->tok &&
1090*698f87a4SGarrett D'Amore 		LIST_diag == mdoc->last->parent->norm->Bl.type;
109195c635efSGarrett D'Amore 
109295c635efSGarrett D'Amore 	/*
109395c635efSGarrett D'Amore 	 * The `Nd' macro has all arguments in its body: it's a hybrid
109495c635efSGarrett D'Amore 	 * of block partial-explicit and full-implicit.  Stupid.
109595c635efSGarrett D'Amore 	 */
109695c635efSGarrett D'Amore 
109795c635efSGarrett D'Amore 	if (MDOC_Nd == tok) {
1098*698f87a4SGarrett D'Amore 		if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
109995c635efSGarrett D'Amore 			return(0);
1100*698f87a4SGarrett D'Amore 		head = mdoc->last;
1101*698f87a4SGarrett D'Amore 		if ( ! rew_sub(MDOC_HEAD, mdoc, tok, line, ppos))
110295c635efSGarrett D'Amore 			return(0);
1103*698f87a4SGarrett D'Amore 		if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
110495c635efSGarrett D'Amore 			return(0);
1105*698f87a4SGarrett D'Amore 		body = mdoc->last;
110695c635efSGarrett D'Amore 	}
110795c635efSGarrett D'Amore 
1108*698f87a4SGarrett D'Amore 	if (MDOC_Bk == tok)
1109*698f87a4SGarrett D'Amore 		mdoc->flags |= MDOC_KEEP;
1110*698f87a4SGarrett D'Amore 
111195c635efSGarrett D'Amore 	ac = ARGS_ERROR;
111295c635efSGarrett D'Amore 
111395c635efSGarrett D'Amore 	for ( ; ; ) {
111495c635efSGarrett D'Amore 		la = *pos;
111595c635efSGarrett D'Amore 		/* Initialise last-phrase-type with ARGS_PEND. */
111695c635efSGarrett D'Amore 		lac = ARGS_ERROR == ac ? ARGS_PEND : ac;
1117*698f87a4SGarrett D'Amore 		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
111895c635efSGarrett D'Amore 
111995c635efSGarrett D'Amore 		if (ARGS_PUNCT == ac)
112095c635efSGarrett D'Amore 			break;
112195c635efSGarrett D'Amore 
112295c635efSGarrett D'Amore 		if (ARGS_ERROR == ac)
112395c635efSGarrett D'Amore 			return(0);
112495c635efSGarrett D'Amore 
112595c635efSGarrett D'Amore 		if (ARGS_EOLN == ac) {
112695c635efSGarrett D'Amore 			if (ARGS_PPHRASE != lac && ARGS_PHRASE != lac)
112795c635efSGarrett D'Amore 				break;
112895c635efSGarrett D'Amore 			/*
112995c635efSGarrett D'Amore 			 * This is necessary: if the last token on a
113095c635efSGarrett D'Amore 			 * line is a `Ta' or tab, then we'll get
113195c635efSGarrett D'Amore 			 * ARGS_EOLN, so we must be smart enough to
113295c635efSGarrett D'Amore 			 * reopen our scope if the last parse was a
113395c635efSGarrett D'Amore 			 * phrase or partial phrase.
113495c635efSGarrett D'Amore 			 */
1135*698f87a4SGarrett D'Amore 			if ( ! rew_sub(MDOC_BODY, mdoc, tok, line, ppos))
113695c635efSGarrett D'Amore 				return(0);
1137*698f87a4SGarrett D'Amore 			if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
113895c635efSGarrett D'Amore 				return(0);
1139*698f87a4SGarrett D'Amore 			body = mdoc->last;
114095c635efSGarrett D'Amore 			break;
114195c635efSGarrett D'Amore 		}
114295c635efSGarrett D'Amore 
114395c635efSGarrett D'Amore 		/*
114495c635efSGarrett D'Amore 		 * Emit leading punctuation (i.e., punctuation before
114595c635efSGarrett D'Amore 		 * the MDOC_HEAD) for non-phrase types.
114695c635efSGarrett D'Amore 		 */
114795c635efSGarrett D'Amore 
114895c635efSGarrett D'Amore 		if (NULL == head &&
114995c635efSGarrett D'Amore 				ARGS_PEND != ac &&
115095c635efSGarrett D'Amore 				ARGS_PHRASE != ac &&
115195c635efSGarrett D'Amore 				ARGS_PPHRASE != ac &&
115295c635efSGarrett D'Amore 				ARGS_QWORD != ac &&
115395c635efSGarrett D'Amore 				DELIM_OPEN == mdoc_isdelim(p)) {
1154*698f87a4SGarrett D'Amore 			if ( ! dword(mdoc, line, la, p, DELIM_OPEN, 0))
115595c635efSGarrett D'Amore 				return(0);
115695c635efSGarrett D'Amore 			continue;
115795c635efSGarrett D'Amore 		}
115895c635efSGarrett D'Amore 
115995c635efSGarrett D'Amore 		/* Open a head if one hasn't been opened. */
116095c635efSGarrett D'Amore 
116195c635efSGarrett D'Amore 		if (NULL == head) {
1162*698f87a4SGarrett D'Amore 			if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
116395c635efSGarrett D'Amore 				return(0);
1164*698f87a4SGarrett D'Amore 			head = mdoc->last;
116595c635efSGarrett D'Amore 		}
116695c635efSGarrett D'Amore 
116795c635efSGarrett D'Amore 		if (ARGS_PHRASE == ac ||
116895c635efSGarrett D'Amore 				ARGS_PEND == ac ||
116995c635efSGarrett D'Amore 				ARGS_PPHRASE == ac) {
117095c635efSGarrett D'Amore 			/*
117195c635efSGarrett D'Amore 			 * If we haven't opened a body yet, rewind the
117295c635efSGarrett D'Amore 			 * head; if we have, rewind that instead.
117395c635efSGarrett D'Amore 			 */
117495c635efSGarrett D'Amore 
117595c635efSGarrett D'Amore 			mtt = body ? MDOC_BODY : MDOC_HEAD;
1176*698f87a4SGarrett D'Amore 			if ( ! rew_sub(mtt, mdoc, tok, line, ppos))
117795c635efSGarrett D'Amore 				return(0);
117895c635efSGarrett D'Amore 
117995c635efSGarrett D'Amore 			/* Then allocate our body context. */
118095c635efSGarrett D'Amore 
1181*698f87a4SGarrett D'Amore 			if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
118295c635efSGarrett D'Amore 				return(0);
1183*698f87a4SGarrett D'Amore 			body = mdoc->last;
118495c635efSGarrett D'Amore 
118595c635efSGarrett D'Amore 			/*
118695c635efSGarrett D'Amore 			 * Process phrases: set whether we're in a
118795c635efSGarrett D'Amore 			 * partial-phrase (this effects line handling)
118895c635efSGarrett D'Amore 			 * then call down into the phrase parser.
118995c635efSGarrett D'Amore 			 */
119095c635efSGarrett D'Amore 
119195c635efSGarrett D'Amore 			if (ARGS_PPHRASE == ac)
1192*698f87a4SGarrett D'Amore 				mdoc->flags |= MDOC_PPHRASE;
119395c635efSGarrett D'Amore 			if (ARGS_PEND == ac && ARGS_PPHRASE == lac)
1194*698f87a4SGarrett D'Amore 				mdoc->flags |= MDOC_PPHRASE;
119595c635efSGarrett D'Amore 
1196*698f87a4SGarrett D'Amore 			if ( ! phrase(mdoc, line, la, buf))
119795c635efSGarrett D'Amore 				return(0);
119895c635efSGarrett D'Amore 
1199*698f87a4SGarrett D'Amore 			mdoc->flags &= ~MDOC_PPHRASE;
120095c635efSGarrett D'Amore 			continue;
120195c635efSGarrett D'Amore 		}
120295c635efSGarrett D'Amore 
120395c635efSGarrett D'Amore 		ntok = nparsed || ARGS_QWORD == ac ?
120495c635efSGarrett D'Amore 			MDOC_MAX : lookup(tok, p);
120595c635efSGarrett D'Amore 
120695c635efSGarrett D'Amore 		if (MDOC_MAX == ntok) {
1207*698f87a4SGarrett D'Amore 			if ( ! dword(mdoc, line, la, p, DELIM_MAX,
1208*698f87a4SGarrett D'Amore 			    MDOC_JOIN & mdoc_macros[tok].flags))
120995c635efSGarrett D'Amore 				return(0);
121095c635efSGarrett D'Amore 			continue;
121195c635efSGarrett D'Amore 		}
121295c635efSGarrett D'Amore 
1213*698f87a4SGarrett D'Amore 		if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf))
121495c635efSGarrett D'Amore 			return(0);
121595c635efSGarrett D'Amore 		break;
121695c635efSGarrett D'Amore 	}
121795c635efSGarrett D'Amore 
121895c635efSGarrett D'Amore 	if (NULL == head) {
1219*698f87a4SGarrett D'Amore 		if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
122095c635efSGarrett D'Amore 			return(0);
1221*698f87a4SGarrett D'Amore 		head = mdoc->last;
122295c635efSGarrett D'Amore 	}
122395c635efSGarrett D'Amore 
1224*698f87a4SGarrett D'Amore 	if (nl && ! append_delims(mdoc, line, pos, buf))
122595c635efSGarrett D'Amore 		return(0);
122695c635efSGarrett D'Amore 
122795c635efSGarrett D'Amore 	/* If we've already opened our body, exit now. */
122895c635efSGarrett D'Amore 
122995c635efSGarrett D'Amore 	if (NULL != body)
123095c635efSGarrett D'Amore 		goto out;
123195c635efSGarrett D'Amore 
123295c635efSGarrett D'Amore 	/*
123395c635efSGarrett D'Amore 	 * If there is an open (i.e., unvalidated) sub-block requiring
123495c635efSGarrett D'Amore 	 * explicit close-out, postpone switching the current block from
123595c635efSGarrett D'Amore 	 * head to body until the rew_sub() call closing out that
123695c635efSGarrett D'Amore 	 * sub-block.
123795c635efSGarrett D'Amore 	 */
1238*698f87a4SGarrett D'Amore 	for (n = mdoc->last; n && n != head; n = n->parent) {
123995c635efSGarrett D'Amore 		if (MDOC_BLOCK == n->type &&
124095c635efSGarrett D'Amore 				MDOC_EXPLICIT & mdoc_macros[n->tok].flags &&
124195c635efSGarrett D'Amore 				! (MDOC_VALID & n->flags)) {
124295c635efSGarrett D'Amore 			n->pending = head;
124395c635efSGarrett D'Amore 			return(1);
124495c635efSGarrett D'Amore 		}
124595c635efSGarrett D'Amore 	}
124695c635efSGarrett D'Amore 
124795c635efSGarrett D'Amore 	/* Close out scopes to remain in a consistent state. */
124895c635efSGarrett D'Amore 
1249*698f87a4SGarrett D'Amore 	if ( ! rew_sub(MDOC_HEAD, mdoc, tok, line, ppos))
125095c635efSGarrett D'Amore 		return(0);
1251*698f87a4SGarrett D'Amore 	if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
125295c635efSGarrett D'Amore 		return(0);
125395c635efSGarrett D'Amore 
125495c635efSGarrett D'Amore out:
1255*698f87a4SGarrett D'Amore 	if ( ! (MDOC_FREECOL & mdoc->flags))
125695c635efSGarrett D'Amore 		return(1);
125795c635efSGarrett D'Amore 
1258*698f87a4SGarrett D'Amore 	if ( ! rew_sub(MDOC_BODY, mdoc, tok, line, ppos))
125995c635efSGarrett D'Amore 		return(0);
1260*698f87a4SGarrett D'Amore 	if ( ! rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos))
126195c635efSGarrett D'Amore 		return(0);
126295c635efSGarrett D'Amore 
1263*698f87a4SGarrett D'Amore 	mdoc->flags &= ~MDOC_FREECOL;
126495c635efSGarrett D'Amore 	return(1);
126595c635efSGarrett D'Amore }
126695c635efSGarrett D'Amore 
126795c635efSGarrett D'Amore 
126895c635efSGarrett D'Amore static int
126995c635efSGarrett D'Amore blk_part_imp(MACRO_PROT_ARGS)
127095c635efSGarrett D'Amore {
127195c635efSGarrett D'Amore 	int		  la, nl;
127295c635efSGarrett D'Amore 	enum mdoct	  ntok;
127395c635efSGarrett D'Amore 	enum margserr	  ac;
127495c635efSGarrett D'Amore 	char		 *p;
127595c635efSGarrett D'Amore 	struct mdoc_node *blk; /* saved block context */
127695c635efSGarrett D'Amore 	struct mdoc_node *body; /* saved body context */
127795c635efSGarrett D'Amore 	struct mdoc_node *n;
127895c635efSGarrett D'Amore 
1279*698f87a4SGarrett D'Amore 	nl = MDOC_NEWLINE & mdoc->flags;
128095c635efSGarrett D'Amore 
128195c635efSGarrett D'Amore 	/*
128295c635efSGarrett D'Amore 	 * A macro that spans to the end of the line.  This is generally
128395c635efSGarrett D'Amore 	 * (but not necessarily) called as the first macro.  The block
128495c635efSGarrett D'Amore 	 * has a head as the immediate child, which is always empty,
128595c635efSGarrett D'Amore 	 * followed by zero or more opening punctuation nodes, then the
128695c635efSGarrett D'Amore 	 * body (which may be empty, depending on the macro), then zero
128795c635efSGarrett D'Amore 	 * or more closing punctuation nodes.
128895c635efSGarrett D'Amore 	 */
128995c635efSGarrett D'Amore 
1290*698f87a4SGarrett D'Amore 	if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, NULL))
129195c635efSGarrett D'Amore 		return(0);
129295c635efSGarrett D'Amore 
1293*698f87a4SGarrett D'Amore 	blk = mdoc->last;
129495c635efSGarrett D'Amore 
1295*698f87a4SGarrett D'Amore 	if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
129695c635efSGarrett D'Amore 		return(0);
1297*698f87a4SGarrett D'Amore 	if ( ! rew_sub(MDOC_HEAD, mdoc, tok, line, ppos))
129895c635efSGarrett D'Amore 		return(0);
129995c635efSGarrett D'Amore 
130095c635efSGarrett D'Amore 	/*
130195c635efSGarrett D'Amore 	 * Open the body scope "on-demand", that is, after we've
130295c635efSGarrett D'Amore 	 * processed all our the leading delimiters (open parenthesis,
130395c635efSGarrett D'Amore 	 * etc.).
130495c635efSGarrett D'Amore 	 */
130595c635efSGarrett D'Amore 
130695c635efSGarrett D'Amore 	for (body = NULL; ; ) {
130795c635efSGarrett D'Amore 		la = *pos;
1308*698f87a4SGarrett D'Amore 		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
130995c635efSGarrett D'Amore 
131095c635efSGarrett D'Amore 		if (ARGS_ERROR == ac)
131195c635efSGarrett D'Amore 			return(0);
131295c635efSGarrett D'Amore 		if (ARGS_EOLN == ac)
131395c635efSGarrett D'Amore 			break;
131495c635efSGarrett D'Amore 		if (ARGS_PUNCT == ac)
131595c635efSGarrett D'Amore 			break;
131695c635efSGarrett D'Amore 
131795c635efSGarrett D'Amore 		if (NULL == body && ARGS_QWORD != ac &&
131895c635efSGarrett D'Amore 				DELIM_OPEN == mdoc_isdelim(p)) {
1319*698f87a4SGarrett D'Amore 			if ( ! dword(mdoc, line, la, p, DELIM_OPEN, 0))
132095c635efSGarrett D'Amore 				return(0);
132195c635efSGarrett D'Amore 			continue;
132295c635efSGarrett D'Amore 		}
132395c635efSGarrett D'Amore 
132495c635efSGarrett D'Amore 		if (NULL == body) {
1325*698f87a4SGarrett D'Amore 		       if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
132695c635efSGarrett D'Amore 			       return(0);
1327*698f87a4SGarrett D'Amore 			body = mdoc->last;
132895c635efSGarrett D'Amore 		}
132995c635efSGarrett D'Amore 
133095c635efSGarrett D'Amore 		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
133195c635efSGarrett D'Amore 
133295c635efSGarrett D'Amore 		if (MDOC_MAX == ntok) {
1333*698f87a4SGarrett D'Amore 			if ( ! dword(mdoc, line, la, p, DELIM_MAX,
1334*698f87a4SGarrett D'Amore 			    MDOC_JOIN & mdoc_macros[tok].flags))
133595c635efSGarrett D'Amore 				return(0);
133695c635efSGarrett D'Amore 			continue;
133795c635efSGarrett D'Amore 		}
133895c635efSGarrett D'Amore 
1339*698f87a4SGarrett D'Amore 		if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf))
134095c635efSGarrett D'Amore 			return(0);
134195c635efSGarrett D'Amore 		break;
134295c635efSGarrett D'Amore 	}
134395c635efSGarrett D'Amore 
134495c635efSGarrett D'Amore 	/* Clean-ups to leave in a consistent state. */
134595c635efSGarrett D'Amore 
134695c635efSGarrett D'Amore 	if (NULL == body) {
1347*698f87a4SGarrett D'Amore 		if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
134895c635efSGarrett D'Amore 			return(0);
1349*698f87a4SGarrett D'Amore 		body = mdoc->last;
135095c635efSGarrett D'Amore 	}
135195c635efSGarrett D'Amore 
135295c635efSGarrett D'Amore 	for (n = body->child; n && n->next; n = n->next)
135395c635efSGarrett D'Amore 		/* Do nothing. */ ;
135495c635efSGarrett D'Amore 
135595c635efSGarrett D'Amore 	/*
135695c635efSGarrett D'Amore 	 * End of sentence spacing: if the last node is a text node and
135795c635efSGarrett D'Amore 	 * has a trailing period, then mark it as being end-of-sentence.
135895c635efSGarrett D'Amore 	 */
135995c635efSGarrett D'Amore 
136095c635efSGarrett D'Amore 	if (n && MDOC_TEXT == n->type && n->string)
136195c635efSGarrett D'Amore 		if (mandoc_eos(n->string, strlen(n->string), 1))
136295c635efSGarrett D'Amore 			n->flags |= MDOC_EOS;
136395c635efSGarrett D'Amore 
136495c635efSGarrett D'Amore 	/* Up-propagate the end-of-space flag. */
136595c635efSGarrett D'Amore 
136695c635efSGarrett D'Amore 	if (n && (MDOC_EOS & n->flags)) {
136795c635efSGarrett D'Amore 		body->flags |= MDOC_EOS;
136895c635efSGarrett D'Amore 		body->parent->flags |= MDOC_EOS;
136995c635efSGarrett D'Amore 	}
137095c635efSGarrett D'Amore 
137195c635efSGarrett D'Amore 	/*
137295c635efSGarrett D'Amore 	 * If there is an open sub-block requiring explicit close-out,
137395c635efSGarrett D'Amore 	 * postpone closing out the current block
137495c635efSGarrett D'Amore 	 * until the rew_sub() call closing out the sub-block.
137595c635efSGarrett D'Amore 	 */
1376*698f87a4SGarrett D'Amore 	for (n = mdoc->last; n && n != body && n != blk->parent;
1377*698f87a4SGarrett D'Amore 			n = n->parent) {
137895c635efSGarrett D'Amore 		if (MDOC_BLOCK == n->type &&
137995c635efSGarrett D'Amore 		    MDOC_EXPLICIT & mdoc_macros[n->tok].flags &&
138095c635efSGarrett D'Amore 		    ! (MDOC_VALID & n->flags)) {
1381*698f87a4SGarrett D'Amore 			make_pending(n, tok, mdoc, line, ppos);
1382*698f87a4SGarrett D'Amore 			if ( ! mdoc_endbody_alloc(mdoc, line, ppos,
138395c635efSGarrett D'Amore 			    tok, body, ENDBODY_NOSPACE))
138495c635efSGarrett D'Amore 				return(0);
138595c635efSGarrett D'Amore 			return(1);
138695c635efSGarrett D'Amore 		}
138795c635efSGarrett D'Amore 	}
138895c635efSGarrett D'Amore 
138995c635efSGarrett D'Amore 	/*
139095c635efSGarrett D'Amore 	 * If we can't rewind to our body, then our scope has already
139195c635efSGarrett D'Amore 	 * been closed by another macro (like `Oc' closing `Op').  This
139295c635efSGarrett D'Amore 	 * is ugly behaviour nodding its head to OpenBSD's overwhelming
139395c635efSGarrett D'Amore 	 * crufty use of `Op' breakage.
139495c635efSGarrett D'Amore 	 */
139595c635efSGarrett D'Amore 	if (n != body)
1396*698f87a4SGarrett D'Amore 		mandoc_vmsg(MANDOCERR_SCOPENEST, mdoc->parse, line, ppos,
139795c635efSGarrett D'Amore 				"%s broken", mdoc_macronames[tok]);
139895c635efSGarrett D'Amore 
1399*698f87a4SGarrett D'Amore 	if (n && ! rew_sub(MDOC_BODY, mdoc, tok, line, ppos))
140095c635efSGarrett D'Amore 		return(0);
140195c635efSGarrett D'Amore 
140295c635efSGarrett D'Amore 	/* Standard appending of delimiters. */
140395c635efSGarrett D'Amore 
1404*698f87a4SGarrett D'Amore 	if (nl && ! append_delims(mdoc, line, pos, buf))
140595c635efSGarrett D'Amore 		return(0);
140695c635efSGarrett D'Amore 
140795c635efSGarrett D'Amore 	/* Rewind scope, if applicable. */
140895c635efSGarrett D'Amore 
1409*698f87a4SGarrett D'Amore 	if (n && ! rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos))
141095c635efSGarrett D'Amore 		return(0);
141195c635efSGarrett D'Amore 
1412*698f87a4SGarrett D'Amore 	/* Move trailing .Ns out of scope. */
1413*698f87a4SGarrett D'Amore 
1414*698f87a4SGarrett D'Amore 	for (n = body->child; n && n->next; n = n->next)
1415*698f87a4SGarrett D'Amore 		/* Do nothing. */ ;
1416*698f87a4SGarrett D'Amore 	if (n && MDOC_Ns == n->tok)
1417*698f87a4SGarrett D'Amore 		mdoc_node_relink(mdoc, n);
1418*698f87a4SGarrett D'Amore 
141995c635efSGarrett D'Amore 	return(1);
142095c635efSGarrett D'Amore }
142195c635efSGarrett D'Amore 
142295c635efSGarrett D'Amore 
142395c635efSGarrett D'Amore static int
142495c635efSGarrett D'Amore blk_part_exp(MACRO_PROT_ARGS)
142595c635efSGarrett D'Amore {
142695c635efSGarrett D'Amore 	int		  la, nl;
142795c635efSGarrett D'Amore 	enum margserr	  ac;
142895c635efSGarrett D'Amore 	struct mdoc_node *head; /* keep track of head */
142995c635efSGarrett D'Amore 	struct mdoc_node *body; /* keep track of body */
143095c635efSGarrett D'Amore 	char		 *p;
143195c635efSGarrett D'Amore 	enum mdoct	  ntok;
143295c635efSGarrett D'Amore 
1433*698f87a4SGarrett D'Amore 	nl = MDOC_NEWLINE & mdoc->flags;
143495c635efSGarrett D'Amore 
143595c635efSGarrett D'Amore 	/*
143695c635efSGarrett D'Amore 	 * The opening of an explicit macro having zero or more leading
143795c635efSGarrett D'Amore 	 * punctuation nodes; a head with optional single element (the
143895c635efSGarrett D'Amore 	 * case of `Eo'); and a body that may be empty.
143995c635efSGarrett D'Amore 	 */
144095c635efSGarrett D'Amore 
1441*698f87a4SGarrett D'Amore 	if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, NULL))
144295c635efSGarrett D'Amore 		return(0);
144395c635efSGarrett D'Amore 
144495c635efSGarrett D'Amore 	for (head = body = NULL; ; ) {
144595c635efSGarrett D'Amore 		la = *pos;
1446*698f87a4SGarrett D'Amore 		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
144795c635efSGarrett D'Amore 
144895c635efSGarrett D'Amore 		if (ARGS_ERROR == ac)
144995c635efSGarrett D'Amore 			return(0);
145095c635efSGarrett D'Amore 		if (ARGS_PUNCT == ac)
145195c635efSGarrett D'Amore 			break;
145295c635efSGarrett D'Amore 		if (ARGS_EOLN == ac)
145395c635efSGarrett D'Amore 			break;
145495c635efSGarrett D'Amore 
145595c635efSGarrett D'Amore 		/* Flush out leading punctuation. */
145695c635efSGarrett D'Amore 
145795c635efSGarrett D'Amore 		if (NULL == head && ARGS_QWORD != ac &&
145895c635efSGarrett D'Amore 				DELIM_OPEN == mdoc_isdelim(p)) {
145995c635efSGarrett D'Amore 			assert(NULL == body);
1460*698f87a4SGarrett D'Amore 			if ( ! dword(mdoc, line, la, p, DELIM_OPEN, 0))
146195c635efSGarrett D'Amore 				return(0);
146295c635efSGarrett D'Amore 			continue;
146395c635efSGarrett D'Amore 		}
146495c635efSGarrett D'Amore 
146595c635efSGarrett D'Amore 		if (NULL == head) {
146695c635efSGarrett D'Amore 			assert(NULL == body);
1467*698f87a4SGarrett D'Amore 			if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
146895c635efSGarrett D'Amore 				return(0);
1469*698f87a4SGarrett D'Amore 			head = mdoc->last;
147095c635efSGarrett D'Amore 		}
147195c635efSGarrett D'Amore 
147295c635efSGarrett D'Amore 		/*
147395c635efSGarrett D'Amore 		 * `Eo' gobbles any data into the head, but most other
147495c635efSGarrett D'Amore 		 * macros just immediately close out and begin the body.
147595c635efSGarrett D'Amore 		 */
147695c635efSGarrett D'Amore 
147795c635efSGarrett D'Amore 		if (NULL == body) {
147895c635efSGarrett D'Amore 			assert(head);
147995c635efSGarrett D'Amore 			/* No check whether it's a macro! */
148095c635efSGarrett D'Amore 			if (MDOC_Eo == tok)
1481*698f87a4SGarrett D'Amore 				if ( ! dword(mdoc, line, la, p, DELIM_MAX, 0))
148295c635efSGarrett D'Amore 					return(0);
148395c635efSGarrett D'Amore 
1484*698f87a4SGarrett D'Amore 			if ( ! rew_sub(MDOC_HEAD, mdoc, tok, line, ppos))
148595c635efSGarrett D'Amore 				return(0);
1486*698f87a4SGarrett D'Amore 			if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
148795c635efSGarrett D'Amore 				return(0);
1488*698f87a4SGarrett D'Amore 			body = mdoc->last;
148995c635efSGarrett D'Amore 
149095c635efSGarrett D'Amore 			if (MDOC_Eo == tok)
149195c635efSGarrett D'Amore 				continue;
149295c635efSGarrett D'Amore 		}
149395c635efSGarrett D'Amore 
149495c635efSGarrett D'Amore 		assert(NULL != head && NULL != body);
149595c635efSGarrett D'Amore 
149695c635efSGarrett D'Amore 		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
149795c635efSGarrett D'Amore 
149895c635efSGarrett D'Amore 		if (MDOC_MAX == ntok) {
1499*698f87a4SGarrett D'Amore 			if ( ! dword(mdoc, line, la, p, DELIM_MAX,
1500*698f87a4SGarrett D'Amore 			    MDOC_JOIN & mdoc_macros[tok].flags))
150195c635efSGarrett D'Amore 				return(0);
150295c635efSGarrett D'Amore 			continue;
150395c635efSGarrett D'Amore 		}
150495c635efSGarrett D'Amore 
1505*698f87a4SGarrett D'Amore 		if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf))
150695c635efSGarrett D'Amore 			return(0);
150795c635efSGarrett D'Amore 		break;
150895c635efSGarrett D'Amore 	}
150995c635efSGarrett D'Amore 
151095c635efSGarrett D'Amore 	/* Clean-up to leave in a consistent state. */
151195c635efSGarrett D'Amore 
151295c635efSGarrett D'Amore 	if (NULL == head)
1513*698f87a4SGarrett D'Amore 		if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
151495c635efSGarrett D'Amore 			return(0);
151595c635efSGarrett D'Amore 
151695c635efSGarrett D'Amore 	if (NULL == body) {
1517*698f87a4SGarrett D'Amore 		if ( ! rew_sub(MDOC_HEAD, mdoc, tok, line, ppos))
151895c635efSGarrett D'Amore 			return(0);
1519*698f87a4SGarrett D'Amore 		if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
152095c635efSGarrett D'Amore 			return(0);
152195c635efSGarrett D'Amore 	}
152295c635efSGarrett D'Amore 
152395c635efSGarrett D'Amore 	/* Standard appending of delimiters. */
152495c635efSGarrett D'Amore 
152595c635efSGarrett D'Amore 	if ( ! nl)
152695c635efSGarrett D'Amore 		return(1);
1527*698f87a4SGarrett D'Amore 	return(append_delims(mdoc, line, pos, buf));
152895c635efSGarrett D'Amore }
152995c635efSGarrett D'Amore 
153095c635efSGarrett D'Amore 
153195c635efSGarrett D'Amore /* ARGSUSED */
153295c635efSGarrett D'Amore static int
153395c635efSGarrett D'Amore in_line_argn(MACRO_PROT_ARGS)
153495c635efSGarrett D'Amore {
153595c635efSGarrett D'Amore 	int		 la, flushed, j, maxargs, nl;
153695c635efSGarrett D'Amore 	enum margserr	 ac;
153795c635efSGarrett D'Amore 	enum margverr	 av;
153895c635efSGarrett D'Amore 	struct mdoc_arg	*arg;
153995c635efSGarrett D'Amore 	char		*p;
154095c635efSGarrett D'Amore 	enum mdoct	 ntok;
154195c635efSGarrett D'Amore 
1542*698f87a4SGarrett D'Amore 	nl = MDOC_NEWLINE & mdoc->flags;
154395c635efSGarrett D'Amore 
154495c635efSGarrett D'Amore 	/*
154595c635efSGarrett D'Amore 	 * A line macro that has a fixed number of arguments (maxargs).
154695c635efSGarrett D'Amore 	 * Only open the scope once the first non-leading-punctuation is
154795c635efSGarrett D'Amore 	 * found (unless MDOC_IGNDELIM is noted, like in `Pf'), then
154895c635efSGarrett D'Amore 	 * keep it open until the maximum number of arguments are
154995c635efSGarrett D'Amore 	 * exhausted.
155095c635efSGarrett D'Amore 	 */
155195c635efSGarrett D'Amore 
155295c635efSGarrett D'Amore 	switch (tok) {
155395c635efSGarrett D'Amore 	case (MDOC_Ap):
155495c635efSGarrett D'Amore 		/* FALLTHROUGH */
155595c635efSGarrett D'Amore 	case (MDOC_No):
155695c635efSGarrett D'Amore 		/* FALLTHROUGH */
155795c635efSGarrett D'Amore 	case (MDOC_Ns):
155895c635efSGarrett D'Amore 		/* FALLTHROUGH */
155995c635efSGarrett D'Amore 	case (MDOC_Ux):
156095c635efSGarrett D'Amore 		maxargs = 0;
156195c635efSGarrett D'Amore 		break;
156295c635efSGarrett D'Amore 	case (MDOC_Bx):
156395c635efSGarrett D'Amore 		/* FALLTHROUGH */
156495c635efSGarrett D'Amore 	case (MDOC_Xr):
156595c635efSGarrett D'Amore 		maxargs = 2;
156695c635efSGarrett D'Amore 		break;
156795c635efSGarrett D'Amore 	default:
156895c635efSGarrett D'Amore 		maxargs = 1;
156995c635efSGarrett D'Amore 		break;
157095c635efSGarrett D'Amore 	}
157195c635efSGarrett D'Amore 
157295c635efSGarrett D'Amore 	for (arg = NULL; ; ) {
157395c635efSGarrett D'Amore 		la = *pos;
1574*698f87a4SGarrett D'Amore 		av = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
157595c635efSGarrett D'Amore 
157695c635efSGarrett D'Amore 		if (ARGV_WORD == av) {
157795c635efSGarrett D'Amore 			*pos = la;
157895c635efSGarrett D'Amore 			break;
157995c635efSGarrett D'Amore 		}
158095c635efSGarrett D'Amore 
158195c635efSGarrett D'Amore 		if (ARGV_EOLN == av)
158295c635efSGarrett D'Amore 			break;
158395c635efSGarrett D'Amore 		if (ARGV_ARG == av)
158495c635efSGarrett D'Amore 			continue;
158595c635efSGarrett D'Amore 
158695c635efSGarrett D'Amore 		mdoc_argv_free(arg);
158795c635efSGarrett D'Amore 		return(0);
158895c635efSGarrett D'Amore 	}
158995c635efSGarrett D'Amore 
159095c635efSGarrett D'Amore 	for (flushed = j = 0; ; ) {
159195c635efSGarrett D'Amore 		la = *pos;
1592*698f87a4SGarrett D'Amore 		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
159395c635efSGarrett D'Amore 
159495c635efSGarrett D'Amore 		if (ARGS_ERROR == ac)
159595c635efSGarrett D'Amore 			return(0);
159695c635efSGarrett D'Amore 		if (ARGS_PUNCT == ac)
159795c635efSGarrett D'Amore 			break;
159895c635efSGarrett D'Amore 		if (ARGS_EOLN == ac)
159995c635efSGarrett D'Amore 			break;
160095c635efSGarrett D'Amore 
160195c635efSGarrett D'Amore 		if ( ! (MDOC_IGNDELIM & mdoc_macros[tok].flags) &&
160295c635efSGarrett D'Amore 				ARGS_QWORD != ac && 0 == j &&
160395c635efSGarrett D'Amore 				DELIM_OPEN == mdoc_isdelim(p)) {
1604*698f87a4SGarrett D'Amore 			if ( ! dword(mdoc, line, la, p, DELIM_OPEN, 0))
160595c635efSGarrett D'Amore 				return(0);
160695c635efSGarrett D'Amore 			continue;
160795c635efSGarrett D'Amore 		} else if (0 == j)
1608*698f87a4SGarrett D'Amore 		       if ( ! mdoc_elem_alloc(mdoc, line, la, tok, arg))
160995c635efSGarrett D'Amore 			       return(0);
161095c635efSGarrett D'Amore 
161195c635efSGarrett D'Amore 		if (j == maxargs && ! flushed) {
1612*698f87a4SGarrett D'Amore 			if ( ! rew_elem(mdoc, tok))
161395c635efSGarrett D'Amore 				return(0);
161495c635efSGarrett D'Amore 			flushed = 1;
161595c635efSGarrett D'Amore 		}
161695c635efSGarrett D'Amore 
161795c635efSGarrett D'Amore 		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
161895c635efSGarrett D'Amore 
161995c635efSGarrett D'Amore 		if (MDOC_MAX != ntok) {
1620*698f87a4SGarrett D'Amore 			if ( ! flushed && ! rew_elem(mdoc, tok))
162195c635efSGarrett D'Amore 				return(0);
162295c635efSGarrett D'Amore 			flushed = 1;
1623*698f87a4SGarrett D'Amore 			if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf))
162495c635efSGarrett D'Amore 				return(0);
162595c635efSGarrett D'Amore 			j++;
162695c635efSGarrett D'Amore 			break;
162795c635efSGarrett D'Amore 		}
162895c635efSGarrett D'Amore 
162995c635efSGarrett D'Amore 		if ( ! (MDOC_IGNDELIM & mdoc_macros[tok].flags) &&
163095c635efSGarrett D'Amore 				ARGS_QWORD != ac &&
163195c635efSGarrett D'Amore 				! flushed &&
163295c635efSGarrett D'Amore 				DELIM_NONE != mdoc_isdelim(p)) {
1633*698f87a4SGarrett D'Amore 			if ( ! rew_elem(mdoc, tok))
163495c635efSGarrett D'Amore 				return(0);
163595c635efSGarrett D'Amore 			flushed = 1;
163695c635efSGarrett D'Amore 		}
163795c635efSGarrett D'Amore 
1638*698f87a4SGarrett D'Amore 		if ( ! dword(mdoc, line, la, p, DELIM_MAX,
1639*698f87a4SGarrett D'Amore 		    MDOC_JOIN & mdoc_macros[tok].flags))
164095c635efSGarrett D'Amore 			return(0);
164195c635efSGarrett D'Amore 		j++;
164295c635efSGarrett D'Amore 	}
164395c635efSGarrett D'Amore 
1644*698f87a4SGarrett D'Amore 	if (0 == j && ! mdoc_elem_alloc(mdoc, line, la, tok, arg))
164595c635efSGarrett D'Amore 	       return(0);
164695c635efSGarrett D'Amore 
164795c635efSGarrett D'Amore 	/* Close out in a consistent state. */
164895c635efSGarrett D'Amore 
1649*698f87a4SGarrett D'Amore 	if ( ! flushed && ! rew_elem(mdoc, tok))
165095c635efSGarrett D'Amore 		return(0);
165195c635efSGarrett D'Amore 	if ( ! nl)
165295c635efSGarrett D'Amore 		return(1);
1653*698f87a4SGarrett D'Amore 	return(append_delims(mdoc, line, pos, buf));
165495c635efSGarrett D'Amore }
165595c635efSGarrett D'Amore 
165695c635efSGarrett D'Amore 
165795c635efSGarrett D'Amore static int
165895c635efSGarrett D'Amore in_line_eoln(MACRO_PROT_ARGS)
165995c635efSGarrett D'Amore {
166095c635efSGarrett D'Amore 	int		 la;
166195c635efSGarrett D'Amore 	enum margserr	 ac;
166295c635efSGarrett D'Amore 	enum margverr	 av;
166395c635efSGarrett D'Amore 	struct mdoc_arg	*arg;
166495c635efSGarrett D'Amore 	char		*p;
166595c635efSGarrett D'Amore 	enum mdoct	 ntok;
166695c635efSGarrett D'Amore 
166795c635efSGarrett D'Amore 	assert( ! (MDOC_PARSED & mdoc_macros[tok].flags));
166895c635efSGarrett D'Amore 
166995c635efSGarrett D'Amore 	if (tok == MDOC_Pp)
1670*698f87a4SGarrett D'Amore 		rew_sub(MDOC_BLOCK, mdoc, MDOC_Nm, line, ppos);
167195c635efSGarrett D'Amore 
167295c635efSGarrett D'Amore 	/* Parse macro arguments. */
167395c635efSGarrett D'Amore 
167495c635efSGarrett D'Amore 	for (arg = NULL; ; ) {
167595c635efSGarrett D'Amore 		la = *pos;
1676*698f87a4SGarrett D'Amore 		av = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
167795c635efSGarrett D'Amore 
167895c635efSGarrett D'Amore 		if (ARGV_WORD == av) {
167995c635efSGarrett D'Amore 			*pos = la;
168095c635efSGarrett D'Amore 			break;
168195c635efSGarrett D'Amore 		}
168295c635efSGarrett D'Amore 		if (ARGV_EOLN == av)
168395c635efSGarrett D'Amore 			break;
168495c635efSGarrett D'Amore 		if (ARGV_ARG == av)
168595c635efSGarrett D'Amore 			continue;
168695c635efSGarrett D'Amore 
168795c635efSGarrett D'Amore 		mdoc_argv_free(arg);
168895c635efSGarrett D'Amore 		return(0);
168995c635efSGarrett D'Amore 	}
169095c635efSGarrett D'Amore 
169195c635efSGarrett D'Amore 	/* Open element scope. */
169295c635efSGarrett D'Amore 
1693*698f87a4SGarrett D'Amore 	if ( ! mdoc_elem_alloc(mdoc, line, ppos, tok, arg))
169495c635efSGarrett D'Amore 		return(0);
169595c635efSGarrett D'Amore 
169695c635efSGarrett D'Amore 	/* Parse argument terms. */
169795c635efSGarrett D'Amore 
169895c635efSGarrett D'Amore 	for (;;) {
169995c635efSGarrett D'Amore 		la = *pos;
1700*698f87a4SGarrett D'Amore 		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
170195c635efSGarrett D'Amore 
170295c635efSGarrett D'Amore 		if (ARGS_ERROR == ac)
170395c635efSGarrett D'Amore 			return(0);
170495c635efSGarrett D'Amore 		if (ARGS_EOLN == ac)
170595c635efSGarrett D'Amore 			break;
170695c635efSGarrett D'Amore 
170795c635efSGarrett D'Amore 		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
170895c635efSGarrett D'Amore 
170995c635efSGarrett D'Amore 		if (MDOC_MAX == ntok) {
1710*698f87a4SGarrett D'Amore 			if ( ! dword(mdoc, line, la, p, DELIM_MAX,
1711*698f87a4SGarrett D'Amore 			    MDOC_JOIN & mdoc_macros[tok].flags))
171295c635efSGarrett D'Amore 				return(0);
171395c635efSGarrett D'Amore 			continue;
171495c635efSGarrett D'Amore 		}
171595c635efSGarrett D'Amore 
1716*698f87a4SGarrett D'Amore 		if ( ! rew_elem(mdoc, tok))
171795c635efSGarrett D'Amore 			return(0);
1718*698f87a4SGarrett D'Amore 		return(mdoc_macro(mdoc, ntok, line, la, pos, buf));
171995c635efSGarrett D'Amore 	}
172095c635efSGarrett D'Amore 
172195c635efSGarrett D'Amore 	/* Close out (no delimiters). */
172295c635efSGarrett D'Amore 
1723*698f87a4SGarrett D'Amore 	return(rew_elem(mdoc, tok));
172495c635efSGarrett D'Amore }
172595c635efSGarrett D'Amore 
172695c635efSGarrett D'Amore 
172795c635efSGarrett D'Amore /* ARGSUSED */
172895c635efSGarrett D'Amore static int
172995c635efSGarrett D'Amore ctx_synopsis(MACRO_PROT_ARGS)
173095c635efSGarrett D'Amore {
173195c635efSGarrett D'Amore 	int		 nl;
173295c635efSGarrett D'Amore 
1733*698f87a4SGarrett D'Amore 	nl = MDOC_NEWLINE & mdoc->flags;
173495c635efSGarrett D'Amore 
173595c635efSGarrett D'Amore 	/* If we're not in the SYNOPSIS, go straight to in-line. */
1736*698f87a4SGarrett D'Amore 	if ( ! (MDOC_SYNOPSIS & mdoc->flags))
1737*698f87a4SGarrett D'Amore 		return(in_line(mdoc, tok, line, ppos, pos, buf));
173895c635efSGarrett D'Amore 
173995c635efSGarrett D'Amore 	/* If we're a nested call, same place. */
174095c635efSGarrett D'Amore 	if ( ! nl)
1741*698f87a4SGarrett D'Amore 		return(in_line(mdoc, tok, line, ppos, pos, buf));
174295c635efSGarrett D'Amore 
174395c635efSGarrett D'Amore 	/*
174495c635efSGarrett D'Amore 	 * XXX: this will open a block scope; however, if later we end
174595c635efSGarrett D'Amore 	 * up formatting the block scope, then child nodes will inherit
174695c635efSGarrett D'Amore 	 * the formatting.  Be careful.
174795c635efSGarrett D'Amore 	 */
174895c635efSGarrett D'Amore 	if (MDOC_Nm == tok)
1749*698f87a4SGarrett D'Amore 		return(blk_full(mdoc, tok, line, ppos, pos, buf));
175095c635efSGarrett D'Amore 	assert(MDOC_Vt == tok);
1751*698f87a4SGarrett D'Amore 	return(blk_part_imp(mdoc, tok, line, ppos, pos, buf));
175295c635efSGarrett D'Amore }
175395c635efSGarrett D'Amore 
175495c635efSGarrett D'Amore 
175595c635efSGarrett D'Amore /* ARGSUSED */
175695c635efSGarrett D'Amore static int
175795c635efSGarrett D'Amore obsolete(MACRO_PROT_ARGS)
175895c635efSGarrett D'Amore {
175995c635efSGarrett D'Amore 
1760*698f87a4SGarrett D'Amore 	mdoc_pmsg(mdoc, line, ppos, MANDOCERR_MACROOBS);
176195c635efSGarrett D'Amore 	return(1);
176295c635efSGarrett D'Amore }
176395c635efSGarrett D'Amore 
176495c635efSGarrett D'Amore 
176595c635efSGarrett D'Amore /*
176695c635efSGarrett D'Amore  * Phrases occur within `Bl -column' entries, separated by `Ta' or tabs.
176795c635efSGarrett D'Amore  * They're unusual because they're basically free-form text until a
176895c635efSGarrett D'Amore  * macro is encountered.
176995c635efSGarrett D'Amore  */
177095c635efSGarrett D'Amore static int
1771*698f87a4SGarrett D'Amore phrase(struct mdoc *mdoc, int line, int ppos, char *buf)
177295c635efSGarrett D'Amore {
177395c635efSGarrett D'Amore 	int		 la, pos;
177495c635efSGarrett D'Amore 	enum margserr	 ac;
177595c635efSGarrett D'Amore 	enum mdoct	 ntok;
177695c635efSGarrett D'Amore 	char		*p;
177795c635efSGarrett D'Amore 
177895c635efSGarrett D'Amore 	for (pos = ppos; ; ) {
177995c635efSGarrett D'Amore 		la = pos;
178095c635efSGarrett D'Amore 
1781*698f87a4SGarrett D'Amore 		ac = mdoc_zargs(mdoc, line, &pos, buf, &p);
178295c635efSGarrett D'Amore 
178395c635efSGarrett D'Amore 		if (ARGS_ERROR == ac)
178495c635efSGarrett D'Amore 			return(0);
178595c635efSGarrett D'Amore 		if (ARGS_EOLN == ac)
178695c635efSGarrett D'Amore 			break;
178795c635efSGarrett D'Amore 
178895c635efSGarrett D'Amore 		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup_raw(p);
178995c635efSGarrett D'Amore 
179095c635efSGarrett D'Amore 		if (MDOC_MAX == ntok) {
1791*698f87a4SGarrett D'Amore 			if ( ! dword(mdoc, line, la, p, DELIM_MAX, 1))
179295c635efSGarrett D'Amore 				return(0);
179395c635efSGarrett D'Amore 			continue;
179495c635efSGarrett D'Amore 		}
179595c635efSGarrett D'Amore 
1796*698f87a4SGarrett D'Amore 		if ( ! mdoc_macro(mdoc, ntok, line, la, &pos, buf))
179795c635efSGarrett D'Amore 			return(0);
1798*698f87a4SGarrett D'Amore 		return(append_delims(mdoc, line, &pos, buf));
179995c635efSGarrett D'Amore 	}
180095c635efSGarrett D'Amore 
180195c635efSGarrett D'Amore 	return(1);
180295c635efSGarrett D'Amore }
180395c635efSGarrett D'Amore 
180495c635efSGarrett D'Amore 
180595c635efSGarrett D'Amore /* ARGSUSED */
180695c635efSGarrett D'Amore static int
180795c635efSGarrett D'Amore phrase_ta(MACRO_PROT_ARGS)
180895c635efSGarrett D'Amore {
1809*698f87a4SGarrett D'Amore 	struct mdoc_node *n;
181095c635efSGarrett D'Amore 	int		  la;
181195c635efSGarrett D'Amore 	enum mdoct	  ntok;
181295c635efSGarrett D'Amore 	enum margserr	  ac;
181395c635efSGarrett D'Amore 	char		 *p;
181495c635efSGarrett D'Amore 
1815*698f87a4SGarrett D'Amore 	/* Make sure we are in a column list or ignore this macro. */
1816*698f87a4SGarrett D'Amore 	n = mdoc->last;
1817*698f87a4SGarrett D'Amore 	while (NULL != n && MDOC_Bl != n->tok)
1818*698f87a4SGarrett D'Amore 		n = n->parent;
1819*698f87a4SGarrett D'Amore 	if (NULL == n || LIST_column != n->norm->Bl.type) {
1820*698f87a4SGarrett D'Amore 		mdoc_pmsg(mdoc, line, ppos, MANDOCERR_STRAYTA);
1821*698f87a4SGarrett D'Amore 		return(1);
1822*698f87a4SGarrett D'Amore 	}
182395c635efSGarrett D'Amore 
1824*698f87a4SGarrett D'Amore 	/* Advance to the next column. */
1825*698f87a4SGarrett D'Amore 	if ( ! rew_sub(MDOC_BODY, mdoc, MDOC_It, line, ppos))
182695c635efSGarrett D'Amore 		return(0);
1827*698f87a4SGarrett D'Amore 	if ( ! mdoc_body_alloc(mdoc, line, ppos, MDOC_It))
182895c635efSGarrett D'Amore 		return(0);
182995c635efSGarrett D'Amore 
183095c635efSGarrett D'Amore 	for (;;) {
183195c635efSGarrett D'Amore 		la = *pos;
1832*698f87a4SGarrett D'Amore 		ac = mdoc_zargs(mdoc, line, pos, buf, &p);
183395c635efSGarrett D'Amore 
183495c635efSGarrett D'Amore 		if (ARGS_ERROR == ac)
183595c635efSGarrett D'Amore 			return(0);
183695c635efSGarrett D'Amore 		if (ARGS_EOLN == ac)
183795c635efSGarrett D'Amore 			break;
183895c635efSGarrett D'Amore 
183995c635efSGarrett D'Amore 		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup_raw(p);
184095c635efSGarrett D'Amore 
184195c635efSGarrett D'Amore 		if (MDOC_MAX == ntok) {
1842*698f87a4SGarrett D'Amore 			if ( ! dword(mdoc, line, la, p, DELIM_MAX,
1843*698f87a4SGarrett D'Amore 			    MDOC_JOIN & mdoc_macros[tok].flags))
184495c635efSGarrett D'Amore 				return(0);
184595c635efSGarrett D'Amore 			continue;
184695c635efSGarrett D'Amore 		}
184795c635efSGarrett D'Amore 
1848*698f87a4SGarrett D'Amore 		if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf))
184995c635efSGarrett D'Amore 			return(0);
1850*698f87a4SGarrett D'Amore 		return(append_delims(mdoc, line, pos, buf));
185195c635efSGarrett D'Amore 	}
185295c635efSGarrett D'Amore 
185395c635efSGarrett D'Amore 	return(1);
185495c635efSGarrett D'Amore }
1855