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