/************************************************************************
 * This program is Copyright (C) 1986 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.						*
 ************************************************************************/

/* Contains some system dependent type things,
   e.g. putting terminal in CBREAK mode, etc.
   [TRH] this was originally in "jove.c"

   [May 89 TRH] facilitate port to non-unix systems,
	You have to supply the following routines:
	 - getchar() {or rawchar(time_out) if you still want to use fkeys.c}
	 - charp()	- input character available?
	 - do_sgtty()	- get current tty state
	 - ttyset(on)	- set / restore tty state
	 - ttsize()	- (re)size screen; should also set ILI to LI - 1
	 - ttinit()	- get additional tty state info (see do-sgtty)
	 - ttexit()	- last-minute tty cleanup actions
	 - DoSit(delay)	- wait for delay * 100 ms
			unix version now uses ANSI clock() if possible
*/

#define NO_PROCDECL	/* kludge for teensy pdp11 compiler */

#include "jove.h"

RCS("$Id: tty.c,v 14.32.0.12 1994/06/24 00:10:20 tom Exp tom $")

#include "io.h"
#ifdef TINY
#   undef putch	/* to un-hide the function */
#endif
#ifndef NO_PROCDECL
#   include "screen.h"
#endif
#include "termcap.h"
#define Extern	public	/* to force definition of variables */
#include "tty.h"

int	done_ttinit ZERO;

DEF_INT( "cps", CharsPerSec, V_BASE10|V_CONST ) = CSPEED;

DEF_STR( "ttyname", tt_name, 20, V_STRING|V_CONST ) _IF(def PRIVATE) = DevTty;

DEF_INT( "erase-char", EraseChar, V_CHAR|V_CONST ) _IF(def PRIVATE) =
	/* On UNIX, this is set to the actual erase character. */
#ifdef STDBINDINGS
	RUBOUT;
#else
	CTL('H');
#endif

#if unix || vms
DEF_INT( "allow-^S-and-^Q", OKXonXoff, V_BOOL|V_TTY_RESET ) _IF( unix||vms) ZERO;
	/* ^S and ^Q initially DON'T work */

DEF_INT( "interrupt-character",	IntChar, V_CHAR|V_TTY_RESET ) _IF( unix||vms) = CTL(']');
#endif

#if unix/*================= START OF UNIX-SPECIFIC STUFF =====================*/

#include "process.h"
#ifdef BSD4_2
#   include <sys/time.h>
#else
#   include <time.h>
#endif
#ifdef _POSIX_SOURCE
#   define O_NDELAY	O_NONBLOCK
#endif

#ifdef BSD4_2	/* on hybrid systems, make sure we use `select' with
		   timeout 0 instead of non-blocking I/O in charp(). */
#   if !aiws	/* for AIX this does not work on console. */
#	undef O_NDELAY
#   endif
#endif

#ifdef TERMIOS
#   define KEY_VMIN	2		/* max "sizeof smbuf" gets jerky */
#   define KEY_VTIME	1
#endif

#ifdef O_NDELAY
void
setblock(fd, on)	/* turn blocking on or off */
register int	fd;
int on;
{
	static int 	saveflags;
	static int 	beenhere = -1;
	register int	flags = saveflags;

	if (beenhere != fd) {
		beenhere = fd;
		if ((flags = fcntl(fd, F_GETFL, 0)) < 0)
			finish(-99);		/* shouldn't fail */
		saveflags = flags;
	}
	if (on)
		flags &= ~O_NDELAY;
	else
		flags |= O_NDELAY;
	if (flags != saveflags) {
		saveflags = flags;
		if (fcntl(fd, F_SETFL, flags) < 0)
			finish(-98);		/*  shouldn't fail */
	}
}
#endif /* O_NDELAY */

private char	smbuf[20],
		*bp = smbuf;
private int	nchars ZERO;

#ifdef IPROCS
#   ifndef PIPEPROCS
#	ifndef MSEC_TIMEOUT		/* allow definition from command line */
/*- #	    define MSEC_TIMEOUT	200 -*/ /* NO -- too short in networking env. */
#	    define MSEC_TIMEOUT	350
#	endif
#   endif
#endif
#ifndef MSEC_TIMEOUT
#   ifdef ITIMER_REAL
#	define MSEC_TIMEOUT	350
#   else
#	define MSEC_TIMEOUT	(ALARM_1_SEC * 1000)
#   endif
#endif
#define MIN_TIMEOUT	250

#ifndef TINY
DEF_INT( "keyseq-timeout-in-ms", msec_timeout, V_BASE10 ) _IF( unix)_IF(ndef TINY)
	= MSEC_TIMEOUT;
