=================================================================== RCS file: /cvs/mandoc/roff.c,v retrieving revision 1.350 retrieving revision 1.358 diff -u -p -r1.350 -r1.358 --- mandoc/roff.c 2018/12/14 05:18:03 1.350 +++ mandoc/roff.c 2018/12/31 07:08:12 1.358 @@ -1,4 +1,4 @@ -/* $Id: roff.c,v 1.350 2018/12/14 05:18:03 schwarze Exp $ */ +/* $Id: roff.c,v 1.358 2018/12/31 07:08:12 schwarze Exp $ */ /* * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons * Copyright (c) 2010-2015, 2017, 2018 Ingo Schwarze @@ -38,6 +38,14 @@ #include "tbl_parse.h" #include "eqn_parse.h" +/* + * ASCII_ESC is used to signal from roff_getarg() to roff_expand() + * that an escape sequence resulted from copy-in processing and + * needs to be checked or interpolated. As it is used nowhere + * else, it is defined here rather than in a header file. + */ +#define ASCII_ESC 27 + /* Maximum number of string expansions per line, to break infinite loops. */ #define EXPAND_LIMIT 1000 @@ -98,7 +106,6 @@ struct mctx { }; struct roff { - struct mparse *parse; /* parse point */ struct roff_man *man; /* mdoc or man parser */ struct roffnode *last; /* leaf of stack */ struct mctx *mstack; /* stack of macro contexts */ @@ -174,7 +181,6 @@ static int roff_als(ROFF_ARGS); static int roff_block(ROFF_ARGS); static int roff_block_text(ROFF_ARGS); static int roff_block_sub(ROFF_ARGS); -static int roff_br(ROFF_ARGS); static int roff_cblock(ROFF_ARGS); static int roff_cc(ROFF_ARGS); static int roff_ccond(struct roff *, int, int); @@ -192,6 +198,8 @@ static int roff_evalnum(struct roff *, int, static int roff_evalpar(struct roff *, int, const char *, int *, int *, int); static int roff_evalstrcond(const char *, int *); +static int roff_expand(struct roff *, struct buf *, + int, int, char); static void roff_free1(struct roff *); static void roff_freereg(struct roffreg *); static void roff_freestr(struct roffkv *); @@ -212,6 +220,7 @@ static int roff_line_ignore(ROFF_ARGS); static void roff_man_alloc1(struct roff_man *); static void roff_man_free1(struct roff_man *); static int roff_manyarg(ROFF_ARGS); +static int roff_noarg(ROFF_ARGS); static int roff_nop(ROFF_ARGS); static int roff_nr(ROFF_ARGS); static int roff_onearg(ROFF_ARGS); @@ -220,7 +229,6 @@ static enum roff_tok roff_parse(struct roff *, char * static int roff_parsetext(struct roff *, struct buf *, int, int *); static int roff_renamed(ROFF_ARGS); -static int roff_res(struct roff *, struct buf *, int, int); static int roff_return(ROFF_ARGS); static int roff_rm(ROFF_ARGS); static int roff_rn(ROFF_ARGS); @@ -249,8 +257,9 @@ static int roff_userdef(ROFF_ARGS); #define ROFFNUM_WHITE (1 << 1) /* Skip whitespace in roff_evalnum(). */ const char *__roff_name[MAN_MAX + 1] = { - "br", "ce", "ft", "ll", - "mc", "po", "rj", "sp", + "br", "ce", "fi", "ft", + "ll", "mc", "nf", + "po", "rj", "sp", "ta", "ti", NULL, "ab", "ad", "af", "aln", "als", "am", "am1", "ami", @@ -349,7 +358,6 @@ const char *__roff_name[MAN_MAX + 1] = { "HP", "SM", "SB", "BI", "IB", "BR", "RB", "R", "B", "I", "IR", "RI", - "nf", "fi", "RE", "RS", "DT", "UC", "PD", "AT", "in", "SY", "YS", "OP", @@ -359,11 +367,13 @@ const char *__roff_name[MAN_MAX + 1] = { const char *const *roff_name = __roff_name; static struct roffmac roffs[TOKEN_NONE] = { - { roff_br, NULL, NULL, 0 }, /* br */ + { roff_noarg, NULL, NULL, 0 }, /* br */ { roff_onearg, NULL, NULL, 0 }, /* ce */ + { roff_noarg, NULL, NULL, 0 }, /* fi */ { roff_onearg, NULL, NULL, 0 }, /* ft */ { roff_onearg, NULL, NULL, 0 }, /* ll */ { roff_onearg, NULL, NULL, 0 }, /* mc */ + { roff_noarg, NULL, NULL, 0 }, /* nf */ { roff_onearg, NULL, NULL, 0 }, /* po */ { roff_onearg, NULL, NULL, 0 }, /* rj */ { roff_onearg, NULL, NULL, 0 }, /* sp */ @@ -393,7 +403,7 @@ static struct roffmac roffs[TOKEN_NONE] = { { roff_unsupp, NULL, NULL, 0 }, /* break */ { roff_line_ignore, NULL, NULL, 0 }, /* breakchar */ { roff_line_ignore, NULL, NULL, 0 }, /* brnl */ - { roff_br, NULL, NULL, 0 }, /* brp */ + { roff_noarg, NULL, NULL, 0 }, /* brp */ { roff_line_ignore, NULL, NULL, 0 }, /* brpnl */ { roff_unsupp, NULL, NULL, 0 }, /* c2 */ { roff_cc, NULL, NULL, 0 }, /* cc */ @@ -780,12 +790,11 @@ roff_free(struct roff *r) } struct roff * -roff_alloc(struct mparse *parse, int options) +roff_alloc(int options) { struct roff *r; r = mandoc_calloc(1, sizeof(struct roff)); - r->parse = parse; r->reqtab = roffhash_alloc(0, ROFF_RENAMED); r->options = options; r->format = options & (MPARSE_MDOC | MPARSE_MAN); @@ -800,9 +809,8 @@ roff_alloc(struct mparse *parse, int options) static void roff_man_free1(struct roff_man *man) { - - if (man->first != NULL) - roff_node_delete(man, man->first); + if (man->meta.first != NULL) + roff_node_delete(man, man->meta.first); free(man->meta.msec); free(man->meta.vol); free(man->meta.os); @@ -810,27 +818,33 @@ roff_man_free1(struct roff_man *man) free(man->meta.title); free(man->meta.name); free(man->meta.date); + free(man->meta.sodest); } -static void -roff_man_alloc1(struct roff_man *man) +void +roff_state_reset(struct roff_man *man) { - - memset(&man->meta, 0, sizeof(man->meta)); - man->first = mandoc_calloc(1, sizeof(*man->first)); - man->first->type = ROFFT_ROOT; - man->last = man->first; + man->last = man->meta.first; man->last_es = NULL; man->flags = 0; - man->macroset = MACROSET_NONE; man->lastsec = man->lastnamed = SEC_NONE; man->next = ROFF_NEXT_CHILD; + roff_setreg(man->roff, "nS", 0, '='); } +static void +roff_man_alloc1(struct roff_man *man) +{ + memset(&man->meta, 0, sizeof(man->meta)); + man->meta.first = mandoc_calloc(1, sizeof(*man->meta.first)); + man->meta.first->type = ROFFT_ROOT; + man->meta.macroset = MACROSET_NONE; + roff_state_reset(man); +} + void roff_man_reset(struct roff_man *man) { - roff_man_free1(man); roff_man_alloc1(man); } @@ -838,19 +852,16 @@ roff_man_reset(struct roff_man *man) void roff_man_free(struct roff_man *man) { - roff_man_free1(man); free(man); } struct roff_man * -roff_man_alloc(struct roff *roff, struct mparse *parse, - const char *os_s, int quick) +roff_man_alloc(struct roff *roff, const char *os_s, int quick) { struct roff_man *man; man = mandoc_calloc(1, sizeof(*man)); - man->parse = parse; man->roff = roff; man->os_s = os_s; man->quick = quick; @@ -1015,7 +1026,7 @@ roff_addtbl(struct roff_man *man, int line, struct tbl struct roff_node *n; struct tbl_span *span; - if (man->macroset == MACROSET_MAN) + if (man->meta.macroset == MACROSET_MAN) man_breakscope(man, ROFF_TS); while ((span = tbl_span(tbl)) != NULL) { n = roff_node_alloc(man, line, 0, ROFFT_TBL, TOKEN_NONE); @@ -1059,8 +1070,8 @@ roff_node_unlink(struct roff_man *man, struct roff_nod man->next = ROFF_NEXT_SIBLING; } } - if (man->first == n) - man->first = NULL; + if (man->meta.first == n) + man->meta.first = NULL; } void @@ -1146,18 +1157,19 @@ deroff(char **dest, const struct roff_node *n) /* --- main functions of the roff parser ---------------------------------- */ /* - * In the current line, expand escape sequences that tend to get - * used in numerical expressions and conditional requests. - * Also check the syntax of the remaining escape sequences. + * In the current line, expand escape sequences that produce parsable + * input text. Also check the syntax of the remaining escape sequences, + * which typically produce output glyphs or change formatter state. */ static int -roff_res(struct roff *r, struct buf *buf, int ln, int pos) +roff_expand(struct roff *r, struct buf *buf, int ln, int pos, char newesc) { struct mctx *ctx; /* current macro call context */ char ubuf[24]; /* buffer to print the number */ struct roff_node *n; /* used for header comments */ const char *start; /* start of the string to process */ char *stesc; /* start of an escape sequence ('\\') */ + const char *esct; /* type of esccape sequence */ char *ep; /* end of comment string */ const char *stnam; /* start of the name, after "[(*" */ const char *cp; /* end of the name, e.g. before ']' */ @@ -1167,7 +1179,6 @@ roff_res(struct roff *r, struct buf *buf, int ln, int size_t naml; /* actual length of the escape name */ size_t asz; /* length of the replacement */ size_t rsz; /* length of the rest of the string */ - enum mandoc_esc esc; /* type of the escape sequence */ int inaml; /* length returned from mandoc_escape() */ int expand_count; /* to avoid infinite loops */ int npos; /* position in numeric expression */ @@ -1176,6 +1187,7 @@ roff_res(struct roff *r, struct buf *buf, int ln, int int done; /* no more input available */ int deftype; /* type of definition to paste */ int rcsid; /* kind of RCS id seen */ + enum mandocerr err; /* for escape sequence problems */ char sign; /* increment number register */ char term; /* character terminating the escape */ @@ -1184,7 +1196,7 @@ roff_res(struct roff *r, struct buf *buf, int ln, int done = 0; start = buf->buf + pos; for (stesc = buf->buf + pos; *stesc != '\0'; stesc++) { - if (stesc[0] != r->escape || stesc[1] == '\0') + if (stesc[0] != newesc || stesc[1] == '\0') continue; stesc++; if (*stesc != '"' && *stesc != '#') @@ -1226,7 +1238,7 @@ roff_res(struct roff *r, struct buf *buf, int ln, int * in the syntax tree. */ - if (r->format == 0) { + if (newesc != ASCII_ESC && r->format == 0) { while (*ep == ' ' || *ep == '\t') ep--; ep[1] = '\0'; @@ -1267,11 +1279,16 @@ roff_res(struct roff *r, struct buf *buf, int ln, int expand_count = 0; while (stesc >= start) { + if (*stesc != newesc) { - /* Search backwards for the next backslash. */ + /* + * If we have a non-standard escape character, + * escape literal backslashes because all + * processing in subsequent functions uses + * the standard escaping rules. + */ - if (*stesc != r->escape) { - if (*stesc == '\\') { + if (newesc != ASCII_ESC && *stesc == '\\') { *stesc = '\0'; buf->sz = mandoc_asprintf(&nbuf, "%s\\e%s", buf->buf, stesc + 1) + 1; @@ -1280,6 +1297,9 @@ roff_res(struct roff *r, struct buf *buf, int ln, int free(buf->buf); buf->buf = nbuf; } + + /* Search backwards for the next escape. */ + stesc--; continue; } @@ -1308,7 +1328,10 @@ roff_res(struct roff *r, struct buf *buf, int ln, int term = '\0'; cp = stesc + 1; - switch (*cp) { + if (*cp == 'E') + cp++; + esct = cp; + switch (*esct) { case '*': case '$': res = NULL; @@ -1324,12 +1347,26 @@ roff_res(struct roff *r, struct buf *buf, int ln, int res = ubuf; break; default: - esc = mandoc_escape(&cp, &stnam, &inaml); - if (esc == ESCAPE_ERROR || - (esc == ESCAPE_SPECIAL && - mchars_spec2cp(stnam, inaml) < 0)) - mandoc_msg(MANDOCERR_ESC_BAD, - ln, (int)(stesc - buf->buf), + err = MANDOCERR_OK; + switch(mandoc_escape(&cp, &stnam, &inaml)) { + case ESCAPE_SPECIAL: + if (mchars_spec2cp(stnam, inaml) >= 0) + break; + /* FALLTHROUGH */ + case ESCAPE_ERROR: + err = MANDOCERR_ESC_BAD; + break; + case ESCAPE_UNDEF: + err = MANDOCERR_ESC_UNDEF; + break; + case ESCAPE_UNSUPP: + err = MANDOCERR_ESC_UNSUPP; + break; + default: + break; + } + if (err != MANDOCERR_OK) + mandoc_msg(err, ln, (int)(stesc - buf->buf), "%.*s", (int)(cp - stesc), stesc); stesc--; continue; @@ -1386,7 +1423,7 @@ roff_res(struct roff *r, struct buf *buf, int ln, int cp++; break; } - if (*cp++ != '\\' || stesc[1] != 'w') { + if (*cp++ != '\\' || *esct != 'w') { naml++; continue; } @@ -1394,6 +1431,7 @@ roff_res(struct roff *r, struct buf *buf, int ln, int case ESCAPE_SPECIAL: case ESCAPE_UNICODE: case ESCAPE_NUMBERED: + case ESCAPE_UNDEF: case ESCAPE_OVERSTRIKE: naml++; break; @@ -1407,7 +1445,7 @@ roff_res(struct roff *r, struct buf *buf, int ln, int * undefined, resume searching for escapes. */ - switch (stesc[1]) { + switch (*esct) { case '*': if (arg_complete) { deftype = ROFFDEF_USER | ROFFDEF_PRE; @@ -1434,15 +1472,15 @@ roff_res(struct roff *r, struct buf *buf, int ln, int break; } ctx = r->mstack + r->mstackpos; - npos = stesc[2] - '1'; + npos = esct[1] - '1'; if (npos >= 0 && npos <= 8) { res = npos < ctx->argc ? ctx->argv[npos] : ""; break; } - if (stesc[2] == '*') + if (esct[1] == '*') quote_args = 0; - else if (stesc[2] == '@') + else if (esct[1] == '@') quote_args = 1; else { mandoc_msg(MANDOCERR_ARG_NONUM, ln, @@ -1504,7 +1542,7 @@ roff_res(struct roff *r, struct buf *buf, int ln, int } if (res == NULL) { - if (stesc[1] == '*') + if (*esct == '*') mandoc_msg(MANDOCERR_STR_UNDEF, ln, (int)(stesc - buf->buf), "%.*s", (int)naml, stnam); @@ -1532,6 +1570,118 @@ roff_res(struct roff *r, struct buf *buf, int ln, int } /* + * Parse a quoted or unquoted roff-style request or macro argument. + * Return a pointer to the parsed argument, which is either the original + * pointer or advanced by one byte in case the argument is quoted. + * NUL-terminate the argument in place. + * Collapse pairs of quotes inside quoted arguments. + * Advance the argument pointer to the next argument, + * or to the NUL byte terminating the argument line. + */ +char * +roff_getarg(struct roff *r, char **cpp, int ln, int *pos) +{ + struct buf buf; + char *cp, *start; + int newesc, pairs, quoted, white; + + /* Quoting can only start with a new word. */ + start = *cpp; + quoted = 0; + if ('"' == *start) { + quoted = 1; + start++; + } + + newesc = pairs = white = 0; + for (cp = start; '\0' != *cp; cp++) { + + /* + * Move the following text left + * after quoted quotes and after "\\" and "\t". + */ + if (pairs) + cp[-pairs] = cp[0]; + + if ('\\' == cp[0]) { + /* + * In copy mode, translate double to single + * backslashes and backslash-t to literal tabs. + */ + switch (cp[1]) { + case 'a': + case 't': + cp[-pairs] = '\t'; + pairs++; + cp++; + break; + case '\\': + newesc = 1; + cp[-pairs] = ASCII_ESC; + pairs++; + cp++; + break; + case ' ': + /* Skip escaped blanks. */ + if (0 == quoted) + cp++; + break; + default: + break; + } + } else if (0 == quoted) { + if (' ' == cp[0]) { + /* Unescaped blanks end unquoted args. */ + white = 1; + break; + } + } else if ('"' == cp[0]) { + if ('"' == cp[1]) { + /* Quoted quotes collapse. */ + pairs++; + cp++; + } else { + /* Unquoted quotes end quoted args. */ + quoted = 2; + break; + } + } + } + + /* Quoted argument without a closing quote. */ + if (1 == quoted) + mandoc_msg(MANDOCERR_ARG_QUOTE, ln, *pos, NULL); + + /* NUL-terminate this argument and move to the next one. */ + if (pairs) + cp[-pairs] = '\0'; + if ('\0' != *cp) { + *cp++ = '\0'; + while (' ' == *cp) + cp++; + } + *pos += (int)(cp - start) + (quoted ? 1 : 0); + *cpp = cp; + + if ('\0' == *cp && (white || ' ' == cp[-1])) + mandoc_msg(MANDOCERR_SPACE_EOL, ln, *pos, NULL); + + start = mandoc_strdup(start); + if (newesc == 0) + return start; + + buf.buf = start; + buf.sz = strlen(start) + 1; + buf.next = NULL; + if (roff_expand(r, &buf, ln, 0, ASCII_ESC) & ROFF_IGN) { + free(buf.buf); + buf.buf = mandoc_strdup(""); + } + return buf.buf; +} + + +/* * Process text streams. */ static int @@ -1625,7 +1775,7 @@ roff_parseln(struct roff *r, int ln, struct buf *buf, /* Expand some escape sequences. */ - e = roff_res(r, buf, ln, pos); + e = roff_expand(r, buf, ln, pos, r->escape); if ((e & ROFF_MASK) == ROFF_IGN) return e; assert(e == ROFF_CONT); @@ -3137,7 +3287,7 @@ roff_EQ(ROFF_ARGS) { struct roff_node *n; - if (r->man->macroset == MACROSET_MAN) + if (r->man->meta.macroset == MACROSET_MAN) man_breakscope(r->man, ROFF_EQ); n = roff_node_alloc(r->man, ln, ppos, ROFFT_EQN, TOKEN_NONE); if (ln > r->man->last->line) @@ -3148,7 +3298,7 @@ roff_EQ(ROFF_ARGS) assert(r->eqn == NULL); if (r->last_eqn == NULL) - r->last_eqn = eqn_alloc(r->parse); + r->last_eqn = eqn_alloc(); else eqn_reset(r->last_eqn); r->eqn = r->last_eqn; @@ -3182,7 +3332,7 @@ roff_TS(ROFF_ARGS) mandoc_msg(MANDOCERR_BLK_BROKEN, ln, ppos, "TS breaks TS"); tbl_end(r->tbl, 0); } - r->tbl = tbl_alloc(ppos, ln, r->parse, r->last_tbl); + r->tbl = tbl_alloc(ppos, ln, r->last_tbl); if (r->last_tbl == NULL) r->first_tbl = r->tbl; r->last_tbl = r->tbl; @@ -3190,6 +3340,26 @@ roff_TS(ROFF_ARGS) } static int +roff_noarg(ROFF_ARGS) +{ + if (r->man->flags & (MAN_BLINE | MAN_ELINE)) + man_breakscope(r->man, tok); + if (tok == ROFF_brp) + tok = ROFF_br; + roff_elem_alloc(r->man, ln, ppos, tok); + if (buf->buf[pos] != '\0') + mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos, + "%s %s", roff_name[tok], buf->buf + pos); + if (tok == ROFF_nf) + r->man->flags |= ROFF_NOFILL; + else if (tok == ROFF_fi) + r->man->flags &= ~ROFF_NOFILL; + r->man->last->flags |= NODE_LINE | NODE_VALID | NODE_ENDED; + r->man->next = ROFF_NEXT_SIBLING; + return ROFF_IGN; +} + +static int roff_onearg(ROFF_ARGS) { struct roff_node *n; @@ -3300,20 +3470,6 @@ roff_als(ROFF_ARGS) } static int -roff_br(ROFF_ARGS) -{ - if (r->man->flags & (MAN_BLINE | MAN_ELINE)) - man_breakscope(r->man, ROFF_br); - roff_elem_alloc(r->man, ln, ppos, ROFF_br); - if (buf->buf[pos] != '\0') - mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos, - "%s %s", roff_name[tok], buf->buf + pos); - r->man->last->flags |= NODE_LINE | NODE_VALID | NODE_ENDED; - r->man->next = ROFF_NEXT_SIBLING; - return ROFF_IGN; -} - -static int roff_cc(ROFF_ARGS) { const char *p; @@ -3659,7 +3815,7 @@ roff_userdef(ROFF_ARGS) ctx->argv = mandoc_reallocarray(ctx->argv, ctx->argsz, sizeof(*ctx->argv)); } - arg = mandoc_getarg(r->parse, &src, ln, &pos); + arg = roff_getarg(r, &src, ln, &pos); sz = 1; /* For the terminating NUL. */ for (ap = arg; *ap != '\0'; ap++) sz += *ap == '"' ? 4 : 1; @@ -3672,6 +3828,7 @@ roff_userdef(ROFF_ARGS) *dst++ = *ap; } *dst = '\0'; + free(arg); } /* Replace the macro invocation by the macro definition. */ @@ -3876,7 +4033,7 @@ roff_getstrn(struct roff *r, const char *name, size_t break; } } - if (r->man->macroset != MACROSET_MAN) { + if (r->man->meta.macroset != MACROSET_MAN) { for (tok = MDOC_Dd; tok < MDOC_MAX; tok++) { if (strncmp(name, roff_name[tok], len) != 0 || roff_name[tok][len] != '\0') @@ -3890,7 +4047,7 @@ roff_getstrn(struct roff *r, const char *name, size_t } } } - if (r->man->macroset != MACROSET_MDOC) { + if (r->man->meta.macroset != MACROSET_MDOC) { for (tok = MAN_TH; tok < MAN_MAX; tok++) { if (strncmp(name, roff_name[tok], len) != 0 || roff_name[tok][len] != '\0') @@ -4021,7 +4178,7 @@ roff_strdup(const struct roff *r, const char *p) /* * We bail out on bad escapes. * No need to warn: we already did so when - * roff_res() was called. + * roff_expand() was called. */ sz = (int)(p - pp); res = mandoc_realloc(res, ssz + sz + 1);