Adds a few useful string and array manipulation functions to utils.[ch] Signed-off-by: Christian Seiler <christ...@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 ------------------------------------------------------------------------------ Learn the latest--Visual Studio 2012, SharePoint 2013, SQL 2012, more! Discover the easy way to master current and previous Microsoft technologies and advance your career. Get an incredible 1,500+ hours of step-by-step tutorial videos with LearnDevNow. Subscribe today and save! http://pubads.g.doubleclick.net/gampad/clk?id=58041391&iu=/4140/ostg.clktrk _______________________________________________ Lxc-devel mailing list Lxc-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/lxc-devel