/* Copyright (c)1994-2000 Begemot Computer Associates. All rights reserved.
 * See the file COPYRIGHT for details of redistribution and use. */

# include <stdio.h>
# include <stdlib.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/ioctl.h>
# include <sys/time.h>
# include <errno.h>
# include <termios.h>
# include <stdarg.h>
# include <signal.h>
# include <setjmp.h>
# include <fcntl.h>
# include <unistd.h>
# include <string.h>
# include <begemot.h>

# include "cdefs.h"
# include "tty.h"

RCSID("$Id: ttysup.c 447 2000-11-14 16:09:21Z hbb $")

# define CHECK_PARENT	10	/* seconds to next check if parent is alive */

# define IAC	0377		/* telnet IAC character */


int	rstate = 0;	/* \r seen 			*/
int	iacstate = 0;	/* telnet IAC seen 		*/
u_char	buf[2048];	/* i/o buffer 			*/
int	got;		/* number of chars		*/
u_int	ticks;		/* parent check tick count	*/
pid_t	ppid;		/* parent pid			*/

/*
 * is parent still alive?
 */
static void
onalrm(int s UNUSED)
{
	if(kill(ppid, 0))
		Exit(0);
	ticks++;
}

/*
 * some signal occured - exit
 */
void
onsig(int s UNUSED)
{
	Exit(0);
}

/*
 * Inform parent that IO is possible, only on systems with
 * broken asynchronous socket IO.
 */
static void
signal_parent(void)
{
# if !defined(HAVE_STREAMS) && !defined(HAVE_O_ASYNC)
	kill(ppid,SIGIO);
# endif
}

/*
 * initialize timer
 */
void
tty_init()
{
	struct itimerval	tim;
	struct sigaction 	sigact;
	sigset_t 		set;

	ppid = getppid();

	sigemptyset(&set);

	memset((caddr_t)&sigact, 0, sizeof(sigact));
	sigact.sa_handler = onalrm;
	sigact.sa_mask = set;
	sigact.sa_flags = SA_RESTART;
	if(sigaction(SIGALRM, &sigact, NULL)) {
		error("sigaction(SIGALRM): %s", strerror(errno));
		Exit(10);
	}

	memset((caddr_t)&tim, 0, sizeof(tim));
	tim.it_interval.tv_sec = CHECK_PARENT;
	tim.it_value.tv_sec = CHECK_PARENT;
	if(setitimer(ITIMER_REAL, &tim, NULL)) {
		error("sigaction(SIGALRM): %s", strerror(errno));
		Exit(11);
	}

	signal(SIGINT, SIG_IGN);
	signal(SIGTERM, onsig);
	signal(SIGHUP, onsig);
}


/*
 * fd 0 is socket to parent
 * If fd is < 0, then no input is expected from there.
 */
void
tty_loop(int fd)
{
	fd_set	iset, irset;

	rstate = iacstate = 0;

	FD_ZERO(&iset);
	FD_SET(0, &iset);
	if(fd > 0)
		FD_SET(fd, &iset);
# if 0
	write( fd, "-----\r\n", 7);
# endif

	while(irset = iset, (got = select(FD_SETSIZE, &irset, 0, 0, 0)) >= 0 || errno == EINTR) {
		if(got < 0) {
			if(errno == EINTR)
				process_end();
			continue;
		}

		if(fd > 0 && FD_ISSET(fd, &irset)) {
			if((got = read(fd, buf, sizeof(buf)/2)) <= 0)
				break;		/* connection closed */
			process_input();
			(void)write(0, buf, got);
			signal_parent();
		}

		if(FD_ISSET(0, &irset)) {
			if((got = read(0, buf, sizeof(buf)/2)) <= 0)
				break;		/* parent died :-( */
			process_output();
			write(fd < 0 ? -fd : fd, buf, got);
		}
		process_end();
	}
# if 0
	write( fd, "\r=====\r\n", 8);
# endif
}


/*
 * strip kl-generated parity on output, remove \377
 */
void
makecs7()
{
	u_char	*wp = buf;
	u_char	*rp = buf;

	while(rp < &buf[got])
		if((*wp++ = (*rp++ & 0177)) == '\177')
			wp--;
	got = wp - buf;
}


/*
 * remove NVT cr/lf mapping
 * \r -> \r\0
 * \n -> \r\n
 */
void
bin2nvt()
{
	u_char	*wp;
	u_char	*bp;
	int	extra;

	for(wp = buf, extra = 0; wp < &buf[got]; wp++)
		if(*wp == '\r' || *wp == '\n')
			extra++;
	bp = &buf[got];
	got += extra;
	wp = &buf[got];
	while(bp > buf) {
		if(*--bp == '\r') {
			*--wp = '\0';
			*--wp = '\r';
		} else if(*bp == '\n') {
			*--wp = '\n';
			*--wp = '\r';
		} else
			*--wp = *bp;
	}
}

/*
 * do NVT mapping
 * \r\0 -> \r
 * \r\n -> \n
 * \r*  -> *
 */
void
nvt2bin()
{
	u_char	*bp = buf;
	u_char	*wp = buf;

	while(bp < &buf[got]) {
		if(rstate) {
			if(*bp == '\0')
				*wp++ = '\r';
			else if(*bp == '\n')
				*wp++ = '\n';
			else
				*wp++ = *bp;
			rstate = 0;
		} else if(*bp == '\r')
			rstate = 1;
		else
			*wp++ = *bp;
		bp++;
	}
	got = wp - buf;
}

/*
 * insert IACs after IACs
 * \377 -> \377\377
 */
void
insiacs()
{
	u_char	*wp;
	u_char	*bp;
	int	extra;

	for(wp = buf, extra = 0; wp < &buf[got]; wp++)
		if(*wp == IAC)
			extra++;
	bp = &buf[got];
	got += extra;
	wp = &buf[got];
	while(bp > buf) {
		if(*--bp == IAC)
			*--wp = IAC;
		*--wp = *bp;
	}
}

/*
 * \377\377 -> \377
 */
void
deliacs()
{
	u_char	*bp = buf;
	u_char	*wp = buf;

	while(bp < &buf[got]) {
		if(iacstate) {
			if(*bp != IAC)
				*wp++ = *bp;
			iacstate = 0;
		} else if(*bp == IAC) {
			*wp++ = *bp;
			iacstate = 1;
		} else
			*wp++ = *bp;
		bp++;
	}
	got = wp - buf;
}
