summaryrefslogtreecommitdiff
path: root/usr.bin/mandoc/roff.c
diff options
context:
space:
mode:
authorIngo Schwarze <schwarze@cvs.openbsd.org>2014-04-08 01:36:51 +0000
committerIngo Schwarze <schwarze@cvs.openbsd.org>2014-04-08 01:36:51 +0000
commit149824b7264545f9974f31e6f67169fbb8b4c5aa (patch)
treea4346fea6ca694967bbb166496d2e86ff0835cc0 /usr.bin/mandoc/roff.c
parentd2ccc22d72cf638e29f0322fbde6a4c0a792d006 (diff)
Fully implement the \B (validate numerical expression) and
partially implement the \w (measure text width) escape sequence in a way that makes them usable in numerical expressions and in conditional requests, similar to how \n (interpolate number register) and \* (expand user-defined string) are implemented. This lets mandoc(1) handle the baroque low-level roff code found at the beginning of the ggrep(1) manual. Thanks to pascal@ for the report.
Diffstat (limited to 'usr.bin/mandoc/roff.c')
-rw-r--r--usr.bin/mandoc/roff.c99
1 files changed, 71 insertions, 28 deletions
diff --git a/usr.bin/mandoc/roff.c b/usr.bin/mandoc/roff.c
index d80d6cf5986..1320bf7d111 100644
--- a/usr.bin/mandoc/roff.c
+++ b/usr.bin/mandoc/roff.c
@@ -1,4 +1,4 @@
-/* $Id: roff.c,v 1.78 2014/04/07 21:00:00 schwarze Exp $ */
+/* $Id: roff.c,v 1.79 2014/04/08 01:36:50 schwarze Exp $ */
/*
* Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010-2014 Ingo Schwarze <schwarze@openbsd.org>
@@ -481,9 +481,9 @@ 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)
@@ -499,6 +499,9 @@ roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos)
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;
@@ -521,16 +524,19 @@ roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos)
continue;
}
- /*
- * Everything except user-defined strings and number
- * registers is only checked, not expanded.
- */
+ /* 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;
@@ -553,20 +559,27 @@ roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos)
* Save a pointer to the name.
*/
- switch (*++cp) {
- case ('\0'):
- continue;
- 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;
@@ -578,10 +591,12 @@ roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos)
(MANDOCERR_BADESCAPE,
r->parse, ln,
(int)(stesc - *bufp), NULL);
- continue;
+ break;
}
- if (0 == maxl && ']' == *cp)
+ if (0 == maxl && *cp == term) {
+ cp++;
break;
+ }
}
/*
@@ -589,11 +604,26 @@ roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos)
* 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
@@ -610,7 +640,7 @@ roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos)
strlcpy(nbuf, *bufp, (size_t)(stesc - *bufp + 1));
strlcat(nbuf, res, *szp);
- strlcat(nbuf, cp + (maxl ? 0 : 1), *szp);
+ strlcat(nbuf, cp, *szp);
/* Prepare for the next replacement. */
@@ -1122,7 +1152,10 @@ 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] == '-';
@@ -1426,9 +1459,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);
}
@@ -1474,6 +1514,9 @@ roff_evalnum(const char *v, int *pos, int *res, int skipwhite)
while (isspace((unsigned char)v[*pos]))
(*pos)++;
+ if (NULL == res)
+ continue;
+
switch (operator) {
case ('+'):
*res += operand2;