string-util: rework strextend() to optionally inset separators between each appended string

This adds a new flavour of strextend(), called
strextend_with_separator(), which takes an optional separator string. If
specified, the separator is inserted between each appended string, as
well as before the first one, but only if the original string was
non-empty.

This new call is particularly useful when appending new options to mount
option strings and suchlike, which need to be comma-separated, and
initially start out from an empty string.
This commit is contained in:
Lennart Poettering 2017-11-28 16:37:53 +01:00
parent 1cfdbe293f
commit bb8ad9eaca
3 changed files with 58 additions and 9 deletions

View File

@ -734,16 +734,20 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
return obuf;
}
char *strextend(char **x, ...) {
va_list ap;
size_t f, l;
char *strextend_with_separator(char **x, const char *separator, ...) {
bool need_separator;
size_t f, l, l_separator;
char *r, *p;
va_list ap;
assert(x);
l = f = strlen_ptr(*x);
va_start(ap, x);
need_separator = !isempty(*x);
l_separator = strlen_ptr(separator);
va_start(ap, separator);
for (;;) {
const char *t;
size_t n;
@ -753,22 +757,29 @@ char *strextend(char **x, ...) {
break;
n = strlen(t);
if (need_separator)
n += l_separator;
if (n > ((size_t) -1) - l) {
va_end(ap);
return NULL;
}
l += n;
need_separator = true;
}
va_end(ap);
need_separator = !isempty(*x);
r = realloc(*x, l+1);
if (!r)
return NULL;
p = r + f;
va_start(ap, x);
va_start(ap, separator);
for (;;) {
const char *t;
@ -776,10 +787,17 @@ char *strextend(char **x, ...) {
if (!t)
break;
if (need_separator && separator)
p = stpcpy(p, separator);
p = stpcpy(p, t);
need_separator = true;
}
va_end(ap);
assert(p == r + l);
*p = 0;
*x = r;

View File

@ -179,7 +179,9 @@ char *strreplace(const char *text, const char *old_string, const char *new_strin
char *strip_tab_ansi(char **p, size_t *l);
char *strextend(char **x, ...) _sentinel_;
char *strextend_with_separator(char **x, const char *separator, ...) _sentinel_;
#define strextend(x, ...) strextend_with_separator(x, NULL, __VA_ARGS__)
char *strrep(const char *s, unsigned n);

View File

@ -104,9 +104,37 @@ static void test_strstrip(void) {
}
static void test_strextend(void) {
_cleanup_free_ char *str = strdup("0123");
strextend(&str, "456", "78", "9", NULL);
assert_se(streq(str, "0123456789"));
_cleanup_free_ char *str = NULL;
assert_se(strextend(&str, NULL));
assert_se(streq_ptr(str, ""));
assert_se(strextend(&str, "", "0", "", "", "123", NULL));
assert_se(streq_ptr(str, "0123"));
assert_se(strextend(&str, "456", "78", "9", NULL));
assert_se(streq_ptr(str, "0123456789"));
}
static void test_strextend_with_separator(void) {
_cleanup_free_ char *str = NULL;
assert_se(strextend_with_separator(&str, NULL, NULL));
assert_se(streq_ptr(str, ""));
str = mfree(str);
assert_se(strextend_with_separator(&str, "...", NULL));
assert_se(streq_ptr(str, ""));
assert_se(strextend_with_separator(&str, "...", NULL));
assert_se(streq_ptr(str, ""));
str = mfree(str);
assert_se(strextend_with_separator(&str, "xyz", "a", "bb", "ccc", NULL));
assert_se(streq_ptr(str, "axyzbbxyzccc"));
str = mfree(str);
assert_se(strextend_with_separator(&str, ",", "start", "", "1", "234", NULL));
assert_se(streq_ptr(str, "start,,1,234"));
assert_se(strextend_with_separator(&str, ";", "more", "5", "678", NULL));
assert_se(streq_ptr(str, "start,,1,234;more;5;678"));
}
static void test_strrep(void) {
@ -399,6 +427,7 @@ int main(int argc, char *argv[]) {
test_streq_ptr();
test_strstrip();
test_strextend();
test_strextend_with_separator();
test_strrep();
test_strappend();
test_string_has_cc();