From: "Daniel P. Berrange" <berrange@xxxxxxxxxx> 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@xxxxxxxxxx> --- 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@xxxxxxxxxx> + */ + +#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@xxxxxxxxxx> + */ + +#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@xxxxxxxxxx> + */ + +#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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list