#else
#   define msec_timeout	MSEC_TIMEOUT
#endif

#ifdef FUNCKEYS
rawchar(time_out)
#else
getchar()
#   define time_out NO
#endif /* FUNCKEYS */
{
	register int	c, n;
	extern void	(*timeout_proc)__(( void ));

#define handle_timeout() {			\
		if (timeout_proc)		\
			(*timeout_proc)();	\
	}

#ifdef time_out
#   define timed_out	NO
#else
	/* handle "real" time-outs left over from last time,
	   but only if we had to wait long for the timeout to happen */
	static int	interrupted ZERO;

	if (interrupted) {
		interrupted = NO;
		if (msec_timeout >= 500) {
			handle_timeout();
		}
	}

#   undef handle_timeout /* ...and redefine this to handle time_out requests */
#   define handle_timeout() {			\
		if (timeout_proc)		\
			(*timeout_proc)();	\
		interrupted++;			\
		if (time_out)			\
			return -1;		\
	}
#endif
	if (nchars <= 0) {

#ifdef IPROCS
#   ifdef PIPEPROCS

retry:	    if (NumProcs != 0) {
		/*
		 * Get input from the Process pipe.  Output from the
		 * keyboard and portsrv processes is multiplexed there and
		 * presented to JOVE in data packets.  Its source is
		 * identified by the header.
		 *   When we detect that all sub-processes have died, we
		 * suspend the kbd process and revert to direct keyboard
		 * input.  However we cannot switch immediately since the
		 * kbd process may have "read ahead" and deposited the
		 * readahead characters in the pipe.  So after suspending
		 * the kbd process we keep draining the pipe until we see
		 * the acknowledgment packet sent by the kbd process.
		 */
		static int	draining ZERO;

#	define DRAINING	(-10000)

		if (NumProcs < 0) {
			if (!draining) {
				/* some sub-processes have been closed
				   since our last "emergency stop" */
				NumProcs = 0;
				goto retry;
			}
			if (NumProcs > draining) {
				/* some processes have been started
				   while we were still draining */
				NumProcs += draining;
				draining = OFF;
			}
		}

		if (time_out || timeout_proc)
			kbd_timeout();

		n = 0;
		do {
			struct header header;

			/* get header */
			if (proc_read(&header, sizeof header) != sizeof header) {
				proc_error("[iproc: bad header]");
			}
			if (header.pid != kbd_pid) {
				/* handle data from sub-process */
				read_proc(header.pid, header.nbytes);

				if (NumProcs <= 0) {
					/* all sub-processes have died; suspend
					   kbd process and setup for draining */
					kbd(OFF);
					NumProcs -= draining = DRAINING;
				}
				continue;
			}
			/* handle data from the kbd process */

			if ((n = header.nbytes) < 0) {

				if (n == -EINTR) { /* timeout */
					handle_timeout();
					continue; /* ignore spurious */
				}
				if (n == KBD_SUSPENDED) {
					NumProcs += draining;
					draining = OFF;
					goto retry;
				}
				s_mess("[kbd error (%s)]", n);
				proc_error((char *) 0);	/* shouldn't happen. */
			}

			if (proc_read(smbuf, n) != n) {
				proc_error("[kbd: bad data]");
			}
		} while (n <= 0);

	    } else /* (NumProcs == 0) */

#   else /* ! PIPEPROCS */

#	define BITS_INT	(sizeof(long) * 8)

	    register int tmp,
			nfds;
	    long	reads;
	    /*
	     * Get a character from the keyboard, first checking for
	     * any pty input from a process.  Deal with terminal input
	     * first, and then handle any pty input.
	     */
	    if (time_out || timeout_proc || NumProcs > 0) {
		n = 0;
		do {
			struct timeval tv;
			register struct timeval	*t = NULL;

			if (time_out) {
				if ((tmp = msec_timeout) < MIN_TIMEOUT)
					msec_timeout = tmp = MIN_TIMEOUT;
				t = &tv;
				t->tv_sec = tmp / 1000;
				t->tv_usec = (tmp % 1000) * 1000L;
			}
			if (timeout_proc) {
				t = &tv;
				t->tv_sec = 1;
				t->tv_usec = 0;
			}
			reads = global_fd;
			if ((nfds = select(BITS_INT, (int *)&reads,
					   (int *)0, (int *)0, t)) <= 0) {
				if (nfds == 0) {
					handle_timeout();
				}
				else if (errno != EINTR) {
					f_mess("[select(0x%X) error. (%s)]",
						global_fd, syserr());
					global_fd = Bit(0);
				}
				continue;
			}

			if (reads & Bit(0)) {		/* ie. JOVEs stdin */
				/* Keyboard input has the highest priority.
				   So bail out if keyboard input present,
				   even if process output is pending; it'll
				   be there next time around... */
				if ((n = read(0, smbuf, sizeof(smbuf))) > 0)
					break;
				if (--nfds == 0)
					continue;
			}

			for (tmp = 0; ++tmp < BITS_INT; ) {
				if (reads & (1L << tmp)) {
					read_proc(tmp);
					if (--nfds == 0)
						break;
				}
			}
		} while (n <= 0);

#	undef t
#	define DONT_TIME_OUT	/* to suppress generation of time-out
				   handling code further on. */

	    } else /* (!time_out && !timeout_proc && NumProcs <= 0) */

#   endif /* !PIPEPROCS */

#endif /* IPROCS */

	    {
#ifndef DONT_TIME_OUT
		/*
		 * If requested, setup to generate a timeout after a short
		 * time when no input is available.  This works fine on SYSV
		 * and BSD systems, and not so good on others (due to the
		 * lack of timer resolution).  {{The following code assumes
		 * that there is always a SIGALRM handler installed. For JOVE
		 * this is indeed the case.  (cf.  update)}}
		 */
#   ifdef ITIMER_REAL	/* use fancy high-precision alarm timer */
		static struct itimerval	timout = {{0}, {MSEC_TIMEOUT/1000,
							MSEC_TIMEOUT%1000*1000L}},
					one_second = {{0}, {1, 0}};
		struct itimerval	old_it;
		register struct itimerval *t = NULL;

		if (time_out)
			t = &timout;
		if (timeout_proc)
			t = &one_second;
		if (t)
			setitimer(ITIMER_REAL, t, &old_it);

#	define clean_timeout() {					\
		if (t && !interrupted && timerisset(&old_it.it_value))	\
			setitimer(ITIMER_REAL, &old_it,			\
				  (struct itimerval *)0);		\
	   }
#   else /* !ITIMER_REAL */	/* use alarm() to setup crude time-out */
		register unsigned old_alarm = 0;

		if (time_out || timeout_proc)
			old_alarm = alarm(MSEC_TIMEOUT/1000);

#	define clean_timeout() {					\
		if (old_alarm && !interrupted)				\
			alarm(old_alarm);				\
	    }
