[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