asprintf is nice to use, but the _documented_ error return convention is
unclear:
> If memory allocation wasn't possible, or some other error occurs,
> these functions will return -1, and the contents of strp are undefined.
What exactly "undefined" means is up for debate: if it was really
undefined, the caller wouldn't be able to meaningfully clean up, because
they wouldn't know if strp is a valid pointer. So far we interpreted
"undefined" — in some parts of the code base — as "either NULL or a
valid pointer that needs to be freed", and — in other parts of the
codebase — as "always NULL". I checked glibc and musl, and they both
uncoditionally set the output pointer to NULL on failure.
There is also no information _why_ asprintf failed. It could be an
allocation error or format string error. But we just don't have this
information.
Let's add a wrapper that either returns a good string or a NULL pointer.
Since there's just one failure result, we don't need a separate return
value and an output argument and can simplify callers.
section of the `alloca(3)` man page.
- If you want to concatenate two or more strings, consider using `strjoina()`
- or `strjoin()` rather than `asprintf()`, as the latter is a lot slower. This
- matters particularly in inner loops (but note that `strjoina()` cannot be
- used there).
+ or `strjoin()` rather than `asprintf()` or `asprintf_safe`, as the latter is
+ a lot slower. This matters particularly in inner loops (but note that
+ `strjoina()` cannot be used there).
## Runtime Behaviour
'sort-util.c',
'stat-util.c',
'static-destruct.c',
+ 'stdio-util.c',
'strbuf.c',
'string-table.c',
'string-util.c',
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "stdio-util.h"
+
+char* asprintf_safe(const char *restrict fmt, ...) {
+ _cleanup_free_ char *buf = NULL;
+ va_list ap;
+ int r;
+
+ va_start(ap, fmt);
+ r = vasprintf(&buf, fmt, ap);
+ va_end(ap);
+
+ if (r < 0)
+ return NULL;
+ return TAKE_PTR(buf);
+}
#define xsprintf(buf, fmt, ...) \
assert_message_se(snprintf_ok(buf, ELEMENTSOF(buf), fmt, ##__VA_ARGS__), "xsprintf: buffer too small")
+char* asprintf_safe(const char *restrict fmt, ...) _printf_(1, 2);
+
#define VA_FORMAT_ADVANCE(format, ap) \
do { \
int _argtypes[128]; \