[ccan] [PATCH] stringbuilder: Functions for joining strings.

Rusty Russell rusty at rustcorp.com.au
Mon Mar 30 16:16:39 AEDT 2015


stuartl at longlandclan.yi.org writes:
> From: Stuart Longland <stuartl at longlandclan.yi.org>
>
> This is a small couple of functions for joining lists of strings
> together.  The lists can either be varadic arguments or arrays, and
> delimiters are optional.
>
> This patch incorporates some advice from David Gibson on the original
> module.

Crap, this fell through the cracks somehow.

I have applied this module.  Please send your ssh key, so I can
configure gitolite so you can update the module from now on as you wish.

Thanks!
Rusty.

> ---
>  Makefile-ccan                      |   1 +
>  ccan/stringbuilder/LICENSE         |   1 +
>  ccan/stringbuilder/_info           |  59 ++++++++++++++++
>  ccan/stringbuilder/stringbuilder.c |  77 +++++++++++++++++++++
>  ccan/stringbuilder/stringbuilder.h | 138 +++++++++++++++++++++++++++++++++++++
>  ccan/stringbuilder/test/run.c      |  67 ++++++++++++++++++
>  6 files changed, 343 insertions(+)
>  create mode 120000 ccan/stringbuilder/LICENSE
>  create mode 100644 ccan/stringbuilder/_info
>  create mode 100644 ccan/stringbuilder/stringbuilder.c
>  create mode 100644 ccan/stringbuilder/stringbuilder.h
>  create mode 100644 ccan/stringbuilder/test/run.c
>
> diff --git a/Makefile-ccan b/Makefile-ccan
> index 40d0389..cc865ca 100644
> --- a/Makefile-ccan
> +++ b/Makefile-ccan
> @@ -83,6 +83,7 @@ MODS_WITH_SRC := antithread \
>  	siphash \
>  	sparse_bsearch \
>  	str \
> +	stringbuilder \
>  	stringmap \
>  	strmap \
>  	strset \
> diff --git a/ccan/stringbuilder/LICENSE b/ccan/stringbuilder/LICENSE
> new file mode 120000
> index 0000000..b7951da
> --- /dev/null
> +++ b/ccan/stringbuilder/LICENSE
> @@ -0,0 +1 @@
> +../../licenses/CC0
> \ No newline at end of file
> diff --git a/ccan/stringbuilder/_info b/ccan/stringbuilder/_info
> new file mode 100644
> index 0000000..4c78254
> --- /dev/null
> +++ b/ccan/stringbuilder/_info
> @@ -0,0 +1,59 @@
> +#include "config.h"
> +#include <stdio.h>
> +#include <string.h>
> +
> +/**
> + * stringbuilder - join lists of strings
> + *
> + * This is a small set of functions for building up strings from a list.
> + * The destination buffer is bounds-checked, the functions return failure
> + * if the concatenated strings overflow the buffer.
> + *
> + * Example:
> + *	#include <stdio.h>
> + *	#include <string.h>
> + *	#include <errno.h>
> + *	#include <ccan/stringbuilder/stringbuilder.h>
> + *
> + *	int main(int argc, char *argv[])
> + *	{
> + *		char mystring[128];
> + *		int res;
> + *
> + *		res = stringbuilder_array(mystring, 128, "', '",
> + *			argc, (const char**)argv);
> + *		if (!res)
> + *			printf("My arguments: '%s'\n", mystring);
> + *		else
> + *			printf("Failed to join arguments: %s\n",
> + *				strerror(res));
> + *		if (!res) {
> + *			res = stringbuilder(mystring, 128, ", ",
> + *				"This", "Is", "A", "Test");
> + *			if (!res)
> + *				printf("My string: '%s'\n", mystring);
> + *			else
> + *				printf("Failed to join strings: %s\n",
> + *					strerror(res));
> + *		}
> + *		return 0;
> + *	}
> + *
> + * License: CC0 (Public domain)
> + * Author: Stuart Longland <stuartl at longlandclan.yi.org>
> + */
> +int main(int argc, char *argv[])
> +{
> +	if (argc != 2)
> +		return 1;
> +
> +	if (strcmp(argv[1], "depends") == 0) {
> +		/*
> +		 * This triggers a circular dependency!
> +		 * printf("ccan/str\n");
> +		 */
> +		return 0;
> +	}
> +
> +	return 1;
> +}
> diff --git a/ccan/stringbuilder/stringbuilder.c b/ccan/stringbuilder/stringbuilder.c
> new file mode 100644
> index 0000000..d34de81
> --- /dev/null
> +++ b/ccan/stringbuilder/stringbuilder.c
> @@ -0,0 +1,77 @@
> +/* CC0 (Public domain) - see LICENSE file for details */
> +#include <ccan/stringbuilder/stringbuilder.h>
> +#include <string.h>
> +#include <errno.h>
> +
> +int stringbuilder_args(char* str, size_t str_sz, const char* delim, ...)
> +{
> +	int res;
> +	va_list ap;
> +	va_start(ap, delim);
> +	res = stringbuilder_va(str, str_sz, delim, ap);
> +	va_end(ap);
> +	return res;
> +}
> +
> +static int stringbuilder_cpy(
> +		char** str, size_t* str_sz, const char* s, size_t s_len)
> +{
> +	if (!s)
> +		return 0;
> +
> +	if (*str != s) {
> +		if (!s_len)
> +			s_len = strlen(s);
> +		if (s_len > *str_sz)
> +			return EMSGSIZE;
> +		strcpy(*str, s);
> +	}
> +	*str += s_len;
> +	*str_sz -= s_len;
> +	return 0;
> +}
> +
> +int stringbuilder_va(char* str, size_t str_sz, const char* delim, va_list ap)
> +{
> +	int res = 0;
> +	size_t delim_len = 0;
> +	const char* s = va_arg(ap, const char*);
> +
> +	if (delim)
> +		delim_len = strlen(delim);
> +
> +	res = stringbuilder_cpy(&str, &str_sz, s, 0);
> +	s = va_arg(ap, const char*);
> +	while(s && !res) {
> +		res = stringbuilder_cpy(&str, &str_sz,
> +				delim, delim_len);
> +		if (!res) {
> +			res = stringbuilder_cpy(&str, &str_sz,
> +						s, 0);
> +			s = va_arg(ap, const char*);
> +		}
> +	}
> +	return res;
> +}
> +
> +int stringbuilder_array(char* str, size_t str_sz, const char* delim,
> +		size_t n_strings, const char** strings)
> +{
> +	int res = 0;
> +	size_t delim_len = 0;
> +
> +	if (delim)
> +		delim_len = strlen(delim);
> +
> +	res = stringbuilder_cpy(&str, &str_sz,
> +			*(strings++), 0);
> +	while(--n_strings && !res) {
> +		res = stringbuilder_cpy(&str, &str_sz,
> +				delim, delim_len);
> +		if (!res)
> +			res = stringbuilder_cpy(&str, &str_sz,
> +					*(strings++), 0);
> +	}
> +	return res;
> +
> +}
> diff --git a/ccan/stringbuilder/stringbuilder.h b/ccan/stringbuilder/stringbuilder.h
> new file mode 100644
> index 0000000..84d79ec
> --- /dev/null
> +++ b/ccan/stringbuilder/stringbuilder.h
> @@ -0,0 +1,138 @@
> +/* CC0 (Public domain) - see LICENSE file for details */
> +#ifndef CCAN_STRINGBUILDER_H
> +#define CCAN_STRINGBUILDER_H
> +#include "config.h"
> +#include <stdarg.h>
> +#include <sys/types.h>
> +
> +/**
> + * stringbuilder - Join strings from a varadic list.  The list of arguments
> + * are all assumed to be of type const char*.  If the first argument is str,
> + * then the contents of str are preserved and appended to.
> + *
> + * @str:	A pointer to a string buffer that will receive the result.
> + * @str_sz:	Size of the buffer pointed to by str.
> + * @delim:	A delimiter to separate the strings with, or NULL.
> + *
> + * Returns:	0 on success
> + * 		EMSGSIZE if the resulting string would overflow the buffer.
> + * 		If an overflow condition is detected, the buffer content is
> + * 		NOT defined.
> + *
> + * Example:
> + * 	int res;
> + * 	char file_name[80];
> + * 	res = stringbuilder(file_name, sizeof(file_name), "/",
> + * 		"/var/lib/foo", "bar", "baz");
> + * 	if (res)
> + * 		printf("Failed to determine file name: %s",
> + * 			strerror(res));
> + * 	else
> + * 		printf("File is at %s", file_name);
> + */
> +#define stringbuilder(str, str_sz, delim, ...) 	\
> +	stringbuilder_args(str, str_sz, delim, __VA_ARGS__, NULL)
> +/**
> + * stringbuilder_args - Join strings from a varadic list.  The list of
> + * arguments are all assumed to be of type const char* and must end with a NULL.
> + * If the first argument is str, then the contents of str are preserved and
> + * appended to.
> + *
> + * @str:	A pointer to a string buffer that will receive the result.
> + * @str_sz:	Size of the buffer pointed to by str.
> + * @delim:	A delimiter to separate the strings with, or NULL.
> + *
> + * Returns:	0 on success
> + * 		EMSGSIZE if the resulting string would overflow the buffer.
> + * 		If an overflow condition is detected, the buffer content is
> + * 		NOT defined.
> + *
> + * Example:
> + * 	int res;
> + * 	char file_name[80];
> + * 	res = stringbuilder_args(file_name, sizeof(file_name), "/",
> + * 		"/var/lib/foo", "bar", "baz",
> + * 		NULL);
> + * 	if (res)
> + * 		printf("Failed to determine file name: %s",
> + * 			strerror(res));
> + * 	else
> + * 		printf("File is at %s", file_name);
> + */
> +int stringbuilder_args(char* str, size_t str_sz, const char* delim, ...);
> +
> +/**
> + * stringbuilder_va - Join strings from a varadic list.  The list of arguments
> + * are all assumed to be of type const char* and must end with a NULL.  If the
> + * first argument is str, then the contents of str are preserved and appended
> + * to.
> + *
> + * @str:	A pointer to a string buffer that will receive the result.
> + * @str_sz:	Size of the buffer pointed to by str.
> + * @delim:	A delimiter to separate the strings with, or NULL.
> + *
> + * Returns:	0 on success
> + * 		EMSGSIZE if the resulting string would overflow the buffer.
> + * 		If an overflow condition is detected, the buffer content is
> + * 		NOT defined.
> + *
> + * Example:
> + * 	#include <ccan/stringbuilder/stringbuilder.h>
> + * 	#include <stdarg.h>
> + * 	#include <stdio.h>
> + * 	#include <string.h>
> + * 	#include <errno.h>
> + *
> + *	int my_stringbuilder(char* str, size_t str_sz,
> + *		const char* delim, ...);
> + *
> + *	int my_stringbuilder(char* str, size_t str_sz,
> + *		const char* delim, ...)
> + *	{
> + *		int res;
> + *		va_list ap;
> + *		va_start(ap, delim);
> + *		res = stringbuilder_va(str, str_sz, delim, ap);
> + *		va_end(ap);
> + *		return res;
> + *	}
> + *
> + *	int main(void) {
> + *		char my_string[80];
> + *		int res = my_stringbuilder(my_string,
> + *			sizeof(my_string), " ", "foo", "bar", NULL);
> + *		if (!res)
> + *			printf("%s\n", my_string);
> + *		return res ? 1 : 0;
> + *	}
> + */
> +int stringbuilder_va(char* str, size_t str_sz, const char* delim, va_list ap);
> +
> +/**
> + * stringbuilder_array - Join strings from an array of const char* pointers.
> + *
> + * @str:	A pointer to a string buffer that will receive the result.
> + * @str_sz:	Size of the buffer pointed to by str.
> + * @delim:	A delimiter to separate the strings with, or NULL.
> + * @n_strings:	The number of strings to join.
> + * @strings:	The array of strings to join.
> + *
> + * Returns:	0 on success
> + * 		EMSGSIZE if the resulting string would overflow the buffer.
> + * 		If an overflow condition is detected, the buffer content is
> + * 		NOT defined.
> + *
> + * Example:
> + * 	char my_args[128];
> + * 	int res = stringbuilder_array(my_args, sizeof(my_args), ", ",
> + * 		argc, (const char**)argv);
> + * 	if (res)
> + * 		printf("Failed to list arguments: %s",
> + * 			strerror(res));
> + * 	else
> + * 		printf("My arguments were %s", my_args);
> + */
> +int stringbuilder_array(char* str, size_t str_sz, const char* delim,
> +		size_t n_strings, const char** strings);
> +
> +#endif /* CCAN_STRINGBUILDER_H */
> diff --git a/ccan/stringbuilder/test/run.c b/ccan/stringbuilder/test/run.c
> new file mode 100644
> index 0000000..4b1209d
> --- /dev/null
> +++ b/ccan/stringbuilder/test/run.c
> @@ -0,0 +1,67 @@
> +#include <ccan/stringbuilder/stringbuilder.h>
> +#include <ccan/stringbuilder/stringbuilder.c>
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <errno.h>
> +/*
> + * This triggers a circular dependency
> + * #include <ccan/str/str.h>
> + *
> + * We only want the following macro:
> + */
> +#define streq(s1,s2) (!strcmp(s1,s2))
> +
> +#include <ccan/tap/tap.h>
> +
> +int main(int argc, char *argv[])
> +{
> +	char string[20];
> +	const char* str_array[] = {
> +		"xxx", "yyy"
> +	};
> +	int res;
> +
> +	res = stringbuilder(string, sizeof(string), NULL,
> +			"aaa", "bbb");
> +	printf("res: %s, string: %s\n",
> +			strerror(res), string);
> +	ok1(res == 0);
> +	ok1(streq(string, "aaabbb"));
> +
> +	res = stringbuilder(string, sizeof(string), NULL,
> +			"aaaaa", "bbbbb", "ccccc", "ddddd",
> +			"eeeee", "fffff");
> +	printf("res: %s, string: %s\n",
> +			strerror(res), string);
> +	ok1(res == EMSGSIZE);
> +
> +	res = stringbuilder(string, sizeof(string), ", ",
> +			"aaa");
> +	printf("res: %s, string: %s\n",
> +			strerror(res), string);
> +	ok1(res == 0);
> +	ok1(streq(string, "aaa"));
> +
> +	res = stringbuilder(string, sizeof(string), ", ",
> +			"aaa", "bbb");
> +	printf("res: %s, string: %s\n",
> +			strerror(res), string);
> +	ok1(res == 0);
> +	ok1(streq(string, "aaa, bbb"));
> +
> +	res = stringbuilder_array(string, sizeof(string), NULL,
> +			sizeof(str_array)/sizeof(str_array[0]), str_array);
> +	printf("res: %s, string: %s\n",
> +			strerror(res), string);
> +	ok1(res == 0);
> +	ok1(streq(string, "xxxyyy"));
> +
> +	res = stringbuilder_array(string, sizeof(string), ", ",
> +			sizeof(str_array)/sizeof(str_array[0]), str_array);
> +	printf("res: %s, string: %s\n",
> +			strerror(res), string);
> +	ok1(res == 0);
> +	ok1(streq(string, "xxx, yyy"));
> +
> +	return exit_status();
> +}				
> -- 
> 1.8.5.5
>
> _______________________________________________
> ccan mailing list
> ccan at lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/ccan


More information about the ccan mailing list