/*===========================================================================
  Copyright (C) 1988-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 tvgets.c
.AUTHOR    	Francois Ochsenbein [ESO-IPG]
.LANGUAGE  	C
.KEYWORDS	On-line input editing
.ENVIRONMENT	TermWindows
.COMMENTS	This module contains routines to perform command input,
		at the Terminal Level
\begin{TeX}
\\
This module is only there to be able to use on-line editiing
facilities without any window. Unlike tw\_gets functions,
commands are not stacked, and the memory is limited to
what was just typed.

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.

The characters that stop output may be defined via the tv\_stopin
funtion.

\end{TeX}

.VERSION 3.0	14-Apr-1988: Creation for MIDAS usage.
.VERSION 3.1	05-Oct-1988: Modifications for MIDAS: Command Stacking allowed.
.VERSION 3.2	21-Feb-1990: Suppress all warnings

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

#define DEBUG		0		/* For debugging	*/

#define PM_LEVEL 	LEVEL_TV

#include <midas_def.h>

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


extern int tv_buffer(), tv_write(), tv_agoto(), tv_pos();
extern int tv_clear(), tv_mods(), tv_open(), tv_help();
extern int tv_getc(), tv_bell(), tv_push();

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



MID_EXTERN TERM	*terms ;		

#if 	DEBUG
#	define ENTER_DEBUG(x)	ENTER(x)
#	define EXIT_DEBUG(x)	EXIT(x)
#	define	TERMTEST	if(!terms->version) tv_gopen();\
				tv_ed(terms)
#else
#	define	TERMTEST	if(!terms->version) tv_gopen()
#	define ENTER_DEBUG(x)	
#	define EXIT_DEBUG(x)	return(x)
#endif

#define FINISH		goto FIN

MID_STATIC unsigned int 	f = 0;		/* Flags to communicate	*/
MID_STATIC unsigned int 	f0= Echo|Stacking;/* Flags to communicate*/
static int	J;			/* Column width		*/
static int 	init_pos;		/* Where is the cursor at beginning */
static char	*p_start;
MID_STATIC char 	*stop_list = (char *)0;
MID_STATIC int	stop_len = 0;

#define Stop		0x0100
#define Stop1		0x0400
#define Special		0x0200
#define NoMore  	0x1000
#define Raw		0x2000

/* Stored Commands as a static structure */

static unsigned char command_buffer[1600] = {0,0,0,0};

static COMMAND command = {
		MASK('\r'), 	/* Which controls exit */
		0,		/* Which arrow keys exit */
		0,		/* Which special keys exit */
		0,		/* last_type	*/
		0,		/* last_char	*/
		sizeof(command_buffer), 
		2,		/* used		*/
		0, 		/* pointed	*/
		0, 		/* pointed_no 	*/
		1,		/* first_no 	*/
		0,		/* last_no  	*/
		command_buffer
	};

static COMMAND *c = &command;
static short command_number;
static int   command_len;

	MONITOR(TVGETS);


/*==========================================================================
 *			o1
 *==========================================================================*/
static int o1(ch, len)
/*+++
.PURPOSE Optimized output of 1 character.
.RETURNS 0 / 1 (number of chars output on Screen)
.REMARKS Complete string rewritten if in InsertMode.
---*/
	char 	*ch;			/* IN: Character to output	*/
	int	 len;			/* IN: Length of input		*/
{
	char 	*p;
	int	state_buffer;

  ENTER_DEBUG("+o1");

  if_not(f&Echo)	EXIT_DEBUG(0);

  p = ch;

  state_buffer = tv_buffer(1);		/* To output only at end */
  status = WriteBinary(p++, 1);

  if ( f & Imode)		/* Continuation to write */
  { 	WriteBinary(p, --len);
  	SetPosition(GetPosition() - len);
  }

  tv_buffer(state_buffer);		/* Output everything	*/

	/* Check if the Screen scrolled; modify accordingly init_pos */
  init_pos = GetPosition() - (p - p_start);

  EXIT_DEBUG(status);
}
 
