/* @(#)tvput.c	19.1 (ES0-DMD) 02/25/03 13:58:15 */
/*===========================================================================
  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 tvput.c
.AUTHOR    	Francois Ochsenbein [ESO-IPG]
.LANGUAGE  	C
.KEYWORDS	Terminal Independant i/o Package.
.ENVIRONMENT 	TermWindows
.COMMENTS	This module groups output and graphic char treatement.
\begin{TeX}
Used Capabilities:
$$\begin{tabular}{|lp{37em}|}	\hline
{\tt se=}	& Exit Stand-out mode				\\
{\tt so=}	& Start Stand-out mode				\\
{\tt us=}	& Start Underline mode				\\
{\tt Va=}	& Set keyboard to ASCII				\\
{\tt Vg=}	& Set keyboard to graphic mode 			\\
{\tt Vn=}	& Set normal Video Attributes			\\
{\tt VB=}	& Set Bold Video Attributes			\\
{\tt Vu=}	& Set Underline Video Attributes		\\
{\tt Vb=}	& Set blink Video Attributes			\\
{\tt Vr=}	& Set reverse Video Attributes			\\
\hline
\end{tabular}$$
\end{TeX}
.VERSION 1.0	07-Jul-1986:	Creation
.VERSION 1.1	12-Aug-1986:	Added tv_wa, writing a string with attributes.

.VERSION 1.2	13-Oct-1986	Some terminals (e.g. HP) need to resend the
			attribute on each new line.
			This feature incorporated with ChangedLine flag
			in standout part of TERM structure.
.VERSION 2.0	03-Dec-1986	New terminal-independant with output buffering version.
.VERSION 2.1	11-Jun-1987	Version '2' of TermWindows.
			Defaut input char is Control_Z.
			Interfaces to xv routines in a seperate module.
			Batch mode allowed. 
.VERSION 2.2	23-Jun-1987	Implementation for UNIX and Minimal Version.
.VERSION 3.0	20-Mar-1988	Version '3' of TermWindows.
----------------------------------------------------------------------------*/

#define DEBUG		0		/* For debugging	*/

#define PM_LEVEL 	LEVEL_TV

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

MID_EXTERN  TERM	*terms ;		
static  char	special;
MID_STATIC  char	gtr = 1;

#define		SEND(p)		tv_send(p,1)

#if 	DEBUG
#	define	TERMTEST	if(!terms->version) tv_gopen();\
				tv_ed(terms)
#else
#	define	TERMTEST	if(!terms->version) tv_gopen()
#endif

#define	ENTER_TV(x)	int 	state_buffer;			\
			ENTER(x);	TERMTEST;		\
			state_buffer  =	tv_buffer(1);
#define	EXIT_TV(f)	FIN:					\
			tv_buffer(state_buffer);		\
			EXIT(f)

		/* Macros just to improve readibility	*/

#define		curl			(terms->pos[0])
#define		curc			(terms->pos[1])
#define		diml			(terms->dim[0])
#define		dimc			(terms->dim[1])
#define		BUFS			(terms->bufs)
#define		Attr			(terms->attr)
#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
#define		IssueCorner(x)		{ special = CornerChar(x);	\
					  tv_wg(&special, 1);	}

/*	MONITOR (ZTPUT); */	/* CG. status defined locally */

/*==========================================================================
 *			isgr0
 *==========================================================================*/
static int isgr0(c)
/*+++
.PURPOSE Test non-graphic mode
.RETURNS 0 (FALSE) / 1 (TRUE)
.REMARKS Not traced
--------*/
	char c;	/* IN: char to test	*/
{
  return((c == '+') || (c == '-') || (c == '|') || (c == '~'));
}  

/*===========================================================================*/
static int strupper ( str )
/*+++++++++
.PURPOSE  Converts (in place) a string to upper case
.RETURNS  Length of string
--------*/
     char *str;	/* MOD: starting address        */
{
	register char *p;

  for (p = str; *p; p++)
	*p = toupper(*p); 

  return(p-str);
}

/*==========================================================================
 *			tv_attr
 *==========================================================================*/
