Consider the following test case:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdarg.h>
void test(char **outa, char **outb, const char* fstra, const char* fstrb, ...) {
va_list ap;
va_start(ap, fstrb);
vasprintf(&outa, fstra, ap);
vasprintf(&outb, fstrb, ap);
va_end(ap);
}
int main(void) {
char *a, *b;
test(&a, &b, "%s", " %s\n", "foo", "bar");
/* ... */
}
The intent here is that the test() function takes two format strings and a list of parameters for both of them. The first format string is supposed to ‘eat’ as many arguments it needs, and the remaining ones are supposed to be used for the second format string.
So, the expected result here would be foo & bar and that’s what I get with glibc. But AFAICS the machine running codepad (guess some *BSD it is), gives foo & foo and my guess is that it uses va_copy() on the argument list.
I guess I’m hitting an undefined (and ugly) behavior here; so the question is: is there a way to achieve double-format-string printf() without reimplementing it from scratch? And is there a nice way to check that behavior using autoconf without using AC_RUN_IFELSE()?
I guess some quick method of scanning format-string for the number of arguments to be consumed could work here as well (+va_copy()).
As the other answer already states, passing
apto a v*() function leavesapin an undetermined state. So, the solution is to not depend on this state. I suggest an alternative workaround.First, initialize ap as normal. Then determine the length of the first formatted string using
vsnprintf(NULL, 0, fstra, ap). Concatenate the format strings, reinitializeap, and split the output using the predetermined length of the first formatted string.It should look something like the following:
As also discussed in the other answer, this approach does not separate positional
printf-style placeholders ("%1$s. I repeat, %1$s."). So the documentation for the interface should clearly state that both format strings share the same positional placeholder namespace—and that if one of the format strings uses positional placeholders then both must.