/*===========================================================================
  Copyright (C) 1986-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 twget.c
.AUTHOR    	Francois Ochsenbein [ESO-IPG]
.LANGUAGE  	C
.KEYWORDS	Window Management
.ENVIRONMENT	TermWindows
.COMMENTS	This module contains routines to perform command input,
		and to manage the associated command stack.
\begin{TeX}
\\
The commands are stacked in a buffer (named {\tt text} in the
{\tt COMMAND} structure). Each command is preceded and followed
by the text length, in order to facilitate forward and backward
movements in the stack.\\[0.5ex]
{\bf Editing possibilities} when a command is typed at the terminal 
are available, like cursor mouvements, moving in the stack,
refreshing the Screen. 

The default actions of control characters are defined as TW\_cc\_default
in {\tt twparam.h}, with choices UNIX-like, VMS-like or a third
set. The action of each control character may be modified with the
tv\_setcc(control\_character, action) (DefineControl) function.

\end{TeX}

.VERSION  1.0	01-Sep-1986:	Creation
.VERSION  2.0	03-Dec-1986	New terminal-independant graphic characters
			with output buffering version.
.VERSION 3.0	20-May-1988: Version '3'
			Functions tw_get1 and tw_getf added.
.VERSION 3.1	17-Feb-1989: Add ^ option in SetStopping, which specifies
			that the arrow should stop if borders encountered.
			Changed tw_getf to tw_modf.
.VERSION 3.2	31-Mar-1989: Removed bug in mv1...
.VERSION 3.3	05-Apr-1989: Possibility of NULL_WINDOW as input.
.VERSION 3.4	17-May-1989: No redisplay with ModsWithCheck if window 
		not active.
.VERSION 3.5	09-Jun-1989: Use negative string size for Stretchable Window.
		Added tw_ms2. Added tw_gc2.
.VERSION 3.6	19-Jun-1989: tw_mods puts the cursor at the BEGINNING of
		the line to edit.
.VERSION 3.7	24-Jul-1989: Catched INTERRUPT
.VERSION 3.8	24-Aug-1989: Clear line if INTERRUPT
.VERSION 3.9	05-Dec-1990: Modified tw_get1: Return 0 if First Char found.
				Added tw_gc1 (use a stopping list)

 090901         last modif
----------------------------------------------------------------------------*/

#define		DEBUG		0	/* For debugging only		*/

#define PM_LEVEL 	LEVEL_TW

#include <midas_def.h>


#define  TW_import	0	/* Do not use macro window definitions	*/
#include <twset.h>


extern int tv_wa(), tv_agoto(), tv_getc();
extern int tv_bell(), tv_setsc(), tv_push();

extern int tw_write(), tw_uc(), tw_box(), tw_r(), tw_agoto(), tw_st();
extern int tw_wa(), tw_clear(), tw_tra(), tw_dc(), tw_mods();
extern int tw_helps(), tw_amark(), tw_occluded(), tw_nl(), tw_line();

extern int pm_enter(), pm_iexit(), pm_ed_tr2(), pm_trace();

extern int eh_ed_str2();


MID_EXTERN	WINDOW *Screen;
MID_EXTERN  TERM   *terms;
typedef void (*FUNCTION)();
extern FUNCTION ostint();


#if 	DEBUG
#define TW_cc_level	3
#define ENTER_DEBUG(x)	ENTER(x)
#define EXIT_DEBUG(x)	EXIT(x)
#define DISPLAY(opt)	zed(opt)
#else
#define DISPLAY(opt)
#define ENTER_DEBUG(x)	
#define EXIT_DEBUG(x)	return(x)
#endif

MID_STATIC WINDOW	*W = NULL_PTR(WINDOW);
MID_STATIC COMMAND	*c = NULL_PTR(COMMAND);
static short int command_number;
MID_STATIC unsigned int	f = 0;		/* Flags to communicate	*/
MID_STATIC char 		*stop_list = (char *)0;
MID_STATIC int		stop_len = 0;
MID_STATIC int		(*f_ed)() = NULL_FCT(int);
					/* Control function for modf */
static int 	old_pos;
static ACHAR	*saved_screen = NULL_PTR(ACHAR);
static int	saved_screen_size = 0;
static int	saved_screen_len  = 0;

static char	*input_start, *input_end, *input_valid;	/* Input string */

#define Stop		0x0100
#define Special		0x0200
#define Stop1		0x0400
#define NoMore  	0x1000
#define Raw		0x2000
#define Occluded 	0x4000
#define	Interrupted 	0x8000

#define BYTE_MAX	0xff
#define ULONG_MAX	0xffffffff

	MONITOR(TWGET);


/*==========================================================================*/
static int InInt(s)
/*+++
.PURPOSE Catch Interrupt
.RETURNS 0
.REMARKS 
---*/
	int	s;	/* IN: Signal */
{
  f |= (Stop | Interrupted);
  return(0);
}
  
/*==========================================================================*/
static int OpenStretchableWindow(len)
/*+++
.PURPOSE Special Window, saving what's presently on the Screen.
.RETURNS OK / NOK (It's a _DISPLAY_ window)
.REMARKS 
---*/
	int 	len;	/* IN: Length of Stretchable Window */
{

  if (saved_screen_size < len)	
  	saved_screen_size = len,
  	saved_screen = MEM_EXP(ACHAR,(char *) saved_screen, saved_screen_size);
  
  saved_screen_len = len;
  tw_tra(W, saved_screen, saved_screen_len);
  
  return(saved_screen_len);
}
  
/*==========================================================================
 *			check
 *==========================================================================*/
static int check(w)
/*+++
.PURPOSE Check if window is Display Only
.RETURNS OK / NOK
--------*/
	WINDOW *w;	/* IN: The concerned Window	*/
{
	int	status;
	
  status = OK;

  W = (w ? w : Screen);

  if_not(c = Command(W))	status = NOK, 
	ERR_ED_STR2("Window is Display-Only: ", W->id, 8);

  return(status);
}

/*==========================================================================
 *			o1
 *==========================================================================*/
