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
Repair(void)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
Inputl(void)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
PushMacro(Char * str)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
eval1_cleanup(void * xstate)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
doeval1(Char ** v)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
RunCommand(Char * str)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
GetCmdChar(Char ch)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
GetNextCommand(KEYCMD * cmdnum,Char * ch)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
UngetNextChar(Char cp)739 UngetNextChar(Char cp)
740 {
741 ungetchar = cp;
742 haveungetchar = 1;
743 }
744
745 int
GetNextChar(Char * cp)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
SpellLine(int cmdonly)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
CompleteLine(void)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