static void append_delims(struct roff_man *mdoc, int line, int *pos, char *buf) { char *p; int la; if (buf[*pos] == '\0') return; for (;;) { la = *pos; if (mdoc_args(mdoc, line, pos, buf, TOKEN_NONE, &p) == ARGS_EOLN) break; dword(mdoc, line, la, p, DELIM_MAX, 1); /* * If we encounter end-of-sentence symbols, then trigger * the double-space. * * XXX: it's easy to allow this to propagate outward to * the last symbol, such that `. )' will cause the * correct double-spacing. However, (1) groff isn't * smart enough to do this and (2) it would require * knowing which symbols break this behaviour, for * example, `. ;' shouldn't propagate the double-space. */ if (mandoc_eos(p, strlen(p))) mdoc->last->flags |= MDOC_EOS; } }
static int append_delims(struct mdoc *m, int line, int *pos, char *buf) { int la; enum margserr ac; char *p; if ('\0' == buf[*pos]) return(1); for (;;) { la = *pos; ac = mdoc_zargs(m, line, pos, buf, ARGS_NOWARN, &p); if (ARGS_ERROR == ac) return(0); else if (ARGS_EOLN == ac) break; assert(DELIM_NONE != mdoc_isdelim(p)); if ( ! mdoc_word_alloc(m, line, la, p)) return(0); /* * If we encounter end-of-sentence symbols, then trigger * the double-space. * * XXX: it's easy to allow this to propogate outward to * the last symbol, such that `. )' will cause the * correct double-spacing. However, (1) groff isn't * smart enough to do this and (2) it would require * knowing which symbols break this behaviour, for * example, `. ;' shouldn't propogate the double-space. */ if (mandoc_eos(p, strlen(p))) m->last->flags |= MDOC_EOS; } return(1); }
static int man_ptext(struct man *man, int line, char *buf, int offs) { int i; /* Literal free-form text whitespace is preserved. */ if (MAN_LITERAL & man->flags) { if ( ! man_word_alloc(man, line, offs, buf + offs)) return(0); return(man_descope(man, line, offs)); } for (i = offs; ' ' == buf[i]; i++) /* Skip leading whitespace. */ ; /* * Blank lines are ignored right after headings * but add a single vertical space elsewhere. */ if ('\0' == buf[i]) { /* Allocate a blank entry. */ if (MAN_SH != man->last->tok && MAN_SS != man->last->tok) { if ( ! man_elem_alloc(man, line, offs, MAN_sp)) return(0); man->next = MAN_NEXT_SIBLING; } return(1); } /* * Warn if the last un-escaped character is whitespace. Then * strip away the remaining spaces (tabs stay!). */ i = (int)strlen(buf); assert(i); if (' ' == buf[i - 1] || '\t' == buf[i - 1]) { if (i > 1 && '\\' != buf[i - 2]) man_pmsg(man, line, i - 1, MANDOCERR_EOLNSPACE); for (--i; i && ' ' == buf[i]; i--) /* Spin back to non-space. */ ; /* Jump ahead of escaped whitespace. */ i += '\\' == buf[i] ? 2 : 1; buf[i] = '\0'; } if ( ! man_word_alloc(man, line, offs, buf + offs)) return(0); /* * 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(i); if (mandoc_eos(buf, (size_t)i, 0)) man->last->flags |= MAN_EOS; return(man_descope(man, line, offs)); }
/* * 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 man_ptext(struct man *m, int line, char *buf, int offs) { int i; /* Literal free-form text whitespace is preserved. */ if (MAN_LITERAL & m->flags) { if ( ! man_word_alloc(m, line, offs, buf + offs)) return(0); return(man_descope(m, line, offs)); } /* Pump blank lines directly into the backend. */ for (i = offs; ' ' == buf[i]; i++) /* Skip leading whitespace. */ ; if ('\0' == buf[i]) { /* Allocate a blank entry. */ if ( ! man_word_alloc(m, line, offs, "")) return(0); return(man_descope(m, line, offs)); } /* * Warn if the last un-escaped character is whitespace. Then * strip away the remaining spaces (tabs stay!). */ i = (int)strlen(buf); assert(i); if (' ' == buf[i - 1] || '\t' == buf[i - 1]) { if (i > 1 && '\\' != buf[i - 2]) man_pmsg(m, line, i - 1, MANDOCERR_EOLNSPACE); for (--i; i && ' ' == buf[i]; i--) /* Spin back to non-space. */ ; /* Jump ahead of escaped whitespace. */ i += '\\' == buf[i] ? 2 : 1; buf[i] = '\0'; } if ( ! man_word_alloc(m, line, offs, buf + offs)) return(0); /* * 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(i); if (mandoc_eos(buf, (size_t)i, 0)) m->last->flags |= MAN_EOS; return(man_descope(m, line, offs)); }
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 ( ! dword(m, line, la, p, DELIM_OPEN)) 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 ( ! dword(m, line, la, p, DELIM_MAX)) 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), 1)) n->flags |= MDOC_EOS; /* Up-propagate the end-of-space flag. */ if (n && (MDOC_EOS & n->flags)) { body->flags |= MDOC_EOS; body->parent->flags |= MDOC_EOS; } /* * If there is an open sub-block requiring explicit close-out, * postpone closing out the current block * until the rew_sub() call closing out the sub-block. */ for (n = m->last; n && n != body && n != blk->parent; n = n->parent) { if (MDOC_BLOCK == n->type && MDOC_EXPLICIT & mdoc_macros[n->tok].flags && ! (MDOC_VALID & n->flags)) { make_pending(n, tok, m, line, ppos); if ( ! mdoc_endbody_alloc(m, line, ppos, tok, body, ENDBODY_NOSPACE)) return(0); return(1); } } /* * 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. */ if (n != body) mandoc_vmsg(MANDOCERR_SCOPENEST, m->parse, line, ppos, "%s broken", mdoc_macronames[tok]); if (n && ! rew_sub(MDOC_BODY, m, tok, line, ppos)) return(0); /* Standard appending of delimiters. */ if (nl && ! append_delims(m, line, pos, buf)) return(0); /* Rewind scope, if applicable. */ if (n && ! rew_sub(MDOC_BLOCK, m, tok, line, ppos)) return(0); return(1); }
void in_line_eoln(MACRO_PROT_ARGS) { int la; char *p; struct roff_node *n; roff_elem_alloc(man, line, ppos, tok); n = man->last; for (;;) { if (buf[*pos] != '\0' && (tok == MAN_br || tok == MAN_fi || tok == MAN_nf)) { mandoc_vmsg(MANDOCERR_ARG_SKIP, man->parse, line, *pos, "%s %s", man_macronames[tok], buf + *pos); break; } if (buf[*pos] != '\0' && man->last != n && (tok == MAN_PD || tok == MAN_ft || tok == MAN_sp)) { mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse, line, *pos, "%s ... %s", man_macronames[tok], buf + *pos); break; } la = *pos; if ( ! man_args(man, line, pos, buf, &p)) break; if (man_macros[tok].flags & MAN_JOIN && man->last->type == ROFFT_TEXT) roff_word_append(man, p); else roff_word_alloc(man, line, la, p); } /* * Append MAN_EOS in case the last snipped argument * ends with a dot, e.g. `.IR syslog (3).' */ if (n != man->last && mandoc_eos(man->last->string, strlen(man->last->string))) man->last->flags |= MAN_EOS; /* * If no arguments are specified and this is MAN_SCOPED (i.e., * next-line scoped), then set our mode to indicate that we're * waiting for terms to load into our context. */ if (n == man->last && man_macros[tok].flags & MAN_SCOPED) { assert( ! (man_macros[tok].flags & MAN_NSCOPED)); man->flags |= MAN_ELINE; return; } assert(man->last->type != ROFFT_ROOT); man->next = ROFF_NEXT_SIBLING; /* Rewind our element scope. */ for ( ; man->last; man->last = man->last->parent) { man_state(man, man->last); if (man->last == n) break; } }
static int man_ptext(struct roff_man *man, int line, char *buf, int offs) { int i; /* Literal free-form text whitespace is preserved. */ if (man->flags & MAN_LITERAL) { roff_word_alloc(man, line, offs, buf + offs); man_descope(man, line, offs); return 1; } for (i = offs; buf[i] == ' '; i++) /* Skip leading whitespace. */ ; /* * Blank lines are ignored in next line scope and right * after headings but add a single vertical space elsewhere. */ if (buf[i] == '\0') { if (man->flags & (MAN_ELINE | MAN_BLINE)) mandoc_msg(MANDOCERR_BLK_BLANK, man->parse, line, 0, NULL); else if (man->last->tok != MAN_SH && man->last->tok != MAN_SS) { roff_elem_alloc(man, line, offs, ROFF_sp); man->next = ROFF_NEXT_SIBLING; } return 1; } /* * Warn if the last un-escaped character is whitespace. Then * strip away the remaining spaces (tabs stay!). */ i = (int)strlen(buf); assert(i); if (' ' == buf[i - 1] || '\t' == buf[i - 1]) { if (i > 1 && '\\' != buf[i - 2]) mandoc_msg(MANDOCERR_SPACE_EOL, man->parse, line, i - 1, NULL); for (--i; i && ' ' == buf[i]; i--) /* Spin back to non-space. */ ; /* Jump ahead of escaped whitespace. */ i += '\\' == buf[i] ? 2 : 1; buf[i] = '\0'; } roff_word_alloc(man, line, offs, buf + offs); /* * 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(i); if (mandoc_eos(buf, (size_t)i)) man->last->flags |= NODE_EOS; man_descope(man, line, offs); return 1; }
/* ARGSUSED */ int in_line_eoln(MACRO_PROT_ARGS) { int la; char *p; struct man_node *n; if ( ! man_elem_alloc(man, line, ppos, tok)) return(0); n = man->last; for (;;) { la = *pos; if ( ! man_args(man, line, pos, buf, &p)) break; if ( ! man_word_alloc(man, line, la, p)) return(0); } /* * Append MAN_EOS in case the last snipped argument * ends with a dot, e.g. `.IR syslog (3).' */ if (n != man->last && mandoc_eos(man->last->string, strlen(man->last->string))) man->last->flags |= MAN_EOS; /* * If no arguments are specified and this is MAN_SCOPED (i.e., * next-line scoped), then set our mode to indicate that we're * waiting for terms to load into our context. */ if (n == man->last && MAN_SCOPED & man_macros[tok].flags) { assert( ! (MAN_NSCOPED & man_macros[tok].flags)); man->flags |= MAN_ELINE; return(1); } /* Set ignorable context, if applicable. */ if (MAN_NSCOPED & man_macros[tok].flags) { assert( ! (MAN_SCOPED & man_macros[tok].flags)); man->flags |= MAN_ILINE; } assert(MAN_ROOT != man->last->type); man->next = MAN_NEXT_SIBLING; /* * Rewind our element scope. Note that when TH is pruned, we'll * be back at the root, so make sure that we don't clobber as * its sibling. */ for ( ; man->last; man->last = man->last->parent) { if (man->last == n) break; if (man->last->type == MAN_ROOT) break; if ( ! man_valid_post(man)) return(0); } assert(man->last); /* * Same here regarding whether we're back at the root. */ if (man->last->type != MAN_ROOT && ! man_valid_post(man)) return(0); return(1); }