static int o1(ch)
/*+++
.PURPOSE Optimized output of 1 character, if Echo is on.
.RETURNS 0 / 1 (number of chars output on Screen)
.REMARKS Optimization only for an active window, not in Insert Mode.
---*/
	char *ch;		/* IN: Character to output	*/
{
	ACHAR 	achar;
	int 	i, j, J, saved_marker;

  ENTER_DEBUG("+o1");

  status = 0;
  saved_marker = W->marker[1];
  if_not(f&Echo)	FINISH;		/* Nothing if no echo	*/
  
  J = W->Nj, i = W->pos/J, j = W->pos%J;

  if ( f & (Imode | Occluded) )
  {
	if (saved_screen_len)	
		W->marker[1] = W->marker[0] + input_valid - input_start;
   	status = tw_write(W, (char *)ch, 1, 0);	 /* Not optimized...	*/
	FINISH;
  }

	/* Convert char to ACHAR, and update Cursor Position on Screen 	*/

  achar = (W->attr & ~HIGH) | ( iscntrl(*ch) ? RubbishAchar() : *ch);
  tw_uc(W);

	/* Update Window's Cursor Position	*/

  W->pos += 1;

	/* First, copy char to window		*/

  *Aij(W,i,j) = achar;

	/* ... and copy char to Screen		*/

  if (W != Screen)
  {
  	J = Screen->Nj, i = Screen->pos/J, j = Screen->pos%J;
  	*Aij(Screen, i, j) = achar;
  	SetPosition(Screen->pos);
  }

	/* ... physically output char to Screen	*/

  WriteAchars(&achar, 1);

	/* ... And finally update Screen Cursor	*/

  tw_uc(W);

  status = 1;

  FIN:
  W->marker[1] = saved_marker;
  EXIT_DEBUG(status);
}
 
/*======================================================================
 *			mv1
 *======================================================================*/
static int mv1(direction, bytes)
/*+++++
.PURPOSE Optimized cursor mouvement of 1 place in the specified
	direction: up, down, right, left or home.
.RETURNS OK / NOK (attempt to move outside window, cursor does not move).
.REMARKS Move is limited within markers.
------*/
	int direction;	/* IN: The direction of the displacement	*/
	int bytes;	/* IN: The maximal displacement			*/
{
	int displacement, saved_marker, status;

  ENTER_DEBUG("mv1");

  status = NOK;
  if_not(f&Echo)	FINISH;		/* Nothing if no echo	*/
  
  status = OK;
  saved_marker = W->marker[1], 	W->marker[1] = W->pos + bytes;

  switch(direction)
  { case _LEFT_:	displacement = -1;	break;
    case _RIGHT_:	displacement =  1;	break;
    case _UP_:		displacement = -W->Nj;	break;
    case _DOWN_:	displacement =  W->Nj;	break;
    default:		displacement =  W->marker[0] - W->pos;
  	if (displacement == 0) displacement = -1;	/* No mouvement, 
  							forces NOK status */
  }

  displacement += W->pos;		/* New position */
  if ( (displacement >= W->marker[0]) && (displacement <= W->marker[1]))
  	tw_agoto(W, displacement);
  else	status = NOK;
  W->marker[1] = saved_marker;

  FIN:
  EXIT_DEBUG(status);
}

/*======================================================================
 *			move
 *======================================================================*/
static int move(out, in, len)
/*+++++
.PURPOSE Copy string to another.
.RETURNS Length of string
.REMARKS Move is limited within markers. 
------*/
	char	*out;	/* OUT: copied string		*/
	char	*in;	/* IN: string to display	*/
	int	len;	/* IN: Length of in		*/
{
	int	l;

  l = MIN (len, W->marker[1] - W->marker[0]);
  oscopy  (out, in, l);
 
  return(l);
}

/*======================================================================
 *			show
 *======================================================================*/
static int show(str, len)
/*+++++
.PURPOSE Redisplay the string (in, len) on the W window.
.RETURNS Length of string
.REMARKS Move is limited within markers. Cursor positioned AFTER marker.
------*/
	char	*str;	/* IN: string to display	*/
	int	len;	/* IN: Length of in		*/
{
	int	l;

  l = MIN (len, W->marker[1] - W->marker[0]);
  if_not(f&Echo)	FINISH;		/* Nothing if no echo	*/

  tw_st(W, Imode|Echo, 0); 
  tw_agoto(W, old_pos);
  if (saved_screen_len)	tw_wa(W, saved_screen, saved_screen_len);
  else			tw_clear(W, _DOWN_);
  tw_write(W, str, l, 0);
  if (f&Echo)	tw_st(W, Echo, 1), tw_r(W,0, NULL_WINDOW);
 
  FIN:
  return(l);
}

/*======================================================================
 *			here_Delete
 *======================================================================*/
static int here_Delete(ndel)
/*+++++
.PURPOSE Delete char interface
.RETURNS Length of string
.REMARKS Move is limited within markers. Cursor positioned AFTER marker.
------*/
	int	ndel;	/* IN: Number of bytes to delete */
{
	int	len;	/* IN: Length of input		*/
	int	status;
 
  len = input_valid - input_start;

  if (saved_screen_len)	
  {
  	status = W->pos;
	W->flags &= ~Imode;
  	tw_st(W, Imode|Echo, 0); 
  	{
  		tw_agoto(W, old_pos + len);
  		tw_wa(W, saved_screen + len, saved_screen_len - len);
  	}
	tw_agoto(W, old_pos);
  	tw_write(W, input_start, len, 0);

  	if (ndel < 0)	status += ndel;
  	tw_agoto(W, status);
	if (f&Imode)	W->flags |= Imode;
  	if (f&Echo)	tw_st(W, Echo, 1), tw_r(W,0, NULL_WINDOW);
  	status = OK;
  }
  else		status = tw_dc(W, ndel);

  return(status);
}

/*==========================================================================
 *			zm
 *==========================================================================*/
