/* @(#)ost.c	19.1 (ES0-DMD) 02/25/03 13:56:14 */
/*===========================================================================
  Copyright (C) 1995 European Southern Observatory (ESO)
 
  This program is free software; you can redistribute it and/or 
  modify it under the terms of the GNU General Public License as 
  published by the Free Software Foundation; either version 2 of 
  the License, or (at your option) any later version.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
 
  You should have received a copy of the GNU General Public 
  License along with this program; if not, write to the Free 
  Software Foundation, Inc., 675 Massachusetss Ave, Cambridge, 
  MA 02139, USA.
 
  Corresponding concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*++++++++++++++++++++
.TYPE		Module
.IDENTIFICATION ost
.AUTHOR    	Guido Russo [ST-ECF], Francois Ochsenbein [ESO-IPG]
.LANGUAGE  	C
.KEYWORDS	Terminal i/o
.ENVIRONMENT	VAX / VMS Version
.COMMENTS	Adapted from Toolpack TIE WINDOWS.		
.VERSION 1.0	30-Jan-1986:	Creation
.VERSION 1.1	19-Jun-1986:	Cosmetic changes (F. Ochsenbein).
		Redefined I/O functions as #define's.
		Traced system errors.
		Modified the Terminator Mask as a short expression.
.VERSION 1.2 	01-Jul-1986: 	Since QIO is used for output and QIOW
		for input, 2 DIFFERENT i/o status blocks are required
		for read and write operations !!!
.VERSION 1.3	14-Aug-1986: The baud rate is also returned.
.VERSION 1.4	10-Oct-1986: A new function, xv_close, does
		the required wait until all text is displayed.
.VERSION 1.5	11-Jun-1987: Cosmetic modifications
.VERSION 1.6	16-Jun-1987: Adapted for Ibm-PC, G. Russo, M. Albrecht.
.VERSION 1.7	22-Jun-1987: Adapted for UNIX		
.VERSION 1.8	19-Jan-1988: Transformed to os routine ostinfo, ostopen, 
			ostclose, ostread, ostwrite. A time-out is ALWAYS used.
			ostin returns the number of bytes in type-ahead buffer.
.VERSION 2.0	09-Mar-1988: Added interrupt catching, to be sure
			that the terminal is correcly reset.
.VERSION 2.1	28-Oct-1988: No ^C catching in VMS version.
.VERSION 2.2	18-May-1989: Added vt300_series terminal type to ostinfo.
			     When terminal is unknown, the string "unknown"
			     is returned. M. Albrecht [ESIS]
.VERSION 2.3	24-Jul-1989: ostint returns previously defined interrupt function
.VERSION 2.4	03-Aug-1990: ostint works also if terminal was not opened.
.VERSION 2.5	08-Sep-1990: allow long output
.VERSION 2.6	17-Oct-1990: Removed GNU-nonunderstandable code...
.VERSION 2.65	06-Nov-1990: Added ostwinch (Window Change)
----------------------------------------------------------------------------*/

#define DEBUG		0

#include <osdefos.h>		
#include <macrogen.h>		/* Classical macros	*/
#include <osterm.h>		/* Terminal Definitions	*/
#include <signal.h>
#include <errno.h>
#ifdef EOF
#undef EOF
#endif

typedef int (*FUNCTION)();	/* Pointer to Function	*/

#ifndef SIG_ERR
#define SIG_ERR (FUNCTION)-1
#endif

int oststop();
static int (*f_int)() = NULL_FCT(int);		/* Interrupt function */
static char buf_name[24];


#	include <ssdef.h>
#	include <iodef.h>
#	include <dcdef.h>
#	include <ttdef.h>
#	include <tt2def.h>
#	define	dsc$w_length	len
#	define	dsc$a_pointer	s
#	include <descrip.h>
#	define ENTER()		oserror = 0, vmserror = SS$_NORMAL
#	define EXIT(x)		FIN: if (!(vmserror&1))	\
				if (oserror == 0) oserror = EIO;\
			      	return (oserror ? -1 : x);
#	define CALL$(f)		if (!((vmserror = f)&1))	FINISH;