int	tv_attr(val)
/*+++++++++
.PURPOSE Set attributes for the next output to screen 
	(using tv_write, tv_put or tv_puts)
.RETURNS OK / NOK
.REMARKS Attributes are defined as numbers to be OR'ed in TERMINAL.H
	The cursor position remains unchanged.
	To revert to the normal attributes, use tv_attr with
	a value of 0.
----------------*/
	int 	val;		/* IN: Desired screen attributes	*/
{
	char 	*p;
	int	new, st, k;

  ENTER_TV("tv_attr");

  TRACE_ED_I("Asked attributes: ", val);

  st = OK;
  new = val &(_BLINK_|_UNDERSCORE_|_BOLD_|_REVERSE_|_GRAPHICS_);
  new ^= terms->attr_init;	/* Useful only if attr_init is not null	*/

  if (terms->standout & ChangedLine)	/* Current Attributes assumed to
  					 * have changed. Reissue. */
  {	terms->standout &= ~ChangedLine;
  	tv_attr((int)terms->attr_init | (new & _GRAPHICS_));
  }

  if(Attr == new) FINISH;	/* No change	*/
  
  k = terms->standout & 7;	/* k > 1 means `no or special standout'	*/
  if ((new & _GRAPHICS_) != (Attr & _GRAPHICS_))
  {	if (new & _GRAPHICS_) 	p = TTattr[Vg];
		else		p = TTattr[Va];
	if (p) 	if ( (st = SEND(p)) != OK)	FINISH;
  }

	/* If only graphics changed, exit now	*/
  if ((new & (~_GRAPHICS_)) == (Attr & (~_GRAPHICS_)))
  {	Attr = new;
	FINISH;
  }

	/* Set first terminal to No Attributes ...	*/
  if (Attr &(~_GRAPHICS_))
  {	p = TTattr[Vn];
	if (p) 	if_not(st = SEND(p))	FINISH;
  }
  Attr = new;
  
	/* ... if no other attribute is asked for, exit	*/
  if ((Attr & (~_GRAPHICS_)) == 0) 		FINISH;

	/* ... and send successively the asked attributes
	 * However, if no attributes exist at all, not necessary 
	 * to search for non-existent capabilities....		*/

  if(k > 1)					FINISH;

	/* A loop is used for attributes	*/

  for (k=1, new = 1; k <= _REVERSE_; k++, new <<= 1)
  {	if (Attr & new)	
		if_not(st = SEND(TTattr[k]))	FINISH;
  }

  EXIT_TV(st);
}
 
/*==========================================================================
 *			tv_bell
 *==========================================================================*/
int	tv_bell()
/*
.PURPOSE Ring the terminal bell.
.RETURNS OK 
.REMARKS Use `bl' capability --- or ASCII char `07' if such a capability
	is not present.
-----------------------------------------------------------------------*/
{
	register char *p;
	int	status;
	MID_RSTATIC char ascii_bell = '\07';

  ENTER_TV("tv_bell");

  status = OK;
  p = SearchCap("bl");
  if (!p)	p = SearchCap("vb");
  if (!p)	status = tv_out(&ascii_bell,1);
  else		status = SEND(p);
  FINISH;

  EXIT_TV(status);
}

/*======================================================================
 *			tv_box
 *======================================================================*/
