From: Lennart Poettering Date: Wed, 5 May 2021 07:56:46 +0000 (+0200) Subject: string-util: add strextendf() helper, that allows extending some allocated string... X-Git-Tag: v249-rc1~281 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=e9b88a6d4ec7818a334ee12f50f23706bcc3c7e0;p=thirdparty%2Fsystemd.git string-util: add strextendf() helper, that allows extending some allocated string via a format string It's not going to be efficient if called in inner loops, but it's oh so handy, and we have some code that does this: asprintf(&p, "%s…", b, …); free(b); b = TAKE_PTR(p); which can now be replaced by the quicker and easier to read: strextendf(&p, "…", …); --- diff --git a/src/basic/string-util.c b/src/basic/string-util.c index 058eec54ff9..60c877ce71f 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -803,6 +803,87 @@ char *strextend_with_separator_internal(char **x, const char *separator, ...) { return p; } +int strextendf(char **x, const char *format, ...) { + size_t m, a; + va_list ap; + int l; + + /* Appends a formatted string to the specified string. Don't use this in inner loops, since then + * we'll spend a tonload of time in determining the length of the string passed in, over and over + * again. */ + + assert(x); + assert(format); + + /* Let's try to use the allocated buffer, if there's room at the end still. Otherwise let's extend by 64 chars. */ + if (*x) { + m = strlen(*x); + a = malloc_usable_size(*x); + assert(a >= m + 1); + } else + m = a = 0; + + if (a - m < 17) { /* if there's less than 16 chars space, then enlarge the buffer first */ + char *n; + + if (_unlikely_(m > SIZE_MAX - 64)) /* overflow check */ + return -ENOMEM; + + n = realloc(*x, m + 64); + if (!n) + return -ENOMEM; + + *x = n; + a = malloc_usable_size(*x); + } + + /* Now, let's try to format the string into it */ + va_start(ap, format); + l = vsnprintf(*x + m, a - m, format, ap); + va_end(ap); + + assert(l >= 0); + + if ((size_t) l < a - m) { + char *n; + + /* Nice! This worked. We are done. But first, let's return the extra space we don't + * need. This should be a cheap operation, since we only lower the allocation size here, + * never increase. */ + n = realloc(*x, m + (size_t) l + 1); + if (n) + *x = n; + } else { + char *n; + + /* Wasn't enough. Then let's allocate exactly what we need. */ + + if (_unlikely_((size_t) l > SIZE_MAX - 1)) /* overflow check #1 */ + goto oom; + if (_unlikely_(m > SIZE_MAX - ((size_t) l + 1))) /* overflow check #2 */ + goto oom; + + a = m + (size_t) l + 1; + n = realloc(*x, a); + if (!n) + goto oom; + *x = n; + + va_start(ap, format); + l = vsnprintf(*x + m, a - m, format, ap); + va_end(ap); + + assert((size_t) l < a - m); + } + + return 0; + +oom: + /* truncate the bytes added after the first vsnprintf() attempt again */ + (*x)[m] = 0; + return -ENOMEM; +} + char *strrep(const char *s, unsigned n) { char *r, *p; size_t l; diff --git a/src/basic/string-util.h b/src/basic/string-util.h index cb2881b64d1..6364a0a55f5 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -152,6 +152,8 @@ char *strextend_with_separator_internal(char **x, const char *separator, ...) _s #define strextend_with_separator(x, separator, ...) strextend_with_separator_internal(x, separator, __VA_ARGS__, NULL) #define strextend(x, ...) strextend_with_separator_internal(x, NULL, __VA_ARGS__, NULL) +int strextendf(char **x, const char *format, ...) _printf_(2,3); + char *strrep(const char *s, unsigned n); int split_pair(const char *s, const char *sep, char **l, char **r); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index ed9b31e63ba..41ae043c6e9 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -2967,19 +2967,17 @@ static int determine_names(void) { if (!arg_machine) { if (arg_directory && path_equal(arg_directory, "/")) arg_machine = gethostname_malloc(); - else { - if (arg_image) { - char *e; + else if (arg_image) { + char *e; - arg_machine = strdup(basename(arg_image)); + arg_machine = strdup(basename(arg_image)); - /* Truncate suffix if there is one */ - e = endswith(arg_machine, ".raw"); - if (e) - *e = 0; - } else - arg_machine = strdup(basename(arg_directory)); - } + /* Truncate suffix if there is one */ + e = endswith(arg_machine, ".raw"); + if (e) + *e = 0; + } else + arg_machine = strdup(basename(arg_directory)); if (!arg_machine) return log_oom(); @@ -2987,20 +2985,11 @@ static int determine_names(void) { if (!hostname_is_valid(arg_machine, 0)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine machine name automatically, please use -M."); - if (arg_ephemeral) { - char *b; - - /* Add a random suffix when this is an - * ephemeral machine, so that we can run many - * instances at once without manually having - * to specify -M each time. */ - - if (asprintf(&b, "%s-%016" PRIx64, arg_machine, random_u64()) < 0) + /* Add a random suffix when this is an ephemeral machine, so that we can run many + * instances at once without manually having to specify -M each time. */ + if (arg_ephemeral) + if (strextendf(&arg_machine, "-%016" PRIx64, random_u64()) < 0) return log_oom(); - - free(arg_machine); - arg_machine = b; - } } return 0; diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c index 15ea24ae4be..8174b128370 100644 --- a/src/test/test-string-util.c +++ b/src/test/test-string-util.c @@ -970,6 +970,22 @@ static void test_strverscmp_improved(void) { assert_se(strverscmp_improved("123_aa2-67.89", "123aa+2-67.89") == 0); } +static void test_strextendf(void) { + _cleanup_free_ char *p = NULL; + + assert_se(strextendf(&p, "<%i>", 77) >= 0); + assert_se(streq(p, "<77>")); + + assert_se(strextendf(&p, "<%i>", 99) >= 0); + assert_se(streq(p, "<77><99>")); + + assert_se(strextendf(&p, "<%80i>", 88) >= 0); + assert_se(streq(p, "<77><99>< 88>")); + + assert_se(strextendf(&p, "<%08x>", 0x1234) >= 0); + assert_se(streq(p, "<77><99>< 88><00001234>")); +} + int main(int argc, char *argv[]) { test_setup_logging(LOG_DEBUG); @@ -1008,6 +1024,7 @@ int main(int argc, char *argv[]) { test_string_contains_word_strv(); test_string_contains_word(); test_strverscmp_improved(); + test_strextendf(); return 0; }