util: add a strreplace function

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
Peter Hutterer 2021-08-27 10:19:06 +10:00
parent 4b3b48291b
commit 43be3ddc89
2 changed files with 95 additions and 0 deletions

View file

@ -24,6 +24,8 @@
#include "config.h"
#include <string.h>
#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

View file

@ -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;