On Fri, Nov 30, 2012 at 03:34:36PM +0000, Daniel P. Berrange wrote: > 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 Opps, the virstring.c file should have this patch spliced in diff --git a/src/util/virstring.c b/src/util/virstring.c index 97dddac..fda378a 100644 --- a/src/util/virstring.c +++ b/src/util/virstring.c @@ -27,6 +27,35 @@ #define VIR_FROM_THIS VIR_FROM_NONE +/* + * The following virStringSplit & virStringJoin methods + * are derived from g_strsplit / g_strjoin in glib2, + * also available under the LGPLv2+ license terms + */ + +/** + * virStringSplit: + * @string: a string to split + * @delim: a string which specifies the places at which to split + * the string. The delimiter is not included in any of the resulting + * strings, unless @max_tokens is reached. + * @max_tokens: the maximum number of pieces to split @string into. + * If this is less than 1, the string is split completely. + * + * Splits a string into a maximum of @max_tokens pieces, using the given + * @delim. If @max_tokens is reached, the remainder of @string is + * appended to the last token. + * + * As a special case, the result of splitting the empty string "" is an empty + * vector, not a vector containing a single string. The reason for this + * special case is that being able to represent a empty vector is typically + * more useful than consistent handling of empty elements. If you do need + * to represent empty elements, you'll need to check for the empty string + * before calling virStringSplit(). + * + * Return value: a newly-allocated NULL-terminated array of strings. Use + * virStringFreeList() to free it. + */ char **virStringSplit(const char *string, const char *delim, size_t max_tokens) @@ -83,6 +112,18 @@ no_memory: } +/** + * virStringJoin: + * @strings: a NULL-terminated array of strings to join + * @delim: a string to insert between each of the strings + * + * Joins a number of strings together to form one long string, with the + * @delim inserted between each of them. The returned string + * should be freed with VIR_FREE(). + * + * Returns: a newly-allocated string containing all of the strings joined + * together, with @delim between them + */ char *virStringJoin(const char **strings, const char *delim) { @@ -118,6 +159,13 @@ char *virStringJoin(const char **strings, } +/** + * virStringFreeList: + * @str_array: a NULL-terminated array of strings to free + * + * Frees a NULL-terminated array of strings, and the array itself. + * If called on a NULL value, virStringFreeList() simply returns. + */ void virStringFreeList(char **strings) { char **tmp = strings; > > 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); > +} Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :| -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list