/* $Header: /home/hyperion/mu/christos/src/sys/tcsh-6.00/RCS/ed.inputl.c,v 3.0 1991/07/04 21:49:28 christos Exp $ */ /* * ed.inputl.c: Input line handling. */ /*- * Copyright (c) 1980, 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(pdp11) static char *rcsid() { return "$Id: ed.inputl.c,v 3.0 1991/07/04 21:49:28 christos Exp $"; } #endif #include "sh.h" #include "ed.h" #include "ed.defns.h" /* for the function names */ #include "tw.h" /* for twenex stuff */ #define OKCMD (INBUFSIZ+INBUFSIZ) extern CCRETVAL e_up_hist(); extern CCRETVAL e_ex_history(); /* ed.inputl -- routines to get a single line from the input. */ extern bool tellwhat; extern bool MapsAreInited; extern bool Tty_raw_mode; /* mismatched first character */ static Char mismatch[] = {'!', '\\', '^', '-', '%', '\0'}; static int G_N_Command __P((KEYCMD *, Char *)); static int SpellLine __P((int)); /* CCRETVAL */ int Inputl() { CCRETVAL retval; KEYCMD cmdnum = 0; extern KEYCMD NumFuns; unsigned char tch; /* the place where read() goes */ Char ch; int num; /* how many chars we have read at NL */ struct varent *crct = adrof(STRcorrect); struct varent *matchbeep = adrof(STRmatchbeep); Char *SaveChar, *CorrChar; Char Origin[INBUFSIZ], Change[INBUFSIZ]; int matchval; /* from tenematch() */ if (!MapsAreInited) /* double extra just in case */ ed_IMaps(); ClearDisp(); /* reset the display stuff */ ResetInLine(); /* reset the input pointers */ if (GettingInput) MacroLvl = -1; /* editor was interrupted during input */ #ifdef FIONREAD if (!Tty_raw_mode && MacroLvl < 0) { long chrs = 0; (void) ioctl(SHIN, FIONREAD, (ioctl_t) & chrs); if (chrs == 0) { if (Rawmode() < 0) return 0; } } #endif GettingInput = 1; NeedsRedraw = 0; if (tellwhat) { copyn(InputBuf, WhichBuf, INBUFSIZ); LastChar = InputBuf + (LastWhich - WhichBuf); Cursor = InputBuf + (CursWhich - WhichBuf); tellwhat = 0; Hist_num = HistWhich; } if (Expand) { (void) e_up_hist(); Expand = 0; } Refresh(); /* print the prompt */ for (num = OKCMD; num == OKCMD;) { /* while still editing this line */ #ifdef DEBUG_EDIT if (Cursor > LastChar) xprintf("Cursor > LastChar\r\n"); if (Cursor < InputBuf) xprintf("Cursor < InputBuf\r\n"); if (Cursor > InputLim) xprintf("Cursor > InputLim\r\n"); if (LastChar > InputLim) xprintf("LastChar > InputLim\r\n"); if (InputLim != &InputBuf[INBUFSIZ - 2]) xprintf("InputLim != &InputBuf[INBUFSIZ-2]\r\n"); if ((!DoingArg) && (Argument != 1)) xprintf("(!DoingArg) && (Argument != 1)\r\n"); if (CcKeyMap[0] == 0) xprintf("CcKeyMap[0] == 0 (maybe not inited)\r\n"); #endif /* if EOF or error */ if ((num = G_N_Command(&cmdnum, &ch)) != OKCMD) { break; } if (cmdnum >= NumFuns) {/* BUG CHECK command */ #ifdef DEBUG_EDIT xprintf("ERROR: illegal command from key 0%o\r\n", ch); #endif continue; /* try again */ } /* now do the real command */ retval = (*CcFuncTbl[cmdnum]) (ch); /* save the last command here */ LastCmd = cmdnum; /* use any return value */ switch (retval) { case CC_REFRESH: Refresh(); /* fall through */ case CC_NORM: /* normal char */ Argument = 1; DoingArg = 0; /* fall through */ case CC_ARGHACK: /* Suggested by Rich Salz */ /* */ break; /* keep going... */ case CC_EOF: /* end of file typed */ #ifdef notdef PromptBuf[0] = '\0'; #endif num = 0; break; case CC_WHICH: /* tell what this command does */ tellwhat = 1; copyn(WhichBuf, InputBuf, INBUFSIZ); LastWhich = WhichBuf + (LastChar - InputBuf); CursWhich = WhichBuf + (Cursor - InputBuf); *LastChar++ = '\n'; /* for the benifit of CSH */ HistWhich = Hist_num; Hist_num = 0; /* for the history commands */ num = LastChar - InputBuf; /* number characters read */ #ifdef notdef ResetInLine(); /* reset the input pointers */ #endif break; case CC_NEWLINE: /* normal end of line */ if (crct && (!Strcmp(*(crct->vec), STRcmd) || !Strcmp(*(crct->vec), STRall))) { (void) Strcpy(Origin, InputBuf); SaveChar = LastChar; if (SpellLine(!Strcmp(*(crct->vec), STRcmd)) == 1) { (void) Strcpy(Change, InputBuf); *Strchr(Change, '\n') = '\0'; CorrChar = LastChar; /* Save the corrected end */ LastChar = InputBuf; /* Null the current line */ Beep(); printprompt(2, Change); Refresh(); (void) read(SHIN, (char *) &tch, 1); ch = tch; if (ch != 'y' && ch != ' ') { (void) Strcpy(InputBuf, Origin); LastChar = SaveChar; xprintf("no\n"); } else { LastChar = CorrChar; /* Restore the corrected end */ xprintf("yes\n"); } flush(); } } /* end CORRECT code */ tellwhat = 0; /* just in case */ Hist_num = 0; /* for the history commands */ num = LastChar - InputBuf; /* return the number of chars read */ /* * For continuation lines, we set the prompt to prompt 2 */ printprompt(1, NULL); #ifdef notdef ResetInLine(); /* reset the input pointers */ PromptBuf[0] = '\0'; #endif break; case CC_CORRECT: if (tenematch(InputBuf, INBUFSIZ, Cursor - InputBuf, SPELL) < 0) Beep(); /* Beep = No match/ambiguous */ if (NeedsRedraw) { ClearLines(); ClearDisp(); NeedsRedraw = 0; } Refresh(); Argument = 1; DoingArg = 0; break; case CC_CORRECT_L: if (SpellLine(FALSE) < 0) Beep(); /* Beep = No match/ambiguous */ if (NeedsRedraw) { ClearLines(); ClearDisp(); NeedsRedraw = 0; } Refresh(); Argument = 1; DoingArg = 0; break; case CC_COMPLETE: if (adrof(STRa_expand)) (void) e_ex_history(); /* * Modified by Martin Boyer (gamin@ireq-robot.hydro.qc.ca): * A separate variable now controls beeping after * completion, independently of autolisting. */ switch (matchval = tenematch(InputBuf, INBUFSIZ, Cursor-InputBuf, RECOGNIZE)) { case 1: if (non_unique_match && matchbeep && (Strcmp(*(matchbeep->vec), STRnotunique) == 0)) Beep(); break; case 0: if (matchbeep) { if (Strcmp(*(matchbeep->vec), STRnomatch) == 0 || Strcmp(*(matchbeep->vec), STRambiguous) == 0 || Strcmp(*(matchbeep->vec), STRnotunique) == 0) Beep(); } else Beep(); break; default: if (matchval < 0) /* Error from tenematch */ Beep(); else if (matchbeep) { if ((Strcmp(*(matchbeep->vec), STRambiguous) == 0 || Strcmp(*(matchbeep->vec), STRnotunique) == 0)) Beep(); } else Beep(); /* * Addition by David C Lawrence : If an * attempted completion is ambiguous, list the choices. * (PWP: this is the best feature addition to tcsh I have * seen in many months.) */ if (adrof(STRa_list)) { PastBottom(); (void) tenematch(InputBuf, INBUFSIZ, Cursor-InputBuf, LIST); } break; } if (NeedsRedraw) { PastBottom(); ClearLines(); ClearDisp(); NeedsRedraw = 0; } Refresh(); Argument = 1; DoingArg = 0; break; case CC_LIST_CHOICES: /* should catch ^C here... */ (void) tenematch(InputBuf, INBUFSIZ, Cursor - InputBuf, LIST); Refresh(); Argument = 1; DoingArg = 0; break; case CC_LIST_GLOB: (void) tenematch(InputBuf, INBUFSIZ, Cursor - InputBuf, GLOB); Refresh(); Argument = 1; DoingArg = 0; break; case CC_EXPAND_GLOB: if (tenematch(InputBuf, INBUFSIZ, Cursor - InputBuf, GLOB_EXPAND) <= 0) Beep(); /* Beep = No match */ if (NeedsRedraw) { ClearLines(); ClearDisp(); NeedsRedraw = 0; } Refresh(); Argument = 1; DoingArg = 0; break; case CC_EXPAND_VARS: if (tenematch(InputBuf, INBUFSIZ, Cursor - InputBuf, VARS_EXPAND) <= 0) Beep(); /* Beep = No match */ if (NeedsRedraw) { ClearLines(); ClearDisp(); NeedsRedraw = 0; } Refresh(); Argument = 1; DoingArg = 0; break; case CC_HELPME: xputchar('\n'); /* should catch ^C here... */ (void) tenematch(InputBuf, INBUFSIZ, LastChar - InputBuf, PRINT_HELP); Refresh(); Argument = 1; DoingArg = 0; break; case CC_FATAL: /* fatal error, reset to known state */ #ifdef DEBUG_EDIT xprintf("*** editor fatal ERROR ***\r\n\n"); #endif /* DEBUG_EDIT */ /* put (real) cursor in a known place */ ClearDisp(); /* reset the display stuff */ ResetInLine(); /* reset the input pointers */ Refresh(); /* print the prompt again */ Argument = 1; DoingArg = 0; break; case CC_ERROR: default: /* functions we don't know about */ DoingArg = 0; Argument = 1; Beep(); flush(); break; } } (void) Cookedmode(); /* make sure the tty is set up correctly */ GettingInput = 0; flush(); /* flush any buffered output */ return num; } void PushMacro(str) Char *str; { if (str != NULL && MacroLvl + 1 < MAXMACROLEVELS) { MacroLvl++; KeyMacro[MacroLvl] = str; } else { Beep(); flush(); } } static int G_N_Command(cmdnum, ch) KEYCMD *cmdnum; register Char *ch; { KEYCMD cmd = 0; int num; Char *str; for (; cmd == 0 || cmd == F_XKEY;) { if ((num = G_N_Char(ch)) != 1) { /* if EOF or error */ return num; } #ifdef KANJI if (*ch & META) { MetaNext = 0; cmd = CcViMap[' ']; break; } else #endif /* KANJI */ if (MetaNext) { MetaNext = 0; *ch |= META; } cmd = Cur_KeyMap[(unsigned char) *ch]; if (cmd == F_XKEY) { if (GetXkey(ch, &str)) cmd = (KEYCMD) * str; else PushMacro(str); } if (!AltKeyMap) { Cur_KeyMap = CcKeyMap; } } *cmdnum = cmd; return OKCMD; } int G_N_Char(cp) register Char *cp; { register int num_read; #if defined(EWOULDBLOCK) || (defined(POSIX) && defined(EAGAIN)) # if defined(FIONBIO) || (defined(F_SETFL) && defined(O_NDELAY)) # define TRY_AGAIN int tried = 0; # endif /* FIONBIO || (F_SETFL && O_NDELAY) */ #endif /* EWOULDBLOCK || (POSIX && EAGAIN) */ unsigned char tcp; for (;;) { if (MacroLvl < 0) { if (!Load_input_line()) break; } if (*KeyMacro[MacroLvl] == 0) { MacroLvl--; continue; } *cp = *KeyMacro[MacroLvl]++ & CHAR; if (*KeyMacro[MacroLvl] == 0) { /* Needed for QMode On */ MacroLvl--; } return (1); } if (Rawmode() < 0) /* make sure the tty is set up correctly */ return 0; /* oops: SHIN was closed */ while ((num_read = read(SHIN, (char *) &tcp, 1)) == -1) switch (errno) { /* * Someone might have set our file descriptor to non blocking From * Gray Watson (gray%antr.uucp@med.pitt.edu), Thanks!!! */ #ifdef EWOULDBLOCK case EWOULDBLOCK: #endif /* EWOULDBLOCK */ #if defined(POSIX) && defined(EAGAIN) # if defined(EWOULDBLOCK) && EAGAIN != EWOULDBLOCK case EAGAIN: # endif /* EWOULDBLOCK && EAGAIN != EWOULDBLOCK */ #endif /* POSIX && EAGAIN */ #ifdef TRY_AGAIN if (!tried) { # if defined(F_SETFL) && defined(O_NDELAY) (void) fcntl(SHIN, F_SETFL, fcntl(SHIN, F_GETFL, 0) & ~O_NDELAY); # endif /* F_SETFL && O_NDELAY */ # ifdef FIONBIO (void) ioctl(SHIN, FIONBIO, (ioctl_t) & tried); # endif /* FIONBIO */ tried = 1; break; } #endif *cp = tcp; return (num_read); #ifdef _SEQUENT_ case EBADF: #endif /* _SEQUENT_ */ case EINTR: break; default: xprintf("G_N_Char(): errno == %d\n", errno); *cp = tcp; return num_read; } *cp = tcp; return num_read; } /* * SpellLine - do spelling correction on the entire command line * (which may have trailing newline). * If cmdonly is set, only check spelling of command words. * Return value: * -1: Something was incorrectible, and nothing was corrected * 0: Everything was correct * 1: Something was corrected */ static int SpellLine(cmdonly) int cmdonly; { int endflag, matchval; Char *argptr, *OldCursor, *OldLastChar; OldLastChar = LastChar; OldCursor = Cursor; argptr = InputBuf; endflag = 1; matchval = 0; do { while (ismeta(*argptr) || iscmdmeta(*argptr)) argptr++; for (Cursor = argptr; *Cursor != '\0' && !ismeta(*Cursor) && !iscmdmeta(*Cursor); Cursor++); if (*Cursor == '\0') { Cursor = LastChar; if (LastChar[-1] == '\n') Cursor--; endflag = 0; } if (!Strchr(mismatch, *argptr) && (!cmdonly || starting_a_command(argptr, InputBuf))) { switch (tenematch(InputBuf, INBUFSIZ, Cursor - InputBuf, SPELL)) { case 1: /* corrected */ matchval = 1; break; case -1: /* couldn't be corrected */ if (!matchval) matchval = -1; break; default: /* was correct */ break; } if (LastChar != OldLastChar) { if (argptr < OldCursor) OldCursor += (LastChar - OldLastChar); OldLastChar = LastChar; } } argptr = Cursor; } while (endflag); Cursor = OldCursor; return matchval; }