1 /* $Header: /p/tcsh/cvsroot/tcsh/ed.inputl.c,v 3.70 2009/06/25 21:15:37 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("$tcsh: ed.inputl.c,v 3.70 2009/06/25 21:15:37 christos Exp $")
38 #include "ed.defns.h" /* for the function names */
39 #include "tw.h" /* for twenex stuff */
43 /* ed.inputl -- routines to get a single line from the input. */
45 extern int MapsAreInited;
47 /* mismatched first character */
48 static Char mismatch[] = { '\\', '-', '%', '\0' };
49 /* don't Strchr() for '\0', obey current history character settings */
50 #define MISMATCH(c) ((c) == '\0' || (c) == HIST || (c) == HISTSUB || \
51 Strchr(mismatch, (c)))
53 static int Repair (void);
54 static int GetNextCommand (KEYCMD *, Char *);
55 static int SpellLine (int);
56 static int CompleteLine (void);
57 static void RunCommand (Char *);
58 static void doeval1 (Char **);
60 static int rotate = 0;
75 return (int) (LastChar - InputBuf);
84 unsigned char tch; /* the place where read() goes */
86 int num; /* how many chars we have read at NL */
88 struct varent *crct = inheredoc ? NULL : adrof(STRcorrect);
89 struct varent *autol = adrof(STRautolist);
90 struct varent *matchbeep = adrof(STRmatchbeep);
91 struct varent *imode = adrof(STRinputmode);
92 Char *SaveChar, *CorrChar;
93 int matchval; /* from tenematch() */
94 int nr_history_exp; /* number of (attempted) history expansions */
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 */
141 copyn(InputBuf, SavedBuf.s, INBUFSIZE);/*FIXBUF*/
142 LastChar = InputBuf + LastSaved;
143 Cursor = InputBuf + CursSaved;
144 Hist_num = HistSaved;
149 Hist_num = HistSaved;
157 Refresh(); /* print the prompt */
159 for (num = OKCMD; num == OKCMD;) { /* while still editing this line */
161 if (Cursor > LastChar)
162 xprintf("Cursor > LastChar\r\n");
163 if (Cursor < InputBuf)
164 xprintf("Cursor < InputBuf\r\n");
165 if (Cursor > InputLim)
166 xprintf("Cursor > InputLim\r\n");
167 if (LastChar > InputLim)
168 xprintf("LastChar > InputLim\r\n");
169 if (InputLim != &InputBuf[INBUFSIZE - 2])/*FIXBUF*/
170 xprintf("InputLim != &InputBuf[INBUFSIZE-2]\r\n");
171 if ((!DoingArg) && (Argument != 1))
172 xprintf("(!DoingArg) && (Argument != 1)\r\n");
173 if (CcKeyMap[0] == 0)
174 xprintf("CcKeyMap[0] == 0 (maybe not inited)\r\n");
177 /* if EOF or error */
178 if ((num = GetNextCommand(&cmdnum, &ch)) != OKCMD) {
182 if (cmdnum >= NumFuns) {/* BUG CHECK command */
184 xprintf(CGETS(6, 1, "ERROR: illegal command from key 0%o\r\n"), ch);
186 continue; /* try again */
189 /* now do the real command */
190 retval = (*CcFuncTbl[cmdnum]) (ch);
192 /* save the last command here */
195 /* make sure fn is initialized */
196 fn = (retval == CC_COMPLETE_ALL) ? LIST_ALL : LIST;
198 /* use any return value */
204 case CC_NORM: /* normal char */
208 case CC_ARGHACK: /* Suggested by Rich Salz */
209 /* <rsalz@pineapple.bbn.com> */
211 curlen = (int) (LastChar - InputBuf);
212 break; /* keep going... */
214 case CC_EOF: /* end of file typed */
216 curlen = (int) (LastChar - InputBuf);
220 case CC_WHICH: /* tell what this command does */
222 *LastChar++ = '\n'; /* for the benifit of CSH */
223 num = (int) (LastChar - InputBuf); /* number characters read */
226 case CC_NEWLINE: /* normal end of line */
230 if (crct && crct->vec != NULL && (!Strcmp(*(crct->vec), STRcmd) ||
231 !Strcmp(*(crct->vec), STRall))) {
235 Origin = Strsave(InputBuf);
236 cleanup_push(Origin, xfree);
238 if (SpellLine(!Strcmp(*(crct->vec), STRcmd)) == 1) {
242 Change = Strsave(InputBuf);
243 cleanup_push(Change, xfree);
244 *Strchr(Change, '\n') = '\0';
245 CorrChar = LastChar; /* Save the corrected end */
246 LastChar = InputBuf; /* Null the current line */
248 printprompt(2, short2str(Change));
249 cleanup_until(Change);
251 if (xread(SHIN, &tch, 1) < 0) {
254 * need to print error message in case file
258 stderror(ERR_SYSTEM, progname, strerror(errno));
260 cleanup_until(Origin);
265 if (ch == 'y' || ch == ' ') {
266 LastChar = CorrChar; /* Restore the corrected end */
267 xprintf("%s", CGETS(6, 2, "yes\n"));
270 Strcpy(InputBuf, Origin);
273 xprintf("%s", CGETS(6, 3, "edit\n"));
276 printprompt(3, NULL);
280 cleanup_until(Origin);
283 else if (ch == 'a') {
284 xprintf("%s", CGETS(6, 4, "abort\n"));
285 LastChar = InputBuf; /* Null the current line */
287 printprompt(0, NULL);
289 cleanup_until(Origin);
292 xprintf("%s", CGETS(6, 5, "no\n"));
296 cleanup_until(Origin);
297 } else if (crct && crct->vec != NULL &&
298 !Strcmp(*(crct->vec), STRcomplete)) {
299 if (LastChar > InputBuf && LastChar[-1] == '\n') {
304 match_unique_match = 1; /* match unique matches */
305 matchval = CompleteLine();
306 match_unique_match = 0;
307 curlen = (int) (LastChar - InputBuf);
312 xprintf("%s", CGETS(6, 6, "No matching command\n"));
313 } else if (matchval == 2) {
314 xprintf("%s", CGETS(6, 7, "Ambiguous command\n"));
329 curlen = (int) (LastChar - InputBuf);
335 tellwhat = 0; /* just in case */
336 Hist_num = 0; /* for the history commands */
337 /* return the number of chars read */
338 num = (int) (LastChar - InputBuf);
340 * For continuation lines, we set the prompt to prompt 2
342 printprompt(1, NULL);
347 if (tenematch(InputBuf, Cursor - InputBuf, SPELL) < 0)
348 SoundBeep(); /* Beep = No match/ambiguous */
353 if (SpellLine(FALSE) < 0)
354 SoundBeep(); /* Beep = No match/ambiguous */
360 case CC_COMPLETE_ALL:
361 case CC_COMPLETE_FWD:
362 case CC_COMPLETE_BACK:
366 curlen = (int) (LastChar - InputBuf);
370 case CC_COMPLETE_ALL:
372 curlen = (int) (LastChar - InputBuf);
376 case CC_COMPLETE_FWD:
377 fn = RECOGNIZE_SCROLL;
381 case CC_COMPLETE_BACK:
382 fn = RECOGNIZE_SCROLL;
389 if (InputBuf[curlen] && rotate) {
390 newlen = (int) (LastChar - InputBuf);
391 for (idx = (int) (Cursor - InputBuf);
392 idx <= newlen; idx++)
393 InputBuf[idx - newlen + curlen] =
395 LastChar = InputBuf + curlen;
396 Cursor = Cursor - newlen + curlen;
398 curlen = (int) (LastChar - InputBuf);
402 autoexpand = varval(STRautoexpand);
403 if (autoexpand != STRNULL)
404 nr_history_exp += ExpandHistory();
406 /* try normal expansion only if no history references were found */
407 if (nr_history_exp == 0 ||
408 Strcmp(autoexpand, STRonlyhistory) != 0) {
410 * Modified by Martin Boyer (gamin@ireq-robot.hydro.qc.ca):
411 * A separate variable now controls beeping after
412 * completion, independently of autolisting.
414 expnum = (int) (Cursor - InputBuf);
415 switch (matchval = tenematch(InputBuf, Cursor-InputBuf, fn)){
417 if (non_unique_match && matchbeep &&
418 matchbeep->vec != NULL &&
419 (Strcmp(*(matchbeep->vec), STRnotunique) == 0))
423 if (matchbeep && matchbeep->vec != NULL) {
424 if (Strcmp(*(matchbeep->vec), STRnomatch) == 0 ||
425 Strcmp(*(matchbeep->vec), STRambiguous) == 0 ||
426 Strcmp(*(matchbeep->vec), STRnotunique) == 0)
433 if (matchval < 0) { /* Error from tenematch */
438 if (matchbeep && matchbeep->vec != NULL) {
439 if ((Strcmp(*(matchbeep->vec), STRambiguous) == 0 ||
440 Strcmp(*(matchbeep->vec), STRnotunique) == 0))
446 * Addition by David C Lawrence <tale@pawl.rpi.edu>: If an
447 * attempted completion is ambiguous, list the choices.
448 * (PWP: this is the best feature addition to tcsh I have
449 * seen in many months.)
451 if (autol && autol->vec != NULL &&
452 (Strcmp(*(autol->vec), STRambiguous) != 0 ||
453 expnum == Cursor - InputBuf)) {
454 if (adrof(STRhighlight) && MarkIsSet) {
455 /* clear highlighting before showing completions */
463 fn = (retval == CC_COMPLETE_ALL) ? LIST_ALL : LIST;
464 (void) tenematch(InputBuf, Cursor-InputBuf, fn);
480 case CC_LIST_CHOICES:
482 if (InputBuf[curlen] && rotate) {
483 newlen = (int) (LastChar - InputBuf);
484 for (idx = (int) (Cursor - InputBuf);
485 idx <= newlen; idx++)
486 InputBuf[idx - newlen + curlen] =
488 LastChar = InputBuf + curlen;
489 Cursor = Cursor - newlen + curlen;
491 curlen = (int) (LastChar - InputBuf);
495 fn = (retval == CC_LIST_ALL) ? LIST_ALL : LIST;
496 /* should catch ^C here... */
497 if (tenematch(InputBuf, Cursor - InputBuf, fn) < 0)
506 if (tenematch(InputBuf, Cursor - InputBuf, GLOB) < 0)
512 if (tenematch(InputBuf, Cursor - InputBuf, GLOB_EXPAND) <= 0)
513 SoundBeep(); /* Beep = No match */
517 case CC_NORMALIZE_PATH:
518 if (tenematch(InputBuf, Cursor - InputBuf, PATH_NORMALIZE) <= 0)
519 SoundBeep(); /* Beep = No match */
524 if (tenematch(InputBuf, Cursor - InputBuf, VARS_EXPAND) <= 0)
525 SoundBeep(); /* Beep = No match */
529 case CC_NORMALIZE_COMMAND:
530 if (tenematch(InputBuf, Cursor - InputBuf, COMMAND_NORMALIZE) <= 0)
531 SoundBeep(); /* Beep = No match */
537 /* should catch ^C here... */
538 (void) tenematch(InputBuf, LastChar - InputBuf, PRINT_HELP);
543 curlen = (int) (LastChar - InputBuf);
546 case CC_FATAL: /* fatal error, reset to known state */
548 xprintf(CGETS(7, 8, "*** editor fatal ERROR ***\r\n\n"));
549 #endif /* DEBUG_EDIT */
550 /* put (real) cursor in a known place */
551 ClearDisp(); /* reset the display stuff */
552 ResetInLine(1); /* reset the input pointers */
553 Refresh(); /* print the prompt again */
557 curlen = (int) (LastChar - InputBuf);
561 default: /* functions we don't know about */
562 if (adrof(STRhighlight)) {
572 curlen = (int) (LastChar - InputBuf);
576 (void) Cookedmode(); /* make sure the tty is set up correctly */
578 flush(); /* flush any buffered output */
585 if (str != NULL && MacroLvl + 1 < MAXMACROLEVELS) {
587 KeyMacro[MacroLvl] = str;
597 Char **evalvec, *evalp;
601 eval1_cleanup(void *xstate)
603 struct eval1_state *state;
606 evalvec = state->evalvec;
607 evalp = state->evalp;
612 * Like eval, only using the current file descriptors
617 struct eval1_state state;
623 gv = v = globall(v, gflag);
625 stderror(ERR_NOMATCH);
634 cleanup_push(gv, blk_cleanup);
636 state.evalvec = evalvec;
640 cleanup_push(&state, eval1_cleanup);
642 cleanup_until(&state);
648 RunCommand(Char *str)
652 xputchar('\n'); /* Start on a clean line */
672 GetNextCommand(KEYCMD *cmdnum, Char *ch)
677 while (cmd == 0 || cmd == F_XKEY) {
678 if ((num = GetNextChar(ch)) != 1) { /* if EOF or error */
688 !adrof(STRnokanji) && (*ch & META)) {
699 /* XXX: This needs to be fixed so that we don't just truncate
700 * the character, we unquote it.
702 if (*ch < NT_NUM_KEYS)
703 cmd = CurrentKeyMap[*ch];
706 cmd = CurrentKeyMap[(unsigned char) *ch];
715 switch (GetXkey(&cstr, &val)) {
720 PushMacro(val.str.buf);
723 RunCommand(val.str.buf);
731 CurrentKeyMap = CcKeyMap;
737 static Char ungetchar;
738 static int haveungetchar;
741 UngetNextChar(Char cp)
748 GetNextChar(Char *cp)
752 char cbuf[MB_LEN_MAX];
762 if (!Load_input_line())
765 if (*KeyMacro[MacroLvl] == 0) {
769 *cp = *KeyMacro[MacroLvl]++ & CHAR;
770 if (*KeyMacro[MacroLvl] == 0) { /* Needed for QuoteMode On */
776 if (Rawmode() < 0) /* make sure the tty is set up correctly */
777 return 0; /* oops: SHIN was closed */
781 #endif /* WINNT_NATIVE */
784 (void) check_window_size(0); /* for window systems */
785 #endif /* SIG_WINDOW */
788 while ((num_read = xread(SHIN, cbuf + cbp, 1)) == -1) {
789 if (!tried && fixio(SHIN, errno) != -1)
793 /* need to print error message in case the file is migrated */
794 stderror(ERR_SYSTEM, progname, strerror(errno));
798 # endif /* WINNT_NATIVE */
799 *cp = '\0'; /* Loses possible partial character */
804 *cp = (unsigned char)*cbuf;
807 if (normal_mbtowc(cp, cbuf, cbp) == -1) {
809 if (cbp < MB_CUR_MAX)
810 continue; /* Maybe a partial character */
811 /* And drop the following bytes, if any */
812 *cp = (unsigned char)*cbuf | INVALID_BYTE;
818 /* This is the part that doesn't work with WIDE_STRINGS */
819 if (__nt_want_vcode == 2)
822 #endif /* WINNT_NATIVE */
827 * SpellLine - do spelling correction on the entire command line
828 * (which may have trailing newline).
829 * If cmdonly is set, only check spelling of command words.
831 * -1: Something was incorrectible, and nothing was corrected
832 * 0: Everything was correct
833 * 1: Something was corrected
836 SpellLine(int cmdonly)
838 int endflag, matchval;
839 Char *argptr, *OldCursor, *OldLastChar;
841 OldLastChar = LastChar;
847 while (ismetahash(*argptr) || iscmdmeta(*argptr))
849 for (Cursor = argptr;
850 *Cursor != '\0' && ((Cursor != argptr && Cursor[-1] == '\\') ||
851 (!ismetahash(*Cursor) && !iscmdmeta(*Cursor)));
854 if (*Cursor == '\0') {
856 if (LastChar[-1] == '\n')
860 if (!MISMATCH(*argptr) &&
861 (!cmdonly || starting_a_command(argptr, InputBuf))) {
864 * This hack avoids correcting drive letter changes
866 if((Cursor - InputBuf) != 2 || (char)InputBuf[1] != ':')
867 #endif /* WINNT_NATIVE */
869 #ifdef HASH_SPELL_CHECK
871 size_t len = Cursor - InputBuf;
873 save = InputBuf[len];
874 InputBuf[len] = '\0';
875 if (find_cmd(InputBuf, 0) != 0) {
876 InputBuf[len] = save;
880 InputBuf[len] = save;
881 #endif /* HASH_SPELL_CHECK */
882 switch (tenematch(InputBuf, Cursor - InputBuf, SPELL)) {
883 case 1: /* corrected */
886 case -1: /* couldn't be corrected */
890 default: /* was correct */
894 if (LastChar != OldLastChar) {
895 if (argptr < OldCursor)
896 OldCursor += (LastChar - OldLastChar);
897 OldLastChar = LastChar;
907 * CompleteLine - do command completion on the entire command line
908 * (which may have trailing newline).
910 * 0: No command matched or failure
911 * 1: One command matched
912 * 2: Several commands matched
918 Char *argptr, *OldCursor, *OldLastChar;
920 OldLastChar = LastChar;
925 while (ismetahash(*argptr) || iscmdmeta(*argptr))
927 for (Cursor = argptr;
928 *Cursor != '\0' && ((Cursor != argptr && Cursor[-1] == '\\') ||
929 (!ismetahash(*Cursor) && !iscmdmeta(*Cursor)));
932 if (*Cursor == '\0') {
934 if (LastChar[-1] == '\n')
938 if (!MISMATCH(*argptr) && starting_a_command(argptr, InputBuf)) {
939 tmatch = tenematch(InputBuf, Cursor - InputBuf, RECOGNIZE);
942 } else if (tmatch > 1) {
945 if (LastChar != OldLastChar) {
946 if (argptr < OldCursor)
947 OldCursor += (LastChar - OldLastChar);
948 OldLastChar = LastChar;