2.11BSD/src/bin/tcsh/ed.inputl.c
/* $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 */
/* <rsalz@pineapple.bbn.com> */
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 <tale@pawl.rpi.edu>: 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;
}