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