ed.inputl.c revision 61519
1/* $Header: /src/pub/tcsh/ed.inputl.c,v 3.48 2000/06/10 20:14:57 kim 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.48 2000/06/10 20:14:57 kim 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 (
665#ifdef DSPMBYTE
666	     _enable_mbdisp &&
667#endif
668	     !adrof(STRnokanji) && (*ch & META)) {
669	    MetaNext = 0;
670	    cmd = F_INSERT;
671	    break;
672	}
673	else
674#endif /* KANJI */
675	if (MetaNext) {
676	    MetaNext = 0;
677	    *ch |= META;
678	}
679	/* XXX: This needs to be fixed so that we don't just truncate
680	 * the character, we unquote it.
681	 */
682	if (*ch < NT_NUM_KEYS)
683	    cmd = CurrentKeyMap[*ch];
684	else
685	    cmd = CurrentKeyMap[(unsigned char) *ch];
686	if (cmd == F_XKEY) {
687	    XmapVal val;
688	    CStr cstr;
689	    cstr.buf = ch;
690	    cstr.len = Strlen(ch);
691	    switch (GetXkey(&cstr, &val)) {
692	    case XK_CMD:
693		cmd = val.cmd;
694		break;
695	    case XK_STR:
696		PushMacro(val.str.buf);
697		break;
698	    case XK_EXE:
699		RunCommand(val.str.buf);
700		break;
701	    default:
702		abort();
703		break;
704	    }
705	}
706	if (!AltKeyMap)
707	    CurrentKeyMap = CcKeyMap;
708    }
709    *cmdnum = cmd;
710    return OKCMD;
711}
712
713int
714GetNextChar(cp)
715    register Char *cp;
716{
717    register int num_read;
718    int     tried = 0;
719    unsigned char tcp;
720
721    for (;;) {
722	if (MacroLvl < 0) {
723	    if (!Load_input_line())
724		break;
725	}
726	if (*KeyMacro[MacroLvl] == 0) {
727	    MacroLvl--;
728	    continue;
729	}
730	*cp = *KeyMacro[MacroLvl]++ & CHAR;
731	if (*KeyMacro[MacroLvl] == 0) {	/* Needed for QuoteMode On */
732	    MacroLvl--;
733	}
734	return (1);
735    }
736
737    if (Rawmode() < 0)		/* make sure the tty is set up correctly */
738	return 0;		/* oops: SHIN was closed */
739
740#ifdef WINNT
741    __nt_want_vcode = 1;
742#endif /* WINNT */
743    while ((num_read = read(SHIN, (char *) &tcp, 1)) == -1) {
744	if (errno == EINTR)
745	    continue;
746	if (!tried && fixio(SHIN, errno) != -1)
747	    tried = 1;
748	else {
749#ifdef convex
750            /* need to print error message in case the file is migrated */
751            if (errno != EINTR)
752                stderror(ERR_SYSTEM, progname, strerror(errno));
753#endif  /* convex */
754#ifdef WINNT
755	    __nt_want_vcode = 0;
756#endif /* WINNT */
757	    *cp = '\0';
758	    return -1;
759	}
760    }
761#ifdef WINNT
762    if (__nt_want_vcode == 2)
763	*cp = __nt_vcode;
764    else
765	*cp = tcp;
766    __nt_want_vcode = 0;
767#else
768    *cp = tcp;
769#endif /* WINNT */
770    return num_read;
771}
772
773/*
774 * SpellLine - do spelling correction on the entire command line
775 * (which may have trailing newline).
776 * If cmdonly is set, only check spelling of command words.
777 * Return value:
778 * -1: Something was incorrectible, and nothing was corrected
779 *  0: Everything was correct
780 *  1: Something was corrected
781 */
782static int
783SpellLine(cmdonly)
784    int     cmdonly;
785{
786    int     endflag, matchval;
787    Char   *argptr, *OldCursor, *OldLastChar;
788
789    OldLastChar = LastChar;
790    OldCursor = Cursor;
791    argptr = InputBuf;
792    endflag = 1;
793    matchval = 0;
794    do {
795	while (ismetahash(*argptr) || iscmdmeta(*argptr))
796	    argptr++;
797	for (Cursor = argptr;
798	     *Cursor != '\0' && ((Cursor != argptr && Cursor[-1] == '\\') ||
799				 (!ismetahash(*Cursor) && !iscmdmeta(*Cursor)));
800	     Cursor++)
801	     continue;
802	if (*Cursor == '\0') {
803	    Cursor = LastChar;
804	    if (LastChar[-1] == '\n')
805		Cursor--;
806	    endflag = 0;
807	}
808	/* Obey current history character settings */
809	mismatch[0] = HIST;
810	mismatch[1] = HISTSUB;
811	if (!Strchr(mismatch, *argptr) &&
812	    (!cmdonly || starting_a_command(argptr, InputBuf))) {
813#ifdef WINNT
814	    /*
815	     * This hack avoids correcting drive letter changes
816	     */
817	    if((Cursor - InputBuf) != 2 || (char)InputBuf[1] != ':')
818#endif /* WINNT */
819	    {
820#ifdef HASH_SPELL_CHECK
821		Char save;
822		size_t len = Cursor - InputBuf;
823
824		save = InputBuf[len];
825		InputBuf[len] = '\0';
826		if (find_cmd(InputBuf, 0) != 0) {
827		    InputBuf[len] = save;
828		    argptr = Cursor;
829		    continue;
830		}
831		InputBuf[len] = save;
832#endif /* HASH_SPELL_CHECK */
833		switch (tenematch(InputBuf, Cursor - InputBuf, SPELL)) {
834		case 1:		/* corrected */
835		    matchval = 1;
836		    break;
837		case -1:		/* couldn't be corrected */
838		    if (!matchval)
839			matchval = -1;
840		    break;
841		default:		/* was correct */
842		    break;
843		}
844	    }
845	    if (LastChar != OldLastChar) {
846		if (argptr < OldCursor)
847		    OldCursor += (LastChar - OldLastChar);
848		OldLastChar = LastChar;
849	    }
850	}
851	argptr = Cursor;
852    } while (endflag);
853    Cursor = OldCursor;
854    return matchval;
855}
856
857/*
858 * CompleteLine - do command completion on the entire command line
859 * (which may have trailing newline).
860 * Return value:
861 *  0: No command matched or failure
862 *  1: One command matched
863 *  2: Several commands matched
864 */
865static int
866CompleteLine()
867{
868    int     endflag, tmatch;
869    Char   *argptr, *OldCursor, *OldLastChar;
870
871    OldLastChar = LastChar;
872    OldCursor = Cursor;
873    argptr = InputBuf;
874    endflag = 1;
875    do {
876	while (ismetahash(*argptr) || iscmdmeta(*argptr))
877	    argptr++;
878	for (Cursor = argptr;
879	     *Cursor != '\0' && ((Cursor != argptr && Cursor[-1] == '\\') ||
880				 (!ismetahash(*Cursor) && !iscmdmeta(*Cursor)));
881	     Cursor++)
882	     continue;
883	if (*Cursor == '\0') {
884	    Cursor = LastChar;
885	    if (LastChar[-1] == '\n')
886		Cursor--;
887	    endflag = 0;
888	}
889	if (!Strchr(mismatch, *argptr) && starting_a_command(argptr, InputBuf)) {
890	    tmatch = tenematch(InputBuf, Cursor - InputBuf, RECOGNIZE);
891	    if (tmatch <= 0) {
892                return 0;
893            } else if (tmatch > 1) {
894                return 2;
895	    }
896	    if (LastChar != OldLastChar) {
897		if (argptr < OldCursor)
898		    OldCursor += (LastChar - OldLastChar);
899		OldLastChar = LastChar;
900	    }
901	}
902	argptr = Cursor;
903    } while (endflag);
904    Cursor = OldCursor;
905    return 1;
906}
907
908