/*
 * popen - open a pipe
 */
/* $Header: popen.c,v 1.3 90/08/28 14:53:34 eck Exp $ */

#include	<sys/types.h>
#include	<limits.h>
#include	<errno.h>
#include	<signal.h>
#include	<stdio.h>

#if	defined(__BSD4_2)
union wait {
	int	w_status;
};
typedef union wait wait_arg;
#else
typedef int wait_arg;
#endif	/* __BSD4_2 */

#include	"../stdio/loc_incl.h"

#ifdef _ANSI
int _close(int d);
int _dup2(int oldd, int newd);		/* not present in System 5 */
int _execl(const char *name, const char *_arg, ... );
pid_t _fork(void);
int _pipe(int fildes[2]);
pid_t _wait(wait_arg *status);
void _exit(int status);
#endif

static int pids[OPEN_MAX];

FILE *
popen(command, type)
_CONST char *command;
_CONST char *type;
{
	int piped[2];
	int Xtype = *type == 'r' ? 0 : *type == 'w' ? 1 : 2;
	int pid;

	if (Xtype == 2 ||
	    _pipe(piped) < 0 ||
	    (pid = _fork()) < 0) return 0;
	
	if (pid == 0) {
		/* child */
		register int *p;

		for (p = pids; p < &pids[OPEN_MAX]; p++) {
			if (*p) _close((int)(p - pids));
		}
		_close(piped[Xtype]);
		_dup2(piped[!Xtype], !Xtype);
		_close(piped[!Xtype]);
		_execl("/bin/sh", "sh", "-c", command, (char *) 0);
		_exit(127);	/* like system() ??? */
	}

	pids[piped[Xtype]] = pid;
	_close(piped[!Xtype]);
	return fdopen(piped[Xtype], type);
}

#if	defined(__BSD4_2)
#define	ret_val	status.w_status
#else
#define	ret_val	status
#endif

int
pclose(stream)
FILE *stream;
{
	int fd = fileno(stream);
	wait_arg status;
	int wret;

#ifdef _ANSI
	void (*intsave)(int) = signal(SIGINT, SIG_IGN);
	void (*quitsave)(int) = signal(SIGQUIT, SIG_IGN);
#else
	void (*intsave)() = signal(SIGINT, SIG_IGN);
	void (*quitsave)() = signal(SIGQUIT, SIG_IGN);
#endif
	fclose(stream);
	while ((wret = _wait(&status)) != -1) {
		if (wret == pids[fd]) break;
	}
	if (wret == -1) ret_val = -1;
	signal(SIGINT, intsave);
	signal(SIGQUIT, quitsave);
	pids[fd] = 0;
	return ret_val;
}

#if	defined(__USG)
int _dup(int fildes);

static int
_dup2(oldd, newd)
int oldd, newd;
{
	int i = 0, fd, tmp;
	int fdbuf[_NFILES];

	/* ignore the error on the close() */
	tmp = errno; (void) _close(newd); errno = tmp;
	while ((fd = _dup(oldd)) != newd) {
		if (fd == -1) break;
		fdbuf[i++] = fd;
	}
	tmp = errno;
	while (--i >= 0) {
		_close(fdbuf[i]);
	}
	errno = tmp;
	return -(fd == -1);
}
#endif	/* __USG */
