1 /* $Header: /src/pub/tcsh/ed.inputl.c,v 3.51 2002/06/25 19:02:11 christos Exp $ */
3 * ed.inputl.c: Input line handling.
6 * Copyright (c) 1980, 1991 The Regents of the University of California.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 RCSID("$Id: ed.inputl.c,v 3.51 2002/06/25 19:02:11 christos Exp $")
38 #include "ed.defns.h" /* for the function names */
39 #include "tw.h" /* for twenex stuff */
41 #define OKCMD (INBUFSIZE+INBUFSIZE)
43 /* ed.inputl -- routines to get a single line from the input. */
46 extern bool MapsAreInited;
47 extern bool Tty_raw_mode;
49 /* mismatched first character */
50 static Char mismatch[] =
51 {'!', '^' , '\\', '-', '%', '\0', '"', '\'', '`', '\0' };
53 static int Repair __P((void));
54 static int GetNextCommand __P((KEYCMD *, Char *));
55 static int SpellLine __P((int));
56 static int CompleteLine __P((void));
57 static void RunCommand __P((Char *));
58 static void doeval1 __P((Char **));
60 static bool rotate = 0;
75 return (int) (LastChar - InputBuf);
84 extern KEYCMD NumFuns;
85 unsigned char tch; /* the place where read() goes */
87 int num; /* how many chars we have read at NL */
89 struct varent *crct = inheredoc ? NULL : adrof(STRcorrect);
90 struct varent *autol = adrof(STRautolist);
91 struct varent *matchbeep = adrof(STRmatchbeep);
92 struct varent *imode = adrof(STRinputmode);
93 Char *SaveChar, *CorrChar;
94 Char Origin[INBUFSIZE], Change[INBUFSIZE];
95 int matchval; /* from tenematch() */
101 if (!MapsAreInited) /* double extra just in case */
104 ClearDisp(); /* reset the display stuff */
105 ResetInLine(0); /* reset the input pointers */
107 MacroLvl = -1; /* editor was interrupted during input */
109 if (imode && imode->vec != NULL) {
110 if (!Strcmp(*(imode->vec), STRinsert))
111 inputmode = MODE_INSERT;
112 else if (!Strcmp(*(imode->vec), STRoverwrite))
113 inputmode = MODE_REPLACE;
116 #if defined(FIONREAD) && !defined(OREO)
117 if (!Tty_raw_mode && MacroLvl < 0) {
122 * *Everyone* else has an int, but SunOS wants long!
123 * This breaks where int != long (alpha)
128 (void) ioctl(SHIN, FIONREAD, (ioctl_t) & chrs);
134 #endif /* FIONREAD && !OREO */
140 copyn(InputBuf, WhichBuf, INBUFSIZE);
141 LastChar = InputBuf + (LastWhich - WhichBuf);
142 Cursor = InputBuf + (CursWhich - WhichBuf);
144 Hist_num = HistWhich;
150 Refresh(); /* print the prompt */
152 for (num = OKCMD; num == OKCMD;) { /* while still editing this line */
154 if (Cursor > LastChar)
155 xprintf("Cursor > LastChar\r\n");
156 if (Cursor < InputBuf)
157 xprintf("Cursor < InputBuf\r\n");
158 if (Cursor > InputLim)
159 xprintf("Cursor > InputLim\r\n");
160 if (LastChar > InputLim)
161 xprintf("LastChar > InputLim\r\n");
162 if (InputLim != &InputBuf[INBUFSIZE - 2])
163 xprintf("InputLim != &InputBuf[INBUFSIZE-2]\r\n");
164 if ((!DoingArg) && (Argument != 1))
165 xprintf("(!DoingArg) && (Argument != 1)\r\n");
166 if (CcKeyMap[0] == 0)
167 xprintf("CcKeyMap[0] == 0 (maybe not inited)\r\n");
170 /* if EOF or error */
171 if ((num = GetNextCommand(&cmdnum, &ch)) != OKCMD) {
175 if (cmdnum >= NumFuns) {/* BUG CHECK command */
177 xprintf(CGETS(6, 1, "ERROR: illegal command from key 0%o\r\n"), ch);
179 continue; /* try again */
182 /* now do the real command */
183 retval = (*CcFuncTbl[cmdnum]) (ch);
185 /* save the last command here */
188 /* make sure fn is initialized */
189 fn = (retval == CC_COMPLETE_ALL) ? LIST_ALL : LIST;
191 /* use any return value */
197 case CC_NORM: /* normal char */
201 case CC_ARGHACK: /* Suggested by Rich Salz */
202 /* <rsalz@pineapple.bbn.com> */
204 curlen = (int) (LastChar - InputBuf);
205 break; /* keep going... */
207 case CC_EOF: /* end of file typed */
209 curlen = (int) (LastChar - InputBuf);
213 case CC_WHICH: /* tell what this command does */
215 copyn(WhichBuf, InputBuf, INBUFSIZE);
216 LastWhich = WhichBuf + (LastChar - InputBuf);
217 CursWhich = WhichBuf + (Cursor - InputBuf);
218 *LastChar++ = '\n'; /* for the benifit of CSH */
219 HistWhich = Hist_num;
220 Hist_num = 0; /* for the history commands */
221 num = (int) (LastChar - InputBuf); /* number characters read */
224 case CC_NEWLINE: /* normal end of line */
228 if (crct && crct->vec != NULL && (!Strcmp(*(crct->vec), STRcmd) ||
229 !Strcmp(*(crct->vec), STRall))) {
231 copyn(Origin, InputBuf, INBUFSIZE);
233 if (SpellLine(!Strcmp(*(crct->vec), STRcmd)) == 1) {
235 copyn(Change, InputBuf, INBUFSIZE);
236 *Strchr(Change, '\n') = '\0';
237 CorrChar = LastChar; /* Save the corrected end */
238 LastChar = InputBuf; /* Null the current line */
240 printprompt(2, short2str(Change));
242 if (read(SHIN, (char *) &tch, 1) < 0)
245 * need to print error message in case file
248 if (errno && errno != EINTR)
249 stderror(ERR_SYSTEM, progname, strerror(errno));
254 if (ch == 'y' || ch == ' ') {
255 LastChar = CorrChar; /* Restore the corrected end */
256 xprintf(CGETS(6, 2, "yes\n"));
259 copyn(InputBuf, Origin, INBUFSIZE);
262 xprintf(CGETS(6, 3, "edit\n"));
265 printprompt(3, NULL);
271 else if (ch == 'a') {
272 xprintf(CGETS(6, 4, "abort\n"));
273 LastChar = InputBuf; /* Null the current line */
275 printprompt(0, NULL);
279 xprintf(CGETS(6, 5, "no\n"));
283 } else if (crct && crct->vec != NULL &&
284 !Strcmp(*(crct->vec), STRcomplete)) {
285 if (LastChar > InputBuf && LastChar[-1] == '\n') {
290 match_unique_match = 1; /* match unique matches */
291 matchval = CompleteLine();
292 match_unique_match = 0;
293 curlen = (int) (LastChar - InputBuf);
298 xprintf(CGETS(6, 6, "No matching command\n"));
299 } else if (matchval == 2) {
300 xprintf(CGETS(6, 7, "Ambiguous command\n"));
315 curlen = (int) (LastChar - InputBuf);
321 tellwhat = 0; /* just in case */
322 Hist_num = 0; /* for the history commands */
323 /* return the number of chars read */
324 num = (int) (LastChar - InputBuf);
326 * For continuation lines, we set the prompt to prompt 2
328 printprompt(1, NULL);
333 if (tenematch(InputBuf, Cursor - InputBuf, SPELL) < 0)
334 SoundBeep(); /* Beep = No match/ambiguous */
339 if (SpellLine(FALSE) < 0)
340 SoundBeep(); /* Beep = No match/ambiguous */
346 case CC_COMPLETE_ALL:
347 case CC_COMPLETE_FWD:
348 case CC_COMPLETE_BACK:
352 curlen = (int) (LastChar - InputBuf);
356 case CC_COMPLETE_ALL:
358 curlen = (int) (LastChar - InputBuf);
362 case CC_COMPLETE_FWD:
363 fn = RECOGNIZE_SCROLL;
367 case CC_COMPLETE_BACK:
368 fn = RECOGNIZE_SCROLL;
375 if (InputBuf[curlen] && rotate) {
376 newlen = (int) (LastChar - InputBuf);
377 for (idx = (int) (Cursor - InputBuf);
378 idx <= newlen; idx++)
379 InputBuf[idx - newlen + curlen] =
381 LastChar = InputBuf + curlen;
382 Cursor = Cursor - newlen + curlen;
384 curlen = (int) (LastChar - InputBuf);
387 if (adrof(STRautoexpand))
388 (void) e_expand_history(0);
390 * Modified by Martin Boyer (gamin@ireq-robot.hydro.qc.ca):
391 * A separate variable now controls beeping after
392 * completion, independently of autolisting.
394 expnum = (int) (Cursor - InputBuf);
395 switch (matchval = tenematch(InputBuf, Cursor-InputBuf, fn)){
397 if (non_unique_match && matchbeep && matchbeep->vec != NULL &&
398 (Strcmp(*(matchbeep->vec), STRnotunique) == 0))
402 if (matchbeep && matchbeep->vec != NULL) {
403 if (Strcmp(*(matchbeep->vec), STRnomatch) == 0 ||
404 Strcmp(*(matchbeep->vec), STRambiguous) == 0 ||
405 Strcmp(*(matchbeep->vec), STRnotunique) == 0)
412 if (matchval < 0) { /* Error from tenematch */
417 if (matchbeep && matchbeep->vec != NULL) {
418 if ((Strcmp(*(matchbeep->vec), STRambiguous) == 0 ||
419 Strcmp(*(matchbeep->vec), STRnotunique) == 0))
425 * Addition by David C Lawrence <tale@pawl.rpi.edu>: If an
426 * attempted completion is ambiguous, list the choices.
427 * (PWP: this is the best feature addition to tcsh I have
428 * seen in many months.)
430 if (autol && autol->vec != NULL &&
431 (Strcmp(*(autol->vec), STRambiguous) != 0 ||
432 expnum == Cursor - InputBuf)) {
434 fn = (retval == CC_COMPLETE_ALL) ? LIST_ALL : LIST;
435 (void) tenematch(InputBuf, Cursor-InputBuf, fn);
450 case CC_LIST_CHOICES:
452 if (InputBuf[curlen] && rotate) {
453 newlen = (int) (LastChar - InputBuf);
454 for (idx = (int) (Cursor - InputBuf);
455 idx <= newlen; idx++)
456 InputBuf[idx - newlen + curlen] =
458 LastChar = InputBuf + curlen;
459 Cursor = Cursor - newlen + curlen;
461 curlen = (int) (LastChar - InputBuf);
465 fn = (retval == CC_LIST_ALL) ? LIST_ALL : LIST;
466 /* should catch ^C here... */
467 if (tenematch(InputBuf, Cursor - InputBuf, fn) < 0)
476 if (tenematch(InputBuf, Cursor - InputBuf, GLOB) < 0)
482 if (tenematch(InputBuf, Cursor - InputBuf, GLOB_EXPAND) <= 0)
483 SoundBeep(); /* Beep = No match */
487 case CC_NORMALIZE_PATH:
488 if (tenematch(InputBuf, Cursor - InputBuf, PATH_NORMALIZE) <= 0)
489 SoundBeep(); /* Beep = No match */
494 if (tenematch(InputBuf, Cursor - InputBuf, VARS_EXPAND) <= 0)
495 SoundBeep(); /* Beep = No match */
499 case CC_NORMALIZE_COMMAND:
500 if (tenematch(InputBuf, Cursor - InputBuf, COMMAND_NORMALIZE) <= 0)
501 SoundBeep(); /* Beep = No match */
507 /* should catch ^C here... */
508 (void) tenematch(InputBuf, LastChar - InputBuf, PRINT_HELP);
513 curlen = (int) (LastChar - InputBuf);
516 case CC_FATAL: /* fatal error, reset to known state */
518 xprintf(CGETS(7, 8, "*** editor fatal ERROR ***\r\n\n"));
519 #endif /* DEBUG_EDIT */
520 /* put (real) cursor in a known place */
521 ClearDisp(); /* reset the display stuff */
522 ResetInLine(1); /* reset the input pointers */
523 Refresh(); /* print the prompt again */
527 curlen = (int) (LastChar - InputBuf);
531 default: /* functions we don't know about */
537 curlen = (int) (LastChar - InputBuf);
541 (void) Cookedmode(); /* make sure the tty is set up correctly */
543 flush(); /* flush any buffered output */
551 if (str != NULL && MacroLvl + 1 < MAXMACROLEVELS) {
553 KeyMacro[MacroLvl] = str;
562 * Like eval, only using the current file descriptors
564 static Char **gv = NULL, **gav = NULL;
582 gflag = 0, tglob(gav);
584 gv = gav = globall(gav);
587 stderror(ERR_NOMATCH);
598 /* PWP: setjmp/longjmp bugfix for optimizing compilers */
600 my_reenter = 1; /* assume non-zero return val */
601 if (setexit() == 0) {
602 my_reenter = 0; /* Oh well, we were wrong */
604 if ((my_reenter = setexit()) == 0) {
621 stderror(ERR_SILENT);
630 xputchar('\n'); /* Start on a clean line */
650 GetNextCommand(cmdnum, ch)
657 while (cmd == 0 || cmd == F_XKEY) {
658 if ((num = GetNextChar(ch)) != 1) { /* if EOF or error */
666 !adrof(STRnokanji) && (*ch & META)) {
677 /* XXX: This needs to be fixed so that we don't just truncate
678 * the character, we unquote it.
680 if (*ch < NT_NUM_KEYS)
681 cmd = CurrentKeyMap[*ch];
683 cmd = CurrentKeyMap[(unsigned char) *ch];
688 cstr.len = Strlen(ch);
689 switch (GetXkey(&cstr, &val)) {
694 PushMacro(val.str.buf);
697 RunCommand(val.str.buf);
705 CurrentKeyMap = CcKeyMap;
715 register int num_read;
721 if (!Load_input_line())
724 if (*KeyMacro[MacroLvl] == 0) {
728 *cp = *KeyMacro[MacroLvl]++ & CHAR;
729 if (*KeyMacro[MacroLvl] == 0) { /* Needed for QuoteMode On */
735 if (Rawmode() < 0) /* make sure the tty is set up correctly */
736 return 0; /* oops: SHIN was closed */
740 #endif /* WINNT_NATIVE */
741 while ((num_read = read(SHIN, (char *) &tcp, 1)) == -1) {
744 if (!tried && fixio(SHIN, errno) != -1)
748 /* need to print error message in case the file is migrated */
750 stderror(ERR_SYSTEM, progname, strerror(errno));
754 #endif /* WINNT_NATIVE */
760 if (__nt_want_vcode == 2)
767 #endif /* WINNT_NATIVE */
772 * SpellLine - do spelling correction on the entire command line
773 * (which may have trailing newline).
774 * If cmdonly is set, only check spelling of command words.
776 * -1: Something was incorrectible, and nothing was corrected
777 * 0: Everything was correct
778 * 1: Something was corrected
784 int endflag, matchval;
785 Char *argptr, *OldCursor, *OldLastChar;
787 OldLastChar = LastChar;
793 while (ismetahash(*argptr) || iscmdmeta(*argptr))
795 for (Cursor = argptr;
796 *Cursor != '\0' && ((Cursor != argptr && Cursor[-1] == '\\') ||
797 (!ismetahash(*Cursor) && !iscmdmeta(*Cursor)));
800 if (*Cursor == '\0') {
802 if (LastChar[-1] == '\n')
806 /* Obey current history character settings */
808 mismatch[1] = HISTSUB;
809 if (!Strchr(mismatch, *argptr) &&
810 (!cmdonly || starting_a_command(argptr, InputBuf))) {
813 * This hack avoids correcting drive letter changes
815 if((Cursor - InputBuf) != 2 || (char)InputBuf[1] != ':')
816 #endif /* WINNT_NATIVE */
818 #ifdef HASH_SPELL_CHECK
820 size_t len = Cursor - InputBuf;
822 save = InputBuf[len];
823 InputBuf[len] = '\0';
824 if (find_cmd(InputBuf, 0) != 0) {
825 InputBuf[len] = save;
829 InputBuf[len] = save;
830 #endif /* HASH_SPELL_CHECK */
831 switch (tenematch(InputBuf, Cursor - InputBuf, SPELL)) {
832 case 1: /* corrected */
835 case -1: /* couldn't be corrected */
839 default: /* was correct */
843 if (LastChar != OldLastChar) {
844 if (argptr < OldCursor)
845 OldCursor += (LastChar - OldLastChar);
846 OldLastChar = LastChar;
856 * CompleteLine - do command completion on the entire command line
857 * (which may have trailing newline).
859 * 0: No command matched or failure
860 * 1: One command matched
861 * 2: Several commands matched
867 Char *argptr, *OldCursor, *OldLastChar;
869 OldLastChar = LastChar;
874 while (ismetahash(*argptr) || iscmdmeta(*argptr))
876 for (Cursor = argptr;
877 *Cursor != '\0' && ((Cursor != argptr && Cursor[-1] == '\\') ||
878 (!ismetahash(*Cursor) && !iscmdmeta(*Cursor)));
881 if (*Cursor == '\0') {
883 if (LastChar[-1] == '\n')
887 if (!Strchr(mismatch, *argptr) && starting_a_command(argptr, InputBuf)) {
888 tmatch = tenematch(InputBuf, Cursor - InputBuf, RECOGNIZE);
891 } else if (tmatch > 1) {
894 if (LastChar != OldLastChar) {
895 if (argptr < OldCursor)
896 OldCursor += (LastChar - OldLastChar);
897 OldLastChar = LastChar;