159243Sobrien/*
259243Sobrien * ed.inputl.c: Input line handling.
359243Sobrien */
459243Sobrien/*-
559243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California.
659243Sobrien * All rights reserved.
759243Sobrien *
859243Sobrien * Redistribution and use in source and binary forms, with or without
959243Sobrien * modification, are permitted provided that the following conditions
1059243Sobrien * are met:
1159243Sobrien * 1. Redistributions of source code must retain the above copyright
1259243Sobrien *    notice, this list of conditions and the following disclaimer.
1359243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1459243Sobrien *    notice, this list of conditions and the following disclaimer in the
1559243Sobrien *    documentation and/or other materials provided with the distribution.
16100616Smp * 3. Neither the name of the University nor the names of its contributors
1759243Sobrien *    may be used to endorse or promote products derived from this software
1859243Sobrien *    without specific prior written permission.
1959243Sobrien *
2059243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2159243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2259243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2359243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2459243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2559243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2659243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2759243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2859243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2959243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3059243Sobrien * SUCH DAMAGE.
3159243Sobrien */
3259243Sobrien#include "sh.h"
3359243Sobrien#include "ed.h"
3459243Sobrien#include "ed.defns.h"		/* for the function names */
3559243Sobrien#include "tw.h"			/* for twenex stuff */
3659243Sobrien
37167465Smp#define OKCMD INT_MAX
3859243Sobrien
3959243Sobrien/* ed.inputl -- routines to get a single line from the input. */
4059243Sobrien
41145479Smpextern int MapsAreInited;
4259243Sobrien
4359243Sobrien/* mismatched first character */
44195609Smpstatic Char mismatch[] = { '\\', '-', '%', '\0' };
45195609Smp/* don't Strchr() for '\0', obey current history character settings */
46195609Smp#define MISMATCH(c) ((c) == '\0' || (c) == HIST || (c) == HISTSUB || \
47195609Smp			Strchr(mismatch, (c)))
4859243Sobrien
49167465Smpstatic	int	Repair		(void);
50167465Smpstatic	int	GetNextCommand	(KEYCMD *, Char *);
51167465Smpstatic	int	SpellLine	(int);
52167465Smpstatic	int	CompleteLine	(void);
53167465Smpstatic	void	RunCommand	(Char *);
54167465Smpstatic  void 	doeval1		(Char **);
5559243Sobrien
56145479Smpstatic int rotate = 0;
5759243Sobrien
5859243Sobrien
5959243Sobrienstatic int
60167465SmpRepair(void)
6159243Sobrien{
6259243Sobrien    if (NeedsRedraw) {
6359243Sobrien	ClearLines();
6459243Sobrien	ClearDisp();
6559243Sobrien	NeedsRedraw = 0;
6659243Sobrien    }
6759243Sobrien    Refresh();
6859243Sobrien    Argument = 1;
6959243Sobrien    DoingArg = 0;
7059243Sobrien    curchoice = -1;
7159243Sobrien    return (int) (LastChar - InputBuf);
7259243Sobrien}
7359243Sobrien
7459243Sobrien/* CCRETVAL */
7559243Sobrienint
76167465SmpInputl(void)
7759243Sobrien{
7859243Sobrien    CCRETVAL retval;
7959243Sobrien    KEYCMD  cmdnum = 0;
8059243Sobrien    unsigned char tch;		/* the place where read() goes */
8159243Sobrien    Char    ch;
8259243Sobrien    int     num;		/* how many chars we have read at NL */
8359243Sobrien    int	    expnum;
8459243Sobrien    struct varent *crct = inheredoc ? NULL : adrof(STRcorrect);
8559243Sobrien    struct varent *autol = adrof(STRautolist);
8659243Sobrien    struct varent *matchbeep = adrof(STRmatchbeep);
8759243Sobrien    struct varent *imode = adrof(STRinputmode);
8859243Sobrien    Char   *SaveChar, *CorrChar;
8959243Sobrien    int     matchval;		/* from tenematch() */
90195609Smp    int     nr_history_exp;     /* number of (attempted) history expansions */
9159243Sobrien    COMMAND fn;
9259243Sobrien    int curlen = 0;
9359243Sobrien    int newlen;
9459243Sobrien    int idx;
95195609Smp    Char *autoexpand;
9659243Sobrien
9759243Sobrien    if (!MapsAreInited)		/* double extra just in case */
9859243Sobrien	ed_InitMaps();
9959243Sobrien
10059243Sobrien    ClearDisp();		/* reset the display stuff */
10159243Sobrien    ResetInLine(0);		/* reset the input pointers */
10259243Sobrien    if (GettingInput)
10359243Sobrien	MacroLvl = -1;		/* editor was interrupted during input */
10459243Sobrien
105100616Smp    if (imode && imode->vec != NULL) {
10659243Sobrien	if (!Strcmp(*(imode->vec), STRinsert))
10759243Sobrien	    inputmode = MODE_INSERT;
10859243Sobrien	else if (!Strcmp(*(imode->vec), STRoverwrite))
10959243Sobrien	    inputmode = MODE_REPLACE;
11059243Sobrien    }
11159243Sobrien
11259243Sobrien#if defined(FIONREAD) && !defined(OREO)
11359243Sobrien    if (!Tty_raw_mode && MacroLvl < 0) {
11459243Sobrien# ifdef SUNOS4
11559243Sobrien	long chrs = 0;
11659243Sobrien# else /* !SUNOS4 */
11759243Sobrien	/*
11859243Sobrien	 * *Everyone* else has an int, but SunOS wants long!
11959243Sobrien	 * This breaks where int != long (alpha)
12059243Sobrien	 */
12159243Sobrien	int chrs = 0;
12259243Sobrien# endif /* SUNOS4 */
12359243Sobrien
12459243Sobrien	(void) ioctl(SHIN, FIONREAD, (ioctl_t) & chrs);
12559243Sobrien	if (chrs == 0) {
12659243Sobrien	    if (Rawmode() < 0)
12759243Sobrien		return 0;
12859243Sobrien	}
12959243Sobrien    }
13059243Sobrien#endif /* FIONREAD && !OREO */
13159243Sobrien
13259243Sobrien    GettingInput = 1;
13359243Sobrien    NeedsRedraw = 0;
134167465Smp    tellwhat = 0;
13559243Sobrien
136167465Smp    if (RestoreSaved) {
137167465Smp	copyn(InputBuf, SavedBuf.s, INBUFSIZE);/*FIXBUF*/
138167465Smp	LastChar = InputBuf + LastSaved;
139167465Smp	Cursor = InputBuf + CursSaved;
140167465Smp	Hist_num = HistSaved;
141167465Smp	HistSaved = 0;
142167465Smp	RestoreSaved = 0;
14359243Sobrien    }
144167465Smp    if (HistSaved) {
145167465Smp	Hist_num = HistSaved;
146167465Smp	GetHistLine();
147167465Smp	HistSaved = 0;
148167465Smp    }
14959243Sobrien    if (Expand) {
15059243Sobrien	(void) e_up_hist(0);
15159243Sobrien	Expand = 0;
15259243Sobrien    }
15359243Sobrien    Refresh();			/* print the prompt */
15459243Sobrien
15559243Sobrien    for (num = OKCMD; num == OKCMD;) {	/* while still editing this line */
15659243Sobrien#ifdef DEBUG_EDIT
15759243Sobrien	if (Cursor > LastChar)
15859243Sobrien	    xprintf("Cursor > LastChar\r\n");
15959243Sobrien	if (Cursor < InputBuf)
16059243Sobrien	    xprintf("Cursor < InputBuf\r\n");
16159243Sobrien	if (Cursor > InputLim)
16259243Sobrien	    xprintf("Cursor > InputLim\r\n");
16359243Sobrien	if (LastChar > InputLim)
16459243Sobrien	    xprintf("LastChar > InputLim\r\n");
165167465Smp	if (InputLim != &InputBuf[INBUFSIZE - 2])/*FIXBUF*/
16659243Sobrien	    xprintf("InputLim != &InputBuf[INBUFSIZE-2]\r\n");
16759243Sobrien	if ((!DoingArg) && (Argument != 1))
16859243Sobrien	    xprintf("(!DoingArg) && (Argument != 1)\r\n");
16959243Sobrien	if (CcKeyMap[0] == 0)
17059243Sobrien	    xprintf("CcKeyMap[0] == 0 (maybe not inited)\r\n");
17159243Sobrien#endif
17259243Sobrien
17359243Sobrien	/* if EOF or error */
17459243Sobrien	if ((num = GetNextCommand(&cmdnum, &ch)) != OKCMD) {
17559243Sobrien	    break;
17659243Sobrien	}
17759243Sobrien
17859243Sobrien	if (cmdnum >= NumFuns) {/* BUG CHECK command */
17959243Sobrien#ifdef DEBUG_EDIT
18059243Sobrien	    xprintf(CGETS(6, 1, "ERROR: illegal command from key 0%o\r\n"), ch);
18159243Sobrien#endif
18259243Sobrien	    continue;		/* try again */
18359243Sobrien	}
18459243Sobrien
18559243Sobrien	/* now do the real command */
18659243Sobrien	retval = (*CcFuncTbl[cmdnum]) (ch);
18759243Sobrien
18859243Sobrien	/* save the last command here */
18959243Sobrien	LastCmd = cmdnum;
19059243Sobrien
19159243Sobrien	/* make sure fn is initialized */
19259243Sobrien	fn = (retval == CC_COMPLETE_ALL) ? LIST_ALL : LIST;
19359243Sobrien
19459243Sobrien	/* use any return value */
19559243Sobrien	switch (retval) {
19659243Sobrien
19759243Sobrien	case CC_REFRESH:
19859243Sobrien	    Refresh();
19959243Sobrien	    /*FALLTHROUGH*/
20059243Sobrien	case CC_NORM:		/* normal char */
20159243Sobrien	    Argument = 1;
20259243Sobrien	    DoingArg = 0;
20359243Sobrien	    /*FALLTHROUGH*/
20459243Sobrien	case CC_ARGHACK:	/* Suggested by Rich Salz */
20559243Sobrien	    /* <rsalz@pineapple.bbn.com> */
20659243Sobrien	    curchoice = -1;
20759243Sobrien	    curlen = (int) (LastChar - InputBuf);
20859243Sobrien	    break;		/* keep going... */
20959243Sobrien
21059243Sobrien	case CC_EOF:		/* end of file typed */
21159243Sobrien	    curchoice = -1;
21259243Sobrien	    curlen = (int) (LastChar - InputBuf);
21359243Sobrien	    num = 0;
21459243Sobrien	    break;
21559243Sobrien
21659243Sobrien	case CC_WHICH:		/* tell what this command does */
21759243Sobrien	    tellwhat = 1;
21859243Sobrien	    *LastChar++ = '\n';	/* for the benifit of CSH */
21959243Sobrien	    num = (int) (LastChar - InputBuf);	/* number characters read */
22059243Sobrien	    break;
22159243Sobrien
22259243Sobrien	case CC_NEWLINE:	/* normal end of line */
22359243Sobrien	    curlen = 0;
22459243Sobrien	    curchoice = -1;
22559243Sobrien	    matchval = 1;
226100616Smp	    if (crct && crct->vec != NULL && (!Strcmp(*(crct->vec), STRcmd) ||
22759243Sobrien			 !Strcmp(*(crct->vec), STRall))) {
228167465Smp		Char *Origin;
229167465Smp
23059243Sobrien                PastBottom();
231167465Smp		Origin = Strsave(InputBuf);
232167465Smp		cleanup_push(Origin, xfree);
23359243Sobrien		SaveChar = LastChar;
23459243Sobrien		if (SpellLine(!Strcmp(*(crct->vec), STRcmd)) == 1) {
235167465Smp		    Char *Change;
236167465Smp
23759243Sobrien                    PastBottom();
238167465Smp		    Change = Strsave(InputBuf);
239167465Smp		    cleanup_push(Change, xfree);
24059243Sobrien		    *Strchr(Change, '\n') = '\0';
24159243Sobrien		    CorrChar = LastChar;	/* Save the corrected end */
24259243Sobrien		    LastChar = InputBuf;	/* Null the current line */
24359243Sobrien		    SoundBeep();
24459243Sobrien		    printprompt(2, short2str(Change));
245167465Smp		    cleanup_until(Change);
24659243Sobrien		    Refresh();
247167465Smp		    if (xread(SHIN, &tch, 1) < 0) {
24859243Sobrien#ifdef convex
24959243Sobrien		        /*
25059243Sobrien			 * need to print error message in case file
25159243Sobrien			 * is migrated
25259243Sobrien			 */
253167465Smp                        if (errno)
25459243Sobrien                            stderror(ERR_SYSTEM, progname, strerror(errno));
25559243Sobrien#else
256167465Smp			cleanup_until(Origin);
25759243Sobrien			break;
25859243Sobrien#endif
259167465Smp		    }
26059243Sobrien		    ch = tch;
26159243Sobrien		    if (ch == 'y' || ch == ' ') {
26259243Sobrien			LastChar = CorrChar;	/* Restore the corrected end */
263195609Smp			xprintf("%s", CGETS(6, 2, "yes\n"));
26459243Sobrien		    }
26559243Sobrien		    else {
266167465Smp			Strcpy(InputBuf, Origin);
26759243Sobrien			LastChar = SaveChar;
26859243Sobrien			if (ch == 'e') {
269195609Smp			    xprintf("%s", CGETS(6, 3, "edit\n"));
27059243Sobrien			    *LastChar-- = '\0';
27159243Sobrien			    Cursor = LastChar;
27259243Sobrien			    printprompt(3, NULL);
27359243Sobrien			    ClearLines();
27459243Sobrien			    ClearDisp();
27559243Sobrien			    Refresh();
276167465Smp			    cleanup_until(Origin);
27759243Sobrien			    break;
27859243Sobrien			}
27959243Sobrien			else if (ch == 'a') {
280195609Smp			    xprintf("%s", CGETS(6, 4, "abort\n"));
28159243Sobrien		            LastChar = InputBuf;   /* Null the current line */
28259243Sobrien			    Cursor = LastChar;
28359243Sobrien			    printprompt(0, NULL);
28459243Sobrien			    Refresh();
285167465Smp			    cleanup_until(Origin);
28659243Sobrien			    break;
28759243Sobrien			}
288195609Smp			xprintf("%s", CGETS(6, 5, "no\n"));
28959243Sobrien		    }
29059243Sobrien		    flush();
29159243Sobrien		}
292167465Smp		cleanup_until(Origin);
293100616Smp	    } else if (crct && crct->vec != NULL &&
294100616Smp		!Strcmp(*(crct->vec), STRcomplete)) {
29559243Sobrien                if (LastChar > InputBuf && LastChar[-1] == '\n') {
29659243Sobrien                    LastChar[-1] = '\0';
29759243Sobrien                    LastChar--;
29859243Sobrien                    Cursor = LastChar;
29959243Sobrien                }
30059243Sobrien                match_unique_match = 1;  /* match unique matches */
30159243Sobrien		matchval = CompleteLine();
30259243Sobrien                match_unique_match = 0;
30359243Sobrien        	curlen = (int) (LastChar - InputBuf);
30459243Sobrien		if (matchval != 1) {
30559243Sobrien                    PastBottom();
30659243Sobrien		}
30759243Sobrien		if (matchval == 0) {
308195609Smp		    xprintf("%s", CGETS(6, 6, "No matching command\n"));
30959243Sobrien		} else if (matchval == 2) {
310195609Smp		    xprintf("%s", CGETS(6, 7, "Ambiguous command\n"));
31159243Sobrien		}
31259243Sobrien	        if (NeedsRedraw) {
31359243Sobrien		    ClearLines();
31459243Sobrien		    ClearDisp();
31559243Sobrien		    NeedsRedraw = 0;
31659243Sobrien	        }
31759243Sobrien	        Refresh();
31859243Sobrien	        Argument = 1;
31959243Sobrien	        DoingArg = 0;
32059243Sobrien		if (matchval == 1) {
32159243Sobrien                    PastBottom();
32259243Sobrien                    *LastChar++ = '\n';
32359243Sobrien                    *LastChar = '\0';
32459243Sobrien		}
32559243Sobrien        	curlen = (int) (LastChar - InputBuf);
32659243Sobrien            }
32759243Sobrien	    else
32859243Sobrien		PastBottom();
32959243Sobrien
33059243Sobrien	    if (matchval == 1) {
33159243Sobrien	        tellwhat = 0;	/* just in case */
33259243Sobrien	        Hist_num = 0;	/* for the history commands */
33359243Sobrien		/* return the number of chars read */
33459243Sobrien	        num = (int) (LastChar - InputBuf);
33559243Sobrien	        /*
33659243Sobrien	         * For continuation lines, we set the prompt to prompt 2
33759243Sobrien	         */
33859243Sobrien	        printprompt(1, NULL);
33959243Sobrien	    }
34059243Sobrien	    break;
34159243Sobrien
34259243Sobrien	case CC_CORRECT:
34359243Sobrien	    if (tenematch(InputBuf, Cursor - InputBuf, SPELL) < 0)
34459243Sobrien		SoundBeep();		/* Beep = No match/ambiguous */
34559243Sobrien	    curlen = Repair();
34659243Sobrien	    break;
34759243Sobrien
34859243Sobrien	case CC_CORRECT_L:
34959243Sobrien	    if (SpellLine(FALSE) < 0)
35059243Sobrien		SoundBeep();		/* Beep = No match/ambiguous */
35159243Sobrien	    curlen = Repair();
35259243Sobrien	    break;
35359243Sobrien
35459243Sobrien
35559243Sobrien	case CC_COMPLETE:
35659243Sobrien	case CC_COMPLETE_ALL:
35759243Sobrien	case CC_COMPLETE_FWD:
35859243Sobrien	case CC_COMPLETE_BACK:
35959243Sobrien	    switch (retval) {
36059243Sobrien	    case CC_COMPLETE:
36159243Sobrien		fn = RECOGNIZE;
36259243Sobrien		curlen = (int) (LastChar - InputBuf);
36359243Sobrien		curchoice = -1;
36459243Sobrien		rotate = 0;
36559243Sobrien		break;
36659243Sobrien	    case CC_COMPLETE_ALL:
36759243Sobrien		fn = RECOGNIZE_ALL;
36859243Sobrien		curlen = (int) (LastChar - InputBuf);
36959243Sobrien		curchoice = -1;
37059243Sobrien		rotate = 0;
37159243Sobrien		break;
37259243Sobrien	    case CC_COMPLETE_FWD:
37359243Sobrien		fn = RECOGNIZE_SCROLL;
37459243Sobrien		curchoice++;
37559243Sobrien		rotate = 1;
37659243Sobrien		break;
37759243Sobrien	    case CC_COMPLETE_BACK:
37859243Sobrien		fn = RECOGNIZE_SCROLL;
37959243Sobrien		curchoice--;
38059243Sobrien		rotate = 1;
38159243Sobrien		break;
38259243Sobrien	    default:
38359243Sobrien		abort();
38459243Sobrien	    }
38559243Sobrien	    if (InputBuf[curlen] && rotate) {
38659243Sobrien		newlen = (int) (LastChar - InputBuf);
38759243Sobrien		for (idx = (int) (Cursor - InputBuf);
38859243Sobrien		     idx <= newlen; idx++)
38959243Sobrien			InputBuf[idx - newlen + curlen] =
39059243Sobrien			InputBuf[idx];
39159243Sobrien		LastChar = InputBuf + curlen;
39259243Sobrien		Cursor = Cursor - newlen + curlen;
39359243Sobrien	    }
39459243Sobrien	    curlen = (int) (LastChar - InputBuf);
39559243Sobrien
39659243Sobrien
397195609Smp	    nr_history_exp = 0;
398195609Smp	    autoexpand = varval(STRautoexpand);
399195609Smp	    if (autoexpand != STRNULL)
400195609Smp		nr_history_exp += ExpandHistory();
401195609Smp
402195609Smp	    /* try normal expansion only if no history references were found */
403195609Smp	    if (nr_history_exp == 0 ||
404195609Smp		Strcmp(autoexpand, STRonlyhistory) != 0) {
405195609Smp		/*
406195609Smp		 * Modified by Martin Boyer (gamin@ireq-robot.hydro.qc.ca):
407195609Smp		 * A separate variable now controls beeping after
408195609Smp		 * completion, independently of autolisting.
409195609Smp		 */
410195609Smp		expnum = (int) (Cursor - InputBuf);
411195609Smp		switch (matchval = tenematch(InputBuf, Cursor-InputBuf, fn)){
412195609Smp		case 1:
413195609Smp		    if (non_unique_match && matchbeep &&
414195609Smp			matchbeep->vec != NULL &&
415195609Smp			(Strcmp(*(matchbeep->vec), STRnotunique) == 0))
41659243Sobrien			SoundBeep();
41759243Sobrien		    break;
418195609Smp		case 0:
419195609Smp		    if (matchbeep && matchbeep->vec != NULL) {
420195609Smp			if (Strcmp(*(matchbeep->vec), STRnomatch) == 0 ||
421195609Smp			    Strcmp(*(matchbeep->vec), STRambiguous) == 0 ||
422195609Smp			    Strcmp(*(matchbeep->vec), STRnotunique) == 0)
423195609Smp			    SoundBeep();
424195609Smp		    }
425195609Smp		    else
42659243Sobrien			SoundBeep();
427195609Smp		    break;
428195609Smp		default:
429195609Smp		    if (matchval < 0) {	/* Error from tenematch */
430195609Smp			curchoice = -1;
431195609Smp			SoundBeep();
432195609Smp			break;
433167465Smp		    }
434195609Smp		    if (matchbeep && matchbeep->vec != NULL) {
435195609Smp			if ((Strcmp(*(matchbeep->vec), STRambiguous) == 0 ||
436195609Smp			     Strcmp(*(matchbeep->vec), STRnotunique) == 0))
437195609Smp			    SoundBeep();
438195609Smp		    }
439195609Smp		    else
440195609Smp			SoundBeep();
441195609Smp		    /*
442195609Smp		     * Addition by David C Lawrence <tale@pawl.rpi.edu>: If an
443195609Smp		     * attempted completion is ambiguous, list the choices.
444195609Smp		     * (PWP: this is the best feature addition to tcsh I have
445195609Smp		     * seen in many months.)
446195609Smp		     */
447195609Smp		    if (autol && autol->vec != NULL &&
448195609Smp			(Strcmp(*(autol->vec), STRambiguous) != 0 ||
449195609Smp					 expnum == Cursor - InputBuf)) {
450195609Smp			if (adrof(STRhighlight) && MarkIsSet) {
451195609Smp			    /* clear highlighting before showing completions */
452195609Smp			    MarkIsSet = 0;
453195609Smp			    ClearLines();
454195609Smp			    ClearDisp();
455195609Smp			    Refresh();
456195609Smp			    MarkIsSet = 1;
457195609Smp			}
458195609Smp			PastBottom();
459195609Smp			fn = (retval == CC_COMPLETE_ALL) ? LIST_ALL : LIST;
460195609Smp			(void) tenematch(InputBuf, Cursor-InputBuf, fn);
461195609Smp		    }
462195609Smp		    break;
46359243Sobrien		}
46459243Sobrien	    }
46559243Sobrien	    if (NeedsRedraw) {
46659243Sobrien		PastBottom();
46759243Sobrien		ClearLines();
46859243Sobrien		ClearDisp();
46959243Sobrien		NeedsRedraw = 0;
47059243Sobrien	    }
47159243Sobrien	    Refresh();
47259243Sobrien	    Argument = 1;
47359243Sobrien	    DoingArg = 0;
47459243Sobrien	    break;
47559243Sobrien
47659243Sobrien	case CC_LIST_CHOICES:
47759243Sobrien	case CC_LIST_ALL:
47859243Sobrien	    if (InputBuf[curlen] && rotate) {
47959243Sobrien		newlen = (int) (LastChar - InputBuf);
48059243Sobrien		for (idx = (int) (Cursor - InputBuf);
48159243Sobrien		     idx <= newlen; idx++)
48259243Sobrien			InputBuf[idx - newlen + curlen] =
48359243Sobrien			InputBuf[idx];
48459243Sobrien		LastChar = InputBuf + curlen;
48559243Sobrien		Cursor = Cursor - newlen + curlen;
48659243Sobrien	    }
48759243Sobrien	    curlen = (int) (LastChar - InputBuf);
48859243Sobrien	    if (curchoice >= 0)
48959243Sobrien		curchoice--;
49059243Sobrien
49159243Sobrien	    fn = (retval == CC_LIST_ALL) ? LIST_ALL : LIST;
49259243Sobrien	    /* should catch ^C here... */
49359243Sobrien	    if (tenematch(InputBuf, Cursor - InputBuf, fn) < 0)
49459243Sobrien		SoundBeep();
49559243Sobrien	    Refresh();
49659243Sobrien	    Argument = 1;
49759243Sobrien	    DoingArg = 0;
49859243Sobrien	    break;
49959243Sobrien
50059243Sobrien
50159243Sobrien	case CC_LIST_GLOB:
50259243Sobrien	    if (tenematch(InputBuf, Cursor - InputBuf, GLOB) < 0)
50359243Sobrien		SoundBeep();
50459243Sobrien	    curlen = Repair();
50559243Sobrien	    break;
50659243Sobrien
50759243Sobrien	case CC_EXPAND_GLOB:
50859243Sobrien	    if (tenematch(InputBuf, Cursor - InputBuf, GLOB_EXPAND) <= 0)
50959243Sobrien		SoundBeep();		/* Beep = No match */
51059243Sobrien	    curlen = Repair();
51159243Sobrien	    break;
51259243Sobrien
51359243Sobrien	case CC_NORMALIZE_PATH:
51459243Sobrien	    if (tenematch(InputBuf, Cursor - InputBuf, PATH_NORMALIZE) <= 0)
51559243Sobrien		SoundBeep();		/* Beep = No match */
51659243Sobrien	    curlen = Repair();
51759243Sobrien	    break;
51859243Sobrien
51959243Sobrien	case CC_EXPAND_VARS:
52059243Sobrien	    if (tenematch(InputBuf, Cursor - InputBuf, VARS_EXPAND) <= 0)
52159243Sobrien		SoundBeep();		/* Beep = No match */
52259243Sobrien	    curlen = Repair();
52359243Sobrien	    break;
52459243Sobrien
52559243Sobrien	case CC_NORMALIZE_COMMAND:
52659243Sobrien	    if (tenematch(InputBuf, Cursor - InputBuf, COMMAND_NORMALIZE) <= 0)
52759243Sobrien		SoundBeep();		/* Beep = No match */
52859243Sobrien	    curlen = Repair();
52959243Sobrien	    break;
53059243Sobrien
53159243Sobrien	case CC_HELPME:
53259243Sobrien	    xputchar('\n');
53359243Sobrien	    /* should catch ^C here... */
53459243Sobrien	    (void) tenematch(InputBuf, LastChar - InputBuf, PRINT_HELP);
53559243Sobrien	    Refresh();
53659243Sobrien	    Argument = 1;
53759243Sobrien	    DoingArg = 0;
53859243Sobrien	    curchoice = -1;
53959243Sobrien	    curlen = (int) (LastChar - InputBuf);
54059243Sobrien	    break;
54159243Sobrien
54259243Sobrien	case CC_FATAL:		/* fatal error, reset to known state */
54359243Sobrien#ifdef DEBUG_EDIT
54459243Sobrien	    xprintf(CGETS(7, 8, "*** editor fatal ERROR ***\r\n\n"));
54559243Sobrien#endif				/* DEBUG_EDIT */
54659243Sobrien	    /* put (real) cursor in a known place */
54759243Sobrien	    ClearDisp();	/* reset the display stuff */
54859243Sobrien	    ResetInLine(1);	/* reset the input pointers */
54959243Sobrien	    Refresh();		/* print the prompt again */
55059243Sobrien	    Argument = 1;
55159243Sobrien	    DoingArg = 0;
55259243Sobrien	    curchoice = -1;
55359243Sobrien	    curlen = (int) (LastChar - InputBuf);
55459243Sobrien	    break;
55559243Sobrien
55659243Sobrien	case CC_ERROR:
55759243Sobrien	default:		/* functions we don't know about */
558167465Smp	    if (adrof(STRhighlight)) {
559167465Smp		ClearLines();
560167465Smp		ClearDisp();
561167465Smp		Refresh();
562167465Smp	    }
56359243Sobrien	    DoingArg = 0;
56459243Sobrien	    Argument = 1;
56559243Sobrien	    SoundBeep();
56659243Sobrien	    flush();
56759243Sobrien	    curchoice = -1;
56859243Sobrien	    curlen = (int) (LastChar - InputBuf);
56959243Sobrien	    break;
57059243Sobrien	}
57159243Sobrien    }
57259243Sobrien    (void) Cookedmode();	/* make sure the tty is set up correctly */
57359243Sobrien    GettingInput = 0;
57459243Sobrien    flush();			/* flush any buffered output */
57559243Sobrien    return num;
57659243Sobrien}
57759243Sobrien
57859243Sobrienvoid
579167465SmpPushMacro(Char *str)
58059243Sobrien{
58159243Sobrien    if (str != NULL && MacroLvl + 1 < MAXMACROLEVELS) {
58259243Sobrien	MacroLvl++;
58359243Sobrien	KeyMacro[MacroLvl] = str;
58459243Sobrien    }
58559243Sobrien    else {
58659243Sobrien	SoundBeep();
58759243Sobrien	flush();
58859243Sobrien    }
58959243Sobrien}
59059243Sobrien
591167465Smpstruct eval1_state
592167465Smp{
593167465Smp    Char **evalvec, *evalp;
594167465Smp};
595167465Smp
596167465Smpstatic void
597167465Smpeval1_cleanup(void *xstate)
598167465Smp{
599167465Smp    struct eval1_state *state;
600167465Smp
601167465Smp    state = xstate;
602167465Smp    evalvec = state->evalvec;
603167465Smp    evalp = state->evalp;
604167465Smp    doneinp = 0;
605167465Smp}
606167465Smp
60759243Sobrien/*
60859243Sobrien * Like eval, only using the current file descriptors
60959243Sobrien */
61059243Sobrienstatic void
611167465Smpdoeval1(Char **v)
61259243Sobrien{
613167465Smp    struct eval1_state state;
614167465Smp    Char  **gv;
615167465Smp    int gflag;
61659243Sobrien
617167465Smp    gflag = tglob(v);
61859243Sobrien    if (gflag) {
619167465Smp	gv = v = globall(v, gflag);
620167465Smp	if (v == 0)
62159243Sobrien	    stderror(ERR_NOMATCH);
622167465Smp	v = copyblk(v);
62359243Sobrien    }
62459243Sobrien    else {
62559243Sobrien	gv = NULL;
626167465Smp	v = copyblk(v);
627167465Smp	trim(v);
62859243Sobrien    }
629167465Smp    if (gv)
630167465Smp	cleanup_push(gv, blk_cleanup);
63159243Sobrien
632167465Smp    state.evalvec = evalvec;
633167465Smp    state.evalp = evalp;
634167465Smp    evalvec = v;
635167465Smp    evalp = 0;
636167465Smp    cleanup_push(&state, eval1_cleanup);
637167465Smp    process(0);
638167465Smp    cleanup_until(&state);
63959243Sobrien    if (gv)
640167465Smp	cleanup_until(gv);
64159243Sobrien}
64259243Sobrien
64359243Sobrienstatic void
644167465SmpRunCommand(Char *str)
64559243Sobrien{
64659243Sobrien    Char *cmd[2];
64759243Sobrien
64859243Sobrien    xputchar('\n');	/* Start on a clean line */
64959243Sobrien
65059243Sobrien    cmd[0] = str;
65159243Sobrien    cmd[1] = NULL;
65259243Sobrien
65359243Sobrien    (void) Cookedmode();
65459243Sobrien    GettingInput = 0;
65559243Sobrien
65659243Sobrien    doeval1(cmd);
657167465Smp
65859243Sobrien    (void) Rawmode();
65959243Sobrien    GettingInput = 1;
66059243Sobrien
66159243Sobrien    ClearLines();
66259243Sobrien    ClearDisp();
66359243Sobrien    NeedsRedraw = 0;
66459243Sobrien    Refresh();
66559243Sobrien}
66659243Sobrien
667354195Sbrooksint
668354195SbrooksGetCmdChar(Char ch)
669354195Sbrooks{
670354195Sbrooks#ifndef WINNT_NATIVE // We use more than 256 for various extended keys
671354195Sbrooks    wint_t c = ch & CHAR;
672354195Sbrooks#else
673354195Sbrooks    wint_t c = ch;
674354195Sbrooks#endif
675354195Sbrooks    return c < NT_NUM_KEYS ? CurrentKeyMap[c] : F_INSERT;
676354195Sbrooks}
677354195Sbrooks
67859243Sobrienstatic int
679167465SmpGetNextCommand(KEYCMD *cmdnum, Char *ch)
68059243Sobrien{
68159243Sobrien    KEYCMD  cmd = 0;
68259243Sobrien    int     num;
68359243Sobrien
68459243Sobrien    while (cmd == 0 || cmd == F_XKEY) {
68559243Sobrien	if ((num = GetNextChar(ch)) != 1) {	/* if EOF or error */
68659243Sobrien	    return num;
68759243Sobrien	}
68859243Sobrien#ifdef	KANJI
68961519Sobrien	if (
69061519Sobrien#ifdef DSPMBYTE
69161519Sobrien	     _enable_mbdisp &&
692145479Smp#else
693316957Sdchagin	     MB_LEN_MAX == 1 &&
69461519Sobrien#endif
69561519Sobrien	     !adrof(STRnokanji) && (*ch & META)) {
69659243Sobrien	    MetaNext = 0;
69759243Sobrien	    cmd = F_INSERT;
69859243Sobrien	    break;
69959243Sobrien	}
70059243Sobrien	else
70159243Sobrien#endif /* KANJI */
70259243Sobrien	if (MetaNext) {
70359243Sobrien	    MetaNext = 0;
70459243Sobrien	    *ch |= META;
70559243Sobrien	}
706354195Sbrooks
707354195Sbrooks	cmd = GetCmdChar(*ch);
70859243Sobrien	if (cmd == F_XKEY) {
70959243Sobrien	    XmapVal val;
71059243Sobrien	    CStr cstr;
71159243Sobrien	    cstr.buf = ch;
712167465Smp	    cstr.len = 1;
71359243Sobrien	    switch (GetXkey(&cstr, &val)) {
71459243Sobrien	    case XK_CMD:
71559243Sobrien		cmd = val.cmd;
71659243Sobrien		break;
71759243Sobrien	    case XK_STR:
71859243Sobrien		PushMacro(val.str.buf);
71959243Sobrien		break;
72059243Sobrien	    case XK_EXE:
72159243Sobrien		RunCommand(val.str.buf);
72259243Sobrien		break;
72359243Sobrien	    default:
72459243Sobrien		abort();
72559243Sobrien		break;
72659243Sobrien	    }
72759243Sobrien	}
72859243Sobrien	if (!AltKeyMap)
72959243Sobrien	    CurrentKeyMap = CcKeyMap;
73059243Sobrien    }
73159243Sobrien    *cmdnum = cmd;
73259243Sobrien    return OKCMD;
73359243Sobrien}
73459243Sobrien
735145479Smpstatic Char ungetchar;
736145479Smpstatic int haveungetchar;
737145479Smp
738145479Smpvoid
739145479SmpUngetNextChar(Char cp)
740145479Smp{
741145479Smp    ungetchar = cp;
742145479Smp    haveungetchar = 1;
743145479Smp}
744145479Smp
74559243Sobrienint
746167465SmpGetNextChar(Char *cp)
74759243Sobrien{
748145479Smp    int num_read;
74959243Sobrien    int     tried = 0;
750145479Smp    char cbuf[MB_LEN_MAX];
751145479Smp    size_t cbp;
75259243Sobrien
753145479Smp    if (haveungetchar) {
754145479Smp	haveungetchar = 0;
755145479Smp	*cp = ungetchar;
756145479Smp	return 1;
757145479Smp    }
75859243Sobrien    for (;;) {
75959243Sobrien	if (MacroLvl < 0) {
76059243Sobrien	    if (!Load_input_line())
76159243Sobrien		break;
76259243Sobrien	}
76359243Sobrien	if (*KeyMacro[MacroLvl] == 0) {
76459243Sobrien	    MacroLvl--;
76559243Sobrien	    continue;
76659243Sobrien	}
76759243Sobrien	*cp = *KeyMacro[MacroLvl]++ & CHAR;
76859243Sobrien	if (*KeyMacro[MacroLvl] == 0) {	/* Needed for QuoteMode On */
76959243Sobrien	    MacroLvl--;
77059243Sobrien	}
77159243Sobrien	return (1);
77259243Sobrien    }
77359243Sobrien
77459243Sobrien    if (Rawmode() < 0)		/* make sure the tty is set up correctly */
77559243Sobrien	return 0;		/* oops: SHIN was closed */
77659243Sobrien
77769408Sache#ifdef WINNT_NATIVE
77859243Sobrien    __nt_want_vcode = 1;
77969408Sache#endif /* WINNT_NATIVE */
780145479Smp#ifdef SIG_WINDOW
781145479Smp    if (windowchg)
782145479Smp	(void) check_window_size(0);	/* for window systems */
783145479Smp#endif /* SIG_WINDOW */
784145479Smp    cbp = 0;
785145479Smp    for (;;) {
786167465Smp	while ((num_read = xread(SHIN, cbuf + cbp, 1)) == -1) {
787145479Smp	    if (!tried && fixio(SHIN, errno) != -1)
788145479Smp		tried = 1;
789145479Smp	    else {
790145479Smp# ifdef convex
791145479Smp		/* need to print error message in case the file is migrated */
792167465Smp		stderror(ERR_SYSTEM, progname, strerror(errno));
793145479Smp# endif  /* convex */
794145479Smp# ifdef WINNT_NATIVE
795145479Smp		__nt_want_vcode = 0;
796145479Smp# endif /* WINNT_NATIVE */
797145479Smp		*cp = '\0'; /* Loses possible partial character */
798145479Smp		return -1;
799145479Smp	    }
80059243Sobrien	}
801354195Sbrooks	if (cbp == 0 /* && *cbuf < NT_NUM_KEYS */
802354195Sbrooks	    && CurrentKeyMap[(unsigned char)*cbuf] == F_XKEY) {
803354195Sbrooks	    *cp = (unsigned char)*cbuf;
804354195Sbrooks	} else {
805354195Sbrooks	    cbp++;
806354195Sbrooks	    if (normal_mbtowc(cp, cbuf, cbp) == -1) {
807354195Sbrooks		reset_mbtowc();
808354195Sbrooks		if (cbp < MB_CUR_MAX)
809354195Sbrooks		    continue; /* Maybe a partial character */
810354195Sbrooks		/* And drop the following bytes, if any */
811354195Sbrooks		*cp = (unsigned char)*cbuf | INVALID_BYTE;
812354195Sbrooks	    }
813145479Smp	}
814145479Smp	break;
81559243Sobrien    }
81669408Sache#ifdef WINNT_NATIVE
817145479Smp    /* This is the part that doesn't work with WIDE_STRINGS */
81859243Sobrien    if (__nt_want_vcode == 2)
81959243Sobrien	*cp = __nt_vcode;
82059243Sobrien    __nt_want_vcode = 0;
82169408Sache#endif /* WINNT_NATIVE */
82259243Sobrien    return num_read;
82359243Sobrien}
82459243Sobrien
82559243Sobrien/*
82659243Sobrien * SpellLine - do spelling correction on the entire command line
82759243Sobrien * (which may have trailing newline).
82859243Sobrien * If cmdonly is set, only check spelling of command words.
82959243Sobrien * Return value:
83059243Sobrien * -1: Something was incorrectible, and nothing was corrected
83159243Sobrien *  0: Everything was correct
83259243Sobrien *  1: Something was corrected
83359243Sobrien */
83459243Sobrienstatic int
835167465SmpSpellLine(int cmdonly)
83659243Sobrien{
83759243Sobrien    int     endflag, matchval;
83859243Sobrien    Char   *argptr, *OldCursor, *OldLastChar;
83959243Sobrien
84059243Sobrien    OldLastChar = LastChar;
84159243Sobrien    OldCursor = Cursor;
84259243Sobrien    argptr = InputBuf;
84359243Sobrien    endflag = 1;
84459243Sobrien    matchval = 0;
84559243Sobrien    do {
84659243Sobrien	while (ismetahash(*argptr) || iscmdmeta(*argptr))
84759243Sobrien	    argptr++;
84859243Sobrien	for (Cursor = argptr;
84959243Sobrien	     *Cursor != '\0' && ((Cursor != argptr && Cursor[-1] == '\\') ||
85059243Sobrien				 (!ismetahash(*Cursor) && !iscmdmeta(*Cursor)));
85159243Sobrien	     Cursor++)
85259243Sobrien	     continue;
85359243Sobrien	if (*Cursor == '\0') {
85459243Sobrien	    Cursor = LastChar;
85559243Sobrien	    if (LastChar[-1] == '\n')
85659243Sobrien		Cursor--;
85759243Sobrien	    endflag = 0;
85859243Sobrien	}
859195609Smp	if (!MISMATCH(*argptr) &&
86059243Sobrien	    (!cmdonly || starting_a_command(argptr, InputBuf))) {
86169408Sache#ifdef WINNT_NATIVE
86259243Sobrien	    /*
86359243Sobrien	     * This hack avoids correcting drive letter changes
86459243Sobrien	     */
86559243Sobrien	    if((Cursor - InputBuf) != 2 || (char)InputBuf[1] != ':')
86669408Sache#endif /* WINNT_NATIVE */
86759243Sobrien	    {
86859243Sobrien#ifdef HASH_SPELL_CHECK
86959243Sobrien		Char save;
87059243Sobrien		size_t len = Cursor - InputBuf;
87159243Sobrien
87259243Sobrien		save = InputBuf[len];
87359243Sobrien		InputBuf[len] = '\0';
87459243Sobrien		if (find_cmd(InputBuf, 0) != 0) {
87559243Sobrien		    InputBuf[len] = save;
87659243Sobrien		    argptr = Cursor;
87759243Sobrien		    continue;
87859243Sobrien		}
87959243Sobrien		InputBuf[len] = save;
88059243Sobrien#endif /* HASH_SPELL_CHECK */
88159243Sobrien		switch (tenematch(InputBuf, Cursor - InputBuf, SPELL)) {
88259243Sobrien		case 1:		/* corrected */
88359243Sobrien		    matchval = 1;
88459243Sobrien		    break;
88559243Sobrien		case -1:		/* couldn't be corrected */
88659243Sobrien		    if (!matchval)
88759243Sobrien			matchval = -1;
88859243Sobrien		    break;
88959243Sobrien		default:		/* was correct */
89059243Sobrien		    break;
89159243Sobrien		}
89259243Sobrien	    }
89359243Sobrien	    if (LastChar != OldLastChar) {
89459243Sobrien		if (argptr < OldCursor)
89559243Sobrien		    OldCursor += (LastChar - OldLastChar);
89659243Sobrien		OldLastChar = LastChar;
89759243Sobrien	    }
89859243Sobrien	}
89959243Sobrien	argptr = Cursor;
90059243Sobrien    } while (endflag);
90159243Sobrien    Cursor = OldCursor;
90259243Sobrien    return matchval;
90359243Sobrien}
90459243Sobrien
90559243Sobrien/*
90659243Sobrien * CompleteLine - do command completion on the entire command line
90759243Sobrien * (which may have trailing newline).
90859243Sobrien * Return value:
90959243Sobrien *  0: No command matched or failure
91059243Sobrien *  1: One command matched
91159243Sobrien *  2: Several commands matched
91259243Sobrien */
91359243Sobrienstatic int
914167465SmpCompleteLine(void)
91559243Sobrien{
91659243Sobrien    int     endflag, tmatch;
91759243Sobrien    Char   *argptr, *OldCursor, *OldLastChar;
91859243Sobrien
91959243Sobrien    OldLastChar = LastChar;
92059243Sobrien    OldCursor = Cursor;
92159243Sobrien    argptr = InputBuf;
92259243Sobrien    endflag = 1;
92359243Sobrien    do {
92459243Sobrien	while (ismetahash(*argptr) || iscmdmeta(*argptr))
92559243Sobrien	    argptr++;
92659243Sobrien	for (Cursor = argptr;
92759243Sobrien	     *Cursor != '\0' && ((Cursor != argptr && Cursor[-1] == '\\') ||
92859243Sobrien				 (!ismetahash(*Cursor) && !iscmdmeta(*Cursor)));
92959243Sobrien	     Cursor++)
93059243Sobrien	     continue;
93159243Sobrien	if (*Cursor == '\0') {
93259243Sobrien	    Cursor = LastChar;
93359243Sobrien	    if (LastChar[-1] == '\n')
93459243Sobrien		Cursor--;
93559243Sobrien	    endflag = 0;
93659243Sobrien	}
937195609Smp	if (!MISMATCH(*argptr) && starting_a_command(argptr, InputBuf)) {
93859243Sobrien	    tmatch = tenematch(InputBuf, Cursor - InputBuf, RECOGNIZE);
93959243Sobrien	    if (tmatch <= 0) {
94059243Sobrien                return 0;
94159243Sobrien            } else if (tmatch > 1) {
94259243Sobrien                return 2;
94359243Sobrien	    }
94459243Sobrien	    if (LastChar != OldLastChar) {
94559243Sobrien		if (argptr < OldCursor)
94659243Sobrien		    OldCursor += (LastChar - OldLastChar);
94759243Sobrien		OldLastChar = LastChar;
94859243Sobrien	    }
94959243Sobrien	}
95059243Sobrien	argptr = Cursor;
95159243Sobrien    } while (endflag);
95259243Sobrien    Cursor = OldCursor;
95359243Sobrien    return 1;
95459243Sobrien}
95559243Sobrien
956