If you want the output in a string (char *), here's an option (for Linux at least):
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <unistd.h>
char* qx(char** cmd, int inc_stderr) {
int stdout_fds[2];
pipe(stdout_fds);
int stderr_fds[2];
if (!inc_stderr) {
pipe(stderr_fds);
}
const pid_t pid = fork();
if (!pid) {
close(stdout_fds[0]);
dup2(stdout_fds[1], 1);
if (inc_stderr) {
dup2(stdout_fds[1], 2);
}
close(stdout_fds[1]);
if (!inc_stderr) {
close(stderr_fds[0]);
dup2(stderr_fds[1], 2);
close(stderr_fds[1]);
}
execvp(*cmd, cmd);
exit(0);
}
close(stdout_fds[1]);
const int buf_size = 4096;
char* out = malloc(buf_size);
int out_size = buf_size;
int i = 0;
do {
const ssize_t r = read(stdout_fds[0], &out[i], buf_size);
if (r > 0) {
i += r;
}
if (out_size - i <= 4096) {
out_size *= 2;
out = realloc(out, out_size);
}
} while (errno == EAGAIN || errno == EINTR);
close(stdout_fds[0]);
if (!inc_stderr) {
close(stderr_fds[1]);
do {
const ssize_t r = read(stderr_fds[0], &out[i], buf_size);
if (r > 0) {
i += r;
}
if (out_size - i <= 4096) {
out_size *= 2;
out = realloc(out, out_size);
}
} while (errno == EAGAIN || errno == EINTR);
close(stderr_fds[0]);
}
int r, status;
do {
r = waitpid(pid, &status, 0);
} while (r == -1 && errno == EINTR);
out[i] = 0;
return out;
}
int main() {
char* argv[3];
argv[0] = "ls";
argv[1] = "-la";
argv[2] = NULL;
char* out = qx(argv, 0);
printf("%s", out);
free(out);
}