Merge from vendor branch BINUTILS:
[dragonfly.git] / contrib / tcsh / ed.inputl.c
1 /* $Header: /src/pub/tcsh/ed.inputl.c,v 3.51 2002/06/25 19:02:11 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("$Id: ed.inputl.c,v 3.51 2002/06/25 19:02:11 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 (INBUFSIZE+INBUFSIZE)
42
43 /* ed.inputl -- routines to get a single line from the input. */
44
45 extern bool tellwhat;
46 extern bool MapsAreInited;
47 extern bool Tty_raw_mode;
48
49 /* mismatched first character */
50 static Char mismatch[] = 
51     {'!', '^' , '\\', '-', '%', '\0', '"', '\'', '`', '\0' };
52
53 static  int     Repair          __P((void));
54 static  int     GetNextCommand  __P((KEYCMD *, Char *));
55 static  int     SpellLine       __P((int));
56 static  int     CompleteLine    __P((void));
57 static  void    RunCommand      __P((Char *));
58 static  void    doeval1         __P((Char **));
59
60 static bool rotate = 0;
61
62
63 static int
64 Repair()
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()
81 {
82     CCRETVAL retval;
83     KEYCMD  cmdnum = 0;
84     extern KEYCMD NumFuns;
85     unsigned char tch;          /* the place where read() goes */
86     Char    ch;
87     int     num;                /* how many chars we have read at NL */
88     int     expnum;
89     struct varent *crct = inheredoc ? NULL : adrof(STRcorrect);
90     struct varent *autol = adrof(STRautolist);
91     struct varent *matchbeep = adrof(STRmatchbeep);
92     struct varent *imode = adrof(STRinputmode);
93     Char   *SaveChar, *CorrChar;
94     Char    Origin[INBUFSIZE], Change[INBUFSIZE];
95     int     matchval;           /* from tenematch() */
96     COMMAND fn;
97     int curlen = 0;
98     int newlen;
99     int idx;
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
139     if (tellwhat) {
140         copyn(InputBuf, WhichBuf, INBUFSIZE);
141         LastChar = InputBuf + (LastWhich - WhichBuf);
142         Cursor = InputBuf + (CursWhich - WhichBuf);
143         tellwhat = 0;
144         Hist_num = HistWhich;
145     }
146     if (Expand) {
147         (void) e_up_hist(0);
148         Expand = 0;
149     }
150     Refresh();                  /* print the prompt */
151
152     for (num = OKCMD; num == OKCMD;) {  /* while still editing this line */
153 #ifdef DEBUG_EDIT
154         if (Cursor > LastChar)
155             xprintf("Cursor > LastChar\r\n");
156         if (Cursor < InputBuf)
157             xprintf("Cursor < InputBuf\r\n");
158         if (Cursor > InputLim)
159             xprintf("Cursor > InputLim\r\n");
160         if (LastChar > InputLim)
161             xprintf("LastChar > InputLim\r\n");
162         if (InputLim != &InputBuf[INBUFSIZE - 2])
163             xprintf("InputLim != &InputBuf[INBUFSIZE-2]\r\n");
164         if ((!DoingArg) && (Argument != 1))
165             xprintf("(!DoingArg) && (Argument != 1)\r\n");
166         if (CcKeyMap[0] == 0)
167             xprintf("CcKeyMap[0] == 0 (maybe not inited)\r\n");
168 #endif
169
170         /* if EOF or error */
171         if ((num = GetNextCommand(&cmdnum, &ch)) != OKCMD) {
172             break;
173         }
174
175         if (cmdnum >= NumFuns) {/* BUG CHECK command */
176 #ifdef DEBUG_EDIT
177             xprintf(CGETS(6, 1, "ERROR: illegal command from key 0%o\r\n"), ch);
178 #endif
179             continue;           /* try again */
180         }
181
182         /* now do the real command */
183         retval = (*CcFuncTbl[cmdnum]) (ch);
184
185         /* save the last command here */
186         LastCmd = cmdnum;
187
188         /* make sure fn is initialized */
189         fn = (retval == CC_COMPLETE_ALL) ? LIST_ALL : LIST;
190
191         /* use any return value */
192         switch (retval) {
193
194         case CC_REFRESH:
195             Refresh();
196             /*FALLTHROUGH*/
197         case CC_NORM:           /* normal char */
198             Argument = 1;
199             DoingArg = 0;
200             /*FALLTHROUGH*/
201         case CC_ARGHACK:        /* Suggested by Rich Salz */
202             /* <rsalz@pineapple.bbn.com> */
203             curchoice = -1;
204             curlen = (int) (LastChar - InputBuf);
205             break;              /* keep going... */
206
207         case CC_EOF:            /* end of file typed */
208             curchoice = -1;
209             curlen = (int) (LastChar - InputBuf);
210             num = 0;
211             break;
212
213         case CC_WHICH:          /* tell what this command does */
214             tellwhat = 1;
215             copyn(WhichBuf, InputBuf, INBUFSIZE);
216             LastWhich = WhichBuf + (LastChar - InputBuf);
217             CursWhich = WhichBuf + (Cursor - InputBuf);
218             *LastChar++ = '\n'; /* for the benifit of CSH */
219             HistWhich = Hist_num;
220             Hist_num = 0;       /* for the history commands */
221             num = (int) (LastChar - InputBuf);  /* number characters read */
222             break;
223
224         case CC_NEWLINE:        /* normal end of line */
225             curlen = 0;
226             curchoice = -1;
227             matchval = 1;
228             if (crct && crct->vec != NULL && (!Strcmp(*(crct->vec), STRcmd) ||
229                          !Strcmp(*(crct->vec), STRall))) {
230                 PastBottom();
231                 copyn(Origin, InputBuf, INBUFSIZE);
232                 SaveChar = LastChar;
233                 if (SpellLine(!Strcmp(*(crct->vec), STRcmd)) == 1) {
234                     PastBottom();
235                     copyn(Change, InputBuf, INBUFSIZE);
236                     *Strchr(Change, '\n') = '\0';
237                     CorrChar = LastChar;        /* Save the corrected end */
238                     LastChar = InputBuf;        /* Null the current line */
239                     SoundBeep();
240                     printprompt(2, short2str(Change));
241                     Refresh();
242                     if (read(SHIN, (char *) &tch, 1) < 0)
243 #ifdef convex
244                         /*
245                          * need to print error message in case file
246                          * is migrated
247                          */
248                         if (errno && errno != EINTR)
249                             stderror(ERR_SYSTEM, progname, strerror(errno));
250 #else
251                         break;
252 #endif
253                     ch = tch;
254                     if (ch == 'y' || ch == ' ') {
255                         LastChar = CorrChar;    /* Restore the corrected end */
256                         xprintf(CGETS(6, 2, "yes\n"));
257                     }
258                     else {
259                         copyn(InputBuf, Origin, INBUFSIZE);
260                         LastChar = SaveChar;
261                         if (ch == 'e') {
262                             xprintf(CGETS(6, 3, "edit\n"));
263                             *LastChar-- = '\0';
264                             Cursor = LastChar;
265                             printprompt(3, NULL);
266                             ClearLines();
267                             ClearDisp();
268                             Refresh();
269                             break;
270                         }
271                         else if (ch == 'a') {
272                             xprintf(CGETS(6, 4, "abort\n"));
273                             LastChar = InputBuf;   /* Null the current line */
274                             Cursor = LastChar;
275                             printprompt(0, NULL);
276                             Refresh();
277                             break;
278                         }
279                         xprintf(CGETS(6, 5, "no\n"));
280                     }
281                     flush();
282                 }
283             } else if (crct && crct->vec != NULL &&
284                 !Strcmp(*(crct->vec), STRcomplete)) {
285                 if (LastChar > InputBuf && LastChar[-1] == '\n') {
286                     LastChar[-1] = '\0';
287                     LastChar--;
288                     Cursor = LastChar;
289                 }
290                 match_unique_match = 1;  /* match unique matches */
291                 matchval = CompleteLine();
292                 match_unique_match = 0;
293                 curlen = (int) (LastChar - InputBuf);
294                 if (matchval != 1) {
295                     PastBottom();
296                 }
297                 if (matchval == 0) {
298                     xprintf(CGETS(6, 6, "No matching command\n"));
299                 } else if (matchval == 2) {
300                     xprintf(CGETS(6, 7, "Ambiguous command\n"));
301                 }
302                 if (NeedsRedraw) {
303                     ClearLines();
304                     ClearDisp();
305                     NeedsRedraw = 0;
306                 }
307                 Refresh();
308                 Argument = 1;
309                 DoingArg = 0;
310                 if (matchval == 1) {
311                     PastBottom();
312                     *LastChar++ = '\n';
313                     *LastChar = '\0';
314                 }
315                 curlen = (int) (LastChar - InputBuf);
316             }
317             else
318                 PastBottom();
319
320             if (matchval == 1) {
321                 tellwhat = 0;   /* just in case */
322                 Hist_num = 0;   /* for the history commands */
323                 /* return the number of chars read */
324                 num = (int) (LastChar - InputBuf);
325                 /*
326                  * For continuation lines, we set the prompt to prompt 2
327                  */
328                 printprompt(1, NULL);
329             }
330             break;
331
332         case CC_CORRECT:
333             if (tenematch(InputBuf, Cursor - InputBuf, SPELL) < 0)
334                 SoundBeep();            /* Beep = No match/ambiguous */
335             curlen = Repair();
336             break;
337
338         case CC_CORRECT_L:
339             if (SpellLine(FALSE) < 0)
340                 SoundBeep();            /* Beep = No match/ambiguous */
341             curlen = Repair();
342             break;
343
344
345         case CC_COMPLETE:
346         case CC_COMPLETE_ALL:
347         case CC_COMPLETE_FWD:
348         case CC_COMPLETE_BACK:
349             switch (retval) {
350             case CC_COMPLETE:
351                 fn = RECOGNIZE;
352                 curlen = (int) (LastChar - InputBuf);
353                 curchoice = -1;
354                 rotate = 0;
355                 break;
356             case CC_COMPLETE_ALL:
357                 fn = RECOGNIZE_ALL;
358                 curlen = (int) (LastChar - InputBuf);
359                 curchoice = -1;
360                 rotate = 0;
361                 break;
362             case CC_COMPLETE_FWD:
363                 fn = RECOGNIZE_SCROLL;
364                 curchoice++;
365                 rotate = 1;
366                 break;
367             case CC_COMPLETE_BACK:
368                 fn = RECOGNIZE_SCROLL;
369                 curchoice--;
370                 rotate = 1;
371                 break;
372             default:
373                 abort();
374             }
375             if (InputBuf[curlen] && rotate) {
376                 newlen = (int) (LastChar - InputBuf);
377                 for (idx = (int) (Cursor - InputBuf); 
378                      idx <= newlen; idx++)
379                         InputBuf[idx - newlen + curlen] =
380                         InputBuf[idx];
381                 LastChar = InputBuf + curlen;
382                 Cursor = Cursor - newlen + curlen;
383             }
384             curlen = (int) (LastChar - InputBuf);
385
386
387             if (adrof(STRautoexpand))
388                 (void) e_expand_history(0);
389             /*
390              * Modified by Martin Boyer (gamin@ireq-robot.hydro.qc.ca):
391              * A separate variable now controls beeping after
392              * completion, independently of autolisting.
393              */
394             expnum = (int) (Cursor - InputBuf);
395             switch (matchval = tenematch(InputBuf, Cursor-InputBuf, fn)){
396             case 1:
397                 if (non_unique_match && matchbeep && matchbeep->vec != NULL &&
398                     (Strcmp(*(matchbeep->vec), STRnotunique) == 0))
399                     SoundBeep();
400                 break;
401             case 0:
402                 if (matchbeep && matchbeep->vec != NULL) {
403                     if (Strcmp(*(matchbeep->vec), STRnomatch) == 0 ||
404                         Strcmp(*(matchbeep->vec), STRambiguous) == 0 ||
405                         Strcmp(*(matchbeep->vec), STRnotunique) == 0)
406                         SoundBeep();
407                 }
408                 else
409                     SoundBeep();
410                 break;
411             default:
412                 if (matchval < 0) {     /* Error from tenematch */
413                     curchoice = -1;
414                     SoundBeep();
415                     break;
416                 }
417                 if (matchbeep && matchbeep->vec != NULL) {
418                     if ((Strcmp(*(matchbeep->vec), STRambiguous) == 0 ||
419                          Strcmp(*(matchbeep->vec), STRnotunique) == 0))
420                         SoundBeep();
421                 }
422                 else
423                     SoundBeep();
424                 /*
425                  * Addition by David C Lawrence <tale@pawl.rpi.edu>: If an 
426                  * attempted completion is ambiguous, list the choices.  
427                  * (PWP: this is the best feature addition to tcsh I have 
428                  * seen in many months.)
429                  */
430                 if (autol && autol->vec != NULL && 
431                     (Strcmp(*(autol->vec), STRambiguous) != 0 || 
432                                      expnum == Cursor - InputBuf)) {
433                     PastBottom();
434                     fn = (retval == CC_COMPLETE_ALL) ? LIST_ALL : LIST;
435                     (void) tenematch(InputBuf, Cursor-InputBuf, fn);
436                 }
437                 break;
438             }
439             if (NeedsRedraw) {
440                 PastBottom();
441                 ClearLines();
442                 ClearDisp();
443                 NeedsRedraw = 0;
444             }
445             Refresh();
446             Argument = 1;
447             DoingArg = 0;
448             break;
449
450         case CC_LIST_CHOICES:
451         case CC_LIST_ALL:
452             if (InputBuf[curlen] && rotate) {
453                 newlen = (int) (LastChar - InputBuf);
454                 for (idx = (int) (Cursor - InputBuf); 
455                      idx <= newlen; idx++)
456                         InputBuf[idx - newlen + curlen] =
457                         InputBuf[idx];
458                 LastChar = InputBuf + curlen;
459                 Cursor = Cursor - newlen + curlen;
460             }
461             curlen = (int) (LastChar - InputBuf);
462             if (curchoice >= 0)
463                 curchoice--;
464
465             fn = (retval == CC_LIST_ALL) ? LIST_ALL : LIST;
466             /* should catch ^C here... */
467             if (tenematch(InputBuf, Cursor - InputBuf, fn) < 0)
468                 SoundBeep();
469             Refresh();
470             Argument = 1;
471             DoingArg = 0;
472             break;
473
474
475         case CC_LIST_GLOB:
476             if (tenematch(InputBuf, Cursor - InputBuf, GLOB) < 0)
477                 SoundBeep();
478             curlen = Repair();
479             break;
480
481         case CC_EXPAND_GLOB:
482             if (tenematch(InputBuf, Cursor - InputBuf, GLOB_EXPAND) <= 0)
483                 SoundBeep();            /* Beep = No match */
484             curlen = Repair();
485             break;
486
487         case CC_NORMALIZE_PATH:
488             if (tenematch(InputBuf, Cursor - InputBuf, PATH_NORMALIZE) <= 0)
489                 SoundBeep();            /* Beep = No match */
490             curlen = Repair();
491             break;
492
493         case CC_EXPAND_VARS:
494             if (tenematch(InputBuf, Cursor - InputBuf, VARS_EXPAND) <= 0)
495                 SoundBeep();            /* Beep = No match */
496             curlen = Repair();
497             break;
498
499         case CC_NORMALIZE_COMMAND:
500             if (tenematch(InputBuf, Cursor - InputBuf, COMMAND_NORMALIZE) <= 0)
501                 SoundBeep();            /* Beep = No match */
502             curlen = Repair();
503             break;
504
505         case CC_HELPME:
506             xputchar('\n');
507             /* should catch ^C here... */
508             (void) tenematch(InputBuf, LastChar - InputBuf, PRINT_HELP);
509             Refresh();
510             Argument = 1;
511             DoingArg = 0;
512             curchoice = -1;
513             curlen = (int) (LastChar - InputBuf);
514             break;
515
516         case CC_FATAL:          /* fatal error, reset to known state */
517 #ifdef DEBUG_EDIT
518             xprintf(CGETS(7, 8, "*** editor fatal ERROR ***\r\n\n"));
519 #endif                          /* DEBUG_EDIT */
520             /* put (real) cursor in a known place */
521             ClearDisp();        /* reset the display stuff */
522             ResetInLine(1);     /* reset the input pointers */
523             Refresh();          /* print the prompt again */
524             Argument = 1;
525             DoingArg = 0;
526             curchoice = -1;
527             curlen = (int) (LastChar - InputBuf);
528             break;
529
530         case CC_ERROR:
531         default:                /* functions we don't know about */
532             DoingArg = 0;
533             Argument = 1;
534             SoundBeep();
535             flush();
536             curchoice = -1;
537             curlen = (int) (LastChar - InputBuf);
538             break;
539         }
540     }
541     (void) Cookedmode();        /* make sure the tty is set up correctly */
542     GettingInput = 0;
543     flush();                    /* flush any buffered output */
544     return num;
545 }
546
547 void
548 PushMacro(str)
549     Char   *str;
550 {
551     if (str != NULL && MacroLvl + 1 < MAXMACROLEVELS) {
552         MacroLvl++;
553         KeyMacro[MacroLvl] = str;
554     }
555     else {
556         SoundBeep();
557         flush();
558     }
559 }
560
561 /*
562  * Like eval, only using the current file descriptors
563  */
564 static Char **gv = NULL, **gav = NULL;
565
566 static void
567 doeval1(v)
568     Char **v;
569 {
570     Char  **oevalvec;
571     Char   *oevalp;
572     int     my_reenter;
573     Char  **savegv;
574     jmp_buf_t osetexit;
575
576     oevalvec = evalvec;
577     oevalp = evalp;
578     savegv = gv;
579     gav = v;
580
581
582     gflag = 0, tglob(gav);
583     if (gflag) {
584         gv = gav = globall(gav);
585         gargv = 0;
586         if (gav == 0)
587             stderror(ERR_NOMATCH);
588         gav = copyblk(gav);
589     }
590     else {
591         gv = NULL;
592         gav = copyblk(gav);
593         trim(gav);
594     }
595
596     getexit(osetexit);
597
598     /* PWP: setjmp/longjmp bugfix for optimizing compilers */
599 #ifdef cray
600     my_reenter = 1;             /* assume non-zero return val */
601     if (setexit() == 0) {
602         my_reenter = 0;         /* Oh well, we were wrong */
603 #else /* !cray */
604     if ((my_reenter = setexit()) == 0) {
605 #endif /* cray */
606         evalvec = gav;
607         evalp = 0;
608         process(0);
609     }
610
611     evalvec = oevalvec;
612     evalp = oevalp;
613     doneinp = 0;
614
615     if (gv)
616         blkfree(gv);
617
618     gv = savegv;
619     resexit(osetexit);
620     if (my_reenter)
621         stderror(ERR_SILENT);
622 }
623
624 static void
625 RunCommand(str)
626     Char *str;
627 {
628     Char *cmd[2];
629
630     xputchar('\n');     /* Start on a clean line */
631
632     cmd[0] = str;
633     cmd[1] = NULL;
634
635     (void) Cookedmode();
636     GettingInput = 0;
637
638     doeval1(cmd);
639     
640     (void) Rawmode();
641     GettingInput = 1;
642
643     ClearLines();
644     ClearDisp();
645     NeedsRedraw = 0;
646     Refresh();
647 }
648
649 static int
650 GetNextCommand(cmdnum, ch)
651     KEYCMD *cmdnum;
652     register Char *ch;
653 {
654     KEYCMD  cmd = 0;
655     int     num;
656
657     while (cmd == 0 || cmd == F_XKEY) {
658         if ((num = GetNextChar(ch)) != 1) {     /* if EOF or error */
659             return num;
660         }
661 #ifdef  KANJI
662         if (
663 #ifdef DSPMBYTE
664              _enable_mbdisp &&
665 #endif
666              !adrof(STRnokanji) && (*ch & META)) {
667             MetaNext = 0;
668             cmd = F_INSERT;
669             break;
670         }
671         else
672 #endif /* KANJI */
673         if (MetaNext) {
674             MetaNext = 0;
675             *ch |= META;
676         }
677         /* XXX: This needs to be fixed so that we don't just truncate
678          * the character, we unquote it.
679          */
680         if (*ch < NT_NUM_KEYS)
681             cmd = CurrentKeyMap[*ch];
682         else
683             cmd = CurrentKeyMap[(unsigned char) *ch];
684         if (cmd == F_XKEY) {
685             XmapVal val;
686             CStr cstr;
687             cstr.buf = ch;
688             cstr.len = Strlen(ch);
689             switch (GetXkey(&cstr, &val)) {
690             case XK_CMD:
691                 cmd = val.cmd;
692                 break;
693             case XK_STR:
694                 PushMacro(val.str.buf);
695                 break;
696             case XK_EXE:
697                 RunCommand(val.str.buf);
698                 break;
699             default:
700                 abort();
701                 break;
702             }
703         }
704         if (!AltKeyMap) 
705             CurrentKeyMap = CcKeyMap;
706     }
707     *cmdnum = cmd;
708     return OKCMD;
709 }
710
711 int
712 GetNextChar(cp)
713     register Char *cp;
714 {
715     register int num_read;
716     int     tried = 0;
717     unsigned char tcp;
718
719     for (;;) {
720         if (MacroLvl < 0) {
721             if (!Load_input_line())
722                 break;
723         }
724         if (*KeyMacro[MacroLvl] == 0) {
725             MacroLvl--;
726             continue;
727         }
728         *cp = *KeyMacro[MacroLvl]++ & CHAR;
729         if (*KeyMacro[MacroLvl] == 0) { /* Needed for QuoteMode On */
730             MacroLvl--;
731         }
732         return (1);
733     }
734
735     if (Rawmode() < 0)          /* make sure the tty is set up correctly */
736         return 0;               /* oops: SHIN was closed */
737
738 #ifdef WINNT_NATIVE
739     __nt_want_vcode = 1;
740 #endif /* WINNT_NATIVE */
741     while ((num_read = read(SHIN, (char *) &tcp, 1)) == -1) {
742         if (errno == EINTR)
743             continue;
744         if (!tried && fixio(SHIN, errno) != -1)
745             tried = 1;
746         else {
747 #ifdef convex
748             /* need to print error message in case the file is migrated */
749             if (errno != EINTR)
750                 stderror(ERR_SYSTEM, progname, strerror(errno));
751 #endif  /* convex */
752 #ifdef WINNT_NATIVE
753             __nt_want_vcode = 0;
754 #endif /* WINNT_NATIVE */
755             *cp = '\0';
756             return -1;
757         }
758     }
759 #ifdef WINNT_NATIVE
760     if (__nt_want_vcode == 2)
761         *cp = __nt_vcode;
762     else
763         *cp = tcp;
764     __nt_want_vcode = 0;
765 #else
766     *cp = tcp;
767 #endif /* WINNT_NATIVE */
768     return num_read;
769 }
770
771 /*
772  * SpellLine - do spelling correction on the entire command line
773  * (which may have trailing newline).
774  * If cmdonly is set, only check spelling of command words.
775  * Return value:
776  * -1: Something was incorrectible, and nothing was corrected
777  *  0: Everything was correct
778  *  1: Something was corrected
779  */
780 static int
781 SpellLine(cmdonly)
782     int     cmdonly;
783 {
784     int     endflag, matchval;
785     Char   *argptr, *OldCursor, *OldLastChar;
786
787     OldLastChar = LastChar;
788     OldCursor = Cursor;
789     argptr = InputBuf;
790     endflag = 1;
791     matchval = 0;
792     do {
793         while (ismetahash(*argptr) || iscmdmeta(*argptr))
794             argptr++;
795         for (Cursor = argptr;
796              *Cursor != '\0' && ((Cursor != argptr && Cursor[-1] == '\\') ||
797                                  (!ismetahash(*Cursor) && !iscmdmeta(*Cursor)));
798              Cursor++)
799              continue;
800         if (*Cursor == '\0') {
801             Cursor = LastChar;
802             if (LastChar[-1] == '\n')
803                 Cursor--;
804             endflag = 0;
805         }
806         /* Obey current history character settings */
807         mismatch[0] = HIST;
808         mismatch[1] = HISTSUB;
809         if (!Strchr(mismatch, *argptr) &&
810             (!cmdonly || starting_a_command(argptr, InputBuf))) {
811 #ifdef WINNT_NATIVE
812             /*
813              * This hack avoids correcting drive letter changes
814              */
815             if((Cursor - InputBuf) != 2 || (char)InputBuf[1] != ':')
816 #endif /* WINNT_NATIVE */
817             {
818 #ifdef HASH_SPELL_CHECK
819                 Char save;
820                 size_t len = Cursor - InputBuf;
821
822                 save = InputBuf[len];
823                 InputBuf[len] = '\0';
824                 if (find_cmd(InputBuf, 0) != 0) {
825                     InputBuf[len] = save;
826                     argptr = Cursor;
827                     continue;
828                 }
829                 InputBuf[len] = save;
830 #endif /* HASH_SPELL_CHECK */
831                 switch (tenematch(InputBuf, Cursor - InputBuf, SPELL)) {
832                 case 1:         /* corrected */
833                     matchval = 1;
834                     break;
835                 case -1:                /* couldn't be corrected */
836                     if (!matchval)
837                         matchval = -1;
838                     break;
839                 default:                /* was correct */
840                     break;
841                 }
842             }
843             if (LastChar != OldLastChar) {
844                 if (argptr < OldCursor)
845                     OldCursor += (LastChar - OldLastChar);
846                 OldLastChar = LastChar;
847             }
848         }
849         argptr = Cursor;
850     } while (endflag);
851     Cursor = OldCursor;
852     return matchval;
853 }
854
855 /*
856  * CompleteLine - do command completion on the entire command line
857  * (which may have trailing newline).
858  * Return value:
859  *  0: No command matched or failure
860  *  1: One command matched
861  *  2: Several commands matched
862  */
863 static int
864 CompleteLine()
865 {
866     int     endflag, tmatch;
867     Char   *argptr, *OldCursor, *OldLastChar;
868
869     OldLastChar = LastChar;
870     OldCursor = Cursor;
871     argptr = InputBuf;
872     endflag = 1;
873     do {
874         while (ismetahash(*argptr) || iscmdmeta(*argptr))
875             argptr++;
876         for (Cursor = argptr;
877              *Cursor != '\0' && ((Cursor != argptr && Cursor[-1] == '\\') ||
878                                  (!ismetahash(*Cursor) && !iscmdmeta(*Cursor)));
879              Cursor++)
880              continue;
881         if (*Cursor == '\0') {
882             Cursor = LastChar;
883             if (LastChar[-1] == '\n')
884                 Cursor--;
885             endflag = 0;
886         }
887         if (!Strchr(mismatch, *argptr) && starting_a_command(argptr, InputBuf)) {
888             tmatch = tenematch(InputBuf, Cursor - InputBuf, RECOGNIZE);
889             if (tmatch <= 0) {
890                 return 0;
891             } else if (tmatch > 1) {
892                 return 2;
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 1;
904 }
905