int mdoc_macro(MACRO_PROT_ARGS) { assert(tok < MDOC_MAX); /* If we're in the body, deny prologue calls. */ if (MDOC_PROLOGUE & mdoc_macros[tok].flags && MDOC_PBODY & m->flags) { mdoc_pmsg(m, line, ppos, MANDOCERR_BADBODY); return(1); } /* If we're in the prologue, deny "body" macros. */ if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) && ! (MDOC_PBODY & m->flags)) { mdoc_pmsg(m, line, ppos, MANDOCERR_BADPROLOG); if (NULL == m->meta.msec) m->meta.msec = mandoc_strdup("1"); if (NULL == m->meta.title) m->meta.title = mandoc_strdup("UNKNOWN"); if (NULL == m->meta.vol) m->meta.vol = mandoc_strdup("LOCAL"); if (NULL == m->meta.os) m->meta.os = mandoc_strdup("LOCAL"); if (0 == m->meta.date) m->meta.date = time(NULL); m->flags |= MDOC_PBODY; } return((*mdoc_macros[tok].fp)(m, tok, line, ppos, pos, buf)); }
static void check_text(struct mdoc *m, int ln, int pos, char *p) { int c; size_t sz; for ( ; *p; p++, pos++) { sz = strcspn(p, "\t\\"); p += (int)sz; if ('\0' == *p) break; pos += (int)sz; if ('\t' == *p) { if ( ! (MDOC_LITERAL & m->flags)) mdoc_pmsg(m, ln, pos, MANDOCERR_BADTAB); continue; } if (0 == (c = mandoc_special(p))) { mdoc_pmsg(m, ln, pos, MANDOCERR_BADESCAPE); continue; } p += c - 1; pos += c - 1; } }
/* ARGSUSED */ static int obsolete(MACRO_PROT_ARGS) { mdoc_pmsg(m, line, ppos, MANDOCERR_MACROOBS); return(1); }
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); }
/* ARGSUSED */ static int phrase_ta(MACRO_PROT_ARGS) { struct mdoc_node *n; int la; enum mdoct ntok; enum margserr ac; char *p; /* Make sure we are in a column list or ignore this macro. */ n = mdoc->last; while (NULL != n && MDOC_Bl != n->tok) n = n->parent; if (NULL == n || LIST_column != n->norm->Bl.type) { mdoc_pmsg(mdoc, line, ppos, MANDOCERR_STRAYTA); return(1); } /* Advance to the next column. */ if ( ! rew_sub(MDOC_BODY, mdoc, MDOC_It, line, ppos)) return(0); if ( ! mdoc_body_alloc(mdoc, line, ppos, MDOC_It)) return(0); for (;;) { la = *pos; ac = mdoc_zargs(mdoc, line, pos, buf, &p); if (ARGS_ERROR == ac) return(0); if (ARGS_EOLN == ac) break; ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup_raw(p); if (MDOC_MAX == ntok) { if ( ! dword(mdoc, line, la, p, DELIM_MAX, MDOC_JOIN & mdoc_macros[tok].flags)) return(0); continue; } if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf)) return(0); return(append_delims(mdoc, line, pos, buf)); } 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); }
int mdoc_addspan(struct mdoc *m, const struct tbl_span *sp) { assert( ! (MDOC_HALT & m->flags)); /* No text before an initial macro. */ if (SEC_NONE == m->lastnamed) { /* FIXME: grab from span. */ mdoc_pmsg(m, 0, 0, MANDOCERR_NOTEXT); return(1); } return(mdoc_span_alloc(m, sp)); }
static int pre_an(PRE_ARGS) { int i; if (NULL == n->args) return(1); for (i = 1; i < (int)n->args->argc; i++) mdoc_pmsg(mdoc, n->args->argv[i].line, n->args->argv[i].pos, MANDOCERR_IGNARGV); if (MDOC_Split == n->args->argv[0].arg) n->norm->An.auth = AUTH_split; else if (MDOC_Nosplit == n->args->argv[0].arg) n->norm->An.auth = AUTH_nosplit; else abort(); return(1); }
int mdoc_addspan(struct mdoc *m, const struct tbl_span *sp) { struct mdoc_node *n; assert( ! (MDOC_HALT & m->flags)); /* No text before an initial macro. */ if (SEC_NONE == m->lastnamed) { mdoc_pmsg(m, sp->line, 0, MANDOCERR_NOTEXT); return(1); } n = node_alloc(m, sp->line, 0, MDOC_MAX, MDOC_TBL); n->span = sp; if ( ! node_append(m, n)) return(0); m->next = MDOC_NEXT_SIBLING; return(1); }
int mdoc_addeqn(struct mdoc *m, const struct eqn *ep) { struct mdoc_node *n; assert( ! (MDOC_HALT & m->flags)); /* No text before an initial macro. */ if (SEC_NONE == m->lastnamed) { mdoc_pmsg(m, ep->ln, ep->pos, MANDOCERR_NOTEXT); return(1); } n = node_alloc(m, ep->ln, ep->pos, MDOC_MAX, MDOC_EQN); n->eqn = ep; if ( ! node_append(m, n)) return(0); m->next = MDOC_NEXT_SIBLING; return(1); }
static int argv_single(struct mdoc *m, int line, struct mdoc_argv *v, int *pos, char *buf) { int ppos; enum margserr ac; char *p; ppos = *pos; ac = args(m, line, pos, buf, ARGSFL_NONE, &p); if (ARGS_EOLN == ac) { mdoc_pmsg(m, line, ppos, MANDOCERR_SYNTARGVCOUNT); return(0); } else if (ARGS_ERROR == ac) return(0); v->sz = 1; v->value = mandoc_malloc(sizeof(char *)); v->value[0] = mandoc_strdup(p); return(1); }
/* * 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(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)); }
/* * Close out block partial/full explicit. */ static int blk_exp_close(MACRO_PROT_ARGS) { int j, lastarg, maxargs, flushed, nl; enum margserr ac; enum mdoct ntok; char *p; nl = MDOC_NEWLINE & m->flags; switch (tok) { case (MDOC_Ec): maxargs = 1; break; default: maxargs = 0; break; } if ( ! (MDOC_CALLABLE & mdoc_macros[tok].flags)) { /* FIXME: do this in validate */ if (buf[*pos]) if ( ! mdoc_pmsg(m, line, ppos, MANDOCERR_ARGSLOST)) return(0); if ( ! rew_sub(MDOC_BODY, m, tok, line, ppos)) return(0); return(rew_sub(MDOC_BLOCK, m, tok, line, ppos)); } if ( ! rew_sub(MDOC_BODY, m, tok, line, ppos)) return(0); if (maxargs > 0) if ( ! mdoc_tail_alloc(m, line, ppos, rew_alt(tok))) return(0); for (flushed = j = 0; ; j++) { lastarg = *pos; if (j == maxargs && ! flushed) { if ( ! rew_sub(MDOC_BLOCK, m, tok, line, ppos)) return(0); flushed = 1; } 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; ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p); if (MDOC_MAX == ntok) { if ( ! mdoc_word_alloc(m, line, lastarg, p)) return(0); continue; } if ( ! flushed) { if ( ! rew_sub(MDOC_BLOCK, m, tok, line, ppos)) return(0); flushed = 1; } if ( ! mdoc_macro(m, ntok, line, lastarg, pos, buf)) return(0); break; } if ( ! flushed && ! rew_sub(MDOC_BLOCK, m, tok, line, ppos)) return(0); if ( ! nl) return(1); return(append_delims(m, line, pos, buf)); }
/* * Parse a macro line, that is, a line beginning with the control * character. */ static int mdoc_pmacro(struct mdoc *m, int ln, char *buf, int offs) { enum mdoct tok; int i, j, sv; char mac[5]; struct mdoc_node *n; /* Empty lines are ignored. */ offs++; if ('\0' == buf[offs]) return(1); i = offs; /* Accept tabs/whitespace after the initial control char. */ if (' ' == buf[i] || '\t' == buf[i]) { i++; while (buf[i] && (' ' == buf[i] || '\t' == buf[i])) i++; if ('\0' == buf[i]) return(1); } sv = i; /* * Copy the first word into a nil-terminated buffer. * Stop copying when a tab, space, or eoln is encountered. */ j = 0; while (j < 4 && '\0' != buf[i] && ' ' != buf[i] && '\t' != buf[i]) mac[j++] = buf[i++]; mac[j] = '\0'; tok = (j > 1 || j < 4) ? mdoc_hash_find(mac) : MDOC_MAX; if (MDOC_MAX == tok) { mdoc_vmsg(m, MANDOCERR_MACRO, ln, sv, "%s", buf + sv - 1); return(1); } /* Disregard the first trailing tab, if applicable. */ if ('\t' == buf[i]) i++; /* Jump to the next non-whitespace word. */ while (buf[i] && ' ' == buf[i]) i++; /* * Trailing whitespace. Note that tabs are allowed to be passed * into the parser as "text", so we only warn about spaces here. */ if ('\0' == buf[i] && ' ' == buf[i - 1]) mdoc_pmsg(m, ln, i - 1, MANDOCERR_EOLNSPACE); /* * If an initial macro or a list invocation, divert directly * into macro processing. */ if (NULL == m->last || MDOC_It == tok || MDOC_El == tok) { if ( ! mdoc_macro(m, tok, ln, sv, &i, buf)) goto err; return(1); } n = m->last; assert(m->last); /* * If the first macro of a `Bl -column', open an `It' block * context around the parsed macro. */ if (MDOC_Bl == n->tok && MDOC_BODY == n->type && LIST_column == n->norm->Bl.type) { m->flags |= MDOC_FREECOL; if ( ! mdoc_macro(m, MDOC_It, ln, sv, &sv, buf)) goto err; return(1); } /* * If we're following a block-level `It' within a `Bl -column' * context (perhaps opened in the above block or in ptext()), * then open an `It' block context around the parsed macro. */ if (MDOC_It == n->tok && MDOC_BLOCK == n->type && NULL != n->parent && MDOC_Bl == n->parent->tok && LIST_column == n->parent->norm->Bl.type) { m->flags |= MDOC_FREECOL; if ( ! mdoc_macro(m, MDOC_It, ln, sv, &sv, buf)) goto err; return(1); } /* Normal processing of a macro. */ if ( ! mdoc_macro(m, tok, ln, sv, &i, buf)) goto err; return(1); err: /* Error out. */ m->flags |= MDOC_HALT; return(0); }
static enum margserr args(struct mdoc *mdoc, int line, int *pos, char *buf, enum argsflag fl, char **v) { char *p, *pp; int pairs; enum margserr rc; if ('\0' == buf[*pos]) { if (MDOC_PPHRASE & mdoc->flags) return(ARGS_EOLN); /* * If we're not in a partial phrase and the flag for * being a phrase literal is still set, the punctuation * is unterminated. */ if (MDOC_PHRASELIT & mdoc->flags) mdoc_pmsg(mdoc, line, *pos, MANDOCERR_BADQUOTE); mdoc->flags &= ~MDOC_PHRASELIT; return(ARGS_EOLN); } *v = &buf[*pos]; if (ARGSFL_DELIM == fl) if (args_checkpunct(buf, *pos)) return(ARGS_PUNCT); /* * First handle TABSEP items, restricted to `Bl -column'. This * ignores conventional token parsing and instead uses tabs or * `Ta' macros to separate phrases. Phrases are parsed again * for arguments at a later phase. */ if (ARGSFL_TABSEP == fl) { /* Scan ahead to tab (can't be escaped). */ p = strchr(*v, '\t'); pp = NULL; /* Scan ahead to unescaped `Ta'. */ if ( ! (MDOC_PHRASELIT & mdoc->flags)) for (pp = *v; ; pp++) { if (NULL == (pp = strstr(pp, "Ta"))) break; if (pp > *v && ' ' != *(pp - 1)) continue; if (' ' == *(pp + 2) || '\0' == *(pp + 2)) break; } /* By default, assume a phrase. */ rc = ARGS_PHRASE; /* * Adjust new-buffer position to be beyond delimiter * mark (e.g., Ta -> end + 2). */ if (p && pp) { *pos += pp < p ? 2 : 1; rc = pp < p ? ARGS_PHRASE : ARGS_PPHRASE; p = pp < p ? pp : p; } else if (p && ! pp) { rc = ARGS_PPHRASE; *pos += 1; } else if (pp && ! p) { p = pp; *pos += 2; } else { rc = ARGS_PEND; p = strchr(*v, 0); } /* Whitespace check for eoln case... */ if ('\0' == *p && ' ' == *(p - 1)) mdoc_pmsg(mdoc, line, *pos, MANDOCERR_EOLNSPACE); *pos += (int)(p - *v); /* Strip delimiter's preceding whitespace. */ pp = p - 1; while (pp > *v && ' ' == *pp) { if (pp > *v && '\\' == *(pp - 1)) break; pp--; } *(pp + 1) = 0; /* Strip delimiter's proceeding whitespace. */ for (pp = &buf[*pos]; ' ' == *pp; pp++, (*pos)++) /* Skip ahead. */ ; return(rc); } /* * Process a quoted literal. A quote begins with a double-quote * and ends with a double-quote NOT preceded by a double-quote. * NUL-terminate the literal in place. * Collapse pairs of quotes inside quoted literals. * Whitespace is NOT involved in literal termination. */ if (MDOC_PHRASELIT & mdoc->flags || '\"' == buf[*pos]) { if ( ! (MDOC_PHRASELIT & mdoc->flags)) *v = &buf[++(*pos)]; if (MDOC_PPHRASE & mdoc->flags) mdoc->flags |= MDOC_PHRASELIT; pairs = 0; for ( ; buf[*pos]; (*pos)++) { /* Move following text left after quoted quotes. */ if (pairs) buf[*pos - pairs] = buf[*pos]; if ('\"' != buf[*pos]) continue; /* Unquoted quotes end quoted args. */ if ('\"' != buf[*pos + 1]) break; /* Quoted quotes collapse. */ pairs++; (*pos)++; } if (pairs) buf[*pos - pairs] = '\0'; if ('\0' == buf[*pos]) { if (MDOC_PPHRASE & mdoc->flags) return(ARGS_QWORD); mdoc_pmsg(mdoc, line, *pos, MANDOCERR_BADQUOTE); return(ARGS_QWORD); } mdoc->flags &= ~MDOC_PHRASELIT; buf[(*pos)++] = '\0'; if ('\0' == buf[*pos]) return(ARGS_QWORD); while (' ' == buf[*pos]) (*pos)++; if ('\0' == buf[*pos]) mdoc_pmsg(mdoc, line, *pos, MANDOCERR_EOLNSPACE); return(ARGS_QWORD); } p = &buf[*pos]; *v = mandoc_getarg(mdoc->parse, &p, line, pos); return(ARGS_WORD); }
/* * Close out block partial/full explicit. */ static int blk_exp_close(MACRO_PROT_ARGS) { struct mdoc_node *body; /* Our own body. */ struct mdoc_node *later; /* A sub-block starting later. */ struct mdoc_node *n; /* For searching backwards. */ int j, lastarg, maxargs, flushed, nl; enum margserr ac; enum mdoct atok, ntok; char *p; nl = MDOC_NEWLINE & m->flags; switch (tok) { case (MDOC_Ec): maxargs = 1; break; default: maxargs = 0; break; } /* * Search backwards for beginnings of blocks, * both of our own and of pending sub-blocks. */ atok = rew_alt(tok); body = later = NULL; for (n = m->last; n; n = n->parent) { if (MDOC_VALID & n->flags) continue; /* Remember the start of our own body. */ if (MDOC_BODY == n->type && atok == n->tok) { if (ENDBODY_NOT == n->end) body = n; continue; } if (MDOC_BLOCK != n->type || MDOC_Nm == n->tok) 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 (NULL == later) break; /* * When there is a pending sub block, * postpone closing out the current block * until the rew_sub() closing out the sub-block. */ make_pending(later, tok, m, line, ppos); /* * Mark the place where the formatting - but not * the scope - of the current block ends. */ if ( ! mdoc_endbody_alloc(m, line, ppos, atok, body, ENDBODY_SPACE)) return(0); break; } /* * When finding an open sub block, remember the last * open explicit block, or, in case there are only * implicit ones, the first open implicit block. */ if (later && MDOC_EXPLICIT & mdoc_macros[later->tok].flags) continue; if (MDOC_CALLABLE & mdoc_macros[n->tok].flags) later = n; } if ( ! (MDOC_CALLABLE & mdoc_macros[tok].flags)) { /* FIXME: do this in validate */ if (buf[*pos]) mdoc_pmsg(m, line, ppos, MANDOCERR_ARGSLOST); if ( ! rew_sub(MDOC_BODY, m, tok, line, ppos)) return(0); return(rew_sub(MDOC_BLOCK, m, tok, line, ppos)); } if ( ! rew_sub(MDOC_BODY, m, tok, line, ppos)) return(0); if (NULL == later && maxargs > 0) if ( ! mdoc_tail_alloc(m, line, ppos, rew_alt(tok))) return(0); for (flushed = j = 0; ; j++) { lastarg = *pos; if (j == maxargs && ! flushed) { if ( ! rew_sub(MDOC_BLOCK, m, tok, line, ppos)) return(0); flushed = 1; } 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; ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p); if (MDOC_MAX == ntok) { if ( ! dword(m, line, lastarg, p, DELIM_MAX)) return(0); continue; } if ( ! flushed) { if ( ! rew_sub(MDOC_BLOCK, m, tok, line, ppos)) return(0); flushed = 1; } if ( ! mdoc_macro(m, ntok, line, lastarg, pos, buf)) return(0); break; } if ( ! flushed && ! rew_sub(MDOC_BLOCK, m, tok, line, ppos)) return(0); if ( ! nl) return(1); return(append_delims(m, line, pos, buf)); }