static int zm(direction)
/*+++
.PURPOSE Move in the command stack according to the specified direction.
.RETURNS OK / NOK (at the top or bottom of the stack)
.REMARKS 
---*/
	int 	direction;	/* IN: _UP_, _DOWN_ or _HOME_ (last command) */
{
#if TWFAC_Stacking

  status = NOK;

  if (c->size == 0)		FINISH;

  DISPLAY(0);

  switch(direction)
  { case _UP_:		/* Towards the beginning of the stack	*/
	if (c->pointed_no <= c->first_no)	
	{	c->pointed_no 	= c->first_no - 1;
		c->pointed 	= 0;
		FINISH;
  	}
	(c->pointed_no)--;
	(c->pointed)--;		/* Index of suffix length	*/
	(c->pointed) -= (1 + *(c->text + c->pointed));
	break;
    case _DOWN_:	/* Towards the bottom of the stack	*/
	if (c->pointed_no >= c->last_no)	
	{	c->pointed_no 	= c->last_no + 1;
		c->pointed	= c->used;
		FINISH;
	}
	(c->pointed_no)++;
	(c->pointed) += (2 + *(c->text + c->pointed));
	break;
    default:		/* The last issued command		*/
	if (c->last_no < c->first_no)	
	{	c->pointed_no 	= 0;
		c->pointed 	= 0;
		FINISH;
  	}
	c->pointed_no 	= c->last_no;
	c->pointed	= (c->used) - 1;
	(c->pointed) -= (1 + *(c->text + c->pointed));
	break;
  }

  DISPLAY(0);

  status = OK;
  FIN:
  return(status);
#else
  return(NOK);
#endif
}

/*==========================================================================
 *			zadd
 *==========================================================================*/
static int zadd(str, nbytes, opt)
/*+++
.PURPOSE Insert into the command stack the string str.
.RETURNS OK / NOK (not enough space)
.REMARKS 
---*/
	char	*str;	/* IN: The string to insert */
	int 	nbytes;		/* IN: Length of the str string	*/
	int 	opt;		/* IN: Option 1 to add effectively / 2 only if\
					differs from previous	*/
{
#if TWFAC_Stacking
	char	*p;
	int 	i,u, l;

  TRACE_ED_STR2("Command = ",str,nbytes);

  status = NOK;

  if (nbytes < 0)		FINISH;
  if (c->size == 0)		FINISH;

  DISPLAY(1);

	/* Check it it's the same command as previous one 	*/
	
  if ((opt == 2) && zm(_HOME_))		/* Get last command	*/
  {	p = (char *)(c->text + c->pointed);
	if ((*(p++) == nbytes) && (oscomp(p, str, nbytes) == 0))
	{	status = OK;
		FINISH;
	}
  }

  u = c->used + nbytes + 2;	/* Used bytes after addition	*/
  i = 2;
  
  while (u > c->size)		/* Look if stack must be compressed	*/
  {	l = *(c->text + i) + 2;	/* Length of command	*/
	(c->first_no)++;
	u -= l, i += l;
  }

  if (u < 2)				/* Not enough space ...	*/
  {	c->used = 2;
	FINISH;
  }

  if (i>2) 
  {	l = c->used - i;		/* Length to copy	*/
#if DEBUG
	TRACE("Compress Stack:");
	DISPLAY(1);
#endif
	c->used = 2 + oscopy((char *)(c->text+2), (char *)(c->text + i), l);
#if DEBUG
	TRACE("Compressed Stack:");
	DISPLAY(1);
#endif
  }

  c->pointed = c->used;

  p 	     = (char *)(c->text + c->used);
  *(p++) = nbytes;			/* Prefix length	*/
  p += oscopy((char *)p, (char *)str, nbytes);
					/* Copy text		*/
  *(p++) = nbytes;			/* Suffix length	*/

  if (opt)				/* Modify only if required */
  {	c->used = (unsigned char *)p - (c->text);
	(c->last_no)++;
	(c->pointed_no) = c->last_no;
  }

  DISPLAY(1);
  status = OK;

  FIN:
  return(status);
#else
  return(NOK);
#endif
}
 
/*==========================================================================
 *			tw_stopin
 *==========================================================================*/
int	tw_stopin(w, type, list)
/*+++
.PURPOSE Define the list of Characters that stop the Input:
	Control Characters, Arrows, Keypad, Function Keys.
.RETURNS OK / NOK (It's a _DISPLAY_ window)
.REMARKS A void string ("") removes all stopping characters; 
	a string starting with + means "add to existing list",
	with - "delete from existing list".
	The * acts as the wild character, e.g. "-*" means "delete all".
	By default, only the \r (Carriage-Return) stops the input.
\begin{TeX}

	For type \_STANDARD\_, {\em list} contains the ASCII equivalence,
	e.g. A for Control A.

	For type \_ARROW\_, individual keys are specified in {\em list} 
	by {\bf u}p, {\bf d}owm, {\bf l}eft, {\bf r}ight and {\bf h}ome.
	The {\bf \^{ }} (carret) means {\em return if a border encountered}

	Other keys cannot be individually specified.

\end{TeX}
---*/
	WINDOW	*w;	/* MOD: Window concerned 	*/
	int 	type;	/* IN: _NORMAL_ , _ARROW_, _KEYPAD_, _PF_ or _FK_ */
	char	*list;	/* IN: The characters that may be used to terminate; \
				the * is used as a wild character \
				(enable all)	*/
{
	int 	wild;
	unsigned int i;
	char	*p;

  ENTER("tw_stopin");

  i = 0;

  if_not(check(w))	EXIT(NOK);
  if_not(list)		EXIT(NOK);
  p = list;
  wild = (*p == '+' ? 1 : (*p == '-' ? -1 : 0));
  
  switch(type)
  { case _ARROW_:
	if_not(wild)	c->exit_ar = 0;
  	for ( ; *p; p++)
  	{	switch(tolower(*p))
  		{ case 'u': i = MASK(_UP_);	break;
  		  case 'd': i = MASK(_DOWN_);	break;
  		  case 'l': i = MASK(_LEFT_);	break;
  		  case 'r': i = MASK(_RIGHT_);	break;
  		  case 'h': i = MASK(_HOME_);	break;
		  case '*': i = 0x1f;		break;
  		  case '^': i = MASK(_TEE_);	break;
		  case '+': wild = 1;		continue;
		  case '-': wild = -1;		continue;
  		}
		if (wild<0)	c->exit_ar &= ~i;
		else		c->exit_ar |=  i;
  	}
	break;

    case _KEYPAD_: case _PF_: case _FK_:
    	i = MASK(type);
	if (*p == '*')	wild = 1;
    	if (wild)	c->exit_pf &= ~i;
    	if (wild > 0)	c->exit_pf |= i;
	break;

    default:		/* Control Characters */
	if_not(wild)	c->exit_cc = MASK('\r');
  	for ( ; *p; p++)
  	{	switch(*p)	
  		{ case '+':	wild = 1;	continue;
  		  case '-':	wild = -1;	continue;
  		  case '*':	i = ULONG_MAX;		break;
  		  default:	i = MASK(Control_(*p)); break;
  		}
		if (wild<0)	c->exit_cc &= ~i;
		else		c->exit_cc |=  i;
  	}
	break;
  }

  EXIT(OK);
}