/*======================================================================
 *			mv1
 *======================================================================*/
static int mv1(direction, max_move)
/*+++++
.PURPOSE Optimized cursor mouvement of 1 place in the specified
	direction: up, down, right, left or home.
.RETURNS The actual displacement in bytes.
.REMARKS Move is limited...
------*/
	int direction;	/* IN: The direction of the displacement	*/
	int max_move;	/* IN: Maximal right / down displacement in bytes */
{
	int pos, new_pos;
	
  ENTER_DEBUG("+mv1");

  pos = GetPosition();
  new_pos = pos;

  switch(direction)
  { case _LEFT_:	new_pos -= 1;	break;
    case _RIGHT_:	new_pos += MIN(max_move, 1);	break;
    case _UP_:		new_pos -= J;	break;
    case _DOWN_:	new_pos += MIN(max_move, J);	break;
    default:		new_pos  = init_pos;
  }

  if (new_pos < init_pos)	new_pos = init_pos;
  SetPosition(new_pos);

  EXIT_DEBUG(new_pos - pos);
}

/*======================================================================
 *			dc
 *======================================================================*/
static int dc(str, nd, len)
/*+++++
.PURPOSE Delete characters at left of cursor.
.RETURNS 0
.REMARKS 
------*/
	char *str;	/* IN: The new string */
	int  nd;	/* IN: Number of bytes to delete	*/
	int  len;	/* IN: Length of text	*/
{
	int 	pos, nb, state_buffer;
	static char blanks[] = "        ";
	
	
  ENTER_DEBUG("dc");

  state_buffer = tv_buffer(1);		/* To output only at end */
  pos = GetPosition() - nd;
  SetPosition(pos);
  /* ClearDown(); */
  if (len)	WriteBinary(str, len);
  for (nb = nd; nb > 0; nb -= sizeof(blanks)-1)	
  	WriteBinary(blanks, MIN(nb, sizeof(blanks)-1));

  SetPosition(pos);
  tv_buffer(state_buffer);		/* Output everything	*/

  EXIT_DEBUG(0);
}

/*======================================================================
 *			show
 *======================================================================*/
static int show(str, in, len)
/*+++++
.PURPOSE Redisplay the string (in, len) in place of the current command,
	and copy it to str.
.RETURNS Length of string
.REMARKS 
------*/
	char	*str;	/* OUT: copied string		*/
	char	*in;	/* IN: string to display	*/
	int	len;	/* IN: Length of in		*/
{
	int	l;

  l = MIN (len, command_len);
  oscopy  (str, in, l);
  SetPosition(init_pos);
  ClearDown();
  WriteBinary(str, l);
	/* Check if the Screen scrolled; modify accordingly init_pos */
  init_pos = GetPosition() - l;
 
  return(l);
}

/*==========================================================================*/
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) */
{

  status = NOK;

  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;
  }

  status = OK;
  FIN:
  return(status);
}

/*==========================================================================
 *			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	*/
{
	char	*p;
	int 	i,u, l;

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

  status = NOK;

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

	/* 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	*/
	c->used = 2 + oscopy((char *)(c->text+2), (char *)(c->text + i), l);
  }

  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;
  }

  status = OK;

  FIN:
  return(status);
}
 
/*==========================================================================
 *			tv_stopin
 *==========================================================================*/
int	tv_stopin(type, list)
/*+++
.PURPOSE Define the list of Characters that stop the Input:
	Control Characters, Arrows, Keypad, Function Keys.
.RETURNS OK / NOK 
.REMARKS For type _STANDARD_ or _ARROW_, list gives the ASCII equivalence,
	e.g. A for Control A, U for the Up Key. 
	For a void string (""), all stopping characters are removed;
	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.
---*/
	int 	type;	/* IN: _STANDARD_ , _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)	*/
{
	char *p;
	int wild;
	unsigned int i;

  ENTER("tv_stopin");

  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': case 'b': i = MASK(_LEFT_);		break;
  		  case 'r': case 'f': i = MASK(_RIGHT_);	break;
  		  default : i = MASK(_HOME_);	break;
		  case '*': i = 0xff; 	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 = 0xffffffff;		break;
  		  default:	i = MASK(Control_(*p)); break;
  		}
		if (wild<0)	c->exit_cc &= ~i;
		else		c->exit_cc |=  i;
  	}
	break;
  }

  EXIT(OK);
}

