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

	ZTrmnl()

	This function sets up the input/output of commands.  Usually, that
means the input/output channels to the terminal,  but TECOC might be run
from a command procedure (under VMS) or a script file (under XENIX),  and
that possibility must be handled.  In addition,  the handling of interrupts
is found here.
	In general,  this function must:

		1. Set TIChan so it can be used to read commands
		2. Set TOChan so it can be used for output
		3. handle interrupts
		4. set TrmTyp

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

#include "ZPort.h"		/* define portability identifiers */
#include "DefTeco.h"		/* define general identifiers */
#include "DefScren.h"		/* define screen identifiers */

extern	VOID	ZAbort();	/* cleanup and exit */
extern	char	*ZAlloc();	/* allocate memory */
extern	VOID	ZDspBf();	/* display a buffer on the terminal */

EXTERN	WORD	EtFlag;		/* ET mode control flag */
EXTERN	WORD	EuFlag;		/* EU mode control flag */
EXTERN	BOOLEAN	GotCtC;		/* indicates that a CTRL_C has been hit */
EXTERN	LONG	TrmTyp;		/* terminal type */




#ifdef UNKNOWN
VOID ZTrmnl()		/* set up I/O to the terminal */
{
	puts("Failing in function ZTrmnl\n");
	exit(1);
}
#endif



#ifdef vax11c

/*****************************************************************************
	On the VAX,  TECO-C can be run under two environments: interactive or
non-interactive (like batch).  In an interactive session,  it uses the
terminal for input and output.  In non-interactive mode,  it goes through RMS.
The difference is the way the I/O completes.
	In interactive mode,  each character the user types is immediately
received by TECO-C (forget type-ahead for now).  This allows TECO-C to echo
the character immediately.
	In non-interactive mode,  input comes from a file and output goes to
another file,  so RMS is used.  RMS returns a bunch of characters to TECO-C
when the user types a "terminator" character, which is defined by VMS.
In non-interactive mode, it isn't important that TECO-C echo characters
immediately.
	Because both modes must be supported, there are two seperate i/o
systems in the code.  TECO-C determines if it is in interactive mode when
this function is called.  It sets TIChan to be the a channel associated with
the terminal for interactive mode,  or leaves TIChan unset (zero) for
non-interactive mode.  Other functions (ZDSpCh, ZDspBf, ZChin) test TIChan and
use either QIOs or RMS to perform I/O.
	Under VMS,  input comes from SYS$INPUT,  output goes to SYS$OUTPUT,
and control-C's come from SYS$COMMAND.  Control-Y's are not explicitly 
handled by TECOC.  If SYS$COMMAND is not a terminal device,  then control-C's
are not enabled.

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

#include dcdef		/* define device class identifiers */
#include "DefChars.h"	/* define identifiers for characters */
#include descrip	/* define string descriptor definition macros */
#include dvidef		/* define dvi item identifier codes */
#include fab		/* define RMS file access block structures */
#include iodef		/* define qio function identifiers */
#include nam		/* define RMS name block structures */
#include rab		/* define RMS record access block structures */
#include rmsdef		/* define RMS return status identifiers */
#include signal		/* define arguments to function signal() */
#include ssdef		/* define system service status return identifiers */
#include ttdef		/* define indentifiers for terminal stuff */
#include tt2def		/* define indentifiers for terminal stuff */

VOID	lib$stop();	/* terminate image with stack dump */
int	sys$assign();	/* VAX/VMS assign device system service */
int	sys$cancel();	/* VAX/VMS "cancel i/o request" system service */
int	sys$connect();	/* RMS CONNECT service */
int	sys$create();	/* RMS CREATE service */
int	sys$getdviw();	/* VAX/VMS get device information system service */
int	sys$open();	/* RMS OPEN service */
int	sys$qiow();	/* VAX/VMS "queue i/o" system service */



EXTERN	char	*TIBBeg;		/* SYS$INPUT file buffer */
EXTERN	char	*TIBEnd;		/* SYS$INPUT file buffer end */
EXTERN	char	*TIBERc;		/* ptr to end of record in buffer */
EXTERN	char	*TIBPtr;		/* ptr to current char in record */
EXTERN	char	TOBBeg[];		/* SYS$OUTPUT file buffer */
EXTERN	char	*TOBEnd;		/* SYS$OUTPUT file buffer end (+1) */
EXTERN	char	*TOBPtr;		/* SYS$OUTPUT file buffer pointer */
EXTERN	struct	FAB TIFab;		/* SYS$INPUT file access block */
EXTERN	struct	RAB TIRab;		/* SYS$INPUT record access block */
EXTERN	struct	FAB TOFab;		/* SYS$OUTPUT file access block */
EXTERN	struct	RAB TORab;		/* SYS$OUTPUT record access block */
EXTERN	short	TCChan;			/* terminal command channel */
EXTERN	short	TIChan;			/* terminal file channel */
EXTERN	short	TOChan;			/* terminal output channel */



int	devclass;			/* device class */
int	devdepend;			/* device dependent attributes */
int	devdepend2;			/* device dependent attributes */
char	devnam[64];			/* device name */
struct	io_status_block iosb;		/* input/output status block */
readonly $DESCRIPTOR(ter_c_descr,"SYS$COMMAND");
readonly $DESCRIPTOR(ter_i_descr,"SYS$INPUT");
readonly $DESCRIPTOR(ter_o_descr,"SYS$OUTPUT");
struct {
	short	buflen1;		/* buffer length */
	short	itmcode1;		/* item code */
	char	*buffer1;		/* buffer address */
	short	*retlen1;		/* returned length */
	short	buflen2;		/* buffer length */
	short	itmcode2;		/* item code */
	char	*buffer2;		/* buffer address */
	short	*retlen2;		/* returned length */
	short	buflen3;		/* buffer length */
	short	itmcode3;		/* item code */
	char	*buffer3;		/* buffer address */
	short	*retlen3;		/* returned length */
	short	buflen4;		/* buffer length */
	short	itmcode4;		/* item code */
	char	*buffer4;		/* buffer address */
	short	*retlen4;		/* returned length */
	int	termin;			/* item list terminator */
} itmlst = {
		4,			/* buffer length */
		DVI$_DEVCLASS,		/* device class */
		&devclass,		/* buffer address */
		0,			/* no returned length */
		4,			/* buffer length */
		DVI$_DEVDEPEND,		/* device dependent data */
		&devdepend,		/* buffer address */
		0,			/* no returned length */
		64,			/* buffer length */
		DVI$_DEVNAM,		/* device name */
		&devnam,		/* buffer address */
		0,			/* no returned length */
		4,			/* buffer length */
		DVI$_DEVDEPEND2,	/* device dependent data */
		&devdepend2,		/* buffer address */
		0,			/* no returned length */
		0			/* item list terminator */
};
short	output_sys_vfc = 1;
int	rms_status;
int	status;

VOID	CntrlC();			/* make the compiler happy */

VOID enable_ctrl_c_ast()
{
	int status;
	struct io_status_block c_iosb;

	status = sys$qiow(	0,		/* event flag number */
				TCChan,		/* channel */
				IO$_SETMODE|
				IO$M_CTRLCAST,	/* I/O func */
				&c_iosb,	/* I/O status block */
				0,		/* ast routine address */
				0,		/* ast parameter */
				CntrlC,		/* control-C routine */
				0,		/* p2 */
				0,		/* p3 */
				0,		/* p4 */
				0,		/* p5 */
				0);		/* p6 */

	if (status != SS$_NORMAL)
		lib$stop(status);
	if (c_iosb.io_status != SS$_NORMAL)
		lib$stop(c_iosb.io_status);
}



VOID CntrlC()					/* control-C ast routine */
/*****************************************************************************
	This function is called whenever a control-C is typed by the user.
It is called asynchronously.
*****************************************************************************/
{
	int	status;

	if (EtFlag & ET_TRAP_CTRL_C)		/* if user wants it */
		EtFlag &= ~ET_TRAP_CTRL_C;	/* turn off bit */
	else
		{
		if (EtFlag & ET_MUNG_MODE)	/* if in MUNG mode */
			ZAbort();
		GotCtC = YES;			/* set "stop soon" flag */
		}

	status = sys$cancel((long)TOChan);	/* cancel current output */
	if (status != SS$_NORMAL)
		lib$stop(status);
	enable_ctrl_c_ast();			/* re-enable the AST */
}


VOID open_terminal_input()
{
	status = sys$getdviw(	1,		/* event flag number */
				0,		/* channel */
				&ter_i_descr,	/* device name */
				&itmlst,	/* item list */
				&iosb,		/* i/o status block */
				0,		/* ast routine address */
				0,		/* ast parameter */
				0);		/* reserved by DEC */
	if (status != SS$_NORMAL)
		devclass = DC$_MISC;
	else
		if ((iosb.io_status != SS$_NORMAL) &&
		    (iosb.io_status != SS$_CONCEALED))
			lib$stop(iosb.io_status);

	if (devclass == DC$_TERM)		/* if it's a terminal */
		{
		if (!(devdepend & TT$M_LOWER))	/* if it has no lowercase */
			EtFlag &= ~ET_READ_LOWER;   /* don't read lowercase */
		status = sys$assign(	&ter_i_descr,	/* device name */
					&TIChan,	/* channel */
					0,		/* access mode */
					0);		/* mailbox name */
		if (status != SS$_NORMAL)
			lib$stop(status);
		return;
		}

	TIBERc = TIBBeg = ZAlloc(WBFINIT, 1);
	TIBEnd = TIBBeg + WBFINIT;
	--TIBEnd;
	TIBPtr = TIBERc;		/* causes the initial read */

	TIFab = cc$rms_fab;		/* initialize FAB defaults */
	TIFab.fab$b_fac = FAB$M_GET;	/* file access = read only */
	TIFab.fab$l_fna = ter_i_descr.dsc$a_pointer;
	TIFab.fab$b_fns = ter_i_descr.dsc$w_length;
	TIFab.fab$l_fop = FAB$M_INP |	/* this is SYS$INPUT and */
			  FAB$M_SQO;	/* sequential access only */

	rms_status = sys$open(&TIFab);		/* open terminal input */
	if (rms_status != RMS$_NORMAL)
		lib$stop(rms_status, TIFab.fab$l_stv);

	TIRab = cc$rms_rab;		/* initialize RAB defaults */
	TIRab.rab$l_fab = &TIFab;	/* address of associated FAB */
	TIRab.rab$b_rac = RAB$C_SEQ;	/* record access mode = sequential */
	TIRab.rab$l_rop = RAB$M_LOC |	/* use locate mode and */
			  RAB$M_RAH;	/* read ahead */
	TIRab.rab$l_ubf = TIBBeg;	/* input buffer */
	TIRab.rab$w_usz = WBFINIT;	/* input buffer size */

	rms_status = sys$connect(&TIRab);	/* connect terminal input */
	if (rms_status != RMS$_NORMAL)
		lib$stop(rms_status, TIRab.rab$l_stv);
}



VOID open_terminal_output()
{
	readonly static char LoadKeys[] = {
		ESCAPE, 'P',		/* control string introducer */
		'1',			/* load/clear only given keys */
		';',			/* seperator */
		'1',			/* do not lock keys */
		'|',			/* seperator */
		'2','3','/','1','B',	/* F11 is ESCAPE */
		';',			/* seperator */
		'2','4','/','8',	/* F12 is BS */
		';',			/* seperator */
		'2','5','/','A',	/* F13 is LF */
		ESCAPE, '\\'		/* terminator */
	};

	status = sys$getdviw(	1,		/* event flag number */
				0,		/* channel */
				&ter_o_descr,	/* device name */
				&itmlst,	/* item list */
				&iosb,		/* i/o status block */
				0,		/* ast routine address */
				0,		/* ast parameter */
				0);		/* reserved by DEC */
	if (status != SS$_NORMAL)
		devclass = DC$_MISC;
	else
		if ((iosb.io_status != SS$_NORMAL) &&
		    (iosb.io_status != SS$_CONCEALED))
			lib$stop(iosb.io_status);

	if (devclass == DC$_TERM)		/* if it's a terminal */
		{
		if (!(devdepend & TT$M_LOWER))	/* if won't show lowercase */
			EuFlag = EU_LOWER;	/* set lowercase flagging */
		if (devdepend & TT$M_SCOPE)	/* if scope */
			EtFlag |= ET_SCOPE;	/* set scope bit */
		if (devdepend2 & TT2$M_DECCRT2)	      /* VT200 compatible? */
			{
			TrmTyp = VT200;
			EtFlag |= ET_VT200;		/* VT200 mode */
			EtFlag |= ET_ACCENT_GRAVE;	/* VT200 mode */
			}
		else if (devdepend2 & TT2$M_DECCRT)   /* VT100 compatible? */
			TrmTyp = VT100;
		else if (devdepend2 & TT2$M_ANSICRT)  /* ANSI compatible? */
			TrmTyp = VK100;
		else				      /* default is VT52 */
/*??? VT05? vt61? should vt52 be the default? */
			TrmTyp = VT52;
		status = sys$assign(	&ter_o_descr,	/* device name */
					&TOChan,	/* channel */
					0,		/* access mode */
					0);		/* mailbox name */
		if (status != SS$_NORMAL)
			lib$stop(status);
/*****************************************************************************
	If the terminal is VT220-compatible,  then we want to load the F11
through F13 keys with the values they have when the terminal is in VT100 mode.
The reason for this is the convenience of the LF and BS immediate mode
commands.  The next block of code loads the keys and establishes an exit
handler which reloads the keys with their normal values before exiting.
	This solution is not optimal,  because if the system crashes or the
process is stopped by someone doing a STOP/ID=xxx, the exit handler is not
invoked and the keys are left with their normal values.  I tried a better
solution: recognizing the escape sequences sent by the keys when the terminal
is in VT200 mode and converting them to LF, BS, etc.  That solution worked,
but wasn't fast enough.
*****************************************************************************/
		if (TrmTyp == VT200)
			ZDspBf(LoadKeys, 23);
		return;
		}
	else					/* else not a terminal */
		{
	TOBEnd = &TOBBeg[WBFINIT]-1L;
	TOBPtr = &TOBBeg[0];

	TOFab = cc$rms_fab;		/* initialize FAB defaults */
	TOFab.fab$b_fac = FAB$M_PUT;	/* file access = write only */
	TOFab.fab$l_fna = ter_o_descr.dsc$a_pointer;
	TOFab.fab$b_fns = ter_o_descr.dsc$w_length;
	TOFab.fab$b_fsz = 2;		/* fixed size control area = 2 */
	TOFab.fab$b_org = FAB$C_SEQ;	/* organization = sequential */
	TOFab.fab$b_rat = FAB$M_PRN;	/* print file format */
	TOFab.fab$b_rfm = FAB$C_VFC;	/* variable with fixed control */

	rms_status = sys$create(&TOFab);	/* open terminal output */
	if (rms_status != RMS$_NORMAL)
		lib$stop(rms_status, TOFab.fab$l_stv);

	TORab = cc$rms_rab;		/* initialize RAB defaults */
	TORab.rab$l_fab = &TOFab;	/* address of associated FAB */
	TORab.rab$l_rhb = &output_sys_vfc;	/* print control buffer */
	TORab.rab$l_rop = RAB$M_WBH;	/* write behind */
	TORab.rab$l_rbf = &TOBBeg[0];	/* output buffer */

	rms_status = sys$connect(&TORab);	/* connect terminal output */
	if (rms_status != RMS$_NORMAL)
		lib$stop(rms_status, TORab.rab$l_stv);
		}
}



