From: "Daniel P. Berrange" <berrange(a)redhat.com>
To facilitate parsing of argv[] style strings, provide a
virStrSplitQuoted API which will split a string on the listed
separators, but also allow for quoting with ' or ".
* src/libvirt_private.syms, src/util/util.c,
src/util/util.h: Implement virStrSplitQuoted
* tests/utiltest.c: Some tests for virStrSplitQuoted
---
src/libvirt_private.syms | 1 +
src/util/util.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++
src/util/util.h | 2 +
tests/utiltest.c | 89 ++++++++++++++++++++++++++++++++--
4 files changed, 203 insertions(+), 6 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 1f55f5d..b6fbafc 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1148,6 +1148,7 @@ virSetUIDGID;
virSkipSpaces;
virSkipSpacesAndBackslash;
virSkipSpacesBackwards;
+virStrSplitQuoted;
virStrToDouble;
virStrToLong_i;
virStrToLong_l;
diff --git a/src/util/util.c b/src/util/util.c
index 15e6cfa..acfb033 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -1951,6 +1951,123 @@ virStrcpy(char *dest, const char *src, size_t destbytes)
return virStrncpy(dest, src, strlen(src), destbytes);
}
+
+static char *
+virStrDupUnescape(const char *src, size_t len)
+{
+ char *ret;
+ size_t i, j;
+ bool escape = false;
+
+ if (VIR_ALLOC_N(ret, len + 1) < 0)
+ return NULL;
+
+ for (i = 0, j = 0 ; i < len ; i++) {
+ if (escape) {
+ escape = false;
+ ret[j++] = src[i];
+ } else if (src[i] == '\\') {
+ escape = true;
+ } else {
+ ret[j++] = src[i];
+ }
+ }
+ ret[j++] = '\0';
+
+ return ret;
+}
+
+/**
+ * virStrSplitQuoted:
+ * @src: string to split
+ * @sep: list of separator characters
+ *
+ * Split the string 'src' into chunks, separated by one or more of
+ * the characters in 'sep'. Allow ' or " to quote strings even if
+ * they contain 'sep'
+ *
+ * Returns NULL terminated array of strings
+ */
+char **
+virStrSplitQuoted(const char *src, const char *sep)
+{
+ size_t alloc = 0;
+ size_t count = 0;
+ char **ret = NULL;
+ const char *start = src;
+ const char *end;
+ bool escape;
+ char match;
+ char *value = NULL;
+ size_t i;
+
+ while (start && *start) {
+ start += strspn(start, sep);
+
+ if (!*start)
+ break;
+
+ if (*start == '\'' || *start == '\"') {
+ match = *start;
+ start++;
+
+ for (end = start, escape = false ; *end ; end++) {
+ if (escape)
+ escape = false;
+ else if (*end == '\\')
+ escape = true;
+ else if (*end == match)
+ break;
+ }
+
+ if (VIR_RESIZE_N(ret, alloc, count, 1) < 0)
+ goto no_memory;
+
+ if (!(ret[count] = virStrDupUnescape(start, end-start)))
+ goto no_memory;
+ count++;
+
+ start = end;
+ if (*start)
+ start++;
+ } else {
+ for (end = start, escape = false ; *end ; end++) {
+ if (escape)
+ escape = false;
+ else if (*end == '\\')
+ escape = true;
+ else if (strspn(end, sep))
+ break;
+ }
+
+ if (VIR_RESIZE_N(ret, alloc, count, 1) < 0)
+ goto no_memory;
+
+ if (!(ret[count] = virStrDupUnescape(start, end-start)))
+ goto no_memory;
+ count++;
+
+ start = end;
+ if (*start)
+ start++;
+ }
+ }
+
+ if (VIR_RESIZE_N(ret, alloc, count, 1) < 0)
+ goto no_memory;
+ ret[count] = NULL;
+
+ return ret;
+
+no_memory:
+ VIR_FREE(value);
+ for (i = 0 ; i < count ; i++)
+ VIR_FREE(ret[i]);
+ VIR_FREE(ret);
+ return NULL;
+}
+
+
int virEnumFromString(const char *const*types,
unsigned int ntypes,
const char *type)
diff --git a/src/util/util.h b/src/util/util.h
index 85e8bd6..404003d 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -182,6 +182,8 @@ char *virStrcpy(char *dest, const char *src, size_t destbytes)
ATTRIBUTE_RETURN_CHECK;
# define virStrcpyStatic(dest, src) virStrcpy((dest), (src), sizeof(dest))
+char **virStrSplitQuoted(const char *src, const char *sep);
+
int virDiskNameToIndex(const char* str);
char *virIndexToDiskName(int idx, const char *prefix);
diff --git a/tests/utiltest.c b/tests/utiltest.c
index 774a2f7..b8c6a8e 100644
--- a/tests/utiltest.c
+++ b/tests/utiltest.c
@@ -151,6 +151,55 @@ testParseVersionString(const void *data ATTRIBUTE_UNUSED)
}
+struct StringSplitData {
+ const char *src;
+ const char *sep;
+ const char **bits;
+};
+
+static int
+testStringSplit(const void *opaque)
+{
+ int ret = -1;
+ const struct StringSplitData *data = opaque;
+ char **actual;
+ char **tmp1;
+ const char **tmp2 = data->bits;
+ size_t i;
+
+ tmp1 = actual = virStrSplitQuoted(data->src, data->sep);
+
+ if (!tmp1)
+ return -1;
+
+ while (*tmp1 && *tmp2) {
+ if (STRNEQ(*tmp1, *tmp2)) {
+ fprintf(stderr, "Expected '%s' got '%s'\n", *tmp2,
*tmp1);
+ goto cleanup;
+ }
+
+ tmp1++;
+ tmp2++;
+ }
+
+ if (*tmp1) {
+ fprintf(stderr, "Unexpected extra value '%s'\n", *tmp1);
+ goto cleanup;
+ }
+
+ if (*tmp2) {
+ fprintf(stderr, "Unexpected missing value '%s'\n", *tmp2);
+ goto cleanup;
+ }
+
+
+ ret = 0;
+cleanup:
+ for (i = 0 ; actual[i] ; i++)
+ VIR_FREE(actual[i]);
+ VIR_FREE(actual);
+ return ret;
+}
static int
@@ -161,17 +210,45 @@ mymain(void)
virSetErrorFunc(NULL, testQuietError);
#define DO_TEST(_name) \
- do { \
- if (virtTestRun("Util "#_name, 1, test##_name,
\
- NULL) < 0) { \
- result = -1; \
- } \
- } while (0)
+ do { \
+ if (virtTestRun("Util "#_name, 1, test##_name, \
+ NULL) < 0) { \
+ result = -1; \
+ } \
+ } while (0)
+
+#define DO_TEST_STRING(str, sep, bits) \
+ do { \
+ struct StringSplitData data = { \
+ str, sep, bits \
+ }; \
+ if (virtTestRun("Util split " str, 1, testStringSplit, \
+ &data) < 0) { \
+ result = -1; \
+ } \
+ } while (0)
DO_TEST(IndexToDiskName);
DO_TEST(DiskNameToIndex);
DO_TEST(ParseVersionString);
+ const char *bits1[] = { "foo", "bar", NULL };
+ DO_TEST_STRING("foo bar", " ", bits1);
+ DO_TEST_STRING("foo 'bar'", " ", bits1);
+ DO_TEST_STRING("foo \"bar\"", " ", bits1);
+ DO_TEST_STRING(" foo \"bar\"", " ", bits1);
+ DO_TEST_STRING(" foo \"bar\"", " ", bits1);
+ DO_TEST_STRING(" foo \"bar\"\n ", " \t\n\r",
bits1);
+
+ const char *bits2[] = { "foo", "bar wizz", "eek", NULL
};
+ DO_TEST_STRING("foo 'bar wizz' eek", " ", bits2);
+ DO_TEST_STRING("foo \"bar wizz\" eek", " ", bits2);
+
+ const char *bits3[] = { "foo", "'bar'
\"wizz\"", "eek", NULL };
+ DO_TEST_STRING("foo '\\'bar\\' \\\"wizz\\\"'
eek", " ", bits3);
+ DO_TEST_STRING("foo \"\\'bar\\' \\\"wizz\\\"\"
eek", " ", bits3);
+ DO_TEST_STRING("foo \"\\'bar\\' \\\"wizz\\\"\"\r\n
eek", " \r\n", bits3);
+
return result == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
--
1.7.7.6