/***************************************************************************
 * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne.  JOVE *
 * is provided to you without charge, and with no warranty.  You may give  *
 * away copies of JOVE, including sources, provided that this notice is    *
 * included in all the files.                                              *
 ***************************************************************************/

/* This program is invoked by JOVE for two purposes related to PIPEPROCS:
 * - "kbd": gather tty input into lumps and send them to JOVE proper.
 * - "portsrv": gather process output into lumps and send them to JOVE
 * These functions are gathered into one program to reduce the number
 * of misc. programs to install.  The resulting program is small anyway.
 */

#include "jove.h"

#ifdef	PIPEPROCS	/* the whole file! */

#include <signal.h>
#include <errno.h>
/*#include <sys/ioctl.h>*/

#include "sysprocs.h"
#include "iproc.h"

private struct lump	lump;

#ifdef	BSD_SIGS
# define pause()	sigpause(0L)
#endif

/* JOVE sends KBDSIG whenever it wants the kbd process (this program)
   to stop competing for input from the keyboard.  JOVE does this when
   JOVE realizes that there are no more interactive processes running.
   The reason we go through all this trouble is that JOVE slows down
   a lot when it's getting its keyboard input via a pipe. */

private SIGRESTYPE strt_read proto((int));

private SIGRESTYPE
hold_read(junk)
int	junk;	/* passed in when invoked by a signal; of no interest */
{
	signal(KBDSIG, strt_read);
	pause();
	return SIGRESVALUE;
}

private SIGRESTYPE
strt_read(junk)
int	junk;
{
	signal(KBDSIG, hold_read);
	return SIGRESVALUE;
}

private void
kbd_process()
{
	int	pid,
		n;

	signal(SIGINT, SIG_IGN);
	pid = getpid();
	lump.header.pid = pid;

	strt_read(0);
	for (;;) {
		n = read(0, (UnivPtr) lump.data, sizeof(lump.data));
		if (n == -1) {
			if (errno != EINTR)
				break;
			continue;
		}
		lump.header.nbytes = n;
		write(1, (UnivPtr) &lump, sizeof(struct header) + n);
	}
}

/* This is a server for jove sub processes.  By the time we get here, our
   standard output goes to jove's process input. */

private int	tty_fd;

private void
proc_write(ptr, n)
UnivConstPtr	ptr;
size_t	n;
{
	(void) write(1, ptr, n);
}

private void
read_pipe(fd)
int	fd;
{
	register size_t	n;

	while ((lump.header.nbytes = read(fd, (UnivPtr) lump.data, sizeof lump.data)) > 0) {
		n = sizeof(struct header) + lump.header.nbytes;
		proc_write((UnivConstPtr) &lump, n);
	}
}

private void
proc_error(str)
char	*str;
{
	lump.header.pid = getpid();
	lump.header.nbytes = strlen(str);
	strcpy(lump.data, str);
	proc_write((UnivConstPtr) &lump, sizeof(struct header) + lump.header.nbytes);
	write(tty_fd, (UnivConstPtr)str, strlen(str));
	exit(-2);
}

private void
portsrv_process(argc, argv)
int	argc;
char	**argv;
{
	int	p[2];
	pid_t	pid;

	if (pipe(p) == -1)
		proc_error("Cannot pipe jove portsrv.\n");

	switch (pid = fork()) {
	case -1:
		proc_error("portsrv: cannot fork.\n");
		/*NOTREACHED*/

	case 0:
		/* We'll intercept child's output in p[0] */
		(void) dup2(p[1], 1);
		(void) dup2(p[1], 2);
		(void) close(p[0]);
		(void) close(p[1]);

#ifdef	SYSV
		(void) setpgrp();
#else
		(void) setpgrp(getpid(), getpid());
#endif
		execv(argv[1], (char *const *) &argv[2]);
		_exit(-4);
		/*NOTREACHED*/

	default:
		(void) close(0);
		tty_fd = open("/dev/tty", 1);

		(void) signal(SIGINT, SIG_IGN);
		(void) signal(SIGQUIT, SIG_IGN);
		(void) close(p[1]);

		/* tell jove the pid of the real child as opposed to us */
		lump.header.pid = getpid();
		lump.header.nbytes = sizeof (pid_t);
		byte_copy((UnivConstPtr) &pid, (UnivPtr) lump.data, sizeof(pid_t));
		(void) write(1, (UnivConstPtr) &lump, sizeof(struct header) + sizeof(pid_t));

		/* read proc's output and send it to jove */
		read_pipe(p[0]);

		/* received EOF - wait for child to die and then exit
		   ourself in the same way so that JOVE knows how the
		   child died.  This is sort of a kludge, but the alternative
		   is to write the childs status to JOVE, which seems sorta
		   yucky, too.

		   Actually, 4 or 5 years later I like that idea much better,
		   so remind me to implement it that way when I get a chance.

		   7-23-89  Gee thanks, whoever implemented this for me! */

		(void) close(p[0]);
		lump.header.pid = getpid();
		lump.header.nbytes = EOF;	/* tell jove we are finished */
		/* try to exit like our child did ... */
		{
			wait_status_t	status;

			do ; while (wait(&status) != pid);
			byte_copy((UnivPtr)&status, (UnivPtr)lump.data,
				sizeof(status));
		}
		(void) write(1, (UnivConstPtr) &lump,
			sizeof(struct header) + sizeof(wait_status_t));
	}
}

int
main(argc, argv)
int	argc;
char	**argv;
{
	if (strcmp(argv[0], "kbd") == 0)
		kbd_process();
	else
		portsrv_process(argc, argv);
	return 0;
}

#else	/* !PIPEPROCS */
int
main()
{
	return 0;
}
#endif	/* !PIPEPROCS */