#   endif /* ITIMER_REAL */
#else
#   define clean_timeout()
#   undef handle_timeout
#   define handle_timeout()
#endif /* DONT_TIME_OUT */

#ifdef DEBUG
		c = NO;
#endif
		while ((n = read(0, smbuf, sizeof smbuf)) <= 0) {
			if (n == 0)
				continue;

			if (errno == EINTR) {
				handle_timeout();
				continue;
			}

			/* This shouldn't happen! */
#ifdef DEBUG
			f_mess("[tty read error. (%s) %s] ", syserr(),
			       c ? "I give up" : "I'll try to reopen");

			/* ...but try to recover if it does. */
			if (c == NO) {
				++c;
				if (close(0) != 0)
					printf("close failed! (%s)", syserr());
				else if ((n = open(DevTty, O_RDONLY)) != 0)
					if (n < 0)
						printf("open failed! (%s)", syserr());
					else
						printf("wrong handle (%d)", n);
				else
					continue;
			}
#endif /* DEBUG */
			finish(-errno);
		}
		clean_timeout();
	    }
		nchars = n;
		bp = smbuf;
		InputPending = n;
	}
	c = *bp++;
#ifndef FUNCKEYS
	if (c & 0200) {
		if (MetaKey) {
			*--bp = (c &= 0177);
			return MetaKey;
		} else {
			static	SendQuote ZERO;

			if (SendQuote ^= 1) {
				--bp;
				return QuoteChar; /* [TRH] my 8-bit kludge. */
			}
		}
	}
#endif /* FUNCKEYS */
	nchars--;
	return _UC_(c);

#undef clean_timeout
#undef handle_timeout
#undef time_out
#undef interrupted
}

/* Returns non-zero if a character waiting */

