taskqueue.9 - Update to reflect tq changes
[dragonfly.git] / contrib / tcsh-6 / ed.inputl.c
1 /* $Header: /p/tcsh/cvsroot/tcsh/ed.inputl.c,v 3.66 2006/11/29 22:32:24 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.66 2006/11/29 22:32:24 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[] = 
49     {'!', '^' , '\\', '-', '%', '\0', '"', '\'', '`', '\0' };
50
51 static  int     Repair          (void);
52 static  int     GetNextCommand  (KEYCMD *, Char *);
53 static  int     SpellLine       (int);
54 static  int     CompleteLine    (void);
55 static  void    RunCommand      (Char *);
56 static  void    doeval1         (Char **);
57
58 static int rotate = 0;
59
60
61 static int
62 Repair(void)
63 {
64     if (NeedsRedraw) {
65         ClearLines();
66         ClearDisp();
67         NeedsRedraw = 0;
68     }
69     Refresh();
70     Argument = 1;
71     DoingArg = 0;
72     curchoice = -1;
73     return (int) (LastChar - InputBuf);
74 }
75
76 /* CCRETVAL */
77 int
78 Inputl(void)
79 {
80     CCRETVAL retval;
81     KEYCMD  cmdnum = 0;
82     unsigned char tch;          /* the place where read() goes */
83     Char    ch;
84     int     num;                /* how many chars we have read at NL */
85     int     expnum;
86     struct varent *crct = inheredoc ? NULL : adrof(STRcorrect);
87     struct varent *autol = adrof(STRautolist);
88     struct varent *matchbeep = adrof(STRmatchbeep);
89     struct varent *imode = adrof(STRinputmode);
90     Char   *SaveChar, *CorrChar;
91     int     matchval;           /* from tenematch() */
92     COMMAND fn;
93     int curlen = 0;
94     int newlen;
95     int idx;
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(CGETS(6, 2, "yes\n"));
264                     }
265                     else {
266                         Strcpy(InputBuf, Origin);
267                         LastChar = SaveChar;
268                         if (ch == 'e') {
269                             xprintf(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(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(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(CGETS(6, 6, "No matching command\n"));
309                 } else if (matchval == 2) {
310                     xprintf(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             if (adrof(STRautoexpand))
398                 (void) e_expand_history(0);
399             /*
400              * Modified by Martin Boyer (gamin@ireq-robot.hydro.qc.ca):
401              * A separate variable now controls beeping after
402              * completion, independently of autolisting.
403              */
404             expnum = (int) (Cursor - InputBuf);
405             switch (matchval = tenematch(InputBuf, Cursor-InputBuf, fn)){
406             case 1:
407                 if (non_unique_match && matchbeep && matchbeep->vec != NULL &&
408                     (Strcmp(*(matchbeep->vec), STRnotunique) == 0))
409                     SoundBeep();
410                 break;
411             case 0:
412                 if (matchbeep && matchbeep->vec != NULL) {
413                     if (Strcmp(*(matchbeep->vec), STRnomatch) == 0 ||
414                         Strcmp(*(matchbeep->vec), STRambiguous) == 0 ||
415                         Strcmp(*(matchbeep->vec), STRnotunique) == 0)
416                         SoundBeep();
417                 }
418                 else
419                     SoundBeep();
420                 break;
421             default:
422                 if (matchval < 0) {     /* Error from tenematch */
423                     curchoice = -1;
424                     SoundBeep();
425                     break;
426                 }
427                 if (matchbeep && matchbeep->vec != NULL) {
428                     if ((Strcmp(*(matchbeep->vec), STRambiguous) == 0 ||
429                          Strcmp(*(matchbeep->vec), STRnotunique) == 0))
430                         SoundBeep();
431                 }
432                 else
433                     SoundBeep();
434                 /*
435                  * Addition by David C Lawrence <tale@pawl.rpi.edu>: If an 
436                  * attempted completion is ambiguous, list the choices.  
437                  * (PWP: this is the best feature addition to tcsh I have 
438                  * seen in many months.)
439                  */
440                 if (autol && autol->vec != NULL && 
441                     (Strcmp(*(autol->vec), STRambiguous) != 0 || 
442                                      expnum == Cursor - InputBuf)) {
443                     if (adrof(STRhighlight) && MarkIsSet) {
444                         /* clear highlighting before showing completions */
445                         MarkIsSet = 0;
446                         ClearLines();
447                         ClearDisp();
448                         Refresh();
449                         MarkIsSet = 1;
450                     }
451                     PastBottom();
452                     fn = (retval == CC_COMPLETE_ALL) ? LIST_ALL : LIST;
453                     (void) tenematch(InputBuf, Cursor-InputBuf, fn);
454                 }
455                 break;
456             }
457             if (NeedsRedraw) {
458                 PastBottom();
459                 ClearLines();
460                 ClearDisp();
461                 NeedsRedraw = 0;
462             }
463             Refresh();
464             Argument = 1;
465             DoingArg = 0;
466             break;
467
468         case CC_LIST_CHOICES:
469         case CC_LIST_ALL:
470             if (InputBuf[curlen] && rotate) {
471                 newlen = (int) (LastChar - InputBuf);
472                 for (idx = (int) (Cursor - InputBuf); 
473                      idx <= newlen; idx++)
474                         InputBuf[idx - newlen + curlen] =
475                         InputBuf[idx];
476                 LastChar = InputBuf + curlen;
477                 Cursor = Cursor - newlen + curlen;
478             }
479             curlen = (int) (LastChar - InputBuf);
480             if (curchoice >= 0)
481                 curchoice--;
482
483             fn = (retval == CC_LIST_ALL) ? LIST_ALL : LIST;
484             /* should catch ^C here... */
485             if (tenematch(InputBuf, Cursor - InputBuf, fn) < 0)
486                 SoundBeep();
487             Refresh();
488             Argument = 1;
489             DoingArg = 0;
490             break;
491
492
493         case CC_LIST_GLOB:
494             if (tenematch(InputBuf, Cursor - InputBuf, GLOB) < 0)
495                 SoundBeep();
496             curlen = Repair();
497             break;
498
499         case CC_EXPAND_GLOB:
500             if (tenematch(InputBuf, Cursor - InputBuf, GLOB_EXPAND) <= 0)
501                 SoundBeep();            /* Beep = No match */
502             curlen = Repair();
503             break;
504
505         case CC_NORMALIZE_PATH:
506             if (tenematch(InputBuf, Cursor - InputBuf, PATH_NORMALIZE) <= 0)
507                 SoundBeep();            /* Beep = No match */
508             curlen = Repair();
509             break;
510
511         case CC_EXPAND_VARS:
512             if (tenematch(InputBuf, Cursor - InputBuf, VARS_EXPAND) <= 0)
513                 SoundBeep();            /* Beep = No match */
514             curlen = Repair();
515             break;
516
517         case CC_NORMALIZE_COMMAND:
518             if (tenematch(InputBuf, Cursor - InputBuf, COMMAND_NORMALIZE) <= 0)
519                 SoundBeep();            /* Beep = No match */
520             curlen = Repair();
521             break;
522
523         case CC_HELPME:
524             xputchar('\n');
525             /* should catch ^C here... */
526             (void) tenematch(InputBuf, LastChar - InputBuf, PRINT_HELP);
527             Refresh();
528             Argument = 1;
529             DoingArg = 0;
530             curchoice = -1;
531             curlen = (int) (LastChar - InputBuf);
532             break;
533
534         case CC_FATAL:          /* fatal error, reset to known state */
535 #ifdef DEBUG_EDIT
536             xprintf(CGETS(7, 8, "*** editor fatal ERROR ***\r\n\n"));
537 #endif                          /* DEBUG_EDIT */
538             /* put (real) cursor in a known place */
539             ClearDisp();        /* reset the display stuff */
540             ResetInLine(1);     /* reset the input pointers */
541             Refresh();          /* print the prompt again */
542             Argument = 1;
543             DoingArg = 0;
544             curchoice = -1;
545             curlen = (int) (LastChar - InputBuf);
546             break;
547
548         case CC_ERROR:
549         default:                /* functions we don't know about */
550             if (adrof(STRhighlight)) {
551                 ClearLines();
552                 ClearDisp();
553                 Refresh();
554             }
555             DoingArg = 0;
556             Argument = 1;
557             SoundBeep();
558             flush();
559             curchoice = -1;
560             curlen = (int) (LastChar - InputBuf);
561             break;
562         }
563     }
564     (void) Cookedmode();        /* make sure the tty is set up correctly */
565     GettingInput = 0;
566     flush();                    /* flush any buffered output */
567     return num;
568 }
569
570 void
571 PushMacro(Char *str)
572 {
573     if (str != NULL && MacroLvl + 1 < MAXMACROLEVELS) {
574         MacroLvl++;
575         KeyMacro[MacroLvl] = str;
576     }
577     else {
578         SoundBeep();
579         flush();
580     }
581 }
582
583 struct eval1_state
584 {
585     Char **evalvec, *evalp;
586 };
587
588 static void
589 eval1_cleanup(void *xstate)
590 {
591     struct eval1_state *state;
592
593     state = xstate;
594     evalvec = state->evalvec;
595     evalp = state->evalp;
596     doneinp = 0;
597 }
598
599 /*
600  * Like eval, only using the current file descriptors
601  */
602 static void
603 doeval1(Char **v)
604 {
605     struct eval1_state state;
606     Char  **gv;
607     int gflag;
608
609     gflag = tglob(v);
610     if (gflag) {
611         gv = v = globall(v, gflag);
612         if (v == 0)
613             stderror(ERR_NOMATCH);
614         v = copyblk(v);
615     }
616     else {
617         gv = NULL;
618         v = copyblk(v);
619         trim(v);
620     }
621     if (gv)
622         cleanup_push(gv, blk_cleanup);
623
624     state.evalvec = evalvec;
625     state.evalp = evalp;
626     evalvec = v;
627     evalp = 0;
628     cleanup_push(&state, eval1_cleanup);
629     process(0);
630     cleanup_until(&state);
631     if (gv)
632         cleanup_until(gv);
633 }
634
635 static void
636 RunCommand(Char *str)
637 {
638     Char *cmd[2];
639
640     xputchar('\n');     /* Start on a clean line */
641
642     cmd[0] = str;
643     cmd[1] = NULL;
644
645     (void) Cookedmode();
646     GettingInput = 0;
647
648     doeval1(cmd);
649
650     (void) Rawmode();
651     GettingInput = 1;
652
653     ClearLines();
654     ClearDisp();
655     NeedsRedraw = 0;
656     Refresh();
657 }
658
659 static int
660 GetNextCommand(KEYCMD *cmdnum, Char *ch)
661 {
662     KEYCMD  cmd = 0;
663     int     num;
664
665     while (cmd == 0 || cmd == F_XKEY) {
666         if ((num = GetNextChar(ch)) != 1) {     /* if EOF or error */
667             return num;
668         }
669 #ifdef  KANJI
670         if (
671 #ifdef DSPMBYTE
672              _enable_mbdisp &&
673 #else
674              MB_LEN_MAX == 1 &&
675 #endif
676              !adrof(STRnokanji) && (*ch & META)) {
677             MetaNext = 0;
678             cmd = F_INSERT;
679             break;
680         }
681         else
682 #endif /* KANJI */
683         if (MetaNext) {
684             MetaNext = 0;
685             *ch |= META;
686         }
687         /* XXX: This needs to be fixed so that we don't just truncate
688          * the character, we unquote it.
689          */
690         if (*ch < NT_NUM_KEYS)
691             cmd = CurrentKeyMap[*ch];
692         else
693 #ifdef WINNT_NATIVE
694             cmd = CurrentKeyMap[(unsigned char) *ch];
695 #else
696             cmd = F_INSERT;
697 #endif
698         if (cmd == F_XKEY) {
699             XmapVal val;
700             CStr cstr;
701             cstr.buf = ch;
702             cstr.len = 1;
703             switch (GetXkey(&cstr, &val)) {
704             case XK_CMD:
705                 cmd = val.cmd;
706                 break;
707             case XK_STR:
708                 PushMacro(val.str.buf);
709                 break;
710             case XK_EXE:
711                 RunCommand(val.str.buf);
712                 break;
713             default:
714                 abort();
715                 break;
716             }
717         }
718         if (!AltKeyMap) 
719             CurrentKeyMap = CcKeyMap;
720     }
721     *cmdnum = cmd;
722     return OKCMD;
723 }
724
725 static Char ungetchar;
726 static int haveungetchar;
727
728 void
729 UngetNextChar(Char cp)
730 {
731     ungetchar = cp;
732     haveungetchar = 1;
733 }
734
735 int
736 GetNextChar(Char *cp)
737 {
738     int num_read;
739     int     tried = 0;
740     char cbuf[MB_LEN_MAX];
741     size_t cbp;
742
743     if (haveungetchar) {
744         haveungetchar = 0;
745         *cp = ungetchar;
746         return 1;
747     }
748     for (;;) {
749         if (MacroLvl < 0) {
750             if (!Load_input_line())
751                 break;
752         }
753         if (*KeyMacro[MacroLvl] == 0) {
754             MacroLvl--;
755             continue;
756         }
757         *cp = *KeyMacro[MacroLvl]++ & CHAR;
758         if (*KeyMacro[MacroLvl] == 0) { /* Needed for QuoteMode On */
759             MacroLvl--;
760         }
761         return (1);
762     }
763
764     if (Rawmode() < 0)          /* make sure the tty is set up correctly */
765         return 0;               /* oops: SHIN was closed */
766
767 #ifdef WINNT_NATIVE
768     __nt_want_vcode = 1;
769 #endif /* WINNT_NATIVE */
770 #ifdef SIG_WINDOW
771     if (windowchg)
772         (void) check_window_size(0);    /* for window systems */
773 #endif /* SIG_WINDOW */
774     cbp = 0;
775     for (;;) {
776         while ((num_read = xread(SHIN, cbuf + cbp, 1)) == -1) {
777             if (!tried && fixio(SHIN, errno) != -1)
778                 tried = 1;
779             else {
780 # ifdef convex
781                 /* need to print error message in case the file is migrated */
782                 stderror(ERR_SYSTEM, progname, strerror(errno));
783 # endif  /* convex */
784 # ifdef WINNT_NATIVE
785                 __nt_want_vcode = 0;
786 # endif /* WINNT_NATIVE */
787                 *cp = '\0'; /* Loses possible partial character */
788                 return -1;
789             }
790         }
791         if (AsciiOnly) {
792             *cp = (unsigned char)*cbuf;
793         } else {
794             cbp++;
795             if (normal_mbtowc(cp, cbuf, cbp) == -1) {
796                 reset_mbtowc();
797                 if (cbp < MB_CUR_MAX)
798                     continue; /* Maybe a partial character */
799                 /* And drop the following bytes, if any */
800                 *cp = (unsigned char)*cbuf | INVALID_BYTE;
801             }
802         }
803         break;
804     }
805 #ifdef WINNT_NATIVE
806     /* This is the part that doesn't work with WIDE_STRINGS */
807     if (__nt_want_vcode == 2)
808         *cp = __nt_vcode;
809     __nt_want_vcode = 0;
810 #endif /* WINNT_NATIVE */
811     return num_read;
812 }
813
814 /*
815  * SpellLine - do spelling correction on the entire command line
816  * (which may have trailing newline).
817  * If cmdonly is set, only check spelling of command words.
818  * Return value:
819  * -1: Something was incorrectible, and nothing was corrected
820  *  0: Everything was correct
821  *  1: Something was corrected
822  */
823 static int
824 SpellLine(int cmdonly)
825 {
826     int     endflag, matchval;
827     Char   *argptr, *OldCursor, *OldLastChar;
828
829     OldLastChar = LastChar;
830     OldCursor = Cursor;
831     argptr = InputBuf;
832     endflag = 1;
833     matchval = 0;
834     do {
835         while (ismetahash(*argptr) || iscmdmeta(*argptr))
836             argptr++;
837         for (Cursor = argptr;
838              *Cursor != '\0' && ((Cursor != argptr && Cursor[-1] == '\\') ||
839                                  (!ismetahash(*Cursor) && !iscmdmeta(*Cursor)));
840              Cursor++)
841              continue;
842         if (*Cursor == '\0') {
843             Cursor = LastChar;
844             if (LastChar[-1] == '\n')
845                 Cursor--;
846             endflag = 0;
847         }
848         /* Obey current history character settings */
849         mismatch[0] = HIST;
850         mismatch[1] = HISTSUB;
851         if (!Strchr(mismatch, *argptr) &&
852             (!cmdonly || starting_a_command(argptr, InputBuf))) {
853 #ifdef WINNT_NATIVE
854             /*
855              * This hack avoids correcting drive letter changes
856              */
857             if((Cursor - InputBuf) != 2 || (char)InputBuf[1] != ':')
858 #endif /* WINNT_NATIVE */
859             {
860 #ifdef HASH_SPELL_CHECK
861                 Char save;
862                 size_t len = Cursor - InputBuf;
863
864                 save = InputBuf[len];
865                 InputBuf[len] = '\0';
866                 if (find_cmd(InputBuf, 0) != 0) {
867                     InputBuf[len] = save;
868                     argptr = Cursor;
869                     continue;
870                 }
871                 InputBuf[len] = save;
872 #endif /* HASH_SPELL_CHECK */
873                 switch (tenematch(InputBuf, Cursor - InputBuf, SPELL)) {
874                 case 1:         /* corrected */
875                     matchval = 1;
876                     break;
877                 case -1:                /* couldn't be corrected */
878                     if (!matchval)
879                         matchval = -1;
880                     break;
881                 default:                /* was correct */
882                     break;
883                 }
884             }
885             if (LastChar != OldLastChar) {
886                 if (argptr < OldCursor)
887                     OldCursor += (LastChar - OldLastChar);
888                 OldLastChar = LastChar;
889             }
890         }
891         argptr = Cursor;
892     } while (endflag);
893     Cursor = OldCursor;
894     return matchval;
895 }
896
897 /*
898  * CompleteLine - do command completion on the entire command line
899  * (which may have trailing newline).
900  * Return value:
901  *  0: No command matched or failure
902  *  1: One command matched
903  *  2: Several commands matched
904  */
905 static int
906 CompleteLine(void)
907 {
908     int     endflag, tmatch;
909     Char   *argptr, *OldCursor, *OldLastChar;
910
911     OldLastChar = LastChar;
912     OldCursor = Cursor;
913     argptr = InputBuf;
914     endflag = 1;
915     do {
916         while (ismetahash(*argptr) || iscmdmeta(*argptr))
917             argptr++;
918         for (Cursor = argptr;
919              *Cursor != '\0' && ((Cursor != argptr && Cursor[-1] == '\\') ||
920                                  (!ismetahash(*Cursor) && !iscmdmeta(*Cursor)));
921              Cursor++)
922              continue;
923         if (*Cursor == '\0') {
924             Cursor = LastChar;
925             if (LastChar[-1] == '\n')
926                 Cursor--;
927             endflag = 0;
928         }
929         if (!Strchr(mismatch, *argptr) && starting_a_command(argptr, InputBuf)) {
930             tmatch = tenematch(InputBuf, Cursor - InputBuf, RECOGNIZE);
931             if (tmatch <= 0) {
932                 return 0;
933             } else if (tmatch > 1) {
934                 return 2;
935             }
936             if (LastChar != OldLastChar) {
937                 if (argptr < OldCursor)
938                     OldCursor += (LastChar - OldLastChar);
939                 OldLastChar = LastChar;
940             }
941         }
942         argptr = Cursor;
943     } while (endflag);
944     Cursor = OldCursor;
945     return 1;
946 }
947