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