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