2
* ed.inputl.c: Input line handling.
5
* Copyright (c) 1980, 1991 The Regents of the University of California.
8
* Redistribution and use in source and binary forms, with or without
9
* modification, are permitted provided that the following conditions
11
* 1. Redistributions of source code must retain the above copyright
12
* notice, this list of conditions and the following disclaimer.
13
* 2. Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimer in the
15
* documentation and/or other materials provided with the distribution.
16
* 3. All advertising materials mentioning features or use of this software
17
* must display the following acknowledgement:
18
* This product includes software developed by the University of
19
* California, Berkeley and its contributors.
20
* 4. Neither the name of the University nor the names of its contributors
21
* may be used to endorse or promote products derived from this software
22
* without specific prior written permission.
24
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41
#include "ed.defns.h" /* for the function names */
42
#include "tw.h" /* for twenex stuff */
44
#define OKCMD (INBUFSIZE+INBUFSIZE)
46
/* ed.inputl -- routines to get a single line from the input. */
49
extern bool MapsAreInited;
50
extern bool Tty_raw_mode;
52
/* mismatched first character */
53
static Char mismatch[] =
54
{'!', '^' , '\\', '-', '%', '\0', '"', '\'', '`', '\0' };
56
static int Repair __P((void));
57
static int GetNextCommand __P((KEYCMD *, Char *));
58
static int SpellLine __P((int));
59
static int CompleteLine __P((void));
60
static void RunCommand __P((Char *));
61
static void doeval1 __P((Char **));
63
static bool rotate = 0;
78
return (int) (LastChar - InputBuf);
87
extern KEYCMD NumFuns;
88
unsigned char tch; /* the place where read() goes */
90
int num; /* how many chars we have read at NL */
92
struct varent *crct = inheredoc ? NULL : adrof(STRcorrect);
93
struct varent *autol = adrof(STRautolist);
94
struct varent *matchbeep = adrof(STRmatchbeep);
95
struct varent *imode = adrof(STRinputmode);
96
Char *SaveChar, *CorrChar;
97
Char Origin[INBUFSIZE], Change[INBUFSIZE];
98
int matchval; /* from tenematch() */
104
if (!MapsAreInited) /* double extra just in case */
107
ClearDisp(); /* reset the display stuff */
108
ResetInLine(0); /* reset the input pointers */
110
MacroLvl = -1; /* editor was interrupted during input */
113
if (!Strcmp(*(imode->vec), STRinsert))
114
inputmode = MODE_INSERT;
115
else if (!Strcmp(*(imode->vec), STRoverwrite))
116
inputmode = MODE_REPLACE;
119
#if defined(FIONREAD) && !defined(OREO)
120
if (!Tty_raw_mode && MacroLvl < 0) {
125
* *Everyone* else has an int, but SunOS wants long!
126
* This breaks where int != long (alpha)
131
(void) ioctl(SHIN, FIONREAD, (ioctl_t) & chrs);
137
#endif /* FIONREAD && !OREO */
143
copyn(InputBuf, WhichBuf, INBUFSIZE);
144
LastChar = InputBuf + (LastWhich - WhichBuf);
145
Cursor = InputBuf + (CursWhich - WhichBuf);
147
Hist_num = HistWhich;
153
Refresh(); /* print the prompt */
155
for (num = OKCMD; num == OKCMD;) { /* while still editing this line */
157
if (Cursor > LastChar)
158
xprintf("Cursor > LastChar\r\n");
159
if (Cursor < InputBuf)
160
xprintf("Cursor < InputBuf\r\n");
161
if (Cursor > InputLim)
162
xprintf("Cursor > InputLim\r\n");
163
if (LastChar > InputLim)
164
xprintf("LastChar > InputLim\r\n");
165
if (InputLim != &InputBuf[INBUFSIZE - 2])
166
xprintf("InputLim != &InputBuf[INBUFSIZE-2]\r\n");
167
if ((!DoingArg) && (Argument != 1))
168
xprintf("(!DoingArg) && (Argument != 1)\r\n");
169
if (CcKeyMap[0] == 0)
170
xprintf("CcKeyMap[0] == 0 (maybe not inited)\r\n");
173
/* if EOF or error */
174
if ((num = GetNextCommand(&cmdnum, &ch)) != OKCMD) {
178
if (cmdnum >= NumFuns) {/* BUG CHECK command */
180
xprintf(CGETS(6, 1, "ERROR: illegal command from key 0%o\r\n"), ch);
182
continue; /* try again */
185
/* now do the real command */
186
retval = (*CcFuncTbl[cmdnum]) (ch);
188
/* save the last command here */
191
/* make sure fn is initialized */
192
fn = (retval == CC_COMPLETE_ALL) ? LIST_ALL : LIST;
194
/* use any return value */
200
case CC_NORM: /* normal char */
204
case CC_ARGHACK: /* Suggested by Rich Salz */
205
/* <rsalz@pineapple.bbn.com> */
207
curlen = (int) (LastChar - InputBuf);
208
break; /* keep going... */
210
case CC_EOF: /* end of file typed */
212
curlen = (int) (LastChar - InputBuf);
216
case CC_WHICH: /* tell what this command does */
218
copyn(WhichBuf, InputBuf, INBUFSIZE);
219
LastWhich = WhichBuf + (LastChar - InputBuf);
220
CursWhich = WhichBuf + (Cursor - InputBuf);
221
*LastChar++ = '\n'; /* for the benifit of CSH */
222
HistWhich = Hist_num;
223
Hist_num = 0; /* for the history commands */
224
num = (int) (LastChar - InputBuf); /* number characters read */
227
case CC_NEWLINE: /* normal end of line */
231
if (crct && (!Strcmp(*(crct->vec), STRcmd) ||
232
!Strcmp(*(crct->vec), STRall))) {
234
copyn(Origin, InputBuf, INBUFSIZE);
236
if (SpellLine(!Strcmp(*(crct->vec), STRcmd)) == 1) {
238
copyn(Change, InputBuf, INBUFSIZE);
239
*Strchr(Change, '\n') = '\0';
240
CorrChar = LastChar; /* Save the corrected end */
241
LastChar = InputBuf; /* Null the current line */
243
printprompt(2, short2str(Change));
245
if (read(SHIN, (char *) &tch, 1) < 0)
248
* need to print error message in case file
251
if (errno && errno != EINTR)
252
stderror(ERR_SYSTEM, progname, strerror(errno));
257
if (ch == 'y' || ch == ' ') {
258
LastChar = CorrChar; /* Restore the corrected end */
259
xprintf(CGETS(6, 2, "yes\n"));
262
copyn(InputBuf, Origin, INBUFSIZE);
265
xprintf(CGETS(6, 3, "edit\n"));
268
printprompt(3, NULL);
274
else if (ch == 'a') {
275
xprintf(CGETS(6, 4, "abort\n"));
276
LastChar = InputBuf; /* Null the current line */
278
printprompt(0, NULL);
282
xprintf(CGETS(6, 5, "no\n"));
286
} else if (crct && !Strcmp(*(crct->vec), STRcomplete)) {
287
if (LastChar > InputBuf && LastChar[-1] == '\n') {
292
match_unique_match = 1; /* match unique matches */
293
matchval = CompleteLine();
294
match_unique_match = 0;
295
curlen = (int) (LastChar - InputBuf);
300
xprintf(CGETS(6, 6, "No matching command\n"));
301
} else if (matchval == 2) {
302
xprintf(CGETS(6, 7, "Ambiguous command\n"));
317
curlen = (int) (LastChar - InputBuf);
323
tellwhat = 0; /* just in case */
324
Hist_num = 0; /* for the history commands */
325
/* return the number of chars read */
326
num = (int) (LastChar - InputBuf);
328
* For continuation lines, we set the prompt to prompt 2
330
printprompt(1, NULL);
335
if (tenematch(InputBuf, Cursor - InputBuf, SPELL) < 0)
336
SoundBeep(); /* Beep = No match/ambiguous */
341
if (SpellLine(FALSE) < 0)
342
SoundBeep(); /* Beep = No match/ambiguous */
348
case CC_COMPLETE_ALL:
349
case CC_COMPLETE_FWD:
350
case CC_COMPLETE_BACK:
354
curlen = (int) (LastChar - InputBuf);
358
case CC_COMPLETE_ALL:
360
curlen = (int) (LastChar - InputBuf);
364
case CC_COMPLETE_FWD:
365
fn = RECOGNIZE_SCROLL;
369
case CC_COMPLETE_BACK:
370
fn = RECOGNIZE_SCROLL;
377
if (InputBuf[curlen] && rotate) {
378
newlen = (int) (LastChar - InputBuf);
379
for (idx = (int) (Cursor - InputBuf);
380
idx <= newlen; idx++)
381
InputBuf[idx - newlen + curlen] =
383
LastChar = InputBuf + curlen;
384
Cursor = Cursor - newlen + curlen;
386
curlen = (int) (LastChar - InputBuf);
389
if (adrof(STRautoexpand))
390
(void) e_expand_history(0);
392
* Modified by Martin Boyer (gamin@ireq-robot.hydro.qc.ca):
393
* A separate variable now controls beeping after
394
* completion, independently of autolisting.
396
expnum = (int) (Cursor - InputBuf);
397
switch (matchval = tenematch(InputBuf, Cursor-InputBuf, fn)){
399
if (non_unique_match && matchbeep &&
400
(Strcmp(*(matchbeep->vec), STRnotunique) == 0))
405
if (Strcmp(*(matchbeep->vec), STRnomatch) == 0 ||
406
Strcmp(*(matchbeep->vec), STRambiguous) == 0 ||
407
Strcmp(*(matchbeep->vec), STRnotunique) == 0)
414
if (matchval < 0) { /* Error from tenematch */
420
if ((Strcmp(*(matchbeep->vec), STRambiguous) == 0 ||
421
Strcmp(*(matchbeep->vec), STRnotunique) == 0))
427
* Addition by David C Lawrence <tale@pawl.rpi.edu>: If an
428
* attempted completion is ambiguous, list the choices.
429
* (PWP: this is the best feature addition to tcsh I have
430
* seen in many months.)
432
if (autol && (Strcmp(*(autol->vec), STRambiguous) != 0 ||
433
expnum == Cursor - InputBuf)) {
435
fn = (retval == CC_COMPLETE_ALL) ? LIST_ALL : LIST;
436
(void) tenematch(InputBuf, Cursor-InputBuf, fn);
451
case CC_LIST_CHOICES:
453
if (InputBuf[curlen] && rotate) {
454
newlen = (int) (LastChar - InputBuf);
455
for (idx = (int) (Cursor - InputBuf);
456
idx <= newlen; idx++)
457
InputBuf[idx - newlen + curlen] =
459
LastChar = InputBuf + curlen;
460
Cursor = Cursor - newlen + curlen;
462
curlen = (int) (LastChar - InputBuf);
466
fn = (retval == CC_LIST_ALL) ? LIST_ALL : LIST;
467
/* should catch ^C here... */
468
if (tenematch(InputBuf, Cursor - InputBuf, fn) < 0)
477
if (tenematch(InputBuf, Cursor - InputBuf, GLOB) < 0)
483
if (tenematch(InputBuf, Cursor - InputBuf, GLOB_EXPAND) <= 0)
484
SoundBeep(); /* Beep = No match */
488
case CC_NORMALIZE_PATH:
489
if (tenematch(InputBuf, Cursor - InputBuf, PATH_NORMALIZE) <= 0)
490
SoundBeep(); /* Beep = No match */
495
if (tenematch(InputBuf, Cursor - InputBuf, VARS_EXPAND) <= 0)
496
SoundBeep(); /* Beep = No match */
500
case CC_NORMALIZE_COMMAND:
501
if (tenematch(InputBuf, Cursor - InputBuf, COMMAND_NORMALIZE) <= 0)
502
SoundBeep(); /* Beep = No match */
508
/* should catch ^C here... */
509
(void) tenematch(InputBuf, LastChar - InputBuf, PRINT_HELP);
514
curlen = (int) (LastChar - InputBuf);
517
case CC_FATAL: /* fatal error, reset to known state */
519
xprintf(CGETS(7, 8, "*** editor fatal ERROR ***\r\n\n"));
520
#endif /* DEBUG_EDIT */
521
/* put (real) cursor in a known place */
522
ClearDisp(); /* reset the display stuff */
523
ResetInLine(1); /* reset the input pointers */
524
Refresh(); /* print the prompt again */
528
curlen = (int) (LastChar - InputBuf);
532
default: /* functions we don't know about */
538
curlen = (int) (LastChar - InputBuf);
542
(void) Cookedmode(); /* make sure the tty is set up correctly */
544
flush(); /* flush any buffered output */
552
if (str != NULL && MacroLvl + 1 < MAXMACROLEVELS) {
554
KeyMacro[MacroLvl] = str;
563
* Like eval, only using the current file descriptors
565
static Char **gv = NULL, **gav = NULL;
583
gflag = 0, tglob(gav);
585
gv = gav = globall(gav);
588
stderror(ERR_NOMATCH);
599
/* PWP: setjmp/longjmp bugfix for optimizing compilers */
601
my_reenter = 1; /* assume non-zero return val */
602
if (setexit() == 0) {
603
my_reenter = 0; /* Oh well, we were wrong */
605
if ((my_reenter = setexit()) == 0) {
622
stderror(ERR_SILENT);
631
xputchar('\n'); /* Start on a clean line */
651
GetNextCommand(cmdnum, ch)
658
while (cmd == 0 || cmd == F_XKEY) {
659
if ((num = GetNextChar(ch)) != 1) { /* if EOF or error */
663
if (!adrof(STRnokanji) && (*ch & META)) {
674
/* XXX: This needs to be fixed so that we don't just truncate
675
* the character, we unquote it.
677
if (*ch < NT_NUM_KEYS)
678
cmd = CurrentKeyMap[*ch];
680
cmd = CurrentKeyMap[(unsigned char) *ch];
685
cstr.len = Strlen(ch);
686
switch (GetXkey(&cstr, &val)) {
691
PushMacro(val.str.buf);
694
RunCommand(val.str.buf);
702
CurrentKeyMap = CcKeyMap;
712
register int num_read;
718
if (!Load_input_line())
721
if (*KeyMacro[MacroLvl] == 0) {
725
*cp = *KeyMacro[MacroLvl]++ & CHAR;
726
if (*KeyMacro[MacroLvl] == 0) { /* Needed for QuoteMode On */
732
if (Rawmode() < 0) /* make sure the tty is set up correctly */
733
return 0; /* oops: SHIN was closed */
738
while ((num_read = read(SHIN, (char *) &tcp, 1)) == -1) {
741
if (!tried && fixio(SHIN, errno) != -1)
745
/* need to print error message in case the file is migrated */
747
stderror(ERR_SYSTEM, progname, strerror(errno));
757
if (__nt_want_vcode == 2)
769
* SpellLine - do spelling correction on the entire command line
770
* (which may have trailing newline).
771
* If cmdonly is set, only check spelling of command words.
773
* -1: Something was incorrectible, and nothing was corrected
774
* 0: Everything was correct
775
* 1: Something was corrected
781
int endflag, matchval;
782
Char *argptr, *OldCursor, *OldLastChar;
784
OldLastChar = LastChar;
790
while (ismetahash(*argptr) || iscmdmeta(*argptr))
792
for (Cursor = argptr;
793
*Cursor != '\0' && ((Cursor != argptr && Cursor[-1] == '\\') ||
794
(!ismetahash(*Cursor) && !iscmdmeta(*Cursor)));
797
if (*Cursor == '\0') {
799
if (LastChar[-1] == '\n')
803
/* Obey current history character settings */
805
mismatch[1] = HISTSUB;
806
if (!Strchr(mismatch, *argptr) &&
807
(!cmdonly || starting_a_command(argptr, InputBuf))) {
810
* This hack avoids correcting drive letter changes
812
if((Cursor - InputBuf) != 2 || (char)InputBuf[1] != ':')
815
#ifdef HASH_SPELL_CHECK
817
size_t len = Cursor - InputBuf;
819
save = InputBuf[len];
820
InputBuf[len] = '\0';
821
if (find_cmd(InputBuf, 0) != 0) {
822
InputBuf[len] = save;
826
InputBuf[len] = save;
827
#endif /* HASH_SPELL_CHECK */
828
switch (tenematch(InputBuf, Cursor - InputBuf, SPELL)) {
829
case 1: /* corrected */
832
case -1: /* couldn't be corrected */
836
default: /* was correct */
840
if (LastChar != OldLastChar) {
841
if (argptr < OldCursor)
842
OldCursor += (LastChar - OldLastChar);
843
OldLastChar = LastChar;
853
* CompleteLine - do command completion on the entire command line
854
* (which may have trailing newline).
856
* 0: No command matched or failure
857
* 1: One command matched
858
* 2: Several commands matched
864
Char *argptr, *OldCursor, *OldLastChar;
866
OldLastChar = LastChar;
871
while (ismetahash(*argptr) || iscmdmeta(*argptr))
873
for (Cursor = argptr;
874
*Cursor != '\0' && ((Cursor != argptr && Cursor[-1] == '\\') ||
875
(!ismetahash(*Cursor) && !iscmdmeta(*Cursor)));
878
if (*Cursor == '\0') {
880
if (LastChar[-1] == '\n')
884
if (!Strchr(mismatch, *argptr) && starting_a_command(argptr, InputBuf)) {
885
tmatch = tenematch(InputBuf, Cursor - InputBuf, RECOGNIZE);
888
} else if (tmatch > 1) {
891
if (LastChar != OldLastChar) {
892
if (argptr < OldCursor)
893
OldCursor += (LastChar - OldLastChar);
894
OldLastChar = LastChar;