Yes, it's a GNU extension, but it's also supported by various BSDs [1][2][3], and yes, Musl has it too. It's present in pretty much any sane C library.
[1] https://man.openbsd.org/man3/printf.3
[2] https://man.netbsd.org/vasprintf.3
[3] https://man.freebsd.org/cgi/man.cgi?query=vasprintf&sektion=...
The problem is that on error the buffer pointer value is undefined, so you can't just unconditionally call free on the pointer. There's at least one proposal for C2x that avoids adopting asprintf for this reason despite it already being added to POSIX.
This undefined'ness is a vestige of the original glibc implementation. The proper solution is to either require that the pointer value be preserved on error (thus preserving NULL if the caller initialized it) or require the implementation set it to NULL. IIRC, when added by the BSDs (1990s) and later Solaris they explicitly documented it to set the pointer to NULL. And it seems that late last year glibc finally adopted this behavior as well.[1]
[1] https://sourceware.org/git/?p=glibc.git;a=commit;h=cb4692ce1...
1. have it free the passed-in buffer, so that you can reuse the same pointer
2. have it do step 1 after the formatting, so the old value can be a format argument
3. when getting the size of the full expansion, don't format to NULL, but do it to a temp buffer (a few KB in size) - then if the expansion is small enough, you can skip the second format into the actual buffer. Just malloc and memcpy. You know how many chars to memcpy, because that's the return value from snprintf
(Don't forget to check for errors and all that.)
You don't really need to, TBH. I pretty much always wrote a malloc-memory `sprintf` alternative if the system didn't have one. it's only a few lines of code, that'll take maybe 10m of your day the first time you realise `sprintf` on the platform doesn't exist.
Here is a sample from more recently: https://github.com/lelanthran/libds/blob/b5289f6437b30139d42...
The lost art of RTFM.
Cisco and many of Ciscro's customers found out the hard way (during CitrixBleed, https://www.assetnote.io/resources/research/citrix-bleed-lea...), leaking random blocks of memory in the proprietary, C-based web server of their security appliance that gets compromised every now and then.
> I have size_with_nul because snprintf man pages say
> The functions snprintf() and vsnprintf() write at most size bytes (including the terminating null byte (‘\0’)) to str.
If 'size' includes the null byte, why do we have to add 1?
The initial call with size 0 tells you the necessary length of the buffer for the string you want, but does not include the null byte.
Way to easy to use the returned value as the “actual length” of the written string. Sure, that was never the intent, but still…
The standard library also makes appending a formatted string to an existing one surprisingly nontrivial…
What should be a 1-liner is about 5-10 lines of code (to include error handling) and is somewhat hard to read. The “cognitive load” for basic operations shouldn’t be high…
I think this was it: https://queue.acm.org/detail.cfm?id=2010365
The number can be determined at comp-time.
Buffer sizes should be computing manually.
https://github.com/RhysU/snprintf_realloc/blob/master/snprin...
Worth critically reviewing before using. It's been a while.