Import tcsh-6.17.00
[dragonfly.git] / contrib / tcsh-6 / ed.inputl.c
1 /* $Header: /p/tcsh/cvsroot/tcsh/ed.inputl.c,v 3.70 2009/06/25 21:15:37 christos Exp $ */
2 /*
3  * ed.inputl.c: Input line handling.
4  */
5 /*-
6  * Copyright (c) 1980, 1991 The Regents of the University of California.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
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.
20  *
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
31  * SUCH DAMAGE.
32  */
33 #include "sh.h"
34
35 RCSID("$tcsh: ed.inputl.c,v 3.70 2009/06/25 21:15:37 christos Exp $")
36
37 #include "ed.h"
38 #include "ed.defns.h"           /* for the function names */
39 #include "tw.h"                 /* for twenex stuff */
40
41 #define OKCMD INT_MAX
42
43 /* ed.inputl -- routines to get a single line from the input. */
44
45 extern int MapsAreInited;
46
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)))
52
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 **);
59
60 static int rotate = 0;
61
62
63 static int
64 Repair(void)
65 {
66     if (NeedsRedraw) {
67         ClearLines();
68         ClearDisp();
69         NeedsRedraw = 0;
70     }
71     Refresh();
72     Argument = 1;
73     DoingArg = 0;
74     curchoice = -1;
75     return (int) (LastChar - InputBuf);
76 }
77
78 /* CCRETVAL */
79 int
80 Inputl(void)
81 {
82     CCRETVAL retval;
83     KEYCMD  cmdnum = 0;
84     unsigned char tch;          /* the place where read() goes */
85     Char    ch;
86     int     num;                /* how many chars we have read at NL */
87     int     expnum;
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 */
95     COMMAND fn;
96     int curlen = 0;
97     int newlen;
98     int idx;
99     Char *autoexpand;
100
101     if (!MapsAreInited)         /* double extra just in case */
102         ed_InitMaps();
103
104     ClearDisp();                /* reset the display stuff */
105     ResetInLine(0);             /* reset the input pointers */
106     if (GettingInput)
107         MacroLvl = -1;          /* editor was interrupted during input */
108
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;
114     }
115
116 #if defined(FIONREAD) && !defined(OREO)
117     if (!Tty_raw_mode && MacroLvl < 0) {
118 # ifdef SUNOS4
119         long chrs = 0;
120 # else /* !SUNOS4 */
121         /* 
122          * *Everyone* else has an int, but SunOS wants long!
123          * This breaks where int != long (alpha)
124          */
125         int chrs = 0;
126 # endif /* SUNOS4 */
127
128         (void) ioctl(SHIN, FIONREAD, (ioctl_t) & chrs);
129         if (chrs == 0) {
130             if (Rawmode() < 0)
131                 return 0;
132         }
133     }
134 #endif /* FIONREAD && !OREO */
135
136     GettingInput = 1;
137     NeedsRedraw = 0;
138     tellwhat = 0;
139
140     if (RestoreSaved) {
141         copyn(InputBuf, SavedBuf.s, INBUFSIZE);/*FIXBUF*/
142         LastChar = InputBuf + LastSaved;
143         Cursor = InputBuf + CursSaved;
144         Hist_num = HistSaved;
145         HistSaved = 0;
146         RestoreSaved = 0;
147     }
148     if (HistSaved) {
149         Hist_num = HistSaved;
150         GetHistLine();
151         HistSaved = 0;
152     }
153     if (Expand) {
154         (void) e_up_hist(0);
155         Expand = 0;
156     }
157     Refresh();                  /* print the prompt */
158
159     for (num = OKCMD; num == OKCMD;) {  /* while still editing this line */
160 #ifdef DEBUG_EDIT
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");
175 #endif
176
177         /* if EOF or error */
178         if ((num = GetNextCommand(&cmdnum, &ch)) != OKCMD) {
179             break;
180         }
181
182         if (cmdnum >= NumFuns) {/* BUG CHECK command */
183 #ifdef DEBUG_EDIT
184             xprintf(CGETS(6, 1, "ERROR: illegal command from key 0%o\r\n"), ch);
185 #endif
186             continue;           /* try again */
187         }
188
189         /* now do the real command */
190         retval = (*CcFuncTbl[cmdnum]) (ch);
191
192         /* save the last command here */
193         LastCmd = cmdnum;
194
195         /* make sure fn is initialized */
196         fn = (retval == CC_COMPLETE_ALL) ? LIST_ALL : LIST;
197
198         /* use any return value */
199         switch (retval) {
200
201         case CC_REFRESH:
202             Refresh();
203             /*FALLTHROUGH*/
204         case CC_NORM:           /* normal char */
205             Argument = 1;
206             DoingArg = 0;
207             /*FALLTHROUGH*/
208         case CC_ARGHACK:        /* Suggested by Rich Salz */
209             /* <rsalz@pineapple.bbn.com> */
210             curchoice = -1;
211             curlen = (int) (LastChar - InputBuf);
212             break;              /* keep going... */
213
214         case CC_EOF:            /* end of file typed */
215             curchoice = -1;
216             curlen = (int) (LastChar - InputBuf);
217             num = 0;
218             break;
219
220         case CC_WHICH:          /* tell what this command does */
221             tellwhat = 1;
222             *LastChar++ = '\n'; /* for the benifit of CSH */
223             num = (int) (LastChar - InputBuf);  /* number characters read */
224             break;
225
226         case CC_NEWLINE:        /* normal end of line */
227             curlen = 0;
228             curchoice = -1;
229             matchval = 1;
230             if (crct && crct->vec != NULL && (!Strcmp(*(crct->vec), STRcmd) ||
231                          !Strcmp(*(crct->vec), STRall))) {
232                 Char *Origin;
233
234                 PastBottom();
235                 Origin = Strsave(InputBuf);
236                 cleanup_push(Origin, xfree);
237                 SaveChar = LastChar;
238                 if (SpellLine(!Strcmp(*(crct->vec), STRcmd)) == 1) {
239                     Char *Change;
240
241                     PastBottom();
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 */
247                     SoundBeep();
248                     printprompt(2, short2str(Change));
249                     cleanup_until(Change);
250                     Refresh();
251                     if (xread(SHIN, &tch, 1) < 0) {
252 #ifdef convex
253                         /*
254                          * need to print error message in case file
255                          * is migrated
256                          */
257                         if (errno)
258                             stderror(ERR_SYSTEM, progname, strerror(errno));
259 #else
260                         cleanup_until(Origin);
261                         break;
262 #endif
263                     }
264                     ch = tch;
265                     if (ch == 'y' || ch == ' ') {
266                         LastChar = CorrChar;    /* Restore the corrected end */
267                         xprintf("%s", CGETS(6, 2, "yes\n"));
268                     }
269                     else {
270                         Strcpy(InputBuf, Origin);
271                         LastChar = SaveChar;
272                         if (ch == 'e') {
273                             xprintf("%s", CGETS(6, 3, "edit\n"));
274                             *LastChar-- = '\0';
275                             Cursor = LastChar;
276                             printprompt(3, NULL);
277                             ClearLines();
278                             ClearDisp();
279                             Refresh();
280                             cleanup_until(Origin);
281                             break;
282                         }
283                         else if (ch == 'a') {
284                             xprintf("%s", CGETS(6, 4, "abort\n"));
285                             LastChar = InputBuf;   /* Null the current line */
286                             Cursor = LastChar;
287                             printprompt(0, NULL);
288                             Refresh();
289                             cleanup_until(Origin);
290                             break;
291                         }
292                         xprintf("%s", CGETS(6, 5, "no\n"));
293                     }
294                     flush();
295                 }
296                 cleanup_until(Origin);
297             } else if (crct && crct->vec != NULL &&
298                 !Strcmp(*(crct->vec), STRcomplete)) {
299                 if (LastChar > InputBuf && LastChar[-1] == '\n') {
300                     LastChar[-1] = '\0';
301                     LastChar--;
302                     Cursor = LastChar;
303                 }
304                 match_unique_match = 1;  /* match unique matches */
305                 matchval = CompleteLine();
306                 match_unique_match = 0;
307                 curlen = (int) (LastChar - InputBuf);
308                 if (matchval != 1) {
309                     PastBottom();
310                 }
311                 if (matchval == 0) {
312                     xprintf("%s", CGETS(6, 6, "No matching command\n"));
313                 } else if (matchval == 2) {
314                     xprintf("%s", CGETS(6, 7, "Ambiguous command\n"));
315                 }
316                 if (NeedsRedraw) {
317                     ClearLines();
318                     ClearDisp();
319                     NeedsRedraw = 0;
320                 }
321                 Refresh();
322                 Argument = 1;
323                 DoingArg = 0;
324                 if (matchval == 1) {
325                     PastBottom();
326                     *LastChar++ = '\n';
327                     *LastChar = '\0';
328                 }
329                 curlen = (int) (LastChar - InputBuf);
330             }
331             else
332                 PastBottom();
333
334             if (matchval == 1) {
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);
339                 /*
340                  * For continuation lines, we set the prompt to prompt 2
341                  */
342                 printprompt(1, NULL);
343             }
344             break;
345
346         case CC_CORRECT:
347             if (tenematch(InputBuf, Cursor - InputBuf, SPELL) < 0)
348                 SoundBeep();            /* Beep = No match/ambiguous */
349             curlen = Repair();
350             break;
351
352         case CC_CORRECT_L:
353             if (SpellLine(FALSE) < 0)
354                 SoundBeep();            /* Beep = No match/ambiguous */
355             curlen = Repair();
356             break;
357
358
359         case CC_COMPLETE:
360         case CC_COMPLETE_ALL:
361         case CC_COMPLETE_FWD:
362         case CC_COMPLETE_BACK:
363             switch (retval) {
364             case CC_COMPLETE:
365                 fn = RECOGNIZE;
366                 curlen = (int) (LastChar - InputBuf);
367                 curchoice = -1;
368                 rotate = 0;
369                 break;
370             case CC_COMPLETE_ALL:
371                 fn = RECOGNIZE_ALL;
372                 curlen = (int) (LastChar - InputBuf);
373                 curchoice = -1;
374                 rotate = 0;
375                 break;
376             case CC_COMPLETE_FWD:
377                 fn = RECOGNIZE_SCROLL;
378                 curchoice++;
379                 rotate = 1;
380                 break;
381             case CC_COMPLETE_BACK:
382                 fn = RECOGNIZE_SCROLL;
383                 curchoice--;
384                 rotate = 1;
385                 break;
386             default:
387                 abort();
388             }
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] =
394                         InputBuf[idx];
395                 LastChar = InputBuf + curlen;
396                 Cursor = Cursor - newlen + curlen;
397             }
398             curlen = (int) (LastChar - InputBuf);
399
400
401             nr_history_exp = 0;
402             autoexpand = varval(STRautoexpand);
403             if (autoexpand != STRNULL)
404                 nr_history_exp += ExpandHistory();
405
406             /* try normal expansion only if no history references were found */
407             if (nr_history_exp == 0 ||
408                 Strcmp(autoexpand, STRonlyhistory) != 0) {
409                 /*
410                  * Modified by Martin Boyer (gamin@ireq-robot.hydro.qc.ca):
411                  * A separate variable now controls beeping after
412                  * completion, independently of autolisting.
413                  */
414                 expnum = (int) (Cursor - InputBuf);
415                 switch (matchval = tenematch(InputBuf, Cursor-InputBuf, fn)){
416                 case 1:
417                     if (non_unique_match && matchbeep &&
418                         matchbeep->vec != NULL &&
419                         (Strcmp(*(matchbeep->vec), STRnotunique) == 0))
420                         SoundBeep();
421                     break;
422                 case 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)
427                             SoundBeep();
428                     }
429                     else
430                         SoundBeep();
431                     break;
432                 default:
433                     if (matchval < 0) { /* Error from tenematch */
434                         curchoice = -1;
435                         SoundBeep();
436                         break;
437                     }
438                     if (matchbeep && matchbeep->vec != NULL) {
439                         if ((Strcmp(*(matchbeep->vec), STRambiguous) == 0 ||
440                              Strcmp(*(matchbeep->vec), STRnotunique) == 0))
441                             SoundBeep();
442                     }
443                     else
444                         SoundBeep();
445                     /*
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.)
450                      */
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 */
456                             MarkIsSet = 0;
457                             ClearLines();
458                             ClearDisp();
459                             Refresh();
460                             MarkIsSet = 1;
461                         }
462                         PastBottom();
463                         fn = (retval == CC_COMPLETE_ALL) ? LIST_ALL : LIST;
464                         (void) tenematch(InputBuf, Cursor-InputBuf, fn);
465                     }
466                     break;
467                 }
468             }
469             if (NeedsRedraw) {
470                 PastBottom();
471                 ClearLines();
472                 ClearDisp();
473                 NeedsRedraw = 0;
474             }
475             Refresh();
476             Argument = 1;
477             DoingArg = 0;
478             break;
479
480         case CC_LIST_CHOICES:
481         case CC_LIST_ALL:
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] =
487                         InputBuf[idx];
488                 LastChar = InputBuf + curlen;
489                 Cursor = Cursor - newlen + curlen;
490             }
491             curlen = (int) (LastChar - InputBuf);
492             if (curchoice >= 0)
493                 curchoice--;
494
495             fn = (retval == CC_LIST_ALL) ? LIST_ALL : LIST;
496             /* should catch ^C here... */
497             if (tenematch(InputBuf, Cursor - InputBuf, fn) < 0)
498                 SoundBeep();
499             Refresh();
500             Argument = 1;
501             DoingArg = 0;
502             break;
503
504
505         case CC_LIST_GLOB:
506             if (tenematch(InputBuf, Cursor - InputBuf, GLOB) < 0)
507                 SoundBeep();
508             curlen = Repair();
509             break;
510
511         case CC_EXPAND_GLOB:
512             if (tenematch(InputBuf, Cursor - InputBuf, GLOB_EXPAND) <= 0)
513                 SoundBeep();            /* Beep = No match */
514             curlen = Repair();
515             break;
516
517         case CC_NORMALIZE_PATH:
518             if (tenematch(InputBuf, Cursor - InputBuf, PATH_NORMALIZE) <= 0)
519                 SoundBeep();            /* Beep = No match */
520             curlen = Repair();
521             break;
522
523         case CC_EXPAND_VARS:
524             if (tenematch(InputBuf, Cursor - InputBuf, VARS_EXPAND) <= 0)
525                 SoundBeep();            /* Beep = No match */
526             curlen = Repair();
527             break;
528
529         case CC_NORMALIZE_COMMAND:
530             if (tenematch(InputBuf, Cursor - InputBuf, COMMAND_NORMALIZE) <= 0)
531                 SoundBeep();            /* Beep = No match */
532             curlen = Repair();
533             break;
534
535         case CC_HELPME:
536             xputchar('\n');
537             /* should catch ^C here... */
538             (void) tenematch(InputBuf, LastChar - InputBuf, PRINT_HELP);
539             Refresh();
540             Argument = 1;
541             DoingArg = 0;
542             curchoice = -1;
543             curlen = (int) (LastChar - InputBuf);
544             break;
545
546         case CC_FATAL:          /* fatal error, reset to known state */
547 #ifdef DEBUG_EDIT
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 */
554             Argument = 1;
555             DoingArg = 0;
556             curchoice = -1;
557             curlen = (int) (LastChar - InputBuf);
558             break;
559
560         case CC_ERROR:
561         default:                /* functions we don't know about */
562             if (adrof(STRhighlight)) {
563                 ClearLines();
564                 ClearDisp();
565                 Refresh();
566             }
567             DoingArg = 0;
568             Argument = 1;
569             SoundBeep();
570             flush();
571             curchoice = -1;
572             curlen = (int) (LastChar - InputBuf);
573             break;
574         }
575     }
576     (void) Cookedmode();        /* make sure the tty is set up correctly */
577     GettingInput = 0;
578     flush();                    /* flush any buffered output */
579     return num;
580 }
581
582 void
583 PushMacro(Char *str)
584 {
585     if (str != NULL && MacroLvl + 1 < MAXMACROLEVELS) {
586         MacroLvl++;
587         KeyMacro[MacroLvl] = str;
588     }
589     else {
590         SoundBeep();
591         flush();
592     }
593 }
594
595 struct eval1_state
596 {
597     Char **evalvec, *evalp;
598 };
599
600 static void
601 eval1_cleanup(void *xstate)
602 {
603     struct eval1_state *state;
604
605     state = xstate;
606     evalvec = state->evalvec;
607     evalp = state->evalp;
608     doneinp = 0;
609 }
610
611 /*
612  * Like eval, only using the current file descriptors
613  */
614 static void
615 doeval1(Char **v)
616 {
617     struct eval1_state state;
618     Char  **gv;
619     int gflag;
620
621     gflag = tglob(v);
622     if (gflag) {
623         gv = v = globall(v, gflag);
624         if (v == 0)
625             stderror(ERR_NOMATCH);
626         v = copyblk(v);
627     }
628     else {
629         gv = NULL;
630         v = copyblk(v);
631         trim(v);
632     }
633     if (gv)
634         cleanup_push(gv, blk_cleanup);
635
636     state.evalvec = evalvec;
637     state.evalp = evalp;
638     evalvec = v;
639     evalp = 0;
640     cleanup_push(&state, eval1_cleanup);
641     process(0);
642     cleanup_until(&state);
643     if (gv)
644         cleanup_until(gv);
645 }
646
647 static void
648 RunCommand(Char *str)
649 {
650     Char *cmd[2];
651
652     xputchar('\n');     /* Start on a clean line */
653
654     cmd[0] = str;
655     cmd[1] = NULL;
656
657     (void) Cookedmode();
658     GettingInput = 0;
659
660     doeval1(cmd);
661
662     (void) Rawmode();
663     GettingInput = 1;
664
665     ClearLines();
666     ClearDisp();
667     NeedsRedraw = 0;
668     Refresh();
669 }
670
671 static int
672 GetNextCommand(KEYCMD *cmdnum, Char *ch)
673 {
674     KEYCMD  cmd = 0;
675     int     num;
676
677     while (cmd == 0 || cmd == F_XKEY) {
678         if ((num = GetNextChar(ch)) != 1) {     /* if EOF or error */
679             return num;
680         }
681 #ifdef  KANJI
682         if (
683 #ifdef DSPMBYTE
684              _enable_mbdisp &&
685 #else
686              MB_LEN_MAX == 1 &&
687 #endif
688              !adrof(STRnokanji) && (*ch & META)) {
689             MetaNext = 0;
690             cmd = F_INSERT;
691             break;
692         }
693         else
694 #endif /* KANJI */
695         if (MetaNext) {
696             MetaNext = 0;
697             *ch |= META;
698         }
699         /* XXX: This needs to be fixed so that we don't just truncate
700          * the character, we unquote it.
701          */
702         if (*ch < NT_NUM_KEYS)
703             cmd = CurrentKeyMap[*ch];
704         else
705 #ifdef WINNT_NATIVE
706             cmd = CurrentKeyMap[(unsigned char) *ch];
707 #else
708             cmd = F_INSERT;
709 #endif
710         if (cmd == F_XKEY) {
711             XmapVal val;
712             CStr cstr;
713             cstr.buf = ch;
714             cstr.len = 1;
715             switch (GetXkey(&cstr, &val)) {
716             case XK_CMD:
717                 cmd = val.cmd;
718                 break;
719             case XK_STR:
720                 PushMacro(val.str.buf);
721                 break;
722             case XK_EXE:
723                 RunCommand(val.str.buf);
724                 break;
725             default:
726                 abort();
727                 break;
728             }
729         }
730         if (!AltKeyMap) 
731             CurrentKeyMap = CcKeyMap;
732     }
733     *cmdnum = cmd;
734     return OKCMD;
735 }
736
737 static Char ungetchar;
738 static int haveungetchar;
739
740 void
741 UngetNextChar(Char cp)
742 {
743     ungetchar = cp;
744     haveungetchar = 1;
745 }
746
747 int
748 GetNextChar(Char *cp)
749 {
750     int num_read;
751     int     tried = 0;
752     char cbuf[MB_LEN_MAX];
753     size_t cbp;
754
755     if (haveungetchar) {
756         haveungetchar = 0;
757         *cp = ungetchar;
758         return 1;
759     }
760     for (;;) {
761         if (MacroLvl < 0) {
762             if (!Load_input_line())
763                 break;
764         }
765         if (*KeyMacro[MacroLvl] == 0) {
766             MacroLvl--;
767             continue;
768         }
769         *cp = *KeyMacro[MacroLvl]++ & CHAR;
770         if (*KeyMacro[MacroLvl] == 0) { /* Needed for QuoteMode On */
771             MacroLvl--;
772         }
773         return (1);
774     }
775
776     if (Rawmode() < 0)          /* make sure the tty is set up correctly */
777         return 0;               /* oops: SHIN was closed */
778
779 #ifdef WINNT_NATIVE
780     __nt_want_vcode = 1;
781 #endif /* WINNT_NATIVE */
782 #ifdef SIG_WINDOW
783     if (windowchg)
784         (void) check_window_size(0);    /* for window systems */
785 #endif /* SIG_WINDOW */
786     cbp = 0;
787     for (;;) {
788         while ((num_read = xread(SHIN, cbuf + cbp, 1)) == -1) {
789             if (!tried && fixio(SHIN, errno) != -1)
790                 tried = 1;
791             else {
792 # ifdef convex
793                 /* need to print error message in case the file is migrated */
794                 stderror(ERR_SYSTEM, progname, strerror(errno));
795 # endif  /* convex */
796 # ifdef WINNT_NATIVE
797                 __nt_want_vcode = 0;
798 # endif /* WINNT_NATIVE */
799                 *cp = '\0'; /* Loses possible partial character */
800                 return -1;
801             }
802         }
803         if (AsciiOnly) {
804             *cp = (unsigned char)*cbuf;
805         } else {
806             cbp++;
807             if (normal_mbtowc(cp, cbuf, cbp) == -1) {
808                 reset_mbtowc();
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;
813             }
814         }
815         break;
816     }
817 #ifdef WINNT_NATIVE
818     /* This is the part that doesn't work with WIDE_STRINGS */
819     if (__nt_want_vcode == 2)
820         *cp = __nt_vcode;
821     __nt_want_vcode = 0;
822 #endif /* WINNT_NATIVE */
823     return num_read;
824 }
825
826 /*
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.
830  * Return value:
831  * -1: Something was incorrectible, and nothing was corrected
832  *  0: Everything was correct
833  *  1: Something was corrected
834  */
835 static int
836 SpellLine(int cmdonly)
837 {
838     int     endflag, matchval;
839     Char   *argptr, *OldCursor, *OldLastChar;
840
841     OldLastChar = LastChar;
842     OldCursor = Cursor;
843     argptr = InputBuf;
844     endflag = 1;
845     matchval = 0;
846     do {
847         while (ismetahash(*argptr) || iscmdmeta(*argptr))
848             argptr++;
849         for (Cursor = argptr;
850              *Cursor != '\0' && ((Cursor != argptr && Cursor[-1] == '\\') ||
851                                  (!ismetahash(*Cursor) && !iscmdmeta(*Cursor)));
852              Cursor++)
853              continue;
854         if (*Cursor == '\0') {
855             Cursor = LastChar;
856             if (LastChar[-1] == '\n')
857                 Cursor--;
858             endflag = 0;
859         }
860         if (!MISMATCH(*argptr) &&
861             (!cmdonly || starting_a_command(argptr, InputBuf))) {
862 #ifdef WINNT_NATIVE
863             /*
864              * This hack avoids correcting drive letter changes
865              */
866             if((Cursor - InputBuf) != 2 || (char)InputBuf[1] != ':')
867 #endif /* WINNT_NATIVE */
868             {
869 #ifdef HASH_SPELL_CHECK
870                 Char save;
871                 size_t len = Cursor - InputBuf;
872
873                 save = InputBuf[len];
874                 InputBuf[len] = '\0';
875                 if (find_cmd(InputBuf, 0) != 0) {
876                     InputBuf[len] = save;
877                     argptr = Cursor;
878                     continue;
879                 }
880                 InputBuf[len] = save;
881 #endif /* HASH_SPELL_CHECK */
882                 switch (tenematch(InputBuf, Cursor - InputBuf, SPELL)) {
883                 case 1:         /* corrected */
884                     matchval = 1;
885                     break;
886                 case -1:                /* couldn't be corrected */
887                     if (!matchval)
888                         matchval = -1;
889                     break;
890                 default:                /* was correct */
891                     break;
892                 }
893             }
894             if (LastChar != OldLastChar) {
895                 if (argptr < OldCursor)
896                     OldCursor += (LastChar - OldLastChar);
897                 OldLastChar = LastChar;
898             }
899         }
900         argptr = Cursor;
901     } while (endflag);
902     Cursor = OldCursor;
903     return matchval;
904 }
905
906 /*
907  * CompleteLine - do command completion on the entire command line
908  * (which may have trailing newline).
909  * Return value:
910  *  0: No command matched or failure
911  *  1: One command matched
912  *  2: Several commands matched
913  */
914 static int
915 CompleteLine(void)
916 {
917     int     endflag, tmatch;
918     Char   *argptr, *OldCursor, *OldLastChar;
919
920     OldLastChar = LastChar;
921     OldCursor = Cursor;
922     argptr = InputBuf;
923     endflag = 1;
924     do {
925         while (ismetahash(*argptr) || iscmdmeta(*argptr))
926             argptr++;
927         for (Cursor = argptr;
928              *Cursor != '\0' && ((Cursor != argptr && Cursor[-1] == '\\') ||
929                                  (!ismetahash(*Cursor) && !iscmdmeta(*Cursor)));
930              Cursor++)
931              continue;
932         if (*Cursor == '\0') {
933             Cursor = LastChar;
934             if (LastChar[-1] == '\n')
935                 Cursor--;
936             endflag = 0;
937         }
938         if (!MISMATCH(*argptr) && starting_a_command(argptr, InputBuf)) {
939             tmatch = tenematch(InputBuf, Cursor - InputBuf, RECOGNIZE);
940             if (tmatch <= 0) {
941                 return 0;
942             } else if (tmatch > 1) {
943                 return 2;
944             }
945             if (LastChar != OldLastChar) {
946                 if (argptr < OldCursor)
947                     OldCursor += (LastChar - OldLastChar);
948                 OldLastChar = LastChar;
949             }
950         }
951         argptr = Cursor;
952     } while (endflag);
953     Cursor = OldCursor;
954     return 1;
955 }
956