int tv_box(topl,topc,boxl,boxc)
/*+++
.PURPOSE Draw a box, and position the cursor at top left corner.
.RETURNS OK / NOK (impossible)
.REMARKS Value of 0 for box dimensions assume all usable space.
	The cursor does not move if the box cannot be drawn.
	Negative values for box dimensions mean right / down adjustments.
---*/
	int topl;	/* IN: Line   of upper-left corner (<0 if counted\
					from bottom)	*/
	int topc;	/* IN: Column of upper-left corner (<0 if counted from
				right)	*/
	int boxl;	/* IN: Line   width of box (0 = whole space)	*/
	int boxc;	/* IN: Column width of box (0 = whole space)	*/
{
	int	status;
	int 	lh, lv, old_attr;
	short int b0[2], bd[2];	/* Final position & dimension of box */

  ENTER_TV("tv_box");

#if DEBUG
	TRACE_ED_I("Top line=",topl);
	TRACE_ED_I("Top col.=",topc);
	TRACE_ED_I("Ext line=",boxl);
	TRACE_ED_I("Ext col.=",boxc);
#endif

  b0[0] = topl;		b0[1] = topc;
  bd[0] = boxl;		bd[1] = boxc;

		/* Adjust box dimensions	*/

  status = tv_rb(b0, bd, terms->dim);
  if (status != OK)		FINISH;

#if DEBUG
	TRACE_ED_I("Kept Top line=",topl);
	TRACE_ED_I("Kept Top col.=",topc);
	TRACE_ED_I("Kept Ext line=",boxl);
	TRACE_ED_I("Kept Ext col.=",boxc);
#endif

  old_attr = Attr;
  tv_attr(old_attr | _GRAPHICS_);	/* All in Graphics Mode	*/

  lh = bd[1] - 2;	lv = bd[0] -2 ;

	/* First, write the upperleft corner	*/
  tv_goto(b0[0],b0[1]);
  IssueCorner(_UPLEFT_);

	/* Write now first horizontal line	*/
  tv_rule(_RIGHT_,lh);

	/* Write upperight corner		*/
  IssueCorner(_UPRIGHT_);

	/* Vertical lines			*/
  tv_goto(b0[0]+1, b0[1] + bd[1] -1);
  tv_rule(_DOWN_,lv);
  IssueCorner(_DOWNRIGHT_);

  tv_goto(b0[0]+1, b0[1]);
  tv_rule(_DOWN_,lv);
  IssueCorner(_DOWNLEFT_);

	/* Bottom horizontal line	*/
  tv_rule(_RIGHT_,lh);

	/* Reset attributes, and go to top left corner	*/
  tv_attr(old_attr);
  tv_goto(b0[0]+1, b0[1]+1);

  EXIT_TV(status);
}

/*======================================================================
 *			tv_graph
 *======================================================================*/
int	tv_graph(k)
/*+++
.PURPOSE Switch Translate or Not the Graphic Characters.
.RETURNS Previous state 0 (False) / 1 (True)
.REMARKS 
---*/
	int	k;	/* IN: Status FALSE / TRUE	*/
{
	register int s;

  s = gtr;
  gtr = k;

  return(s);
}

/*======================================================================
 *			tv_gtr
 *======================================================================*/
static int tv_gtr(str, len)
/*+++
.PURPOSE Translate the terminal-independant graphic chars into
	terminal-dependant graphic characters
.RETURNS OK / NOK (there is at leat 1 non-graphic char)
.REMARKS *** For Internal Use ***
---*/
	char *str;	/* MOD: String to translate	*/
	int  len;	/* IN: Length of str		*/
{
	int	status;
	char 	*p;
	int 	i, k;

  ENTER("tv_gtr");

  status = OK;

  if (gtr)
  {	for (p= str, i=len; --i>=0; p++)
	{	k = *p - 'a';
		if ( (k < 0) || ( k >= ITEMS(terms->gchars)) )	k = _RUBBISH_;
		*p = terms->gchars[k];
		if (isgr0(*p))	status = NOK;
	}
  }

  EXIT(status);
}

/*==========================================================================
 *			tv_line
 *==========================================================================*/
int tv_line(str, len)
/*+++++++++++++++++
.PURPOSE Write a string made of normal characters to the terminal, on a
	single line.
.RETURNS OK / NOK
.REMARKS The line is truncated if the right margin would be passed.
	If the cursor was at the end of the line, issue a new line first.
	If the terminal is hardcopy and stand-out mode is on, underline.
----------------*/
	char *str;		/* IN: String to display on the terminal */
	int  len;		/* IN: Length of String			*/
{
	int	status;
	register int l, col;
	register char *p;

  ENTER_TV("tv_line");

#if DEBUG
	tv_ed(terms);
#endif

  status = OK;
  if (len <= 0) FINISH;		/* No output			*/

  if(curc >= dimc) tv_nl();
  col = curc;
  p = str;

	/* Send attributes if necessary	*/
  if (terms->standout & ChangedLine)
  {	l = terms->attr;
	terms->attr = terms->attr_init;
	tv_attr(l);
  }

	/* Truncate length if necessary	*/
  l = dimc - curc;
  l = MIN ( l, len);

  if(Attr)
  {	switch(terms->standout & 7)
	{ case 2:	/* Write first the underline, if stand-out mode
			 * --- but not if only Graphics			*/
		if (Attr & (~_GRAPHICS_))
		{	oscfill(BUFS,l,'_');
			status = tv_out(BUFS,l);
			curc += l;
			tv_goto(curl,col);	/* Go again to required column*/
		}
	  default:
		if (Attr & _GRAPHICS_)	/* Translate graphic chars	*/
		{	p = BUFS;
			oscopy(p, str, l);
			if (tv_gtr(p, l) != OK)		/* Note: if some non- */
			{	while (--l >= 0)	/* graph. chars	      */
				{	if (isgr0(*p))
						tv_attr((int)Attr & ~_GRAPHICS_);
					else	tv_attr((int)Attr | _GRAPHICS_);
					tv_out(p, 1), p++, curc++;
				}
				tv_attr((int)Attr | _GRAPHICS_);
				FINISH;
			}
		}
		status = tv_out(p, l);
		break;

	  case 3:			/* Change Case	- 
					 * but not if Graphics!	*/
		p = BUFS;
		oscopy(p, str, l);
		if (Attr & _GRAPHICS_)	tv_gtr(p, l);
		else			strupper(p);
		status = tv_out(p, l);
		break;
	}
  }
  else		status = tv_out(p, l);
  
  curc += l;

  EXIT_TV(status);
}

