summaryrefslogtreecommitdiff
path: root/libc/stdio/popen.c
blob: 0a91f0e2219cc802c98ab60306e34456197a5edc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/*
 * Modified     3/03/2001       Manuel Novoa III
 *
 * Added check for legal mode arg.
 * Call fdopen and check return value before forking.
 * Reduced code size by using variables pr and pnr instead of array refs.
 */

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>

FILE *popen (const char *command, const char *mode)
{
	FILE *fp;
	int pipe_fd[2];
	int pid, reading;
	int pr, pnr;

	reading = (mode[0] == 'r');
	if ((!reading && (mode[0] != 'w')) || mode[1]) {
		__set_errno(EINVAL);			/* Invalid mode arg. */
	} else if (pipe(pipe_fd) == 0) {
		pr = pipe_fd[reading];
		pnr = pipe_fd[1-reading];
		if ((fp = fdopen(pnr, mode)) != NULL) {
			if ((pid = vfork()) == 0) {	/* vfork -- child */
				close(pnr);
				close(reading);
				if (pr != reading) {
					dup2(pr, reading);
					close(pr);
				}
				execl("/bin/sh", "sh", "-c", command, (char *) 0);
				_exit(255);		/* execl failed! */
			} else {			/* vfork -- parent or failed */
				close(pr);
				if (pid > 0) {	/* vfork -- parent */
					return fp;
				} else {		/* vfork -- failed! */
					fclose(fp);
				}
			}
		} else {				/* fdopen failed */
			close(pr);
			close(pnr);
		}
	}
	return NULL;
}

int pclose(FILE *fd)
{
	int waitstat;

	if (fclose(fd) != 0) {
		return EOF;
	}
	wait(&waitstat);
	return waitstat;
}