/*==========================================================================
 *			tw_getc
 *==========================================================================*/
int	tw_getc(w, ch)
/*+++
.PURPOSE Get a single character on the window.
.RETURNS _STANDARD_, NOK (unknown escape sequence) _INTERRUPT_ _EOF_, _PF_, _FK_ , _KEYPAD_
.REMARKS Help Window Removed if _EOF_ or \r.
---*/
	WINDOW	*w;	/* MOD: Window concerned		*/
	char	*ch;	/* OUT: The character typed		*/
{
	char	buffer[2];
	int	type;

  ENTER("tw_getc");
  
  f = Stop1;	/* Stop after first byte */
  type = tw_mods(w, buffer, sizeof(buffer), 0);
  *ch = buffer[0];
  f = 0;

  if( (type < 0) || ((type == _STANDARD_) && (buffer[0] == '\r')))
  	tw_helps(W,0);		/* Remove Help Window	*/

  EXIT(type);
}


int tw_gc1(w, ch, stop1)
/*+++
.PURPOSE Similar to gc, but uses a list of characters that are NOT
	interpreted
.RETURNS _EOF_ / _INTERRUPT_ / 0(stopped after first input) / 
	_STANDARD_ / _ARROW_ / _KEYPAD_ / _FK_ / _PF_
	(see tw_mods)
---*/
	WINDOW	*w;	/* MOD: Window to echo the input	*/
	char	*ch;	/* OUT: The character typed		*/
	char	*stop1;	/* IN: List of characters to stop	*/
{
	int	status;

  ENTER("tw_gc1");

  stop_list = stop1;
  stop_len  = strlen(stop1);
  status    = tw_getc(w, ch);
  if (stop_len < 0) 	status = 0;	/* Stopped due to 1st char */
  stop_len  = 0;

  EXIT(status);
}
  
/*==========================================================================
 *			tw_get1
 *==========================================================================*/
int tw_get1(w, str, nbytes, stop1)
/*+++
.PURPOSE Get a command string from terminal, made of up to (nbytes-1)
	characters, but stop if one of the characters
	in stop1 was hit as the {\em first} input.
.RETURNS _EOF_ / _INTERRUPT_ / 0(stopped after first input) / 
	_STANDARD_ / _ARROW_ / _KEYPAD_ / _FK_ / _PF_
	(see tw_mods)
---*/
	WINDOW	*w;		/* MOD: Window to echo the input	*/
	char	*str;		/* MOD: The buffer to get the input	*/
	int 	nbytes;		/* IN: Length of the input buffer	*/
	char	*stop1;		/* IN: List of characters to stop	*/
{
	int	status;

  ENTER("tw_get1");

  stop_list = stop1;
  stop_len  = strlen(stop1);
  status    = tw_mods(w, str, nbytes, 0);
  if (stop_len < 0) 	status = 0;	/* Stopped due to 1st char */
  stop_len  = 0;

  EXIT(status);
}
  
/*==========================================================================
 *			tw_ms2
 *==========================================================================*/
int tw_ms2(w, str, nbytes, linput, fct)
/*+++
.PURPOSE Similar to tw_modf, but returns a composite status.
.RETURNS A composite code including the kind of key and the actual key
	typed at the terminal. Codes are:
\begin{TeX}
	\begin{itemize}
	\item 0:  the \b r terminated the input.
	\item \_INTERRUPT\_:  the {\em Interrupt} key was hit.
	\item \_EOF\_:  the {\em EOF} key was hit.
	\item a control char:  the returned character terminated the input.
	\item \_ARROW({\em key}):  one of the Arrows was hit, e.g. 
		{\tt \_ARROW(\_UP\_)}
	\item \_KEYPAD({\em key}):  one of the Kitpad Keys was hit,
		e.g. {\tt \_KEYPAD('6')}
	\item \_PF({\em key}):  one of the PF Keys was hit, 
		e.g. {\tt \_PF(1)}
	\item \_FK({\em key}):  one of the FK Keys was hit, 
		e.g. {\tt \_FK(12)}
	\end{itemize}
\end{TeX}
---*/
	WINDOW	*w;		/* MOD: Window to echo the input	*/
	char	*str;		/* MOD: The buffer to get the input	*/
	int 	nbytes;		/* IN: Length of the input buffer (<0 stretchable)	*/
	int 	linput;		/* IN: Length of the string to modify	*/
	int	(*fct)();	/* IN: The check function	*/
{
	int	status;
	char	thechar;

  ENTER("tw_ms2");

  f_ed = fct;

  status = tw_mods(w, str, nbytes, linput);
  if (status < 0)	*str = EOS;
  else switch(status)
  {
	case _STANDARD_:	/* Normal \r terminated string 	*/
		status = 0;	
		break;

	case 0:		/* Special Character stopped the Input 	*/
		status = GetChar(&thechar);	/* Get it...	*/
		switch(status)
		{
		  case _EOF_:			break;
		  case _STANDARD_:
		  	status = thechar;	break;
		  default:
		  	status = (status << 8) | thechar;
		  				break;
		}
		break;

	default:	
		status = (status << 8) | *str;
		*str = EOS;	
		break;
  }
  f_ed = NULL_FCT(int);

  EXIT(status);
}
  
/*==========================================================================
 *			tw_gc2
 *==========================================================================*/