int
charp()
{
	if (inIOread)		/* force update of non-interactive processes */
		return 0;
	if (InJoverc || nchars > 0)
		return 1;
#ifdef BRLUNIX
	{
		SGTTY gttyBuf;

		gtty(0, (char *) &gttyBuf);
		return (gttyBuf.sg_xflags & INWAIT) != 0;
	}
#endif /* BRLUNIX */
#ifdef O_NDELAY
#   ifdef PIPEPROCS
	if (NumProcs != 0)	/* DON'T readahead if kbd process is active! */
 		return 0;
#   endif
	setblock(0, NO); 	/* turn blocking off */
	nchars = read(0, smbuf, sizeof smbuf);	/* Is anything there? */
	setblock(0, YES); 	/* turn blocking on */
	if (nchars > 0) {	/* something was there */
		bp = smbuf; 	/* make sure bp points to it */
		return 1;	/* just say we found something */
	}
	return 0;
#endif /* O_NDELAY */
#ifdef BSD4_2
	{
		static struct timeval	nulltime ZERO;
		int	readfds = 1;

		return select(1, &readfds, (int *)0, (int *)0, &nulltime) == 1;
	}
#endif
#ifdef FIONREAD
	{
		long c;

		return (ioctl(0, FIONREAD, (SGTTYB *) &c) == 0 && c > 0);
	}
#endif /* FIONREAD */
#ifdef TIOCNRD	/* (some versions of??) V7 */
	{
		int	c;

		return (ioctl(0, TIOCNRD, (SGTTYB *) &c) == 0 && c > 0);
	}
#endif /* TIOCNRD */
#ifdef c70
	return !empty(0);
#endif
	return 0;	/* if none of the above compiles... */
}

void
ttsize()
{
#ifdef TIOCGWINSZ
	struct winsize win;

	if (ioctl(0, TIOCGWINSZ, &win) == 0) {
		if (win.ws_col)
			CO = win.ws_col;
		if (win.ws_row)
			LI = win.ws_row;
	}
#   define ADJUST_SIZE
#endif /* TIOCGWINSZ */
#ifdef VARTERM
    {	extern short	newdim[2];	/* from term.c */
	register int	c, l;

	if (c = newdim[0]) {
		newdim[0] = 0;
		CO = c;
	}
	if (l = newdim[1]) {
		newdim[1] = 0;
		LI = l;
	}
    }
#   define ADJUST_SIZE
#endif /* VARTERM */
#ifdef ADJUST_SIZE
	if (LI < 2 + 1)		/* window (1 line + modeline) + minibuf */
		LI = 2 + 1;
	if (CO < 2)
		CO = 2;
	if (CO > MAXCOLS)
		CO = MAXCOLS;
#   undef ADJUST_SIZE
#endif
#ifdef TIOCGWINSZ
#   ifdef TIOCSWINSZ
	if (win.ws_col != CO || win.ws_row != LI) { /* let the kernel know. */
		win.ws_col = CO;
		win.ws_row = LI;
		ioctl(0, TIOCSWINSZ, &win);
	}
#   endif
#endif /* TIOCGWINSZ */
	ILI = LI - 1;
}

extern char	*ttyname __(( int _(fd) ));

#ifdef BIFF

DEF_INT( "disable-biff", BiffChk, V_BOOL|V_TTY_RESET ) _IF(def BIFF)_IF( unix)_IF(def PRIVATE) ZERO;
	/* whether or not to turn off biff while in JOVE */

private int	tt_mode ZERO;		/* mode flags of tty, for biff */

private void	biff_init __(( void ));
private void
biff_init()
{
	struct stat	tt_stat;

	tt_mode = 0;

	if (fstat(0, &tt_stat) == 0)
		tt_mode = tt_stat.st_mode;
}

private void	biff __(( int _(off) ));
private void
biff(off)
{
	/* Only change state if "disable-biff" is on, and we're using biff. */
	if (True(BiffChk)) {
		register int	mode = tt_mode;

		if (mode & S_IXUSR) {		/* using biff... */
			if (off)
				mode &= ~S_IXUSR;
#   ifndef BSD4_2
			chmod(tt_name, mode);
#   else
			fchmod(0, mode);
#   endif
		}
	}
}

#endif /* BIFF */

/* Determine tty speed in characters per second from `ospeed' */

#define CPS(baud)	(((baud) + 5) / 10)

#if (B9600 == 96 && B2400 == 24)	/* Minix (1.5) encoding (baud/100). */
#   define SPEED(ospeed)	((ospeed) ? CPS(ospeed * 100) : CSPEED)
#endif

#ifndef SPEED
private const int speeds[] = {
	CSPEED,
	CPS(50),
	CPS(75),
	CPS(110),
	CPS(134),
	CPS(150),
#if (B200)
	CPS(200),
#endif
	CPS(300),
	CPS(600),
#if (B900)
	CPS(900),
#endif
	CPS(1200),
	CPS(1800),
#if (B2000 || vms)
	CPS(2000),
#endif
	CPS(2400),
#if (B3600 || vms)
	CPS(3600),
#endif
	CPS(4800),
#if (B7200 || vms)
	CPS(7200),
#endif
	CPS(9600),
	CPS(19200),
	CPS(38400)
};
#   undef CPS

#   define SPEED(ospeed)	speeds[(ospeed) - (B50 - 1)]

#endif /* SPEED */