#	define func_ssm		IO$_SENSEMODE
#	define func_sen		IO$_SENSEMODE|IO$M_TYPEAHDCNT
#	define func_out		IO$_WRITELBLK|IO$M_NOFORMAT
#	define func_cin		IO$_READLBLK|IO$M_NOFILTR|IO$M_NOECHO|IO$M_TIMED
	int SYS$WAITFR();
	static int tt_chan 	= 0;		/* Channel number	*/
	static int tt_efn_r 	= 0;		/* Event flag read	*/
	static int tt_efn_w 	= 0;		/* Event flag write	*/
	static int mask[2] = {0,0};	/* Short Mask, no terminator char */
	static unsigned short iosb_r[4]= {0};	/* I/O status block for read  */
	static unsigned short iosb_w[4]= {SS$_NORMAL, 0, 0, 0};	
						/* I/O status block for write */
	static unsigned short io_sm[4] = {0};

	static char tt_des_s[]	= "TT:";
	static struct dsc$descriptor_s tt_des = 
		{ sizeof(tt_des_s)-1, DSC$K_DTYPE_T, DSC$K_CLASS_S, tt_des_s };


#define	FINISH		goto FIN

MID_EXTERN int oserror;
MID_EXTERN char *oserrmsg;
MID_EXTERN int  vmserror;		/* VMS-specific error code */

/*==========================================================================
 *			VMS-specific internal routines
 *==========================================================================*/
static int assign()
/*+++++
.PURPOSE Assign channel to terminal + event flags
.RETURNS 0 (sucess) / -1 (failure), oserror explains
.REMARKS If no terminal attached, returns -1.
--------------*/
{
  ENTER();

  if (tt_chan == 0)
  {	CALL$(LIB$GET_EF(&tt_efn_r));	/* Get an event flag for read 	*/
	CALL$(SYS$CLREF(tt_efn_r));	/* ... and clear it       	*/

	CALL$(LIB$GET_EF(&tt_efn_w));	/* Get an event flag for write	*/
	CALL$(SYS$CLREF(tt_efn_w));	/* ... and clear it       	*/

	CALL$(SYS$ASSIGN(&tt_des,&tt_chan,0,0)); /* Assign a channel to TT */
  }

  EXIT(0);
}

/*==========================================================================
 *			ostinfo
 *==========================================================================*/
int	ostinfo (tstat)
/*+++++
.PURPOSE Get info about the terminal in termstatus structure (see osterm.h),
	i.e. terminal name, number of lines, of columns, baud rate,
	and interrupt control characters.
.RETURNS 0 (sucess) / -1 (failure), oserror explains
.REMARKS If no terminal attached, returns -1.
--------------*/
	struct termstatus *tstat;	/* OUT: Terminal description */
{
	register int i;
	register char *p, *q;

  ENTER();

  tstat->lines    = 0;	tstat->columns  = 0; 

  if (assign() < 0)	FINISH;
  CALL$(SYS$QIOW(tt_efn_r,tt_chan,func_ssm,iosb_r,0,0,
             io_sm,sizeof(io_sm),0,0,0,0));	/* Get device characteristics */

  tstat->lines    = io_sm[3] >> 8;	/* Screen length 		*/
  tstat->columns  = io_sm[1];		/* Width of screen 		*/

 	/* Get the baud rate --- However, some rounding is sufficient */

  switch(iosb_r[1] & 0xFF)
  { case TT$C_BAUD_150: 	
    case TT$C_BAUD_134:   i = 150; 	break;
    case TT$C_BAUD_110:   i = 110; 	break;
    case TT$C_BAUD_50:    i = 50 ; 	break;
    case TT$C_BAUD_75:    i = 75 ; 	break;
    case TT$C_BAUD_300:   i = 300; 	break;
    case TT$C_BAUD_600:   i = 600; 	break;
    default:
    case TT$C_BAUD_1200:  
    case TT$C_BAUD_1800:  i = 1200; 	break;
    case TT$C_BAUD_2000:  i = 2000; 	break;
    case TT$C_BAUD_2400:  i = 2400; 	break;
    case TT$C_BAUD_3600:  i = 3600; 	break;
    case TT$C_BAUD_4800:  i = 4800; 	break;
    case TT$C_BAUD_7200:  i = 7200; 	break;
    case TT$C_BAUD_9600:  i = 9600; 	break;
    case TT$C_BAUD_19200: i = 19200; 	break;
  }