int tw_gc2(w)
/*+++
.PURPOSE Similar to tw_getc, but returns a composite status.
.RETURNS A composite code including the kind of key and the actual key
	typed at the terminal (see tw_ms2).
---*/
	WINDOW	*w;		/* MOD: Window to echo the input	*/
{
	char	buffer[2];
	int	type;

  ENTER("tw_gc2");
  
  f = Stop1;	/* Stop after first byte */

  type = tw_mods(w, buffer, sizeof(buffer), 0);
  if (type < 0)	buffer[0] = EOS;
  else switch(type)
  {
	case _STANDARD_:	/* Normal \r terminated string 	*/
		type = (buffer[0] == '\r' ? 0 : buffer[0]);	
		break;

	default:	
		type = (type << 8) | buffer[0];
		break;
  }
  f = 0;

  if (type <= 0) 	tw_helps(W,0);		/* Remove Help Window	*/

  EXIT(type);
}
  
/*==========================================================================
 *			tw_modf
 *==========================================================================*/
int tw_modf(w, str, nbytes, linput, fct)
/*+++
.PURPOSE Get a command string from terminal, made of up to (nbytes-1)
	characters, but with check using the user--supplied
	function fct. 
\begin{TeX}
The supplied {\em fct} function is called before returning from
	the general input function, \ie just after the \meta{Carriage Return}
	or any other character that stops the input.
	The fct function has three arguments:
	\begin{enumerate}
	\item The window used
	\item	the input string
	\item	the size of this string (for modifications)
	\end{enumerate}
ans should return OK (the input is good) or NOK (the input is bad).
\end{TeX}
.RETURNS _EOF_ / _INTERRUPT_ / 0 / _STANDARD_ / _ARROW_ / _KEYPAD_ / _FK_ / _PF_
	(see tw_mods)
---*/
	WINDOW	*w;		/* MOD: Window to echo the input	*/
	char	*str;		/* MOD: The buffer to get the input	*/
	int 	nbytes;		/* IN: Length of the input buffer (<0 stretchable)	*/
	int 	linput;		/* IN: Length of the string to modify	*/
	int	(*fct)();	/* IN: The check function	*/
{
	int	status;

  ENTER("tw_modf");

  f_ed = fct;
  status    = tw_mods(w, str, nbytes, linput);
  f_ed = NULL_FCT(int);

  EXIT(status);
}
  
/*==========================================================================
 *			tw_mods
 *==========================================================================*/
