ref: 0a6bcc7b83d9b5fac12229cac57207b9dc91ba2f
dir: /sys/src/cmd/troff/popen.c/
/* * popen, pclose - open and close pipes (Plan 9 version) */ #include <u.h> #include <libc.h> #include <stdio.h> enum { Stdin, Stdout, }; enum { Rd, Wr, Maxfd = 200, }; typedef struct { long pid; char *sts; /* malloced; valid if non-nil */ } Pipe; static Pipe pipes[Maxfd]; static int _pipefd(int rfd, int wfd) { close(wfd); return rfd; } FILE * popen(char *file, char *mode) { int pipedes[2]; long pid; if (pipe(pipedes) < 0) /* cat's got the last pipe */ return 0; if ((pid = fork()) < 0) { /* can't fork */ close(pipedes[Rd]); close(pipedes[Wr]); return 0; } /* * The pipe was created and the fork succeeded. * Now fiddle the file descriptors in both processes. */ if (pid == 0) { /* child process */ int sts; /* * If the mode is 'r', the child writes on stdout so the * parent can read on its stdin from the child. * If the mode is not 'r', the child reads on stdin so the * parent can write on its stdout to the child. */ if (mode[0] == 'r') /* read from child */ sts = dup(pipedes[Wr], Stdout); else /* write to child */ sts = dup(pipedes[Rd], Stdin); if (sts < 0) /* couldn't fiddle fd's */ _exits("no pipe"); close(pipedes[Rd]); close(pipedes[Wr]); execl("/bin/rc", "rc", "-c", file, (char *)nil); _exits("no /bin/rc"); /* no shell */ } else { /* parent process */ int fd; /* * If the mode is 'r', the parent reads on its stdin the child; * otherwise the parent writes on its stdout to the child. */ if (mode[0] == 'r') /* read from child */ fd = _pipefd(pipedes[Rd], pipedes[Wr]); else fd = _pipefd(pipedes[Wr], pipedes[Rd]); if (fd >= 0 && fd < Maxfd) { Pipe *pp = pipes + fd; pp->pid = pid; /* save fd's child's pid */ free(pp->sts); pp->sts = nil; } return fdopen(fd, mode[0] == 'r'? "w": "r"); } } static volatile int waiting; static int gotnote(void *, char *note) { if (strcmp(note, "interrupt") == 0) if (waiting) return 1; /* NCONT */ return 0; /* not a known note: NDFLT */ } int pclose(FILE *fp) { int fd; int pid; /* pid, wait status for some child */ Pipe *fpp, *app = nil, *spp; Waitmsg *wm; static int registered; fd = fileno(fp); if (fd < 0 || fd >= Maxfd) return -1; // "fd out of range"; fpp = pipes + fd; if (fpp->pid <= 0) return -1; // "no child process for fd"; close(fd); /* * Ignore notes in case this process was catching them. * Otherwise both this process and its child(ren) would * catch these notes. * Ideally I suppose popen should ignore the notes. */ if (!registered) { atnotify(gotnote, 1); registered = 1; } waiting = 1; /* * Wait for fd's child to die. * ``Bring out your dead!'' */ while (fpp->sts == nil && (wm = wait()) != nil) { pid = wm->pid; /* * See if any fd is attached to this corpse; * if so, give that fd its wait status. */ if (pid == fpp->pid) /* quick check */ app = fpp; else for (spp = pipes; spp < pipes + Maxfd; spp++) if (pid == spp->pid) { app = spp; break; } if (app != nil) { /* record pid's status, possibly for later use */ free(app->sts); app->sts = strdup(wm->msg); } free(wm); } waiting = 0; return fpp->sts != nil && fpp->sts[0] != '\0'? 1: 0; }