xref: /freebsd/contrib/tcsh/ed.inputl.c (revision af23369a6deaaeb612ab266eb88b8bb8d560c322)
1 /*
2  * ed.inputl.c: Input line handling.
3  */
4 /*-
5  * Copyright (c) 1980, 1991 The Regents of the University of California.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 #include "sh.h"
33 #include "ed.h"
34 #include "ed.defns.h"		/* for the function names */
35 #include "tw.h"			/* for twenex stuff */
36 
37 #define OKCMD INT_MAX
38 
39 /* ed.inputl -- routines to get a single line from the input. */
40 
41 extern int MapsAreInited;
42 
43 /* mismatched first character */
44 static Char mismatch[] = { '\\', '-', '%', '\0' };
45 /* don't Strchr() for '\0', obey current history character settings */
46 #define MISMATCH(c) ((c) == '\0' || (c) == HIST || (c) == HISTSUB || \
47 			Strchr(mismatch, (c)))
48 
49 static	int	Repair		(void);
50 static	int	GetNextCommand	(KEYCMD *, Char *);
51 static	int	SpellLine	(int);
52 static	int	CompleteLine	(void);
53 static	void	RunCommand	(Char *);
54 static  void 	doeval1		(Char **);
55 
56 static int rotate = 0;
57 
58 
59 static int
60 Repair(void)
61 {
62     if (NeedsRedraw) {
63 	ClearLines();
64 	ClearDisp();
65 	NeedsRedraw = 0;
66     }
67     Refresh();
68     Argument = 1;
69     DoingArg = 0;
70     curchoice = -1;
71     return (int) (LastChar - InputBuf);
72 }
73 
74 /* CCRETVAL */
75 int
76 Inputl(void)
77 {
78     CCRETVAL retval;
79     KEYCMD  cmdnum = 0;
80     unsigned char tch;		/* the place where read() goes */
81     Char    ch;
82     int     num;		/* how many chars we have read at NL */
83     int	    expnum;
84     struct varent *crct = inheredoc ? NULL : adrof(STRcorrect);
85     struct varent *autol = adrof(STRautolist);
86     struct varent *matchbeep = adrof(STRmatchbeep);
87     struct varent *imode = adrof(STRinputmode);
88     Char   *SaveChar, *CorrChar;
89     int     matchval;		/* from tenematch() */
90     int     nr_history_exp;     /* number of (attempted) history expansions */
91     COMMAND fn;
92     int curlen = 0;
93     int newlen;
94     int idx;
95     Char *autoexpand;
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("%s", CGETS(6, 2, "yes\n"));
264 		    }
265 		    else {
266 			Strcpy(InputBuf, Origin);
267 			LastChar = SaveChar;
268 			if (ch == 'e') {
269 			    xprintf("%s", 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("%s", 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("%s", 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("%s", CGETS(6, 6, "No matching command\n"));
309 		} else if (matchval == 2) {
310 		    xprintf("%s", 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 	    nr_history_exp = 0;
398 	    autoexpand = varval(STRautoexpand);
399 	    if (autoexpand != STRNULL)
400 		nr_history_exp += ExpandHistory();
401 
402 	    /* try normal expansion only if no history references were found */
403 	    if (nr_history_exp == 0 ||
404 		Strcmp(autoexpand, STRonlyhistory) != 0) {
405 		/*
406 		 * Modified by Martin Boyer (gamin@ireq-robot.hydro.qc.ca):
407 		 * A separate variable now controls beeping after
408 		 * completion, independently of autolisting.
409 		 */
410 		expnum = (int) (Cursor - InputBuf);
411 		switch (matchval = tenematch(InputBuf, Cursor-InputBuf, fn)){
412 		case 1:
413 		    if (non_unique_match && matchbeep &&
414 			matchbeep->vec != NULL &&
415 			(Strcmp(*(matchbeep->vec), STRnotunique) == 0))
416 			SoundBeep();
417 		    break;
418 		case 0:
419 		    if (matchbeep && matchbeep->vec != NULL) {
420 			if (Strcmp(*(matchbeep->vec), STRnomatch) == 0 ||
421 			    Strcmp(*(matchbeep->vec), STRambiguous) == 0 ||
422 			    Strcmp(*(matchbeep->vec), STRnotunique) == 0)
423 			    SoundBeep();
424 		    }
425 		    else
426 			SoundBeep();
427 		    break;
428 		default:
429 		    if (matchval < 0) {	/* Error from tenematch */
430 			curchoice = -1;
431 			SoundBeep();
432 			break;
433 		    }
434 		    if (matchbeep && matchbeep->vec != NULL) {
435 			if ((Strcmp(*(matchbeep->vec), STRambiguous) == 0 ||
436 			     Strcmp(*(matchbeep->vec), STRnotunique) == 0))
437 			    SoundBeep();
438 		    }
439 		    else
440 			SoundBeep();
441 		    /*
442 		     * Addition by David C Lawrence <tale@pawl.rpi.edu>: If an
443 		     * attempted completion is ambiguous, list the choices.
444 		     * (PWP: this is the best feature addition to tcsh I have
445 		     * seen in many months.)
446 		     */
447 		    if (autol && autol->vec != NULL &&
448 			(Strcmp(*(autol->vec), STRambiguous) != 0 ||
449 					 expnum == Cursor - InputBuf)) {
450 			if (adrof(STRhighlight) && MarkIsSet) {
451 			    /* clear highlighting before showing completions */
452 			    MarkIsSet = 0;
453 			    ClearLines();
454 			    ClearDisp();
455 			    Refresh();
456 			    MarkIsSet = 1;
457 			}
458 			PastBottom();
459 			fn = (retval == CC_COMPLETE_ALL) ? LIST_ALL : LIST;
460 			(void) tenematch(InputBuf, Cursor-InputBuf, fn);
461 		    }
462 		    break;
463 		}
464 	    }
465 	    if (NeedsRedraw) {
466 		PastBottom();
467 		ClearLines();
468 		ClearDisp();
469 		NeedsRedraw = 0;
470 	    }
471 	    Refresh();
472 	    Argument = 1;
473 	    DoingArg = 0;
474 	    break;
475 
476 	case CC_LIST_CHOICES:
477 	case CC_LIST_ALL:
478 	    if (InputBuf[curlen] && rotate) {
479 		newlen = (int) (LastChar - InputBuf);
480 		for (idx = (int) (Cursor - InputBuf);
481 		     idx <= newlen; idx++)
482 			InputBuf[idx - newlen + curlen] =
483 			InputBuf[idx];
484 		LastChar = InputBuf + curlen;
485 		Cursor = Cursor - newlen + curlen;
486 	    }
487 	    curlen = (int) (LastChar - InputBuf);
488 	    if (curchoice >= 0)
489 		curchoice--;
490 
491 	    fn = (retval == CC_LIST_ALL) ? LIST_ALL : LIST;
492 	    /* should catch ^C here... */
493 	    if (tenematch(InputBuf, Cursor - InputBuf, fn) < 0)
494 		SoundBeep();
495 	    Refresh();
496 	    Argument = 1;
497 	    DoingArg = 0;
498 	    break;
499 
500 
501 	case CC_LIST_GLOB:
502 	    if (tenematch(InputBuf, Cursor - InputBuf, GLOB) < 0)
503 		SoundBeep();
504 	    curlen = Repair();
505 	    break;
506 
507 	case CC_EXPAND_GLOB:
508 	    if (tenematch(InputBuf, Cursor - InputBuf, GLOB_EXPAND) <= 0)
509 		SoundBeep();		/* Beep = No match */
510 	    curlen = Repair();
511 	    break;
512 
513 	case CC_NORMALIZE_PATH:
514 	    if (tenematch(InputBuf, Cursor - InputBuf, PATH_NORMALIZE) <= 0)
515 		SoundBeep();		/* Beep = No match */
516 	    curlen = Repair();
517 	    break;
518 
519 	case CC_EXPAND_VARS:
520 	    if (tenematch(InputBuf, Cursor - InputBuf, VARS_EXPAND) <= 0)
521 		SoundBeep();		/* Beep = No match */
522 	    curlen = Repair();
523 	    break;
524 
525 	case CC_NORMALIZE_COMMAND:
526 	    if (tenematch(InputBuf, Cursor - InputBuf, COMMAND_NORMALIZE) <= 0)
527 		SoundBeep();		/* Beep = No match */
528 	    curlen = Repair();
529 	    break;
530 
531 	case CC_HELPME:
532 	    xputchar('\n');
533 	    /* should catch ^C here... */
534 	    (void) tenematch(InputBuf, LastChar - InputBuf, PRINT_HELP);
535 	    Refresh();
536 	    Argument = 1;
537 	    DoingArg = 0;
538 	    curchoice = -1;
539 	    curlen = (int) (LastChar - InputBuf);
540 	    break;
541 
542 	case CC_FATAL:		/* fatal error, reset to known state */
543 #ifdef DEBUG_EDIT
544 	    xprintf(CGETS(7, 8, "*** editor fatal ERROR ***\r\n\n"));
545 #endif				/* DEBUG_EDIT */
546 	    /* put (real) cursor in a known place */
547 	    ClearDisp();	/* reset the display stuff */
548 	    ResetInLine(1);	/* reset the input pointers */
549 	    Refresh();		/* print the prompt again */
550 	    Argument = 1;
551 	    DoingArg = 0;
552 	    curchoice = -1;
553 	    curlen = (int) (LastChar - InputBuf);
554 	    break;
555 
556 	case CC_ERROR:
557 	default:		/* functions we don't know about */
558 	    if (adrof(STRhighlight)) {
559 		ClearLines();
560 		ClearDisp();
561 		Refresh();
562 	    }
563 	    DoingArg = 0;
564 	    Argument = 1;
565 	    SoundBeep();
566 	    flush();
567 	    curchoice = -1;
568 	    curlen = (int) (LastChar - InputBuf);
569 	    break;
570 	}
571     }
572     (void) Cookedmode();	/* make sure the tty is set up correctly */
573     GettingInput = 0;
574     flush();			/* flush any buffered output */
575     return num;
576 }
577 
578 void
579 PushMacro(Char *str)
580 {
581     if (str != NULL && MacroLvl + 1 < MAXMACROLEVELS) {
582 	MacroLvl++;
583 	KeyMacro[MacroLvl] = str;
584     }
585     else {
586 	SoundBeep();
587 	flush();
588     }
589 }
590 
591 struct eval1_state
592 {
593     Char **evalvec, *evalp;
594 };
595 
596 static void
597 eval1_cleanup(void *xstate)
598 {
599     struct eval1_state *state;
600 
601     state = xstate;
602     evalvec = state->evalvec;
603     evalp = state->evalp;
604     doneinp = 0;
605 }
606 
607 /*
608  * Like eval, only using the current file descriptors
609  */
610 static void
611 doeval1(Char **v)
612 {
613     struct eval1_state state;
614     Char  **gv;
615     int gflag;
616 
617     gflag = tglob(v);
618     if (gflag) {
619 	gv = v = globall(v, gflag);
620 	if (v == 0)
621 	    stderror(ERR_NOMATCH);
622 	v = copyblk(v);
623     }
624     else {
625 	gv = NULL;
626 	v = copyblk(v);
627 	trim(v);
628     }
629     if (gv)
630 	cleanup_push(gv, blk_cleanup);
631 
632     state.evalvec = evalvec;
633     state.evalp = evalp;
634     evalvec = v;
635     evalp = 0;
636     cleanup_push(&state, eval1_cleanup);
637     process(0);
638     cleanup_until(&state);
639     if (gv)
640 	cleanup_until(gv);
641 }
642 
643 static void
644 RunCommand(Char *str)
645 {
646     Char *cmd[2];
647 
648     xputchar('\n');	/* Start on a clean line */
649 
650     cmd[0] = str;
651     cmd[1] = NULL;
652 
653     (void) Cookedmode();
654     GettingInput = 0;
655 
656     doeval1(cmd);
657 
658     (void) Rawmode();
659     GettingInput = 1;
660 
661     ClearLines();
662     ClearDisp();
663     NeedsRedraw = 0;
664     Refresh();
665 }
666 
667 int
668 GetCmdChar(Char ch)
669 {
670 #ifndef WINNT_NATIVE // We use more than 256 for various extended keys
671     wint_t c = ch & CHAR;
672 #else
673     wint_t c = ch;
674 #endif
675     return c < NT_NUM_KEYS ? CurrentKeyMap[c] : F_INSERT;
676 }
677 
678 static int
679 GetNextCommand(KEYCMD *cmdnum, Char *ch)
680 {
681     KEYCMD  cmd = 0;
682     int     num;
683 
684     while (cmd == 0 || cmd == F_XKEY) {
685 	if ((num = GetNextChar(ch)) != 1) {	/* if EOF or error */
686 	    return num;
687 	}
688 #ifdef	KANJI
689 	if (
690 #ifdef DSPMBYTE
691 	     _enable_mbdisp &&
692 #else
693 	     MB_LEN_MAX == 1 &&
694 #endif
695 	     !adrof(STRnokanji) && (*ch & META)) {
696 	    MetaNext = 0;
697 	    cmd = F_INSERT;
698 	    break;
699 	}
700 	else
701 #endif /* KANJI */
702 	if (MetaNext) {
703 	    MetaNext = 0;
704 	    *ch |= META;
705 	}
706 
707 	cmd = GetCmdChar(*ch);
708 	if (cmd == F_XKEY) {
709 	    XmapVal val;
710 	    CStr cstr;
711 	    cstr.buf = ch;
712 	    cstr.len = 1;
713 	    switch (GetXkey(&cstr, &val)) {
714 	    case XK_CMD:
715 		cmd = val.cmd;
716 		break;
717 	    case XK_STR:
718 		PushMacro(val.str.buf);
719 		break;
720 	    case XK_EXE:
721 		RunCommand(val.str.buf);
722 		break;
723 	    default:
724 		abort();
725 		break;
726 	    }
727 	}
728 	if (!AltKeyMap)
729 	    CurrentKeyMap = CcKeyMap;
730     }
731     *cmdnum = cmd;
732     return OKCMD;
733 }
734 
735 static Char ungetchar;
736 static int haveungetchar;
737 
738 void
739 UngetNextChar(Char cp)
740 {
741     ungetchar = cp;
742     haveungetchar = 1;
743 }
744 
745 int
746 GetNextChar(Char *cp)
747 {
748     int num_read;
749     int     tried = 0;
750     char cbuf[MB_LEN_MAX];
751     size_t cbp;
752 
753     if (haveungetchar) {
754 	haveungetchar = 0;
755 	*cp = ungetchar;
756 	return 1;
757     }
758     for (;;) {
759 	if (MacroLvl < 0) {
760 	    if (!Load_input_line())
761 		break;
762 	}
763 	if (*KeyMacro[MacroLvl] == 0) {
764 	    MacroLvl--;
765 	    continue;
766 	}
767 	*cp = *KeyMacro[MacroLvl]++ & CHAR;
768 	if (*KeyMacro[MacroLvl] == 0) {	/* Needed for QuoteMode On */
769 	    MacroLvl--;
770 	}
771 	return (1);
772     }
773 
774     if (Rawmode() < 0)		/* make sure the tty is set up correctly */
775 	return 0;		/* oops: SHIN was closed */
776 
777 #ifdef WINNT_NATIVE
778     __nt_want_vcode = 1;
779 #endif /* WINNT_NATIVE */
780 #ifdef SIG_WINDOW
781     if (windowchg)
782 	(void) check_window_size(0);	/* for window systems */
783 #endif /* SIG_WINDOW */
784     cbp = 0;
785     for (;;) {
786 	while ((num_read = xread(SHIN, cbuf + cbp, 1)) == -1) {
787 	    if (!tried && fixio(SHIN, errno) != -1)
788 		tried = 1;
789 	    else {
790 # ifdef convex
791 		/* need to print error message in case the file is migrated */
792 		stderror(ERR_SYSTEM, progname, strerror(errno));
793 # endif  /* convex */
794 # ifdef WINNT_NATIVE
795 		__nt_want_vcode = 0;
796 # endif /* WINNT_NATIVE */
797 		*cp = '\0'; /* Loses possible partial character */
798 		return -1;
799 	    }
800 	}
801 	if (cbp == 0 /* && *cbuf < NT_NUM_KEYS */
802 	    && CurrentKeyMap[(unsigned char)*cbuf] == F_XKEY) {
803 	    *cp = (unsigned char)*cbuf;
804 	} else {
805 	    cbp++;
806 	    if (normal_mbtowc(cp, cbuf, cbp) == -1) {
807 		reset_mbtowc();
808 		if (cbp < MB_CUR_MAX)
809 		    continue; /* Maybe a partial character */
810 		/* And drop the following bytes, if any */
811 		*cp = (unsigned char)*cbuf | INVALID_BYTE;
812 	    }
813 	}
814 	break;
815     }
816 #ifdef WINNT_NATIVE
817     /* This is the part that doesn't work with WIDE_STRINGS */
818     if (__nt_want_vcode == 2)
819 	*cp = __nt_vcode;
820     __nt_want_vcode = 0;
821 #endif /* WINNT_NATIVE */
822     return num_read;
823 }
824 
825 /*
826  * SpellLine - do spelling correction on the entire command line
827  * (which may have trailing newline).
828  * If cmdonly is set, only check spelling of command words.
829  * Return value:
830  * -1: Something was incorrectible, and nothing was corrected
831  *  0: Everything was correct
832  *  1: Something was corrected
833  */
834 static int
835 SpellLine(int cmdonly)
836 {
837     int     endflag, matchval;
838     Char   *argptr, *OldCursor, *OldLastChar;
839 
840     OldLastChar = LastChar;
841     OldCursor = Cursor;
842     argptr = InputBuf;
843     endflag = 1;
844     matchval = 0;
845     do {
846 	while (ismetahash(*argptr) || iscmdmeta(*argptr))
847 	    argptr++;
848 	for (Cursor = argptr;
849 	     *Cursor != '\0' && ((Cursor != argptr && Cursor[-1] == '\\') ||
850 				 (!ismetahash(*Cursor) && !iscmdmeta(*Cursor)));
851 	     Cursor++)
852 	     continue;
853 	if (*Cursor == '\0') {
854 	    Cursor = LastChar;
855 	    if (LastChar[-1] == '\n')
856 		Cursor--;
857 	    endflag = 0;
858 	}
859 	if (!MISMATCH(*argptr) &&
860 	    (!cmdonly || starting_a_command(argptr, InputBuf))) {
861 #ifdef WINNT_NATIVE
862 	    /*
863 	     * This hack avoids correcting drive letter changes
864 	     */
865 	    if ((Cursor - InputBuf) != 2 || (char)InputBuf[1] != ':')
866 #endif /* WINNT_NATIVE */
867 	    {
868 #ifdef HASH_SPELL_CHECK
869 		Char save;
870 		size_t len = Cursor - InputBuf;
871 
872 		save = InputBuf[len];
873 		InputBuf[len] = '\0';
874 		if (find_cmd(InputBuf, 0) != 0) {
875 		    InputBuf[len] = save;
876 		    argptr = Cursor;
877 		    continue;
878 		}
879 		InputBuf[len] = save;
880 #endif /* HASH_SPELL_CHECK */
881 		switch (tenematch(InputBuf, Cursor - InputBuf, SPELL)) {
882 		case 1:		/* corrected */
883 		    matchval = 1;
884 		    break;
885 		case -1:		/* couldn't be corrected */
886 		    if (!matchval)
887 			matchval = -1;
888 		    break;
889 		default:		/* was correct */
890 		    break;
891 		}
892 	    }
893 	    if (LastChar != OldLastChar) {
894 		if (argptr < OldCursor)
895 		    OldCursor += (LastChar - OldLastChar);
896 		OldLastChar = LastChar;
897 	    }
898 	}
899 	argptr = Cursor;
900     } while (endflag);
901     Cursor = OldCursor;
902     return matchval;
903 }
904 
905 /*
906  * CompleteLine - do command completion on the entire command line
907  * (which may have trailing newline).
908  * Return value:
909  *  0: No command matched or failure
910  *  1: One command matched
911  *  2: Several commands matched
912  */
913 static int
914 CompleteLine(void)
915 {
916     int     endflag, tmatch;
917     Char   *argptr, *OldCursor, *OldLastChar;
918 
919     OldLastChar = LastChar;
920     OldCursor = Cursor;
921     argptr = InputBuf;
922     endflag = 1;
923     do {
924 	while (ismetahash(*argptr) || iscmdmeta(*argptr))
925 	    argptr++;
926 	for (Cursor = argptr;
927 	     *Cursor != '\0' && ((Cursor != argptr && Cursor[-1] == '\\') ||
928 				 (!ismetahash(*Cursor) && !iscmdmeta(*Cursor)));
929 	     Cursor++)
930 	     continue;
931 	if (*Cursor == '\0') {
932 	    Cursor = LastChar;
933 	    if (LastChar[-1] == '\n')
934 		Cursor--;
935 	    endflag = 0;
936 	}
937 	if (!MISMATCH(*argptr) && starting_a_command(argptr, InputBuf)) {
938 	    tmatch = tenematch(InputBuf, Cursor - InputBuf, RECOGNIZE);
939 	    if (tmatch <= 0) {
940                 return 0;
941             } else if (tmatch > 1) {
942                 return 2;
943 	    }
944 	    if (LastChar != OldLastChar) {
945 		if (argptr < OldCursor)
946 		    OldCursor += (LastChar - OldLastChar);
947 		OldLastChar = LastChar;
948 	    }
949 	}
950 	argptr = Cursor;
951     } while (endflag);
952     Cursor = OldCursor;
953     return 1;
954 }
955 
956