int	tw_mods(w, str, nbytes, linput)
/*+++
.PURPOSE Display a template command, and get a command string 
	from terminal, made of up to (nbytes-1)
	characters, and echo on specified window.
	The input stops when the one of the characters
	specified by tw_stopin is hit (the Carriage Return only by default).
\begin{TeX}
	{\em Note that keypad characters are considered as normal chars,
	unless {\tt tw\_stopin(w, \_KEYPAD\_,"*")}  was called.}
\end{TeX}
.RETURNS OK or _STANDARD_, EOF, NOK, _KEYPAD_, _PF_, _FK_ with the 
following meanings:
\begin{TeX}
\begin{itemize}
\item	OK or \_STANDARD\_: a $<$Carriage\_Return$>$ or $<$Enter$>$
	terminates the input;
	this last character is not reported to the string,
	but {\em is replaced by the EOS character.}
\item	NOK: either
	\begin{itemize}
	\item the window is Display Only (no input available)
	\item the size of the input string is 1 character or less
		(nbytes $<2$)
	\item a control / arrow / keypad / PK / Function character was typed 
		at the terminal, which is {\em not the 
		first} input char;
		the returned string contains the previous characters
		(\ie before the stopping char), and a next call to 
		the function will return the control character.
	\end{itemize}
\item	\_INTERRUPT\_: the {\em Interrupt}(Control\_C) 
	char was typed. Output string is blanked.
\item	\_EOF\_: the EOF (Control\_Z or Control\_D, depending on InitWindow) 
	char was typed as the
	{\em first} input character; the output string contains only
	a zero (EOS).
	{\em Note that the EOF character not typed as the first char
	returns NOK.}
\item	\_KEYPAD\_: a keypad key is the 
	{\em first} input character and the Keypad is enabled
	via a previous call to {\tt tw\_stopin(w, \_KEYPAD\_, "*")}; 
	the output string contains the equivalence keypad.
	If the Keypad is enabled via {\em tw\_stopin}, and a Keypad
	was hit after normal ASCII characters, the function returns NOK.
	Note that keypad keys are translated if the keypad is not enabled.
\item	\_PF\_: a PF key is the 
	{\em first} input character if PF keys were previously enabled
	via {\tt tw\_stopin(w, \_PF\_, "*")}.
\item	\_FK\_: a Function is the 
	{\em first} input character if Function  keys were previously enabled
	via {\tt tw\_stopin(w, \_FK\_, "*")}.
\end{itemize}
\end{TeX}
.REMARKS 
---*/
	WINDOW	*w;		/* MOD: Window to echo the input	*/
	char	*str;		/* MOD: The buffer to get the input	*/
	int 	nbytes;	/* IN: Length of the input buffer (<0 stretchable) */
	int	linput;		/* IN: Length of template string	*/
{
	char	*p;		/* Current position in str	*/
	int	type;
	int	i, l, k, old_stoplen;
	int 	old_marker[2];
	char	ch;
	unsigned char old_flags, old_control;
	FUNCTION old_int;
	
  ENTER("tw_mods");

  type = 0;
  old_control = ' ';
  i = ABSOLUTE(nbytes);
  if (i < 2)		EXIT(NOK);		/* Buffer too small	*/

  saved_screen_len = 0;
  input_start = str;

  if_not(check(w))	
  {	*input_start = EOS;
	EXIT(NOK);
  }

  if (nbytes < 0)			/* Use Stretchable Window	*/
	nbytes = -nbytes, 
	OpenStretchableWindow(nbytes-1);


  old_pos = W->pos;	/* Keep first initial data */
  old_marker[0] = W->marker[0], old_marker[1] = W->marker[1];
  old_flags = W->flags;
  old_stoplen = stop_len;

  if (old_pos >= W->marker[1])	EXIT(NOK);
  
  p = input_start, input_valid = p;
  f |= W->flags & (Imode|Echo|Stacking);
  if ( (f&Echo) == 0)	f &= ~Stacking;		/* No stacking if no echo ... */


	/* To be sure that wrapping allowed, install a Range on the
	 * Window. Anything will then be limited to the Range
	 */

  l = tw_amark(W, W->pos, nbytes-1);
  input_end = p + l;

  if(f&Echo)	if (tw_occluded(W,0))	f |= Occluded;
				/* Can't optimize output...		*/

  if (f&Stacking)		/* Set stack to (last+1) command	*/
  {	c->pointed = c->used;
  	c->pointed_no = c->last_no + 1;
	command_number = c->pointed_no;
  }
  else	command_number = -1;

	/* Echo template string, and position cursor to End of Template */
	/**** Note: If No Echo, DON'T DISPLAY INPUT ....................*/

  if ((linput > 0) && (f&Echo))
  {	i = MIN(linput, input_end - input_start);
  	tw_write(W, input_start, i, 0);
	/* p += i, input_valid = p;	If Cursor at END of text */
	input_valid = input_start + i; tw_agoto(W, old_pos); 
					/* Cursor at BEGINNING of text */
  }
  tw_agoto(W, old_pos); 

  old_int = OnInterrupt((void *)InInt);		/* Install Interrupt Function 	*/
  
	/* Loop on the input char	*/

  while (1)
  {	
  	if (f&Stop)	/* Check with the Function	*/
  	{
		if (f&Interrupted)	/* Delete the Whole Line */
		{	tw_agoto(W, old_pos);
			i = input_valid - input_start;	/* Number of chars to delete */
			p = input_start, input_valid = p;
			here_Delete(i);
			break;
		}

  		if (f_ed)
  		{	*input_valid = EOS;
			/* tw_agoto(W, old_pos + (input_valid - input_start));*/
  			k = (*f_ed)(W, input_start, nbytes);	/* Call... */
  			l = strlen(input_start);/* Don't redisplay if stretch */
			if (saved_screen_len == 0) 
				l = show(input_start, l);
			input_valid = input_start + l;
			if (p > input_valid)	p = input_valid;
			tw_agoto(W, old_pos + (p - input_start));
			if (k)	break;			/* Anything OK	*/
			f &= ~Stop;			/* Bad input...	*/
			continue;
  		}
		break;
  	}

	if ((f & Stop1) && (p == input_end)	)	break;

	if (f & NoMore)		/* Too long string...	*/
	{	Bell();		
		f &= ~NoMore;
	}

		/* When there is a stopping list, install the terminal
		   as NOMAP. This is done via SetControls
		*/
	if (stop_len > 0)	old_control = SetControls(0);
	type = GetChar(&ch);
	if (stop_len > 0)	SetControls(old_control);
	if(f&Interrupted)	continue;

#if DEBUG
	TRACE_ED_STR2("Total   string is: ",input_start,input_valid-input_start);
	TRACE_ED_STR2("Present string is: ",input_start,p-input_start);
	TRACE_ED_STR2("Just read        : ",&ch, 1);
	TRACE_ED_I   (" ... of type: ",type);
#endif

	if ( type == NOK)	/* Bad Escape Sequence...	*/
	{	if (f & Stop1)	f |= (Stop|Special);
  		if (input_valid == input_start)	/* Was First Input Character */
				f |= (Stop|Special);
		else		Bell();
		continue;
	}

	if (f & Raw)		type = _STANDARD_;

	if (f&Imode)	W->flags |= Imode;
		else	W->flags &= ~Imode;

	switch(type)
	{ case EOF:	f |= (Stop|Special);
		continue;

	  case _PF_: case _FK_:
		if (c->exit_pf & MASK(type))
			f |= (Stop|Special);
		else		Bell();
		continue;

	  case _ARROW_: 
		i = MASK(ch);
		if (c->exit_ar & i) 			/* Possible Stop */
		{	if(c->exit_ar & MASK(_TEE_));	/* not Immediate */
			else 	{ f |= (Stop|Special); continue; }
		}
	  	if ((f&Stacking) && (i&(MASK(_UP_)|MASK(_DOWN_))))
		{		/* Copy retrieved command, and edit it	*/
			if (c->pointed_no == command_number)
				/* Save what has been typed */
				zadd(input_start, input_valid-input_start, 0);
#	if TWFAC_Stacking
			zm(ch);		/* Move in Stack */
			i = c->pointed;
			l = *(c->text + i);
			l = move(input_start, (char *)(c->text + i + 1), l);
			show(input_start, l);
			p = input_start + l, input_valid = p;
#	endif
			continue;
		}
			  		/* Reflect Cursor Displacement 	*/
	  	if_not (mv1(ch, input_valid-p))		/* No possible move	*/
			if (c->exit_ar & i) 		/* Must Stop 	*/
	  			f |= (Stop|Special);
	  	p = input_start + (W->pos - W->marker[0]);	/* Pos in buf 	*/
		continue;
		
	  case _KEYPAD_:
		if (c->exit_pf & MASK(_KEYPAD_))
		{	f |= (Stop|Special);
			continue;
		}
		type = _STANDARD_;  /* Keypad chars are considered as normal */
		break;
	}		

	if (stop_len > 0)  {		/* Check first byte	*/
		if (oscloc(stop_list, stop_len, ch) < stop_len) {
			stop_len = -1;		/* Found*/
			f |= (Stop|Special);
			continue;
		}
		stop_len = 0; 
	}

	/* --- Here Only if it's an ASCII character ---- */

	if ((isprint(ch)) || (f&Raw) || (terms->flags & TERM_NOMAP)) {
		f &= ~Raw;
		if (f & Imode)
		{	if ( (input_valid) >= input_end)	f |= NoMore;
			else oscopy ( p+1, p, input_valid++ - p);
		}
		else		/* Replace Mode	*/
		{	if (input_valid <= p)		input_valid = p+1;
			if (input_valid > input_end)	input_valid--, f |= NoMore;
		}
		if (f & NoMore)		/* Too long string...	*/
			continue;
		*(p++) = ch;
		o1(&ch);		/* Echo the character...*/
		if (stop_len <= 0)	continue;
	}

		/*==============================================*/
		/* ---     Here only if it's a Control Char 	*/
		/*==============================================*/
	
	if (ch == '\r')				/* Normal End	*/
	{	f |= Stop;
		if (f&Stop1)	f |= Special;
		continue;
	}

	i = *(unsigned char *)(&ch);
	if (i > 32)		/* Delete char	*/
	{	i = 32;
		if (f&Stop1)	{ f |= Stop|Special; continue; }
	}

	if (c->exit_cc & MASK(i))		/* Exit on Control	*/
	{	f |= (Stop|Special);
		continue;
	}

	switch(terms->tc[i])
	{ default:		Bell();		/* Just ignore...	*/
		continue;

	  case TW_cc_EOF:	f |= (Stop|Special);
	  	type = EOF;
		continue;

	  case TW_cc_RAW:	f |= Raw;
		continue;

	  case TW_cc_REFR:			/* Refresh Screen 	*/
	  	tw_r(NULL_WINDOW,1,NULL_WINDOW);
		continue;

	  case TW_cc_HELP:	tw_helps(W,1);	/* Switch the Help Wind	*/
		f &= ~Occluded;
		if(f&Echo)	if (tw_occluded(W,0))	f |= Occluded;
		continue;

	  case TW_cc_MODE:	f ^= Imode;	/* Change mode	*/
		continue;	/* Window Imode flag done at loop top	*/

  	  case TW_cc_NW:			/* Move to next word	*/
		p += oscscan((unsigned char *)p, input_valid-p, _SPACE_, main_ascii);
		p += oscspan((unsigned char *)p, input_valid-p, _SPACE_, main_ascii);
		tw_agoto(W, old_pos + (p-input_start));
		continue;

	  case TW_cc_EOL:			/* Move cursor to end	*/
		tw_agoto(W, old_pos + (input_valid - input_start));
		p = input_valid;
		continue;

	  case TW_cc_DELC:			/* Delete Char	*/
		if (p > input_start) 
		{	p--, input_valid--;
			oscopy(p, p+1, input_valid-p);
			here_Delete(-1);
		}
		continue;

	  case TW_cc_DELL:			/* Clear Line = delete all */
		tw_agoto(W, old_pos);
		i = input_valid - input_start;	/* Number of chars to delete */
		p = input_start, input_valid = p;
		here_Delete(i);
		stop_len = old_stoplen;
		continue;

  	  case TW_cc_DELE:			/* Delete to the right	*/
		i = input_valid - p;
		input_valid = p;
		here_Delete(i);
		continue;

  	  case TW_cc_DELW:			/* Delete Previous Word	*/
		i = oscbspan((unsigned char *)input_start, p-input_start, _SPACE_, main_ascii);
		i = oscbscan((unsigned char *)input_start, i+1, _SPACE_, main_ascii);
		i = (p-input_start) - (i+1);	/* Number of chars to delete */
		oscopy(p-i, p, input_valid-p);
  	  	p -= i, input_valid -= i;
		here_Delete(-i);
		continue;
	}
  }

  if (f&Interrupted)	type = _INTERRUPT_, input_valid = input_start;
  p = input_valid, l = p - input_start;		/* Final Length of String */


	/* Add the string to command stack	*/

  if ( (f&Stacking) && (type <= _STANDARD_)  && (l > 0))
  	zadd(input_start, l, 2);

  if (f & Special)
  {	if (input_valid == input_start)		/* Was First Input Character */
  		*(p++) = ch;
	else  	tv_push(type, ch), type = NOK;
  }
  *p = EOS;



  if_not(f&Stop1)	tw_helps(W,0);		/* Remove Help Window	*/
  W->marker[0] = old_marker[0], W->marker[1] = old_marker[1];
  W->flags = old_flags;

  if(saved_screen_len)			/* Restore The Text	*/
  {
	tw_st(W, Echo, 0);
	k = W->pos;
  	tw_agoto(W, old_pos);
  	tw_wa(W, saved_screen, saved_screen_len);
	tw_agoto(W, k);
  	if (f&Echo)	tw_st(W, Echo, 1), tw_r(W,0,NULL_WINDOW);
  }
  f = 0;
  OnInterrupt(old_int);

  TRACE(str);

  EXIT(type);
}

