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