/* @(#)tvget.c 17.1.1.1 (ES0-DMD) 01/25/02 17:36:56 */ /*=========================================================================== 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 tvget.c .AUTHOR Francois Ochsenbein [ESO-IPG] .LANGUAGE C .KEYWORDS Terminal Independant i/o Package. .ENVIRONMENT TermWindows .COMMENTS Only Input Functions are here. \begin{TeX} Used Capabilities: $$\begin{tabular}{|lp{37em}|} \hline {\tt ...} & and all input keys, \ie {\tt ku kd kr kl {\it or} kb kh} for arrows, {\tt k1}\dots{\tt k9} for PF keys, {\tt K0}\dots{\tt K9}, {\tt K. K, K- KE} for keypad, extended with {\tt Kf, Ki, Kr}\dots for VT200, {\tt F0}\dots {\tt F}$n$ for function keys. \\ \hline \end{tabular}$$ \end{TeX} .VERSION 1.0 07-Jul-1986: Creation .VERSION 1.1 12-Aug-1986: Added tv_wa, writing a string with attributes. .VERSION 1.2 13-Oct-1986 Some terminals (e.g. HP) need to resend the attribute on each new line. This feature incorporated with ChangedLine flag in standout part of TERM structure. .VERSION 2.0 03-Dec-1986 New terminal-independant with output buffering. .VERSION 2.1 11-Jun-1987 Version '2' of TermWindows --- Defaut input char is Control_Z Redefined interfaces to xv routines, allowing a batch mode, or no terminal connected at all. .VERSION 2.2 23-Jun-1987 Adapted to UNIX and Minimal Implementation. .VERSION 2.3 02-Dec-1987 Use of external function while waiting for input .VERSION 3.0 20-Mar-1988 Version '3' of TermWindows. ----------------------------------------------------------------------------*/ #define DEBUG 0 /* For debugging */ #define PM_LEVEL LEVEL_TV #define TW_MACROS 0 /* Don't use TermWindows Macros */ #define TW_import 0 #define TW_STRUCT 0 /* Do not use window Structures */ #include MID_EXTERN TERM *terms ; MID_STATIC short int *low = NULL_PTR(short int); MID_STATIC short int *high= NULL_PTR(short int);/* To scan input sequences */ MID_STATIC int TICK = 5; /* Check input every 5 sec.*/ #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 #define ENTER_TV(x) int state_buffer; \ ENTER(x); TERMTEST; \ state_buffer = tv_buffer(1); #define EXIT_TV(f) FIN: \ tv_buffer(state_buffer); \ EXIT(f) /* Macros just to improve readibility */ #define curl (terms->pos[0]) #define curc (terms->pos[1]) #define diml (terms->dim[0]) #define dimc (terms->dim[1]) #define BUFR (terms->bufr) #define bufp (terms->ir) #define buftop (terms->irtop) #define BUFS (terms->bufs) #define CAPLIST (terms->caplist) #define RAW_MODE (terms->flags & TERM_RAW) #define KEYPAD (terms->flags & TERM_KPAD) MONITOR (TVGET); /*========================================================================== * tv_gto *==========================================================================*/ int tv_gto(sec) /*++++++++++ .PURPOSE Defines the Time-out .RETURNS Previous time-out .REMARKS --------------*/ int sec; /* IN: The asked time-out (seconds) */ { int old; old = TICK; TICK = MAX(sec, 1); /* At least 1 second... */ return(old); } /*========================================================================== * tv_gaj *==========================================================================*/ static int tv_gaj(ch, index) /*++++++++++ .PURPOSE Modifies low and high limits to match an input sequence. .RETURNS Length of the lower matching sequence; 0 if no matching. .REMARKS For internal use (tv_getc) only. --------------*/ char *ch; /* IN: Character to match */ int index; /* IN: Position of the char in the caplist */ { register char *capl, *capu; register int i; #if DEBUG short int *m; char *cap; TRACE_ED_STR2("Trying to match char ",ch,1); TRACE_ED_I(".... at offset: ", index); for (m = low; m <= high; m++) { cap = *m + CAPLIST; TRACE_ED_STR2("...sequence", cap, 10); } #endif i = index+4; capl = *low + CAPLIST; if(*ch < *(capl+i)) return(0); /* Match impossible */ capu = *high + CAPLIST; #if DEBUG TRACE_ED_STR2("Up sequence is:", capu, 10); TRACE_ED_STR2("Low sequence is:", capl, 10); #endif if (*ch > *(capu+i)) return(0); /* Match impossible */ while (*ch > *(capl+i)) /* Increment the lower limit until match */ capl = *(++low) + CAPLIST; if (*ch != *(capl+i)) return(0); while (*ch < *(capu+i)) /* Decrement the upper limit until match */ capu = *(--high) + CAPLIST; #if DEBUG TRACE_ED_STR2("Low sequence is:", capl, 10); TRACE_ED_STR2("Up sequence is:", capu, 10); #endif if (*ch != *(capu+i)) return(0); return(*capl); } /*========================================================================== * tv_in *==========================================================================*/ static int tv_in() /*++++++++++++ .PURPOSE Read input from terminal .RETURNS Length of read string / EOF .REMARKS While the program is waiting for input, the function defined by tv_fw is called and executed. ---------------*/ { int len, count; TVSAVE saved; ENTER_DEBUG("+tv_in"); for (count = 32767; (buftop == 0) && (count > 0); ) { if (terms->term_type) len = osaread(0, BUFR+buftop, terms->buf_size-buftop); /* Read from File */ else len = ostread(BUFR+buftop, terms->buf_size-buftop, TICK); /* Read Terminal */ if (len) break; if (terms->fct) /* Call fw routine */ { tv_cus(&saved); /* .. save cursor */ count = (*(terms->fct))(count); if (count > 0) tv_cur(&saved); /* .. restore cursor */ } else count -= TICK; } if (len == 0) len = buftop, buftop=0; /* Was got via tv_supply */ if (len < 0) ERROR(osmsg()); else if (terms->term_type) /* Read from File */ { if (len >= terms->buf_size) len = terms->buf_size, ERROR("Input truncated"); else *(BUFR + len++) = '\r'; } TRACE_STR2(BUFR, len); EXIT_DEBUG(len); } /*========================================================================== * tv_inw *==========================================================================*/ static int tv_inw(s) /*+++++++++++ .PURPOSE Read input from terminal with time_out .RETURNS Length / EOF .REMARKS Unique interface to xv_cin -------------*/ int s; /* IN: time-out in seconds */ { register int len; ENTER_DEBUG("+tv_inw"); if (terms->term_type == 0) /* Actual terminal */ { len = ostread(BUFR, terms->buf_size, s); if (len < 0) ERROR(osmsg()); else if (len == 0) len = buftop; /* Was got via tv_supply */ TRACE_STR2(BUFR, len); EXIT_DEBUG(len); } EXIT_DEBUG(tv_in()); /* No terminal */ } /*========================================================================== * tv_getc *==========================================================================*/ int tv_getc(ch) /*+++++++++++++ .PURPOSE Single-character input routine from the terminal; the character is not echoed on the screen. The meaning of the returned ch depends on type returned. .RETURNS EOF / NOK / _STANDARD_ / _ARROW_ / _KEYPAD_ / _PF_ / _FK_ \begin{TeX} \begin{itemize} \item (NOK) if an error occured, or a very long time-out period expired. \item (\_EOF\_) if input file terminated (in batch mode); ch contains the control char (0 in Batch) \item (\_STANDARD\_) for a normal (ASCII printable or control character) \item (\_ARROW\_) for arrows; ch contains the following: \begin{itemize} \item (\_UP\_) up arrow key \item (\_DOWN\_) down arrow key \item (\_RIGHT\_) right arrow key \item (\_LEFT\_) left arrow key \end{itemize} Note that the return is also \_ARROW\_ if some control characters were mapped to cursor movements (see tv\_open) \item (\_KEYPAD\_) for a character from the keypad; ch contains `0' \dots `9' \quad `-' \quad `.' \quad `,' \quad `\b r' (Enter) \item (\_PF\_) for Programmable Function keys: ch contains 1 for PF1, 2 for PF2, etc\dots \item (\_FK\_) for other function keys: ch contains 1 for FK1, etc\dots \end{itemize} \end{TeX} .REMARKS The cursor position remains unchanged. The default char (no input) is null. ------------------------*/ char *ch; /* OUT: Character typed at keyboard */ { register int index, llow; register char *p; int type, seq; ENTER("tv_getc"); #if DEBUG TRACE_ED_STR2("In Buffer: ", BUFR + bufp, buftop-bufp); #endif if (terms->last_type) /* Something already exists */ { *ch = terms->last_char; type = terms->last_type; FINISH; } if(bufp >= buftop ) /* The buffer is empty */ { bufp = 0, buftop = 0; type = tv_in(); if (type < 0) FINISH; /* Nothing more */ buftop += type; type = NOK; if(buftop == 0) FINISH; /* ... still empty! */ } *ch = BUFR[bufp++]; type= _STANDARD_; /* Testing the char; Raw mode does not check the incoming * char. Note that the raw flag is set by the TW_cc_RAW * input character */ if (RAW_MODE) FINISH; /* Look in the Control Char Definitions if the typed char * is mapped to an arrow, the EOF or RAW. If yes, transform type */ index = *(unsigned char *)ch; if (index < 32) { switch(terms->tc[index]) { case TW_cc_RET: *ch = '\r'; FINISH; case TW_cc_EOF: type = EOF; FINISH; case TW_cc_UP: case TW_cc_DOWN: case TW_cc_HOME: case TW_cc_LEFT: case TW_cc_RIGHT: type = _ARROW_; *ch = terms->tc[index] - TW_cc_UP; FINISH; } /* Test if the read char can be the starting byte * of an input sequence: corresponding bit is set to 1 */ if (!(terms->specials & MASK(index))) FINISH; } else FINISH; /* Recognition of an escape sequence: a complete matching * occurs when lower and higher limits of partial match * become identical. Remember that the escape sequence is * coded as (length) (2 chars cap-code) `=' (sequence) */ low = (short int *)(CAPLIST + terms->indexr); high = low + (terms->nsr-1); index = 0; for (seq=0; ;seq++) { llow = tv_gaj(ch,index); index++; if(llow < index) { /* The sequence cannot be understood ... */ p = *low + 4 + CAPLIST; index--; oscopy(BUFS, p, index); *(BUFS + index++) = *ch; ERR_ED_STR2("I can't understand sequence:", BUFS, index); EXIT(NOK); } if ((high == low) && (index == llow)) break; /* Read Next character - if nothing after some delay, assume not an escape sequence */ if (bufp >= buftop ) { bufp = 0, buftop = 0; type = tv_inw(2); if (type < 0) FINISH; buftop += type; if (seq == 0) type = _STANDARD_; if (buftop == 0) /* still empty... */ FINISH; } *ch = BUFR[bufp++]; } /* The sequence was recognized - Set the correct char * and correct type */ p = *low + CAPLIST; /* Address of escape sequence */ *ch = *(p+2); /* 2nd char of capability */ switch (*(p+1)) /* First char of capability */ { case 'k': /* PF or arrow keys */ type = _ARROW_; switch(*ch) { case 'u': *ch = _UP_; break; case 'd': *ch = _DOWN_; break; case 'b': /* in some termcapfiles... */ case 'l': *ch = _LEFT_; break; case 'r': *ch = _RIGHT_; break; case 'h': *ch = _HOME_; break; /* home */ default : type = _PF_; if (isdigit(*ch)) *ch -= '0'; else *ch -= 'A'-10; } break; case 'K': /* Keypad keys */ type = _KEYPAD_; if (*ch == 'E') *ch = '\r'; /* ENTER key */ break; case 'F': /* Other function keys */ type = _FK_; if (isdigit(*ch)) *ch -= '0'; else *ch -= 'A'-10; break; } FIN: terms->last_char = 0; terms->last_type = 0; EXIT (type); } /*==========================================================================*/ int tv_getb(buf, lbuf) /*+++++++++++++ .PURPOSE Raw input mode. Up to lbuf bytes are read, but at least 1. .RETURNS Number of bytes .REMARKS ---------------*/ char *buf; /* OUT: What was typed at the terminal */ int lbuf; /* IN: Max number of bytes */ { char *p, *pe, *pc; int i; char tc[2]; MID_RSTATIC char arrows[] = {'u', 'd', 'l', 'r', 'h'}; ENTER("tv_getb"); p = buf, pe = p + lbuf; if (terms->last_type) /* Something already exists */ { pc = NULL_PTR(char); tc[1] = terms->last_char; switch(terms->last_type) { case _ARROW_: tc[1] = arrows[tc[1]]; case _PF_: tc[0] = 'k'; break; case _KEYPAD_: tc[0] = 'K'; break; case _FK_: tc[0] = 'F'; tc[1] += (tc[1] > 9 ? 'A'-10 : '0'); break; default: pc = &(terms->last_char); } if (pc) *(p++) = *pc; else if (pc = SearchCap(tc)) p += oscopy(p, pc+4, *pc); if(bufp >= buftop ) /* The buffer is empty */ FINISH; } if(bufp >= buftop ) /* The buffer is empty */ { bufp = 0, buftop = 0; if ((i = tv_in()) < 0) FINISH; /* Nothing more */ buftop += i; if(buftop == 0) FINISH; /* ... still empty! */ } i = MIN(pe-p, buftop - bufp); p += oscopy(p, &(BUFR[bufp]), i); bufp += i; FIN: terms->last_char = 0; terms->last_type = 0; EXIT (p - buf); } /*========================================================================== * tv_inc *==========================================================================*/ int tv_inc() /*+++++++++++++ .PURPOSE Check if input characters are waiting. .RETURNS Number of input chars waiting, 0 if none. ---------------*/ { register int n; ENTER("+tv_inc"); n = buftop - bufp; if (n <= 0) n = ostin(); EXIT(n); } /*========================================================================== * tv_push *==========================================================================*/ int tv_push(type, ch) /*+++++++++++++ .PURPOSE Supply (will be the next input) the read character. .RETURNS OK .REMARKS ---------------*/ int type; /* IN: Class of character */ char ch; /* IN: Character in class */ { terms->last_char = ch; terms->last_type = type; return(OK); } /*========================================================================== * tv_supply *==========================================================================*/ int tv_supply(str, len, queue) /*+++++++++++++ .PURPOSE Supply (will be the next input) or queue a response, as if it were read from the terminal. May be used e.g. in the function used while waiting for input (cf tv_fw). .RETURNS OK / NOK (string too long, truncated) .REMARKS A length of zero indicates an EOS-terminated string, and terminate the string with the carriage-return. ---------------*/ char *str; /* IN: String to supply as if read from terminal */ int len; /* IN: Length of String (0 for standard string) */ int queue; /* IN: 0 for top, 1 for queue */ { register int l; char *p; ENTER("tv_supply"); if (len <= 0) len = strlen(str), p = str + len++, *p = '\r'; else p = NULL_PTR(char); TRACE_STR2(str, len); /* Suppress what's already read... */ if (bufp) buftop = oscopy(BUFR, BUFR+bufp, buftop - bufp), bufp = 0; /* If it's a queue, be sure that all type ahead buffer is * exhausted */ if (queue) buftop += tv_in(); l = terms->buf_size - buftop; /* Remaining bytes */ if (len > l) status = NOK, ERROR("Input truncated"); else status = OK , l = len; if (queue) oscopy(BUFR+buftop, str, l); else oscopy(BUFR+l, BUFR, buftop), oscopy(BUFR, str, l); buftop += l; if (p) *p = EOS; EXIT(status); } /*========================================================================== * tv_fw *==========================================================================*/ int tv_fw (f) /*+++ .PURPOSE Specify the function to execute while the terminal is waiting for input from keyboard. .RETURNS OK .REMARKS This entry acts as a declaration. The function f has one parameter, the remaining count in s before the time-out, and should return the modified time-out. ---*/ int (*f)(); /* IN: Function to execute (NULL_FCT to do nothing) */ { ENTER("tv_fw"); TRACE_ED_I("Function: ", (long)f); terms->fct = f; EXIT(OK); }