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); }
/* * 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 man_descope(struct roff_man *man, int line, int offs) { /* * Co-ordinate what happens with having a next-line scope open: * first close out the element scope (if applicable), then close * out the block scope (also if applicable). */ if (man->flags & MAN_ELINE) { man->flags &= ~MAN_ELINE; man_unscope(man, man->last->parent); } if ( ! (man->flags & MAN_BLINE)) return; man->flags &= ~MAN_BLINE; man_unscope(man, man->last->parent); roff_body_alloc(man, line, offs, man->last->tok); }
/* * Parse an implicit-block macro. These contain a ROFFT_HEAD and a * ROFFT_BODY contained within a ROFFT_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 roff_node *n; rew_scope(man, tok); n = roff_block_alloc(man, line, ppos, tok); if (n->tok == MAN_SH || n->tok == MAN_SS) man->flags &= ~MAN_LITERAL; n = roff_head_alloc(man, line, ppos, tok); /* Add line arguments. */ for (;;) { la = *pos; if ( ! man_args(man, line, pos, buf, &p)) break; roff_word_alloc(man, line, la, p); } /* * For macros having optional next-line scope, * keep the head open if there were no arguments. * For `TP', always keep the head open. */ if (man_macros[tok].flags & MAN_SCOPED && (tok == MAN_TP || n == man->last)) { man->flags |= MAN_BLINE; return; } /* Close out the head and open the body. */ man_unscope(man, n); roff_body_alloc(man, line, ppos, tok); }
void blk_exp(MACRO_PROT_ARGS) { struct roff_node *head; char *p; int la; rew_scope(man, tok); roff_block_alloc(man, line, ppos, tok); head = roff_head_alloc(man, line, ppos, tok); la = *pos; if (man_args(man, line, pos, buf, &p)) roff_word_alloc(man, line, la, p); if (buf[*pos] != '\0') mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse, line, *pos, "%s ... %s", roff_name[tok], buf + *pos); man_unscope(man, head); roff_body_alloc(man, line, ppos, tok); }
/* * 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; } } } }
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 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); }
void man_breakscope(struct roff_man *man, int tok) { struct roff_node *n; /* * An element next line scope is open, * and the new macro is not allowed inside elements. * Delete the element that is being broken. */ if (man->flags & MAN_ELINE && (tok < MAN_TH || ! (man_macros[tok].flags & MAN_NSCOPED))) { n = man->last; assert(n->type != ROFFT_TEXT); if (man_macros[n->tok].flags & MAN_NSCOPED) n = n->parent; mandoc_vmsg(MANDOCERR_BLK_LINE, man->parse, n->line, n->pos, "%s breaks %s", roff_name[tok], roff_name[n->tok]); roff_node_delete(man, n); man->flags &= ~MAN_ELINE; } /* * Weird special case: * Switching fill mode closes section headers. */ if (man->flags & MAN_BLINE && (tok == MAN_nf || tok == MAN_fi) && (man->last->tok == MAN_SH || man->last->tok == MAN_SS)) { n = man->last; man_unscope(man, n); roff_body_alloc(man, n->line, n->pos, n->tok); man->flags &= ~MAN_BLINE; } /* * A block header next line scope is open, * and the new macro is not allowed inside block headers. * Delete the block that is being broken. */ if (man->flags & MAN_BLINE && (tok < MAN_TH || man_macros[tok].flags & MAN_BSCOPE)) { n = man->last; if (n->type == ROFFT_TEXT) n = n->parent; if ( ! (man_macros[n->tok].flags & MAN_BSCOPE)) n = n->parent; assert(n->type == ROFFT_HEAD); n = n->parent; assert(n->type == ROFFT_BLOCK); assert(man_macros[n->tok].flags & MAN_SCOPED); mandoc_vmsg(MANDOCERR_BLK_LINE, man->parse, n->line, n->pos, "%s breaks %s", roff_name[tok], roff_name[n->tok]); roff_node_delete(man, n); man->flags &= ~MAN_BLINE; } }
static int man_pmacro(struct roff_man *man, int ln, char *buf, int offs) { struct roff_node *n; const char *cp; size_t sz; enum roff_tok tok; int ppos; int bline; /* Determine the line macro. */ ppos = offs; tok = TOKEN_NONE; for (sz = 0; sz < 4 && strchr(" \t\\", buf[offs]) == NULL; sz++) offs++; if (sz > 0 && sz < 4) tok = roffhash_find(man->manmac, buf + ppos, sz); if (tok == TOKEN_NONE) { mandoc_msg(MANDOCERR_MACRO, man->parse, ln, ppos, buf + ppos - 1); return 1; } /* Skip a leading escape sequence or tab. */ switch (buf[offs]) { case '\\': cp = buf + offs + 1; mandoc_escape(&cp, NULL, NULL); offs = cp - buf; break; case '\t': offs++; break; default: break; } /* Jump to the next non-whitespace word. */ while (buf[offs] == ' ') offs++; /* * Trailing whitespace. Note that tabs are allowed to be passed * into the parser as "text", so we only warn about spaces here. */ if (buf[offs] == '\0' && buf[offs - 1] == ' ') mandoc_msg(MANDOCERR_SPACE_EOL, man->parse, ln, offs - 1, NULL); /* * Some macros break next-line scopes; otherwise, remember * whether we are in next-line scope for a block head. */ man_breakscope(man, tok); bline = man->flags & MAN_BLINE; /* * If the line in next-line scope ends with \c, keep the * next-line scope open for the subsequent input line. * That is not at all portable, only groff >= 1.22.4 * does it, but *if* this weird idiom occurs in a manual * page, that's very likely what the author intended. */ if (bline) { cp = strchr(buf + offs, '\0') - 2; if (cp >= buf && cp[0] == '\\' && cp[1] == 'c') bline = 0; } /* Call to handler... */ assert(man_macros[tok].fp); (*man_macros[tok].fp)(man, tok, ln, ppos, &offs, buf); /* In quick mode (for mandocdb), abort after the NAME section. */ if (man->quick && tok == MAN_SH) { n = man->last; if (n->type == ROFFT_BODY && strcmp(n->prev->child->string, "NAME")) return 2; } /* * If we are in a next-line scope for a block head, * close it out now and switch to the body, * unless the next-line scope is allowed to continue. */ if ( ! bline || man->flags & MAN_ELINE || man_macros[tok].flags & MAN_NSCOPED) return 1; assert(man->flags & MAN_BLINE); man->flags &= ~MAN_BLINE; man_unscope(man, man->last->parent); roff_body_alloc(man, ln, ppos, man->last->tok); return 1; }
static int man_pmacro(struct roff_man *man, int ln, char *buf, int offs) { struct roff_node *n; const char *cp; int tok; int i, ppos; int bline; char mac[5]; ppos = offs; /* * Copy the first word into a nil-terminated buffer. * Stop when a space, tab, escape, or eoln is encountered. */ i = 0; while (i < 4 && strchr(" \t\\", buf[offs]) == NULL) mac[i++] = buf[offs++]; mac[i] = '\0'; tok = (i > 0 && i < 4) ? man_hash_find(mac) : TOKEN_NONE; if (tok == TOKEN_NONE) { mandoc_msg(MANDOCERR_MACRO, man->parse, ln, ppos, buf + ppos - 1); return 1; } /* Skip a leading escape sequence or tab. */ switch (buf[offs]) { case '\\': cp = buf + offs + 1; mandoc_escape(&cp, NULL, NULL); offs = cp - buf; break; case '\t': offs++; break; default: break; } /* Jump to the next non-whitespace word. */ while (buf[offs] && buf[offs] == ' ') offs++; /* * Trailing whitespace. Note that tabs are allowed to be passed * into the parser as "text", so we only warn about spaces here. */ if (buf[offs] == '\0' && buf[offs - 1] == ' ') mandoc_msg(MANDOCERR_SPACE_EOL, man->parse, ln, offs - 1, NULL); /* * Some macros break next-line scopes; otherwise, remember * whether we are in next-line scope for a block head. */ man_breakscope(man, tok); bline = man->flags & MAN_BLINE; /* Call to handler... */ assert(man_macros[tok].fp); (*man_macros[tok].fp)(man, tok, ln, ppos, &offs, buf); /* In quick mode (for mandocdb), abort after the NAME section. */ if (man->quick && tok == MAN_SH) { n = man->last; if (n->type == ROFFT_BODY && strcmp(n->prev->child->string, "NAME")) return 2; } /* * If we are in a next-line scope for a block head, * close it out now and switch to the body, * unless the next-line scope is allowed to continue. */ if ( ! bline || man->flags & MAN_ELINE || man_macros[tok].flags & MAN_NSCOPED) return 1; assert(man->flags & MAN_BLINE); man->flags &= ~MAN_BLINE; man_unscope(man, man->last->parent); roff_body_alloc(man, ln, ppos, man->last->tok); return 1; }