I do not trust popen/pclose , since I worked on too many systems where SIGCHLD handled slightly differently. And I don't trust the sh parsing used by popen , as I rarely use it.
A short 22-year-old book by O'Reilly Using C on UNIX , Dave Curry is still a very good reference for this kind of thing.
Anyway, here is some code. It is a bit long as it parses a sample string "/bin/ls /etc" in the array {"/bin/ls", "/etc", 0} . But I find that using the string format is simpler and shorter than 98% of the time, although this example contradicts this.
This code generates a list /etc. You will need to change some things, for example, for example. NUMBER() , which is the same as XtNumber() . And you need to decide if this matches your SIGCHLD processing.
int main(void) { // list the files in /etc char buf[100]; FILE *fp; int pid = spawnfp("/bin/ls /etc", &fp); while (fgets(buf, sizeof buf, fp)) printf("%s", buf); fclose(fp); // pclose() replacement kill(pid, SIGKILL); // pclose() replacement return 0; }
The following routines are listed below:
static int spawnpipe(const char *argv[], int *fd) // popen() replacement { int pid; int pipe_fds[2]; if (pipe(pipe_fds) < 0) FatalError("pipe"); switch ((pid = fork())) { case -1: FatalError("fork"); case 0: // child close(1); close(2); dup(pipe_fds[0]); dup(pipe_fds[1]); close(pipe_fds[0]); close(pipe_fds[1]); execv(argv[0], (char * const *)argv); perror("execv"); _exit(EXIT_FAILURE); // sic, not exit() default: *fd = pipe_fds[0]; close(pipe_fds[1]); return pid; } }
This converts the ascii string to an argv list, which is probably useless for you:
Bool convertStringToArgvList(char *p, const char **argv, int maxNumArgs) { // Break up a string into tokens, on spaces, except that quoted bits, // with single-quotes, are kept together, without the quotes. Such // single-quotes cannot be escaped. A double-quote is just an ordinary char. // This is a *very* basic parsing, but ok for pre-programmed strings. int cnt = 0; while (*p) { while (*p && *p <= ' ') // skip spaces p++; if (*p == '\'') // single-quote block { if (cnt < maxNumArgs) argv[cnt++] = ++p; // drop quote while (*p && *p != '\'') p++; } else if (*p) // simple space-delineated token { if (cnt < maxNumArgs) argv[cnt++] = p; while (*p > ' ') p++; } if (*p) *p++ = 0; // nul-terminate } if (cnt < maxNumArgs) argv[cnt++] = 0; return cnt <= maxNumArgs; // check for too many tokens (unlikely) }
This converts the argument string to tokens and, more importantly, fd to fp , as the OP requested stdout :
int spawnfp(const char *command, FILE **fpp) { const char *argv[100]; int fd, pid; if (!convertStringToArgvList(strdupa(command), argv, NUMBER(argv))) FatalError("spawnfp"); pid = spawnpipe(argv, &fd); *fpp = fdopen(fd, "r"); return pid; }