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