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