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