/*==========================================================================
 *			tv_puts
 *==========================================================================*/
int	tv_puts(str)
/*+++++++++
.PURPOSE Write an EOS-terminated string to the terminal, followed by a newline.
.RETURNS OK / NOK
.REMARKS Control characters are automatically treated. 
-----------*/
	char *str;	/* IN: EOS-terminated string to send to the terminal */
{
	register int s;		/* Status	*/

  ENTER_TV("tv_puts");

  s = tv_write(str,strlen(str), 1);
  if (s == OK)	s = tv_nl();
  FINISH;

  EXIT_TV(s);
}

/*======================================================================
 *			tv_rule
 *======================================================================*/
int tv_rule(direction, len)
/*+++
.PURPOSE Writes a rule, horizontal or vertical
.RETURNS OK / NOK
.REMARKS Uses the graphic character if it exists.
.REMARKS On return, the cursor is just after (at the left, right, up or
	down depending on the direction) the last issued character
---*/
	int direction;	/* IN: The direction(_UP_, _LEFT_, etc) of the rule */
	int len;	/* IN: Rule length, in characters	*/
{
	int 	i, l, dir, old_attr;
	int	status;

  ENTER_TV("tv_rule");

  status 	= OK;
  old_attr 	= Attr;

  dir = direction; 
  l = len;
  if (len  < 0) 			/* Change direction		*/
  {	dir = 1^direction;
	l   = -len;
  }

  l = MIN(l, dimc-curc);		/* Not too many !		*/
  if (l == 0) 		FINISH;

  i = dir >> 1;				/* Direction horiz./vertical 	*/
  if (i > 1)				/* Bad direction...		*/
  {	status = NOK;
	ERR_ED_I("Bad rule orientation: ", direction);
	FINISH;
  }
  special = RuleChar(i);		/* Get the rule char	*/
  tv_attr(old_attr | _GRAPHICS_);

	/* Now, issue as many rule characters as required		*/

  while(--l>=0)				   /* Write char + BS 		*/
  {	status = tv_line(&special, 1);  /* Note, cursor moves right	*/
	if (status != OK)	break;
	if (dir != _RIGHT_)
	{	tv_mvc(_LEFT_,1);
		tv_mvc(dir,1); 
	}
  }

  tv_attr(old_attr);

  EXIT_TV(status);
}

/*==========================================================================
 *			tv_write
 *==========================================================================*/
