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); }
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 in_line_argn(MACRO_PROT_ARGS) { struct mdoc_arg *arg; char *p; enum margserr ac; int ntok; int state; /* arg#; -1: not yet open; -2: closed */ int la, maxargs, nl; nl = mdoc->flags & MDOC_NEWLINE; /* * A line macro that has a fixed number of arguments (maxargs). * Only open the scope once the first non-leading-punctuation is * found (unless MDOC_IGNDELIM is noted, like in `Pf'), then * keep it open until the maximum number of arguments are * exhausted. */ switch (tok) { case MDOC_Ap: case MDOC_Ns: case MDOC_Ux: maxargs = 0; break; case MDOC_Bx: case MDOC_Es: case MDOC_Xr: maxargs = 2; break; default: maxargs = 1; break; } mdoc_argv(mdoc, line, tok, &arg, pos, buf); state = -1; p = NULL; for (;;) { la = *pos; ac = mdoc_args(mdoc, line, pos, buf, tok, &p); if (ac == ARGS_WORD && state == -1 && ! (mdoc_macros[tok].flags & MDOC_IGNDELIM) && mdoc_isdelim(p) == DELIM_OPEN) { dword(mdoc, line, la, p, DELIM_OPEN, 0); continue; } if (state == -1 && tok != MDOC_In && tok != MDOC_St && tok != MDOC_Xr) { mdoc_elem_alloc(mdoc, line, ppos, tok, arg); state = 0; } if (ac == ARGS_PUNCT || ac == ARGS_EOLN) { if (abs(state) < 2 && tok == MDOC_Pf) mandoc_vmsg(MANDOCERR_PF_SKIP, mdoc->parse, line, ppos, "Pf %s", p == NULL ? "at eol" : p); break; } if (state == maxargs) { rew_elem(mdoc, tok); state = -2; } ntok = (ac == ARGS_QWORD || (tok == MDOC_Pf && state == 0)) ? TOKEN_NONE : lookup(mdoc, tok, line, la, p); if (ntok != TOKEN_NONE) { if (state >= 0) { rew_elem(mdoc, tok); state = -2; } mdoc_macro(mdoc, ntok, line, la, pos, buf); break; } if (ac == ARGS_QWORD || mdoc_macros[tok].flags & MDOC_IGNDELIM || mdoc_isdelim(p) == DELIM_NONE) { if (state == -1) { mdoc_elem_alloc(mdoc, line, ppos, tok, arg); state = 1; } else if (state >= 0) state++; } else if (state >= 0) { rew_elem(mdoc, tok); state = -2; } dword(mdoc, line, la, p, DELIM_MAX, MDOC_JOIN & mdoc_macros[tok].flags); } if (state == -1) { mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, line, ppos, mdoc_macronames[tok]); return; } if (state == 0 && tok == MDOC_Pf) append_delims(mdoc, line, pos, buf); if (state >= 0) rew_elem(mdoc, tok); if (nl) append_delims(mdoc, line, pos, buf); }
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)); }
/* * Parse free-form text, that is, a line that does not begin with the * control character. */ static int mdoc_ptext(struct mdoc *m, int line, char *buf, int offs) { char *c, *ws, *end; struct mdoc_node *n; /* Ignore bogus comments. */ if ('\\' == buf[offs] && '.' == buf[offs + 1] && '"' == buf[offs + 2]) { mdoc_pmsg(m, line, offs, MANDOCERR_BADCOMMENT); return(1); } /* No text before an initial macro. */ if (SEC_NONE == m->lastnamed) { mdoc_pmsg(m, line, offs, MANDOCERR_NOTEXT); return(1); } assert(m->last); n = m->last; /* * Divert directly to list processing if we're encountering a * columnar MDOC_BLOCK with or without a prior MDOC_BLOCK entry * (a MDOC_BODY means it's already open, in which case we should * process within its context in the normal way). */ if (MDOC_Bl == n->tok && MDOC_BODY == n->type && LIST_column == n->norm->Bl.type) { /* `Bl' is open without any children. */ m->flags |= MDOC_FREECOL; return(mdoc_macro(m, MDOC_It, line, offs, &offs, buf)); } if (MDOC_It == n->tok && MDOC_BLOCK == n->type && NULL != n->parent && MDOC_Bl == n->parent->tok && LIST_column == n->parent->norm->Bl.type) { /* `Bl' has block-level `It' children. */ m->flags |= MDOC_FREECOL; return(mdoc_macro(m, MDOC_It, line, offs, &offs, buf)); } /* * Search for the beginning of unescaped trailing whitespace (ws) * and for the first character not to be output (end). */ /* FIXME: replace with strcspn(). */ ws = NULL; for (c = end = buf + offs; *c; c++) { switch (*c) { case '-': if (mandoc_hyph(buf + offs, c)) *c = ASCII_HYPH; ws = NULL; break; case ' ': if (NULL == ws) ws = c; continue; case '\t': /* * Always warn about trailing tabs, * even outside literal context, * where they should be put on the next line. */ if (NULL == ws) ws = c; /* * Strip trailing tabs in literal context only; * outside, they affect the next line. */ if (MDOC_LITERAL & m->flags) continue; break; case '\\': /* Skip the escaped character, too, if any. */ if (c[1]) c++; /* FALLTHROUGH */ default: ws = NULL; break; } end = c + 1; } *end = '\0'; if (ws) mdoc_pmsg(m, line, (int)(ws-buf), MANDOCERR_EOLNSPACE); if ('\0' == buf[offs] && ! (MDOC_LITERAL & m->flags)) { mdoc_pmsg(m, line, (int)(c-buf), MANDOCERR_NOBLANKLN); /* * Insert a `sp' in the case of a blank line. Technically, * blank lines aren't allowed, but enough manuals assume this * behaviour that we want to work around it. */ if ( ! mdoc_elem_alloc(m, line, offs, MDOC_sp, NULL)) return(0); m->next = MDOC_NEXT_SIBLING; return(1); } if ( ! mdoc_word_alloc(m, line, offs, buf+offs)) return(0); if (MDOC_LITERAL & m->flags) return(1); /* * End-of-sentence check. If the last character is an unescaped * EOS character, then flag the node as being the end of a * sentence. The front-end will know how to interpret this. */ assert(buf < end); if (mandoc_eos(buf+offs, (size_t)(end-buf-offs), 0)) m->last->flags |= MDOC_EOS; return(1); }
static int in_line_eoln(MACRO_PROT_ARGS) { int la; enum margserr ac; enum margverr av; struct mdoc_arg *arg; char *p; enum mdoct ntok; assert( ! (MDOC_PARSED & mdoc_macros[tok].flags)); if (tok == MDOC_Pp) rew_sub(MDOC_BLOCK, m, MDOC_Nm, line, ppos); /* Parse macro arguments. */ 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); } /* Open element scope. */ if ( ! mdoc_elem_alloc(m, line, ppos, tok, arg)) return(0); /* Parse argument terms. */ for (;;) { la = *pos; ac = mdoc_args(m, line, pos, buf, tok, &p); if (ARGS_ERROR == ac) return(0); if (ARGS_EOLN == ac) break; ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p); if (MDOC_MAX == ntok) { if ( ! dword(m, line, la, p, DELIM_MAX)) return(0); continue; } if ( ! rew_elem(m, tok)) return(0); return(mdoc_macro(m, ntok, line, la, pos, buf)); } /* Close out (no delimiters). */ return(rew_elem(m, tok)); }
/* ARGSUSED */ static int in_line_argn(MACRO_PROT_ARGS) { int la, flushed, j, maxargs, nl; enum margserr ac; enum margverr av; struct mdoc_arg *arg; char *p; enum mdoct ntok; nl = MDOC_NEWLINE & m->flags; /* * A line macro that has a fixed number of arguments (maxargs). * Only open the scope once the first non-leading-punctuation is * found (unless MDOC_IGNDELIM is noted, like in `Pf'), then * keep it open until the maximum number of arguments are * exhausted. */ switch (tok) { case (MDOC_Ap): /* FALLTHROUGH */ case (MDOC_No): /* FALLTHROUGH */ case (MDOC_Ns): /* FALLTHROUGH */ case (MDOC_Ux): maxargs = 0; break; case (MDOC_Bx): /* FALLTHROUGH */ case (MDOC_Xr): maxargs = 2; break; default: maxargs = 1; 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 (flushed = j = 0; ; ) { la = *pos; ac = mdoc_args(m, line, pos, buf, tok, &p); if (ARGS_ERROR == ac) return(0); if (ARGS_PUNCT == ac) break; if (ARGS_EOLN == ac) break; if ( ! (MDOC_IGNDELIM & mdoc_macros[tok].flags) && ARGS_QWORD != ac && 0 == j && DELIM_OPEN == mdoc_isdelim(p)) { if ( ! dword(m, line, la, p, DELIM_OPEN)) return(0); continue; } else if (0 == j) if ( ! mdoc_elem_alloc(m, line, la, tok, arg)) return(0); if (j == maxargs && ! flushed) { if ( ! rew_elem(m, tok)) return(0); flushed = 1; } ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p); if (MDOC_MAX != ntok) { if ( ! flushed && ! rew_elem(m, tok)) return(0); flushed = 1; if ( ! mdoc_macro(m, ntok, line, la, pos, buf)) return(0); j++; break; } if ( ! (MDOC_IGNDELIM & mdoc_macros[tok].flags) && ARGS_QWORD != ac && ! flushed && DELIM_NONE != mdoc_isdelim(p)) { if ( ! rew_elem(m, tok)) return(0); flushed = 1; } if ( ! dword(m, line, la, p, DELIM_MAX)) return(0); j++; } if (0 == j && ! mdoc_elem_alloc(m, line, la, tok, arg)) return(0); /* Close out in a consistent state. */ if ( ! flushed && ! rew_elem(m, tok)) return(0); if ( ! nl) return(1); return(append_delims(m, line, pos, buf)); }
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; } }
/* * Close out block partial/full explicit. */ static void blk_exp_close(MACRO_PROT_ARGS) { struct mdoc_node *body; /* Our own body. */ struct mdoc_node *endbody; /* Our own end marker. */ struct mdoc_node *itblk; /* An It block starting later. */ struct mdoc_node *later; /* A sub-block starting later. */ struct mdoc_node *n; /* Search back to our block. */ int j, lastarg, maxargs, nl; enum margserr ac; enum mdoct 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 == MDOC_BODY && atok == n->tok) { if (n->end == ENDBODY_NOT) body = n; continue; } if (n->type != MDOC_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 = MDOC_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 (maxargs && endbody == NULL) { /* * Stray .Ec without previous .Eo: * Break the output line, keep the arguments. */ mdoc_elem_alloc(mdoc, line, ppos, MDOC_br, NULL); 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; for (j = 0; ; j++) { lastarg = *pos; if (j == maxargs && n != NULL) { rew_pending(mdoc, n); n = NULL; } ac = mdoc_args(mdoc, line, pos, buf, tok, &p); if (ac == ARGS_PUNCT || ac == ARGS_EOLN) break; ntok = ac == ARGS_QWORD ? MDOC_MAX : lookup(mdoc, tok, line, lastarg, p); if (ntok == MDOC_MAX) { dword(mdoc, line, lastarg, p, DELIM_MAX, MDOC_JOIN & mdoc_macros[tok].flags); continue; } if (n != NULL) { rew_pending(mdoc, n); n = NULL; } mdoc->flags &= ~MDOC_NEWLINE; mdoc_macro(mdoc, ntok, line, lastarg, pos, buf); break; } if (n != NULL) rew_pending(mdoc, n); if (nl) append_delims(mdoc, line, pos, buf); }
/* ARGSUSED */ static int in_line_argn(MACRO_PROT_ARGS) { int la, flushed, j, maxargs, nl; enum margserr ac; enum margverr av; struct mdoc_arg *arg; char *p; enum mdoct ntok; nl = MDOC_NEWLINE & m->flags; /* * A line macro that has a fixed number of arguments (maxargs). * Only open the scope once the first non-leading-punctuation is * found (unless MDOC_IGNDELIM is noted, like in `Pf'), then * keep it open until the maximum number of arguments are * exhausted. */ switch (tok) { case (MDOC_Ap): /* FALLTHROUGH */ case (MDOC_No): /* FALLTHROUGH */ case (MDOC_Ns): /* FALLTHROUGH */ case (MDOC_Ux): maxargs = 0; break; case (MDOC_Xr): maxargs = 2; break; default: maxargs = 1; 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 (flushed = j = 0; ; ) { la = *pos; ac = mdoc_args(m, line, pos, buf, tok, &p); if (ARGS_ERROR == ac) return(0); if (ARGS_PUNCT == ac) break; if (ARGS_EOLN == ac) break; if ( ! (MDOC_IGNDELIM & mdoc_macros[tok].flags) && ARGS_QWORD != ac && 0 == j && DELIM_OPEN == mdoc_isdelim(p)) { if ( ! mdoc_word_alloc(m, line, la, p)) return(0); continue; } else if (0 == j) if ( ! mdoc_elem_alloc(m, line, la, tok, arg)) return(0); if (j == maxargs && ! flushed) { if ( ! rew_elem(m, tok)) return(0); flushed = 1; } ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p); if (MDOC_MAX != ntok) { if ( ! flushed && ! rew_elem(m, tok)) return(0); flushed = 1; if ( ! mdoc_macro(m, ntok, line, la, pos, buf)) return(0); j++; break; } if ( ! (MDOC_IGNDELIM & mdoc_macros[tok].flags) && ARGS_QWORD != ac && ! flushed && DELIM_NONE != mdoc_isdelim(p)) { if ( ! rew_elem(m, tok)) return(0); flushed = 1; } /* * XXX: this is a hack to work around groff's ugliness * as regards `Xr' and extraneous arguments. It should * ideally be deprecated behaviour, but because this is * code is no here, it's unlikely to be removed. */ #ifdef __OpenBSD__ if (MDOC_Xr == tok && j == maxargs) { if ( ! mdoc_elem_alloc(m, line, la, MDOC_Ns, NULL)) return(0); if ( ! rew_elem(m, MDOC_Ns)) return(0); } #endif if ( ! mdoc_word_alloc(m, line, la, p)) return(0); j++; } if (0 == j && ! mdoc_elem_alloc(m, line, la, tok, arg)) return(0); /* Close out in a consistent state. */ if ( ! flushed && ! rew_elem(m, tok)) return(0); if ( ! nl) return(1); return(append_delims(m, line, pos, buf)); }