/* Original Author:         Andrew Trevorrow
   Implementation: Modula-2 under VAX/UNIX 4.2 BSD
   Date Started:   June, 1986

   Description:
   Implements terminal i/o routines used by DVItoVDU.  Highly SYSDEP!

   This version converted to C and ported to BSD and System V UNIX by
   some chaps at Kernel Technology up to September 1989.  Contact
   mjh@uk.co.kernel (Mark J. Hewitt) with bug fixes etc.

   Involved were:	Mark J. Hewitt
   			Dave Dixon
			Marc Hadley
*/

/* SYSDEP: we need to write a block of characters (including NULLs) */

#include "def.h"
#include <math.h>
#include "screenio.h"
#include "dvitovdu.h"

static char *sccsid[] = "@(#)screenio.c	1.1";

/* SYSDEP: C routines written by Alex Dickinson */
/* WARNING: To handle ^Z suspension properly, screenio needs to use some of
   the vduinterface routines.  But vduinterface imports screenio, so there
   is a circular dependency.
   Everything should be ok as long as the vduinterface routines
   are only called in the reading routines.  DVItoVDU calls
   the first ReadString()only AFTER calling InitVDUInterface()
*/

extern void singlecharon(),
            singlecharoff(),
            echoon(),
            echooff(),
	    restoretty(),
	    savetty();

extern int getcharnowait();

void Abort ();

#define MAXBUFSIZE 1024		/* size of output buffer */
#define NULLCHAR '\0'		/* SYSDEP: used to terminate a string */
#define EOL '\012'		/* SYSDEP: char read upon hitting RETURN */
#define LF '\012'
#define CR '\015'
#define CTRLC '\0'		/* SYSDEP: interrupt, see unixio */
#define CTRLZ '\01'		/* SYSDEP: suspend, see unixio */

 /* SYSDEP: unixio puts CTRLC and CTRLZ into input buffer (along with CR) upon
    getting a ^C or ^Z interrupt.  (It can't put '\3' and '\32' into buffer
    because tty will detect another interrupt and we'll loop forever!) */

char  buffer[MAXBUFSIZE];	/* output buffer */
int   buflen;			/* number of characters in output buffer */

/******************************************************************************/

void Read (ch)
char *ch;
{
  int   dummy;

/* SYSDEP: Readc assumes singlecharon has been called */

  if ((dummy = getchar ()) == EOF)
  {
#ifdef DEBUG
      (void) printf("Readc failed in Read()\n");
#endif /* DEBUG */
    Abort ();
  }
  else
  {
    *ch = (char) dummy;
  /* check for CTRLC or CTRLZ */
    if (*ch == CTRLC)
    {
    /* interrupt */
      (void) getchar ();	/* remove terminator */
      *ch = EOL;		/* return to Command: level */
    }
    else
      if (*ch == CTRLZ)
      {
      /* suspend */
	(void) getchar ();	/* remove terminator */
	StartText ();
	ClearScreen ();
	WriteLn ();
	ResetVDU ();
	RestoreTerminal ();
	suspend ();
	savetty ();
	singlecharon ();
	echooff ();
	InitVDUInterface ();
	StartText ();
	ClearScreen ();
	*ch = EOL;		/* return to Command: level */
      }
      else
      {
	dummy = (int) * ch;
	(void) putchar (dummy);	/* echo ch since echooff has been called */
      }
  }
}

/******************************************************************************/

void ReadString (s, maxlength)

unsigned int  maxlength;
char *s;
{

/* Read()a string of characters.
   The routine is terminated upon carriage return.
   DEL can be used to erase the last character entered.
   If s is not full then NULLCHAR will terminate the string.
*/

  unsigned int  i;

  singlecharoff ();		/* read string in cooked mode */
  echoon ();			/* echo characters */
  s[0] = NULLCHAR;		/* we seem to need this in case user just hits
				   return */
/*  (void) printf ("%%"); */
  if (fgets (s, (int)maxlength, stdin) == NULL)
  {
  /* SYSDEP: ^D will cause end of input file; no more reads are possible! */
    Abort ();
  }

  if(strlen(s) > 0)
      s[strlen (s) - 1] = '\0';	/* remove \n from string */

  i = 0;
  for (;;)
  {
    if ((i > maxlength - 1) || (s[i] == NULLCHAR))
      break;
    if (s[i] == CTRLC)
    {
    /* interrupt */
      s[0] = NULLCHAR;		/* simply return an empty string */
      break;
    }
    else
      if (s[i] == CTRLZ)
      {
      /* suspend */
	StartText ();
	ClearScreen ();
	WriteLn ();
	ResetVDU ();
	RestoreTerminal ();
	suspend ();
	savetty ();
      /* singlecharon and echooff are called below */
	InitVDUInterface ();
	StartText ();
	ClearScreen ();
	s[0] = NULLCHAR;	/* return an empty string */
	break;
      }
    i++;
  }
  singlecharon ();		/* return to cbreak mode */
  echooff ();			/* and no echo */
}

