/**************************************************************
 *
 *	CRISP - Custom Reduced Instruction Set Programmers Editor
 *
 *	(C) Paul Fox, 1989
 *
 *    Please See COPYLEFT notice.
 *
 **************************************************************/

# include       "list.h"
# include	"tty.h"
# if	defined(HAVE_TERMIO)
	static struct termio    ot;
	static struct termio    nt;
# endif
# if	defined(HAVE_SGTTY)
	struct sgttyb		ot;
	struct sgttyb		nt;
# endif
# include	<sys/signal.h>
# include	"clk.h"
/* #ifdef LINUX
#include	<linux/time.h>
#endif  */


/**********************************************************************/
/*   Directory entries for CRISP under VMS.			      */
/**********************************************************************/
char	*termcap_dir = "/etc/termcap";
char	*bhelp = "ROOT/help";
char	*bpath = "ROOT/macros;.;ROOT/src";



# define	CENTISECONDS	5	/* x0.1 seconds */
void	turn_on_delay PROTO((int));
void	turn_off_delay PROTO((int));
void	ttflush();

extern int ega43_flag;
int	console_mode = -1;	/* Only used on EGA screen. */

int kbdflgs = 0;                    	/* saved keyboard fd flags      */
int	current_tty_flags;		/* Used to optimise calls to fcntl() */
static int	ttyactivep = FALSE;

/**********************************************************************/
/*   This  function  is  used  to set up the terminal modes, i.e. no  */
/*   echo,  single  character  input  mode.  Unfortunately  with the  */
/*   advent  of  POSIX  we have about 4 or 5 different ways we COULD  */
/*   do  things.  Best  thing  is  to  move  to  the POSIX style and  */
/*   emulate  on  the  other systems, but the Sun is a disaster area  */
/*   becuase  it  supports  ALL  types. This makes it very difficult  */
/*   to verify things properly.					      */
/**********************************************************************/
void
sys_open()
{
	/***********************************************/
	/*   If  terminal  already open then don't do  */
	/*   anything.				       */
	/***********************************************/
	if (ttyactivep)
		return;
# if	defined(CONS_GET) && !defined(NOFUNCTIONS)

	/***********************************************/
	/*   We  want  to handle the peculiarities of  */
	/*   EGA  43  line mode on those systems that  */
	/*   support  it.  We  need  to handle things  */
	/*   like  ALT-Z  being used to change screen  */
	/*   modes.   Setting   old_ega_flag   to  -1  */
	/*   ensures  that  when  the sub-shells exit  */
	/*   we  make  sure  the  screen  is  in  the  */
	/*   right mode.			       */
	/***********************************************/
	{
	int	val;
	extern int old_ega_flag;
	static int old_mode = -1;
	if (old_mode == -1)
		old_mode = console_mode = ioctl(0, CONS_GET, &val);
	else
		old_ega_flag = -1;
	}
# endif

	/***********************************************/
	/*   Get current settings and save them.       */
	/***********************************************/
	ioctl(0, TCGETA, &ot);
	nt = ot;

	/***********************************************/
	/*   Set the modes we're interested in.	       */
	/***********************************************/
# if	defined(HAVE_TERMIO)
		/***********************************************/
		/*   Note  that  ^S/^Q and ^Z will be handled  */
		/*   by  system  and  not  seen  by CRISP. If  */
		/*   you   want   to   see  these  characters  */
		/*   you'll  have  to  do  input_mode()  flow  */
		/*   control.				       */
		/***********************************************/
		nt.c_cc[VINTR] = 'Y' & 0x1f; /* Default interrupt key. */
		nt.c_cc[VQUIT] = 0xff;
		nt.c_cc[VMIN] = 1;      /* one character read is OK     */
		nt.c_cc[VTIME] = 0;     /* Never time out.              */
		nt.c_iflag |= IGNBRK;
		nt.c_iflag &= ~(ICRNL | INLCR | ISTRIP | IXOFF );
		nt.c_oflag &= ~OPOST;
		nt.c_cflag |= CS8;      /* allow 8th bit on input       */
		nt.c_cflag &= ~PARENB;  /* Don't check parity           */
		nt.c_lflag &= ~(ECHO | ICANON);
		
# 	if defined(IEXTEN)
		nt.c_lflag &= ~IEXTEN;
# 	endif

# else
		nt.sg_flags |= CBREAK | PASS8;
		nt.sg_flags &= ~(ECHO|CRMOD);
# endif
	
	kbdflgs = fcntl(0, F_GETFL, 0);
	current_tty_flags = kbdflgs;
	
	ioctl(0, TCSETA, &nt);

# if defined(TIOCLGET) && defined(LPASS8)
	{int flags;
	ioctl(0, TIOCLGET, &flags);
	flags |= LPASS8;
	ioctl(0, TIOCLSET, &flags);
	}
# endif

	/***********************************************/
	/*   Set  flag  so  we  can't  corrupt  saved  */
	/*   settings.				       */
	/***********************************************/
	ttyactivep = TRUE;
}
/**********************************************************************/
/*   Routine  called  by  input_mode()  primitive  to enable certain  */
/*   keys to be seen by CRISP, e.g. XON/XOFF, ^Z.		      */
/**********************************************************************/
int
sys_enable_char(ch, enable)
int	ch;
int	enable;
{	int	ret = 0;

	if (!ttyactivep)
		return 0;
# if	defined(HAVE_TERMIO)
	switch (ch) {
	  case 's' & 0x1f:
	  case 'q' & 0x1f:
	  	ret = (nt.c_iflag & IXON) == 0;
	  	if (enable)
			nt.c_iflag &= ~IXON;
		else
			nt.c_iflag |= IXON;
		ioctl(0, TCSETA, &nt);
		break;
	  case 'z' & 0x1f:
	  	ret = (nt.c_lflag & ISIG) == 0;
	  	if (enable)
			nt.c_lflag &= ~ISIG;
		else
			nt.c_lflag |= ISIG;
		ioctl(0, TCSETA, &nt);
		break;
		}
# endif
	return ret;
}
void
sys_timeout(yes)
int	yes;
{
	if (!ttyactivep)
		return;
# if	defined(SYSV)
	if (yes) {
		nt.c_cc[VTIME] = CENTISECONDS;
		nt.c_cc[VMIN] = 0;
		}
	else {
		nt.c_cc[VTIME] = 0;
		nt.c_cc[VMIN] = 1;
		}
	ioctl(0, TCSETA, &nt);
# endif
}
/*
 * This function gets called just
 * before we go back home to the shell. Put all of
 * the terminal parameters back.
 */
