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 void blk_part_exp(MACRO_PROT_ARGS) { int la, nl; enum margserr ac; struct roff_node *head; /* keep track of head */ char *p; nl = MDOC_NEWLINE & mdoc->flags; /* * The opening of an explicit macro having zero or more leading * punctuation nodes; a head with optional single element (the * case of `Eo'); and a body that may be empty. */ roff_block_alloc(mdoc, line, ppos, tok); head = NULL; for (;;) { la = *pos; ac = mdoc_args(mdoc, line, pos, buf, tok, &p); if (ac == ARGS_PUNCT || ac == ARGS_EOLN) break; /* Flush out leading punctuation. */ if (head == NULL && ac != ARGS_QWORD && mdoc_isdelim(p) == DELIM_OPEN) { dword(mdoc, line, la, p, DELIM_OPEN, 0); continue; } if (head == NULL) { head = roff_head_alloc(mdoc, line, ppos, tok); if (tok == MDOC_Eo) /* Not parsed. */ dword(mdoc, line, la, p, DELIM_MAX, 0); rew_last(mdoc, head); roff_body_alloc(mdoc, line, ppos, tok); if (tok == MDOC_Eo) continue; } if (macro_or_word(mdoc, tok, line, la, pos, buf, 1)) break; } /* Clean-up to leave in a consistent state. */ if (head == NULL) { rew_last(mdoc, roff_head_alloc(mdoc, line, ppos, tok)); roff_body_alloc(mdoc, line, ppos, tok); } if (nl) append_delims(mdoc, line, pos, buf); }
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); }
/* * Rewind up to a specific block, including all blocks that broke it. */ static void rew_pending(struct mdoc *mdoc, const struct mdoc_node *n) { for (;;) { rew_last(mdoc, n); switch (n->type) { case MDOC_HEAD: mdoc_body_alloc(mdoc, n->line, n->pos, n->tok); return; case MDOC_BLOCK: break; default: return; } if ( ! (n->flags & MDOC_BROKEN)) return; for (;;) { if ((n = n->parent) == NULL) return; if (n->type == MDOC_BLOCK || n->type == MDOC_HEAD) { if (n->flags & MDOC_ENDED) break; else return; } } } }
static void in_line_eoln(MACRO_PROT_ARGS) { struct roff_node *n; struct mdoc_arg *arg; if ((tok == MDOC_Pp || tok == MDOC_Lp) && ! (mdoc->flags & MDOC_SYNOPSIS)) { n = mdoc->last; if (mdoc->next == ROFF_NEXT_SIBLING) n = n->parent; if (n->tok == MDOC_Nm) rew_last(mdoc, n->parent); } if (buf[*pos] == '\0' && (tok == MDOC_Fd || mdoc_macronames[tok][0] == '%')) { mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, line, ppos, mdoc_macronames[tok]); return; } mdoc_argv(mdoc, line, tok, &arg, pos, buf); mdoc_elem_alloc(mdoc, line, ppos, tok, arg); if (parse_rest(mdoc, tok, line, pos, buf)) return; rew_elem(mdoc, tok); }
/* * Phrases occur within `Bl -column' entries, separated by `Ta' or tabs. * They're unusual because they're basically free-form text until a * macro is encountered. */ static void phrase_ta(MACRO_PROT_ARGS) { struct roff_node *body, *n; /* Make sure we are in a column list or ignore this macro. */ body = NULL; for (n = mdoc->last; n != NULL; n = n->parent) { if (n->flags & MDOC_ENDED) continue; if (n->tok == MDOC_It && n->type == ROFFT_BODY) body = n; if (n->tok == MDOC_Bl) break; } if (n == NULL || n->norm->Bl.type != LIST_column) { mandoc_msg(MANDOCERR_TA_STRAY, mdoc->parse, line, ppos, "Ta"); return; } /* Advance to the next column. */ rew_last(mdoc, body); roff_body_alloc(mdoc, line, ppos, MDOC_It); parse_rest(mdoc, TOKEN_NONE, line, pos, buf); }
static void rew_elem(struct mdoc *mdoc, enum mdoct tok) { struct mdoc_node *n; n = mdoc->last; if (MDOC_ELEM != n->type) n = n->parent; assert(MDOC_ELEM == n->type); assert(tok == n->tok); rew_last(mdoc, n); }
static void rew_elem(struct roff_man *mdoc, int tok) { struct roff_node *n; n = mdoc->last; if (n->type != ROFFT_ELEM) n = n->parent; assert(n->type == ROFFT_ELEM); assert(tok == n->tok); rew_last(mdoc, n); }
/* * This is called at the end of parsing. It must traverse up the tree, * closing out open [implicit] scopes. Obviously, open explicit scopes * are errors. */ int mdoc_macroend(struct mdoc *m) { struct mdoc_node *n; /* Scan for open explicit scopes. */ n = MDOC_VALID & m->last->flags ? m->last->parent : m->last; for ( ; n; n = n->parent) if (MDOC_BLOCK == n->type && MDOC_EXPLICIT & mdoc_macros[n->tok].flags) mdoc_nmsg(m, n, MANDOCERR_SCOPEEXIT); /* Rewind to the first. */ return(rew_last(m, m->first)); }
/* * This is called at the end of parsing. It must traverse up the tree, * closing out open [implicit] scopes. Obviously, open explicit scopes * are errors. */ void mdoc_macroend(struct mdoc *mdoc) { struct mdoc_node *n; /* Scan for open explicit scopes. */ n = mdoc->last->flags & MDOC_VALID ? mdoc->last->parent : mdoc->last; for ( ; n; n = n->parent) if (n->type == MDOC_BLOCK && mdoc_macros[n->tok].flags & MDOC_EXPLICIT) mandoc_msg(MANDOCERR_BLK_NOEND, mdoc->parse, n->line, n->pos, mdoc_macronames[n->tok]); /* Rewind to the first. */ rew_last(mdoc, mdoc->first); }
/* * This is called at the end of parsing. It must traverse up the tree, * closing out open [implicit] scopes. Obviously, open explicit scopes * are errors. */ int mdoc_macroend(struct mdoc *m) { struct mdoc_node *n; /* Scan for open explicit scopes. */ n = MDOC_VALID & m->last->flags ? m->last->parent : m->last; for ( ; n; n = n->parent) { if (MDOC_BLOCK != n->type) continue; if ( ! (MDOC_EXPLICIT & mdoc_macros[n->tok].flags)) continue; mdoc_nmsg(m, n, MANDOCERR_SYNTSCOPE); return(0); } /* Rewind to the first. */ return(rew_last(m, m->first)); }
/* * Rewind up to a specific block, including all blocks that broke it. */ static void rew_pending(struct roff_man *mdoc, const struct roff_node *n) { for (;;) { rew_last(mdoc, n); if (mdoc->last == n) { switch (n->type) { case ROFFT_HEAD: roff_body_alloc(mdoc, n->line, n->pos, n->tok); return; case ROFFT_BLOCK: break; default: return; } if ( ! (n->flags & MDOC_BROKEN)) return; } else n = mdoc->last; for (;;) { if ((n = n->parent) == NULL) return; if (n->type == ROFFT_BLOCK || n->type == ROFFT_HEAD) { if (n->flags & MDOC_ENDED) break; else return; } } } }
/* * Close out block partial/full explicit. */ static void blk_exp_close(MACRO_PROT_ARGS) { struct roff_node *body; /* Our own body. */ struct roff_node *endbody; /* Our own end marker. */ struct roff_node *itblk; /* An It block starting later. */ struct roff_node *later; /* A sub-block starting later. */ struct roff_node *n; /* Search back to our block. */ struct roff_node *target; /* For find_pending(). */ int j, lastarg, maxargs, nl, pending; enum margserr ac; int atok, ntok; char *p; nl = MDOC_NEWLINE & mdoc->flags; switch (tok) { case MDOC_Ec: maxargs = 1; break; case MDOC_Ek: mdoc->flags &= ~MDOC_KEEP; /* FALLTHROUGH */ default: maxargs = 0; break; } /* * Search backwards for beginnings of blocks, * both of our own and of pending sub-blocks. */ atok = rew_alt(tok); body = endbody = itblk = later = NULL; for (n = mdoc->last; n; n = n->parent) { if (n->flags & MDOC_ENDED) { if ( ! (n->flags & MDOC_VALID)) n->flags |= MDOC_BROKEN; continue; } /* Remember the start of our own body. */ if (n->type == ROFFT_BODY && atok == n->tok) { if (n->end == ENDBODY_NOT) body = n; continue; } if (n->type != ROFFT_BLOCK || n->tok == MDOC_Nm) continue; if (n->tok == MDOC_It) { itblk = n; continue; } if (atok == n->tok) { assert(body); /* * Found the start of our own block. * When there is no pending sub block, * just proceed to closing out. */ if (later == NULL || (tok == MDOC_El && itblk == NULL)) break; /* * When there is a pending sub block, postpone * closing out the current block until the * rew_pending() closing out the sub-block. * Mark the place where the formatting - but not * the scope - of the current block ends. */ mandoc_vmsg(MANDOCERR_BLK_NEST, mdoc->parse, line, ppos, "%s breaks %s", mdoc_macronames[atok], mdoc_macronames[later->tok]); endbody = mdoc_endbody_alloc(mdoc, line, ppos, atok, body, ENDBODY_SPACE); if (tok == MDOC_El) itblk->flags |= MDOC_ENDED | MDOC_BROKEN; /* * If a block closing macro taking arguments * breaks another block, put the arguments * into the end marker. */ if (maxargs) mdoc->next = ROFF_NEXT_CHILD; break; } /* Explicit blocks close out description lines. */ if (n->tok == MDOC_Nd) { rew_last(mdoc, n); continue; } /* Breaking an open sub block. */ n->flags |= MDOC_BROKEN; if (later == NULL) later = n; } if (body == NULL) { mandoc_msg(MANDOCERR_BLK_NOTOPEN, mdoc->parse, line, ppos, mdoc_macronames[tok]); if (later != NULL) later->flags &= ~MDOC_BROKEN; if (maxargs && endbody == NULL) { /* * Stray .Ec without previous .Eo: * Break the output line, keep the arguments. */ roff_elem_alloc(mdoc, line, ppos, MDOC_br); rew_elem(mdoc, MDOC_br); } } else if (endbody == NULL) { rew_last(mdoc, body); if (maxargs) mdoc_tail_alloc(mdoc, line, ppos, atok); } if ( ! (mdoc_macros[tok].flags & MDOC_PARSED)) { if (buf[*pos] != '\0') mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse, line, ppos, "%s %s", mdoc_macronames[tok], buf + *pos); if (endbody == NULL && n != NULL) rew_pending(mdoc, n); return; } if (endbody != NULL) n = endbody; ntok = TOKEN_NONE; for (j = 0; ; j++) { lastarg = *pos; if (j == maxargs && n != NULL) rew_last(mdoc, n); ac = mdoc_args(mdoc, line, pos, buf, tok, &p); if (ac == ARGS_PUNCT || ac == ARGS_EOLN) break; ntok = ac == ARGS_QWORD ? TOKEN_NONE : lookup(mdoc, tok, line, lastarg, p); if (ntok == TOKEN_NONE) { dword(mdoc, line, lastarg, p, DELIM_MAX, MDOC_JOIN & mdoc_macros[tok].flags); continue; } if (n != NULL) rew_last(mdoc, n); mdoc->flags &= ~MDOC_NEWLINE; mdoc_macro(mdoc, ntok, line, lastarg, pos, buf); break; } if (n != NULL) { if (ntok != TOKEN_NONE && n->flags & MDOC_BROKEN) { target = n; do target = target->parent; while ( ! (target->flags & MDOC_ENDED)); pending = find_pending(mdoc, ntok, line, ppos, target); } else pending = 0; if ( ! pending) rew_pending(mdoc, n); } if (nl) append_delims(mdoc, line, pos, buf); }
static void blk_part_imp(MACRO_PROT_ARGS) { int la, nl; enum margserr ac; char *p; struct mdoc_node *blk; /* saved block context */ struct mdoc_node *body; /* saved body context */ struct mdoc_node *n; nl = MDOC_NEWLINE & mdoc->flags; /* * A macro that spans to the end of the line. This is generally * (but not necessarily) called as the first macro. The block * has a head as the immediate child, which is always empty, * followed by zero or more opening punctuation nodes, then the * body (which may be empty, depending on the macro), then zero * or more closing punctuation nodes. */ blk = mdoc_block_alloc(mdoc, line, ppos, tok, NULL); rew_last(mdoc, mdoc_head_alloc(mdoc, line, ppos, tok)); /* * Open the body scope "on-demand", that is, after we've * processed all our the leading delimiters (open parenthesis, * etc.). */ for (body = NULL; ; ) { la = *pos; ac = mdoc_args(mdoc, line, pos, buf, tok, &p); if (ac == ARGS_EOLN || ac == ARGS_PUNCT) break; if (body == NULL && ac != ARGS_QWORD && mdoc_isdelim(p) == DELIM_OPEN) { dword(mdoc, line, la, p, DELIM_OPEN, 0); continue; } if (body == NULL) body = mdoc_body_alloc(mdoc, line, ppos, tok); if (macro_or_word(mdoc, tok, line, la, pos, buf, 1)) break; } if (body == NULL) body = mdoc_body_alloc(mdoc, line, ppos, tok); /* * If there is an open sub-block requiring explicit close-out, * postpone closing out the current block until the * rew_pending() call closing out the sub-block. */ for (n = mdoc->last; n && n != body && n != blk->parent; n = n->parent) { if (n->flags & MDOC_ENDED) { if ( ! (n->flags & MDOC_VALID)) n->flags |= MDOC_BROKEN; continue; } if (n->type == MDOC_BLOCK && mdoc_macros[n->tok].flags & MDOC_EXPLICIT) { n->flags |= MDOC_BROKEN; if ( ! (body->flags & MDOC_ENDED)) { mandoc_vmsg(MANDOCERR_BLK_NEST, mdoc->parse, line, ppos, "%s breaks %s", mdoc_macronames[tok], mdoc_macronames[n->tok]); mdoc_endbody_alloc(mdoc, line, ppos, tok, body, ENDBODY_NOSPACE); } } } assert(n == body); if (body->flags & MDOC_ENDED) return; rew_last(mdoc, body); if (nl) append_delims(mdoc, line, pos, buf); rew_pending(mdoc, blk); /* Move trailing .Ns out of scope. */ for (n = body->child; n && n->next; n = n->next) /* Do nothing. */ ; if (n && n->tok == MDOC_Ns) mdoc_node_relink(mdoc, n); }
static void blk_full(MACRO_PROT_ARGS) { int la, nl, parsed; struct mdoc_arg *arg; struct mdoc_node *blk; /* Our own or a broken block. */ struct mdoc_node *head; /* Our own head. */ struct mdoc_node *body; /* Our own body. */ struct mdoc_node *n; enum margserr ac, lac; char *p; nl = MDOC_NEWLINE & mdoc->flags; if (buf[*pos] == '\0' && (tok == MDOC_Sh || tok == MDOC_Ss)) { mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, line, ppos, mdoc_macronames[tok]); return; } if ( ! (mdoc_macros[tok].flags & MDOC_EXPLICIT)) { /* Here, tok is one of Sh Ss Nm Nd It. */ blk = NULL; for (n = mdoc->last; n != NULL; n = n->parent) { if (n->flags & MDOC_ENDED) { if ( ! (n->flags & MDOC_VALID)) n->flags |= MDOC_BROKEN; continue; } if (n->type != MDOC_BLOCK) continue; if (tok == MDOC_It && n->tok == MDOC_Bl) { if (blk != NULL) { mandoc_vmsg(MANDOCERR_BLK_BROKEN, mdoc->parse, line, ppos, "It breaks %s", mdoc_macronames[blk->tok]); rew_pending(mdoc, blk); } break; } if (mdoc_macros[n->tok].flags & MDOC_EXPLICIT) { switch (tok) { case MDOC_Sh: /* FALLTHROUGH */ case MDOC_Ss: mandoc_vmsg(MANDOCERR_BLK_BROKEN, mdoc->parse, line, ppos, "%s breaks %s", mdoc_macronames[tok], mdoc_macronames[n->tok]); rew_pending(mdoc, n); n = mdoc->last; continue; case MDOC_It: /* Delay in case it's astray. */ blk = n; continue; default: break; } break; } /* Here, n is one of Sh Ss Nm Nd It. */ if (tok != MDOC_Sh && (n->tok == MDOC_Sh || (tok != MDOC_Ss && (n->tok == MDOC_Ss || (tok != MDOC_It && n->tok == MDOC_It))))) break; /* Item breaking an explicit block. */ if (blk != NULL) { mandoc_vmsg(MANDOCERR_BLK_BROKEN, mdoc->parse, line, ppos, "It breaks %s", mdoc_macronames[blk->tok]); rew_pending(mdoc, blk); blk = NULL; } /* Close out prior implicit scopes. */ rew_last(mdoc, n); } /* Skip items outside lists. */ if (tok == MDOC_It && (n == NULL || n->tok != MDOC_Bl)) { mandoc_vmsg(MANDOCERR_IT_STRAY, mdoc->parse, line, ppos, "It %s", buf + *pos); mdoc_elem_alloc(mdoc, line, ppos, MDOC_br, NULL); rew_elem(mdoc, MDOC_br); return; } } /* * This routine accommodates implicitly- and explicitly-scoped * macro openings. Implicit ones first close out prior scope * (seen above). Delay opening the head until necessary to * allow leading punctuation to print. Special consideration * for `It -column', which has phrase-part syntax instead of * regular child nodes. */ mdoc_argv(mdoc, line, tok, &arg, pos, buf); blk = mdoc_block_alloc(mdoc, line, ppos, tok, arg); head = body = NULL; /* * Exception: Heads of `It' macros in `-diag' lists are not * parsed, even though `It' macros in general are parsed. */ parsed = tok != MDOC_It || mdoc->last->parent->tok != MDOC_Bl || mdoc->last->parent->norm->Bl.type != LIST_diag; /* * The `Nd' macro has all arguments in its body: it's a hybrid * of block partial-explicit and full-implicit. Stupid. */ if (tok == MDOC_Nd) { head = mdoc_head_alloc(mdoc, line, ppos, tok); rew_last(mdoc, head); body = mdoc_body_alloc(mdoc, line, ppos, tok); } if (tok == MDOC_Bk) mdoc->flags |= MDOC_KEEP; ac = ARGS_PEND; for (;;) { la = *pos; lac = ac; ac = mdoc_args(mdoc, line, pos, buf, tok, &p); if (ac == ARGS_EOLN) { if (lac != ARGS_PPHRASE && lac != ARGS_PHRASE) break; /* * This is necessary: if the last token on a * line is a `Ta' or tab, then we'll get * ARGS_EOLN, so we must be smart enough to * reopen our scope if the last parse was a * phrase or partial phrase. */ if (body != NULL) rew_last(mdoc, body); body = mdoc_body_alloc(mdoc, line, ppos, tok); break; } if (tok == MDOC_Bd || tok == MDOC_Bk) { mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, line, la, "%s ... %s", mdoc_macronames[tok], buf + la); break; } if (tok == MDOC_Rs) { mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse, line, la, "Rs %s", buf + la); break; } if (ac == ARGS_PUNCT) break; /* * Emit leading punctuation (i.e., punctuation before * the MDOC_HEAD) for non-phrase types. */ if (head == NULL && ac != ARGS_PEND && ac != ARGS_PHRASE && ac != ARGS_PPHRASE && ac != ARGS_QWORD && mdoc_isdelim(p) == DELIM_OPEN) { dword(mdoc, line, la, p, DELIM_OPEN, 0); continue; } /* Open a head if one hasn't been opened. */ if (head == NULL) head = mdoc_head_alloc(mdoc, line, ppos, tok); if (ac == ARGS_PHRASE || ac == ARGS_PEND || ac == ARGS_PPHRASE) { /* * If we haven't opened a body yet, rewind the * head; if we have, rewind that instead. */ rew_last(mdoc, body == NULL ? head : body); body = mdoc_body_alloc(mdoc, line, ppos, tok); /* * Process phrases: set whether we're in a * partial-phrase (this effects line handling) * then call down into the phrase parser. */ if (ac == ARGS_PPHRASE) mdoc->flags |= MDOC_PPHRASE; if (ac == ARGS_PEND && lac == ARGS_PPHRASE) mdoc->flags |= MDOC_PPHRASE; parse_rest(mdoc, MDOC_MAX, line, &la, buf); mdoc->flags &= ~MDOC_PPHRASE; continue; } if (macro_or_word(mdoc, tok, line, la, pos, buf, parsed)) break; } if (blk->flags & MDOC_VALID) return; if (head == NULL) head = mdoc_head_alloc(mdoc, line, ppos, tok); if (nl && tok != MDOC_Bd && tok != MDOC_Bl && tok != MDOC_Rs) append_delims(mdoc, line, pos, buf); if (body != NULL) goto out; /* * If there is an open (i.e., unvalidated) sub-block requiring * explicit close-out, postpone switching the current block from * head to body until the rew_pending() call closing out that * sub-block. */ for (n = mdoc->last; n && n != head; n = n->parent) { if (n->flags & MDOC_ENDED) { if ( ! (n->flags & MDOC_VALID)) n->flags |= MDOC_BROKEN; continue; } if (n->type == MDOC_BLOCK && mdoc_macros[n->tok].flags & MDOC_EXPLICIT) { n->flags = MDOC_BROKEN; head->flags = MDOC_ENDED; } } if (head->flags & MDOC_ENDED) return; /* Close out scopes to remain in a consistent state. */ rew_last(mdoc, head); body = mdoc_body_alloc(mdoc, line, ppos, tok); out: if (mdoc->flags & MDOC_FREECOL) { rew_last(mdoc, body); rew_last(mdoc, blk); mdoc->flags &= ~MDOC_FREECOL; } }
static void blk_part_imp(MACRO_PROT_ARGS) { int la, nl; enum margserr ac; char *p; struct roff_node *blk; /* saved block context */ struct roff_node *body; /* saved body context */ struct roff_node *n; nl = MDOC_NEWLINE & mdoc->flags; /* * A macro that spans to the end of the line. This is generally * (but not necessarily) called as the first macro. The block * has a head as the immediate child, which is always empty, * followed by zero or more opening punctuation nodes, then the * body (which may be empty, depending on the macro), then zero * or more closing punctuation nodes. */ blk = mdoc_block_alloc(mdoc, line, ppos, tok, NULL); rew_last(mdoc, roff_head_alloc(mdoc, line, ppos, tok)); /* * Open the body scope "on-demand", that is, after we've * processed all our the leading delimiters (open parenthesis, * etc.). */ for (body = NULL; ; ) { la = *pos; ac = mdoc_args(mdoc, line, pos, buf, tok, &p); if (ac == ARGS_EOLN || ac == ARGS_PUNCT) break; if (body == NULL && ac != ARGS_QWORD && mdoc_isdelim(p) == DELIM_OPEN) { dword(mdoc, line, la, p, DELIM_OPEN, 0); continue; } if (body == NULL) body = roff_body_alloc(mdoc, line, ppos, tok); if (macro_or_word(mdoc, tok, line, la, pos, buf, 1)) break; } if (body == NULL) body = roff_body_alloc(mdoc, line, ppos, tok); if (find_pending(mdoc, tok, line, ppos, body)) return; rew_last(mdoc, body); if (nl) append_delims(mdoc, line, pos, buf); rew_pending(mdoc, blk); /* Move trailing .Ns out of scope. */ for (n = body->child; n && n->next; n = n->next) /* Do nothing. */ ; if (n && n->tok == MDOC_Ns) mdoc_node_relink(mdoc, n); }
static int blk_part_imp(MACRO_PROT_ARGS) { int la, nl; enum mdoct ntok; enum margserr ac; char *p; struct mdoc_node *blk; /* saved block context */ struct mdoc_node *body; /* saved body context */ struct mdoc_node *n; nl = MDOC_NEWLINE & m->flags; /* * A macro that spans to the end of the line. This is generally * (but not necessarily) called as the first macro. The block * has a head as the immediate child, which is always empty, * followed by zero or more opening punctuation nodes, then the * body (which may be empty, depending on the macro), then zero * or more closing punctuation nodes. */ if ( ! mdoc_block_alloc(m, line, ppos, tok, NULL)) return(0); blk = m->last; if ( ! mdoc_head_alloc(m, line, ppos, tok)) return(0); if ( ! rew_sub(MDOC_HEAD, m, tok, line, ppos)) return(0); /* * Open the body scope "on-demand", that is, after we've * processed all our the leading delimiters (open parenthesis, * etc.). */ for (body = NULL; ; ) { la = *pos; ac = mdoc_args(m, line, pos, buf, tok, &p); if (ARGS_ERROR == ac) return(0); if (ARGS_EOLN == ac) break; if (ARGS_PUNCT == ac) break; if (NULL == body && ARGS_QWORD != ac && DELIM_OPEN == mdoc_isdelim(p)) { if ( ! mdoc_word_alloc(m, line, la, p)) return(0); continue; } if (NULL == body) { if ( ! mdoc_body_alloc(m, line, ppos, tok)) return(0); body = m->last; } ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p); if (MDOC_MAX == ntok) { if ( ! mdoc_word_alloc(m, line, la, p)) return(0); continue; } if ( ! mdoc_macro(m, ntok, line, la, pos, buf)) return(0); break; } /* Clean-ups to leave in a consistent state. */ if (NULL == body) { if ( ! mdoc_body_alloc(m, line, ppos, tok)) return(0); body = m->last; } for (n = body->child; n && n->next; n = n->next) /* Do nothing. */ ; /* * End of sentence spacing: if the last node is a text node and * has a trailing period, then mark it as being end-of-sentence. */ if (n && MDOC_TEXT == n->type && n->string) if (mandoc_eos(n->string, strlen(n->string))) n->flags |= MDOC_EOS; /* Up-propogate the end-of-space flag. */ if (n && (MDOC_EOS & n->flags)) { body->flags |= MDOC_EOS; body->parent->flags |= MDOC_EOS; } /* * If we can't rewind to our body, then our scope has already * been closed by another macro (like `Oc' closing `Op'). This * is ugly behaviour nodding its head to OpenBSD's overwhelming * crufty use of `Op' breakage. * * FIXME - this should be ifdef'd OpenBSD? */ for (n = m->last; n; n = n->parent) if (body == n) break; if (NULL == n && ! mdoc_nmsg(m, body, MANDOCERR_SCOPE)) return(0); if (n && ! rew_last(m, body)) return(0); /* Standard appending of delimiters. */ if (nl && ! append_delims(m, line, pos, buf)) return(0); /* Rewind scope, if applicable. */ if (n && ! rew_last(m, blk)) return(0); return(1); }
static void in_line(MACRO_PROT_ARGS) { int la, scope, cnt, firstarg, mayopen, nc, nl; int ntok; enum margserr ac; enum mdelim d; struct mdoc_arg *arg; char *p; nl = MDOC_NEWLINE & mdoc->flags; /* * Whether we allow ignored elements (those without content, * usually because of reserved words) to squeak by. */ switch (tok) { case MDOC_An: case MDOC_Ar: case MDOC_Fl: case MDOC_Mt: case MDOC_Nm: case MDOC_Pa: nc = 1; break; default: nc = 0; break; } mdoc_argv(mdoc, line, tok, &arg, pos, buf); d = DELIM_NONE; firstarg = 1; mayopen = 1; for (cnt = scope = 0;; ) { la = *pos; ac = mdoc_args(mdoc, line, pos, buf, tok, &p); /* * At the end of a macro line, * opening delimiters do not suppress spacing. */ if (ac == ARGS_EOLN) { if (d == DELIM_OPEN) mdoc->last->flags &= ~MDOC_DELIMO; break; } /* * The rest of the macro line is only punctuation, * to be handled by append_delims(). * If there were no other arguments, * do not allow the first one to suppress spacing, * even if it turns out to be a closing one. */ if (ac == ARGS_PUNCT) { if (cnt == 0 && (nc == 0 || tok == MDOC_An)) mdoc->flags |= MDOC_NODELIMC; break; } ntok = (ac == ARGS_QWORD || (tok == MDOC_Fn && !cnt)) ? TOKEN_NONE : lookup(mdoc, tok, line, la, p); /* * In this case, we've located a submacro and must * execute it. Close out scope, if open. If no * elements have been generated, either create one (nc) * or raise a warning. */ if (ntok != TOKEN_NONE) { if (scope) rew_elem(mdoc, tok); if (nc && ! cnt) { mdoc_elem_alloc(mdoc, line, ppos, tok, arg); rew_last(mdoc, mdoc->last); } else if ( ! nc && ! cnt) { mdoc_argv_free(arg); mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, line, ppos, mdoc_macronames[tok]); } mdoc_macro(mdoc, ntok, line, la, pos, buf); if (nl) append_delims(mdoc, line, pos, buf); return; } /* * Non-quote-enclosed punctuation. Set up our scope, if * a word; rewind the scope, if a delimiter; then append * the word. */ d = ac == ARGS_QWORD ? DELIM_NONE : mdoc_isdelim(p); if (DELIM_NONE != d) { /* * If we encounter closing punctuation, no word * has been emitted, no scope is open, and we're * allowed to have an empty element, then start * a new scope. */ if ((d == DELIM_CLOSE || (d == DELIM_MIDDLE && tok == MDOC_Fl)) && !cnt && !scope && nc && mayopen) { mdoc_elem_alloc(mdoc, line, ppos, tok, arg); scope = 1; cnt++; if (tok == MDOC_Nm) mayopen = 0; } /* * Close out our scope, if one is open, before * any punctuation. */ if (scope) rew_elem(mdoc, tok); scope = 0; if (tok == MDOC_Fn) mayopen = 0; } else if (mayopen && !scope) { mdoc_elem_alloc(mdoc, line, ppos, tok, arg); scope = 1; cnt++; } dword(mdoc, line, la, p, d, MDOC_JOIN & mdoc_macros[tok].flags); /* * If the first argument is a closing delimiter, * do not suppress spacing before it. */ if (firstarg && d == DELIM_CLOSE && !nc) mdoc->last->flags &= ~MDOC_DELIMC; firstarg = 0; /* * `Fl' macros have their scope re-opened with each new * word so that the `-' can be added to each one without * having to parse out spaces. */ if (scope && tok == MDOC_Fl) { rew_elem(mdoc, tok); scope = 0; } } if (scope) rew_elem(mdoc, tok); /* * If no elements have been collected and we're allowed to have * empties (nc), open a scope and close it out. Otherwise, * raise a warning. */ if ( ! cnt) { if (nc) { mdoc_elem_alloc(mdoc, line, ppos, tok, arg); rew_last(mdoc, mdoc->last); } else { mdoc_argv_free(arg); mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, line, ppos, mdoc_macronames[tok]); } } if (nl) append_delims(mdoc, line, pos, buf); }
static void blk_full(MACRO_PROT_ARGS) { int la, nl, parsed; struct mdoc_arg *arg; struct roff_node *blk; /* Our own or a broken block. */ struct roff_node *head; /* Our own head. */ struct roff_node *body; /* Our own body. */ struct roff_node *n; enum margserr ac, lac; char *p; nl = MDOC_NEWLINE & mdoc->flags; if (buf[*pos] == '\0' && (tok == MDOC_Sh || tok == MDOC_Ss)) { mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, line, ppos, mdoc_macronames[tok]); return; } if ( ! (mdoc_macros[tok].flags & MDOC_EXPLICIT)) { /* Here, tok is one of Sh Ss Nm Nd It. */ blk = NULL; for (n = mdoc->last; n != NULL; n = n->parent) { if (n->flags & MDOC_ENDED) { if ( ! (n->flags & MDOC_VALID)) n->flags |= MDOC_BROKEN; continue; } if (n->type != ROFFT_BLOCK) continue; if (tok == MDOC_It && n->tok == MDOC_Bl) { if (blk != NULL) { mandoc_vmsg(MANDOCERR_BLK_BROKEN, mdoc->parse, line, ppos, "It breaks %s", mdoc_macronames[blk->tok]); rew_pending(mdoc, blk); } break; } if (mdoc_macros[n->tok].flags & MDOC_EXPLICIT) { switch (tok) { case MDOC_Sh: case MDOC_Ss: mandoc_vmsg(MANDOCERR_BLK_BROKEN, mdoc->parse, line, ppos, "%s breaks %s", mdoc_macronames[tok], mdoc_macronames[n->tok]); rew_pending(mdoc, n); n = mdoc->last; continue; case MDOC_It: /* Delay in case it's astray. */ blk = n; continue; default: break; } break; } /* Here, n is one of Sh Ss Nm Nd It. */ if (tok != MDOC_Sh && (n->tok == MDOC_Sh || (tok != MDOC_Ss && (n->tok == MDOC_Ss || (tok != MDOC_It && n->tok == MDOC_It))))) break; /* Item breaking an explicit block. */ if (blk != NULL) { mandoc_vmsg(MANDOCERR_BLK_BROKEN, mdoc->parse, line, ppos, "It breaks %s", mdoc_macronames[blk->tok]); rew_pending(mdoc, blk); blk = NULL; } /* Close out prior implicit scopes. */ rew_last(mdoc, n); } /* Skip items outside lists. */ if (tok == MDOC_It && (n == NULL || n->tok != MDOC_Bl)) { mandoc_vmsg(MANDOCERR_IT_STRAY, mdoc->parse, line, ppos, "It %s", buf + *pos); roff_elem_alloc(mdoc, line, ppos, MDOC_br); rew_elem(mdoc, MDOC_br); return; } } /* * This routine accommodates implicitly- and explicitly-scoped * macro openings. Implicit ones first close out prior scope * (seen above). Delay opening the head until necessary to * allow leading punctuation to print. Special consideration * for `It -column', which has phrase-part syntax instead of * regular child nodes. */ mdoc_argv(mdoc, line, tok, &arg, pos, buf); blk = mdoc_block_alloc(mdoc, line, ppos, tok, arg); head = body = NULL; /* * Exception: Heads of `It' macros in `-diag' lists are not * parsed, even though `It' macros in general are parsed. */ parsed = tok != MDOC_It || mdoc->last->parent->tok != MDOC_Bl || mdoc->last->parent->norm->Bl.type != LIST_diag; /* * The `Nd' macro has all arguments in its body: it's a hybrid * of block partial-explicit and full-implicit. Stupid. */ if (tok == MDOC_Nd) { head = roff_head_alloc(mdoc, line, ppos, tok); rew_last(mdoc, head); body = roff_body_alloc(mdoc, line, ppos, tok); } if (tok == MDOC_Bk) mdoc->flags |= MDOC_KEEP; ac = ARGS_EOLN; for (;;) { /* * If we are right after a tab character, * do not parse the first word for macros. */ if (mdoc->flags & MDOC_PHRASEQN) { mdoc->flags &= ~MDOC_PHRASEQN; mdoc->flags |= MDOC_PHRASEQF; } la = *pos; lac = ac; ac = mdoc_args(mdoc, line, pos, buf, tok, &p); if (ac == ARGS_EOLN) { if (lac != ARGS_PHRASE || ! (mdoc->flags & MDOC_PHRASEQF)) break; /* * This line ends in a tab; start the next * column now, with a leading blank. */ if (body != NULL) rew_last(mdoc, body); body = roff_body_alloc(mdoc, line, ppos, tok); roff_word_alloc(mdoc, line, ppos, "\\&"); break; } if (tok == MDOC_Bd || tok == MDOC_Bk) { mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, line, la, "%s ... %s", mdoc_macronames[tok], buf + la); break; } if (tok == MDOC_Rs) { mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse, line, la, "Rs %s", buf + la); break; } if (ac == ARGS_PUNCT) break; /* * Emit leading punctuation (i.e., punctuation before * the ROFFT_HEAD) for non-phrase types. */ if (head == NULL && ac != ARGS_PHRASE && ac != ARGS_QWORD && mdoc_isdelim(p) == DELIM_OPEN) { dword(mdoc, line, la, p, DELIM_OPEN, 0); continue; } /* Open a head if one hasn't been opened. */ if (head == NULL) head = roff_head_alloc(mdoc, line, ppos, tok); if (ac == ARGS_PHRASE) { /* * If we haven't opened a body yet, rewind the * head; if we have, rewind that instead. */ rew_last(mdoc, body == NULL ? head : body); body = roff_body_alloc(mdoc, line, ppos, tok); /* Process to the tab or to the end of the line. */ mdoc->flags |= MDOC_PHRASE; parse_rest(mdoc, TOKEN_NONE, line, &la, buf); mdoc->flags &= ~MDOC_PHRASE; /* There may have been `Ta' macros. */ while (body->next != NULL) body = body->next; continue; } if (macro_or_word(mdoc, tok, line, la, pos, buf, parsed)) break; } if (blk->flags & MDOC_VALID) return; if (head == NULL) head = roff_head_alloc(mdoc, line, ppos, tok); if (nl && tok != MDOC_Bd && tok != MDOC_Bl && tok != MDOC_Rs) append_delims(mdoc, line, pos, buf); if (body != NULL) goto out; if (find_pending(mdoc, tok, line, ppos, head)) return; /* Close out scopes to remain in a consistent state. */ rew_last(mdoc, head); body = roff_body_alloc(mdoc, line, ppos, tok); out: if (mdoc->flags & MDOC_FREECOL) { rew_last(mdoc, body); rew_last(mdoc, blk); mdoc->flags &= ~MDOC_FREECOL; } }
static int in_line(MACRO_PROT_ARGS) { int la, scope, cnt, nc, nl; enum margverr av; enum mdoct ntok; enum margserr ac; enum mdelim d; struct mdoc_arg *arg; char *p; nl = MDOC_NEWLINE & m->flags; /* * Whether we allow ignored elements (those without content, * usually because of reserved words) to squeak by. */ switch (tok) { case (MDOC_An): /* FALLTHROUGH */ case (MDOC_Ar): /* FALLTHROUGH */ case (MDOC_Fl): /* FALLTHROUGH */ case (MDOC_Mt): /* FALLTHROUGH */ case (MDOC_Nm): /* FALLTHROUGH */ case (MDOC_Pa): nc = 1; break; default: nc = 0; break; } for (arg = NULL;; ) { la = *pos; av = mdoc_argv(m, line, tok, &arg, pos, buf); if (ARGV_WORD == av) { *pos = la; break; } if (ARGV_EOLN == av) break; if (ARGV_ARG == av) continue; mdoc_argv_free(arg); return(0); } for (cnt = scope = 0;; ) { la = *pos; ac = mdoc_args(m, line, pos, buf, tok, &p); if (ARGS_ERROR == ac) return(0); if (ARGS_EOLN == ac) break; if (ARGS_PUNCT == ac) break; ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p); /* * In this case, we've located a submacro and must * execute it. Close out scope, if open. If no * elements have been generated, either create one (nc) * or raise a warning. */ if (MDOC_MAX != ntok) { if (scope && ! rew_elem(m, tok)) return(0); if (nc && 0 == cnt) { if ( ! mdoc_elem_alloc(m, line, ppos, tok, arg)) return(0); if ( ! rew_last(m, m->last)) return(0); } else if ( ! nc && 0 == cnt) { mdoc_argv_free(arg); mdoc_pmsg(m, line, ppos, MANDOCERR_MACROEMPTY); } if ( ! mdoc_macro(m, ntok, line, la, pos, buf)) return(0); if ( ! nl) return(1); return(append_delims(m, line, pos, buf)); } /* * Non-quote-enclosed punctuation. Set up our scope, if * a word; rewind the scope, if a delimiter; then append * the word. */ d = ARGS_QWORD == ac ? DELIM_NONE : mdoc_isdelim(p); if (DELIM_NONE != d) { /* * If we encounter closing punctuation, no word * has been omitted, no scope is open, and we're * allowed to have an empty element, then start * a new scope. `Ar', `Fl', and `Li', only do * this once per invocation. There may be more * of these (all of them?). */ if (0 == cnt && (nc || MDOC_Li == tok) && DELIM_CLOSE == d && ! scope) { if ( ! mdoc_elem_alloc(m, line, ppos, tok, arg)) return(0); if (MDOC_Ar == tok || MDOC_Li == tok || MDOC_Fl == tok) cnt++; scope = 1; } /* * Close out our scope, if one is open, before * any punctuation. */ if (scope && ! rew_elem(m, tok)) return(0); scope = 0; } else if ( ! scope) { if ( ! mdoc_elem_alloc(m, line, ppos, tok, arg)) return(0); scope = 1; } if (DELIM_NONE == d) cnt++; if ( ! dword(m, line, la, p, d)) return(0); /* * `Fl' macros have their scope re-opened with each new * word so that the `-' can be added to each one without * having to parse out spaces. */ if (scope && MDOC_Fl == tok) { if ( ! rew_elem(m, tok)) return(0); scope = 0; } } if (scope && ! rew_elem(m, tok)) return(0); /* * If no elements have been collected and we're allowed to have * empties (nc), open a scope and close it out. Otherwise, * raise a warning. */ if (nc && 0 == cnt) { if ( ! mdoc_elem_alloc(m, line, ppos, tok, arg)) return(0); if ( ! rew_last(m, m->last)) return(0); } else if ( ! nc && 0 == cnt) { mdoc_argv_free(arg); mdoc_pmsg(m, line, ppos, MANDOCERR_MACROEMPTY); } if ( ! nl) return(1); return(append_delims(m, line, pos, buf)); }