xref: /openbsd-src/usr.bin/mandoc/man_macro.c (revision 84680f53fcf50f9c0d74c7e6cd5986183774cd75)
1*84680f53Sschwarze /* $OpenBSD: man_macro.c,v 1.111 2023/11/13 19:13:00 schwarze Exp $ */
2f73abda9Skristaps /*
36369db70Sschwarze  * Copyright (c) 2012-2015,2017-2020,2022 Ingo Schwarze <schwarze@openbsd.org>
4a35fc07aSschwarze  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
5d581c541Sschwarze  * Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
6f73abda9Skristaps  *
7f73abda9Skristaps  * Permission to use, copy, modify, and distribute this software for any
8a6464425Sschwarze  * purpose with or without fee is hereby granted, provided that the above
9a6464425Sschwarze  * copyright notice and this permission notice appear in all copies.
10f73abda9Skristaps  *
11d1982c71Sschwarze  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
12a6464425Sschwarze  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13d1982c71Sschwarze  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
14a6464425Sschwarze  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15a6464425Sschwarze  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16a6464425Sschwarze  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17a6464425Sschwarze  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18f73abda9Skristaps  */
19daac8107Sschwarze #include <sys/types.h>
20daac8107Sschwarze 
21f73abda9Skristaps #include <assert.h>
22f73abda9Skristaps #include <ctype.h>
23e501e731Sschwarze #include <stdio.h>
24f73abda9Skristaps #include <stdlib.h>
25f73abda9Skristaps #include <string.h>
26f73abda9Skristaps 
276e03d529Sschwarze #include "mandoc.h"
28d1982c71Sschwarze #include "roff.h"
29d1982c71Sschwarze #include "man.h"
30a35fc07aSschwarze #include "libmandoc.h"
31fa2127f9Sschwarze #include "roff_int.h"
32f73abda9Skristaps #include "libman.h"
33f73abda9Skristaps 
34eae2af84Sschwarze static	void		 blk_close(MACRO_PROT_ARGS);
35eae2af84Sschwarze static	void		 blk_exp(MACRO_PROT_ARGS);
36eae2af84Sschwarze static	void		 blk_imp(MACRO_PROT_ARGS);
37eae2af84Sschwarze static	void		 in_line_eoln(MACRO_PROT_ARGS);
38ede1b9d0Sschwarze static	int		 man_args(struct roff_man *, int,
39a35fc07aSschwarze 				int *, char *, char **);
4014a309e3Sschwarze static	void		 rew_scope(struct roff_man *, enum roff_tok);
41e35cb253Sschwarze 
4216fe0cfcSschwarze static const struct man_macro man_macros[MAN_MAX - MAN_TH] = {
4363f82d49Sschwarze 	{ in_line_eoln, MAN_XSCOPE }, /* TH */
4463f82d49Sschwarze 	{ blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* SH */
4563f82d49Sschwarze 	{ blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* SS */
4663f82d49Sschwarze 	{ blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* TP */
4763f82d49Sschwarze 	{ blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* TQ */
4863f82d49Sschwarze 	{ blk_imp, MAN_XSCOPE }, /* LP */
4963f82d49Sschwarze 	{ blk_imp, MAN_XSCOPE }, /* PP */
5063f82d49Sschwarze 	{ blk_imp, MAN_XSCOPE }, /* P */
5163f82d49Sschwarze 	{ blk_imp, MAN_XSCOPE }, /* IP */
5263f82d49Sschwarze 	{ blk_imp, MAN_XSCOPE }, /* HP */
5363f82d49Sschwarze 	{ in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* SM */
5463f82d49Sschwarze 	{ in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* SB */
55e35cb253Sschwarze 	{ in_line_eoln, 0 }, /* BI */
56e35cb253Sschwarze 	{ in_line_eoln, 0 }, /* IB */
57e35cb253Sschwarze 	{ in_line_eoln, 0 }, /* BR */
58e35cb253Sschwarze 	{ in_line_eoln, 0 }, /* RB */
5963f82d49Sschwarze 	{ in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* R */
6063f82d49Sschwarze 	{ in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* B */
6163f82d49Sschwarze 	{ in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* I */
62e35cb253Sschwarze 	{ in_line_eoln, 0 }, /* IR */
63e35cb253Sschwarze 	{ in_line_eoln, 0 }, /* RI */
6463f82d49Sschwarze 	{ blk_close, MAN_XSCOPE }, /* RE */
6563f82d49Sschwarze 	{ blk_exp, MAN_XSCOPE }, /* RS */
661ad7d38cSschwarze 	{ in_line_eoln, MAN_NSCOPED }, /* DT */
671ad7d38cSschwarze 	{ in_line_eoln, MAN_NSCOPED }, /* UC */
685cd28c26Sschwarze 	{ in_line_eoln, MAN_NSCOPED }, /* PD */
691ad7d38cSschwarze 	{ in_line_eoln, MAN_NSCOPED }, /* AT */
70318aa4c1Sschwarze 	{ in_line_eoln, MAN_NSCOPED }, /* in */
7163f82d49Sschwarze 	{ blk_imp, MAN_XSCOPE }, /* SY */
7263f82d49Sschwarze 	{ blk_close, MAN_XSCOPE }, /* YS */
7366ae7cc0Sschwarze 	{ in_line_eoln, 0 }, /* OP */
7463f82d49Sschwarze 	{ in_line_eoln, MAN_XSCOPE }, /* EX */
7563f82d49Sschwarze 	{ in_line_eoln, MAN_XSCOPE }, /* EE */
7663f82d49Sschwarze 	{ blk_exp, MAN_XSCOPE }, /* UR */
7763f82d49Sschwarze 	{ blk_close, MAN_XSCOPE }, /* UE */
7863f82d49Sschwarze 	{ blk_exp, MAN_XSCOPE }, /* MT */
7963f82d49Sschwarze 	{ blk_close, MAN_XSCOPE }, /* ME */
80f6697133Sschwarze 	{ in_line_eoln, 0 }, /* MR */
81f73abda9Skristaps };
82e35cb253Sschwarze 
83e35cb253Sschwarze 
8416fe0cfcSschwarze const struct man_macro *
man_macro(enum roff_tok tok)8516fe0cfcSschwarze man_macro(enum roff_tok tok)
8616fe0cfcSschwarze {
8716fe0cfcSschwarze 	assert(tok >= MAN_TH && tok <= MAN_MAX);
8816fe0cfcSschwarze 	return man_macros + (tok - MAN_TH);
8916fe0cfcSschwarze }
9016fe0cfcSschwarze 
91eae2af84Sschwarze void
man_unscope(struct roff_man * man,const struct roff_node * to)92ede1b9d0Sschwarze man_unscope(struct roff_man *man, const struct roff_node *to)
93e35cb253Sschwarze {
943a0d07afSschwarze 	struct roff_node *n;
95e35cb253Sschwarze 
968b93ac39Sschwarze 	to = to->parent;
978b93ac39Sschwarze 	n = man->last;
988b93ac39Sschwarze 	while (n != to) {
9947400284Sschwarze 
10047400284Sschwarze 		/* Reached the end of the document? */
10147400284Sschwarze 
102c4b0939cSschwarze 		if (to == NULL && ! (n->flags & NODE_VALID)) {
10347400284Sschwarze 			if (man->flags & (MAN_BLINE | MAN_ELINE) &&
10463f82d49Sschwarze 			    man_macro(n->tok)->flags &
10563f82d49Sschwarze 			     (MAN_BSCOPED | MAN_NSCOPED)) {
106a5a5f808Sschwarze 				mandoc_msg(MANDOCERR_BLK_LINE,
107a5a5f808Sschwarze 				    n->line, n->pos,
10814a309e3Sschwarze 				    "EOF breaks %s", roff_name[n->tok]);
10908821f0dSschwarze 				if (man->flags & MAN_ELINE) {
1106369db70Sschwarze 					if (n->parent->type == ROFFT_ROOT ||
1116369db70Sschwarze 					    (man_macro(n->parent->tok)->flags &
11208821f0dSschwarze 					    MAN_ESCOPED) == 0)
11347400284Sschwarze 						man->flags &= ~MAN_ELINE;
11408821f0dSschwarze 				} else {
115d1982c71Sschwarze 					assert(n->type == ROFFT_HEAD);
11647400284Sschwarze 					n = n->parent;
11747400284Sschwarze 					man->flags &= ~MAN_BLINE;
11847400284Sschwarze 				}
11947400284Sschwarze 				man->last = n;
12047400284Sschwarze 				n = n->parent;
121fa2127f9Sschwarze 				roff_node_delete(man, man->last);
12247400284Sschwarze 				continue;
12347400284Sschwarze 			}
124d1982c71Sschwarze 			if (n->type == ROFFT_BLOCK &&
12516fe0cfcSschwarze 			    man_macro(n->tok)->fp == blk_exp)
1265e5ff21eSschwarze 				mandoc_msg(MANDOCERR_BLK_NOEND,
127a5a5f808Sschwarze 				    n->line, n->pos, "%s",
12814a309e3Sschwarze 				    roff_name[n->tok]);
12947400284Sschwarze 		}
13047400284Sschwarze 
1315e94b789Sschwarze 		/*
1328b93ac39Sschwarze 		 * We might delete the man->last node
1338b93ac39Sschwarze 		 * in the post-validation phase.
1348b93ac39Sschwarze 		 * Save a pointer to the parent such that
1358b93ac39Sschwarze 		 * we know where to continue the iteration.
1365e94b789Sschwarze 		 */
137daac8107Sschwarze 
1387ead8a4eSschwarze 		man->last = n;
1398b93ac39Sschwarze 		n = n->parent;
140c4b0939cSschwarze 		man->last->flags |= NODE_VALID;
1418b93ac39Sschwarze 	}
142daac8107Sschwarze 
143daac8107Sschwarze 	/*
144daac8107Sschwarze 	 * If we ended up at the parent of the node we were
145daac8107Sschwarze 	 * supposed to rewind to, that means the target node
146daac8107Sschwarze 	 * got deleted, so add the next node we parse as a child
147daac8107Sschwarze 	 * of the parent instead of as a sibling of the target.
148daac8107Sschwarze 	 */
149daac8107Sschwarze 
150daac8107Sschwarze 	man->next = (man->last == to) ?
151ede1b9d0Sschwarze 	    ROFF_NEXT_CHILD : ROFF_NEXT_SIBLING;
152e35cb253Sschwarze }
153e35cb253Sschwarze 
154e35cb253Sschwarze /*
155e35cb253Sschwarze  * Rewinding entails ascending the parse tree until a coherent point,
156e35cb253Sschwarze  * for example, the `SH' macro will close out any intervening `SS'
157e35cb253Sschwarze  * scopes.  When a scope is closed, it must be validated and actioned.
158e35cb253Sschwarze  */
159eae2af84Sschwarze static void
rew_scope(struct roff_man * man,enum roff_tok tok)16014a309e3Sschwarze rew_scope(struct roff_man *man, enum roff_tok tok)
161e35cb253Sschwarze {
1623a0d07afSschwarze 	struct roff_node *n;
163e35cb253Sschwarze 
1646f5bf183Sschwarze 	/* Preserve empty paragraphs before RS. */
1656f5bf183Sschwarze 
1666f5bf183Sschwarze 	n = man->last;
16730e5ee06Sschwarze 	if (tok == MAN_RS && n->child == NULL &&
1686f5bf183Sschwarze 	    (n->tok == MAN_P || n->tok == MAN_PP || n->tok == MAN_LP))
169eae2af84Sschwarze 		return;
1706f5bf183Sschwarze 
1716f5bf183Sschwarze 	for (;;) {
1726f5bf183Sschwarze 		if (n->type == ROFFT_ROOT)
1736f5bf183Sschwarze 			return;
174c4b0939cSschwarze 		if (n->flags & NODE_VALID) {
1756f5bf183Sschwarze 			n = n->parent;
1766f5bf183Sschwarze 			continue;
177e35cb253Sschwarze 		}
1786f5bf183Sschwarze 		if (n->type != ROFFT_BLOCK) {
1796f5bf183Sschwarze 			if (n->parent->type == ROFFT_ROOT) {
180eae2af84Sschwarze 				man_unscope(man, n);
1816f5bf183Sschwarze 				return;
1826f5bf183Sschwarze 			} else {
1836f5bf183Sschwarze 				n = n->parent;
1846f5bf183Sschwarze 				continue;
1856f5bf183Sschwarze 			}
1866f5bf183Sschwarze 		}
1876f5bf183Sschwarze 		if (tok != MAN_SH && (n->tok == MAN_SH ||
1886f5bf183Sschwarze 		    (tok != MAN_SS && (n->tok == MAN_SS ||
18916fe0cfcSschwarze 		     man_macro(n->tok)->fp == blk_exp))))
1906f5bf183Sschwarze 			return;
1916f5bf183Sschwarze 		man_unscope(man, n);
1926f5bf183Sschwarze 		n = man->last;
1936f5bf183Sschwarze 	}
194e35cb253Sschwarze }
195e35cb253Sschwarze 
196e35cb253Sschwarze 
197ac531cf1Sschwarze /*
198ac531cf1Sschwarze  * Close out a generic explicit macro.
199ac531cf1Sschwarze  */
200eae2af84Sschwarze void
blk_close(MACRO_PROT_ARGS)201fbaebbd2Sschwarze blk_close(MACRO_PROT_ARGS)
202fbaebbd2Sschwarze {
203252b7e4aSschwarze 	enum roff_tok		 ctok, ntok;
2043a0d07afSschwarze 	const struct roff_node	*nn;
205e94357f9Sschwarze 	char			*p, *ep;
206e94357f9Sschwarze 	int			 cline, cpos, la, nrew, target;
207fbaebbd2Sschwarze 
20804d44d89Sschwarze 	nrew = 1;
209fbaebbd2Sschwarze 	switch (tok) {
21049aff9f8Sschwarze 	case MAN_RE:
211fbaebbd2Sschwarze 		ntok = MAN_RS;
212e94357f9Sschwarze 		la = *pos;
21304d44d89Sschwarze 		if ( ! man_args(man, line, pos, buf, &p))
21404d44d89Sschwarze 			break;
21504d44d89Sschwarze 		for (nn = man->last->parent; nn; nn = nn->parent)
216d1982c71Sschwarze 			if (nn->tok == ntok && nn->type == ROFFT_BLOCK)
21704d44d89Sschwarze 				nrew++;
218e94357f9Sschwarze 		target = strtol(p, &ep, 10);
219e94357f9Sschwarze 		if (*ep != '\0')
220a5a5f808Sschwarze 			mandoc_msg(MANDOCERR_ARG_EXCESS, line,
221e94357f9Sschwarze 			    la + (buf[la] == '"') + (int)(ep - p),
222e94357f9Sschwarze 			    "RE ... %s", ep);
223e94357f9Sschwarze 		free(p);
22404d44d89Sschwarze 		if (target == 0)
22504d44d89Sschwarze 			target = 1;
22604d44d89Sschwarze 		nrew -= target;
22704d44d89Sschwarze 		if (nrew < 1) {
228a5a5f808Sschwarze 			mandoc_msg(MANDOCERR_RE_NOTOPEN,
22904d44d89Sschwarze 			    line, ppos, "RE %d", target);
23004d44d89Sschwarze 			return;
23104d44d89Sschwarze 		}
232fbaebbd2Sschwarze 		break;
2335e5a9c61Sschwarze 	case MAN_YS:
2345e5a9c61Sschwarze 		ntok = MAN_SY;
2355e5a9c61Sschwarze 		break;
23649aff9f8Sschwarze 	case MAN_UE:
2373aeff926Sschwarze 		ntok = MAN_UR;
2383aeff926Sschwarze 		break;
239df9a9479Sbentley 	case MAN_ME:
240df9a9479Sbentley 		ntok = MAN_MT;
241df9a9479Sbentley 		break;
242fbaebbd2Sschwarze 	default:
243fbaebbd2Sschwarze 		abort();
244fbaebbd2Sschwarze 	}
245fbaebbd2Sschwarze 
2467ead8a4eSschwarze 	for (nn = man->last->parent; nn; nn = nn->parent)
247d1982c71Sschwarze 		if (nn->tok == ntok && nn->type == ROFFT_BLOCK && ! --nrew)
248fbaebbd2Sschwarze 			break;
249fbaebbd2Sschwarze 
250eae2af84Sschwarze 	if (nn == NULL) {
251a5a5f808Sschwarze 		mandoc_msg(MANDOCERR_BLK_NOTOPEN,
252a5a5f808Sschwarze 		    line, ppos, "%s", roff_name[tok]);
2536f5bf183Sschwarze 		rew_scope(man, MAN_PP);
254bb7bc875Sschwarze 		if (tok == MAN_RE) {
255bb7bc875Sschwarze 			roff_elem_alloc(man, line, ppos, ROFF_br);
256bb7bc875Sschwarze 			man->last->flags |= NODE_LINE |
257bb7bc875Sschwarze 			    NODE_VALID | NODE_ENDED;
258bb7bc875Sschwarze 			man->next = ROFF_NEXT_SIBLING;
259bb7bc875Sschwarze 		}
260252b7e4aSschwarze 		return;
261252b7e4aSschwarze 	}
262252b7e4aSschwarze 
263252b7e4aSschwarze 	cline = man->last->line;
264252b7e4aSschwarze 	cpos = man->last->pos;
265252b7e4aSschwarze 	ctok = man->last->tok;
2668b93ac39Sschwarze 	man_unscope(man, nn);
2674ca21368Sschwarze 
268ed5ade8aSschwarze 	if (tok == MAN_RE && nn->head->aux > 0)
269252b7e4aSschwarze 		roff_setreg(man->roff, "an-margin", nn->head->aux, '-');
270252b7e4aSschwarze 
271252b7e4aSschwarze 	/* Trailing text. */
272252b7e4aSschwarze 
273252b7e4aSschwarze 	if (buf[*pos] != '\0') {
274252b7e4aSschwarze 		roff_word_alloc(man, line, ppos, buf + *pos);
275252b7e4aSschwarze 		man->last->flags |= NODE_DELIMC;
276b1e77ba0Sschwarze 		if (mandoc_eos(man->last->string, strlen(man->last->string)))
277b1e77ba0Sschwarze 			man->last->flags |= NODE_EOS;
278252b7e4aSschwarze 	}
279ed5ade8aSschwarze 
2804ca21368Sschwarze 	/* Move a trailing paragraph behind the block. */
2814ca21368Sschwarze 
282252b7e4aSschwarze 	if (ctok == MAN_LP || ctok == MAN_PP || ctok == MAN_P) {
2834ca21368Sschwarze 		*pos = strlen(buf);
2843ae439c0Sschwarze 		blk_imp(man, ctok, cline, cpos, pos, buf);
2854ca21368Sschwarze 	}
28608d1df32Sschwarze 
28708d1df32Sschwarze 	/* Synopsis blocks need an explicit end marker for spacing. */
28808d1df32Sschwarze 
28908d1df32Sschwarze 	if (tok == MAN_YS && man->last == nn) {
29008d1df32Sschwarze 		roff_elem_alloc(man, line, ppos, tok);
29108d1df32Sschwarze 		man_unscope(man, man->last);
29208d1df32Sschwarze 	}
293fbaebbd2Sschwarze }
294fbaebbd2Sschwarze 
295eae2af84Sschwarze void
blk_exp(MACRO_PROT_ARGS)296ac531cf1Sschwarze blk_exp(MACRO_PROT_ARGS)
297ac531cf1Sschwarze {
2983a0d07afSschwarze 	struct roff_node *head;
299ac531cf1Sschwarze 	char		*p;
30039839348Sschwarze 	int		 la;
301ac531cf1Sschwarze 
3025c01bbd0Sschwarze 	if (tok == MAN_RS) {
3036f5bf183Sschwarze 		rew_scope(man, tok);
3045c01bbd0Sschwarze 		man->flags |= ROFF_NONOFILL;
3055c01bbd0Sschwarze 	}
306e32c44d4Sschwarze 	roff_block_alloc(man, line, ppos, tok);
307fa2127f9Sschwarze 	head = roff_head_alloc(man, line, ppos, tok);
308ac531cf1Sschwarze 
309ac531cf1Sschwarze 	la = *pos;
310ed5ade8aSschwarze 	if (man_args(man, line, pos, buf, &p)) {
31169c34eaaSschwarze 		roff_word_alloc(man, line, la, p);
312ed5ade8aSschwarze 		if (tok == MAN_RS) {
313ed5ade8aSschwarze 			if (roff_getreg(man->roff, "an-margin") == 0)
314ed5ade8aSschwarze 				roff_setreg(man->roff, "an-margin",
315*84680f53Sschwarze 				    5 * 24, '=');
316ed5ade8aSschwarze 			if ((head->aux = strtod(p, NULL) * 24.0) > 0)
317ed5ade8aSschwarze 				roff_setreg(man->roff, "an-margin",
318ed5ade8aSschwarze 				    head->aux, '+');
319ed5ade8aSschwarze 		}
320e94357f9Sschwarze 		free(p);
321ed5ade8aSschwarze 	}
322ac531cf1Sschwarze 
32339839348Sschwarze 	if (buf[*pos] != '\0')
324a5a5f808Sschwarze 		mandoc_msg(MANDOCERR_ARG_EXCESS, line, *pos,
325a5a5f808Sschwarze 		    "%s ... %s", roff_name[tok], buf + *pos);
326ac531cf1Sschwarze 
32739839348Sschwarze 	man_unscope(man, head);
328fa2127f9Sschwarze 	roff_body_alloc(man, line, ppos, tok);
3295c01bbd0Sschwarze 	man->flags &= ~ROFF_NONOFILL;
330ac531cf1Sschwarze }
331ac531cf1Sschwarze 
332e35cb253Sschwarze /*
333d1982c71Sschwarze  * Parse an implicit-block macro.  These contain a ROFFT_HEAD and a
334d1982c71Sschwarze  * ROFFT_BODY contained within a ROFFT_BLOCK.  Rules for closing out other
335e35cb253Sschwarze  * scopes, such as `SH' closing out an `SS', are defined in the rew
336e35cb253Sschwarze  * routines.
337e35cb253Sschwarze  */
338eae2af84Sschwarze void
blk_imp(MACRO_PROT_ARGS)339e35cb253Sschwarze blk_imp(MACRO_PROT_ARGS)
340f73abda9Skristaps {
341a35fc07aSschwarze 	int		 la;
342f73abda9Skristaps 	char		*p;
3433a0d07afSschwarze 	struct roff_node *n;
344f73abda9Skristaps 
3456f5bf183Sschwarze 	rew_scope(man, tok);
34694a3c318Sschwarze 	man->flags |= ROFF_NONOFILL;
3470438bfdfSschwarze 	if (tok == MAN_SH || tok == MAN_SS)
3480438bfdfSschwarze 		man->flags &= ~ROFF_NOFILL;
3490438bfdfSschwarze 	roff_block_alloc(man, line, ppos, tok);
350fa2127f9Sschwarze 	n = roff_head_alloc(man, line, ppos, tok);
351fbaebbd2Sschwarze 
352e35cb253Sschwarze 	/* Add line arguments. */
353f73abda9Skristaps 
354f73abda9Skristaps 	for (;;) {
355f73abda9Skristaps 		la = *pos;
3567ead8a4eSschwarze 		if ( ! man_args(man, line, pos, buf, &p))
357f73abda9Skristaps 			break;
35869c34eaaSschwarze 		roff_word_alloc(man, line, la, p);
359e94357f9Sschwarze 		free(p);
360f73abda9Skristaps 	}
361f73abda9Skristaps 
36270a5c2d4Sschwarze 	/*
36370a5c2d4Sschwarze 	 * For macros having optional next-line scope,
36470a5c2d4Sschwarze 	 * keep the head open if there were no arguments.
36563f82d49Sschwarze 	 * For `TP' and `TQ', always keep the head open.
36670a5c2d4Sschwarze 	 */
367e35cb253Sschwarze 
36863f82d49Sschwarze 	if (man_macro(tok)->flags & MAN_BSCOPED &&
369d991fc2cSschwarze 	    (tok == MAN_TP || tok == MAN_TQ || n == man->last)) {
3707ead8a4eSschwarze 		man->flags |= MAN_BLINE;
371eae2af84Sschwarze 		return;
372fbaebbd2Sschwarze 	}
37370a5c2d4Sschwarze 
37470a5c2d4Sschwarze 	/* Close out the head and open the body. */
37570a5c2d4Sschwarze 
376de972994Sschwarze 	man_unscope(man, n);
377fa2127f9Sschwarze 	roff_body_alloc(man, line, ppos, tok);
37894a3c318Sschwarze 	man->flags &= ~ROFF_NONOFILL;
379eae2af84Sschwarze }
380fbaebbd2Sschwarze 
381eae2af84Sschwarze void
in_line_eoln(MACRO_PROT_ARGS)382e35cb253Sschwarze in_line_eoln(MACRO_PROT_ARGS)
383e35cb253Sschwarze {
384a35fc07aSschwarze 	int		 la;
385e35cb253Sschwarze 	char		*p;
3863a0d07afSschwarze 	struct roff_node *n;
387e35cb253Sschwarze 
388e32c44d4Sschwarze 	roff_elem_alloc(man, line, ppos, tok);
3897ead8a4eSschwarze 	n = man->last;
390e35cb253Sschwarze 
391be32d3bdSschwarze 	if (tok == MAN_EX)
392be32d3bdSschwarze 		man->flags |= ROFF_NOFILL;
393be32d3bdSschwarze 	else if (tok == MAN_EE)
394be32d3bdSschwarze 		man->flags &= ~ROFF_NOFILL;
395be32d3bdSschwarze 
396e35cb253Sschwarze 	for (;;) {
3976561cb23Sschwarze 		if (buf[*pos] != '\0' && man->last != n && tok == MAN_PD) {
398a5a5f808Sschwarze 			mandoc_msg(MANDOCERR_ARG_EXCESS, line, *pos,
399a5a5f808Sschwarze 			    "%s ... %s", roff_name[tok], buf + *pos);
400e5d5fd76Sschwarze 			break;
401e5d5fd76Sschwarze 		}
402e35cb253Sschwarze 		la = *pos;
4037ead8a4eSschwarze 		if ( ! man_args(man, line, pos, buf, &p))
404e35cb253Sschwarze 			break;
40516fe0cfcSschwarze 		if (man_macro(tok)->flags & MAN_JOIN &&
406d1982c71Sschwarze 		    man->last->type == ROFFT_TEXT)
40769c34eaaSschwarze 			roff_word_append(man, p);
408eae2af84Sschwarze 		else
40969c34eaaSschwarze 			roff_word_alloc(man, line, la, p);
410e94357f9Sschwarze 		free(p);
411e35cb253Sschwarze 	}
412e35cb253Sschwarze 
41387e50d6cSschwarze 	/*
414c4b0939cSschwarze 	 * Append NODE_EOS in case the last snipped argument
4156838389dSschwarze 	 * ends with a dot, e.g. `.IR syslog (3).'
4166838389dSschwarze 	 */
4176838389dSschwarze 
4186838389dSschwarze 	if (n != man->last &&
419f29433e9Sschwarze 	    mandoc_eos(man->last->string, strlen(man->last->string)))
420c4b0939cSschwarze 		man->last->flags |= NODE_EOS;
4216838389dSschwarze 
4226838389dSschwarze 	/*
42363f82d49Sschwarze 	 * If no arguments are specified and this is MAN_ESCOPED (i.e.,
42487e50d6cSschwarze 	 * next-line scoped), then set our mode to indicate that we're
42587e50d6cSschwarze 	 * waiting for terms to load into our context.
42687e50d6cSschwarze 	 */
42787e50d6cSschwarze 
42863f82d49Sschwarze 	if (n == man->last && man_macro(tok)->flags & MAN_ESCOPED) {
4297ead8a4eSschwarze 		man->flags |= MAN_ELINE;
430eae2af84Sschwarze 		return;
431f73abda9Skristaps 	}
432f73abda9Skristaps 
433d1982c71Sschwarze 	assert(man->last->type != ROFFT_ROOT);
434ede1b9d0Sschwarze 	man->next = ROFF_NEXT_SIBLING;
4356f897e4aSschwarze 
436b3cd4c9aSschwarze 	/* Rewind our element scope. */
437f73abda9Skristaps 
4387ead8a4eSschwarze 	for ( ; man->last; man->last = man->last->parent) {
439542a495fSschwarze 		man->last->flags |= NODE_VALID;
4407ead8a4eSschwarze 		if (man->last == n)
441f73abda9Skristaps 			break;
442f73abda9Skristaps 	}
44363f82d49Sschwarze 
44463f82d49Sschwarze 	/* Rewind next-line scoped ancestors, if any. */
44563f82d49Sschwarze 
44663f82d49Sschwarze 	if (man_macro(tok)->flags & MAN_ESCOPED)
44763f82d49Sschwarze 		man_descope(man, line, ppos, NULL);
448f73abda9Skristaps }
449f73abda9Skristaps 
450eae2af84Sschwarze void
man_endparse(struct roff_man * man)451e0c8c906Sschwarze man_endparse(struct roff_man *man)
452f73abda9Skristaps {
4536b86842eSschwarze 	man_unscope(man, man->meta.first);
454f73abda9Skristaps }
455f73abda9Skristaps 
456a35fc07aSschwarze static int
man_args(struct roff_man * man,int line,int * pos,char * buf,char ** v)457ede1b9d0Sschwarze man_args(struct roff_man *man, int line, int *pos, char *buf, char **v)
458a35fc07aSschwarze {
459a35fc07aSschwarze 	char	 *start;
460a35fc07aSschwarze 
461a35fc07aSschwarze 	assert(*pos);
462a35fc07aSschwarze 	*v = start = buf + *pos;
463a35fc07aSschwarze 	assert(' ' != *start);
464a35fc07aSschwarze 
465a35fc07aSschwarze 	if ('\0' == *start)
466526e306bSschwarze 		return 0;
467a35fc07aSschwarze 
468e94357f9Sschwarze 	*v = roff_getarg(man->roff, v, line, pos);
469526e306bSschwarze 	return 1;
470a35fc07aSschwarze }
471