	/* Set terminal type according to physical device */
  switch(io_sm[0] >> 8) 	/* VMS-supplied terminal type 	*/
  {
           case	TT$_VT52:
	   case	TT$_VT55:	p = "vt52";	/* VT52  class */
				break;
	   case	TT$_VT100:
	   case	TT$_VT101:
	   case	TT$_VT105:
	   case	TT$_VT125:	p = "vt100";	/* VT100 class */
				break;
	   case	TT$_VT102:
	   case	TT$_VT131:
	   case	TT$_VT132:	p = "vt102";	/* VT102 class */
				break;
	   case	TT$_VT200_SERIES:
	   			p = "vt200";	/* VT200 class */
				break;
	   case TT$_VT300_SERIES:
	   			p = "vt300";	/* VT300 class */
	   			break;
	   case	TT$_LA12:
	   case	TT$_LA34:
	   case	TT$_LA36:
	   case	TT$_LA38:
	   case	TT$_LA120:
	   case	TT$_LA100:	p = "ht";	/* Hardcopy */
				break;
	   default:		p = "unknown";	/* unknown  */
				break;
  }
  tstat->cc_INT   = Control_('C');	
  tstat->cc_QUIT  = Control_('Y');	

  tstat->baud_rate = i;
	/* Copy terminal name to buffer */
  i = strlen(p);
  if (i > sizeof(buf_name)-1)	i = sizeof(buf_name)-1;
  for (q = buf_name; --i >= 0; )	*(q++) = *(p++);
  *q = EOS;
  tstat->termname      = buf_name;

  EXIT(0);
}

/*==========================================================================
 *			ostopen
 *==========================================================================*/
int	ostopen ()
/*+++++++++++++
.PURPOSE Initialize the terminal. 
.RETURNS 0 (sucess) / -1 (failure), oserror explains
.REMARKS If no terminal attached, returns -1.
--------------*/
{
  ENTER();

  if (assign() < 0)	FINISH;

  CALL$(SYS$QIOW(tt_efn_r,tt_chan,func_ssm,iosb_r,0,0,
             io_sm,sizeof(io_sm),0,0,0,0));	/* Get device characteristics */
#if 0		/* Don't catch ^C	*/
  CALL$(SYS$QIOW(tt_efn_w,tt_chan,IO$_SETMODE|IO$M_CTRLCAST,iosb_w,0,0,
             f_int,SIGINT,0,0,0,0));		/* Enable Control C */
#endif

  EXIT(0);
}

/*==========================================================================
 *			ostclose
 *==========================================================================*/
int	ostclose ()
/*+++++++
.PURPOSE Close the terminal (reset it to normal)
.RETURNS 0 / -1 on failure 
----------*/
{	
    register int l;
    
  ENTER();

  l = 0;

#if DEBUG
  puts("\n....Closed Terminal");
#endif

  EXIT(l);
}

/*==========================================================================
 *			oststop
 *==========================================================================*/
int	oststop (s)
/*++++++++++++
.PURPOSE Terminate program in case of interrput
.RETURNS To OS
----------------------------------------------------------------------------*/
	int s;	/* IN: Signal identification */
{	
  ENTER();

  ostclose();
  
  SYS$EXIT(SS$_NORMAL);
  EXIT(0);
}

/*==========================================================================
 *			ostset
 *==========================================================================*/
int	ostset (tstat)
/*++++
.PURPOSE Set Terminal.
.RETURNS 0 / -1
.REMARKS Only interrupt / quit controls are reset.
----------------------------------------------------------------------------*/
	struct termstatus *tstat;	/* IN: Status structure with controls\
					 to modify */
{	
  ENTER();

  EXIT(0);
}

/*==========================================================================
 *			ostraw
 *==========================================================================*/
int	ostraw (op)
/*++++
.PURPOSE Set Terminal to raw (op = 1), to Normal (0)
.RETURNS 0 / -1
.REMARKS Does nothing...
---------------------------------*/
	int	op;	/* IN: 0 to reset to standard, 1 to reset to raw */
{	
  ENTER();

  EXIT(0);
}

/*==========================================================================
 *			ostint
 *==========================================================================*/
FUNCTION ostint (f)
/*++++
.PURPOSE Define a function for interrupt
.RETURNS Previous function
----------------------------------------------------------------------------*/
	FUNCTION f;	/* IN: Function to call in case of Interrupt */
{	
	FUNCTION old_f;
	
  ENTER();

  old_f = f_int;
  f_int = f;

  if (!tt_chan)		assign();
  CALL$(SYS$QIOW(tt_efn_w,tt_chan,IO$_SETMODE|IO$M_CTRLCAST,
	  	iosb_w,0,0,0,0,0,0,0,0));		/* Disable Control C */
  CALL$(SYS$QIOW(tt_efn_w,tt_chan,IO$_SETMODE|IO$M_CTRLCAST,
	  	iosb_w,0,0,f_int,SIGINT,0,0,0,0));	/* Enable Control C */
  CALL$(SYS$QIOW(tt_efn_w,tt_chan,IO$_SETMODE|IO$M_CTRLCAST,
	  	iosb_w,0,0,SYS$WAITFR,tt_efn_w,0,0,0,0)); /* Called before */

  FIN:
  return(old_f);
}

