/* * 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 int rew_scope(enum man_type type, struct man *man, enum mant tok) { struct man_node *n; enum rew c; /* LINTED */ 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(1); if (REW_REWIND == c) break; } /* * Rewind until the current point. Warn if we're a roff * instruction that's mowing over explicit scopes. */ assert(n); return(man_unscope(man, n, MANDOCERR_MAX)); }
static int rew_sub(enum mdoc_type t, struct mdoc *mdoc, enum mdoct tok, int line, int ppos) { struct mdoc_node *n; n = mdoc->last; while (n) { switch (rew_dohalt(tok, t, n)) { case (REWIND_NONE): return(1); case (REWIND_THIS): n->lastline = line - (MDOC_NEWLINE & mdoc->flags && ! (MDOC_EXPLICIT & mdoc_macros[tok].flags)); break; case (REWIND_FORCE): mandoc_vmsg(MANDOCERR_SCOPEBROKEN, mdoc->parse, line, ppos, "%s breaks %s", mdoc_macronames[tok], mdoc_macronames[n->tok]); /* FALLTHROUGH */ case (REWIND_MORE): n->lastline = line - (MDOC_NEWLINE & mdoc->flags ? 1 : 0); n = n->parent; continue; case (REWIND_LATER): if (make_pending(n, tok, mdoc, line, ppos) || MDOC_BLOCK != t) return(1); /* FALLTHROUGH */ case (REWIND_ERROR): mdoc_pmsg(mdoc, line, ppos, MANDOCERR_NOSCOPE); return(1); } break; } assert(n); if ( ! rew_last(mdoc, n)) return(0); /* * The current block extends an enclosing block. * Now that the current block ends, close the enclosing block, too. */ while (NULL != (n = n->pending)) { if ( ! rew_last(mdoc, n)) return(0); if (MDOC_HEAD == n->type && ! mdoc_body_alloc(mdoc, n->line, n->pos, n->tok)) return(0); } return(1); }
static int rew_sub(enum mdoc_type t, struct mdoc *m, enum mdoct tok, int line, int ppos) { struct mdoc_node *n; enum rew c; /* LINTED */ for (n = m->last; n; n = n->parent) { c = rew_dohalt(tok, t, n); if (REWIND_HALT == c) { if (MDOC_BLOCK != t) return(1); if ( ! (MDOC_EXPLICIT & mdoc_macros[tok].flags)) return(1); /* FIXME: shouldn't raise an error */ mdoc_pmsg(m, line, ppos, MANDOCERR_SYNTNOSCOPE); return(0); } if (REWIND_REWIND == c) break; else if (rew_dobreak(tok, n)) continue; if ( ! swarn(m, t, line, ppos, n)) return(0); } assert(n); if ( ! rew_last(m, n)) return(0); #ifdef UGLY /* * The current block extends an enclosing block beyond a line * break. Now that the current block ends, close the enclosing * block, too. */ if (NULL != (n = n->pending)) { assert(MDOC_HEAD == n->type); if ( ! rew_last(m, n)) return(0); if ( ! mdoc_body_alloc(m, n->line, n->pos, n->tok)) return(0); } #endif return(1); }
/* * We are trying to close a block identified by tok, * but the child block *broken is still open. * Thus, postpone closing the tok block * until the rew_sub call closing *broken. */ static int make_pending(struct mdoc_node *broken, enum mdoct tok, struct mdoc *m, int line, int ppos) { struct mdoc_node *breaker; /* * Iterate backwards, searching for the block matching tok, * that is, the block breaking the *broken block. */ for (breaker = broken->parent; breaker; breaker = breaker->parent) { /* * If the *broken block had already been broken before * and we encounter its breaker, make the tok block * pending on the inner breaker. * Graphically, "[A breaker=[B broken=[C->B B] tok=A] C]" * becomes "[A broken=[B [C->B B] tok=A] C]" * and finally "[A [B->A [C->B B] A] C]". */ if (breaker == broken->pending) { broken = breaker; continue; } if (REWIND_THIS != rew_dohalt(tok, MDOC_BLOCK, breaker)) continue; if (MDOC_BODY == broken->type) broken = broken->parent; /* * Found the breaker. * If another, outer breaker is already pending on * the *broken block, we must not clobber the link * to the outer breaker, but make it pending on the * new, now inner breaker. * Graphically, "[A breaker=[B broken=[C->A A] tok=B] C]" * becomes "[A breaker=[B->A broken=[C A] tok=B] C]" * and finally "[A [B->A [C->B A] B] C]". */ if (broken->pending) { struct mdoc_node *taker; /* * If the breaker had also been broken before, * it cannot take on the outer breaker itself, * but must hand it on to its own breakers. * Graphically, this is the following situation: * "[A [B breaker=[C->B B] broken=[D->A A] tok=C] D]" * "[A taker=[B->A breaker=[C->B B] [D->C A] C] D]" */ taker = breaker; while (taker->pending) taker = taker->pending; taker->pending = broken->pending; } broken->pending = breaker; mandoc_vmsg(MANDOCERR_SCOPENEST, m->parse, line, ppos, "%s breaks %s", mdoc_macronames[tok], mdoc_macronames[broken->tok]); return(1); } /* * Found no matching block for tok. * Are you trying to close a block that is not open? */ return(0); }