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