FUNCTION ostwinch (f)
/*++++
.PURPOSE Define a function for Window Change
.RETURNS Previous function
----------------------------------------------------------------------------*/
	FUNCTION f;	/* IN: Function to call in case of Window Change */
{	
	FUNCTION old_f;

  ENTER();

  old_f = SIG_ERR;
  oserror = -1, oserrmsg = "WINCH not available";

  return(old_f);
}

/*==========================================================================
 *			ostin
 *==========================================================================*/
int	ostin()
/*+++
.PURPOSE Retrieve the number of characters waiting for input (type-ahead buffer)
.RETURNS Number of bytes waiting in type-ahead buffer / -1 for error.
.REMARKS No actual read is performed.
--------*/
{
	register int l;

  ENTER();

	/* Call system routine to get number of characters 
	 * in type-ahead buffer					*/

  CALL$(SYS$QIOW(tt_efn_r,tt_chan,func_sen,iosb_r,0,0,
		io_sm,sizeof(io_sm),0,0,0,0));

  l = io_sm[0];

  EXIT(l);
}

/*==========================================================================
 *			ostread
 *==========================================================================*/
int	ostread(string, length, timeout)
/*+++
.PURPOSE Input from the terminal (a timeout is REQUIRED!).
	 A null timeout performs the reading of what is in the
	 type-ahead buffer only.

\begin{TeX}
	 This routine must perform binary input --- \ie the input is not
	 echoed or edited, and the program receives the user's input
	 without having to wait for a RETURN / newline / etc.
	 
	 If the user has not
	 typed anything within the maximum time passed (in seconds)
	 it should return with the value zero.
	 It is desirable (but not
	 strictly necessary) that the "wait" be cut short if input is
	 received after the routine call but before the expiry of the
	 time period --- if this is not the case then the calling routines
	 may simply be a bit slow.
\end{TeX}
.RETURNS Number of bytes read / -1 (End of File or Failure)
-----------------------------------------------------------------------*/
	char	*string;	/* OUT: string read 		*/
	int 	length;		/* IN: size of string 		*/
	int	timeout;	/* IN: Timeout time in seconds 	*/
{
	register int l, time;
	static long int  num_char;

  ENTER();

  if (length <=0) 		/* Impossible to get 0 char	*/
  {	oserror = EINVAL;
	FINISH;
  }
				/* Test timeout			*/
  time = (timeout < 0 ? 0 : timeout);


	/* Call system routine to get number of characters 
	 * in type-ahead buffer					*/

  CALL$(SYS$QIOW(tt_efn_r,tt_chan,func_sen,iosb_r,0,0,
		io_sm,sizeof(io_sm),0,0,0,0));

				/* if buffer empty, input 1 character */
  l = (io_sm[0] ? io_sm[0] : 1 );
  l = MIN(l, length); 		/* read as many as the buffer 	*/

	/* Call system routine to actually perform read 
	 * from the type-ahead buffer				*/

  CALL$(SYS$QIOW(tt_efn_r,tt_chan,func_cin,iosb_r,0,0,string,l,
               time,mask,0,0));

	/* Check for successfull completion. 
	 * Note that timeout is not an error,
	 * and number of read bytes need not to be identical with request.
	 */

  l = iosb_r[1];	/* actual number of char read */

  switch(iosb_r[0])
  { case SS$_NORMAL: case SS$_TIMEOUT:		/* successful completion */
	vmserror = SS$_NORMAL;
    default:	break;				/* Report system error	*/
  }

  EXIT(l);
}

/*==========================================================================
 *			ostwrite
 *==========================================================================*/
int	ostwrite (string, length)
/*+++
.PURPOSE Physical output of a string to the terminal.
.RETURNS Number of bytes written / -1 (failure, oserror explains)
-----------------------------------------------------------------------------*/
	char	*string;		/* IN: string to be written */
	int	length;			/* IN: length of string */
{
	register int l;
	register char *p, *pe;

  ENTER();

  for (p = string, pe = p + length; p < pe; p += l) {
	l = pe - p;
	if (l > 512)	l = 512;		/* Limit	*/
	CALL$(SYS$QIOW(tt_efn_w,tt_chan,func_out,iosb_w,0,0,
			p,l,0,0,0,0));
  }
  
  EXIT(l);
}

