Commit 495f0115 authored by Stuart Longland's avatar Stuart Longland Committed by Rusty Russell

stringbuilder: Functions for joining strings.

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.
parent f2f6e753
......@@ -90,6 +90,7 @@ MODS_WITH_SRC := antithread \
siphash \
sparse_bsearch \
str \
stringbuilder \
stringmap \
strmap \
strset \
......
../../licenses/CC0
\ No newline at end of file
#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;
}
/* 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;
}
/* 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 */
#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();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment