/*===========================================================================
  Copyright (C) 1987-2009 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 tvinit.c
.AUTHOR    	Francois Ochsenbein [ESO-IPG]
.LANGUAGE  	C
.KEYWORDS	Terminal Independant i/o Package
.ENVIRONMENT	TermWindows
.COMMENTS	This module provides functions to open/close the
		terminal.
		PhysiCal output makes use of ost.
		*** NOTE *** This version includes the OS-dependant
		fi_date routine ***
\begin{TeX}
Used Capabilities:
$$\begin{tabular}{|lp{30em}|}\hline
{\tt .M=}	& Switch on automatic margin				\\
{\tt .m=}	& Switch off automatic margin				\\
{\tt am}	& Terminal has automatic margins			\\
{\tt co\#}	& Number of columns on the screen			\\
{\tt is=}	& Initialization sequence				\\
{\tt ti=}	& Initialization sequence for special modes	\\
{\tt te=}	& Undo what's done by {\tt ti}		\\
{\tt ks=}	& Start keypad mode					\\
{\tt ke=}	& Exit keypad mode					\\
{\tt li\#}	& Number of lines on the screen				\\
{\tt rs=}	& Reset terminal sequence				\\
{\tt se=}	& Exit Stand-out mode (normal attributes)		\\
{\tt Va=}	& Set keyboard to ASCII					\\
{\tt Vn=}	& Set normal Video attributes				\\
{\tt up=}	& Move Cursor up					\\
{\tt do=}	& Move Cursor down					\\
{\tt bc=}	& Move Cursor left					\\
{\tt nd=}	& Move Cursor right					\\
{\tt nl=}	& Write a newline					\\
{\tt cm=}	& Cursor absolute positionning				\\
\hline
\end{tabular}$$
The default TERMCAP file the logical {\tt TERMCAPFILE}
(or some other definition in {\tt stdef:terminal.h}).
However, the login directory is searched for an existing (binary)
copy of terminal capabilities, named from terminal name with extension
{\tt .tty}.\\[0.5ex]
{\bf Notes}:
\begin{enumerate}
\item	if the terminal has automatic margins (the cursor moves to
	the beginning of the next line when the right margin is reached),
	the number of columns is decremented to avoid wrapping problems.
\item	if no terminal is available (\eg in batch mode),
	what should appear on the terminal is simply directed to stdout.
\end{enumerate}

\end{TeX}

.VERSION 1.0	07-Jul-1986:	Creation
.VERSION 1.1	02-Sep-1986:	The default directory is searched
		for a binary copy of terminal capabilities.
		The TERMCAPFILE is only used if such a binary copy does not
		exist.
.VERSION 1.2	26-Sep-1986:	At initialisation, set scrolling region
				to whole screen.
.VERSION 1.3	10-Oct-1986:	At closing, use of newly defined
				tt_close function.
.VERSION 2.0	03-Dec-1986:	New terminal-independant version.
				Buffering capabilities.
.VERSION 2.1	12-Jun-1987:	Possibility of Batch Mode.
				Modified bug in ztinit.
				Version '2' of TermCap: check if Termcapfile
					was not modified.
.VERSION 2.2	23-Jun-1987:	Minimal implementation in Version 2,
		with UNIX compatibility.
		This minimal implementation has the restrictions:
\begin{TeX}
\begin{itemize}
\item	no batch mode
\item	a binary copy of the termcapabilities for the specific terminal
	exist: no need to parse the TermCapFile and to encode the
	capabilities.
\item	no batch possibility
\item	no insert/delete/scrolling
\end{itemize}
\end{TeX}

.VERSION 2.3	02-Dec-1987	Use of external function while waiting for input
.VERSION 3.0	15-Mar-1988	Adapted on top of OS routines. Version 3
			which allows definition of control characters.
.VERSION 3.1	21-Feb-1989	Added tv_setcc
.VERSION 3.2	28-Mar-1989	Added DEFAULT_TERMINAL = ansi
.VERSION 3.3	17-May-1989	Check minimal capabilities
.VERSION 3.4	19-Jun-1989	Allow :nl=\n capability
.VERSION 3.5	29-Jun-1989	Check correctly the version...
.VERSION 3.6	25-Jul-1989	Reset Automatic Margins at Close...
.VERSION 3.7	23-Aug-1989	`ti' cap is required for SunView...
			Also added (for Unix) a lookup in Local /etc/termcap
.VERSION 3.8	28-May-1990	Allow hard terminals...
.VERSION 3.9	27-Aug-1990	Leave Int = ^C as default in Unix
.VERSION 3.91	07-Sep-1990	Let write a message if a problem occurs...
.VERSION 3.92	09-Oct-1990	Allow file output (Unix background)
.VERSION 3.93	03-Dec-1990	tv_setsc allows the change of Control Char Set
.VERSION 3.94	26-Feb-1991	Was calling tv_open recursively in batch...
				Also added socket option in tv_setsc
.VERSION 4.0 	28-Jan-1994	Create the binary copy in MID_WORK vs HOME. CG.

 090831		last modif
-------------------------------------------------------*/

#define	DEBUG		0		/* For debugging only		*/
#define	PM_LEVEL 	LEVEL_TV

#define Closing_Wait	500		/* ms to wait at Closing	*/

#define DEFAULT_TERMINAL	"ansi"

#include <midas_def.h>

#define	TW_MACROS	0		/* Don't use TermWindows Macros */
#define	TW_import	0	
#define TW_STRUCT	0	/* Do not use window Structures		*/
#include <osterm.h>		/* OS Terminal Definitions		*/
#include <twset.h>

#include <stdlib.h>


extern int tv_attr(), tv_where(), tv_goto(), tv_sr0();
extern int tv_clear(), tv_send(), tv_open(), tv_nl();
extern int tv_getc(), tv_bell(), tv_push();

extern int pm_ed_tr2(), pm_enter(), pm_iexit(), pm_lfile(), pm_ed_trace();

extern int eh_put1(), eh_class(), eh_ed_as(), eh_clear();

extern int tu_load();



#ifndef NULL
#define NULL	NULL_PTR(char)
#endif

#define EXT_TTY ".tty"

#if MID_OS_MSDOS				/* --- For Ibm-PC (M. Albrecht)	*/
	MID_STATIC  char *DIR_TTY =	"\\";	/* Default Directory	*/
#	define  DIR_TTY_TRAILER	""
#else
#if MID_OS_VMS
	MID_STATIC	char *DIR_TTY =	"SYS$LOGIN:";
#	define  DIR_TTY_TRAILER	""
#else					/* UNIX		*/
	MID_STATIC  char *DIR_TTY = NULL;	/* Default Directory	*/
#	define  DIR_TTY_TRAILER	"/"
# 	define	local_termcapfile	"/etc/termcap"
#endif
#endif

MID_STATIC TERM 	sterm = {	/* The Terminal Definition	*/
	1,			/* No Terminal		*/
	0			/* The version id	*/
 };
static struct termstatus tst;

MID_GLOBAL TERM	*terms = &sterm;
			/* The terminal structure common to TV routines */

static	short int old_pos[2];

MID_STATIC char ccdef[4][33] = {	/* Control Characters */
    { 	TW_cc_NONE, TW_cc_MODE, TW_cc_HOME, TW_cc_INT,		/* User */
	TW_cc_DOWN, TW_cc_EOL,  TW_cc_REFR, TW_cc_NONE,	/* ^D  ^E  ^F  ^G */
	TW_cc_HELP, TW_cc_NW, 	TW_cc_RET , TW_cc_HELP,	/* ^H  ^I  ^J  ^K */
	TW_cc_LEFT, TW_cc_RET,  TW_cc_NW,   TW_cc_NONE,	/* ^L  ^M  ^N  ^O */
	TW_cc_NONE, TW_cc_NONE, TW_cc_RIGHT,TW_cc_NONE,	/* ^P  ^Q  ^R  ^S */
	TW_cc_NONE, TW_cc_UP,   TW_cc_DELE, TW_cc_DELW,	/* ^T  ^U  ^V  ^W */
	TW_cc_DELL, TW_cc_QUIT, TW_cc_EOF,  TW_cc_NONE,	/* ^X  ^Y  ^Z  ^[ */
	TW_cc_RAW,  TW_cc_NONE, TW_cc_MODE, TW_cc_NONE,	/* ^\  ^]  ^^  ^_ */
	TW_cc_DELC},				/* Delete */
    { 	TW_cc_NONE, TW_cc_MODE, TW_cc_UP,   TW_cc_INT,		/* VMS */
	TW_cc_LEFT, TW_cc_EOL,  TW_cc_RIGHT,TW_cc_HELP,	/* ^D  ^E  ^F  ^G */
	TW_cc_HOME, TW_cc_NW,   TW_cc_RET , TW_cc_HELP,	/* ^H  ^I  ^J  ^K */
	TW_cc_NONE, TW_cc_RET,  TW_cc_DOWN, TW_cc_NONE,	/* ^L  ^M  ^N  ^O */
	TW_cc_NONE, TW_cc_NONE, TW_cc_REFR, TW_cc_NONE,	/* ^P  ^Q  ^R  ^S */
	TW_cc_NONE, TW_cc_DELL, TW_cc_DELE, TW_cc_DELW,	/* ^T  ^U  ^V  ^W */
	TW_cc_NONE, TW_cc_QUIT, TW_cc_EOF,  TW_cc_NONE,	/* ^X  ^Y  ^Z  ^[ */
	TW_cc_RAW,  TW_cc_NONE, TW_cc_MODE, TW_cc_NONE,	/* ^\  ^]  ^^  ^_ */
	TW_cc_DELC},				/* Delete */
    { 	TW_cc_NONE, TW_cc_MODE, TW_cc_LEFT, TW_cc_INT,		/* Unix */
	TW_cc_EOF,  TW_cc_EOL,  TW_cc_RIGHT,TW_cc_NONE,	/* ^D  ^E  ^F  ^G */
	TW_cc_DELC, TW_cc_NW,   TW_cc_RET,  TW_cc_HELP,	/* ^H  ^I  ^J  ^K */
	TW_cc_HOME, TW_cc_RET,  TW_cc_DOWN, TW_cc_NONE,	/* ^L  ^M  ^N  ^O */
	TW_cc_UP,   TW_cc_NONE, TW_cc_REFR, TW_cc_NONE,	/* ^P  ^Q  ^R  ^S */
	TW_cc_NONE, TW_cc_DELL, TW_cc_RAW,  TW_cc_DELW,	/* ^T  ^U  ^V  ^W */
	TW_cc_DELE, TW_cc_NONE, TW_cc_NONE, TW_cc_NONE,	/* ^X  ^Y  ^Z  ^[ */
	TW_cc_QUIT, TW_cc_NONE, TW_cc_MODE, TW_cc_NONE,	/* ^\  ^]  ^^  ^_ */
	TW_cc_DELC},				/* Delete */
    { 	TW_cc_NONE, TW_cc_NONE, TW_cc_NONE, TW_cc_NONE,		/* Socket */
	TW_cc_EOF,  TW_cc_EOL,  TW_cc_REFR, TW_cc_NONE,	/* ^D  ^E  ^F  ^G */
	TW_cc_HELP, TW_cc_NONE, TW_cc_RET , TW_cc_HELP,	/* ^H  ^I  ^J  ^K */
	TW_cc_RET,  TW_cc_RET,  TW_cc_NONE, TW_cc_NONE,	/* ^L  ^M  ^N  ^O */
	TW_cc_NONE, TW_cc_NONE, TW_cc_NONE, TW_cc_NONE,	/* ^P  ^Q  ^R  ^S */
	TW_cc_NONE, TW_cc_NONE, TW_cc_RAW,  TW_cc_NONE,	/* ^T  ^U  ^V  ^W */
	TW_cc_NONE, TW_cc_NONE, TW_cc_EOF,  TW_cc_NONE,	/* ^X  ^Y  ^Z  ^[ */
	TW_cc_NONE, TW_cc_NONE, TW_cc_MODE, TW_cc_NONE,	/* ^\  ^]  ^^  ^_ */
	TW_cc_DELC},				/* Delete */
 };

		/* Macros just to improve readibility	*/

#define		SEND(p)			tv_send(p,1)
#define		curl			(terms->pos[0])
#define		curc			(terms->pos[1])
#define		diml			(terms->dim[0])
#define		dimc			(terms->dim[1])
#define		oldl			old_pos[0]
#define		oldc			old_pos[1]
#define		KEYPAD			(terms->flags & TERM_KPAD)

#define		TTattr			terms->tt_attr
#define		Vn			0
#define		VB			1
#define		Vu			2
#define		Vb			3
#define		Vr			4
#define		Vg			5
#define		Va			6

	/* MONITOR(TVINIT); */


/*==========================================================================
 *			tv_setcc
 *==========================================================================*/
int tv_setcc(c, action)
/*+++++++
.PURPOSE Define the meaning of Control Characters for TermWindow Usage.
.RETURNS Actual action of cc (differs if could not be changed)
.REMARKS If cc is alphabetic, it's converted to control character.
----------*/
	int c;		/* IN: The control character */
	int action;	/* IN: Action TW_cc_xxx as defined in twdef.h */
{
	register char *p;
	register int  k;
	static  char cc;

  ENTER("+tv_setcc");

  p = NULL_PTR(char);
  cc = (iscntrl(c) ? c : Control_(c));
  k = (cc < 32 ? cc : 32);	/* Position in terms->tc */
  switch(action)
  { case TW_cc_INT:  p = &(tst.cc_INT);  break;
    case TW_cc_QUIT: p = &(tst.cc_QUIT); break;
    default:	if ((action < 0) || (action > TW_cc_max))
		FINISH;
  }

	/* Check it's not an escape sequence */
  if (terms->specials & MASK(k))	FINISH;

  if (p)	/* Must reset terminal... */
  { 	if (ostinfo(&tst)) FINISH;	/* Be sure interrupt chars are right */
	if (*p == cc)	{ terms->tc[k] = action; FINISH;}	/* No change */
  	*p = cc;	/* Change it ..	*/
  	ostset(&tst);	/* Change terminal ... 	*/
  	ostinfo(&tst);	/* Verify it's done 	*/
	if (*p != cc)	terms->standout |= ChangedControl,
			terms->tc[(*p < 32 ? *p : 32)] = action;
  }
  if (terms->tc[k] != action)	terms->standout |= ChangedControl;
  terms->tc[k] = action;

  FIN:
  k = terms->tc[k];
  EXIT(k);
}

int tv_setsc(option)
/*+++++++
.PURPOSE Define the meaning of Control Characters for TermWindow Usage.
.RETURNS Previous definitions
.REMARKS 
----------*/
	int option;	/* IN: TW_cc_NONE (no control mapping) / TW_cc_VMS / \
			  TW_cc_UNIX / TW_cc_SET */
{
	int	old_option, i, k;
	char	cc;
	
  ENTER("+tv_setsc");

  old_option = (terms->flags & TERM_NOMAP ? TW_cc_NONE : TW_cc_SET);
  k = -1;
  switch(option) {
    case TW_cc_NONE:	
    	terms->flags |= TERM_NOMAP;		break;
    case TW_cc_SET:
    	terms->flags &= ~TERM_NOMAP;		break;
    case -1:		k = TW_cc_default;	break;
    case TW_cc_USER: 	k = 0;			break;
    case TW_cc_VMS: case TW_cc_UNIX: 
    			k = option;		break;
    case TW_cc_SOCKET:
    			k = option-1;		break;
  }

  if (k >= 0) {
	for (i = 32, cc = DEL; i >= 0; cc = --i)  
		tv_setcc(cc, ccdef[k][i]);
  	terms->flags &= ~TERM_NOMAP;
  }

  EXIT(old_option);
}

/*==========================================================================
 *			tv_getcc
 *==========================================================================*/
int tv_getcc(action)
/*+++++++
.PURPOSE Retrieve the Control Character corresponding to a TermWindow Action.
.RETURNS The Control Character corresponding to the action (-1 for None)
.REMARKS If several control characters are mapped to an action,
	the first encountered is returned.
----------*/
	int action;	/* IN: Action TW_cc_xxx as defined in twdef.h */
{
	int	i;
	
  for (i = 0; i < sizeof(terms->tc); i++)
    	if (terms->tc[i] == action)	break;
  
  if (i == sizeof(terms->tc) - 1)	i = DEL;
  else if (i >= sizeof(terms->tc))	i = -1;

  return(i);
}

/*==========================================================================
 *			tv_aload
 *==========================================================================*/
static int tv_aload()
/*+++
.PURPOSE Load attributes and graphic chars in the TERM structure from 
	TERMCAPabilities.
.RETURNS OK
.REMARKS Some termcap files use `me' `mb' `md' `mr' for
	exit mode, blink, bold, reverse respectively.
----------*/
{
	register char *p, *pso;
	register int  i;
	int  k;
	MID_RSTATIC char gcap[]  = "GvGhG0G1G2G3G4GuGdGlGrGg";
	MID_RSTATIC char video[] = "VnmeVBmdVuusVbmbVrmr"; /* Video attr.*/

	/* If TTattr not yet loaded, do it now	
	 * Note that no Stand-Out mode implies Overstrike or ChangeCase */

  if(terms->standout&7)		return(OK);		/* Already done	*/

  k = 1;			/* Last 3 bits of terms->standout	*/

			/* Load the defaults graphic characters		*/
  terms->gchars[_VERTICAL_] 	= '|';
  terms->gchars[_HORIZONTAL_]	= '-';
  for (i = ITEMS(terms->gchars); --i>=2; )
	terms->gchars[i] = '+';
  terms->gchars[_RUBBISH_] = '~';

  pso = SearchCap("so");		/* Default stand-out mode	*/

  p = SearchCap("Vg");				/* Graphic	*/
				  	/* Some Termcap files use "as"	*/
  if (!p)		p = SearchCap("as");
  TTattr[Vg] = p;

					/* Load now the graphic chars	*/
  for (i=0; i < ITEMS(terms->gchars); i++)
  {	p = SearchCap(&gcap[i*2]);
	if (p)	terms->gchars[i] = *(p+4);
  }
  
  TRACE_ED_STR2("Graphic chars: ", terms->gchars, sizeof(terms->gchars));

  p = SearchCap("Va");		/* ASCII	*/
			  	/* Some Termcap files use "ae"	*/
  if (!p)	p = SearchCap("ae");
  TTattr[Va] = p;

	/* Load now Video Attributes	*/

  for (i = 0; i < sizeof(video)/4; i++)
  {	p = SearchCap(&video[i*4]); 			/* V. 	*/
	if (!p) 	p = SearchCap(&video[i*4+2]);	/* m.	*/
	if (!p)		p = pso;
	TTattr[i] = p;
	TRACE_ED_STR2("TTattr is: ", TTattr[i], 12);
  }

  p = TTattr[Vn];					/* Normal	*/
  if ( (!p) || (p == pso) )
	p = SearchCap("se");
  TTattr[Vn] = p;

  if (!p) 			/* No existing attribute	*/
	k = (SearchCap("os") ? 
		2:				/* Use Overstrike	*/
		3);				/* Use Case Change	*/

  terms->standout |= k;
  return(OK);
}

/*==========================================================================
 *			tv_close
 *==========================================================================*/
int tv_close ()
/*+++
.PURPOSE Close the opened terminal
.RETURNS OK
.REMARKS To be called at the end of program using tv_... functions.
---*/
{ 
	register char *p;

  ENTER("tv_close");

  if(!terms->version)		FINISH;		/* Was not opened ! 	*/
  if (terms->term_type)		FINISH;		/* Is NOT a terminal!	*/
  
  tv_where(old_pos);
						/* Reset the scrolling	*/
  tv_attr(terms->attr_init);
  tv_sr0();
  tv_goto(oldl,oldc);

  	/* Go to newline if cursor not positioned at the left margin	*/
  if (curc) 	tv_nl();

	/* Reset Automatic Margins */
	
  if (SearchCap("am"))
  {	p = SearchCap(".M");
	if (p)	SEND(p);
  }

  	/* Clear the lower part of the screen	*/

  if(!(terms->term_type & TERM_hard)) tv_clear(_DOWN_);

				/* Reset the original attributes	*/
  tv_attr(terms->attr_init);

		/* Reset the terminal to init --- use `rs' capability	*/
  SendCap("te");	/* Undo ti */
  SendCap("rs");	/* Reset */
 
#if MID_OS_VMS
  tv_wms(Closing_Wait);		/* Wait a bit ...	*/
#endif

  ostclose();

  FIN:	terms->version = 0;
  EXIT(OK);
}

/*==========================================================================
 *			tv_gterm
 *==========================================================================*/
char *tv_gterm()
/*+++
.PURPOSE Retrieve terminal name
.RETURNS Pointer to terminal name / NULL if not opened
.REMARKS 
---*/
{ 	
  tv_open(NULL_PTR(char), NULL_PTR(char), -1);	/* Default Open */

  return(terms->term_name);
}

/*==========================================================================
 *			tv_kpad
 *==========================================================================*/
static int tv_kpad (k)
/*+++
.PURPOSE Enable or disables the keypad.
.RETURNS OK if possible, NOK if not possible
.REMARKS 
---*/
	int	k;	/* IN: non-zero to enable, zero to disable	*/
{
	register char *p;
	register int s;

  ENTER("tv_kpad");

  s = OK;
  if(k)			/* Trying to enable ...	*/
  {	if (KEYPAD) 	FINISH;
	p = SearchCap("ks");
	if (p) 		terms->flags |= TERM_KPAD;
  } 
  else			/* Trying to disable .. */
  {	if (!KEYPAD) 	FINISH;
	p = SearchCap("ke");
	terms->flags &= (~TERM_KPAD);
  }
  if (!p) 		s = NOK;
  else			SEND(p);

  FIN:
  EXIT(s);
}	

/*==========================================================================
 *			tv_init
 *==========================================================================*/
int tv_reset()
/*+++
.PURPOSE Reset the Terminal: Get new number of lines/cols.
.RETURNS OK / NOK
.REMARKS 
---*/
{
	short cur_pos[2];
	
  cur_pos[0] = terms->pos[0];	cur_pos[1] = terms->pos[1];

  if (ostinfo(&tst))	return(NOK);	/* Get Characteristics 	*/
  if (tst.lines <= 0)	return(NOK);	/* Bad lines count	*/

  if (tst.columns > 0) 
  	terms->dim[1] = terms->dim_init[1] = tst.columns;	

  if ((tst.lines > 0) && (tst.lines != terms->dim_init[0])) {
  	terms->dim[0] = terms->dim_init[0] = tst.lines;
  	tv_sr0();	/* Reset scrolling region */
  }
  tv_goto (cur_pos[0], cur_pos[1]);
  return(OK);
}

static int resize ()
/*+++
.PURPOSE Function called when window resized.
.RETURNS OK / NOK
----*/
{
  ostwinch((void *)(resize));	/* Reset function for next Window Resize	*/
  return(tv_reset());
}
  

static int tv_init (opt)
/*+++
.PURPOSE Initializes the terminal (number of lines, col, etc)
.RETURNS OK / NOK
.REMARKS List of capabilities is assumed to be loaded in the Minimal Version.
	If the terminal has automatic margin (am), number of
	columns is decremented.
---*/
	int opt;	/* IN: Option for control characters */
{ 	
	register char *p;
	register int   i;
	MID_RSTATIC  char  motions[] = "updobcndnlcm" ;	/* Cursor seq.	*/
	MID_RSTATIC  char  swidth[]  = "lico" ;		/* Screen dim.	*/
	MID_RSTATIC	 char  no_cap[]	 = "";

  ENTER("tv_init");

  terms->baud		= MAX(tst.baud_rate, 20000);
  terms->attr_init 	= 0;
  terms->attr 		= 0;
  terms->dim_init[0] 	= tst.lines;	/* Default number of lines	*/
  terms->dim_init[1] 	= tst.columns;	/* Default number of columns	*/
  terms->pos[0] 	= tst.lines-1;	/* Default position 	*/
  terms->pos[1] 	= 0;		/* Default position 	*/
  terms->scregion[0] 	= 0;	/* Default top scrolling region	*/
  terms->scregion[1] 	= 0;	/* Default top scrolling region	*/
  terms->flags 	    	= 0;	/* No flag			*/
  terms->pad_char 	= 0;	/* Default padding character	*/
  terms->standout 	= 0;	/* Unloaded attribute sequences */
  terms->ir   		= 0;	/* Read buffer			*/
  terms->irtop		= 0;	/* Read buffer			*/
  terms->iw   		= 0;	/* Write buffer			*/
  terms->last_char	= 0;
  terms->last_type	= 0;

	/* Number of lines / columns	*/
  for (i = 0; i < 2; i++)
  {	if (terms->dim_init[i])	continue;
  	p = SearchCap(&swidth[i*2]);
  	if (p)	terms->dim_init[i] = atoi(p+4);
  }

	/* Padding char, if known		*/
  p = SearchCap("pc");
  if (p)		terms->pad_char = *(p+4);

	/* Set the control characters	*/
  if (terms->term_type == 0) {
	if (ostopen() < 0)	
		opt = TW_cc_SOCKET, ERROR(osmsg());
	tv_setsc(opt);
  }

	/* Determine basic motions sequences	*/

  for (i = 0; i < ITEMS(terms->tt_ptr); i++)
  {
  	p = SearchCap(&motions[i*2]);
  	if_not(p)	p = no_cap;
  	terms->tt_ptr[i] = p;
  }

	/* Check the NewLine: can't be only \n	*/

  p = terms->tt_ptr[_NEWLINE_];
  if ( (*p == '\01') && (*(p+4) == '\n'))
  	terms->tt_ptr[_NEWLINE_] = "\02nl=\r\n";

  if (*terms->tt_ptr[_LEFT_] == 0)	/* No left motion, try bs */
  	if_not (SearchCap("bs"))
		terms->tt_ptr[_LEFT_] = SearchCap("_H");


#if (TW_LEVEL == 0)		/* Test all cursor movements exist	*/
  for (i = 0; i < ITEMS(terms->tt_ptr); i++)
  {	if(*(terms->tt_ptr[i]) == 0)	
	{	ERR_ED_STR2("Missing Capability: ", terms->tt_ptr[i]+1, 2);
		EXIT(NOK);
	}
  }
#else			/* If no up capability, set terminal to `hard'	*/
  if(*(terms->tt_ptr[_UP_]) == 0)	
	terms->flags |= TERM_hard;
#endif

		/* Clear Capability		*/
  p = SearchCap("cl");
  if (!p)		p = SearchCap("ff");
  terms->tt_clear = p;

	/* Get Graphic Chars and Change Attribute sequences	*/
  tv_aload();

	/* Send init sequence if necessary	*/
  p = SearchCap("is");
  if (p) 		SEND(p);
  else		/* Some TERMCAP files have i1, i2, i3 cap's	*/
  {	SendCap("i1");
	SendCap("i2");
	SendCap("i3");
  }
  SendCap("ti");	/* Special Initialisation */

	/* Look for automatic margins. If possible to disable, do it.	*/

  if (SearchCap("am"))
  {	p = SearchCap(".m");
	if (p)	SEND(p), (terms->flags) |= TERM_am;
	else	(terms->dim_init[1])--;
  }

  terms->dim[0] = terms->dim_init[0];
  terms->dim[1] = terms->dim_init[1];

	/* Set the default attributes	and char set	*/
  SEND(terms->tt_attr[Vn]);
  SEND(terms->tt_attr[Va]);

	/* Enable keypad mode		*/
  tv_kpad(1);
  
	/* Set the scrolling region to whole screen ... (superseedes init) */
  tv_sr0();

  EXIT(OK);
}

/*==========================================================================
 *			tv_open
 *==========================================================================*/
int tv_open (device, term_file, option)
/*+++++
.PURPOSE Open a specified device, reading specifications in the term_file
	(list of TERMCAPabilities)
	terminal descriptions.
.RETURNS OK / NOK
.REMARKS In Minimal Implementation, Binary file MUST exist,
	and No batch possibilities.
	The option allows a choice of control characters, as TW_cc_VMS,
	TW_cc_UNIX or TW_cc_USER. The control characters to use
	may be set BEFORE calling tv_open.
\begin{TeX}

The actions of the Control Characters are defined as follows:
$$\begin{tabular}{|l|ccc|l|}
\hline
Mnemonic & User & VMS & UNIX & Action \\  
\hline
{\tt TW\_cc\_INT}	& \^{}C & \^{}C & {\em Del.} &	Interrupt \\
{\tt TW\_cc\_QUIT}	& \^{}Y & \^{}Y & \^{}\b  & Quit (Stop)	\\
{\tt TW\_cc\_RET}	& \^{}M & \^{}M & \^{}M & Return Key	\\
{\tt TW\_cc\_EOF}	& \^{}Z & \^{}Z & \^{}D & End of File character \\
{\tt TW\_cc\_RAW}	& \^{}\b & \^{}\b & \^{}V & No interpretation of next 
							character \\
{\tt TW\_cc\_MODE}	& \^{}A & \^{}A & \^{}A & Switch Insert / Replace Modes \\
{\tt TW\_cc\_REFRESH}	& \^{}F & \^{}R & \^{}R & Refresh Screen	\\
{\tt TW\_cc\_HELP}	& \^{}H & \^{}G & \^{}K & Help Window	\\
{\tt TW\_cc\_UP}	& \^{}U & \^{}B & \^{}P & Move Up \\
{\tt TW\_cc\_DOWN}	& \^{}D & \^{}N & \^{}N & Move Down \\
{\tt TW\_cc\_LEFT}	& \^{}L & \^{}D & \^{}B & Move Left (Backwards) \\
{\tt TW\_cc\_RIGHT}	& \^{}R & \^{}F & \^{}F & Move Right (Forwards) \\
{\tt TW\_cc\_HOME}	& \^{}B & \^{}H & \^{}L & Move to Left Margin \\
{\tt TW\_cc\_EOL}	& \^{}E & \^{}E & \^{}E & Move to End of Line \\
{\tt TW\_cc\_NW}	& \^{}I & \^{}I & \^{}I & Move to Next Word \\
{\tt TW\_cc\_DELC}	& {\em Del.} & {\em Del.}& \^{}H & Delete 
						character at left of cursor \\
{\tt TW\_cc\_DELW}	& \^{}W & \^{}W & \^{}W & Delete Word at Left of
						cursor\\
{\tt TW\_cc\_DELE}	& \^{}J & \^{}J & \^{}X & Delete to End of Line \\
{\tt TW\_cc\_DELL}	& \^{}X & \^{}X & \^{}U & Delete Complete Line \\
\hline
\end{tabular}$$
\end{TeX}
-----*/
	char *device;	/* IN: The type of device; NULL gets from environment */
	char *term_file;/* IN: The Binary file; from device by default	*/
	int option;	/* IN: Option for control characters	*/
{
	register char *p, *p1, *p2;
  	static int  bfile;
#if (TW_LEVEL > 0)		/* Full Implementation	*/
  	TERM *term2;
	long int t;
	int	status;
	MID_STATIC char binput = 0;
	MID_STATIC char *termcapfile = NULL;
#endif

  ENTER("tv_open");

  if (sterm.version == TW_VERSION)	EXIT(OK);	/* Already done */

#if (TW_LEVEL > 0)		/* Full Implementation	*/

		/* Define first the TermCap filename, using defaults,
		 * and retrieve date of creation in case of modifs	*/

  termcapfile = (term_file ? term_file : TERMCAPFILE);
  if (!termcapfile)	termcapfile = oshenv("TERMCAP",NULL_PTR(char));
  if (!termcapfile)	
#if MID_OS_MSDOS
	termcapfile = "\\termcap.dat";
#else
  	{ ERROR("TERMCAPFILE not defined"); goto BAD; }
#endif
  t = osfdate(termcapfile);
#endif
		/* Get first Terminal Status.
		 * Note that, if no terminal is associated with the process
		 * (eg batch run, indicated by a bad return code
		 * in ostinfo), stdin/stdout are assumed	*/

  if (ostinfo(&tst) < 0)	/* No terminal... */
  	terms->term_type	= 1,	/* Indicates NOT a terminal  */
  	ERROR(osmsg());
  else	terms->term_type	= 0;	/* Indicates a true terminal */
  terms->version   = TW_VERSION;	/* Corrected 26-Feb-1991 */
  terms->term_name = tst.termname;

  p = (device ? device : terms->term_name);	/* Terminal name	*/
  /* if (!(*p))	p = terms->term_name; */
     if (!p)	p = DEFAULT_TERMINAL;
  /* if (!(*p))	p = DEFAULT_TERMINAL; */

  TRACE_ED_STRING("Terminal id: ", p);

		/* Copy terminal name and termcapfile to a new buffer	*/

  if (!DIR_TTY)		DIR_TTY = oshenv("MID_WORK", (char *)0);
  if (!DIR_TTY)		DIR_TTY = oshenv("HOME", (char *)0); 

  terms->term_name = MEM_GET(char, 2*strlen(p) + strlen(DIR_TTY) + 
	1 + strlen(termcapfile) +
	sizeof(DIR_TTY_TRAILER) + sizeof(EXT_TTY));
  for (p2 = terms->term_name, p1 = p; *p1; p1++, p2++)	
#if MID_OS_VMS		/* Convert to lower case for VAX...	*/
  	*p2 = tolower(*p1);
#else
	*p2 = *p1;
#endif
  *p2 = EOS;
  terms->save_file = ++p2;

				/* Transform device name to file name	*/
  for (p1 = DIR_TTY;         *p1; p1++, p2++)	*p2 = *p1;
  for (p1 = DIR_TTY_TRAILER; *p1; p1++, p2++)	*p2 = *p1;
  for (p1 = p; *p1; p1++)
#if MID_OS_VMS		/* VMS doesn't like hyphens in filenames...	*/
	if (isalnum(*p1))	
#endif
			*(p2++) = *p1;

  for (p1 = EXT_TTY;         *p1; p1++, p2++)	*p2 = *p1;
  *p2 = EOS;
  p1 = termcapfile, termcapfile = ++p2;
  for (; *p1; p1++, p2++)	*p2 = *p1;
  *p2 = EOS;

#if (TW_LEVEL > 0)	/* ------------- F U L L  Implementation -----	*/
			/* Look if a binary file with loaded capabilities 
			 * already exists; but don't use it if its date
			 * is earlier than revision date of TermCap file! 
			 * *** 	Note that, if TermCap file doesn't exist,
			 *	the check will always be OK.		*/

  bfile = 0;
  if (osfdate(terms->save_file) > t)
	bfile = osdopen(terms->save_file, READ);
  binput = (bfile > 0);

  if (binput)
  {	term2 = MEM_GET(TERM,1);
	*term2 = *terms;				/* Save 	*/
	status = osdread(bfile, (char *)terms, sizeof(TERM));
	TRACE_ED_STR2("Read TERM: ", (char *)terms, sizeof(TERM));
	if ( (status != sizeof(TERM)) || (terms->version != TW_VERSION) )
  	{
		binput = 0;
		*terms = *term2;
		MEM_FREE(term2);
	}
  }

  if (binput)
  {	terms->save_file 	= term2->save_file;
	terms->term_name 	= term2->term_name;
	MEM_FREE(term2);
	terms->caplist		= MEM_GET(char,terms->size);
	status = osdread(bfile, terms->caplist, terms->size);
	TRACE_ED_STR2("Read capl: ", terms->caplist, status);
	if (status != terms->size)	binput = 0;
  }
  if (bfile>0)		osdclose(bfile);

			/* If no binary input, acquire the terminal
			 * capabilities	directly from TermCapFile	*/
  if (!binput)
  {	eh_class(_WARNING_);	/* Next message is a Warning */
  	ERR_ED_STRING("Obsolete version (recreated): ", terms->save_file);
  	ERR_CLEAR();
    	bfile = osdopen(terms->save_file, WRITE);
 	status = tu_load(termcapfile, terms->term_name, terms);
#ifdef 	local_termcapfile
 	if (status != OK)	
 		status = tu_load(local_termcapfile, terms->term_name, terms);
#endif
 	if (status != OK) {
  	if (GetLog() > 2) /* There is a Log File. Issue also the message to 
  		the terminal, to let the user be aware of the problem... */
	
	  	{  	p = ERR_LOC();			/* Retrieve Error ... */
	  		ostwrite("**** ", 5);		/* and display	*/
	  		ostwrite(p, strlen(p));		/* and display	*/
	  		ostwrite("\r\n", 2);
	  		ospwait(4);	/* Just wait, to give some time to read
		  		   the so important above message ...	*/
	  	}
 		goto BAD;
 	}

			/* Copy structure to the binary file	*/
	terms->size = terms->captop;	/* Limit the size */
	TRACE_ED_STR2("Writ TERM: ", (char *)terms, sizeof(TERM));
  	osdwrite(bfile, (char *)terms, sizeof(TERM));
	TRACE_ED_STR2("Writ capl: ", terms->caplist, terms->size);
	osdwrite(bfile, terms->caplist, terms->size);
	osdclose(bfile);
  }
#else			/* ---------- Minimal Implementation --------	*/
			/* Load the binary file with loaded capabilities */

  p2 = (term_file ? term_file : terms->save_file);
  bfile = osdopen(p2, READ);
  if (bfile <= 0)
  {	ERR_ED_STRING("Can't find ", p2);
	goto BAD;
  }
  p1 = terms->term_name;				/* Save 	*/
  status = osdread(bfile, terms, sizeof(TERM));
  if ( (status != sizeof(TERM)) || (terms->version != TW_VERSION) )
  {	ERR_ED_STRING("Bad Version of ", p2);
	goto BAD;
  }
  terms->term_name = p1;		/* Reset addresses	*/
  terms->save_file = p1 + strlen(p1) + 1;

	/* Read the CapList	*/
  if (!(terms->caplist = MEM_GET(char,terms->size)))	goto BAD;
  if (osdread(bfile, terms->caplist, terms->size) != terms->size)
  {	ERR_ED_STRING("Bad ", p2);
	goto BAD;
  }
  osdclose(bfile);
#endif

  terms->buf_size = TVBUF;
  if (!(terms->bufr = MEM_GET(char, 3*TVBUF)))	goto BAD;
  terms->bufw = terms->bufr + terms->buf_size;
  terms->bufs = terms->bufw + terms->buf_size;

			/* Initialize  the terminal		*/

  if (tv_init(option) != OK)		goto BAD;
  ostwinch((void *)resize);	/* Set function for Window Resize	*/

  status = OK;
  FINISH;

  BAD:		
  status = NOK, ostclose();

  FIN:
  EXIT(status);
}

/*==========================================================================
 *			tv_stop
 *==========================================================================*/
int tv_stop (stop)
/*+++
.PURPOSE Stop (stop!=0) or Start (stop==0) the terminal
.RETURNS The previous state, i.e. 0 if terminal was active, not-zero
	if terminal was already stopped.
	(always 0 in Minimal Implementation)
.REMARKS What is in the buffer is NOT sent to the terminal (immediate stop)
---*/
	int	stop;		/* IN: 0 to start, 1 to stop	*/

{ 	
#if (TW_LEVEL == 0)
	return(0);
#else
	register int returned;

  ENTER("tv_stop");

  returned = (terms->flags) & (TERM_NOWRITE);

  if(stop)	terms->flags |=   TERM_NOWRITE;
  else		terms->flags &= ~(TERM_NOWRITE);
  
  EXIT(returned);
#endif
}

/*==========================================================================
 *			tv_wait
 *==========================================================================*/
int tv_wait (ms)
/*+++
.PURPOSE Set a waiting time (ms) after each output to screen.
.RETURNS The previous waiting time (always 0 in Minimal Implementation)
.REMARKS Useful to have an idea about the speed of a program
	as seen from a remote terminal...
	The waiting time is limited to 30000 (0.5 mn).
---*/
	int	ms;		/* IN: milliseconds to wait	*/
{
#if (TW_LEVEL == 0)
	return(0);
#else
	register int old_ms;

  ENTER("+tv_wait");

  if (terms)
	old_ms 	= terms->msw, 
	terms->msw = (ms < 30000 ? ms : 30000);
  else  old_ms = 0;

  EXIT(old_ms);
#endif
}

/*==========================================================================
 *			tv_dim
 *==========================================================================*/
int tv_dim(coo)
/*+++++++++++
.PURPOSE Returns the current screen dimensions
.RETURNS OK
.REMARKS The position is adjusted within the screen limits.
	Not traced.
------------*/
	short int coo[2]; /* OUT: screen dimensions: line (0=top), col (0=left) */
{
  tv_open(NULL_PTR(char), NULL_PTR(char), -1);	/* Default Open */

  coo[0] = terms->dim[0];
  coo[1] = terms->dim[1];
  return(OK);
}
 
#if 0
/*==========================================================================
 *			tv_width
 *==========================================================================*/
int tv_width (width)
/*+++
.PURPOSE Set the width (number of columns) to a specified value
.RETURNS OK (was done, new dimension loaded), NOK (not possible, always
	in Minimal Implementation)
.REMARKS Only enlarge or reduce width. Enlarge is `.S' with width `.W#',
	reduce is `.s' with width `.w#'
	* Note that, in case of output to a file, the width may always
	  be changed!
---*/
	int	width;		/* IN: New column width	*/
{ 
#if (TW_LEVEL < 2)
	return(NOK);
#else
	register char *p, *pw;

  ENTER("tv_width");

  TRACE_ED_I("Present Width: ",terms->dim[1]);
  TRACE_ED_I("Asked   Width: ",width);

  if ( (width == terms->dim[1]) || (terms->term_type) )
  {	terms->dim[1] = width;
	EXIT(OK);
  }

  status = OK;
  if (width > terms->dim[1])	p = ".S", pw = ".W";	/* Enlarge */
  else 				p = ".s", pw = ".w";	/* Reduce  */

  p = SearchCap(p), pw = SearchCap(pw);
  if ((p) && (pw)) 	SEND(p), terms->dim[1] = atoi(pw+4);
  else			status = NOK;
  
  EXIT(status);
#endif
}
#endif

/*==========================================================================
 *			Internal Routines
 *==========================================================================*/

#if DEBUG
/*==========================================================================
 *			tv_ed
 *==========================================================================*/
int tv_ed ()
/*+++
.PURPOSE Edit the terminal characteristics on the log file.
.RETURNS OK 
.REMARKS Only used for debugging.
---*/
{
	register char *p, *pw;
  	MID_STATIC char msg[80] = "";

  sprintf(msg,"....Device %s[%d] of dim. [%d,%d] -- Cursor at [%d,%d]",
	terms->term_name, terms->term_type, 
	terms->dim[0], terms->dim[1],
	terms->pos[0], terms->pos[1]);
  LOG(msg);
  sprintf(msg,".... attr=%X, flags=%X",
	terms->attr, terms->flags);
  LOG(msg);
  return(OK);
}
#endif