/******************************************************************************/

short   BusyRead (ch)
char *ch;
{

/* Return TRUE if ch is waiting in input buffer, and read it with no echo.
   If nothing in input buffer then ch is undefined and we return FALSE.
*/
  int   dummy;

/* SYSDEP: getcharnowait assumes singlecharon and echooff have been called */
  if ((dummy = getcharnowait ()) == EOF)
  {
    return (FALSE);
  }
  else
  {
    *ch = (char) dummy;
    if (*ch == CTRLC)
    {
    /* interrupt */
      *ch = EOL;		/* main module will return to Command: level */
    }
    else
      if (*ch == CTRLZ)
      {
      /* suspend */
#ifndef USG
	StartText ();
	ClearScreen ();
	WriteLn ();
	ResetVDU ();
	RestoreTerminal ();
	suspend ();
	savetty ();
	singlecharon ();
	echooff ();
	InitVDUInterface ();
	StartText ();
	ClearScreen ();
#endif /* USG */
	*ch = EOL;		/* after suspend, return to Command: level */
      }
    return (TRUE);
  }
}

/******************************************************************************/

void Abort ()
{
/* HALT after restoring terminal to a nice clean state. */

  StartText ();
  ClearScreen ();
  WriteLn ();
  ResetVDU ();
  RestoreTerminal ();
  exitprog (1);
}

/******************************************************************************/

void Write (ch)
char  ch;
{

/* This is the only place where a character is put into the output buffer.*/

  if (buflen == MAXBUFSIZE)
  {
    (void) write (1, buffer, (unsigned) buflen);/* write entire buffer */
    buflen = 0;
  }
  buffer[buflen] = ch;
  buflen++;
}

/******************************************************************************/

void WriteString (s)
char *s;
{

/* SYSDEP: we assume end of string is first NULLCHAR, or string is full */

    while(*s != NULLCHAR)
	Write(*s++);
}

/******************************************************************************/

void WriteInt (i)
int   i;
{

/* We call WriteCard()after writing any '-' sign. */

  if (i < 0)
  {
    Write ('-');
    i = ABS (i);
  }
  WriteCard ((unsigned) i);
}

/******************************************************************************/

void WriteCard (c)
unsigned int  c;
{

/* Since the vast majority of given values will be small numbers, we avoid
   recursion until c >= 100.
*/

  if (c < 10)
  {
    Write ((char) ('0' + c));
  }
  else
    if (c < 100)
    {
      Write ((char) ('0' + (c / 10)));
      Write ((char) ('0' + (c % 10)));
    }
    else
    {
      WriteCard (c / 100);	/* recursive if c >= 100 */
      c = c % 100;
      Write ((char) ('0' + (c / 10)));
      Write ((char) ('0' + (c % 10)));
    }
}

/******************************************************************************/

void WriteLn ()
{
  Write (CR);
  Write (LF);
  WriteBuffer ();		/* WriteLn()also updates terminal */
}

/******************************************************************************/

void WriteBuffer ()
{

/* Output the buffer; either buffer is full or client has explicitly
   requested the terminal to be updated.
*/

  if (buflen > 0)
  {
    (void) write (1, buffer, (unsigned) buflen);
    buflen = 0;
  }
}

/******************************************************************************/

void RestoreTerminal ()
{
/* RestoreTerminal()should be called before any client module terminates. */

  WriteBuffer ();		/* make sure terminal is up-to-date */
  restoretty ();		/* SYSDEP: reset terminal characteristics saved
				   below */
}

/******************************************************************************/

void Initscreenio ()
{
  buflen = 0;
/* SYSDEP: we first save the current terminal characteristics.
   savetty also sets up ^C/^Z interrupt handlers; see unixio.c.
*/
  savetty ();
  singlecharon ();		/* cbreak mode for Read()and BusyRead() */
  echooff ();			/* no echo for BusyRead() */
}
