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