summaryrefslogtreecommitdiffstats
path: root/roff.c
diff options
context:
space:
mode:
authorIngo Schwarze <schwarze@openbsd.org>2015-02-21 14:46:58 +0000
committerIngo Schwarze <schwarze@openbsd.org>2015-02-21 14:46:58 +0000
commit4c2af21a8a623f5d22f660bdcfff1e1c23b9bcd2 (patch)
tree7b23c9858176d195dda6a5f85d75b0b7b2665bf7 /roff.c
parentc61dac4b4477899e79fcb43ff7ac3ae460c8a724 (diff)
downloadmandoc-4c2af21a8a623f5d22f660bdcfff1e1c23b9bcd2.tar.gz
Escape quotes when expanding macro arguments.
This fixes a bug naddy@ found in plan9/rc(1).
Diffstat (limited to 'roff.c')
-rw-r--r--roff.c92
1 files changed, 76 insertions, 16 deletions
diff --git a/roff.c b/roff.c
index 1370a58b..be15f1ac 100644
--- a/roff.c
+++ b/roff.c
@@ -2653,14 +2653,16 @@ roff_so(ROFF_ARGS)
static enum rofferr
roff_userdef(ROFF_ARGS)
{
- const char *arg[9];
+ const char *arg[9], *ap;
char *cp, *n1, *n2;
int i;
+ size_t asz, rsz;
/*
* Collect pointers to macro argument strings
* and NUL-terminate them.
*/
+
cp = buf->buf + pos;
for (i = 0; i < 9; i++)
arg[i] = *cp == '\0' ? "" :
@@ -2669,31 +2671,89 @@ roff_userdef(ROFF_ARGS)
/*
* Expand macro arguments.
*/
- buf->sz = 0;
- n1 = cp = mandoc_strdup(r->current_string);
- while ((cp = strstr(cp, "\\$")) != NULL) {
- i = cp[2] - '1';
- if (0 > i || 8 < i) {
- /* Not an argument invocation. */
- cp += 2;
+
+ buf->sz = strlen(r->current_string) + 1;
+ n1 = cp = mandoc_malloc(buf->sz);
+ memcpy(n1, r->current_string, buf->sz);
+ while (*cp != '\0') {
+
+ /* Scan ahead for the next argument invocation. */
+
+ if (*cp++ != '\\')
+ continue;
+ if (*cp++ != '$')
continue;
+ i = *cp - '1';
+ if (0 > i || 8 < i)
+ continue;
+ cp -= 2;
+
+ /*
+ * Determine the size of the expanded argument,
+ * taking escaping of quotes into account.
+ */
+
+ asz = 0;
+ for (ap = arg[i]; *ap != '\0'; ap++) {
+ asz++;
+ if (*ap == '"')
+ asz += 3;
+ }
+ if (asz != 3) {
+
+ /*
+ * Determine the size of the rest of the
+ * unexpanded macro, including the NUL.
+ */
+
+ rsz = buf->sz - (cp - n1) - 3;
+
+ /*
+ * When shrinking, move before
+ * releasing the storage.
+ */
+
+ if (asz < 3)
+ memmove(cp + asz, cp + 3, rsz);
+
+ /*
+ * Resize the storage for the macro
+ * and readjust the parse pointer.
+ */
+
+ buf->sz += asz - 3;
+ n2 = mandoc_realloc(n1, buf->sz);
+ cp = n2 + (cp - n1);
+ n1 = n2;
+
+ /*
+ * When growing, make room
+ * for the expanded argument.
+ */
+
+ if (asz > 3)
+ memmove(cp + asz, cp + 3, rsz);
+ }
+
+ /* Copy the expanded argument, escaping quotes. */
+
+ n2 = cp;
+ for (ap = arg[i]; *ap != '\0'; ap++) {
+ if (*ap == '"') {
+ memcpy(n2, "\\(dq", 4);
+ n2 += 4;
+ } else
+ *n2++ = *ap;
}
- *cp = '\0';
- buf->sz = mandoc_asprintf(&n2, "%s%s%s",
- n1, arg[i], cp + 3) + 1;
- cp = n2 + (cp - n1);
- free(n1);
- n1 = n2;
}
/*
* Replace the macro invocation
* by the expanded macro.
*/
+
free(buf->buf);
buf->buf = n1;
- if (buf->sz == 0)
- buf->sz = strlen(buf->buf) + 1;
*offs = 0;
return(buf->sz > 1 && buf->buf[buf->sz - 2] == '\n' ?