[lxc-devel] [PATCH 4/6] utils: Add string and array utility functions

Serge Hallyn serge.hallyn at ubuntu.com
Tue Sep 10 04:24:32 UTC 2013


Quoting Christian Seiler (christian at iwakd.de):
> Adds a few useful string and array manipulation functions to utils.[ch]
> 
> Signed-off-by: Christian Seiler <christian at iwakd.de>

Acked-by: Serge E. Hallyn <serge.hallyn at ubuntu.com>

However, a comment about
+/* 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);

That's fine for what you're doing with cgroup paths, but given the
function name people might want to start using it for general pathnames.
If they do, they'll need to separately check path[0] to determine
whether the normalized path was absolute or not.

Maybe the comment you have here is clear enough to warn anyone against
getting confused...  I'm just a bit worried it could bite us later.

> ---
>  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 at lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/lxc-devel




More information about the lxc-devel mailing list