void
sys_close()
{
	if (!ttyactivep)
		return;
	ioctl(0, TCSETA, &ot);
	fcntl(0, F_SETFL, kbdflgs);
	current_tty_flags = kbdflgs;
	ttyactivep = FALSE;
# if	defined(SW_ENHC80x25)
	switch (console_mode) {
	  case M_ENH_C80x43:
		ioctl(1, SW_ENHC80x43, 0);
		break;
	  case M_ENH_C80x25:
		ioctl(1, SW_ENHC80x25, 0);
		break;
		}
# endif
}
int
sys_checkc()
{	unsigned char	kbdq1 = 0;
	
	if (!ttyactivep) {
		return 0;
		}
	if ((current_tty_flags & O_NDELAY) == 0)
		turn_off_delay(TTY_FD);
	read(TTY_FD, (char *) &kbdq1, 1);
	return kbdq1;
}

/*********************************************************/
/*   On  System  V using the VMIN/VTIME facility, there  */
/*   is  a  bug  which  causes the internal timeout not  */
/*   to   be   cleared   when   a   key   is   pressed.  */
/*   Subsequently  on  the next timeout, we may delay a  */
/*   lot longer than we would really like.		 */
/*********************************************************/
# define VMIN_BUG
# if defined(VMIN_BUG)
void
getkey_alarm_handler()
{
}
# endif
# if defined(SYSV)
int
sys_getchar(fd, ibuf, tmo)
int	fd;
int 	*ibuf;
long	tmo;
{	int n;
	long	delta = 0;
	extern int use_vmin;
	unsigned char buf[1];

# define	MAX_SYSV_TIMEOUT	(127 * 100)
#ifdef LINUX
	long chars_waiting;
	int flag16;
	
	delta = 100;
    	if (tmo == 0)
		tmo = 60000; /*  1 minute  */
  	while (1)
		{	  
		ioctl(TTY_FD,FIONREAD,&chars_waiting);
		if (chars_waiting)
			{
			n = read(TTY_FD, (char *) buf, 1);
			*ibuf = buf[0];
			return 1;
			}
		if (typeahead())
			{
			*ibuf = get_typeahead(&flag16);
			if (flag16)
				return 1;
			}
		usleep(delta*1000);
		tmo -= delta;
		if (tmo <= 0)
			return -1;
	 	}		
#else
	turn_on_delay(TTY_FD);
	while (1) {
		if (!use_vmin) {
			if (tmo) {
				void getkey_alarm_handler();
				signal(SIGALRM, getkey_alarm_handler);
				n = tmo < (1 SECOND) ? 1 : tmo / (1 SECOND);
				alarm(n);
				}
			n = read(TTY_FD, (char *) buf, 1);
			tmo = 0;
			alarm(0);
			}
		else {
			if (tmo == 0) {
				nt.c_cc[VMIN] = 1;
				nt.c_cc[VTIME] = 0;
				}
			else if (tmo < MAX_SYSV_TIMEOUT) {
				delta = tmo;
				nt.c_cc[VTIME] = tmo / 100;
				nt.c_cc[VMIN] = 0;
				}
			else {
				delta = MAX_SYSV_TIMEOUT;
				nt.c_cc[VTIME] = 127;
				nt.c_cc[VMIN] = 0;
				}
			
			ioctl(TTY_FD, TCSETA, &nt);
			n = read(TTY_FD, (char *) buf, 1);
			}
		*ibuf = buf[0];
		if (n == 1)
			return 1;
		tmo -= delta;
		if (tmo <= 0)
			return -1;
		}
	return 0;
#endif
}
# endif
char *
sys_delim()
{
	return "/";
}

