=================================================================== RCS file: /cvs/mandoc/roff.c,v retrieving revision 1.354 retrieving revision 1.355 diff -u -p -r1.354 -r1.355 --- mandoc/roff.c 2018/12/20 03:41:54 1.354 +++ mandoc/roff.c 2018/12/21 17:15:19 1.355 @@ -1,4 +1,4 @@ -/* $Id: roff.c,v 1.354 2018/12/20 03:41:54 schwarze Exp $ */ +/* $Id: roff.c,v 1.355 2018/12/21 17:15:19 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 @@ -191,6 +199,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 *); @@ -219,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); @@ -1142,12 +1151,12 @@ 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 */ @@ -1181,7 +1190,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 != '#') @@ -1223,7 +1232,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'; @@ -1264,11 +1273,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; @@ -1277,6 +1291,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; } @@ -1556,10 +1573,11 @@ roff_res(struct roff *r, struct buf *buf, int ln, int * or to the NUL byte terminating the argument line. */ char * -mandoc_getarg(char **cpp, int ln, int *pos) +roff_getarg(struct roff *r, char **cpp, int ln, int *pos) { - char *start, *cp; - int quoted, pairs, white; + struct buf buf; + char *cp, *start; + int newesc, pairs, quoted, white; /* Quoting can only start with a new word. */ start = *cpp; @@ -1569,8 +1587,7 @@ mandoc_getarg(char **cpp, int ln, int *pos) start++; } - pairs = 0; - white = 0; + newesc = pairs = white = 0; for (cp = start; '\0' != *cp; cp++) { /* @@ -1589,8 +1606,12 @@ mandoc_getarg(char **cpp, int ln, int *pos) case 'a': case 't': cp[-pairs] = '\t'; - /* FALLTHROUGH */ + pairs++; + cp++; + break; case '\\': + newesc = 1; + cp[-pairs] = ASCII_ESC; pairs++; cp++; break; @@ -1639,7 +1660,18 @@ mandoc_getarg(char **cpp, int ln, int *pos) if ('\0' == *cp && (white || ' ' == cp[-1])) mandoc_msg(MANDOCERR_SPACE_EOL, ln, *pos, NULL); - return start; + 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; } @@ -1737,7 +1769,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); @@ -3771,7 +3803,7 @@ roff_userdef(ROFF_ARGS) ctx->argv = mandoc_reallocarray(ctx->argv, ctx->argsz, sizeof(*ctx->argv)); } - arg = mandoc_getarg(&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; @@ -3784,6 +3816,7 @@ roff_userdef(ROFF_ARGS) *dst++ = *ap; } *dst = '\0'; + free(arg); } /* Replace the macro invocation by the macro definition. */ @@ -4133,7 +4166,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);