void
do_sgtty()
{
	register char	None = -1;
#ifdef TERMIOS
	CHECK( tcgetattr(0, &sg[OFF]), finish(-97) );
	sg[ON] = sg[OFF];

	/* determine VDISABLE character. */
#   ifdef _POSIX_VDISABLE
	None = _POSIX_VDISABLE;
	/* actually incorrect if _POSIX_VDISABLE == -1 (cannot be disabled) */
#   else
#	ifdef _PC_VDISABLE
	None = fpathconf(0, _PC_VDISABLE);
#	else
#	    ifdef VDISABLE
#		if (0 <= VDISABLE && VDISABLE < NCCS)
	None = sg[OFF].c_cc[VDISABLE];
#		endif
#	    endif
#	endif
#   endif

#   ifdef CURSOPT
#	ifdef OXTABS
	TABS = ((sg[OFF].c_oflag & OXTABS) != OXTABS);
#	endif
#   endif
	ospeed = cfgetospeed(&sg[OFF]);
	sg[ON].c_iflag &= ~(INLCR|ICRNL|IGNCR);
	if (MetaKey) {	/* [TRH] turn off input stripping (and parity check?) */
		sg[ON].c_iflag &= ~(ISTRIP);
		sg[ON].c_cflag = (sg[ON].c_cflag & ~(CSIZE|PARENB)) | CS8;
	}
	if (True(OKXonXoff)) {	/* [TRH] turn off output flow control */
		sg[ON].c_iflag &= ~(IXON);
#   ifdef VSTART
#	ifdef VSTOP
		sg[ON].c_cc[VSTART] = sg[ON].c_cc[VSTOP] = None;
#	endif
#   endif
	}
#   ifdef ASCEDIT
	/*
	 * AIX: disable enhanced edit since it loses NULs.
	 * Also disable break signaling since pty cannot distinguish between
	 * NUL and BREAK. This is useful only if you are running JOVE over
	 * a network connection or in a window, but does not seem to harm
	 * anything in other cases.
	 */
	sg[ON].c_iflag &= ~(ASCEDIT|IGNBRK|BRKINT);
#   endif
#   ifdef IEXTEN
	sg[ON].c_lflag &= ~(IEXTEN);
	/* Department of silly bugs: this is needed for SunOS 4.1.1b since
	   otherwise ^O (or more accurately c_cc[VDISCARD]) is gobbled up
	   even though canonical mode is turned off. */
#   endif
	sg[ON].c_lflag &= ~(ICANON|ECHO);
	sg[ON].c_oflag &= ~(OCRNL|ONLCR);
	sg[ON].c_cc[VMIN] = KEY_VMIN;
	sg[ON].c_cc[VTIME] = KEY_VTIME;
	sg[ON].c_cc[VINTR] = IntChar;
	/* on HP-UX (and maybe some other SYSV unices) the quit character
	   cannot be turned off; setting it to -1 will bind it to Meta-DEL,
	   which is not what we want.  So set it to IntChar (and make sure
	   SIGINT and SIGQUIT are treated alike in the interrupt handler
	   so we don't care which one is received.) */
	sg[ON].c_cc[VQUIT] = IntChar;
#   ifdef VSWTCH
	sg[ON].c_cc[VSWTCH] = None;
#   endif
#   ifdef VSUSP
	sg[ON].c_cc[VSUSP] = None;
#   endif
#   ifdef VDSUSP
	sg[ON].c_cc[VDSUSP] = None;
#   endif

	EraseChar = sg[OFF].c_cc[VERASE];

#else /* !TERMIOS */

#   ifdef TIOCGETA
	CHECK( ioctl(0, TIOCGETA, &sg[OFF]), finish(-97) );
#   else
	CHECK( gtty(0, &sg[OFF]), finish(-97) );
#   endif
	sg[ON] = sg[OFF];
#   ifdef CURSOPT
	TABS = !(sg[OFF].sg_flags & XTABS);
#   endif
	ospeed = sg[OFF].sg_ospeed;
	sg[ON].sg_flags &= ~(ECHO | CRMOD);
#   ifdef BRLUNIX
	sg[ON].sg_flags |= CBREAK;

	/* VT100 Kludge: leave STALL on for flow control if DC3DC1 (Yuck.) */
	sg[ON].sg_xflags &= ~((sg[ON].sg_xflags&DC3DC1 ? 0 : STALL) | PAGE);
#   endif /* BRLUNIX */

#   ifdef EUNICE
	sg[ON].sg_flags |= RAW;	/* Eunice needs RAW mode last I heard. */
#   else
#    ifdef PURDUE_EE
#     ifdef pdp11
	sg[ON].sg_flags |= RAW;
#     else
	sg[ON].sg_flags |= (MetaKey ? RAW : CBREAK);
#     endif
#    else
#     ifdef TIOCGETA			/* extended 7th Ed. sgtty */
	sg[ON].sg_length = 0;	/* so page mode is off even in CBREAK */
#     endif
#     ifdef TIOCLGET		/* local mode; the "new" BSD tty driver */
	CHECK( ioctl(0, TIOCLGET, (SGTTYB *) &lm[OFF]), finish(-96) );
	lm[ON] = lm[OFF];
#      if (BPC == 8)
	if ((sg[ON].sg_flags & ANYP) == ANYP)	/* if not inhibited by parity */
		lm[ON] |= LLITOUT;		/* allow 8-bit char output */
#      endif
#      ifdef LPASS8
	if (MetaKey)
		lm[ON] |= LPASS8;
#      endif
#      ifdef TANDEM
	if (True(OKXonXoff))
		sg[ON].sg_flags &= ~TANDEM;
#	ifdef LPASS8
	sg[ON].sg_flags |= CBREAK;
#	else
	sg[ON].sg_flags |= (MetaKey) ? RAW : CBREAK;
#	endif
#      else /* !TANDEM */
#	ifdef LPASS8
	sg[ON].sg_flags |= True(OKXonXoff) ? RAW : CBREAK;
#	else
	sg[ON].sg_flags |= (MetaKey && True(OKXonXoff)) ? RAW : CBREAK;
#	endif
#      endif
#     else /* !TIOCLGET */
	/* Assume we can't do Xon-Xoff flow control in raw mode */
	sg[ON].sg_flags |= (MetaKey && True(OKXonXoff)) ? RAW : CBREAK;
#     endif /* TIOCLGET */
#    endif /* PURDUE_EE */
#   endif /* EUNICE */
#   ifdef TIOCGETC
	/* Change interrupt and quit characters. */
	CHECK( ioctl(0, TIOCGETC, (SGTTYB *) &tc[OFF]), finish(-94) );
	tc[ON] = tc[OFF];
	tc[ON].t_intrc = IntChar;
	tc[ON].t_quitc = None;
	if (True(OKXonXoff)) {
		tc[ON].t_stopc = None;
		tc[ON].t_startc = None;
	}
#   endif /* TIOCGETC */

	EraseChar = sg[OFF].sg_erase;
#endif /* TERMIOS */

#ifdef TIOCGLTC	/* !! outside !TERMIOS to accommodate HP-UX braindamage. */
	CHECK( ioctl(0, TIOCGLTC, (SGTTYB *) &ls[OFF]), finish(-95) );
	ls[ON] = ls[OFF];
	ls[ON].t_suspc = None;
	ls[ON].t_dsuspc = None;
	ls[ON].t_flushc = None;
	ls[ON].t_lnextc = None;
#endif

	CharsPerSec = SPEED(ospeed);

	/* this is to disable padding when we're using Xon/Xoff flow control. */
	if (False(OKXonXoff))
		ospeed = 0;
}