/*==========================================================================
 *			tv_get1
 *==========================================================================*/
int tv_get1(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_ / 0 / _STANDARD_ / _ARROW_ / _KEYPAD_ / _FK_ / _PF_
	(see tv_mods)
---*/
	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("tv_get1");

  stop_list = stop1;
  stop_len  = strlen(stop1);
  status    = tv_mods(str, nbytes, 0);
  stop_len  = 0;

  EXIT(status);
}
  
/*==========================================================================
 *			tv_getp
 *==========================================================================*/
int tv_getp(str, nbytes)
/*+++
.PURPOSE Get a password (no echo)
.RETURNS _EOF_ / 0 / _STANDARD_ / _ARROW_ / _KEYPAD_ / _FK_ / _PF_
	(see tv_mods)
---*/
	char	*str;		/* MOD: The buffer to get the input	*/
	int 	nbytes;		/* IN: Length of the input buffer	*/
{
	int	status;

  ENTER("tv_getp");

  f0 = 0;
  status    = tv_mods(str, nbytes, 0);
  f0 = Echo | Stacking;

  EXIT(status);
}
  
/*==========================================================================
 *			tv_mods
 *==========================================================================*/
int	tv_mods(str, nbytes, linput)
/*+++
.PURPOSE Display a template command, and get a command string 
	from terminal, made of up to (nbytes-1)
	characters, and echo. 
	The input stops when the one of the characters
	specified by tv_stopin is hit (the Carriage Return only by default).
\begin{TeX}
	{\em Note that} keypad characters are considered as normal chars,
	unless {\tt tv\_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 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	EOF: the EOF (Control\_Z or Control\_D, depending on initialization) 
	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 tv\_stopin(w, \_KEYPAD\_, "*")}; 
	the output string contains the equivalence keypad.
	If the Keypad is enabled via {\em tv\_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 tv\_stopin(w, \_PF\_, "*")}.
\item	\_FK\_: a Function is the 
	{\em first} input character if Function  keys were previously enabled
	via {\tt tv\_stopin(w, \_FK\_, "*")}.
\end{itemize}
\end{TeX}
.REMARKS 
---*/
	char	*str;		/* MOD: The buffer to get the input	*/
	int 	nbytes;		/* IN: Length of the input buffer	*/
	int	linput;		/* IN: Length of template string	*/
{
	register char	*p;	/* Current position in str	*/
		 char	*pw, *p_end;	/* pw is last valid char in str	*/
		 int	type;
	static	 char	ch;
		 int	i, l;

  ENTER("tv_mods");

  type = 0;
  f = f0, f0 = Echo | Stacking;

  if (nbytes < 2)	EXIT(NOK);		/* Buffer too small	*/

  TERMTEST;		/* Open Terminal if not yet done	*/
  
  p_start = str;
  p = p_start, pw = p;
  command_len = nbytes - 1;
  p_end = p + command_len;

  init_pos = GetPosition();	/* Keep first initial data */
  J = terms->dim[1];		/* Column width		   */

  if (linput)	TRACE_STR2(str, linput);


	/* Echo template string, and position cursor to End of Template */

  if (linput > 0) 
  {	i = MIN(linput, p_end - p_start);
  	WriteBinary(p_start, i);
	p += i, pw = p;
  }	

	/* Initialize the Stack of Commands	*/

  c->pointed = c->used;
  c->pointed_no = c->last_no + 1;
  command_number = c->pointed_no;


	/* ============ Loop on the input char ========	*/

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

	type = GetChar(&ch);

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

	if ( type == NOK)	/* Bad Escape Sequence...	*/
	{	Bell();
		continue;
	}

	if (f & Raw)		type = _STANDARD_;

	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_: 
		if (c->exit_ar & MASK(ch))
		{	f |= (Stop|Special);
			continue;
		}
		switch(ch)	/* Execute the Cursor Movements */
		{ case _LEFT_:	
	  	  case _RIGHT_:	
			p += mv1(ch, pw - p);	/* Maximal Displacement is pw-p */
			continue;
		  default:
	  		SetPosition(init_pos), p = p_start;
			continue;
	  	  case _UP_:
	  	  case _DOWN_:
		  	/* Copy retrieved command, and edit it	*/
			if_not(f&Echo)	continue;
			if (c->pointed_no == command_number)
				/* Save what has been typed */
				zadd(str, pw-str, 0);
			zm(ch);		/* Move in Stack */
			i = c->pointed;
			l = *(c->text + i);
			l = show(p_start, (char *)(c->text + i + 1), l);
			p = str + l, pw = p;
		}
		continue;
		
	  case _KEYPAD_:
		if (c->exit_pf & MASK(_KEYPAD_))
		{	f |= (Stop|Special);
			continue;
		}
		type = _STANDARD_;  /* Keypad chars are considered as normal */
		break;
	}		

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

	if ((isprint(ch)) || (f&Raw))
	{	f &= ~Raw;
		if (f & Imode)
		{	if ( (pw) >= p_end)	f |= NoMore;
			else oscopy ( p+1, p, pw++ - p);
		}
		else		/* Replace Mode	*/
		{	if (pw <= p)	pw = p+1;
			if (pw > p_end)	pw--, f |= NoMore;
		}
		if (f & NoMore)		/* Too long string...	*/
			continue;
		*p = ch;
		o1(p, pw-p);		/* Echo the character...*/
		p++;
		if (stop_len)		/* Check first byte	*/
		{	if (oscloc(stop_list, stop_len, ch) < stop_len)
				ch = '\r';
			else	ch = EOS;
			stop_len = 0;
			if(ch == EOS)	continue;
		}
		else	continue;
	}

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

	i = *(unsigned char *)(&ch);
	if (i > 32)		/* Delete char	*/
		i = 32;

	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_RAW:	f |= Raw;
		continue;

	  case TW_cc_REFR:			/* Refresh Screen 	*/
	  	SetPosition(init_pos);
	  	ClearDown();
	  	WriteBinary(p_start, pw-p_start);
	  	SetPosition(init_pos + (p-p_start));
		continue;

	  case TW_cc_HELP:	/* Go first to end of text, show help,
	  			 * and reissue text */
		SetPosition(init_pos + (pw - p_start));
		ShowHelp();	
	  	WriteBinary(p_start, pw-p_start);
		init_pos = GetPosition() - (pw - p_start);
		SetPosition(init_pos + (p - p_start));
		continue;

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

  	  case TW_cc_NW:			/* Move to next word	*/
		p += oscscan((unsigned char *)p, pw-p, _SPACE_, main_ascii);
		p += oscspan((unsigned char *)p, pw-p, _SPACE_, main_ascii);
		SetPosition(init_pos + (p-p_start));
		continue;

	  case TW_cc_EOL:			/* Move cursor to end	*/
		SetPosition(init_pos + (pw - p_start));
		p = pw;
		continue;

	  case TW_cc_DELC:			/* Delete Char	*/
		if (p == p_start) 	continue;
		p--, pw--;
		oscopy(p, p+1, pw-p);
		dc(p, 1, pw-p);
		continue;

	  case TW_cc_DELL:			/* Clear Line		*/
		p = p_start, pw = p;
		SetPosition(init_pos);
		ClearDown();
		continue;

  	  case TW_cc_DELE:			/* Delete to the right	*/
		pw = p;
		ClearDown();
		continue;

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

  p = pw, l = p - p_start;		/* Final Length of String	*/
  SetPosition(init_pos + l);		/* Where the cursor must be	*/

	/* Add the string to command stack	*/

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

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


  TRACE(p_start);

  EXIT(type);
}