int tv_write(str, len, opt)
/*++++++++++++
.PURPOSE Write a string to the terminal, taking into account control chars
	or replacing them with "rubbish" characters.
.RETURNS OK / NOK
.REMARKS If option is non-zero, the \n (newline), \b (backskpace), 
	\r (carriage-return), \f (form-feed)	\t (tab) and
	`Delete' control characters are automatically treated.
	Other control chars (or all if option is zero) are issued as "rubbish".
---------------*/
	char *str;		/* IN: String to display on the terminal */
	int  len;		/* IN: Length of String			*/
	int  opt;		/* IN: 0 to treat control chars, 1 otherwise */
{
	char 	*p;
	int 	i, l, old_attr;
	int	status;
	char	control_char;
	MID_RSTATIC char rubbish_char = RubbishChar();

  ENTER_TV("tv_write");

  status = OK;
  if (len <= 0) FINISH;		/* No output			*/
  TRACE_STR2(str, len);

	/* Scan the string until a control char is found	*/

  for (p = str, l = len, i = 0; l > 0; p += i, l -= i)
  {	if(curc >= dimc) tv_nl();
	i = oscscan(p, l, _CNTRL_, main_ascii);
	i = MIN(i, dimc - curc);	/* Not more than one line */
						/* Send the line */
	if (i)	/* Normal Characters */
	{	if (!(status = tv_line(p, i))) 	FINISH;
		continue;
	}

	/* A control char was found ---- Process these chars		*/
	control_char = *p;
	if (opt == 0)		/* Don't process, i.e. replace by something
				 * leading to `default' in following switch   */
		control_char = EOS;

   	switch(control_char)
	{ case '\n' :	/* Start a new line	*/
		tv_nl();		break; 
	  case '\r' :	/* go to beginning of line	*/
		tv_goto(curl,0); 	break; 
	  case '\f' :	/* Clear screen		*/
		tv_clear(_WHOLE_);	break; 
	  case '\b' :	/* backspace		*/
		tv_mvc(_LEFT_,1); 	break; 
	  case DEL  :	/* delete: bs, then clear cursor	*/
		tv_mvc(_LEFT_,1); 
		tv_cc(1); 		break; 
	  case '\t' :	/* Tabs are assumed to be 7,15,etc	*/
		if(curc >= dimc) tv_nl();
					/* Output the tab	*/
		if ( (status = tv_line(p, 1)) != OK) 	FINISH;
		curc++;
		while (curc&7) curc++;	/* Adjust the cursor position	*/
					break;
	  default   :	/* Any other control, issue RUBBISH	*/
		old_attr = Attr;
		tv_attr(old_attr | _GRAPHICS_);
		status = tv_line(&rubbish_char, 1);
		tv_attr(old_attr);
		if (status != OK) 	FINISH;
					break;
	}
	i = 1;
  }

  EXIT_TV (status);
}
 
/*==========================================================================
 *			tv_wa
 *==========================================================================*/
int tv_wa(str, len)
/*+++++++++++
.PURPOSE Write a string made of (attribute + char) to the terminal.
.RETURNS OK / NOK
.REMARKS All control characters are translated to rubbish.
------------*/
	ACHAR	*str;		/* IN: String to display on the terminal */
	int  	len;		/* IN: Length of String			 */
{
	ACHAR	*p;
	int 	i, l;
	char	*pc;
	ACHAR	a;
	char	wbuf[81];
	int	status;

  ENTER_TV("tv_wa");

  status = OK;
  if (len <= 0) FINISH;		/* No output			*/

  TRACE_STR2((char *)str, sizeof(ACHAR)*len);

  for (p = str, l = len, i = 0; l > 0; l -= i)
  {	a = *p & 0xff00;
	tv_attr((int)(a >> 8));
	for (pc = wbuf, i = MIN(sizeof(wbuf), l); 
		(--i >= 0) && ((*p & 0xff00) == a); 
		p++, pc++) 	*pc = *p & HIGH;
	i = pc - wbuf;		/* String Length */
	if_not(status = tv_write(wbuf,i, 0))	FINISH;
  }

  EXIT_TV(status);
}

/*==========================================================================
 *			tv_wg
 *==========================================================================*/
int tv_wg(str, len)
/*++++++++
.PURPOSE Write graphic characters.
.RETURNS OK / NOK
.REMARKS All bad chars are translated into rubbish
-----------------------------------------------------------------------*/
	char *str;		/* IN: String to display on the terminal */
	int  len;		/* IN: Length of String			*/
{
	int	old_attr;
	int	status;

  ENTER_TV("tv_wg");

  TRACE_ED_STR2("Output graphics: ", str, len);

  old_attr = Attr;
  tv_attr(old_attr | _GRAPHICS_);
  status = tv_write(str, len, 0);
  tv_attr(old_attr);
  FINISH;

  EXIT_TV(status);
}

/*==========================================================================
 *			tv_wsend
 *==========================================================================*/
int tv_wsend(str,len)
/*
.PURPOSE Directly passes a string to OS-dependant routines
.RETURNS OK / NOK
.REMARKS The cursor is assumed not to move ...
-----------------------------------------------------------------------*/
	char *str;	/* IN: String to display on the terminal	*/
	int  len;	/* IN: Length of String			*/
{
	register int s;		/* Status	*/

  ENTER_TV("tv_wsend");

  s = tv_out(str,len);
  FINISH;

  EXIT_TV(s);
}