VOID open_terminal_command()
{
	status = sys$getdviw(	1,		/* event flag number */
				0,		/* channel */
				&ter_c_descr,	/* device name */
				&itmlst,	/* item list */
				&iosb,		/* i/o status block */
				0,		/* ast routine address */
				0,		/* ast parameter */
				0);		/* reserved by DEC */
	if (status != SS$_NORMAL)
		devclass = DC$_MISC;
	else
		if ((iosb.io_status != SS$_NORMAL) &&
		    (iosb.io_status != SS$_CONCEALED))
			lib$stop(iosb.io_status);

	if (devclass == DC$_TERM)		/* if it's a terminal */
		{
		status = sys$assign(	&ter_c_descr,	/* device name */
					&TCChan,	/* channel */
					0,		/* access mode */
					0);		/* mailbox name */
		if (status != SS$_NORMAL)
			lib$stop(status);
		signal(SIGINT,SIG_IGN);		/* ignore SIGINT */
		enable_ctrl_c_ast();		/* enable control-C */
		}
}



VOID ZTrmnl()		/* set up I/O to the terminal */
{
	open_terminal_input();		/* open SYS$INPUT */
	open_terminal_output();		/* open SYS$OUTPUT */
	open_terminal_command();	/* open SYS$COMMAND */
}
#endif




#ifdef XENIX
/*****************************************************************************

	The terminal will be set up for buffered I/O,  so characters won't be
received until a RETURN is hit,  and they will be automatically echoed.  Set
the terminal up for raw I/O,  so each character is received when it is struck,
and no echoing is performed.  Save the terminal characteristics so we can
reset them (in ZAbort) to what they were before we changed them.

*****************************************************************************/
#include <sgtty.h>		/* define the "sgtty" structure tag */
#include <signal.h>		/* define arguments to signal() */

EXTERN	int	tty_fd;		/* terminal file descriptor */
EXTERN	struct	sgttyb tty_cb;	/* terminal characteristics block */
EXTERN	int	savd_tty_flags;	/* saved terminal flags */
EXTERN	BOOLEAN	tty_opened;	/* YES if the terminal has been opened */
EXTERN	BOOLEAN tty_set;	/* YES if the terminal has been set */


VOID CntrlC()
{
	if (EtFlag & ET_TRAP_CTRL_C)		/* if user wants it */
		EtFlag &= ~ET_TRAP_CTRL_C;	/* turn off bit */
	else
		{
		if (EtFlag & ET_MUNG_MODE)	/* if in MUNG mode */
			ZAbort();
		GotCtC = YES;			/* set "stop soon" flag */
		}
	signal(SIGINT, CntrlC);
}


VOID ZTrmnl()			/* set up I/O to the terminal */
{
	signal(SIGINT, CntrlC);			/* call CntrlC on interrupt */

	if (tty_fd = open("/dev/tty",2) == -1)	/* for reading and writing */
		{	
		puts("Unable to open a channel to the terminal");
		tty_fd = 0;
		ZAbort();
		}
	tty_opened = YES;

	if (gtty(tty_fd, &tty_cb) != 0)		/* get characteristics */
		{
		puts("Unable to get terminal characteristics");
		ZAbort();
		}	

	savd_tty_flags = tty_cb.sg_flags;	/* save the flags */
	tty_cb.sg_flags |= CBREAK;		/* turn on CBREAK */
	tty_cb.sg_flags &= ~ECHO;		/* turn off ECHO */

	if (stty(tty_fd, &tty_cb) != 0)		/* set characteristics */
		{
		puts("Unable to set terminal characteristics");
		ZAbort();
		}	
	tty_set = YES;

	TrmTyp = VT102;
}
#endif
