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

stuartl at longlandclan.yi.org stuartl at longlandclan.yi.org
Sat Aug 23 09:25:09 EST 2014


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.
---
 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



More information about the ccan mailing list