/***************************************************************************** ReadCS() Read command string. *****************************************************************************/ #if CURSES #if ULTRIX #include /* has to come before zport.h */ #else #include /* has to come before zport.h */ #endif #endif #include "zport.h" /* define portability identifiers */ #include "tecoc.h" /* define general identifiers */ #include "defext.h" /* define external global variables */ #include "dchars.h" /* define identifiers for characters */ #include "chmacs.h" /* define character processing macros */ #include "deferr.h" /* define identifiers for error messages */ #include "dscren.h" /* define identifiers for screen i/o */ #if USE_PROTOTYPES static VVOID InpDel(void); static unsigned char FirstChar(void); static BOOLEAN ChkHlp(charptr HlpEnd); #endif /***************************************************************************** CRCnt maintains a count of the number of carriage returns that have been typed by the user while entering a command string. It is used to recognize when the user executes a HELP command, which is the only TECO command that does not need to be terminated by double escapes. When the very first carriage return in the user's command string is struck, TECO checks if the text preceding the carriage return is a valid HELP command. This checking is only performed when the first carriage return is struck. *****************************************************************************/ static LONG CRCnt; /* carriage-return count */ static charptr Prompt = (charptr)"\n\r*"; #if CURSES extern int skiprefresh; #endif /***************************************************************************** InpDel() This function is called when the user types the DELETE key while typing in a command string. What is displayed on his terminal when this happens depends on what kind of terminal he has and what the deleted character is. This function displays the required character(s). *****************************************************************************/ static VVOID InpDel() { int i; charptr TmpPtr; /* temporary pointer */ DBGFEN(4, "InpDel", NULL); --CBfPtr; /* back up over the DEL */ if (CBfPtr < CBfBeg) { /* if cmd buffer is empty */ ZDspBf(Prompt, 3); /* prompt */ DBGFEX(4, DbgFNm, NULL); return; } if (EtFlag & ET_SCOPE) { /* if it's a scope */ if (((*CBfPtr > USCHAR) && /* if it was ... */ (*CBfPtr < DELETE)) || /* ... printable or ... */ (*CBfPtr == ESCAPE) || /* ... escape or ... */ (*CBfPtr & '\200')) { /* ....eighth bit is on */ ZDspBf("\b \b", 3); /* erase one character */ } else { switch (*CBfPtr) { case LINEFD: ZScrOp(SCR_CUP); /* fall through */ case DELETE: break; case VRTTAB: case FORMFD: for (i = 1; i <= FFLINS; i++) ZScrOp(SCR_CUP); /* fall through */ case TABCHR: case BAKSPC: case CRETRN: ZDspCh(CRETRN); ZScrOp(SCR_EEL); if (*CBfPtr == CRETRN) { --CRCnt; } TmpPtr = CBfPtr; while (TmpPtr > CBfBeg) { TmpPtr--; if (IsEOL(*TmpPtr)) { ++TmpPtr; break; } } if (TmpPtr == CBfBeg) { ZDspCh('*'); } TypBuf(TmpPtr, CBfPtr); break; default: ZDspBf("\b \b\b \b", 6); break; } /* end of switch */ } } else { /* else (it's not a scope) */ EchoIt(*CBfPtr); } --CBfPtr; /* actually delete the char */ DBGFEX(4,DbgFNm,NULL); } /***************************************************************************** FirstChar() This function deals with the following immediate mode commands: ? display previous command string up to erroneous command / display verbose explanation of last error *q copy last command string to q-register q This function is called by ReadCS to read the very first command typed by the user. It handles the above immediate mode commands without destroying the previous command buffer. Once this function returns, the previous command string and error are considered lost by ReadCS, which then overwrites the old command string with a new one. This function does not deal with the LF and BS immediate mode commands because of the following possible sequence of events: the user starts typing a command string, then does a ^U to delete it, then tries to use LF or BS to position himself in the edit buffer. If this function were responsible for LF and BS, then they would not work. ReadCS is responsible for handling the LF and BS immediate mode commands. *****************************************************************************/ static unsigned char FirstChar() { BOOLEAN ImdCmd; /* TRUE if it's '?' or '*' or '/' */ unsigned char LclStr[2]; /* local string for * processing */ DEFAULT Status; /* return value for FindQR() */ charptr TCBfPt; /* temporary holder of CBfPtr */ charptr TCStEn; /* temporary holder of CStEnd */ DEFAULT TmpChr; /* temporary character */ ptrdiff_t TmpSiz; #if CURSES chtype curses_char; int yy; int xx; #endif do { if (EvFlag) { /* if EV (edit verify) flag is set */ DoEvEs(EvFlag); /* handle EV flag */ } #if CURSES if (ScroLn > 0 && !skiprefresh) redraw(); #endif ZDspCh('*'); /* prompt */ #if CURSES /* * This works on a Sun but may be somewhat system dependent - depending on * the type of chtype */ ccs(); keypad_on(); l1: getyx(stdscr,yy,xx); curses_char = ZChIn(FALSE); TmpChr = curses_char; if (curses_char == KEY_RIGHT) { do_right(); move(yy,xx); goto l1; } else if (curses_char == KEY_LEFT) { do_left(); move(yy,xx); goto l1; } else if (curses_char == KEY_DOWN || curses_char == KEY_B2) { do_down(); move(yy,xx); goto l1; } else if (curses_char == KEY_UP) { do_up(); move(yy,xx); goto l1; } else if ((curses_char == KEY_F(1)) || (curses_char == KEY_ENTER)) { do_seetog(); move(yy,xx); goto l1; } else if ((curses_char == KEY_SF) || (curses_char == KEY_NPAGE)) { do_sf(); move(yy,xx); goto l1; } else if ((curses_char == KEY_SR) || (curses_char == KEY_PPAGE)) { do_sr(); move(yy,xx); goto l1; } else if (curses_char >= 0400) { ZDspCh('?'); move(yy,xx); goto l1; } keypad_off(); #else TmpChr = ZChIn(FALSE); /* get a character */ #endif ImdCmd = FALSE; /* assume not an immediate command */ switch (TmpChr) { case DELETE: case CTRL_U: ZDspCh(CRETRN); ImdCmd = TRUE; break; case '/': /* display verbose error message */ if (LstErr != ERR_XXX) { ZDspCh('/'); ErrVrb(LstErr); ImdCmd = TRUE; } break; case '?': /* display erroneous command string */ if (LstErr != ERR_XXX) { /* if there was an error */ ZDspCh('?'); TypESt(); ImdCmd = TRUE; } break; case '*': /* store last cmd string in q-reg */ ZDspCh('*'); /* echo the "*" */ TmpChr = ZChIn(FALSE); /* get a q-register name */ if (GotCtC) { /* user typed control-C? */ GotCtC = FALSE; return CTRL_C; } switch (TmpChr) { case CRETRN: TmpChr = 0; break; case DELETE: case CTRL_U: case BAKSPC: ZDspCh(CRETRN); break; default: EchoIt((unsigned char)TmpChr); TCBfPt = CBfPtr; /* save CBfPtr */ TCStEn = CStEnd; /* save CStEnd */ LclStr[0] = (unsigned char)TmpChr; CBfPtr = &LclStr[0]; if (TmpChr == (DEFAULT)'.') { LclStr[1] = (unsigned char)ZChIn(FALSE); EchoIt((unsigned char)TmpChr); CStEnd = &LclStr[1]; } else { CStEnd = &LclStr[0]; } Status = FindQR(); /* find q-register */ CBfPtr = TCBfPt; /* restore CBfPtr */ CStEnd = TCStEn; /* restore CStEnd */ if (Status == SUCCESS) { if (QR->Start != NULL) { ZFree((charptr)QR->Start); QR->Start = QR->End_P1 = NULL; } TmpSiz = (CStEnd-CStBeg) + 1; if (TmpSiz > 0) { if (MakRom((SIZE_T)TmpSiz) != FAILURE) { MEMMOVE(QR->Start,CStBeg,(SIZE_T)TmpSiz); QR->End_P1 = QR->Start + TmpSiz; } } ZDspBf("\r\n", 2); } } /* end switch */ ImdCmd = TRUE; break; } /* end switch */ } while (ImdCmd); return (unsigned char)TmpChr; } /***************************************************************************** ChkHlp() This function checks if the command string buffer contains a HELP command. If it does, the help command is processed and a TRUE is returned. If the command buffer does not contain a HELP command, a FALSE is returned. A HELP command must be the only command in the command string. It takes the form HELP [/LIBRARY=[=]name] [key-1] [key-2] ... *****************************************************************************/ static BOOLEAN ChkHlp(HlpEnd) /* check for HELP command */ charptr HlpEnd; /* end of help command */ { DBGFEN(3, "ChkHlp", NULL); if ((*CBfBeg != 'H' && *CBfBeg != 'h') || (*(CBfBeg+1) != 'E' && *(CBfBeg+1) != 'e') || (*(CBfBeg+2) != 'L' && *(CBfBeg+2) != 'l') || (*(CBfBeg+3) != 'P' && *(CBfBeg+3) != 'p')) { DBGFEX(3, DbgFNm, "FALSE"); return FALSE; } if ((*(CBfBeg+4) != '/') && (*(CBfBeg+5) != 'S') && (*(CBfBeg+5) != 's')) { ZHelp(CBfBeg+4, HlpEnd-1, FALSE, TRUE); } else { ZHelp(CBfBeg+6, HlpEnd-1, TRUE, TRUE); } DBGFEX(3, DbgFNm, "TRUE"); return TRUE; } /***************************************************************************** ReadCS() This function reads a command string from the terminal. It returns to it's caller when the command buffer pointed to by CBfBeg contains a command string. In addition to echoing the characters typed by the user, this function handles the following special things: 1. DEL delete last character 2. ^G* retype command string 3. ^G retype command line 4. ^G^G delete command string 5. ^U delete command line 6. ^Z^Z^Z exit TECO-C 7. LF immediate mode: do a 1L1T command 8. BS immediate mode: do a -1L1T 9. *q immediate mode: store last command string in q 10. HELP immediate mode: access HELP subsystem 11. / immediate mode: display explanation of last error 12. ? immediate mode: display erroneous command string *****************************************************************************/ #define PRV_CTC 1 /* previous character was control-C */ #define PRV_CTG 2 /* previous character was control-G */ #define PRV_DEF 3 /* previous character wasn't ^Z, ^G or ESC */ #define PRV_ESC 4 /* previous character was escape */ #define PRV_Z1 5 /* previous character was one control-Z */ #define PRV_Z2 6 /* previous character was two control-Z */ VVOID ReadCS() { BOOLEAN BadSeq; /* bad escape sequence indicator */ unsigned char ctrstr[3]; /* temp buffer to display ^chars as string */ LONG HowFar; int PrvChr; /* previous character flags */ unsigned char TmpChr; /* temporary character */ charptr TmpPtr; /* temporary pointer */ int i; ctrstr[0] = '^'; /* caret */ ctrstr[2] = '\0'; /* end-of-string */ CRCnt = 0; /* count of carriage-returns */ PrvChr = PRV_DEF; /* no previous characters */ TmpChr = FirstChar(); /* get first character */ CBfPtr = CBfBeg; /* make command buffer empty */ *CBfPtr = TmpChr; LstErr = ERR_XXX; /* last error message is undefined */ FOREVER { if (*CBfPtr == ESCAPE) { if (EtFlag & ET_VT200) { /* if vt200 mode */ *CBfPtr = (unsigned char)ZChIn(FALSE); BadSeq = FALSE; /* initialize BadSeq */ if (*CBfPtr != '[') { /* if not ESC sequence */ BadSeq = TRUE; } else { *CBfPtr = (unsigned char)ZChIn(FALSE); switch (*CBfPtr) { case '2': /* f9 - f16 */ *CBfPtr = (unsigned char)ZChIn(FALSE); if (*CBfPtr == '4') { TmpChr = (unsigned char)ZChIn(FALSE); if (TmpChr != '~') { BadSeq = TRUE; } else { *CBfPtr = BAKSPC; } } else if (*CBfPtr == '5') { TmpChr = (unsigned char)ZChIn(FALSE); if (TmpChr != '~') { BadSeq = TRUE; } else { *CBfPtr = LINEFD; } } else { BadSeq = TRUE; } break; #if FALSE case 'A': /* up arrow */ case 'B': /* down arrow */ case 'C': /* right arrow */ case 'D': /* left arrow */ #endif default: BadSeq = TRUE; } /* end of switch */ } /* */ if (BadSeq) { ZDspBf("\r\n\n\tInvalid escape sequence.\r\n\n", 31); ZDspBf("\tThe 16384 bit of the ET flag is set, which means\r\n", 52); ZDspBf("\tthat you are in VT200 mode. In this mode, the\r\n", 50); ZDspBf("\tescape character is not used to terminate commands.\r\n", 54); ZDspBf("\tIt is used to introduce escape sequences. This\r\n", 50); ZDspBf("\tallows the function keys to take on meanings. The\r\n", 53); ZDspBf("\taccent grave (~) character is the command terminator.\r\n", 56); ZDspBf("\tIf you want to turn off VT200 mode, say 16384,0ET``\r\n", 55); ZDspBf("\tNote that the recognition of accent grave as a\r\n", 49); ZDspBf("\tcommand terminator is controlled by the 8192 bit\r\n", 51); ZDspBf("\tof the ET flag, separate from the VT200 bit.\r\n\n", 49); ZDspBf("\tThere may be a part of the unrecognized escape\r\n", 49); ZDspBf("\tsequence in the command string. The last line of\r\n", 52); ZDspBf("\tthe command string is shown to help you recover.\r\n", 51); PrvChr = PRV_CTG; CBfPtr++; *CBfPtr = ' '; } /* end if BadSeq) */ } else { /* else not vt200 mode */ ZDspCh('$'); if (PrvChr == PRV_ESC) { ZDspBf("\r\n", 2); CStEnd = CBfPtr; return; } PrvChr = PRV_ESC; } } else { /* else *CBfPtr != ESCAPE */ switch (ChrMsk[(unsigned char)*CBfPtr] & '\17') { case RCS_LWR: /* lowercase */ if ((EtFlag & ET_READ_LOWER) == 0) { *CBfPtr &= '\137'; /* convert to uppercase */ } /* fall through */ case RCS_DEF: /* default case */ ZDspCh(*CBfPtr); /* echo the character */ PrvChr = PRV_DEF; break; case RCS_GRV: /* accent-grave */ ZDspCh('`'); if (EtFlag & ET_ACCENT_GRAVE) { *CBfPtr = ESCAPE; if (PrvChr == PRV_ESC) { ZDspBf("\r\n", 2); CStEnd = CBfPtr; return; } PrvChr = PRV_ESC; } else { PrvChr = PRV_DEF; } break; case RCS_SP: /* space */ if (PrvChr == PRV_CTG) { /* if previous char was ^G */ CBfPtr--; /* remove the space */ TmpPtr = CBfPtr; while (TmpPtr > CBfBeg) { TmpPtr--; if (IsEOL(*TmpPtr)) { ++TmpPtr; break; } } ZDspBf("\r\n", 2); if (TmpPtr == CBfBeg) { ZDspCh('*'); } TypBuf(TmpPtr, CBfPtr); CBfPtr--; /* remove ^G */ } else { /* previous char was not ^G */ ZDspCh(SPACE); /* just echo space */ } PrvChr = PRV_DEF; break; case RCS_DEL: /* delete */ InpDel(); PrvChr = PRV_DEF; break; case RCS_CR: /* carriage return */ ZDspCh(CRETRN); if ((CRCnt == 0) && ChkHlp(CBfPtr)) { ZDspCh('*'); CBfPtr = CBfBeg - 1; } else { ++CRCnt; } PrvChr = PRV_DEF; break; case RCS_LF: /* line feed */ if (CBfPtr == CBfBeg) { /* if immediate mode */ #if CURSES if (ScroLn == 0 || skiprefresh) #endif if (EtFlag & ET_SCOPE) { ZDspCh(CRETRN); ZScrOp(SCR_EEL); } HowFar = Ln2Chr((LONG)1); MEMMOVE(GapBeg, GapEnd+1, (SIZE_T)HowFar); GapBeg += HowFar; GapEnd += HowFar; if (EvFlag) { DoEvEs(EvFlag); } else { #if CURSES if (ScroLn == 0 || skiprefresh) #endif TypBuf(GapEnd+1, GapEnd+Ln2Chr((LONG)1)+1); } #if CURSES if (ScroLn > 0 || skiprefresh) dolf(HowFar); if (ScroLn == 0 || skiprefresh) #endif ZDspCh('*'); #if CURSES clrtoeol(); #endif CBfPtr = CBfBeg - 1; } else { ZDspCh(LINEFD); } PrvChr = PRV_DEF; break; case RCS_CTC: /* control-C */ GotCtC = FALSE; /* clear "stop soon" flag */ ctrstr[1] = 'C'; ZDspBf(ctrstr, 2); /* display "^C" */ if (PrvChr == PRV_CTC) { /* if second control-C */ TAbort(EXIT_SUCCESS); /* terminate */ } else { PrvChr = PRV_CTC; } ZDspBf(Prompt, 3); /* prompt */ CBfPtr = CBfBeg - 1; /* reset cmd-buffer pointer */ CRCnt = 0; /* carriage-return cnt = 0 */ break; case RCS_BS: /* backspace */ if (CBfPtr == CBfBeg) { /* if immediate mode */ #if CURSES if (ScroLn == 0 || skiprefresh) #endif if (EtFlag & ET_SCOPE) { ZDspCh(CRETRN); ZScrOp(SCR_EEL); } HowFar = Ln2Chr(-1L); GapBeg += HowFar; GapEnd += HowFar; MEMMOVE(GapEnd+1, GapBeg, (SIZE_T)-HowFar); if (EvFlag) { DoEvEs(EvFlag); } else { #if CURSES if (ScroLn == 0 || skiprefresh) #endif TypBuf(GapEnd+1, GapEnd+Ln2Chr((LONG)1)+1); } #if CURSES if (ScroLn > 0 && !skiprefresh) dobs(HowFar); if (ScroLn == 0 || skiprefresh) #endif ZDspCh('*'); #if CURSES clrtoeol(); #endif CBfPtr = CBfBeg - 1; } else { ZDspCh(BAKSPC); } PrvChr = PRV_DEF; break; case RCS_CTG: /* control-G */ ZBell(); /* ring the bell (or flash) */ ZDspBf("^G", 2); if (PrvChr == PRV_CTG) { /* if previous char was ^G */ CBfPtr = CBfBeg - 1; ZDspBf(Prompt, 3); CRCnt = 0; PrvChr = PRV_DEF; } else { PrvChr = PRV_CTG; } break; case RCS_AST: /* asterisk */ ZDspCh('*'); if (PrvChr == PRV_CTG) { CBfPtr -= 2; ZDspBf(Prompt, 3); CRCnt = 0; TypBuf(CBfBeg, CBfPtr); } PrvChr = PRV_DEF; break; case RCS_CTZ: /* control-Z */ ctrstr[1] = 'Z'; ZDspBf(ctrstr, 2); if (PrvChr == PRV_Z2) { TAbort(EXIT_SUCCESS); } else if (PrvChr == PRV_Z1) { PrvChr = PRV_Z2; } else { PrvChr = PRV_Z1; } break; case RCS_CCH: /* other control char */ #if CURSES if (*CBfPtr == CTRL_W) { if (CBfPtr == CBfBeg) { if (ScroLn > 0) { centre(); } else { ZDspBf(Prompt, 3); } clrtoeol(); CBfPtr = CBfBeg - 1; } else { ZDspBf("^W", 2); } PrvChr = PRV_DEF; break; } #endif ctrstr[1] = *CBfPtr | '\100'; ZDspBf(ctrstr, 2); PrvChr = PRV_DEF; break; case RCS_CTU: /* control-U */ while (--CBfPtr >= CBfBeg) { if (IsEOL(*CBfPtr)) { break; } } ZDspCh(CRETRN); if (EtFlag & ET_SCOPE) { ZScrOp(SCR_EEL); /* erase line */ } else { ZDspCh(LINEFD); } if (CBfPtr < CBfBeg) { ZDspCh('*'); } PrvChr = PRV_DEF; break; case RCS_VF: /* vert. tab or form feed */ ZDspCh(CRETRN); for (i = 1; i <= FFLINS; i++) { ZDspCh(LINEFD); } PrvChr = PRV_DEF; break; } /* end of switch */ } if (++CBfPtr > CBfEnd) { ZDspBf("command buffer overflow\r\n", 25); /* ??? */ TAbort(EXIT_FAILURE); /* exit TECO-C */ } *CBfPtr = (unsigned char)ZChIn(FALSE); /* read a character */ } /* end of FOREVER loop */ }