/**********************************************************************/
/*   Function  to  turn  on  delay  (clear  O_NDELAY)  only  if  not  */
/*   already set.						      */
/**********************************************************************/
void
turn_on_delay(fd)
int	fd;
{
	if (!ttyactivep)
		return;
	if (current_tty_flags & O_NDELAY) {
		current_tty_flags &= ~O_NDELAY;
		fcntl(fd, F_SETFL, current_tty_flags);
		}
}
/**********************************************************************/
/*   Function  to  turn off delay (set O_NDELAY) only if not already  */
/*   set.							      */
/**********************************************************************/
void
turn_off_delay(fd)
int	fd;
{
	if (!ttyactivep)
		return;
	if ((current_tty_flags & O_NDELAY) == 0) {
		current_tty_flags |= O_NDELAY;
		fcntl(fd, F_SETFL, current_tty_flags);
		}
}

/**********************************************************************/
/*   Set  flag  to  indicate  file descriptor should be closed on an  */
/*   exec.							      */
/**********************************************************************/
void
noinherit(fd)
int	fd;
{
# if defined(FIOCLEX)
	fcntl(fd, FIOCLEX, 0);
# endif
}
/**********************************************************************/
/*   Function  called  from  PTY  code to set up terminal modes when  */
/*   we  create  a new shell buffer. We put it here so we can ensure  */
/*   that   when   compiling   with  GNU  C  that  we  can  turn  on  */
/*   -traditional for this file.				      */
/**********************************************************************/
void
setup_pty_mode(fd)
int	fd;
{
# if defined(ICANON)
	struct termio t;
	ioctl(fd, TCGETA, &t);
	t.c_lflag &= ~(ECHO);
	t.c_lflag |= ICANON;
	ioctl(fd, TCSETA, &t);
# endif
}