void
ttinit()
{
#ifndef TINY
	tt_name = ttyname(0);
#endif
	do_sgtty();
	done_ttinit = YES;
}

/* If n is OFF reset to original modes */

void
ttyset(on)
register int	on;
{
	if (!done_ttinit)	/* Try to reset before we've set! */
		return;
#ifdef TERMIOS
	CHECK( tcsetattr(0, TCSADRAIN, &sg[on]), finish(-93) );
#else
#   ifdef TIOCSETA
	CHECK( ioctl(0, TIOCSETA, &sg[on]), finish(-93) );
#   else
#     ifdef TIOCSETN
	CHECK( ioctl(0, TIOCSETN, &sg[on]), finish(-93) );
#     else
	CHECK( stty(0, &sg[on]), finish(-93) );
#     endif
#   endif
#   ifdef TIOCSETC
	CHECK( ioctl(0, TIOCSETC, (SGTTYB *) &tc[on]), finish(-92) );
#   endif
#   ifdef TIOCLSET
	CHECK( ioctl(0, TIOCLSET, (SGTTYB *) &lm[on]), finish(-90) );
#   endif
#endif /* TERMIOS */
#ifdef TIOCSLTC	/* !! outside !TERMIOS to accommodate HP-UX braindamage. */
	CHECK( ioctl(0, TIOCSLTC, (SGTTYB *) &ls[on]), finish(-91) );
#endif
#ifdef BIFF
	biff(on);
#endif
}

/*
 * [TRH] handle signal entry/exit for non-interactive processes:
 *	old_state = ttsigset(OFF);
 *	... * fork/exec process *
 *	... * wait for completion *
 *	ttsigset(old_state);
 * {{to alleviate catch-22 on 7th Ed.:
 *   CBREAK strips parity bit (so MetaKey would be useless)
 *   and RAW disables Interrupts...
 *   so now at least we are able to interrupt sub-processes.
 * }}
 */
