diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 07a35333b1..28e595fe58 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1286,6 +1286,7 @@ virBufferContentAndReset; virBufferCurrentContent; virBufferError; virBufferEscape; +virBufferEscapeN; virBufferEscapeSexpr; virBufferEscapeShell; virBufferEscapeString; diff --git a/src/util/virbuffer.c b/src/util/virbuffer.c index d582e7dbec..41d541b321 100644 --- a/src/util/virbuffer.c +++ b/src/util/virbuffer.c @@ -33,6 +33,7 @@ #include "virbuffer.h" #include "viralloc.h" #include "virerror.h" +#include "virstring.h" /* If adding more fields, ensure to edit buf.h to match @@ -588,6 +589,101 @@ virBufferEscape(virBufferPtr buf, char escape, const char *toescape, VIR_FREE(escaped); } + +struct _virBufferEscapePair { + char escape; + char *toescape; +}; + + +/** + * virBufferEscapeN: + * @buf: the buffer to append to + * @format: a printf like format string but with only one %s parameter + * @str: the string argument which needs to be escaped + * @...: the variable list of escape pairs + * + * The variable list of arguments @... must be composed of + * 'char escape, char *toescape' pairs followed by NULL. + * + * This has the same functionality as virBufferEscape with the extension + * that allows to specify multiple pairs of chars that needs to be escaped. + */ +void +virBufferEscapeN(virBufferPtr buf, + const char *format, + const char *str, + ...) +{ + int len; + size_t i; + char *escaped = NULL; + char *out; + const char *cur; + struct _virBufferEscapePair escapeItem; + struct _virBufferEscapePair *escapeList = NULL; + size_t nescapeList = 0; + va_list ap; + + if ((format == NULL) || (buf == NULL) || (str == NULL)) + return; + + if (buf->error) + return; + + len = strlen(str); + + va_start(ap, str); + + while ((escapeItem.escape = va_arg(ap, int))) { + if (!(escapeItem.toescape = va_arg(ap, char *))) { + virBufferSetError(buf, errno); + goto cleanup; + } + + if (strcspn(str, escapeItem.toescape) == len) + continue; + + if (VIR_APPEND_ELEMENT_QUIET(escapeList, nescapeList, escapeItem) < 0) { + virBufferSetError(buf, errno); + goto cleanup; + } + } + + if (nescapeList == 0) { + virBufferAsprintf(buf, format, str); + goto cleanup; + } + + if (xalloc_oversized(2, len) || + VIR_ALLOC_N_QUIET(escaped, 2 * len + 1) < 0) { + virBufferSetError(buf, errno); + goto cleanup; + } + + cur = str; + out = escaped; + while (*cur != 0) { + for (i = 0; i < nescapeList; i++) { + if (strchr(escapeList[i].toescape, *cur)) { + *out++ = escapeList[i].escape; + break; + } + } + *out++ = *cur; + cur++; + } + *out = 0; + + virBufferAsprintf(buf, format, escaped); + + cleanup: + va_end(ap); + VIR_FREE(escapeList); + VIR_FREE(escaped); +} + + /** * virBufferURIEncodeString: * @buf: the buffer to append to diff --git a/src/util/virbuffer.h b/src/util/virbuffer.h index 144a1ba06e..94f14b5b16 100644 --- a/src/util/virbuffer.h +++ b/src/util/virbuffer.h @@ -82,6 +82,8 @@ void virBufferStrcat(virBufferPtr buf, ...) ATTRIBUTE_SENTINEL; void virBufferEscape(virBufferPtr buf, char escape, const char *toescape, const char *format, const char *str); +void virBufferEscapeN(virBufferPtr buf, const char *format, + const char *str, ...); void virBufferEscapeString(virBufferPtr buf, const char *format, const char *str); void virBufferEscapeSexpr(virBufferPtr buf, const char *format, diff --git a/tests/virbuftest.c b/tests/virbuftest.c index 22407ab6a8..34160e6b28 100644 --- a/tests/virbuftest.c +++ b/tests/virbuftest.c @@ -375,6 +375,35 @@ testBufEscapeStr(const void *opaque ATTRIBUTE_UNUSED) } +static int +testBufEscapeN(const void *opaque) +{ + const struct testBufAddStrData *data = opaque; + virBuffer buf = VIR_BUFFER_INITIALIZER; + char *actual; + int ret = -1; + + virBufferEscapeN(&buf, "%s", data->data, '\\', "=", ',', ",", NULL); + + if (!(actual = virBufferContentAndReset(&buf))) { + VIR_TEST_DEBUG("testBufEscapeN: buf is empty"); + goto cleanup; + } + + if (STRNEQ_NULLABLE(actual, data->expect)) { + VIR_TEST_DEBUG("testBufEscapeN: Strings don't match:\n"); + virTestDifference(stderr, data->expect, actual); + goto cleanup; + } + + ret = 0; + + cleanup: + VIR_FREE(actual); + return ret; +} + + static int mymain(void) { @@ -422,6 +451,18 @@ mymain(void) DO_TEST_ESCAPE("\x01\x01\x02\x03\x05\x08", "\n \n"); +#define DO_TEST_ESCAPEN(data, expect) \ + do { \ + struct testBufAddStrData info = { data, expect }; \ + if (virTestRun("Buf: EscapeN", testBufEscapeN, &info) < 0) \ + ret = -1; \ + } while (0) + + DO_TEST_ESCAPEN("noescape", "noescape"); + DO_TEST_ESCAPEN("comma,escape", "comma,,escape"); + DO_TEST_ESCAPEN("equal=escape", "equal\\=escape"); + DO_TEST_ESCAPEN("comma,equal=escape", "comma,,equal\\=escape"); + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; }