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

stuartl at longlandclan.yi.org stuartl at longlandclan.yi.org
Sun Aug 17 21:16:05 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.
---
 Makefile-ccan                      |   1 +
 ccan/stringbuilder/LICENSE         |   1 +
 ccan/stringbuilder/_info           |  55 ++++++++++++++++++
 ccan/stringbuilder/stringbuilder.c |  71 ++++++++++++++++++++++++
 ccan/stringbuilder/stringbuilder.h | 111 +++++++++++++++++++++++++++++++++++++
 ccan/stringbuilder/test/run.c      |  68 +++++++++++++++++++++++
 6 files changed, 307 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..f2780e7
--- /dev/null
+++ b/ccan/stringbuilder/_info
@@ -0,0 +1,55 @@
+#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, 
+ *			(const char**)argv, argc);
+ *		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", NULL);
+ *			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) {
+		return 0;
+	}
+
+	return 1;
+}
diff --git a/ccan/stringbuilder/stringbuilder.c b/ccan/stringbuilder/stringbuilder.c
new file mode 100644
index 0000000..cb2bc2d
--- /dev/null
+++ b/ccan/stringbuilder/stringbuilder.c
@@ -0,0 +1,71 @@
+/* CC0 (Public domain) - see LICENSE file for details */
+#include <ccan/stringbuilder/stringbuilder.h>
+#include <string.h>
+#include <errno.h>
+
+int stringbuilder(const char* delim, char* str, size_t str_sz, ...)
+{
+	int res;
+	va_list ap;
+	va_start(ap, str_sz);
+	res = stringbuilder_va(delim, str, str_sz, ap);
+	va_end(ap);
+	return res;
+}
+
+static inline int stringbuilder_cpy(
+		char** str, size_t* str_sz, const char* s, size_t s_len)
+{
+	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(const char* delim, char* str, size_t str_sz, 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);
+
+	if (s)
+		res = stringbuilder_cpy(&str, &str_sz, s, 0);
+	while(s && !res) {
+		s = va_arg(ap, const char*);
+		if (s && delim)
+			res = stringbuilder_cpy(&str, &str_sz,
+					delim, delim_len);
+		if (s && !res)
+			res = stringbuilder_cpy(&str, &str_sz,
+						s, 0);
+	}
+	return res;
+}
+
+int stringbuilder_array(const char* delim, char* str, size_t str_sz,
+		const char** strings, size_t strings_sz)
+{
+	int res = 0;
+	size_t delim_len = 0;
+
+	if (delim)
+		delim_len = strlen(delim);
+
+	while(strings_sz-- && !res) {
+		res = stringbuilder_cpy(&str, &str_sz, *(strings++), 0);
+		if (!res && delim)
+			res = stringbuilder_cpy(&str, &str_sz,
+					delim, delim_len);
+	}
+	return res;
+
+}
diff --git a/ccan/stringbuilder/stringbuilder.h b/ccan/stringbuilder/stringbuilder.h
new file mode 100644
index 0000000..0d02dcb
--- /dev/null
+++ b/ccan/stringbuilder/stringbuilder.h
@@ -0,0 +1,111 @@
+/* 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* and must end with a NULL.  If the
+ * first argument is str, then the contents of str are preserved and appended
+ * to.
+ *
+ * @delim:	A delimiter to separate the strings with, or NULL.
+ * @str:	A pointer to a string buffer that will receive the result.
+ * @str_sz:	Size of the buffer pointed to by str.
+ *
+ * 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",
+ * 		NULL);
+ * 	if (res)
+ * 		printf("Failed to determine file name: %s",
+ * 			strerror(res));
+ * 	else
+ * 		printf("File is at %s", file_name);
+ */
+int stringbuilder(const char* delim, char* str, size_t str_sz, ...);
+
+/**
+ * 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.
+ *
+ * @delim:	A delimiter to separate the strings with, or NULL.
+ * @str:	A pointer to a string buffer that will receive the result.
+ * @str_sz:	Size of the buffer pointed to by str.
+ *
+ * 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(const char* delim, char* str,
+ *		size_t str_sz, ...);
+ *
+ *	int my_stringbuilder(const char* delim, char* str,
+ *		size_t str_sz, ...)
+ *	{
+ *		int res;
+ *		va_list ap;
+ *		va_start(ap, str_sz);
+ *		res = stringbuilder_va(delim, str, str_sz, 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(const char* delim, char* str, size_t str_sz, va_list ap);
+
+/**
+ * stringbuilder_array - Join strings from an array of const char* pointers.
+ *
+ * @delim:	A delimiter to separate the strings with, or NULL.
+ * @str:	A pointer to a string buffer that will receive the result.
+ * @str_sz:	Size of the buffer pointed to by str.
+ * @strings:	The array of strings to join.
+ * @strings_sz:	The number 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),
+ * 		(const char**)argv, argc);
+ * 	if (res)
+ * 		printf("Failed to list arguments: %s",
+ * 			strerror(res));
+ * 	else
+ * 		printf("My arguments were %s", my_args);
+ */
+int stringbuilder_array(const char* delim, char* str, size_t str_sz,
+		const char** strings, size_t strings_sz);
+
+#endif /* CCAN_STRINGBUILDER_H */
diff --git a/ccan/stringbuilder/test/run.c b/ccan/stringbuilder/test/run.c
new file mode 100644
index 0000000..08d61a0
--- /dev/null
+++ b/ccan/stringbuilder/test/run.c
@@ -0,0 +1,68 @@
+#include <ccan/stringbuilder/stringbuilder.h>
+#include <ccan/stringbuilder/stringbuilder.c>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ccan/tap/tap.h>
+
+/* 
+ * Even though stringbuilder is implemented using
+ * stringbuilder_va, we need to test it explicitly.
+ */
+int test_stringbuilder_va(const char* delim, char* str, size_t str_sz, ...);
+
+int test_stringbuilder_va(const char* delim, char* str, size_t str_sz, ...)
+{
+	int res;
+	va_list ap;
+	va_start(ap, str_sz);
+	res = stringbuilder_va(delim, str, str_sz, ap);
+	va_end(ap);
+	return res;
+}
+
+int main(int argc, char *argv[])
+{
+	char string[20];
+	const char* str_array[] = {
+		"xxx", "yyy"
+	};
+	int res;
+
+	res = test_stringbuilder_va(NULL, string, sizeof(string),
+			"aaa", "bbb", NULL);
+	printf("res: %s, string: %s\n",
+			strerror(res), string);
+	ok1(res == 0);
+	ok1(!strcmp(string, "aaabbb"));
+
+	res = stringbuilder(NULL, string, sizeof(string),
+			"aaa", "bbb", NULL);
+	printf("res: %s, string: %s\n",
+			strerror(res), string);
+	ok1(res == 0);
+	ok1(!strcmp(string, "aaabbb"));
+
+	res = stringbuilder(NULL, string, sizeof(string),
+			"aaaaa", "bbbbb", "ccccc", "ddddd",
+			"eeeee", "fffff", NULL);
+	printf("res: %s, string: %s\n",
+			strerror(res), string);
+	ok1(res == EMSGSIZE);
+
+	res = stringbuilder(", ", string, sizeof(string),
+			"aaa", "bbb", NULL);
+	printf("res: %s, string: %s\n",
+			strerror(res), string);
+	ok1(res == 0);
+	ok1(!strcmp(string, "aaa, bbb"));
+
+	res = stringbuilder_array(NULL, string, sizeof(string),
+			str_array, sizeof(str_array)/sizeof(str_array[0]));
+	printf("res: %s, string: %s\n",
+			strerror(res), string);
+	ok1(res == 0);
+	ok1(!strcmp(string, "xxxyyy"));
+
+	return exit_status();
+}				
-- 
1.8.5.5



More information about the ccan mailing list