[lxc-devel] [PATCH 4/6] utils: Add string and array utility functions
Christian Seiler
christian at iwakd.de
Sun Sep 8 19:44:42 UTC 2013
Adds a few useful string and array manipulation functions to utils.[ch]
Signed-off-by: Christian Seiler <christian at iwakd.de>
---
src/lxc/utils.c | 284 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/lxc/utils.h | 32 +++++++
2 files changed, 316 insertions(+)
diff --git a/src/lxc/utils.c b/src/lxc/utils.c
index b188c47..dc98443 100644
--- a/src/lxc/utils.c
+++ b/src/lxc/utils.c
@@ -37,6 +37,7 @@
#include <libgen.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <assert.h>
#include "utils.h"
#include "log.h"
@@ -523,3 +524,286 @@ FILE *fopen_cloexec(const char *path, const char *mode)
errno = saved_errno;
return ret;
}
+
+char *lxc_string_replace(const char *needle, const char *replacement, const char *haystack)
+{
+ ssize_t len = -1, saved_len = -1;
+ char *result = NULL;
+ size_t replacement_len = strlen(replacement);
+ size_t needle_len = strlen(needle);
+
+ /* should be executed exactly twice */
+ while (len == -1 || result == NULL) {
+ char *p;
+ char *last_p;
+ ssize_t part_len;
+
+ if (len != -1) {
+ result = calloc(1, len + 1);
+ if (!result)
+ return NULL;
+ saved_len = len;
+ }
+
+ len = 0;
+
+ for (last_p = (char *)haystack, p = strstr(last_p, needle); p; last_p = p, p = strstr(last_p, needle)) {
+ part_len = (ssize_t)(p - last_p);
+ if (result && part_len > 0)
+ memcpy(&result[len], last_p, part_len);
+ len += part_len;
+ if (result && replacement_len > 0)
+ memcpy(&result[len], replacement, replacement_len);
+ len += replacement_len;
+ p += needle_len;
+ }
+ part_len = strlen(last_p);
+ if (result && part_len > 0)
+ memcpy(&result[len], last_p, part_len);
+ len += part_len;
+ }
+
+ /* make sure we did the same thing twice,
+ * once for calculating length, the other
+ * time for copying data */
+ assert(saved_len == len);
+ /* make sure we didn't overwrite any buffer,
+ * due to calloc the string should be 0-terminated */
+ assert(result[len] == '\0');
+
+ return result;
+}
+
+bool lxc_string_in_array(const char *needle, const char **haystack)
+{
+ for (; haystack && *haystack; haystack++)
+ if (!strcmp(needle, *haystack))
+ return true;
+ return false;
+}
+
+char *lxc_string_join(const char *sep, const char **parts, bool use_as_prefix)
+{
+ char *result;
+ char **p;
+ size_t sep_len = strlen(sep);
+ size_t result_len = use_as_prefix * sep_len;
+
+ /* calculate new string length */
+ for (p = (char **)parts; *p; p++)
+ result_len += (p > (char **)parts) * sep_len + strlen(*p);
+
+ result = calloc(result_len + 1, 1);
+ if (!result)
+ return NULL;
+
+ if (use_as_prefix)
+ strcpy(result, sep);
+ for (p = (char **)parts; *p; p++) {
+ if (p > (char **)parts)
+ strcat(result, sep);
+ strcat(result, *p);
+ }
+
+ return result;
+}
+
+char **lxc_normalize_path(const char *path)
+{
+ char **components;
+ char **p;
+ size_t components_len = 0;
+ size_t pos = 0;
+
+ components = lxc_string_split(path, '/');
+ if (!components)
+ return NULL;
+ for (p = components; *p; p++)
+ components_len++;
+
+ /* resolve '.' and '..' */
+ for (pos = 0; pos < components_len; ) {
+ if (!strcmp(components[pos], ".") || (!strcmp(components[pos], "..") && pos == 0)) {
+ /* eat this element */
+ free(components[pos]);
+ memmove(&components[pos], &components[pos+1], sizeof(char *) * (components_len - pos));
+ components_len--;
+ } else if (!strcmp(components[pos], "..")) {
+ /* eat this and the previous element */
+ free(components[pos - 1]);
+ free(components[pos]);
+ memmove(&components[pos-1], &components[pos+1], sizeof(char *) * (components_len - pos));
+ components_len -= 2;
+ pos--;
+ } else {
+ pos++;
+ }
+ }
+
+ return components;
+}
+
+bool lxc_string_in_list(const char *needle, const char *haystack, char _sep)
+{
+ char *token, *str, *saveptr = NULL;
+ char sep[2] = { _sep, '\0' };
+
+ if (!haystack || !needle)
+ return 0;
+
+ str = alloca(strlen(haystack)+1);
+ strcpy(str, haystack);
+ for (; (token = strtok_r(str, sep, &saveptr)); str = NULL) {
+ if (strcmp(needle, token) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+char **lxc_string_split(const char *string, char _sep)
+{
+ char *token, *str, *saveptr = NULL;
+ char sep[2] = { _sep, '\0' };
+ char **result = NULL;
+ size_t result_capacity = 0;
+ size_t result_count = 0;
+ int r, saved_errno;
+
+ if (!string)
+ return calloc(1, sizeof(char *));
+
+ str = alloca(strlen(string)+1);
+ strcpy(str, string);
+ for (; (token = strtok_r(str, sep, &saveptr)); str = NULL) {
+ r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 16);
+ if (r < 0)
+ goto error_out;
+ result[result_count] = strdup(token);
+ if (!result[result_count])
+ goto error_out;
+ result_count++;
+ }
+
+ /* if we allocated too much, reduce it */
+ return realloc(result, (result_count + 1) * sizeof(char *));
+error_out:
+ saved_errno = errno;
+ lxc_free_array((void **)result, free);
+ errno = saved_errno;
+ return NULL;
+}
+
+char **lxc_string_split_and_trim(const char *string, char _sep)
+{
+ char *token, *str, *saveptr = NULL;
+ char sep[2] = { _sep, '\0' };
+ char **result = NULL;
+ size_t result_capacity = 0;
+ size_t result_count = 0;
+ int r, saved_errno;
+ size_t i = 0;
+
+ if (!string)
+ return calloc(1, sizeof(char *));
+
+ str = alloca(strlen(string)+1);
+ strcpy(str, string);
+ for (; (token = strtok_r(str, sep, &saveptr)); str = NULL) {
+ while (token[0] == ' ' || token[0] == '\t')
+ token++;
+ i = strlen(token);
+ while (i > 0 && (token[i - 1] == ' ' || token[i - 1] == '\t')) {
+ token[i - 1] = '\0';
+ i--;
+ }
+ r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 16);
+ if (r < 0)
+ goto error_out;
+ result[result_count] = strdup(token);
+ if (!result[result_count])
+ goto error_out;
+ result_count++;
+ }
+
+ /* if we allocated too much, reduce it */
+ return realloc(result, (result_count + 1) * sizeof(char *));
+error_out:
+ saved_errno = errno;
+ lxc_free_array((void **)result, free);
+ errno = saved_errno;
+ return NULL;
+}
+
+void lxc_free_array(void **array, lxc_free_fn element_free_fn)
+{
+ void **p;
+ for (p = array; p && *p; p++)
+ element_free_fn(*p);
+ free((void*)array);
+}
+
+int lxc_grow_array(void ***array, size_t* capacity, size_t new_size, size_t capacity_increment)
+{
+ size_t new_capacity;
+ void **new_array;
+
+ /* first time around, catch some trivial mistakes of the user
+ * only initializing one of these */
+ if (!*array || !*capacity) {
+ *array = NULL;
+ *capacity = 0;
+ }
+
+ new_capacity = *capacity;
+ while (new_size + 1 > new_capacity)
+ new_capacity += capacity_increment;
+ if (new_capacity != *capacity) {
+ /* we have to reallocate */
+ new_array = realloc(*array, new_capacity * sizeof(void *));
+ if (!new_array)
+ return -1;
+ memset(&new_array[*capacity], 0, (new_capacity - (*capacity)) * sizeof(void *));
+ *array = new_array;
+ *capacity = new_capacity;
+ }
+
+ /* array has sufficient elements */
+ return 0;
+}
+
+size_t lxc_array_len(void **array)
+{
+ void **p;
+ size_t result = 0;
+
+ for (p = array; p && *p; p++)
+ result++;
+
+ return result;
+}
+
+void **lxc_dup_array(void **array, lxc_dup_fn element_dup_fn, lxc_free_fn element_free_fn)
+{
+ size_t l = lxc_array_len(array);
+ void **result = calloc(l + 1, sizeof(void *));
+ void **pp;
+ void *p;
+ int saved_errno = 0;
+
+ if (!result)
+ return NULL;
+
+ for (l = 0, pp = array; pp && *pp; pp++, l++) {
+ p = element_dup_fn(*pp);
+ if (!p) {
+ saved_errno = errno;
+ lxc_free_array(result, element_free_fn);
+ errno = saved_errno;
+ return NULL;
+ }
+ result[l] = p;
+ }
+
+ return result;
+}
diff --git a/src/lxc/utils.h b/src/lxc/utils.h
index b79be44..7261846 100644
--- a/src/lxc/utils.h
+++ b/src/lxc/utils.h
@@ -26,6 +26,7 @@
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
+#include <stdbool.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
@@ -197,4 +198,35 @@ extern int sha1sum_file(char *fnam, unsigned char *md_value);
extern char** lxc_va_arg_list_to_argv(va_list ap, size_t skip, int do_strdup);
extern const char** lxc_va_arg_list_to_argv_const(va_list ap, size_t skip);
+/* Some simple string functions; if they return pointers, they are allocated buffers. */
+extern char *lxc_string_replace(const char *needle, const char *replacement, const char *haystack);
+extern bool lxc_string_in_array(const char *needle, const char **haystack);
+extern char *lxc_string_join(const char *sep, const char **parts, bool use_as_prefix);
+/* Normalize and split path: Leading and trailing / are removed, multiple
+ * / are compactified, .. and . are resolved (.. on the top level is considered
+ * identical to .).
+ * Examples:
+ * / -> { NULL }
+ * foo/../bar -> { bar, NULL }
+ * ../../ -> { NULL }
+ * ./bar/baz/.. -> { bar, NULL }
+ * foo//bar -> { foo, bar, NULL }
+ */
+extern char **lxc_normalize_path(const char *path);
+/* Note: the following two functions use strtok(), so they will never
+ * consider an empty element, even if two delimiters are next to
+ * each other.
+ */
+extern bool lxc_string_in_list(const char *needle, const char *haystack, char sep);
+extern char **lxc_string_split(const char *string, char sep);
+extern char **lxc_string_split_and_trim(const char *string, char sep);
+
+/* some simple array manipulation utilities */
+typedef void (*lxc_free_fn)(void *);
+typedef void *(*lxc_dup_fn)(void *);
+extern int lxc_grow_array(void ***array, size_t* capacity, size_t new_size, size_t capacity_increment);
+extern void lxc_free_array(void **array, lxc_free_fn element_free_fn);
+extern size_t lxc_array_len(void **array);
+extern void **lxc_dup_array(void **array, lxc_dup_fn element_dup_fn, lxc_free_fn element_free_fn);
+
#endif
--
1.7.10.4
More information about the lxc-devel
mailing list