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