For a project of mine, I created a function to run external commands (reduced to what's important for my question):
int run_command(char **output, int *retval, const char *command, const char* const args[])
{
...
pid_t pid = fork();
if (pid == 0) {
...
execvp(command, (char * const *)args);
}
...
}
The function is called like this:
char *output;
int retval;
const char *command = "find";
const char* const args[] = { command, "/tmp", "-type", "f", NULL };
run_command(&output, &retval, command, args);
Now, I created a wrapper that uses variadic arguments instead of an array of arguments:
int run_command2(char **output, int *retval, const char *command, ...)
{
va_list val;
const char **args = NULL;
int argc;
int result;
// Determine number of variadic arguments
va_start(val, command);
argc = 2; // leading command + trailing NULL
while (va_arg(val, const char *) != NULL)
argc++;
va_end(val);
// Allocate args, put references to command / variadic arguments + NULL in args
args = (const char **) malloc(argc * sizeof(char*));
args[0] = command;
va_start(val, command);
int i = 0;
do {
fprintf(stderr, "Argument %i: %s\n", i, args[i]);
i++;
args[i] = va_arg(val, const char *);
} while (args[i] != NULL);
va_end(val);
// Run command, free args, return result
result = run_command(output, retval, command, args);
free(args);
return result;
}
EDIT: note on do-while loop:
For the last element, fprintf(stderr, "Argument %i: %s\n", i, NULL) is called, which is valid on GCC and will simply print '(null)'. For other compilers, behavior might be different or undefined. Thanks to @GiovanniCerretani for pointing this out.
The wrapper is called like this:
char *output;
int retval;
run_command2(&output, &retval, "find", "/tmp", "-type", "f", NULL);
My question:
The wrapper seems to work fine (Linux/x64/GCC 9.2.0), but is this actually a valid way to convert variadic arguments to array? Or does this just work by accident? The documentation on va_* is quite thin, e.g. there's no mention if a string retrieved using va_arg() remains valid when va_arg() is called again or after calling va_end().
do/whileseems bugged as you are printing aNULLpointer with%s.NULL, that is setargs's last element toNULL.args's last element is NULLchar *args[argc[ ; argv[0] = command ; ...