#if DEBUG
/*==========================================================================
 *			zed
 *==========================================================================*/
static int zed(opt)
/*+++
.PURPOSE Edit a stack (for debugging purposes)
.RETURNS OK 
.REMARKS Option 1 to display the whole stack
---*/
	int	opt;		/* IN: Edition option	*/
{
	char 	msg[80];
	char	*p;
	int 	i,u,l;
 
  sprintf(msg,"!size=%d, used=%d, pointed=%d, no=%d, first=%d, last=%d",
	c->size, c->used, c->pointed, c->pointed_no, c->first_no,
	c->last_no);
  LOG(msg);

  if(opt)
  {	TRACE_ED_HEXA("Stack is:",c->text,c->used);
	for (p = (char *)(c->text)+2, i=c->first_no; i<= c->last_no; i++)
	{	sprintf(msg,"!No %4d=>",i);
		TRACE_ED_STR2(msg,p+1,*p);
		p += *p;	p += 2;
	}
  }

  return(OK);
}
#endif

/*==========================================================================
 *			tw_zadd
 *==========================================================================*/
int	tw_zadd(w, str, nbytes)
/*+++
.PURPOSE Insert into the command stack the string str.
.RETURNS OK / NOK (not enough space)
.REMARKS The command IS NOT inserted if it's the same as the previoue one.
---*/
	WINDOW *w;		/* IN: The concerned Window	*/
	char	*str;		/* IN: The string to insert */
	int 	nbytes;		/* IN: Length of the str string	*/
{
  ENTER("tw_zadd");
  status = check(w);	
  if(status)		status = zadd(str, nbytes, 2);
  EXIT(status)  ;
}

/*==========================================================================*/
static int zclear()
/*+++
.PURPOSE Clear the command stack.
.RETURNS OK 
.REMARKS 
---*/
{	
#if TWFAC_Stacking
	unsigned char *p;

  if (c->size < 2)		FINISH;

  DISPLAY(0);

  c->used = 2;
  c->pointed = 0;
  c->pointed_no = 0;
  c->first_no = 1;
  c->last_no  = 0;
  p = c->text;

  *(p++) = 0;
  *(p++) = 0;

  DISPLAY(0);

  FIN:
  return(OK);
#else
  return(NOK);
#endif
}
 
/*==========================================================================
 *			tw_zclear
 *==========================================================================*/
