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
;