ttsigset(state)
int	state;
{
#ifndef TERMIOS
#   ifdef RAW	/* hope this is specific enough */
#	ifdef CBREAK
	register int	prev = sg[ON].sg_flags,
			new;

	if ((new = state) == OFF)
		new = (prev & ~RAW) | CBREAK;

	if (new != prev) {	/* something changes */
		sg[ON].sg_flags = new;
		ttyset(ON);
	}
	return prev;
#	endif /* CBREAK */
#   endif /* RAW */
#endif /* TERMIOS */
}

/*
 * Tries to pause for delay/10 seconds OR until a character is typed
 * at the keyboard.  This works well on BSD4_2 and on systems with a
 * clock() with sufficiently high resolution and not so well on the
 * rest.  Returns 1 if it returned because of keyboard input, or 0
 * otherwise.
 * This does the actual pause, without redisplay.
 */
DoSit(delay)
{
#ifdef BSD4_2
	struct timeval	timer;
	int	readfds = 1;

	timer.tv_sec = (delay / 10);
	timer.tv_usec = (delay % 10) * 100 * 1000L;

	return select(1, &readfds, (int *)0, (int *)0, &timer);
#else
	register int	input_pending = NO;

#   if (CLK_TCK - 0 >= 10) && 0 /* busy-waiting is A Bad Thing(tm) */
	register clock_t	goal = clock() + delay * (CLK_TCK / 10);

	while (clock() < goal) {
		if (InputPending = charp()) {
			input_pending++;
			break;
		}
	}
#   else
	while (--delay >= 0) {
		register int	i = CharsPerSec / 10;
		do
			putch(PC);
		while (--i > 0);
		flusho();
		if (InputPending = charp()) {
			input_pending++;
			break;
		}
	}
#   endif /* CLK_TCK */
	return input_pending;
#endif /* BSD4_2 */
}

#endif	/*================== END OF UNIX-SPECIFIC STUFF ======================*/

void
ResetTerm()
{
	putp(TI);
	putp(VS);
	if (MetaKey)
		putp(MM);
#ifdef FUNCKEYS
	if (True(UseKeyPad))
#endif
		putp(KS);
#ifdef ID_CHAR
	putp(EI);	/* just to be sure */
#endif
#ifdef COLOR
	set_color(DefColor);
#endif
#ifdef MOUSE
	mouseinit();
#endif
#ifdef BIFF
	biff_init();
#endif
#ifdef MAIL
	chkmail(YES);	/* force it to check to we can be accurate */
#endif
	ttinit();		/* this is so if you change baudrate or stuff
				   like that, JOVE will notice. */
	ttyset(ON);
}

void
UnsetTerm(mesg)
const char	*mesg;
{
	putp(KE);
	putp(VE);
	putp(TE);
	putp(MO);
	ttyset(OFF);
#ifdef ID_CHAR
	INSmode(0);
#endif
#ifdef COLOR
	if (COorig)
		putp(COorig);
	else
		set_color(ExitColor);
#endif
#ifdef MOUSE
	mousefinish();
#endif
	curstoLL();
	putp(CE);
	if (mesg)
		printf("%s\n", mesg);
	flusho();
}

void
tty_reset()
{
	if (!done_ttinit)
		return;

	ttyset(OFF);	/* go back to original modes */
	ttinit();
	ttyset(ON);

	/* so these are updated when variables are changed */
	putp((const char *)(MetaKey ? MM : MO));
#ifdef FUNCKEYS
	putp((const char *)(True(UseKeyPad) ? KS : KE));
#endif
	/* {the casts are necessary for Borland C--BOOH!!!} */
}

SitFor(delay)
{
	if (charp() || Inputp)
		return YES;
	/* gross that I had to snarf this from getch() */
	if (mesgbuf[0] && !UpdMesg && !Asking && !errormsg)
		message(NullStr);
#ifdef AUTOSCROLL
	curwind->w_flags |= W_NOAUTOSCROLL;
#endif
	redisplay();
#ifdef AUTOSCROLL
	curwind->w_flags |= W_NOAUTOSCROLL;
#endif
	return DoSit(delay);
}

