+ /* We cannot progress beyond the root ever. */
+ if (MAN_ROOT == n->type)
+ return(REW_HALT);
+
+ assert(n->parent);
+
+ /* Normal nodes shouldn't go to the level of the root. */
+ if (MAN_ROOT == n->parent->type)
+ return(REW_REWIND);
+
+ /* Already-validated nodes should be closed out. */
+ if (MAN_VALID & n->flags)
+ return(REW_NOHALT);
+
+ /* First: rewind to ourselves. */
+ if (type == n->type && tok == n->tok) {
+ if (MAN_EXPLICIT & man_macros[n->tok].flags)
+ return(REW_HALT);
+ else
+ return(REW_REWIND);
+ }
+
+ /*
+ * Next follow the implicit scope-smashings as defined by man.7:
+ * section, sub-section, etc.
+ */
+
+ switch (tok) {
+ case MAN_SH:
+ break;
+ case MAN_SS:
+ /* Rewind to a section, if a block. */
+ if (REW_NOHALT != (c = rew_block(MAN_SH, type, n)))
+ return(c);
+ break;
+ case MAN_RS:
+ /* Preserve empty paragraphs before RS. */
+ if (0 == n->nchild && (MAN_P == n->tok ||
+ MAN_PP == n->tok || MAN_LP == n->tok))
+ return(REW_HALT);
+ /* Rewind to a subsection, if a block. */
+ if (REW_NOHALT != (c = rew_block(MAN_SS, type, n)))
+ return(c);
+ /* Rewind to a section, if a block. */
+ if (REW_NOHALT != (c = rew_block(MAN_SH, type, n)))
+ return(c);
+ break;
+ default:
+ /* Rewind to an offsetter, if a block. */
+ if (REW_NOHALT != (c = rew_block(MAN_RS, type, n)))
+ return(c);
+ /* Rewind to a subsection, if a block. */
+ if (REW_NOHALT != (c = rew_block(MAN_SS, type, n)))
+ return(c);
+ /* Rewind to a section, if a block. */
+ if (REW_NOHALT != (c = rew_block(MAN_SH, type, n)))
+ return(c);
+ break;
+ }
+
+ return(REW_NOHALT);
+}
+
+/*
+ * Rewinding entails ascending the parse tree until a coherent point,
+ * for example, the `SH' macro will close out any intervening `SS'
+ * scopes. When a scope is closed, it must be validated and actioned.
+ */
+static void
+rew_scope(enum man_type type, struct man *man, enum mant tok)
+{
+ struct man_node *n;
+ enum rew c;
+
+ for (n = man->last; n; n = n->parent) {
+ /*
+ * Whether we should stop immediately (REW_HALT), stop
+ * and rewind until this point (REW_REWIND), or keep
+ * rewinding (REW_NOHALT).
+ */
+ c = rew_dohalt(tok, type, n);
+ if (REW_HALT == c)
+ return;
+ if (REW_REWIND == c)
+ break;
+ }
+
+ /*
+ * Rewind until the current point. Warn if we're a roff
+ * instruction that's mowing over explicit scopes.
+ */
+
+ man_unscope(man, n);
+}
+
+
+/*
+ * Close out a generic explicit macro.
+ */
+void
+blk_close(MACRO_PROT_ARGS)
+{
+ enum mant ntok;
+ const struct man_node *nn;
+ char *p;
+ int nrew, target;
+
+ nrew = 1;
+ switch (tok) {
+ case MAN_RE:
+ ntok = MAN_RS;
+ if ( ! man_args(man, line, pos, buf, &p))
+ break;
+ for (nn = man->last->parent; nn; nn = nn->parent)
+ if (nn->tok == ntok && nn->type == MAN_BLOCK)
+ nrew++;
+ target = strtol(p, &p, 10);
+ if (*p != '\0')
+ mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse,
+ line, p - buf, "RE ... %s", p);
+ if (target == 0)
+ target = 1;
+ nrew -= target;
+ if (nrew < 1) {
+ mandoc_vmsg(MANDOCERR_RE_NOTOPEN, man->parse,
+ line, ppos, "RE %d", target);
+ return;
+ }
+ break;
+ case MAN_UE:
+ ntok = MAN_UR;
+ break;
+ default:
+ abort();
+ /* NOTREACHED */
+ }
+
+ for (nn = man->last->parent; nn; nn = nn->parent)
+ if (nn->tok == ntok && nn->type == MAN_BLOCK && ! --nrew)
+ break;
+
+ if (nn == NULL) {
+ mandoc_msg(MANDOCERR_BLK_NOTOPEN, man->parse,
+ line, ppos, man_macronames[tok]);
+ rew_scope(MAN_BLOCK, man, MAN_PP);
+ } else {
+ line = man->last->line;
+ ppos = man->last->pos;
+ ntok = man->last->tok;
+ man_unscope(man, nn);
+
+ /* Move a trailing paragraph behind the block. */
+
+ if (ntok == MAN_LP || ntok == MAN_PP || ntok == MAN_P) {
+ *pos = strlen(buf);
+ blk_imp(man, ntok, line, ppos, pos, buf);
+ }
+ }
+}
+
+void
+blk_exp(MACRO_PROT_ARGS)
+{
+ struct man_node *head;
+ char *p;
+ int la;
+
+ rew_scope(MAN_BLOCK, man, tok);
+ man_block_alloc(man, line, ppos, tok);
+ man_head_alloc(man, line, ppos, tok);
+ head = man->last;
+
+ la = *pos;
+ if (man_args(man, line, pos, buf, &p))
+ man_word_alloc(man, line, la, p);
+
+ if (buf[*pos] != '\0')
+ mandoc_vmsg(MANDOCERR_ARG_EXCESS,
+ man->parse, line, *pos, "%s ... %s",
+ man_macronames[tok], buf + *pos);
+
+ man_unscope(man, head);
+ man_body_alloc(man, line, ppos, tok);
+}
+
+/*
+ * Parse an implicit-block macro. These contain a MAN_HEAD and a
+ * MAN_BODY contained within a MAN_BLOCK. Rules for closing out other
+ * scopes, such as `SH' closing out an `SS', are defined in the rew
+ * routines.
+ */
+void
+blk_imp(MACRO_PROT_ARGS)
+{
+ int la;
+ char *p;
+ struct man_node *n;
+
+ rew_scope(MAN_BODY, man, tok);
+ rew_scope(MAN_BLOCK, man, tok);
+ man_block_alloc(man, line, ppos, tok);
+ man_head_alloc(man, line, ppos, tok);
+ n = man->last;
+
+ /* Add line arguments. */