1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
|
/*
* 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.
*
* This is based on the from rra-c-util util/vector.c library, but that
* library uses xmalloc routines to exit the program if memory allocation
* fails. This is a modified version of the vector library that instead
* returns false on failure to allocate memory, allowing the caller to do
* appropriate recovery.
*
* Vectors require list of strings, not arbitrary binary data, and cannot
* handle data elements containing nul characters.
*
* Only the portions of the vector library needed by this module is
* implemented.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2013
* The Board of Trustees of the Leland Stanford Junior University
*
* See LICENSE for licensing terms.
*/
#include <config.h>
#include <portable/system.h>
#include <plugin/internal.h>
/*
* Allocate a new, empty vector. Returns NULL if memory allocation fails.
*/
struct vector *
strength_vector_new(void)
{
return calloc(1, sizeof(struct vector));
}
/*
* Resize a vector (using realloc to resize the table). Return false if
* memory allocation fails.
*/
static bool
strength_vector_resize(struct vector *vector, size_t size)
{
size_t i;
char **strings;
/* If we're shrinking the vector, free the excess strings. */
if (vector->count > size) {
for (i = size; i < vector->count; i++)
free(vector->strings[i]);
vector->count = size;
}
/* If resizing to zero, free all storage. Otherwise, realloc. */
if (size == 0) {
free(vector->strings);
vector->strings = NULL;
} else {
strings = realloc(vector->strings, size * sizeof(char *));
if (strings == NULL)
return false;
vector->strings = strings;
}
vector->allocated = size;
return true;
}
/*
* 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.
* Return false if memory allocation fails.
*/
bool
strength_vector_add(struct vector *vector, const char *string)
{
size_t next = vector->count;
if (vector->count == vector->allocated)
if (!strength_vector_resize(vector, vector->allocated + 1))
return false;
vector->strings[next] = strdup(string);
if (vector->strings[next] == NULL)
return false;
vector->count++;
return true;
}
/*
* Empty a vector but keep the allocated memory for the pointer table.
*/
static void
strength_vector_clear(struct vector *vector)
{
size_t i;
for (i = 0; i < vector->count; i++)
if (vector->strings[i] != NULL)
free(vector->strings[i]);
vector->count = 0;
}
/*
* Free a vector completely.
*/
void
strength_vector_free(struct vector *vector)
{
if (vector == NULL)
return;
strength_vector_clear(vector);
free(vector->strings);
free(vector);
}
/*
* 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*. Returns NULL if
* memory allocation fails.
*/
static struct vector *
strength_vector_reuse(struct vector *vector)
{
if (vector == NULL)
return strength_vector_new();
else {
strength_vector_clear(vector);
return vector;
}
}
/*
* 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.
*/
static size_t
split_multi_count(const char *string, const char *seps)
{
const char *p;
size_t count;
/* If the string is empty, the count of components is zero. */
if (*string == '\0')
return 0;
/* Otherwise, walk the string looking for non-consecutive separators. */
for (count = 1, p = string + 1; *p != '\0'; p++)
if (strchr(seps, *p) != NULL && strchr(seps, p[-1]) == NULL)
count++;
/*
* If the string ends in separators, we've overestimated the number of
* strings by one.
*/
if (strchr(seps, p[-1]) != NULL)
count--;
return count;
}
/*
* 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. Returns NULL on
* memory allocation failure, after which the provided vector may only have
* partial results.
*/
struct vector *
strength_vector_split_multi(const char *string, const char *seps,
struct vector *vector)
{
const char *p, *start;
size_t i, count;
bool created = false;
/* Set up the vector we'll use to store the results. */
if (vector == NULL)
created = true;
vector = strength_vector_reuse(vector);
if (vector == NULL)
return NULL;
/* Count how big a vector we need and resize accordingly. */
count = split_multi_count(string, seps);
if (count == 0)
return vector;
if (vector->allocated < count && !strength_vector_resize(vector, count))
goto fail;
/* Now, walk the string and build the components. */
vector->count = 0;
for (start = string, p = string, i = 0; *p != '\0'; p++)
if (strchr(seps, *p) != NULL) {
if (start != p) {
vector->strings[i] = strndup(start, (size_t) (p - start));
if (vector->strings[i] == NULL)
goto fail;
i++;
vector->count++;
}
start = p + 1;
}
/* If there is anything left in the string, we have one more component. */
if (start != p) {
vector->strings[i] = strndup(start, (size_t) (p - start));
if (vector->strings[i] == NULL)
goto fail;
vector->count++;
}
return vector;
fail:
if (created)
strength_vector_free(vector);
return NULL;
}
|