man_macro.c revision 1.2 1 1.2 christos /* $Id: man_macro.c,v 1.2 2016/01/07 20:00:35 christos Exp $ */
2 1.1 joerg /*
3 1.2 christos * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps (at) bsd.lv>
4 1.2 christos * Copyright (c) 2012, 2013, 2014, 2015 Ingo Schwarze <schwarze (at) openbsd.org>
5 1.2 christos * Copyright (c) 2013 Franco Fichtner <franco (at) lastsummer.de>
6 1.1 joerg *
7 1.1 joerg * Permission to use, copy, modify, and distribute this software for any
8 1.1 joerg * purpose with or without fee is hereby granted, provided that the above
9 1.1 joerg * copyright notice and this permission notice appear in all copies.
10 1.1 joerg *
11 1.1 joerg * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 1.1 joerg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 1.1 joerg * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 1.1 joerg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 1.1 joerg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 1.1 joerg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 1.1 joerg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 1.1 joerg */
19 1.2 christos #include "config.h"
20 1.2 christos
21 1.2 christos #include <sys/types.h>
22 1.2 christos
23 1.1 joerg #include <assert.h>
24 1.1 joerg #include <ctype.h>
25 1.1 joerg #include <stdlib.h>
26 1.1 joerg #include <string.h>
27 1.1 joerg
28 1.2 christos #include "man.h"
29 1.2 christos #include "mandoc.h"
30 1.2 christos #include "libmandoc.h"
31 1.1 joerg #include "libman.h"
32 1.1 joerg
33 1.2 christos enum rew {
34 1.2 christos REW_REWIND,
35 1.2 christos REW_NOHALT,
36 1.2 christos REW_HALT
37 1.2 christos };
38 1.1 joerg
39 1.2 christos static void blk_close(MACRO_PROT_ARGS);
40 1.2 christos static void blk_exp(MACRO_PROT_ARGS);
41 1.2 christos static void blk_imp(MACRO_PROT_ARGS);
42 1.2 christos static void in_line_eoln(MACRO_PROT_ARGS);
43 1.2 christos static int man_args(struct man *, int,
44 1.2 christos int *, char *, char **);
45 1.2 christos
46 1.2 christos static void rew_scope(enum man_type,
47 1.2 christos struct man *, enum mant);
48 1.2 christos static enum rew rew_dohalt(enum mant, enum man_type,
49 1.1 joerg const struct man_node *);
50 1.2 christos static enum rew rew_block(enum mant, enum man_type,
51 1.1 joerg const struct man_node *);
52 1.1 joerg
53 1.1 joerg const struct man_macro __man_macros[MAN_MAX] = {
54 1.2 christos { in_line_eoln, MAN_NSCOPED }, /* br */
55 1.2 christos { in_line_eoln, MAN_BSCOPE }, /* TH */
56 1.2 christos { blk_imp, MAN_BSCOPE | MAN_SCOPED }, /* SH */
57 1.2 christos { blk_imp, MAN_BSCOPE | MAN_SCOPED }, /* SS */
58 1.2 christos { blk_imp, MAN_BSCOPE | MAN_SCOPED | MAN_FSCOPED }, /* TP */
59 1.2 christos { blk_imp, MAN_BSCOPE }, /* LP */
60 1.2 christos { blk_imp, MAN_BSCOPE }, /* PP */
61 1.2 christos { blk_imp, MAN_BSCOPE }, /* P */
62 1.2 christos { blk_imp, MAN_BSCOPE }, /* IP */
63 1.2 christos { blk_imp, MAN_BSCOPE }, /* HP */
64 1.2 christos { in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* SM */
65 1.2 christos { in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* SB */
66 1.1 joerg { in_line_eoln, 0 }, /* BI */
67 1.1 joerg { in_line_eoln, 0 }, /* IB */
68 1.1 joerg { in_line_eoln, 0 }, /* BR */
69 1.1 joerg { in_line_eoln, 0 }, /* RB */
70 1.2 christos { in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* R */
71 1.2 christos { in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* B */
72 1.2 christos { in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* I */
73 1.1 joerg { in_line_eoln, 0 }, /* IR */
74 1.1 joerg { in_line_eoln, 0 }, /* RI */
75 1.2 christos { in_line_eoln, MAN_NSCOPED }, /* sp */
76 1.2 christos { in_line_eoln, MAN_BSCOPE }, /* nf */
77 1.2 christos { in_line_eoln, MAN_BSCOPE }, /* fi */
78 1.2 christos { blk_close, MAN_BSCOPE }, /* RE */
79 1.2 christos { blk_exp, MAN_BSCOPE | MAN_EXPLICIT }, /* RS */
80 1.1 joerg { in_line_eoln, 0 }, /* DT */
81 1.1 joerg { in_line_eoln, 0 }, /* UC */
82 1.2 christos { in_line_eoln, 0 }, /* PD */
83 1.2 christos { in_line_eoln, 0 }, /* AT */
84 1.2 christos { in_line_eoln, 0 }, /* in */
85 1.2 christos { in_line_eoln, 0 }, /* ft */
86 1.2 christos { in_line_eoln, 0 }, /* OP */
87 1.2 christos { in_line_eoln, MAN_BSCOPE }, /* EX */
88 1.2 christos { in_line_eoln, MAN_BSCOPE }, /* EE */
89 1.2 christos { blk_exp, MAN_BSCOPE | MAN_EXPLICIT }, /* UR */
90 1.2 christos { blk_close, MAN_BSCOPE }, /* UE */
91 1.2 christos { in_line_eoln, 0 }, /* ll */
92 1.1 joerg };
93 1.1 joerg
94 1.1 joerg const struct man_macro * const man_macros = __man_macros;
95 1.1 joerg
96 1.1 joerg
97 1.2 christos void
98 1.2 christos man_unscope(struct man *man, const struct man_node *to)
99 1.1 joerg {
100 1.2 christos struct man_node *n;
101 1.1 joerg
102 1.2 christos to = to->parent;
103 1.2 christos n = man->last;
104 1.2 christos while (n != to) {
105 1.2 christos
106 1.2 christos /* Reached the end of the document? */
107 1.2 christos
108 1.2 christos if (to == NULL && ! (n->flags & MAN_VALID)) {
109 1.2 christos if (man->flags & (MAN_BLINE | MAN_ELINE) &&
110 1.2 christos man_macros[n->tok].flags & MAN_SCOPED) {
111 1.2 christos mandoc_vmsg(MANDOCERR_BLK_LINE,
112 1.2 christos man->parse, n->line, n->pos,
113 1.2 christos "EOF breaks %s",
114 1.2 christos man_macronames[n->tok]);
115 1.2 christos if (man->flags & MAN_ELINE)
116 1.2 christos man->flags &= ~MAN_ELINE;
117 1.2 christos else {
118 1.2 christos assert(n->type == MAN_HEAD);
119 1.2 christos n = n->parent;
120 1.2 christos man->flags &= ~MAN_BLINE;
121 1.2 christos }
122 1.2 christos man->last = n;
123 1.2 christos n = n->parent;
124 1.2 christos man_node_delete(man, man->last);
125 1.2 christos continue;
126 1.2 christos }
127 1.2 christos if (n->type == MAN_BLOCK &&
128 1.2 christos man_macros[n->tok].flags & MAN_EXPLICIT)
129 1.2 christos mandoc_msg(MANDOCERR_BLK_NOEND,
130 1.2 christos man->parse, n->line, n->pos,
131 1.2 christos man_macronames[n->tok]);
132 1.2 christos }
133 1.1 joerg
134 1.2 christos /*
135 1.2 christos * We might delete the man->last node
136 1.2 christos * in the post-validation phase.
137 1.2 christos * Save a pointer to the parent such that
138 1.2 christos * we know where to continue the iteration.
139 1.2 christos */
140 1.2 christos
141 1.2 christos man->last = n;
142 1.2 christos n = n->parent;
143 1.2 christos man_valid_post(man);
144 1.1 joerg }
145 1.1 joerg
146 1.2 christos /*
147 1.2 christos * If we ended up at the parent of the node we were
148 1.2 christos * supposed to rewind to, that means the target node
149 1.2 christos * got deleted, so add the next node we parse as a child
150 1.2 christos * of the parent instead of as a sibling of the target.
151 1.2 christos */
152 1.2 christos
153 1.2 christos man->next = (man->last == to) ?
154 1.2 christos MAN_NEXT_CHILD : MAN_NEXT_SIBLING;
155 1.1 joerg }
156 1.1 joerg
157 1.2 christos static enum rew
158 1.2 christos rew_block(enum mant ntok, enum man_type type, const struct man_node *n)
159 1.1 joerg {
160 1.1 joerg
161 1.2 christos if (type == MAN_BLOCK && ntok == n->parent->tok &&
162 1.2 christos n->parent->type == MAN_BODY)
163 1.1 joerg return(REW_REWIND);
164 1.1 joerg return(ntok == n->tok ? REW_HALT : REW_NOHALT);
165 1.1 joerg }
166 1.1 joerg
167 1.1 joerg /*
168 1.1 joerg * There are three scope levels: scoped to the root (all), scoped to the
169 1.1 joerg * section (all less sections), and scoped to subsections (all less
170 1.1 joerg * sections and subsections).
171 1.1 joerg */
172 1.2 christos static enum rew
173 1.2 christos rew_dohalt(enum mant tok, enum man_type type, const struct man_node *n)
174 1.1 joerg {
175 1.2 christos enum rew c;
176 1.1 joerg
177 1.2 christos /* We cannot progress beyond the root ever. */
178 1.1 joerg if (MAN_ROOT == n->type)
179 1.1 joerg return(REW_HALT);
180 1.2 christos
181 1.1 joerg assert(n->parent);
182 1.2 christos
183 1.2 christos /* Normal nodes shouldn't go to the level of the root. */
184 1.1 joerg if (MAN_ROOT == n->parent->type)
185 1.1 joerg return(REW_REWIND);
186 1.2 christos
187 1.2 christos /* Already-validated nodes should be closed out. */
188 1.1 joerg if (MAN_VALID & n->flags)
189 1.1 joerg return(REW_NOHALT);
190 1.1 joerg
191 1.2 christos /* First: rewind to ourselves. */
192 1.2 christos if (type == n->type && tok == n->tok) {
193 1.2 christos if (MAN_EXPLICIT & man_macros[n->tok].flags)
194 1.2 christos return(REW_HALT);
195 1.2 christos else
196 1.2 christos return(REW_REWIND);
197 1.2 christos }
198 1.2 christos
199 1.2 christos /*
200 1.2 christos * Next follow the implicit scope-smashings as defined by man.7:
201 1.2 christos * section, sub-section, etc.
202 1.2 christos */
203 1.1 joerg
204 1.1 joerg switch (tok) {
205 1.2 christos case MAN_SH:
206 1.1 joerg break;
207 1.2 christos case MAN_SS:
208 1.1 joerg /* Rewind to a section, if a block. */
209 1.1 joerg if (REW_NOHALT != (c = rew_block(MAN_SH, type, n)))
210 1.1 joerg return(c);
211 1.1 joerg break;
212 1.2 christos case MAN_RS:
213 1.2 christos /* Preserve empty paragraphs before RS. */
214 1.2 christos if (0 == n->nchild && (MAN_P == n->tok ||
215 1.2 christos MAN_PP == n->tok || MAN_LP == n->tok))
216 1.2 christos return(REW_HALT);
217 1.1 joerg /* Rewind to a subsection, if a block. */
218 1.1 joerg if (REW_NOHALT != (c = rew_block(MAN_SS, type, n)))
219 1.1 joerg return(c);
220 1.1 joerg /* Rewind to a section, if a block. */
221 1.1 joerg if (REW_NOHALT != (c = rew_block(MAN_SH, type, n)))
222 1.1 joerg return(c);
223 1.1 joerg break;
224 1.1 joerg default:
225 1.1 joerg /* Rewind to an offsetter, if a block. */
226 1.1 joerg if (REW_NOHALT != (c = rew_block(MAN_RS, type, n)))
227 1.1 joerg return(c);
228 1.1 joerg /* Rewind to a subsection, if a block. */
229 1.1 joerg if (REW_NOHALT != (c = rew_block(MAN_SS, type, n)))
230 1.1 joerg return(c);
231 1.1 joerg /* Rewind to a section, if a block. */
232 1.1 joerg if (REW_NOHALT != (c = rew_block(MAN_SH, type, n)))
233 1.1 joerg return(c);
234 1.1 joerg break;
235 1.1 joerg }
236 1.1 joerg
237 1.1 joerg return(REW_NOHALT);
238 1.1 joerg }
239 1.1 joerg
240 1.1 joerg /*
241 1.1 joerg * Rewinding entails ascending the parse tree until a coherent point,
242 1.1 joerg * for example, the `SH' macro will close out any intervening `SS'
243 1.1 joerg * scopes. When a scope is closed, it must be validated and actioned.
244 1.1 joerg */
245 1.2 christos static void
246 1.2 christos rew_scope(enum man_type type, struct man *man, enum mant tok)
247 1.1 joerg {
248 1.1 joerg struct man_node *n;
249 1.2 christos enum rew c;
250 1.1 joerg
251 1.2 christos for (n = man->last; n; n = n->parent) {
252 1.2 christos /*
253 1.1 joerg * Whether we should stop immediately (REW_HALT), stop
254 1.1 joerg * and rewind until this point (REW_REWIND), or keep
255 1.1 joerg * rewinding (REW_NOHALT).
256 1.1 joerg */
257 1.1 joerg c = rew_dohalt(tok, type, n);
258 1.1 joerg if (REW_HALT == c)
259 1.2 christos return;
260 1.1 joerg if (REW_REWIND == c)
261 1.1 joerg break;
262 1.1 joerg }
263 1.1 joerg
264 1.2 christos /*
265 1.2 christos * Rewind until the current point. Warn if we're a roff
266 1.2 christos * instruction that's mowing over explicit scopes.
267 1.2 christos */
268 1.2 christos if (n != NULL)
269 1.2 christos man_unscope(man, n);
270 1.1 joerg }
271 1.1 joerg
272 1.1 joerg
273 1.2 christos /*
274 1.2 christos * Close out a generic explicit macro.
275 1.2 christos */
276 1.2 christos void
277 1.1 joerg blk_close(MACRO_PROT_ARGS)
278 1.1 joerg {
279 1.2 christos enum mant ntok;
280 1.1 joerg const struct man_node *nn;
281 1.2 christos char *p;
282 1.2 christos int nrew, target;
283 1.1 joerg
284 1.2 christos nrew = 1;
285 1.1 joerg switch (tok) {
286 1.2 christos case MAN_RE:
287 1.1 joerg ntok = MAN_RS;
288 1.2 christos if ( ! man_args(man, line, pos, buf, &p))
289 1.2 christos break;
290 1.2 christos for (nn = man->last->parent; nn; nn = nn->parent)
291 1.2 christos if (nn->tok == ntok && nn->type == MAN_BLOCK)
292 1.2 christos nrew++;
293 1.2 christos target = strtol(p, &p, 10);
294 1.2 christos if (*p != '\0')
295 1.2 christos mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse,
296 1.2 christos line, p - buf, "RE ... %s", p);
297 1.2 christos if (target == 0)
298 1.2 christos target = 1;
299 1.2 christos nrew -= target;
300 1.2 christos if (nrew < 1) {
301 1.2 christos mandoc_vmsg(MANDOCERR_RE_NOTOPEN, man->parse,
302 1.2 christos line, ppos, "RE %d", target);
303 1.2 christos return;
304 1.2 christos }
305 1.2 christos break;
306 1.2 christos case MAN_UE:
307 1.2 christos ntok = MAN_UR;
308 1.1 joerg break;
309 1.1 joerg default:
310 1.1 joerg abort();
311 1.1 joerg /* NOTREACHED */
312 1.1 joerg }
313 1.1 joerg
314 1.2 christos for (nn = man->last->parent; nn; nn = nn->parent)
315 1.2 christos if (nn->tok == ntok && nn->type == MAN_BLOCK && ! --nrew)
316 1.1 joerg break;
317 1.1 joerg
318 1.2 christos if (nn == NULL) {
319 1.2 christos mandoc_msg(MANDOCERR_BLK_NOTOPEN, man->parse,
320 1.2 christos line, ppos, man_macronames[tok]);
321 1.2 christos rew_scope(MAN_BLOCK, man, MAN_PP);
322 1.2 christos } else {
323 1.2 christos line = man->last->line;
324 1.2 christos ppos = man->last->pos;
325 1.2 christos ntok = man->last->tok;
326 1.2 christos man_unscope(man, nn);
327 1.2 christos
328 1.2 christos /* Move a trailing paragraph behind the block. */
329 1.2 christos
330 1.2 christos if (ntok == MAN_LP || ntok == MAN_PP || ntok == MAN_P) {
331 1.2 christos *pos = strlen(buf);
332 1.2 christos blk_imp(man, ntok, line, ppos, pos, buf);
333 1.2 christos }
334 1.2 christos }
335 1.2 christos }
336 1.2 christos
337 1.2 christos void
338 1.2 christos blk_exp(MACRO_PROT_ARGS)
339 1.2 christos {
340 1.2 christos struct man_node *head;
341 1.2 christos char *p;
342 1.2 christos int la;
343 1.1 joerg
344 1.2 christos rew_scope(MAN_BLOCK, man, tok);
345 1.2 christos man_block_alloc(man, line, ppos, tok);
346 1.2 christos man_head_alloc(man, line, ppos, tok);
347 1.2 christos head = man->last;
348 1.2 christos
349 1.2 christos la = *pos;
350 1.2 christos if (man_args(man, line, pos, buf, &p))
351 1.2 christos man_word_alloc(man, line, la, p);
352 1.2 christos
353 1.2 christos if (buf[*pos] != '\0')
354 1.2 christos mandoc_vmsg(MANDOCERR_ARG_EXCESS,
355 1.2 christos man->parse, line, *pos, "%s ... %s",
356 1.2 christos man_macronames[tok], buf + *pos);
357 1.2 christos
358 1.2 christos man_unscope(man, head);
359 1.2 christos man_body_alloc(man, line, ppos, tok);
360 1.1 joerg }
361 1.1 joerg
362 1.1 joerg /*
363 1.1 joerg * Parse an implicit-block macro. These contain a MAN_HEAD and a
364 1.1 joerg * MAN_BODY contained within a MAN_BLOCK. Rules for closing out other
365 1.1 joerg * scopes, such as `SH' closing out an `SS', are defined in the rew
366 1.1 joerg * routines.
367 1.1 joerg */
368 1.2 christos void
369 1.1 joerg blk_imp(MACRO_PROT_ARGS)
370 1.1 joerg {
371 1.2 christos int la;
372 1.1 joerg char *p;
373 1.1 joerg struct man_node *n;
374 1.1 joerg
375 1.2 christos rew_scope(MAN_BODY, man, tok);
376 1.2 christos rew_scope(MAN_BLOCK, man, tok);
377 1.2 christos man_block_alloc(man, line, ppos, tok);
378 1.2 christos man_head_alloc(man, line, ppos, tok);
379 1.2 christos n = man->last;
380 1.1 joerg
381 1.1 joerg /* Add line arguments. */
382 1.1 joerg
383 1.1 joerg for (;;) {
384 1.1 joerg la = *pos;
385 1.2 christos if ( ! man_args(man, line, pos, buf, &p))
386 1.1 joerg break;
387 1.2 christos man_word_alloc(man, line, la, p);
388 1.1 joerg }
389 1.1 joerg
390 1.1 joerg /* Close out head and open body (unless MAN_SCOPE). */
391 1.1 joerg
392 1.2 christos if (man_macros[tok].flags & MAN_SCOPED) {
393 1.1 joerg /* If we're forcing scope (`TP'), keep it open. */
394 1.2 christos if (man_macros[tok].flags & MAN_FSCOPED) {
395 1.2 christos man->flags |= MAN_BLINE;
396 1.2 christos return;
397 1.2 christos } else if (n == man->last) {
398 1.2 christos man->flags |= MAN_BLINE;
399 1.2 christos return;
400 1.1 joerg }
401 1.1 joerg }
402 1.2 christos rew_scope(MAN_HEAD, man, tok);
403 1.2 christos man_body_alloc(man, line, ppos, tok);
404 1.1 joerg }
405 1.1 joerg
406 1.2 christos void
407 1.1 joerg in_line_eoln(MACRO_PROT_ARGS)
408 1.1 joerg {
409 1.2 christos int la;
410 1.1 joerg char *p;
411 1.1 joerg struct man_node *n;
412 1.1 joerg
413 1.2 christos man_elem_alloc(man, line, ppos, tok);
414 1.2 christos n = man->last;
415 1.1 joerg
416 1.1 joerg for (;;) {
417 1.2 christos if (buf[*pos] != '\0' && (tok == MAN_br ||
418 1.2 christos tok == MAN_fi || tok == MAN_nf)) {
419 1.2 christos mandoc_vmsg(MANDOCERR_ARG_SKIP,
420 1.2 christos man->parse, line, *pos, "%s %s",
421 1.2 christos man_macronames[tok], buf + *pos);
422 1.2 christos break;
423 1.2 christos }
424 1.2 christos if (buf[*pos] != '\0' && man->last != n &&
425 1.2 christos (tok == MAN_PD || tok == MAN_ft || tok == MAN_sp)) {
426 1.2 christos mandoc_vmsg(MANDOCERR_ARG_EXCESS,
427 1.2 christos man->parse, line, *pos, "%s ... %s",
428 1.2 christos man_macronames[tok], buf + *pos);
429 1.2 christos break;
430 1.2 christos }
431 1.1 joerg la = *pos;
432 1.2 christos if ( ! man_args(man, line, pos, buf, &p))
433 1.2 christos break;
434 1.2 christos if (man_macros[tok].flags & MAN_JOIN &&
435 1.2 christos man->last->type == MAN_TEXT)
436 1.2 christos man_word_append(man, p);
437 1.2 christos else
438 1.2 christos man_word_alloc(man, line, la, p);
439 1.2 christos }
440 1.1 joerg
441 1.2 christos /*
442 1.2 christos * Append MAN_EOS in case the last snipped argument
443 1.2 christos * ends with a dot, e.g. `.IR syslog (3).'
444 1.2 christos */
445 1.2 christos
446 1.2 christos if (n != man->last &&
447 1.2 christos mandoc_eos(man->last->string, strlen(man->last->string)))
448 1.2 christos man->last->flags |= MAN_EOS;
449 1.2 christos
450 1.2 christos /*
451 1.2 christos * If no arguments are specified and this is MAN_SCOPED (i.e.,
452 1.2 christos * next-line scoped), then set our mode to indicate that we're
453 1.2 christos * waiting for terms to load into our context.
454 1.2 christos */
455 1.1 joerg
456 1.2 christos if (n == man->last && man_macros[tok].flags & MAN_SCOPED) {
457 1.2 christos assert( ! (man_macros[tok].flags & MAN_NSCOPED));
458 1.2 christos man->flags |= MAN_ELINE;
459 1.2 christos return;
460 1.1 joerg }
461 1.1 joerg
462 1.2 christos assert(man->last->type != MAN_ROOT);
463 1.2 christos man->next = MAN_NEXT_SIBLING;
464 1.1 joerg
465 1.1 joerg /*
466 1.2 christos * Rewind our element scope. Note that when TH is pruned, we'll
467 1.2 christos * be back at the root, so make sure that we don't clobber as
468 1.2 christos * its sibling.
469 1.1 joerg */
470 1.1 joerg
471 1.2 christos for ( ; man->last; man->last = man->last->parent) {
472 1.2 christos if (man->last == n)
473 1.1 joerg break;
474 1.2 christos if (man->last->type == MAN_ROOT)
475 1.1 joerg break;
476 1.2 christos man_valid_post(man);
477 1.1 joerg }
478 1.1 joerg
479 1.2 christos assert(man->last);
480 1.1 joerg
481 1.1 joerg /*
482 1.2 christos * Same here regarding whether we're back at the root.
483 1.1 joerg */
484 1.1 joerg
485 1.2 christos if (man->last->type != MAN_ROOT)
486 1.2 christos man_valid_post(man);
487 1.2 christos }
488 1.2 christos
489 1.2 christos
490 1.2 christos void
491 1.2 christos man_macroend(struct man *man)
492 1.2 christos {
493 1.1 joerg
494 1.2 christos man_unscope(man, man->first);
495 1.1 joerg }
496 1.1 joerg
497 1.2 christos static int
498 1.2 christos man_args(struct man *man, int line, int *pos, char *buf, char **v)
499 1.1 joerg {
500 1.2 christos char *start;
501 1.1 joerg
502 1.2 christos assert(*pos);
503 1.2 christos *v = start = buf + *pos;
504 1.2 christos assert(' ' != *start);
505 1.1 joerg
506 1.2 christos if ('\0' == *start)
507 1.2 christos return(0);
508 1.1 joerg
509 1.2 christos *v = mandoc_getarg(man->parse, v, line, pos);
510 1.2 christos return(1);
511 1.1 joerg }
512