xref: /freebsd/contrib/mandoc/man_macro.c (revision 6d38604f)
1*6d38604fSBaptiste Daroussin /* $Id: man_macro.c,v 1.145 2020/09/09 17:01:10 schwarze Exp $ */
261d06d6bSBaptiste Daroussin /*
361d06d6bSBaptiste Daroussin  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4*6d38604fSBaptiste Daroussin  * Copyright (c) 2012-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org>
561d06d6bSBaptiste Daroussin  * Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
661d06d6bSBaptiste Daroussin  *
761d06d6bSBaptiste Daroussin  * Permission to use, copy, modify, and distribute this software for any
861d06d6bSBaptiste Daroussin  * purpose with or without fee is hereby granted, provided that the above
961d06d6bSBaptiste Daroussin  * copyright notice and this permission notice appear in all copies.
1061d06d6bSBaptiste Daroussin  *
1161d06d6bSBaptiste Daroussin  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1261d06d6bSBaptiste Daroussin  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1361d06d6bSBaptiste Daroussin  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1461d06d6bSBaptiste Daroussin  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1561d06d6bSBaptiste Daroussin  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1661d06d6bSBaptiste Daroussin  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1761d06d6bSBaptiste Daroussin  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1861d06d6bSBaptiste Daroussin  */
1961d06d6bSBaptiste Daroussin #include "config.h"
2061d06d6bSBaptiste Daroussin 
2161d06d6bSBaptiste Daroussin #include <sys/types.h>
2261d06d6bSBaptiste Daroussin 
2361d06d6bSBaptiste Daroussin #include <assert.h>
2461d06d6bSBaptiste Daroussin #include <ctype.h>
257295610fSBaptiste Daroussin #include <stdio.h>
2661d06d6bSBaptiste Daroussin #include <stdlib.h>
2761d06d6bSBaptiste Daroussin #include <string.h>
2861d06d6bSBaptiste Daroussin 
2961d06d6bSBaptiste Daroussin #include "mandoc.h"
3061d06d6bSBaptiste Daroussin #include "roff.h"
3161d06d6bSBaptiste Daroussin #include "man.h"
3261d06d6bSBaptiste Daroussin #include "libmandoc.h"
3361d06d6bSBaptiste Daroussin #include "roff_int.h"
3461d06d6bSBaptiste Daroussin #include "libman.h"
3561d06d6bSBaptiste Daroussin 
3661d06d6bSBaptiste Daroussin static	void		 blk_close(MACRO_PROT_ARGS);
3761d06d6bSBaptiste Daroussin static	void		 blk_exp(MACRO_PROT_ARGS);
3861d06d6bSBaptiste Daroussin static	void		 blk_imp(MACRO_PROT_ARGS);
3961d06d6bSBaptiste Daroussin static	void		 in_line_eoln(MACRO_PROT_ARGS);
4061d06d6bSBaptiste Daroussin static	int		 man_args(struct roff_man *, int,
4161d06d6bSBaptiste Daroussin 				int *, char *, char **);
4261d06d6bSBaptiste Daroussin static	void		 rew_scope(struct roff_man *, enum roff_tok);
4361d06d6bSBaptiste Daroussin 
447295610fSBaptiste Daroussin static const struct man_macro man_macros[MAN_MAX - MAN_TH] = {
457295610fSBaptiste Daroussin 	{ in_line_eoln, MAN_XSCOPE }, /* TH */
467295610fSBaptiste Daroussin 	{ blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* SH */
477295610fSBaptiste Daroussin 	{ blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* SS */
487295610fSBaptiste Daroussin 	{ blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* TP */
497295610fSBaptiste Daroussin 	{ blk_imp, MAN_XSCOPE | MAN_BSCOPED }, /* TQ */
507295610fSBaptiste Daroussin 	{ blk_imp, MAN_XSCOPE }, /* LP */
517295610fSBaptiste Daroussin 	{ blk_imp, MAN_XSCOPE }, /* PP */
527295610fSBaptiste Daroussin 	{ blk_imp, MAN_XSCOPE }, /* P */
537295610fSBaptiste Daroussin 	{ blk_imp, MAN_XSCOPE }, /* IP */
547295610fSBaptiste Daroussin 	{ blk_imp, MAN_XSCOPE }, /* HP */
557295610fSBaptiste Daroussin 	{ in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* SM */
567295610fSBaptiste Daroussin 	{ in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* SB */
5761d06d6bSBaptiste Daroussin 	{ in_line_eoln, 0 }, /* BI */
5861d06d6bSBaptiste Daroussin 	{ in_line_eoln, 0 }, /* IB */
5961d06d6bSBaptiste Daroussin 	{ in_line_eoln, 0 }, /* BR */
6061d06d6bSBaptiste Daroussin 	{ in_line_eoln, 0 }, /* RB */
617295610fSBaptiste Daroussin 	{ in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* R */
627295610fSBaptiste Daroussin 	{ in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* B */
637295610fSBaptiste Daroussin 	{ in_line_eoln, MAN_NSCOPED | MAN_ESCOPED | MAN_JOIN }, /* I */
6461d06d6bSBaptiste Daroussin 	{ in_line_eoln, 0 }, /* IR */
6561d06d6bSBaptiste Daroussin 	{ in_line_eoln, 0 }, /* RI */
667295610fSBaptiste Daroussin 	{ blk_close, MAN_XSCOPE }, /* RE */
677295610fSBaptiste Daroussin 	{ blk_exp, MAN_XSCOPE }, /* RS */
6861d06d6bSBaptiste Daroussin 	{ in_line_eoln, 0 }, /* DT */
6961d06d6bSBaptiste Daroussin 	{ in_line_eoln, 0 }, /* UC */
7061d06d6bSBaptiste Daroussin 	{ in_line_eoln, MAN_NSCOPED }, /* PD */
7161d06d6bSBaptiste Daroussin 	{ in_line_eoln, 0 }, /* AT */
7261d06d6bSBaptiste Daroussin 	{ in_line_eoln, MAN_NSCOPED }, /* in */
737295610fSBaptiste Daroussin 	{ blk_imp, MAN_XSCOPE }, /* SY */
747295610fSBaptiste Daroussin 	{ blk_close, MAN_XSCOPE }, /* YS */
7561d06d6bSBaptiste Daroussin 	{ in_line_eoln, 0 }, /* OP */
767295610fSBaptiste Daroussin 	{ in_line_eoln, MAN_XSCOPE }, /* EX */
777295610fSBaptiste Daroussin 	{ in_line_eoln, MAN_XSCOPE }, /* EE */
787295610fSBaptiste Daroussin 	{ blk_exp, MAN_XSCOPE }, /* UR */
797295610fSBaptiste Daroussin 	{ blk_close, MAN_XSCOPE }, /* UE */
807295610fSBaptiste Daroussin 	{ blk_exp, MAN_XSCOPE }, /* MT */
817295610fSBaptiste Daroussin 	{ blk_close, MAN_XSCOPE }, /* ME */
8261d06d6bSBaptiste Daroussin };
8361d06d6bSBaptiste Daroussin 
8461d06d6bSBaptiste Daroussin 
857295610fSBaptiste Daroussin const struct man_macro *
man_macro(enum roff_tok tok)867295610fSBaptiste Daroussin man_macro(enum roff_tok tok)
877295610fSBaptiste Daroussin {
887295610fSBaptiste Daroussin 	assert(tok >= MAN_TH && tok <= MAN_MAX);
897295610fSBaptiste Daroussin 	return man_macros + (tok - MAN_TH);
907295610fSBaptiste Daroussin }
917295610fSBaptiste Daroussin 
9261d06d6bSBaptiste Daroussin void
man_unscope(struct roff_man * man,const struct roff_node * to)9361d06d6bSBaptiste Daroussin man_unscope(struct roff_man *man, const struct roff_node *to)
9461d06d6bSBaptiste Daroussin {
9561d06d6bSBaptiste Daroussin 	struct roff_node *n;
9661d06d6bSBaptiste Daroussin 
9761d06d6bSBaptiste Daroussin 	to = to->parent;
9861d06d6bSBaptiste Daroussin 	n = man->last;
9961d06d6bSBaptiste Daroussin 	while (n != to) {
10061d06d6bSBaptiste Daroussin 
10161d06d6bSBaptiste Daroussin 		/* Reached the end of the document? */
10261d06d6bSBaptiste Daroussin 
10361d06d6bSBaptiste Daroussin 		if (to == NULL && ! (n->flags & NODE_VALID)) {
10461d06d6bSBaptiste Daroussin 			if (man->flags & (MAN_BLINE | MAN_ELINE) &&
1057295610fSBaptiste Daroussin 			    man_macro(n->tok)->flags &
1067295610fSBaptiste Daroussin 			     (MAN_BSCOPED | MAN_NSCOPED)) {
1077295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_BLK_LINE,
1087295610fSBaptiste Daroussin 				    n->line, n->pos,
10961d06d6bSBaptiste Daroussin 				    "EOF breaks %s", roff_name[n->tok]);
110*6d38604fSBaptiste Daroussin 				if (man->flags & MAN_ELINE) {
111*6d38604fSBaptiste Daroussin 					if ((man_macro(n->parent->tok)->flags &
112*6d38604fSBaptiste Daroussin 					    MAN_ESCOPED) == 0)
11361d06d6bSBaptiste Daroussin 						man->flags &= ~MAN_ELINE;
114*6d38604fSBaptiste Daroussin 				} else {
11561d06d6bSBaptiste Daroussin 					assert(n->type == ROFFT_HEAD);
11661d06d6bSBaptiste Daroussin 					n = n->parent;
11761d06d6bSBaptiste Daroussin 					man->flags &= ~MAN_BLINE;
11861d06d6bSBaptiste Daroussin 				}
11961d06d6bSBaptiste Daroussin 				man->last = n;
12061d06d6bSBaptiste Daroussin 				n = n->parent;
12161d06d6bSBaptiste Daroussin 				roff_node_delete(man, man->last);
12261d06d6bSBaptiste Daroussin 				continue;
12361d06d6bSBaptiste Daroussin 			}
12461d06d6bSBaptiste Daroussin 			if (n->type == ROFFT_BLOCK &&
1257295610fSBaptiste Daroussin 			    man_macro(n->tok)->fp == blk_exp)
12661d06d6bSBaptiste Daroussin 				mandoc_msg(MANDOCERR_BLK_NOEND,
1277295610fSBaptiste Daroussin 				    n->line, n->pos, "%s",
12861d06d6bSBaptiste Daroussin 				    roff_name[n->tok]);
12961d06d6bSBaptiste Daroussin 		}
13061d06d6bSBaptiste Daroussin 
13161d06d6bSBaptiste Daroussin 		/*
13261d06d6bSBaptiste Daroussin 		 * We might delete the man->last node
13361d06d6bSBaptiste Daroussin 		 * in the post-validation phase.
13461d06d6bSBaptiste Daroussin 		 * Save a pointer to the parent such that
13561d06d6bSBaptiste Daroussin 		 * we know where to continue the iteration.
13661d06d6bSBaptiste Daroussin 		 */
13761d06d6bSBaptiste Daroussin 
13861d06d6bSBaptiste Daroussin 		man->last = n;
13961d06d6bSBaptiste Daroussin 		n = n->parent;
14061d06d6bSBaptiste Daroussin 		man->last->flags |= NODE_VALID;
14161d06d6bSBaptiste Daroussin 	}
14261d06d6bSBaptiste Daroussin 
14361d06d6bSBaptiste Daroussin 	/*
14461d06d6bSBaptiste Daroussin 	 * If we ended up at the parent of the node we were
14561d06d6bSBaptiste Daroussin 	 * supposed to rewind to, that means the target node
14661d06d6bSBaptiste Daroussin 	 * got deleted, so add the next node we parse as a child
14761d06d6bSBaptiste Daroussin 	 * of the parent instead of as a sibling of the target.
14861d06d6bSBaptiste Daroussin 	 */
14961d06d6bSBaptiste Daroussin 
15061d06d6bSBaptiste Daroussin 	man->next = (man->last == to) ?
15161d06d6bSBaptiste Daroussin 	    ROFF_NEXT_CHILD : ROFF_NEXT_SIBLING;
15261d06d6bSBaptiste Daroussin }
15361d06d6bSBaptiste Daroussin 
15461d06d6bSBaptiste Daroussin /*
15561d06d6bSBaptiste Daroussin  * Rewinding entails ascending the parse tree until a coherent point,
15661d06d6bSBaptiste Daroussin  * for example, the `SH' macro will close out any intervening `SS'
15761d06d6bSBaptiste Daroussin  * scopes.  When a scope is closed, it must be validated and actioned.
15861d06d6bSBaptiste Daroussin  */
15961d06d6bSBaptiste Daroussin static void
rew_scope(struct roff_man * man,enum roff_tok tok)16061d06d6bSBaptiste Daroussin rew_scope(struct roff_man *man, enum roff_tok tok)
16161d06d6bSBaptiste Daroussin {
16261d06d6bSBaptiste Daroussin 	struct roff_node *n;
16361d06d6bSBaptiste Daroussin 
16461d06d6bSBaptiste Daroussin 	/* Preserve empty paragraphs before RS. */
16561d06d6bSBaptiste Daroussin 
16661d06d6bSBaptiste Daroussin 	n = man->last;
16761d06d6bSBaptiste Daroussin 	if (tok == MAN_RS && n->child == NULL &&
16861d06d6bSBaptiste Daroussin 	    (n->tok == MAN_P || n->tok == MAN_PP || n->tok == MAN_LP))
16961d06d6bSBaptiste Daroussin 		return;
17061d06d6bSBaptiste Daroussin 
17161d06d6bSBaptiste Daroussin 	for (;;) {
17261d06d6bSBaptiste Daroussin 		if (n->type == ROFFT_ROOT)
17361d06d6bSBaptiste Daroussin 			return;
17461d06d6bSBaptiste Daroussin 		if (n->flags & NODE_VALID) {
17561d06d6bSBaptiste Daroussin 			n = n->parent;
17661d06d6bSBaptiste Daroussin 			continue;
17761d06d6bSBaptiste Daroussin 		}
17861d06d6bSBaptiste Daroussin 		if (n->type != ROFFT_BLOCK) {
17961d06d6bSBaptiste Daroussin 			if (n->parent->type == ROFFT_ROOT) {
18061d06d6bSBaptiste Daroussin 				man_unscope(man, n);
18161d06d6bSBaptiste Daroussin 				return;
18261d06d6bSBaptiste Daroussin 			} else {
18361d06d6bSBaptiste Daroussin 				n = n->parent;
18461d06d6bSBaptiste Daroussin 				continue;
18561d06d6bSBaptiste Daroussin 			}
18661d06d6bSBaptiste Daroussin 		}
18761d06d6bSBaptiste Daroussin 		if (tok != MAN_SH && (n->tok == MAN_SH ||
18861d06d6bSBaptiste Daroussin 		    (tok != MAN_SS && (n->tok == MAN_SS ||
1897295610fSBaptiste Daroussin 		     man_macro(n->tok)->fp == blk_exp))))
19061d06d6bSBaptiste Daroussin 			return;
19161d06d6bSBaptiste Daroussin 		man_unscope(man, n);
19261d06d6bSBaptiste Daroussin 		n = man->last;
19361d06d6bSBaptiste Daroussin 	}
19461d06d6bSBaptiste Daroussin }
19561d06d6bSBaptiste Daroussin 
19661d06d6bSBaptiste Daroussin 
19761d06d6bSBaptiste Daroussin /*
19861d06d6bSBaptiste Daroussin  * Close out a generic explicit macro.
19961d06d6bSBaptiste Daroussin  */
20061d06d6bSBaptiste Daroussin void
blk_close(MACRO_PROT_ARGS)20161d06d6bSBaptiste Daroussin blk_close(MACRO_PROT_ARGS)
20261d06d6bSBaptiste Daroussin {
2037295610fSBaptiste Daroussin 	enum roff_tok		 ctok, ntok;
20461d06d6bSBaptiste Daroussin 	const struct roff_node	*nn;
2057295610fSBaptiste Daroussin 	char			*p, *ep;
2067295610fSBaptiste Daroussin 	int			 cline, cpos, la, nrew, target;
20761d06d6bSBaptiste Daroussin 
20861d06d6bSBaptiste Daroussin 	nrew = 1;
20961d06d6bSBaptiste Daroussin 	switch (tok) {
21061d06d6bSBaptiste Daroussin 	case MAN_RE:
21161d06d6bSBaptiste Daroussin 		ntok = MAN_RS;
2127295610fSBaptiste Daroussin 		la = *pos;
21361d06d6bSBaptiste Daroussin 		if ( ! man_args(man, line, pos, buf, &p))
21461d06d6bSBaptiste Daroussin 			break;
21561d06d6bSBaptiste Daroussin 		for (nn = man->last->parent; nn; nn = nn->parent)
21661d06d6bSBaptiste Daroussin 			if (nn->tok == ntok && nn->type == ROFFT_BLOCK)
21761d06d6bSBaptiste Daroussin 				nrew++;
2187295610fSBaptiste Daroussin 		target = strtol(p, &ep, 10);
2197295610fSBaptiste Daroussin 		if (*ep != '\0')
2207295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_ARG_EXCESS, line,
2217295610fSBaptiste Daroussin 			    la + (buf[la] == '"') + (int)(ep - p),
2227295610fSBaptiste Daroussin 			    "RE ... %s", ep);
2237295610fSBaptiste Daroussin 		free(p);
22461d06d6bSBaptiste Daroussin 		if (target == 0)
22561d06d6bSBaptiste Daroussin 			target = 1;
22661d06d6bSBaptiste Daroussin 		nrew -= target;
22761d06d6bSBaptiste Daroussin 		if (nrew < 1) {
2287295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_RE_NOTOPEN,
22961d06d6bSBaptiste Daroussin 			    line, ppos, "RE %d", target);
23061d06d6bSBaptiste Daroussin 			return;
23161d06d6bSBaptiste Daroussin 		}
23261d06d6bSBaptiste Daroussin 		break;
2337295610fSBaptiste Daroussin 	case MAN_YS:
2347295610fSBaptiste Daroussin 		ntok = MAN_SY;
2357295610fSBaptiste Daroussin 		break;
23661d06d6bSBaptiste Daroussin 	case MAN_UE:
23761d06d6bSBaptiste Daroussin 		ntok = MAN_UR;
23861d06d6bSBaptiste Daroussin 		break;
23961d06d6bSBaptiste Daroussin 	case MAN_ME:
24061d06d6bSBaptiste Daroussin 		ntok = MAN_MT;
24161d06d6bSBaptiste Daroussin 		break;
24261d06d6bSBaptiste Daroussin 	default:
24361d06d6bSBaptiste Daroussin 		abort();
24461d06d6bSBaptiste Daroussin 	}
24561d06d6bSBaptiste Daroussin 
24661d06d6bSBaptiste Daroussin 	for (nn = man->last->parent; nn; nn = nn->parent)
24761d06d6bSBaptiste Daroussin 		if (nn->tok == ntok && nn->type == ROFFT_BLOCK && ! --nrew)
24861d06d6bSBaptiste Daroussin 			break;
24961d06d6bSBaptiste Daroussin 
25061d06d6bSBaptiste Daroussin 	if (nn == NULL) {
2517295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_NOTOPEN,
2527295610fSBaptiste Daroussin 		    line, ppos, "%s", roff_name[tok]);
25361d06d6bSBaptiste Daroussin 		rew_scope(man, MAN_PP);
2547295610fSBaptiste Daroussin 		if (tok == MAN_RE) {
2557295610fSBaptiste Daroussin 			roff_elem_alloc(man, line, ppos, ROFF_br);
2567295610fSBaptiste Daroussin 			man->last->flags |= NODE_LINE |
2577295610fSBaptiste Daroussin 			    NODE_VALID | NODE_ENDED;
2587295610fSBaptiste Daroussin 			man->next = ROFF_NEXT_SIBLING;
2597295610fSBaptiste Daroussin 		}
2607295610fSBaptiste Daroussin 		return;
2617295610fSBaptiste Daroussin 	}
2627295610fSBaptiste Daroussin 
2637295610fSBaptiste Daroussin 	cline = man->last->line;
2647295610fSBaptiste Daroussin 	cpos = man->last->pos;
2657295610fSBaptiste Daroussin 	ctok = man->last->tok;
26661d06d6bSBaptiste Daroussin 	man_unscope(man, nn);
26761d06d6bSBaptiste Daroussin 
26861d06d6bSBaptiste Daroussin 	if (tok == MAN_RE && nn->head->aux > 0)
2697295610fSBaptiste Daroussin 		roff_setreg(man->roff, "an-margin", nn->head->aux, '-');
2707295610fSBaptiste Daroussin 
2717295610fSBaptiste Daroussin 	/* Trailing text. */
2727295610fSBaptiste Daroussin 
2737295610fSBaptiste Daroussin 	if (buf[*pos] != '\0') {
2747295610fSBaptiste Daroussin 		roff_word_alloc(man, line, ppos, buf + *pos);
2757295610fSBaptiste Daroussin 		man->last->flags |= NODE_DELIMC;
2767295610fSBaptiste Daroussin 		if (mandoc_eos(man->last->string, strlen(man->last->string)))
2777295610fSBaptiste Daroussin 			man->last->flags |= NODE_EOS;
2787295610fSBaptiste Daroussin 	}
27961d06d6bSBaptiste Daroussin 
28061d06d6bSBaptiste Daroussin 	/* Move a trailing paragraph behind the block. */
28161d06d6bSBaptiste Daroussin 
2827295610fSBaptiste Daroussin 	if (ctok == MAN_LP || ctok == MAN_PP || ctok == MAN_P) {
28361d06d6bSBaptiste Daroussin 		*pos = strlen(buf);
2847295610fSBaptiste Daroussin 		blk_imp(man, ctok, cline, cpos, pos, buf);
28561d06d6bSBaptiste Daroussin 	}
2867295610fSBaptiste Daroussin 
2877295610fSBaptiste Daroussin 	/* Synopsis blocks need an explicit end marker for spacing. */
2887295610fSBaptiste Daroussin 
2897295610fSBaptiste Daroussin 	if (tok == MAN_YS && man->last == nn) {
2907295610fSBaptiste Daroussin 		roff_elem_alloc(man, line, ppos, tok);
2917295610fSBaptiste Daroussin 		man_unscope(man, man->last);
29261d06d6bSBaptiste Daroussin 	}
29361d06d6bSBaptiste Daroussin }
29461d06d6bSBaptiste Daroussin 
29561d06d6bSBaptiste Daroussin void
blk_exp(MACRO_PROT_ARGS)29661d06d6bSBaptiste Daroussin blk_exp(MACRO_PROT_ARGS)
29761d06d6bSBaptiste Daroussin {
29861d06d6bSBaptiste Daroussin 	struct roff_node *head;
29961d06d6bSBaptiste Daroussin 	char		*p;
30061d06d6bSBaptiste Daroussin 	int		 la;
30161d06d6bSBaptiste Daroussin 
3027295610fSBaptiste Daroussin 	if (tok == MAN_RS) {
30361d06d6bSBaptiste Daroussin 		rew_scope(man, tok);
3047295610fSBaptiste Daroussin 		man->flags |= ROFF_NONOFILL;
3057295610fSBaptiste Daroussin 	}
30661d06d6bSBaptiste Daroussin 	roff_block_alloc(man, line, ppos, tok);
30761d06d6bSBaptiste Daroussin 	head = roff_head_alloc(man, line, ppos, tok);
30861d06d6bSBaptiste Daroussin 
30961d06d6bSBaptiste Daroussin 	la = *pos;
31061d06d6bSBaptiste Daroussin 	if (man_args(man, line, pos, buf, &p)) {
31161d06d6bSBaptiste Daroussin 		roff_word_alloc(man, line, la, p);
31261d06d6bSBaptiste Daroussin 		if (tok == MAN_RS) {
31361d06d6bSBaptiste Daroussin 			if (roff_getreg(man->roff, "an-margin") == 0)
31461d06d6bSBaptiste Daroussin 				roff_setreg(man->roff, "an-margin",
31561d06d6bSBaptiste Daroussin 				    7 * 24, '=');
31661d06d6bSBaptiste Daroussin 			if ((head->aux = strtod(p, NULL) * 24.0) > 0)
31761d06d6bSBaptiste Daroussin 				roff_setreg(man->roff, "an-margin",
31861d06d6bSBaptiste Daroussin 				    head->aux, '+');
31961d06d6bSBaptiste Daroussin 		}
3207295610fSBaptiste Daroussin 		free(p);
32161d06d6bSBaptiste Daroussin 	}
32261d06d6bSBaptiste Daroussin 
32361d06d6bSBaptiste Daroussin 	if (buf[*pos] != '\0')
3247295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_EXCESS, line, *pos,
3257295610fSBaptiste Daroussin 		    "%s ... %s", roff_name[tok], buf + *pos);
32661d06d6bSBaptiste Daroussin 
32761d06d6bSBaptiste Daroussin 	man_unscope(man, head);
32861d06d6bSBaptiste Daroussin 	roff_body_alloc(man, line, ppos, tok);
3297295610fSBaptiste Daroussin 	man->flags &= ~ROFF_NONOFILL;
33061d06d6bSBaptiste Daroussin }
33161d06d6bSBaptiste Daroussin 
33261d06d6bSBaptiste Daroussin /*
33361d06d6bSBaptiste Daroussin  * Parse an implicit-block macro.  These contain a ROFFT_HEAD and a
33461d06d6bSBaptiste Daroussin  * ROFFT_BODY contained within a ROFFT_BLOCK.  Rules for closing out other
33561d06d6bSBaptiste Daroussin  * scopes, such as `SH' closing out an `SS', are defined in the rew
33661d06d6bSBaptiste Daroussin  * routines.
33761d06d6bSBaptiste Daroussin  */
33861d06d6bSBaptiste Daroussin void
blk_imp(MACRO_PROT_ARGS)33961d06d6bSBaptiste Daroussin blk_imp(MACRO_PROT_ARGS)
34061d06d6bSBaptiste Daroussin {
34161d06d6bSBaptiste Daroussin 	int		 la;
34261d06d6bSBaptiste Daroussin 	char		*p;
34361d06d6bSBaptiste Daroussin 	struct roff_node *n;
34461d06d6bSBaptiste Daroussin 
34561d06d6bSBaptiste Daroussin 	rew_scope(man, tok);
3467295610fSBaptiste Daroussin 	man->flags |= ROFF_NONOFILL;
3477295610fSBaptiste Daroussin 	if (tok == MAN_SH || tok == MAN_SS)
3487295610fSBaptiste Daroussin 		man->flags &= ~ROFF_NOFILL;
3497295610fSBaptiste Daroussin 	roff_block_alloc(man, line, ppos, tok);
35061d06d6bSBaptiste Daroussin 	n = roff_head_alloc(man, line, ppos, tok);
35161d06d6bSBaptiste Daroussin 
35261d06d6bSBaptiste Daroussin 	/* Add line arguments. */
35361d06d6bSBaptiste Daroussin 
35461d06d6bSBaptiste Daroussin 	for (;;) {
35561d06d6bSBaptiste Daroussin 		la = *pos;
35661d06d6bSBaptiste Daroussin 		if ( ! man_args(man, line, pos, buf, &p))
35761d06d6bSBaptiste Daroussin 			break;
35861d06d6bSBaptiste Daroussin 		roff_word_alloc(man, line, la, p);
3597295610fSBaptiste Daroussin 		free(p);
36061d06d6bSBaptiste Daroussin 	}
36161d06d6bSBaptiste Daroussin 
36261d06d6bSBaptiste Daroussin 	/*
36361d06d6bSBaptiste Daroussin 	 * For macros having optional next-line scope,
36461d06d6bSBaptiste Daroussin 	 * keep the head open if there were no arguments.
3657295610fSBaptiste Daroussin 	 * For `TP' and `TQ', always keep the head open.
36661d06d6bSBaptiste Daroussin 	 */
36761d06d6bSBaptiste Daroussin 
3687295610fSBaptiste Daroussin 	if (man_macro(tok)->flags & MAN_BSCOPED &&
3697295610fSBaptiste Daroussin 	    (tok == MAN_TP || tok == MAN_TQ || n == man->last)) {
37061d06d6bSBaptiste Daroussin 		man->flags |= MAN_BLINE;
37161d06d6bSBaptiste Daroussin 		return;
37261d06d6bSBaptiste Daroussin 	}
37361d06d6bSBaptiste Daroussin 
37461d06d6bSBaptiste Daroussin 	/* Close out the head and open the body. */
37561d06d6bSBaptiste Daroussin 
37661d06d6bSBaptiste Daroussin 	man_unscope(man, n);
37761d06d6bSBaptiste Daroussin 	roff_body_alloc(man, line, ppos, tok);
3787295610fSBaptiste Daroussin 	man->flags &= ~ROFF_NONOFILL;
37961d06d6bSBaptiste Daroussin }
38061d06d6bSBaptiste Daroussin 
38161d06d6bSBaptiste Daroussin void
in_line_eoln(MACRO_PROT_ARGS)38261d06d6bSBaptiste Daroussin in_line_eoln(MACRO_PROT_ARGS)
38361d06d6bSBaptiste Daroussin {
38461d06d6bSBaptiste Daroussin 	int		 la;
38561d06d6bSBaptiste Daroussin 	char		*p;
38661d06d6bSBaptiste Daroussin 	struct roff_node *n;
38761d06d6bSBaptiste Daroussin 
38861d06d6bSBaptiste Daroussin 	roff_elem_alloc(man, line, ppos, tok);
38961d06d6bSBaptiste Daroussin 	n = man->last;
39061d06d6bSBaptiste Daroussin 
3917295610fSBaptiste Daroussin 	if (tok == MAN_EX)
3927295610fSBaptiste Daroussin 		man->flags |= ROFF_NOFILL;
3937295610fSBaptiste Daroussin 	else if (tok == MAN_EE)
3947295610fSBaptiste Daroussin 		man->flags &= ~ROFF_NOFILL;
3957295610fSBaptiste Daroussin 
39661d06d6bSBaptiste Daroussin 	for (;;) {
39761d06d6bSBaptiste Daroussin 		if (buf[*pos] != '\0' && man->last != n && tok == MAN_PD) {
3987295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_ARG_EXCESS, line, *pos,
3997295610fSBaptiste Daroussin 			    "%s ... %s", roff_name[tok], buf + *pos);
40061d06d6bSBaptiste Daroussin 			break;
40161d06d6bSBaptiste Daroussin 		}
40261d06d6bSBaptiste Daroussin 		la = *pos;
40361d06d6bSBaptiste Daroussin 		if ( ! man_args(man, line, pos, buf, &p))
40461d06d6bSBaptiste Daroussin 			break;
4057295610fSBaptiste Daroussin 		if (man_macro(tok)->flags & MAN_JOIN &&
40661d06d6bSBaptiste Daroussin 		    man->last->type == ROFFT_TEXT)
40761d06d6bSBaptiste Daroussin 			roff_word_append(man, p);
40861d06d6bSBaptiste Daroussin 		else
40961d06d6bSBaptiste Daroussin 			roff_word_alloc(man, line, la, p);
4107295610fSBaptiste Daroussin 		free(p);
41161d06d6bSBaptiste Daroussin 	}
41261d06d6bSBaptiste Daroussin 
41361d06d6bSBaptiste Daroussin 	/*
41461d06d6bSBaptiste Daroussin 	 * Append NODE_EOS in case the last snipped argument
41561d06d6bSBaptiste Daroussin 	 * ends with a dot, e.g. `.IR syslog (3).'
41661d06d6bSBaptiste Daroussin 	 */
41761d06d6bSBaptiste Daroussin 
41861d06d6bSBaptiste Daroussin 	if (n != man->last &&
41961d06d6bSBaptiste Daroussin 	    mandoc_eos(man->last->string, strlen(man->last->string)))
42061d06d6bSBaptiste Daroussin 		man->last->flags |= NODE_EOS;
42161d06d6bSBaptiste Daroussin 
42261d06d6bSBaptiste Daroussin 	/*
4237295610fSBaptiste Daroussin 	 * If no arguments are specified and this is MAN_ESCOPED (i.e.,
42461d06d6bSBaptiste Daroussin 	 * next-line scoped), then set our mode to indicate that we're
42561d06d6bSBaptiste Daroussin 	 * waiting for terms to load into our context.
42661d06d6bSBaptiste Daroussin 	 */
42761d06d6bSBaptiste Daroussin 
4287295610fSBaptiste Daroussin 	if (n == man->last && man_macro(tok)->flags & MAN_ESCOPED) {
42961d06d6bSBaptiste Daroussin 		man->flags |= MAN_ELINE;
43061d06d6bSBaptiste Daroussin 		return;
43161d06d6bSBaptiste Daroussin 	}
43261d06d6bSBaptiste Daroussin 
43361d06d6bSBaptiste Daroussin 	assert(man->last->type != ROFFT_ROOT);
43461d06d6bSBaptiste Daroussin 	man->next = ROFF_NEXT_SIBLING;
43561d06d6bSBaptiste Daroussin 
43661d06d6bSBaptiste Daroussin 	/* Rewind our element scope. */
43761d06d6bSBaptiste Daroussin 
43861d06d6bSBaptiste Daroussin 	for ( ; man->last; man->last = man->last->parent) {
4397295610fSBaptiste Daroussin 		man->last->flags |= NODE_VALID;
44061d06d6bSBaptiste Daroussin 		if (man->last == n)
44161d06d6bSBaptiste Daroussin 			break;
44261d06d6bSBaptiste Daroussin 	}
4437295610fSBaptiste Daroussin 
4447295610fSBaptiste Daroussin 	/* Rewind next-line scoped ancestors, if any. */
4457295610fSBaptiste Daroussin 
4467295610fSBaptiste Daroussin 	if (man_macro(tok)->flags & MAN_ESCOPED)
4477295610fSBaptiste Daroussin 		man_descope(man, line, ppos, NULL);
44861d06d6bSBaptiste Daroussin }
44961d06d6bSBaptiste Daroussin 
45061d06d6bSBaptiste Daroussin void
man_endparse(struct roff_man * man)45161d06d6bSBaptiste Daroussin man_endparse(struct roff_man *man)
45261d06d6bSBaptiste Daroussin {
4537295610fSBaptiste Daroussin 	man_unscope(man, man->meta.first);
45461d06d6bSBaptiste Daroussin }
45561d06d6bSBaptiste Daroussin 
45661d06d6bSBaptiste Daroussin static int
man_args(struct roff_man * man,int line,int * pos,char * buf,char ** v)45761d06d6bSBaptiste Daroussin man_args(struct roff_man *man, int line, int *pos, char *buf, char **v)
45861d06d6bSBaptiste Daroussin {
45961d06d6bSBaptiste Daroussin 	char	 *start;
46061d06d6bSBaptiste Daroussin 
46161d06d6bSBaptiste Daroussin 	assert(*pos);
46261d06d6bSBaptiste Daroussin 	*v = start = buf + *pos;
46361d06d6bSBaptiste Daroussin 	assert(' ' != *start);
46461d06d6bSBaptiste Daroussin 
46561d06d6bSBaptiste Daroussin 	if ('\0' == *start)
46661d06d6bSBaptiste Daroussin 		return 0;
46761d06d6bSBaptiste Daroussin 
4687295610fSBaptiste Daroussin 	*v = roff_getarg(man->roff, v, line, pos);
46961d06d6bSBaptiste Daroussin 	return 1;
47061d06d6bSBaptiste Daroussin }
471