/* @(#)twget.c 17.1.1.1 (ES0-DMD) 01/25/02 17:48:06 */ /*=========================================================================== 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 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) ----------------------------------------------------------------------------*/ #define DEBUG 0 /* For debugging only */ #define PM_LEVEL LEVEL_TW #define TW_import 0 /* Do not use macro window definitions */ #include 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); /* if (ostin() <= 0) tv_push(_STANDARD_, ' '); */ 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, 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"); 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; char buffer[2]; 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"); 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(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(p, input_valid-p, _SPACE_, main_ascii); p += oscspan(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(input_start, p-input_start, _SPACE_, main_ascii); i = oscbscan(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) ; }