diff --git a/src/util-strings.c b/src/util-strings.c index f7edc7e..9cd3d5e 100644 --- a/src/util-strings.c +++ b/src/util-strings.c @@ -24,6 +24,8 @@ #include "config.h" +#include + #include "util-strings.h" /** @@ -156,6 +158,71 @@ strv_join(char **strv, const char *joiner) return str; } +char * +strreplace(const char *string, const char *separator, const char *replacement) +{ + assert(string != NULL); + assert(string[0] != '\0'); + + /* Enough to replace every character in the string with the + * replacement. This will blow up on extremely long strings with long + * replacements, but meh. It saves us having to write resizing code. + */ + size_t slen = strlen(string); + size_t splen = strlen(separator); + + const char *current = string; + const char *next; + + next = strstr(current, separator); + if (!next) /* No separator found */ + return xstrdup(string); + + size_t rlen = strlen(replacement); + size_t max = slen * max(rlen, 1); + char *r = calloc(max + 1, 1); /* the result, one extra for terminating \0 */ + char *destptr = r; + +/* our strncpy calls truncate the second argument, we know... */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-truncation" + while (next) { + size_t len = next - current; + /* silently truncate because we really don't care about this + * case */ + if (destptr + len > r + max) + break; + + /* Copy the source string over, then append the separator */ + strncpy(destptr, current, len); + destptr += len; + if (destptr + rlen > r + max) + break; + strncpy(destptr, replacement, rlen); + destptr += rlen; + + current = next + splen; + if (current > string + max) + break; + next = strstr(current, separator); + + } + + size_t len = strlen(current); + /* silently truncate because we really don't care about this + * case */ + if (destptr + len <= r + max) { + strncpy(destptr, current, len); + destptr += len; + } +#pragma GCC diagnostic pop + + void *tmp = realloc(r, (destptr - r) + 1); + assert(tmp); + return tmp; +} + + #if _enable_tests_ #include "util-munit.h" @@ -377,4 +444,30 @@ MUNIT_TEST(test_strendswith) return MUNIT_OK; } + +MUNIT_TEST(test_strreplace) +{ + struct strtest { + const char *string; + const char *separator; + const char *replacement; + const char *expected; + } tests[] = { + { "teststring", "-", ".", "teststring" }, + { "test-string", "-", ".", "test.string" }, + { "test.string.", ".", "xyz", "testxyzstringxyz" }, + { "ftestfstringf", "f", "", "teststring" }, + { "xxx", "x", "y", "yyy" }, + { "xyz", "x", "y", "yyz" }, + { "xyz", "xy", "y", "yz" }, + { .string = NULL }, + }; + + for (struct strtest *t = tests; t->string; t++) { + _cleanup_free_ char *s = strreplace(t->string, t->separator, t->replacement); + munit_assert_string_equal(t->expected, s); + } + + return MUNIT_OK; +} #endif diff --git a/src/util-strings.h b/src/util-strings.h index 99747e8..d2891c0 100644 --- a/src/util-strings.h +++ b/src/util-strings.h @@ -257,6 +257,8 @@ strv_free(char **strv) { DEFINE_TRIVIAL_CLEANUP_FUNC(char **, strv_free); +char * strreplace(const char *string, const char *separator, const char *replacement); + struct key_value_str{ char *key; char *value;