int	tw_zclear(w)
/*+++
.PURPOSE Clear the command stack.
.RETURNS OK 
.REMARKS 
---*/
	WINDOW *w;	/* IN: The concerned Window	*/
{
  ENTER("tw_zclear");
  status = check(w);
  if(status)		status = zclear();
  EXIT(status)  ;
}

/*==========================================================================
 *			tw_zm
 *==========================================================================*/
int	tw_zm(w, direction)
/*+++
.PURPOSE Move in the command stack according to the specified direction.
.RETURNS OK / NOK (at the top or bottom of the stack)
.REMARKS 
---*/
	WINDOW *w;		/* IN: The concerned Window	*/
	int 	direction;	/* IN: _UP_, _DOWN_ or _HOME_ (last command) */
{
  ENTER("tw_zm");
  status = check(w);
  if(status)		status = zm(direction);
  EXIT(status)  ;
}

/*==========================================================================
 *			tw_zn
 *==========================================================================*/
static int zn(number)
/*+++
.PURPOSE Move in the stack to a specified numbered command
.RETURNS OK / NOK (numbered command not in stack)
.REMARKS 
---*/
	int 	number;		/* IN: Command number	*/
{
#if TWFAC_Stacking
	int n;

#if DEBUG
	TRACE_ED_I("Getting command #",number);
#endif

  status = NOK;

  if (c->size == 0)		FINISH;

  DISPLAY(0);

  n = number;
  if (n < c->first_no)		FINISH;
  if (n > c->last_no)		FINISH;

  if ( (n - c->first_no) < 3)
  {	c->pointed = 2;
	c->pointed_no = c->first_no;
  }

  while (c->pointed_no > n)	zm(_UP_);
  while (c->pointed_no < n)	zm(_DOWN_);

  DISPLAY(0);

  status = OK;
  FIN:
  return(status);
#else
  return(NOK);
#endif
}

/*==========================================================================*/
int	tw_zn(w, number)
/*+++
.PURPOSE Move in the stack to a specified numbered command
.RETURNS OK / NOK (numbered command not in stack)
.REMARKS 
---*/
	WINDOW *w;		/* IN: The concerned Window	*/
	int 	number;		/* IN: Command number	*/
{
  ENTER("tw_zn");
  status = check(w);
  if(status)		status = zn(number);
  EXIT(status)  ;
}

/*==========================================================================
 *			tw_zo1
 *==========================================================================*/
static int zo1(w, option)
/*+++
.PURPOSE Edit on a specified window the current stacked command
.RETURNS OK / NOK (no stacked command)
.REMARKS The edition starts on a new line
---*/
	WINDOW	*w;		/* MOD: Window for output	*/
	int 	option;		/* IN: 1 to edit with number */
{
#if TWFAC_Stacking
	unsigned char 	*p;
	int 		n, i;
	MID_STATIC char c_no[6] = {' ', ' ', ' ', ' ', ':', ' '};

  status = NOK;

  if (c->size == 0)			FINISH;
  if (c->pointed_no < c->first_no)	FINISH;
  if (c->pointed_no > c->last_no)	FINISH;

  DISPLAY(0);

  f = w->flags & Echo;
  w->flags &= ~Echo;  

  if (w->pos % w->Nj)		tw_nl(w);

  if (option)	
  {	i = sizeof(c_no) - 2;
	for (n = c->pointed_no; --i >= 0; n /= 10)
		c_no[i] = (n ? n%10+'0' : ' ');
	tw_write(w, c_no, sizeof(c_no), 0);
  }

  p = c->text + c->pointed;
  tw_line(w, p+1, *p, 0);

  if (f)
  {	w->flags |= Echo, f = 0;
	tw_rw(w, 0,NULL_WINDOW);
  }
  status = OK;

  FIN:
  return(OK);
#else
  return(NOK);
#endif
}

/*==========================================================================*/
int	tw_zo1(w, win, option)
/*+++
.PURPOSE Edit on a specified window the current stacked command
.RETURNS OK / NOK (no stacked command)
.REMARKS The edition starts on a new line
---*/
	WINDOW	*w;		/* MOD: Window for output	*/
	WINDOW *win;		/* IN: The concerned Window	*/
	int 	option;		/* IN: 1 to edit with number */
{
  ENTER("tw_zo1");
  status = check(win);
  if(status)	status = zo1(w, option);
  EXIT(status)  ;
}

/*==========================================================================
 *			tw_zo
 *==========================================================================*/
static int zo(w, option)
/*+++
.PURPOSE Edit on a specified window the stack contents
.RETURNS OK / NOK (no stacked command)
.REMARKS The edition starts on a new line. The window is cleared before
	the edition. If the window is active with scrolling, 
	ALL commands are listed.
---*/
	WINDOW	*w;		/* MOD: Window for output	*/
	int 	option;		/* IN: 1 to edit with number */
{
#if TWFAC_Stacking
	int n;

  status = NOK;

  if (c->size == 0)		FINISH;
  if (c->pointed_no == 0)	FINISH;

  DISPLAY(1);

	/* Computation of the number of commands that can be issued	*/
  n = c->last_no - c->first_no + 1;
  if ( n > w->Ni )	n = c->last_no - w->Ni + 1;
	else		n = c->first_no;
  /* if ( (w->active) && (w->scroll) )	n = c->first_no; */

  zn(n);
  tw_clear(w,_WHOLE_);

  while (1)
  {	zo1(w,option);
	if_not(zm(_DOWN_))	break;
  }

  status = OK;
  FIN:
  return(status);
#else
  return(NOK);
#endif
}

/*==========================================================================*/
int	tw_zo(w, win, option)
/*+++
.PURPOSE Edit on a specified window the stack contents
.RETURNS OK / NOK (no stacked command)
.REMARKS The edition starts on a new line. The window is cleared before
	the edition. If the window is active with scrolling, 
	ALL commands are listed.
---*/
	WINDOW	*w;		/* MOD: Window for output	*/
	WINDOW *win;		/* IN: The concerned Window	*/
	int 	option;		/* IN: 1 to edit with number */
{
  ENTER("tw_zo");
  status = check(win);
  if(status)	status = zo(w, option);
  EXIT(status)  ;
}

