/* unixio.c - Unix terminal and signal handling
   $Id: unixio.c,v 0.2 1997/03/28 03:17:33 tjchol01 Exp $
   Authors: Andrew Trevorrow, Ian Dall, Geoffrey Tobin, Tomasz J. Cholewo
 */

#include "dvgt.h"
#include "unixio.h"
#include "screenio.h"		/* for MesgString, MesgLine */

/* Set DV_FD (ioctl file descriptor) to 0 or 1 ? */
#define DV_FD  STDIN_FILENO

sig_flags_t sig_flags;
cmode_flags_t cmode_flags;

/* Alex Dickinson
   Procedures for setting and resetting UNIX tty characteristics.
   Interesting functions are:
   save_init_tty;
   restore_init_tty;
   save_temp_tty;
   restore_temp_tty;
   echoon;
   echooff;
   singlecharon;
   singlecharoff;
   buffercount;
   suspend;
   A side effect of calling save_temp_tty is to set up signal handling
   to reset the terminal characteristics appropriately for the various
   interrupt signals.
 */

/*****************************************************************
*    terminal handling
*/

#if defined(HAVE_TERMIO_H)
#include <fcntl.h>
#include <termio.h>
#define TERMIO
#ifdef NCC
#define MAXCC NCC
#else
#define MAXCC 256
#endif
#define DV_GTTY TCGETA
#define DV_STTY TCSETAW
typedef struct termio dv_tty;
unsigned char saved_cc[NCC];
#else
#include <sgtty.h>
#define DV_GTTY TIOCGETP
#define DV_STTY TIOCSETN
typedef struct sgttyb dv_tty;
#endif /* TERMIOS || TERMIO */

#include <signal.h>
#include <errno.h>

static dv_tty init_tty_state;	/* store initial terminal characteristics */
static dv_tty temp_tty_state;	/* current terminal characteristics */

/* locally defined and used functions and data */

static void setsignals ();
static void reportio PARAMS ((int iostat));
static int iostatus;		/* return status of ioctl system function */

static void 
reportio (int iostat)
{
  if (iostat < 0)
    {				/* an ioctl error */
      char *str = "";
      String cstr;
      switch (errno)
	{
	  /* generic errors */
#ifdef EBADF
	case EBADF:
	  str = "Not a valid, open, file descriptor!";
	  break;
#endif
#ifdef EINTR
	case EINTR:
	  str = "Signal caught.";
	  break;
#endif
#ifdef ENOTTY
	case ENOTTY:
	  str = "No device driver, or doesn't accept control functions!";
	  break;
#endif
	  /* specialised errors */
#ifdef EFAULT
	case EFAULT:
	  str = "Illegal data transfer address!";
	  break;
#endif
#ifdef EINVAL
	case EINVAL:
	  str = "Driver ignorant of this request or arg!";
	  break;
#endif
#ifdef EIO
	case EIO:
	  str = "Physical I/O error!";
	  break;
#endif
#ifdef ENOLINK
	case ENOLINK:
	  str = "Remote machine link no longer active!";
	  break;
#endif
#ifdef ENXIO
	case ENXIO:
	  str = "Subdevice cannot perform requested service!";
	  break;
#endif
	  /* gt - should be ample space in cstr. */
	default:
	  sprintf (cstr, "Unknown error %d !", errno);
	  str = cstr;
	  break;
	}
      MesgString ("ioctl: ");
      MesgString (str);
      MesgLine ();
    }
}				/* reportio */

void 
save_init_tty ()
{
  /* Save the original tty characteristics and set up the signalling. */
  save_temp_tty ();
  init_tty_state = temp_tty_state;
}				/* save_init_tty */

void 
restore_init_tty ()
{
  /* Restore the original tty characteristics. */
  temp_tty_state = init_tty_state;
  restore_temp_tty ();
}				/* restore_init_tty */

void 
save_temp_tty ()
{
  /* Save the current tty characteristics and set up the signalling. */
  iostatus = ioctl (DV_FD, DV_GTTY, &temp_tty_state);
  reportio (iostatus);
  setsignals ();
}				/* save_temp_tty */

void 
restore_temp_tty ()
{
  /* Restore the recent tty characteristics. */
  iostatus = ioctl (DV_FD, DV_STTY, &temp_tty_state);
  reportio (iostatus);
}				/* restore_temp_tty */

void 
singlecharon ()
{
  /* Set driver to read characters as they are typed without waiting for a
     terminator. Echo remains unchanged. */
  dv_tty s;

  if (cmode_flags.cbreak != 1)
    {
      reportio (ioctl (DV_FD, DV_GTTY, &s));
#ifndef TERMIO
      s.sg_flags |= CBREAK;
#else /* TERMIO */
      saved_cc[VEOL] = s.c_cc[VEOL];
      saved_cc[VEOF] = s.c_cc[VEOF];
      s.c_lflag &= ~ICANON;	/* Disable erase/kill processing */
      s.c_cc[VMIN] = 1;		/* Input should wait for at least 1 char */
      s.c_cc[VTIME] = 0;	/* no matter how long that takes.  */
#endif /* TERMIO */
      reportio (ioctl (DV_FD, DV_STTY, &s));
#ifdef FLUSH_STDIN
      fflush (stdin);
#endif /* FLUSH_STDIN */

      cmode_flags.cbreak = 1;
    }
}				/* singlecharon */

