From: "Daniel P. Berrange" <berrange(a)redhat.com>
This introduces a few new APIs for dealing with strings.
One to split a char * into a char **, another to join a
char ** into a char *, and finally one to free a char **
There is a simple test suite to validate the edge cases
too. No more need to use the horrible strtok_r() API,
or hand-written code for splitting strings.
Signed-off-by: Daniel P. Berrange <berrange(a)redhat.com>
---
src/Makefile.am | 1 +
src/libvirt_private.syms | 6 ++
src/util/virstring.c | 129 +++++++++++++++++++++++++++++++++++++
src/util/virstring.h | 36 +++++++++++
tests/Makefile.am | 6 ++
tests/virstringtest.c | 164 +++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 342 insertions(+)
create mode 100644 src/util/virstring.c
create mode 100644 src/util/virstring.h
create mode 100644 tests/virstringtest.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 6401dec..b5c20c8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -112,6 +112,7 @@ UTIL_SOURCES = \
util/virnetlink.c util/virnetlink.h \
util/virrandom.h util/virrandom.c \
util/virsocketaddr.h util/virsocketaddr.c \
+ util/virstring.h util/virstring.c \
util/virtime.h util/virtime.c \
util/viruri.h util/viruri.c
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 93a21cc..08974d0 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1812,6 +1812,12 @@ virSetErrorLogPriorityFunc;
virStrerror;
+# virstring.h
+virStringSplit;
+virStringJoin;
+virStringFreeList;
+
+
# virtime.h
virTimeFieldsNow;
virTimeFieldsNowRaw;
diff --git a/src/util/virstring.c b/src/util/virstring.c
new file mode 100644
index 0000000..97dddac
--- /dev/null
+++ b/src/util/virstring.c
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2007-2012 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <
http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Daniel P. Berrange <berrange(a)redhat.com>
+ */
+
+#include <config.h>
+
+#include "virstring.h"
+#include "memory.h"
+#include "virterror_internal.h"
+
+#define VIR_FROM_THIS VIR_FROM_NONE
+
+char **virStringSplit(const char *string,
+ const char *delim,
+ size_t max_tokens)
+{
+ char **tokens = NULL;
+ size_t ntokens = 0;
+ size_t maxtokens = 0;
+ const char *remainder = string;
+ char *tmp;
+ size_t i;
+
+ if (max_tokens < 1)
+ max_tokens = INT_MAX;
+
+ tmp = strstr(remainder, delim);
+ if (tmp) {
+ size_t delimlen = strlen(delim);
+
+ while (--max_tokens && tmp) {
+ size_t len = tmp - remainder;
+
+ if (VIR_RESIZE_N(tokens, maxtokens, ntokens, 1) < 0)
+ goto no_memory;
+
+ if (!(tokens[ntokens] = strndup(remainder, len)))
+ goto no_memory;
+ ntokens++;
+ remainder = tmp + delimlen;
+ tmp = strstr(remainder, delim);
+ }
+ }
+ if (*string) {
+ if (VIR_RESIZE_N(tokens, maxtokens, ntokens, 1) < 0)
+ goto no_memory;
+
+ if (!(tokens[ntokens] = strdup(remainder)))
+ goto no_memory;
+ ntokens++;
+ }
+
+ if (VIR_RESIZE_N(tokens, maxtokens, ntokens, 1) < 0)
+ goto no_memory;
+ tokens[ntokens++] = NULL;
+
+ return tokens;
+
+no_memory:
+ virReportOOMError();
+ for (i = 0 ; i < ntokens ; i++) {
+ VIR_FREE(tokens[i]);
+ }
+ VIR_FREE(tokens);
+ return NULL;
+}
+
+
+char *virStringJoin(const char **strings,
+ const char *delim)
+{
+ size_t len = 0;
+ size_t delimlen = strlen(delim);
+ const char **tmp = strings;
+ char *string;
+ char *offset;
+
+ while (tmp && *tmp) {
+ len += strlen(*tmp);
+ len += delimlen;
+ tmp++;
+ }
+
+ if (VIR_ALLOC_N(string, len + 1) < 0) {
+ virReportOOMError();
+ return NULL;
+ }
+
+ tmp = strings;
+ offset = string;
+ while (tmp && *tmp) {
+ offset = stpcpy(offset, *tmp);
+ if (*(tmp+1))
+ offset = stpcpy(offset, delim);
+ len += strlen(*tmp);
+ len += delimlen;
+ tmp++;
+ }
+
+ return string;
+}
+
+
+void virStringFreeList(char **strings)
+{
+ char **tmp = strings;
+ while (tmp && *tmp) {
+ VIR_FREE(*tmp);
+ tmp++;
+ }
+ VIR_FREE(strings);
+}
diff --git a/src/util/virstring.h b/src/util/virstring.h
new file mode 100644
index 0000000..14c77dc
--- /dev/null
+++ b/src/util/virstring.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2007-2012 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <
http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Daniel P. Berrange <berrange(a)redhat.com>
+ */
+
+#ifndef __VIR_STRING_H__
+# define __VIR_STRING_H__
+
+# include "internal.h"
+
+char **virStringSplit(const char *string,
+ const char *delim,
+ size_t max_tokens);
+
+char *virStringJoin(const char **strings,
+ const char *delim);
+
+void virStringFreeList(char **strings);
+
+#endif /* __VIR_STRING_H__ */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 18f5b51..8435e1a 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -95,6 +95,7 @@ test_programs = virshtest sockettest \
virauthconfigtest \
virbitmaptest \
virlockspacetest \
+ virstringtest \
$(NULL)
if WITH_SECDRIVER_SELINUX
@@ -539,6 +540,11 @@ virtimetest_SOURCES = \
virtimetest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\""
$(AM_CFLAGS)
virtimetest_LDADD = $(LDADDS)
+virstringtest_SOURCES = \
+ virstringtest.c testutils.h testutils.c
+virstringtest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\""
$(AM_CFLAGS)
+virstringtest_LDADD = $(LDADDS)
+
virlockspacetest_SOURCES = \
virlockspacetest.c testutils.h testutils.c
virlockspacetest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\""
$(AM_CFLAGS)
diff --git a/tests/virstringtest.c b/tests/virstringtest.c
new file mode 100644
index 0000000..de050d6
--- /dev/null
+++ b/tests/virstringtest.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <
http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange(a)redhat.com>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <signal.h>
+
+#include "testutils.h"
+#include "util.h"
+#include "virterror_internal.h"
+#include "memory.h"
+#include "logging.h"
+
+#include "virstring.h"
+
+#define VIR_FROM_THIS VIR_FROM_NONE
+
+struct testSplitData {
+ const char *string;
+ const char *delim;
+ size_t max_tokens;
+ const char **tokens;
+};
+
+
+struct testJoinData {
+ const char *string;
+ const char *delim;
+ const char **tokens;
+};
+
+static int testSplit(const void *args)
+{
+ const struct testSplitData *data = args;
+ char **got;
+ char **tmp1;
+ const char **tmp2;
+ int ret = -1;
+
+ if (!(got = virStringSplit(data->string, data->delim, data->max_tokens))) {
+ VIR_DEBUG("Got no tokens at all");
+ return -1;
+ }
+
+ tmp1 = got;
+ tmp2 = data->tokens;
+ while (*tmp1 && *tmp2) {
+ if (STRNEQ(*tmp1, *tmp2)) {
+ fprintf(stderr, "Mismatch '%s' vs '%s'\n", *tmp1,
*tmp2);
+ goto cleanup;
+ }
+ tmp1++;
+ tmp2++;
+ }
+ if (*tmp1) {
+ fprintf(stderr, "Too many pieces returned\n");
+ goto cleanup;
+ }
+ if (*tmp2) {
+ fprintf(stderr, "Too few pieces returned\n");
+ goto cleanup;
+ }
+
+ ret = 0;
+cleanup:
+ virStringFreeList(got);
+
+ return ret;
+}
+
+
+static int testJoin(const void *args)
+{
+ const struct testJoinData *data = args;
+ char *got;
+ int ret = -1;
+
+ if (!(got = virStringJoin(data->tokens, data->delim))) {
+ VIR_DEBUG("Got no result");
+ return -1;
+ }
+ if (STRNEQ(got, data->string)) {
+ fprintf(stderr, "Mismatch '%s' vs '%s'\n", got,
data->string);
+ goto cleanup;
+ }
+
+ ret = 0;
+cleanup:
+ VIR_FREE(got);
+
+ return ret;
+}
+
+
+static int
+mymain(void)
+{
+ int ret = 0;
+
+ signal(SIGPIPE, SIG_IGN);
+
+#define TEST_SPLIT(str, del, max, toks) \
+ do { \
+ struct testSplitData splitData = { \
+ .string = str, \
+ .delim = del, \
+ .max_tokens = max, \
+ .tokens = toks, \
+ }; \
+ struct testJoinData joinData = { \
+ .string = str, \
+ .delim = del, \
+ .tokens = toks, \
+ }; \
+ if (virtTestRun("Split " #str, 1, testSplit, &splitData) < 0)
\
+ ret = -1; \
+ if (virtTestRun("Join " #str, 1, testJoin, &joinData) < 0)
\
+ ret = -1; \
+ } while (0)
+
+ const char *tokens1[] = { NULL };
+ TEST_SPLIT("", " ", 0, tokens1);
+
+ const char *tokens2[] = { "", "", NULL };
+ TEST_SPLIT(" ", " ", 0, tokens2);
+
+ const char *tokens3[] = { "", "", "", NULL };
+ TEST_SPLIT(" ", " ", 0, tokens3);
+
+ const char *tokens4[] = { "The", "quick", "brown",
"fox", NULL };
+ TEST_SPLIT("The quick brown fox", " ", 0, tokens4);
+
+ const char *tokens5[] = { "The quick ", " fox", NULL };
+ TEST_SPLIT("The quick brown fox", "brown", 0, tokens5);
+
+ const char *tokens6[] = { "", "The", "quick",
"brown", "fox", NULL };
+ TEST_SPLIT(" The quick brown fox", " ", 0, tokens6);
+
+ const char *tokens7[] = { "The", "quick", "brown",
"fox", "", NULL };
+ TEST_SPLIT("The quick brown fox ", " ", 0, tokens7);
+
+
+ return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+VIRT_TEST_MAIN(mymain)
--
1.7.11.7