=================================================================== RCS file: /cvs/mandoc/roff.c,v retrieving revision 1.204 retrieving revision 1.206 diff -u -p -r1.204 -r1.206 --- mandoc/roff.c 2014/04/07 15:07:13 1.204 +++ mandoc/roff.c 2014/04/08 01:37:27 1.206 @@ -1,4 +1,4 @@ -/* $Id: roff.c,v 1.204 2014/04/07 15:07:13 schwarze Exp $ */ +/* $Id: roff.c,v 1.206 2014/04/08 01:37:27 schwarze Exp $ */ /* * Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons * Copyright (c) 2010-2014 Ingo Schwarze @@ -485,57 +485,77 @@ roff_alloc(struct mparse *parse, int options) } /* - * In the current line, expand user-defined strings ("\*") - * and references to number registers ("\n"). - * Also check the syntax of other escape sequences. + * 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. */ static enum rofferr roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos) { char ubuf[12]; /* buffer to print the number */ + const char *start; /* start of the string to process */ const char *stesc; /* start of an escape sequence ('\\') */ const char *stnam; /* start of the name, after "[(*" */ const char *cp; /* end of the name, e.g. before ']' */ const char *res; /* the string to be substituted */ char *nbuf; /* new buffer to copy bufp to */ - size_t nsz; /* size of the new buffer */ size_t maxl; /* expected length of the escape name */ size_t naml; /* actual length of the escape name */ + size_t ressz; /* size of the replacement string */ int expand_count; /* to avoid infinite loops */ + int npos; /* position in numeric expression */ + int irc; /* return code from roff_evalnum() */ + char term; /* character terminating the escape */ expand_count = 0; + start = *bufp + pos; + stesc = strchr(start, '\0') - 1; + while (stesc-- > start) { -again: - cp = *bufp + pos; - while (NULL != (cp = strchr(cp, '\\'))) { - stesc = cp++; + /* Search backwards for the next backslash. */ - /* - * The second character must be an asterisk or an n. - * If it isn't, skip it anyway: It is escaped, - * so it can't start another escape sequence. - */ + if ('\\' != *stesc) + continue; - if ('\0' == *cp) - return(ROFF_CONT); + /* If it is escaped, skip it. */ + for (cp = stesc - 1; cp >= start; cp--) + if ('\\' != *cp) + break; + + if (0 == (stesc - cp) % 2) { + stesc = cp; + continue; + } + + /* Decide whether to expand or to check only. */ + + term = '\0'; + cp = stesc + 1; switch (*cp) { case ('*'): res = NULL; break; + case ('B'): + /* FALLTHROUGH */ + case ('w'): + term = cp[1]; + /* FALLTHROUGH */ case ('n'): res = ubuf; break; default: - if (ESCAPE_ERROR != mandoc_escape(&cp, NULL, NULL)) - continue; - mandoc_msg - (MANDOCERR_BADESCAPE, r->parse, - ln, (int)(stesc - *bufp), NULL); - return(ROFF_CONT); + if (ESCAPE_ERROR == mandoc_escape(&cp, NULL, NULL)) + mandoc_msg(MANDOCERR_BADESCAPE, r->parse, + ln, (int)(stesc - *bufp), NULL); + continue; } - cp++; + if (EXPAND_LIMIT < ++expand_count) { + mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, + ln, (int)(stesc - *bufp), NULL); + return(ROFF_IGN); + } /* * The third character decides the length @@ -543,20 +563,27 @@ again: * Save a pointer to the name. */ - switch (*cp) { - case ('\0'): - return(ROFF_CONT); - case ('('): - cp++; - maxl = 2; - break; - case ('['): - cp++; + if ('\0' == term) { + switch (*++cp) { + case ('\0'): + maxl = 0; + break; + case ('('): + cp++; + maxl = 2; + break; + case ('['): + cp++; + term = ']'; + maxl = 0; + break; + default: + maxl = 1; + break; + } + } else { + cp += 2; maxl = 0; - break; - default: - maxl = 1; - break; } stnam = cp; @@ -568,10 +595,12 @@ again: (MANDOCERR_BADESCAPE, r->parse, ln, (int)(stesc - *bufp), NULL); - return(ROFF_CONT); + break; } - if (0 == maxl && ']' == *cp) + if (0 == maxl && *cp == term) { + cp++; break; + } } /* @@ -579,11 +608,26 @@ again: * undefined, resume searching for escapes. */ - if (NULL == res) + switch (stesc[1]) { + case ('*'): res = roff_getstrn(r, stnam, naml); - else + break; + case ('B'): + npos = 0; + irc = roff_evalnum(stnam, &npos, NULL, 0); + ubuf[0] = irc && stnam + npos + 1 == cp + ? '1' : '0'; + ubuf[1] = '\0'; + break; + case ('n'): snprintf(ubuf, sizeof(ubuf), "%d", roff_getregn(r, stnam, naml)); + break; + case ('w'): + snprintf(ubuf, sizeof(ubuf), "%d", + 24 * (int)naml); + break; + } if (NULL == res) { mandoc_msg @@ -591,29 +635,23 @@ again: ln, (int)(stesc - *bufp), NULL); res = ""; } + ressz = strlen(res); /* Replace the escape sequence by the string. */ - pos = stesc - *bufp; + *szp += ressz + 1; + nbuf = mandoc_malloc(*szp); - nsz = *szp + strlen(res) + 1; - nbuf = mandoc_malloc(nsz); - strlcpy(nbuf, *bufp, (size_t)(stesc - *bufp + 1)); - strlcat(nbuf, res, nsz); - strlcat(nbuf, cp + (maxl ? 0 : 1), nsz); + strlcat(nbuf, res, *szp); + strlcat(nbuf, cp, *szp); - free(*bufp); + /* Prepare for the next replacement. */ + start = nbuf + pos; + stesc = nbuf + (stesc - *bufp) + ressz; + free(*bufp); *bufp = nbuf; - *szp = nsz; - - if (EXPAND_LIMIT >= ++expand_count) - goto again; - - /* Just leave the string unexpanded. */ - mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, ln, pos, NULL); - return(ROFF_IGN); } return(ROFF_CONT); } @@ -1118,8 +1156,11 @@ roff_cond_text(ROFF_ARGS) static int roff_getnum(const char *v, int *pos, int *res) { - int p, n; + int myres, n, p; + if (NULL == res) + res = &myres; + p = *pos; n = v[p] == '-'; if (n) @@ -1422,9 +1463,16 @@ roff_evalpar(const char *v, int *pos, int *res) if ( ! roff_evalnum(v, pos, res, 1)) return(0); - /* If the trailing parenthesis is missing, ignore the error. */ + /* + * Omission of the closing parenthesis + * is an error in validation mode, + * but ignored in evaluation mode. + */ + if (')' == v[*pos]) (*pos)++; + else if (NULL == res) + return(0); return(1); } @@ -1469,6 +1517,9 @@ roff_evalnum(const char *v, int *pos, int *res, int sk if (skipwhite) while (isspace((unsigned char)v[*pos])) (*pos)++; + + if (NULL == res) + continue; switch (operator) { case ('+'):