xref: /freebsd/contrib/tcsh/ed.inputl.c (revision f0adf7f5cdd241db2f2c817683191a6ef64a4e95)
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 
906