xref: /dragonfly/contrib/tcsh-6/ed.inputl.c (revision 62f7f702)
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 
948