stringbuilder: Functions for joining strings.
authorStuart Longland <stuartl@longlandclan.yi.org>
Fri, 22 Aug 2014 23:25:09 +0000 (09:25 +1000)
committerRusty Russell <rusty@rustcorp.com.au>
Mon, 30 Mar 2015 06:46:32 +0000 (17:16 +1030)
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
ccan/stringbuilder/LICENSE [new symlink]
ccan/stringbuilder/_info [new file with mode: 0644]
ccan/stringbuilder/stringbuilder.c [new file with mode: 0644]
ccan/stringbuilder/stringbuilder.h [new file with mode: 0644]
ccan/stringbuilder/test/run.c [new file with mode: 0644]

index 432705f4b7819e588214ccb6140a5e8e930a2017..54430c03e1adde21f680a24b2e8dbf21d22f266d 100644 (file)
@@ -90,6 +90,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 (symlink)
index 0000000..b7951da
--- /dev/null
@@ -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 (file)
index 0000000..4c78254
--- /dev/null
@@ -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@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 (file)
index 0000000..d34de81
--- /dev/null
@@ -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 (file)
index 0000000..84d79ec
--- /dev/null
@@ -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 (file)
index 0000000..2d119f2
--- /dev/null
@@ -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();
+}