#ifdef LOAD_AV
# if unix
/* [TRH] get_la() now returns (int) load-average times 100 */
/* {{This needs revision (maybe we should start to use GNU getloadavg)?}} */
int
get_la()
{
#  ifdef BSD4_2
#    ifdef PURDUE_EE

	extern double	load_av();

	return (int) (load_av(0) + .5);

#    else /* PURDUE_EE */

#	include <nlist.h>

#     ifndef KERNEL_IMAGE		/* so we can define it elsewhere. */
#	if hpux
#		define KERNEL_IMAGE	"/hp-ux"
#	else
#	  if NeXT
#		define KERNEL_IMAGE	"/mach"
#		define NLIST_UNION
#	  else
#		define KERNEL_IMAGE	"/vmunix"
#	  endif
#	endif
#   endif

	/* {Note the double braces to accommodate systems (NeXT!) that declare
	    the `n_name' field as part of a union; other systems don't seem
	    to mind. (the pdp11 K&R compiler groks this construct -- and
	    how much dumber can you get :-)} Unfortunately it _doesn't_
	    work on systems that matter, like suns.  Sigh... */

	static struct	nlist nl[] = {
#   ifdef NLIST_UNION
		{{ "_avenrun" }},
		{{ "" }}
#   else
		{ "_avenrun" },
		{ "" }
#   endif
	};
#	define X_AVENRUN	0
	static int	kmem ZERO;
#   if NeXT
	long		avenrun[3];
#   else
	double		avenrun[3];
#   endif

	if (kmem == 0) {
		if ((kmem = open("/dev/kmem", O_RDONLY)) < 0) {
			message("Can't open kmem for load average.");
		} else {
			nlist(KERNEL_IMAGE, nl);
			if (nl[X_AVENRUN].n_value == 0) {
				message("Can't get address of load average.");
				close(kmem);
				kmem = -1;
			}
		}
	}
	if (kmem > 0) {
		lseek(kmem, (long) nl[X_AVENRUN].n_value, SEEK_SET);
		read(kmem, (void_*) avenrun, sizeof(avenrun));
#	if NeXT
		/* NeXT stores load averages, in milli-units, as longs. */
		return (int) ((avenrun[0] + 5) / 10);
#	else
		return (int) (avenrun[0] * 100.0 + .5);
#	endif
	}

#    endif /* PURDUE_EE */
#  else /* !BSD4_2 */

	/* [TRH] I don't know what system is meant by this (BSD 2.9 perhaps?) */

	short	avg[3];

	if (gldav(avg) >= 0)
		return /* (avg[0] * 100 + 128) / 256 */
			  (avg[0] * 25 + 32) / 64;

#  endif /* BSD4_2 */

	/* if none of the above compiles, or fails */

	return 4 * 100;		/* So shell commands will say "Chugging" */
}
# endif /* unix */
#endif /* LOAD_AV */

/*======================================================================
 * $Log: tty.c,v $
 * Revision 14.32.0.12  1994/06/24  00:10:20  tom
 * (rawchar): return immediately if keyboard input read, even if process
 *  output is pending.
 *
 * Revision 14.32.0.10  1994/04/08  19:21:38  tom
 * ("keyseq-timeout-in-ms"): new variable, replaces fixed MSEC_TIMEOUT;
 * (rawchar): use it, fix bug in select() timeout handling.
 *
 * Revision 14.32.0.9  1994/01/31  17:56:16  tom
 * reorganize TERMIOS/ltchars handling to accommodate HP-UX braindamage.
 *
 * Revision 14.32.0.5  1993/08/29  23:36:41  tom
 * (DoSit): disable busy-waiting; comment fixes.
 *
 * Revision 14.32  1993/07/05  17:49:40  tom
 * (ttsize): check LI, CO against minimum values.
 *
 * Revision 14.31.0.1  1993/03/22  10:34:52  tom
 * (some?) SysV systems do not define VSTART and VSTOP: made conditional.
 *
 * Revision 14.31  1993/02/18  02:27:11  tom
 * fix BIFF code to take effect immediately if user changes "disable-biff";
 * remove (void) casts; lotsa random optimizations.
 *
 * Revision 14.30  1993/01/26  18:43:14  tom
 * cleanup whitespace.
 *
 * Revision 14.29  1992/12/30  10:19:47  tom
 * workaround bug in SunOS 4.1.1; fix NLIST portability.
 *
 * Revision 14.28  1992/10/24  01:24:23  tom
 * convert to "port{ansi,defs}.h" conventions; make VARTERM notify changes to
 * tty driver.
 * convert to "port{ansi,defs}.h" conventions.
 *
 * Revision 14.27  1992/09/22  00:15:28  tom
 * replace CTL('Q') with `QuoteChar'; cleanup empty `#' directives.
 *
 * Revision 14.26  1992/08/27  00:06:54  tom
 * add "erase-char" support; use Posix' VDISABLE to disable special characters
 * (when available); PRIVATE-ized some Variable defs; add RCS directives.
 *
 */