void 
singlecharoff ()
{
  /* Turn off single character read mode. */
  dv_tty s;

  if (cmode_flags.cbreak != 0)
    {
      iostatus = ioctl (DV_FD, DV_GTTY, &s);
      reportio (iostatus);
#ifndef TERMIO
      s.sg_flags &= ~CBREAK;
#else /* TERMIO */
      s.c_lflag |= ICANON;	/* Enable erase/kill processing */
      s.c_cc[VEOL] = saved_cc[VEOL];
      s.c_cc[VEOF] = saved_cc[VEOF];
#endif /* TERMIO */
      iostatus = ioctl (DV_FD, DV_STTY, &s);
      reportio (iostatus);

      cmode_flags.cbreak = 0;
    }
}				/* singlecharoff */

void 
echoon ()
{
  /* Turn character echoing on. */
  dv_tty s;

  reportio (ioctl (DV_FD, DV_GTTY, &s));
#ifndef TERMIO
  s.sg_flags |= ECHO;
#else /* TERMIO */
  s.c_lflag |= ECHO;
#endif /* TERMIO */
  reportio (ioctl (DV_FD, DV_STTY, &s));
  cmode_flags.echo = 1;
}				/* echoon */

void 
echooff ()
{
  /* Turn character echoing off. */
  dv_tty s;

  iostatus = ioctl (DV_FD, DV_GTTY, &s);
  reportio (iostatus);
#ifndef TERMIO
  s.sg_flags &= ~ECHO;
#else /* TERMIO */
  s.c_lflag &= ~ECHO;
#endif /* TERMIO */
  iostatus = ioctl (DV_FD, DV_STTY, &s);
  reportio (iostatus);
  cmode_flags.echo = 0;
}				/* echooff */

void 
rawouton ()
{
  dv_tty s;

  iostatus = ioctl (DV_FD, DV_GTTY, &s);
  reportio (iostatus);
#ifdef TERMIO
  s.c_oflag &= ~OPOST;		/* Disables tab interpretation etc. */
#endif /* TERMIO */
  iostatus = ioctl (DV_FD, DV_STTY, &s);
  reportio (iostatus);

  cmode_flags.raw = 1;
}				/* rawouton */

void 
rawoutoff ()
{
  dv_tty s;

  iostatus = ioctl (DV_FD, DV_GTTY, &s);
  reportio (iostatus);
#ifdef TERMIO			/* TERMIO */
  s.c_oflag |= OPOST;
#endif /* TERMIO */
  iostatus = ioctl (DV_FD, DV_STTY, &s);
  reportio (iostatus);

  cmode_flags.raw = 0;
}				/* rawoutoff */

int 
buffercount ()
{
  /* Return true if there are any characters currently in the input buffer. */
#ifndef TERMIO
  long count;
  iostatus = ioctl (DV_FD, FIONREAD, &count);
  reportio (iostatus);
  return (count > 0);
#else /* TERMIO */
  int c, flags, ret;

  /* Save the current fcntl flags and make reads return without waiting */
  flags = fcntl (0, F_GETFL, 0);
  fcntl (0, F_SETFL, flags | O_NDELAY);		/* This might be SysV specific */
  if ((c = getchar ()) != EOF)
    {
      ungetc (c, stdin);
      ret = 1;
    }
  else
    {
      ret = 0;
    }
  fcntl (0, F_SETFL, flags);	/* This might be SysV specific */
  return ret;
#endif /* TERMIO */
}				/* buffercount */

RETSIGTYPE 
handleint (int sig)
{
  /* Catch signals from tty.
     If sig is an interrupt, set the intr flag,
     Otherwise it was a suspend, so set the tstop flag. */
  fflush (stdin);
  if (sig == SIGINT)
    {
      sig_flags.intr = 1;
    }
  else
    {
      sig_flags.tstop = 1;
    }
#ifdef TERMIO			/* TERMIO */
  setsignals ();		/* Because SysV forgets its handlers */
#endif /* TERMIO */
}				/* handleint */

static void 
setsignals ()
{
  /* Signal initialization. */
  signal (SIGINT, handleint);
#ifndef TERMIO
  signal (SIGTSTP, handleint);
#endif /* TERMIO */
}				/* setsignals */

void 
suspend ()
{
  /* Suspend the process */
#ifndef TERMIO
  signal (SIGTSTP, SIG_DFL);
  kill (0, SIGTSTP);
  /* resumed again, goody! */
  setsignals ();
#else
  /* don't know what to do */
#endif /* TERMIO */
}				/* suspend */

#undef IN_UNIXIO_C