1 /* $Header: /p/tcsh/cvsroot/tcsh/ed.inputl.c,v 3.73 2012/10/19 15:23:32 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.73 2012/10/19 15:23:32 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 if (normal_mbtowc(cp
, cbuf
, cbp
) == -1) {
806 if (cbp
< MB_CUR_MAX
)
807 continue; /* Maybe a partial character */
808 /* And drop the following bytes, if any */
809 *cp
= (unsigned char)*cbuf
| INVALID_BYTE
;
814 /* This is the part that doesn't work with WIDE_STRINGS */
815 if (__nt_want_vcode
== 2)
818 #endif /* WINNT_NATIVE */
823 * SpellLine - do spelling correction on the entire command line
824 * (which may have trailing newline).
825 * If cmdonly is set, only check spelling of command words.
827 * -1: Something was incorrectible, and nothing was corrected
828 * 0: Everything was correct
829 * 1: Something was corrected
832 SpellLine(int cmdonly
)
834 int endflag
, matchval
;
835 Char
*argptr
, *OldCursor
, *OldLastChar
;
837 OldLastChar
= LastChar
;
843 while (ismetahash(*argptr
) || iscmdmeta(*argptr
))
845 for (Cursor
= argptr
;
846 *Cursor
!= '\0' && ((Cursor
!= argptr
&& Cursor
[-1] == '\\') ||
847 (!ismetahash(*Cursor
) && !iscmdmeta(*Cursor
)));
850 if (*Cursor
== '\0') {
852 if (LastChar
[-1] == '\n')
856 if (!MISMATCH(*argptr
) &&
857 (!cmdonly
|| starting_a_command(argptr
, InputBuf
))) {
860 * This hack avoids correcting drive letter changes
862 if((Cursor
- InputBuf
) != 2 || (char)InputBuf
[1] != ':')
863 #endif /* WINNT_NATIVE */
865 #ifdef HASH_SPELL_CHECK
867 size_t len
= Cursor
- InputBuf
;
869 save
= InputBuf
[len
];
870 InputBuf
[len
] = '\0';
871 if (find_cmd(InputBuf
, 0) != 0) {
872 InputBuf
[len
] = save
;
876 InputBuf
[len
] = save
;
877 #endif /* HASH_SPELL_CHECK */
878 switch (tenematch(InputBuf
, Cursor
- InputBuf
, SPELL
)) {
879 case 1: /* corrected */
882 case -1: /* couldn't be corrected */
886 default: /* was correct */
890 if (LastChar
!= OldLastChar
) {
891 if (argptr
< OldCursor
)
892 OldCursor
+= (LastChar
- OldLastChar
);
893 OldLastChar
= LastChar
;
903 * CompleteLine - do command completion on the entire command line
904 * (which may have trailing newline).
906 * 0: No command matched or failure
907 * 1: One command matched
908 * 2: Several commands matched
914 Char
*argptr
, *OldCursor
, *OldLastChar
;
916 OldLastChar
= LastChar
;
921 while (ismetahash(*argptr
) || iscmdmeta(*argptr
))
923 for (Cursor
= argptr
;
924 *Cursor
!= '\0' && ((Cursor
!= argptr
&& Cursor
[-1] == '\\') ||
925 (!ismetahash(*Cursor
) && !iscmdmeta(*Cursor
)));
928 if (*Cursor
== '\0') {
930 if (LastChar
[-1] == '\n')
934 if (!MISMATCH(*argptr
) && starting_a_command(argptr
, InputBuf
)) {
935 tmatch
= tenematch(InputBuf
, Cursor
- InputBuf
, RECOGNIZE
);
938 } else if (tmatch
> 1) {
941 if (LastChar
!= OldLastChar
) {
942 if (argptr
< OldCursor
)
943 OldCursor
+= (LastChar
- OldLastChar
);
944 OldLastChar
= LastChar
;