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