INN commit: trunk (3 files)
INN Commit
rra at isc.org
Wed Sep 3 17:54:26 UTC 2014
Date: Wednesday, September 3, 2014 @ 10:54:25
Author: iulius
Revision: 9672
sync the vector library with its latest rra-c-util version
Changes are:
- Always allocate room for at least one string to be stored in the
vector. It simplifies some of the internal logic and, more to the
point, unconfuses clang, which otherwise produces tons of warnings
about possibly dereferencing NULL pointers since it can't follow
the logic.
- Update the confparse test to expect another value for the allocated
size of a vector, further to the previous change.
- Add __warn_unused_result__ to some vector functions that return
newly-allocated memory.
- Add asserts to the vector library to catch incoming NULL vectors.
These will probably be optimized away by gcc, but they also help
unconfuse clang.
- Allow NULL to be passed to (c)vector_free. It's much more convenient
for freeing data structures to be able to call free routines
unconditionally without first testing for NULL. Support this in the
vector interface.
- Define *_split_space in terms of *_split_multi to save some code.
- Check for integer overflow when determining the size of the results of
vector_join and vector_cjoin.
- Handle empty vectors in vector_join and cvector_join.
- Don't check whether a pointer is NULL before passing it into free
and instead assume free can handle NULL pointers properly. This
has been true for many years.
- Fix vector_free to support taking NULL pointers and doing nothing with them.
- Allocate memory with calloc and assume this sets pointers to NULL
instead of explicitly initializing them. We already had to assume
this in various places, and architectures where the all-zero bit
pattern is not the NULL pointer are exceedingly rare.
Modified:
trunk/include/inn/vector.h
trunk/lib/vector.c
trunk/tests/lib/confparse-t.c
-------------------------+
include/inn/vector.h | 119 +++++++++-----
lib/vector.c | 379 +++++++++++++++++++++++-----------------------
tests/lib/confparse-t.c | 4
3 files changed, 271 insertions(+), 231 deletions(-)
Modified: include/inn/vector.h
===================================================================
--- include/inn/vector.h 2014-09-03 17:40:31 UTC (rev 9671)
+++ include/inn/vector.h 2014-09-03 17:54:25 UTC (rev 9672)
@@ -1,27 +1,35 @@
-/* $Id$
-**
-** Vector handling (counted lists of char *'s).
-**
-** Written by Russ Allbery <rra at stanford.edu>
-** This work is hereby placed in the public domain by its author.
-**
-** A vector is a simple array of char *'s combined with a count. It's a
-** convenient way of managing a list of strings, as well as a reasonable
-** output data structure for functions that split up a string. There are
-** two basic types of vectors, regular vectors (in which case strings are
-** copied when put into a vector and freed when the vector is freed) and
-** cvectors or const vectors (where each pointer is a const char * to some
-** external string that isn't freed when the vector is freed).
-**
-** There are two interfaces here, one for vectors and one for cvectors,
-** with the basic operations being the same between the two.
-*/
+/* $Id$
+ *
+ * Prototypes for vector handling.
+ *
+ * A vector is a list of strings, with dynamic resizing of the list as new
+ * strings are added and support for various operations on strings (such as
+ * splitting them on delimiters).
+ *
+ * Vectors require list of strings, not arbitrary binary data, and cannot
+ * handle data elements containing nul characters.
+ *
+ * The canonical version of this file is maintained in the rra-c-util package,
+ * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+ *
+ * Written by Russ Allbery <eagle at eyrie.org>
+ *
+ * The authors hereby relinquish any claim to any copyright that they may have
+ * in this work, whether granted under contract or by operation of law or
+ * international treaty, and hereby commit to the public, at large, that they
+ * shall not, at any time in the future, seek to enforce any copyright in this
+ * work against any person or entity, or prevent any person or entity from
+ * copying, publishing, distributing or creating derivative works of this
+ * work.
+ */
#ifndef INN_VECTOR_H
#define INN_VECTOR_H 1
#include <inn/defines.h>
+#include <stddef.h>
+
struct vector {
size_t count;
size_t allocated;
@@ -37,29 +45,44 @@
BEGIN_DECLS
/* Create a new, empty vector. */
-struct vector *vector_new(void);
-struct cvector *cvector_new(void);
+struct vector *vector_new(void)
+ __attribute__((__warn_unused_result__, __malloc__));
+struct cvector *cvector_new(void)
+ __attribute__((__warn_unused_result__, __malloc__));
/* Add a string to a vector. Resizes the vector if necessary. */
-void vector_add(struct vector *, const char *string);
-void cvector_add(struct cvector *, const char *string);
+void vector_add(struct vector *, const char *string)
+ __attribute__((__nonnull__));
+void cvector_add(struct cvector *, const char *string)
+ __attribute__((__nonnull__));
/* Add a counted string to a vector. Only available for vectors. */
void vector_addn(struct vector *, const char *string, size_t length)
__attribute__((__nonnull__));
-/* Resize the array of strings to hold size entries. Saves reallocation work
- in vector_add if it's known in advance how many entries there will be. */
-void vector_resize(struct vector *, size_t size);
-void cvector_resize(struct cvector *, size_t size);
+/*
+ * Resize the array of strings to hold size entries. Saves reallocation work
+ * in vector_add if it's known in advance how many entries there will be.
+ */
+void vector_resize(struct vector *, size_t size)
+ __attribute__((__nonnull__));
+void cvector_resize(struct cvector *, size_t size)
+ __attribute__((__nonnull__));
-/* Reset the number of elements to zero, freeing all of the strings for a
- regular vector, but not freeing the strings array (to cut down on memory
- allocations if the vector will be reused). */
-void vector_clear(struct vector *);
-void cvector_clear(struct cvector *);
+/*
+ * Reset the number of elements to zero, freeing all of the strings for a
+ * regular vector, but not freeing the strings array (to cut down on memory
+ * allocations if the vector will be reused).
+ */
+void vector_clear(struct vector *)
+ __attribute__((__nonnull__));
+void cvector_clear(struct cvector *)
+ __attribute__((__nonnull__));
-/* Free the vector and all resources allocated for it. */
+/*
+ * Free the vector and all resources allocated for it. NULL may be passed in
+ * safely and will be ignored.
+ */
void vector_free(struct vector *);
void cvector_free(struct cvector *);
@@ -75,7 +98,9 @@
* Empty strings will yield zero-length vectors. Adjacent delimiters are
* treated as a single delimiter by *_split_space and *_split_multi, but *not*
* by *_split, so callers of *_split should be prepared for zero-length
- * strings in the vector.
+ * strings in the vector. *_split_space and *_split_multi ignore any leading
+ * or trailing delimiters, so those functions will never create zero-length
+ * strings (similar to the behavior of strtok).
*/
struct vector *vector_split(const char *string, char sep, struct vector *)
__attribute__((__nonnull__(1)));
@@ -92,17 +117,25 @@
struct cvector *cvector_split_space(char *string, struct cvector *)
__attribute__((__nonnull__(1)));
-/* Build a string from a vector by joining its components together with the
- specified string as separator. Returns a newly allocated string; caller is
- responsible for freeing. */
-char *vector_join(const struct vector *, const char *seperator);
-char *cvector_join(const struct cvector *, const char *separator);
+/*
+ * Build a string from a vector by joining its components together with the
+ * specified string as separator. Returns a newly allocated string; caller is
+ * responsible for freeing.
+ */
+char *vector_join(const struct vector *, const char *separator)
+ __attribute__((__malloc__, __nonnull__, __warn_unused_result__));
+char *cvector_join(const struct cvector *, const char *separator)
+ __attribute__((__malloc__, __nonnull__, __warn_unused_result__));
-/* Exec the given program with the vector as its arguments. Return behavior
- is the same as execv. Note the argument order is different than the other
- vector functions (but the same as execv). */
-int vector_exec(const char *path, struct vector *);
-int cvector_exec(const char *path, struct cvector *);
+/*
+ * Exec the given program with the vector as its arguments. Return behavior
+ * is the same as execv. Note the argument order is different than the other
+ * vector functions (but the same as execv).
+ */
+int vector_exec(const char *path, struct vector *)
+ __attribute__((__nonnull__));
+int cvector_exec(const char *path, struct cvector *)
+ __attribute__((__nonnull__));
END_DECLS
Modified: lib/vector.c
===================================================================
--- lib/vector.c 2014-09-03 17:40:31 UTC (rev 9671)
+++ lib/vector.c 2014-09-03 17:54:25 UTC (rev 9672)
@@ -1,46 +1,59 @@
-/* $Id$
-**
-** Vector handling (counted lists of char *'s).
-**
-** Written by Russ Allbery <rra at stanford.edu>
-** This work is hereby placed in the public domain by its author.
-**
-** A vector is a table for handling a list of strings with less overhead than
-** linked list. The intention is for vectors, once allocated, to be reused;
-** this saves on memory allocations once the array of char *'s reaches a
-** stable size.
-**
-** There are two types of vectors. Standard vectors copy strings when
-** they're inserted into the vector, whereas cvectors just accept pointers
-** to external strings to store. There are therefore two entry points for
-** every vector function, one for vectors and one for cvectors.
-**
-** There's a whole bunch of code duplication here. This would be a lot
-** cleaner with C++ features (either inheritance or templates would
-** probably help). One could probably in some places just cast a cvector
-** to a vector and perform the same operations, but I'm leery of doing that
-** as I'm not sure if it's a violation of the C type aliasing rules.
-*/
+/* $Id$
+ *
+ * Vector handling (counted lists of char *'s).
+ *
+ * A vector is a table for handling a list of strings with less overhead than
+ * linked list. The intention is for vectors, once allocated, to be reused;
+ * this saves on memory allocations once the array of char *'s reaches a
+ * stable size.
+ *
+ * There are two types of vectors. Standard vectors copy strings when they're
+ * inserted into the vector, whereas cvectors just accept pointers to external
+ * strings to store. There are therefore two entry points for every vector
+ * function, one for vectors and one for cvectors.
+ *
+ * Vectors require list of strings, not arbitrary binary data, and cannot
+ * handle data elements containing nul characters.
+ *
+ * There's a whole bunch of code duplication here. This would be a lot
+ * cleaner with C++ features (either inheritance or templates would probably
+ * help). One could probably in some places just cast a cvector to a vector
+ * and perform the same operations, but I'm leery of doing that as I'm not
+ * sure if it's a violation of the C type aliasing rules.
+ *
+ * The canonical version of this file is maintained in the rra-c-util package,
+ * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+ *
+ * Written by Russ Allbery <eagle at eyrie.org>
+ *
+ * The authors hereby relinquish any claim to any copyright that they may have
+ * in this work, whether granted under contract or by operation of law or
+ * international treaty, and hereby commit to the public, at large, that they
+ * shall not, at any time in the future, seek to enforce any copyright in this
+ * work against any person or entity, or prevent any person or entity from
+ * copying, publishing, distributing or creating derivative works of this
+ * work.
+ */
#include "config.h"
#include "clibrary.h"
+#include <assert.h>
#include <ctype.h>
#include "inn/vector.h"
#include "inn/libinn.h"
/*
-** Allocate a new, empty vector.
-*/
+ * Allocate a new, empty vector.
+ */
struct vector *
vector_new(void)
{
struct vector *vector;
- vector = xmalloc(sizeof(struct vector));
- vector->count = 0;
- vector->allocated = 0;
- vector->strings = NULL;
+ vector = xcalloc(1, sizeof(struct vector));
+ vector->allocated = 1;
+ vector->strings = xcalloc(1, sizeof(char *));
return vector;
}
@@ -49,62 +62,60 @@
{
struct cvector *vector;
- vector = xmalloc(sizeof(struct cvector));
- vector->count = 0;
- vector->allocated = 0;
- vector->strings = NULL;
+ vector = xcalloc(1, sizeof(struct cvector));
+ vector->allocated = 1;
+ vector->strings = xcalloc(1, sizeof(const char *));
return vector;
}
/*
-** Resize a vector (using realloc to resize the table).
-*/
+ * Resize a vector (using realloc to resize the table). Maintain a minimum
+ * allocated size of 1 so that the strings data element is never NULL. This
+ * simplifies other code.
+ */
void
vector_resize(struct vector *vector, size_t size)
{
size_t i;
+ assert(vector != NULL);
if (vector->count > size) {
for (i = size; i < vector->count; i++)
free(vector->strings[i]);
vector->count = size;
}
- if (size == 0) {
- free(vector->strings);
- vector->strings = NULL;
- } else {
- vector->strings = xrealloc(vector->strings, size * sizeof(char *));
- }
+ if (size == 0)
+ size = 1;
+ vector->strings = xrealloc(vector->strings, size * sizeof(char *));
vector->allocated = size;
}
void
cvector_resize(struct cvector *vector, size_t size)
{
+ assert(vector != NULL);
if (vector->count > size)
vector->count = size;
- if (size == 0) {
- free(vector->strings);
- vector->strings = NULL;
- } else {
- vector->strings =
- xrealloc(vector->strings, size * sizeof(const char *));
- }
+ if (size == 0)
+ size = 1;
+ vector->strings
+ = xrealloc(vector->strings, size * sizeof(const char *));
vector->allocated = size;
}
/*
-** Add a new string to the vector, resizing the vector as necessary. The
-** vector is resized an element at a time; if a lot of resizes are expected,
-** vector_resize should be called explicitly with a more suitable size.
-*/
+ * Add a new string to the vector, resizing the vector as necessary. The
+ * vector is resized an element at a time; if a lot of resizes are expected,
+ * vector_resize should be called explicitly with a more suitable size.
+ */
void
vector_add(struct vector *vector, const char *string)
{
size_t next = vector->count;
+ assert(vector != NULL);
if (vector->count == vector->allocated)
vector_resize(vector, vector->allocated + 1);
vector->strings[next] = xstrdup(string);
@@ -116,6 +127,7 @@
{
size_t next = vector->count;
+ assert(vector != NULL);
if (vector->count == vector->allocated)
cvector_resize(vector, vector->allocated + 1);
vector->strings[next] = string;
@@ -124,16 +136,17 @@
/*
- * Add a new counted string to the vector, resizing the vector as necessary
- * the same as with vector_add. This function is only available for vectors,
- * not cvectors, since it requires the duplication of the input string to be
- * sure it's nul-terminated.
+ * Add a new string to the vector, copying at most length characters of the
+ * string, resizing the vector as necessary the same as with vector_add. This
+ * function is only available for vectors, not cvectors, since it requires the
+ * duplication of the input string to be sure it's nul-terminated.
*/
void
vector_addn(struct vector *vector, const char *string, size_t length)
{
size_t next = vector->count;
+ assert(vector != NULL);
if (vector->count == vector->allocated)
vector_resize(vector, vector->allocated + 1);
vector->strings[next] = xstrndup(string, length);
@@ -142,13 +155,14 @@
/*
-** Empty a vector but keep the allocated memory for the pointer table.
-*/
+ * Empty a vector but keep the allocated memory for the pointer table.
+ */
void
vector_clear(struct vector *vector)
{
size_t i;
+ assert(vector != NULL);
for (i = 0; i < vector->count; i++)
free(vector->strings[i]);
vector->count = 0;
@@ -157,16 +171,19 @@
void
cvector_clear(struct cvector *vector)
{
+ assert(vector != NULL);
vector->count = 0;
}
/*
-** Free a vector completely.
-*/
+ * Free a vector completely.
+ */
void
vector_free(struct vector *vector)
{
+ if (vector == NULL)
+ return;
vector_clear(vector);
free(vector->strings);
free(vector);
@@ -175,6 +192,8 @@
void
cvector_free(struct cvector *vector)
{
+ if (vector == NULL)
+ return;
cvector_clear(vector);
free(vector->strings);
free(vector);
@@ -182,9 +201,9 @@
/*
-** Given a vector that we may be reusing, clear it out. If the first
-** argument is NULL, allocate a new vector. Used by vector_split*.
-*/
+ * Given a vector that we may be reusing, clear it out. If the argument is
+ * NULL, allocate a new vector. Helper function for vector_split*.
+ */
static struct vector *
vector_reuse(struct vector *vector)
{
@@ -209,9 +228,9 @@
/*
-** Given a string and a separator character, count the number of strings
-** that it will split into.
-*/
+ * Given a string and a separator character, count the number of strings that
+ * it will split into.
+ */
static size_t
split_count(const char *string, char separator)
{
@@ -220,7 +239,7 @@
if (*string == '\0')
return 1;
- for (count = 1, p = string; *p; p++)
+ for (count = 1, p = string; *p != '\0'; p++)
if (*p == separator)
count++;
return count;
@@ -228,53 +247,61 @@
/*
-** Given a string and a separator character, form a vector by splitting the
-** string at those separators. Do a first pass to size the vector, and if
-** the third argument isn't NULL, reuse it. Otherwise, allocate a new one.
-*/
+ * Given a string and a separator character, form a vector by splitting the
+ * string at occurrences of that separator. Consecutive occurrences of the
+ * character will result in empty strings added to the vector. Reuse the
+ * provided vector if non-NULL.
+ */
struct vector *
vector_split(const char *string, char separator, struct vector *vector)
{
const char *p, *start;
size_t i, count;
+ /* If the vector argument isn't NULL, reuse it. */
vector = vector_reuse(vector);
+ /* Do a first pass to size the vector. */
count = split_count(string, separator);
if (vector->allocated < count)
vector_resize(vector, count);
- for (start = string, p = string, i = 0; *p; p++)
+ /* Walk the string and create the new strings with xstrndup. */
+ for (start = string, p = string, i = 0; *p != '\0'; p++)
if (*p == separator) {
vector->strings[i++] = xstrndup(start, p - start);
start = p + 1;
}
vector->strings[i++] = xstrndup(start, p - start);
vector->count = i;
-
return vector;
}
/*
-** Given a modifiable string and a separator character, form a cvector by
-** modifying the string in-place to add nuls at the separators and then
-** building a vector of pointers into the string. Do a first pass to size
-** the vector, and if the third argument isn't NULL, reuse it. Otherwise,
-** allocate a new one.
-*/
+ * Given a modifiable string and a separator character, form a cvector by
+ * modifying the string in-place to add nuls at the separators and then
+ * building a vector of pointers into the string. Reuse the provided vector
+ * if non-NULL.
+ */
struct cvector *
cvector_split(char *string, char separator, struct cvector *vector)
{
char *p, *start;
size_t i, count;
+ /* If the vector argument isn't NULL, reuse it. */
vector = cvector_reuse(vector);
+ /* Do a first pass to size the vector. */
count = split_count(string, separator);
if (vector->allocated < count)
cvector_resize(vector, count);
+ /*
+ * Walk the string and replace separators with nul, and store the pointers
+ * to the start of each string segment.
+ */
for (start = string, p = string, i = 0; *p; p++)
if (*p == separator) {
*p = '\0';
@@ -283,7 +310,6 @@
}
vector->strings[i++] = start;
vector->count = i;
-
return vector;
}
@@ -291,7 +317,8 @@
/*
* Given a string and a set of separators expressed as a string, count the
* number of strings that it will split into when splitting on those
- * separators.
+ * separators. Unlike with split_count, multiple consecutive separator
+ * characters will be treated the same as a single separator.
*/
static size_t
split_multi_count(const char *string, const char *seps)
@@ -299,8 +326,14 @@
const char *p;
size_t count;
+ /* The empty string produces no substrings. */
if (*string == '\0')
return 0;
+
+ /*
+ * Walk the string looking for the first separator not preceeded by
+ * another separator (and ignore a separator at the start of the string).
+ */
for (count = 1, p = string + 1; *p != '\0'; p++)
if (strchr(seps, *p) != NULL && strchr(seps, p[-1]) == NULL)
count++;
@@ -317,9 +350,8 @@
/*
* Given a string, split it at any of the provided separators to form a
- * vector, copying each string segment. If the third argument isn't NULL,
- * reuse that vector; otherwise, allocate a new one. Any number of
- * consecutive separators are considered a single separator.
+ * vector, copying each string segment. Any number of consecutive separators
+ * are considered a single separator. Reuse the provided vector if non-NULL.
*/
struct vector *
vector_split_multi(const char *string, const char *seps,
@@ -328,13 +360,20 @@
const char *p, *start;
size_t i, count;
+ /* If the vector argument isn't NULL, reuse it. */
vector = vector_reuse(vector);
+ /* Count the number of strings we'll create and size the vector. */
count = split_multi_count(string, seps);
if (vector->allocated < count)
vector_resize(vector, count);
- for (start = string, p = string, i = 0; *p; p++)
+ /*
+ * Walk the string and look for separators. start tracks the
+ * non-separator that starts a new string, so as long as start == p, we're
+ * tracking a sequence of separators.
+ */
+ for (start = string, p = string, i = 0; *p != '\0'; p++)
if (strchr(seps, *p) != NULL) {
if (start != p)
vector->strings[i++] = xstrndup(start, p - start);
@@ -343,7 +382,6 @@
if (start != p)
vector->strings[i++] = xstrndup(start, p - start);
vector->count = i;
-
return vector;
}
@@ -351,9 +389,8 @@
/*
* Given a string, split it at any of the provided separators to form a
* vector, destructively modifying the string to nul-terminate each segment.
- * If the third argument isn't NULL, reuse that vector; otherwise, allocate a
- * new one. Any number of consecutive separators are considered a single
- * separator.
+ * Any number of consecutive separators are considered a single separator.
+ * Reuse the provided vector if non-NULL.
*/
struct cvector *
cvector_split_multi(char *string, const char *seps, struct cvector *vector)
@@ -361,13 +398,21 @@
char *p, *start;
size_t i, count;
+ /* If the vector argument isn't NULL, reuse it. */
vector = cvector_reuse(vector);
+ /* Count the number of strings we'll create and size the vector. */
count = split_multi_count(string, seps);
if (vector->allocated < count)
cvector_resize(vector, count);
- for (start = string, p = string, i = 0; *p; p++)
+ /*
+ * Walk the string and look for separators, replacing the ones that
+ * terminate a substring with a nul. start tracks the non-separator that
+ * starts a new string, so as long as start == p, we're tracking a
+ * sequence of separators.
+ */
+ for (start = string, p = string, i = 0; *p != '\0'; p++)
if (strchr(seps, *p) != NULL) {
if (start != p) {
*p = '\0';
@@ -378,158 +423,119 @@
if (start != p)
vector->strings[i++] = start;
vector->count = i;
-
return vector;
}
/*
-** Given a string, count the number of strings that it will split into when
-** splitting on whitespace.
-*/
-static size_t
-split_space_count(const char *string)
-{
- const char *p;
- size_t count;
-
- if (*string == '\0')
- return 0;
- for (count = 1, p = string + 1; *p != '\0'; p++)
- if ((*p == ' ' || *p == '\t') && !(p[-1] == ' ' || p[-1] == '\t'))
- count++;
-
- /* If the string ends in whitespace, we've overestimated the number of
- strings by one. */
- if (p[-1] == ' ' || p[-1] == '\t')
- count--;
- return count;
-}
-
-
-/*
-** Given a string, split it at whitespace to form a vector, copying each
-** string segment. If the second argument isn't NULL, reuse that vector;
-** otherwise, allocate a new one. Any number of consecutive whitespace
-** characters is considered a single separator.
-*/
+ * Given a string, split it at whitespace to form a vector, copying each
+ * string segment. Any number of consecutive whitespace characters are
+ * considered a single separator. Reuse the provided vector if non-NULL.
+ * This is just a special case of vector_split_multi.
+ */
struct vector *
vector_split_space(const char *string, struct vector *vector)
{
- const char *p, *start;
- size_t i, count;
-
- vector = vector_reuse(vector);
-
- count = split_space_count(string);
- if (vector->allocated < count)
- vector_resize(vector, count);
-
- for (start = string, p = string, i = 0; *p; p++)
- if (*p == ' ' || *p == '\t') {
- if (start != p)
- vector->strings[i++] = xstrndup(start, p - start);
- start = p + 1;
- }
- if (start != p)
- vector->strings[i++] = xstrndup(start, p - start);
- vector->count = i;
-
- return vector;
+ return vector_split_multi(string, " \t", vector);
}
/*
-** Given a string, split it at whitespace to form a vector, destructively
-** modifying the string to nul-terminate each segment. If the second
-** argument isn't NULL, reuse that vector; otherwise, allocate a new one.
-** Any number of consecutive whitespace characters is considered a single
-** separator.
-*/
+ * Given a string, split it at whitespace to form a vector, destructively
+ * modifying the string to nul-terminate each segment. Any number of
+ * consecutive whitespace characters are considered a single separator. Reuse
+ * the provided vector if non-NULL. This is just a special case of
+ * cvector_split_multi.
+ */
struct cvector *
cvector_split_space(char *string, struct cvector *vector)
{
- char *p, *start;
- size_t i, count;
-
- vector = cvector_reuse(vector);
-
- count = split_space_count(string);
- if (vector->allocated < count)
- cvector_resize(vector, count);
-
- for (start = string, p = string, i = 0; *p; p++)
- if (*p == ' ' || *p == '\t') {
- if (start != p) {
- *p = '\0';
- vector->strings[i++] = start;
- }
- start = p + 1;
- }
- if (start != p)
- vector->strings[i++] = start;
- vector->count = i;
-
- return vector;
+ return cvector_split_multi(string, " \t", vector);
}
/*
-** Given a vector and a separator string, allocate and build a new string
-** composed of all the strings in the vector separated from each other by the
-** seperator string. Caller is responsible for freeing.
-*/
+ * Given a vector and a separator string, allocate and build a new string
+ * composed of all the strings in the vector separated from each other by the
+ * separator string. Caller is responsible for freeing.
+ */
char *
-vector_join(const struct vector *vector, const char *seperator)
+vector_join(const struct vector *vector, const char *separator)
{
char *string;
size_t i, size, seplen;
- seplen = strlen(seperator);
- for (size = 0, i = 0; i < vector->count; i++)
+ /* If the vector is empty, this is trivial. */
+ assert(vector != NULL);
+ if (vector->count == 0)
+ return xstrdup("");
+
+ /*
+ * Determine the total size of the resulting string. Be careful of
+ * integer overflow while doing so.
+ */
+ seplen = strlen(separator);
+ for (size = 0, i = 0; i < vector->count; i++) {
+ assert(SIZE_MAX - size >= strlen(vector->strings[i]) + seplen + 1);
size += strlen(vector->strings[i]);
+ }
+ assert(SIZE_MAX - size >= (vector->count - 1) * seplen + 1);
size += (vector->count - 1) * seplen + 1;
+ /* Allocate the memory and build up the string using strlcat. */
string = xmalloc(size);
strlcpy(string, vector->strings[0], size);
for (i = 1; i < vector->count; i++) {
- strlcat(string, seperator, size);
+ strlcat(string, separator, size);
strlcat(string, vector->strings[i], size);
}
-
return string;
}
char *
-cvector_join(const struct cvector *vector, const char *seperator)
+cvector_join(const struct cvector *vector, const char *separator)
{
char *string;
size_t i, size, seplen;
- seplen = strlen(seperator);
- for (size = 0, i = 0; i < vector->count; i++)
+ /* If the vector is empty, this is trivial. */
+ assert(vector != NULL);
+ if (vector->count == 0)
+ return xstrdup("");
+
+ /*
+ * Determine the total size of the resulting string. Be careful of
+ * integer overflow while doing so.
+ */
+ seplen = strlen(separator);
+ for (size = 0, i = 0; i < vector->count; i++) {
+ assert(SIZE_MAX - size >= strlen(vector->strings[i]));
size += strlen(vector->strings[i]);
+ }
+ assert(SIZE_MAX - size >= (vector->count - 1) * seplen + 1);
size += (vector->count - 1) * seplen + 1;
+ /* Allocate the memory and build up the string using strlcat. */
string = xmalloc(size);
strlcpy(string, vector->strings[0], size);
for (i = 1; i < vector->count; i++) {
- strlcat(string, seperator, size);
+ strlcat(string, separator, size);
strlcat(string, vector->strings[i], size);
}
-
return string;
}
/*
-** Given a vector and a path to a program, exec that program with the vector
-** as its arguments. This requires adding a NULL terminator to the vector
-** and casting it appropriately.
-*/
+ * Given a vector and a path to a program, exec that program with the vector
+ * as its arguments. This requires adding a NULL terminator to the vector
+ * (which we do not add to count, so it will be invisible to other users of
+ * the vector) and casting it appropriately.
+ */
int
vector_exec(const char *path, struct vector *vector)
{
+ assert(vector != NULL);
if (vector->allocated == vector->count)
vector_resize(vector, vector->count + 1);
vector->strings[vector->count] = NULL;
@@ -539,6 +545,7 @@
int
cvector_exec(const char *path, struct cvector *vector)
{
+ assert(vector != NULL);
if (vector->allocated == vector->count)
cvector_resize(vector, vector->count + 1);
vector->strings[vector->count] = NULL;
Modified: tests/lib/confparse-t.c
===================================================================
--- tests/lib/confparse-t.c 2014-09-03 17:40:31 UTC (rev 9671)
+++ tests/lib/confparse-t.c 2014-09-03 17:54:25 UTC (rev 9672)
@@ -514,7 +514,7 @@
exit(1);
vector = config_params(group);
ok_int(67, 2, vector->count);
- ok_int(68, 2, vector->allocated);
+ ok_int(68, 3, vector->allocated);
if (strcmp(vector->strings[0], "foo") == 0)
ok_string(69, "bar", vector->strings[1]);
else if (strcmp(vector->strings[0], "bar") == 0)
@@ -637,7 +637,7 @@
vector = config_params(subgroup);
ok(153, vector != NULL);
ok_int(154, 3, vector->count);
- ok_int(155, 3, vector->allocated);
+ ok_int(155, 4, vector->allocated);
ok_string(156, "third", vector->strings[0]);
ok_string(157, "second", vector->strings[1]);
ok_string(158, "first", vector->strings[2]);
More information about the inn-committers
mailing list