1 /* $Header: /p/tcsh/cvsroot/tcsh/ed.inputl.c,v 3.66 2006/11/29 22:32:24 christos Exp $ */ 2 /* 3 * ed.inputl.c: Input line handling. 4 */ 5 /*- 6 * Copyright (c) 1980, 1991 The Regents of the University of California. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 #include "sh.h" 34 35 RCSID("$tcsh: ed.inputl.c,v 3.66 2006/11/29 22:32:24 christos Exp $") 36 37 #include "ed.h" 38 #include "ed.defns.h" /* for the function names */ 39 #include "tw.h" /* for twenex stuff */ 40 41 #define OKCMD INT_MAX 42 43 /* ed.inputl -- routines to get a single line from the input. */ 44 45 extern int MapsAreInited; 46 47 /* mismatched first character */ 48 static Char mismatch[] = 49 {'!', '^' , '\\', '-', '%', '\0', '"', '\'', '`', '\0' }; 50 51 static int Repair (void); 52 static int GetNextCommand (KEYCMD *, Char *); 53 static int SpellLine (int); 54 static int CompleteLine (void); 55 static void RunCommand (Char *); 56 static void doeval1 (Char **); 57 58 static int rotate = 0; 59 60 61 static int 62 Repair(void) 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(void) 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 int matchval; /* from tenematch() */ 92 COMMAND fn; 93 int curlen = 0; 94 int newlen; 95 int idx; 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(CGETS(6, 2, "yes\n")); 264 } 265 else { 266 Strcpy(InputBuf, Origin); 267 LastChar = SaveChar; 268 if (ch == 'e') { 269 xprintf(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(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(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(CGETS(6, 6, "No matching command\n")); 309 } else if (matchval == 2) { 310 xprintf(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 if (adrof(STRautoexpand)) 398 (void) e_expand_history(0); 399 /* 400 * Modified by Martin Boyer (gamin@ireq-robot.hydro.qc.ca): 401 * A separate variable now controls beeping after 402 * completion, independently of autolisting. 403 */ 404 expnum = (int) (Cursor - InputBuf); 405 switch (matchval = tenematch(InputBuf, Cursor-InputBuf, fn)){ 406 case 1: 407 if (non_unique_match && matchbeep && matchbeep->vec != NULL && 408 (Strcmp(*(matchbeep->vec), STRnotunique) == 0)) 409 SoundBeep(); 410 break; 411 case 0: 412 if (matchbeep && matchbeep->vec != NULL) { 413 if (Strcmp(*(matchbeep->vec), STRnomatch) == 0 || 414 Strcmp(*(matchbeep->vec), STRambiguous) == 0 || 415 Strcmp(*(matchbeep->vec), STRnotunique) == 0) 416 SoundBeep(); 417 } 418 else 419 SoundBeep(); 420 break; 421 default: 422 if (matchval < 0) { /* Error from tenematch */ 423 curchoice = -1; 424 SoundBeep(); 425 break; 426 } 427 if (matchbeep && matchbeep->vec != NULL) { 428 if ((Strcmp(*(matchbeep->vec), STRambiguous) == 0 || 429 Strcmp(*(matchbeep->vec), STRnotunique) == 0)) 430 SoundBeep(); 431 } 432 else 433 SoundBeep(); 434 /* 435 * Addition by David C Lawrence <tale@pawl.rpi.edu>: If an 436 * attempted completion is ambiguous, list the choices. 437 * (PWP: this is the best feature addition to tcsh I have 438 * seen in many months.) 439 */ 440 if (autol && autol->vec != NULL && 441 (Strcmp(*(autol->vec), STRambiguous) != 0 || 442 expnum == Cursor - InputBuf)) { 443 if (adrof(STRhighlight) && MarkIsSet) { 444 /* clear highlighting before showing completions */ 445 MarkIsSet = 0; 446 ClearLines(); 447 ClearDisp(); 448 Refresh(); 449 MarkIsSet = 1; 450 } 451 PastBottom(); 452 fn = (retval == CC_COMPLETE_ALL) ? LIST_ALL : LIST; 453 (void) tenematch(InputBuf, Cursor-InputBuf, fn); 454 } 455 break; 456 } 457 if (NeedsRedraw) { 458 PastBottom(); 459 ClearLines(); 460 ClearDisp(); 461 NeedsRedraw = 0; 462 } 463 Refresh(); 464 Argument = 1; 465 DoingArg = 0; 466 break; 467 468 case CC_LIST_CHOICES: 469 case CC_LIST_ALL: 470 if (InputBuf[curlen] && rotate) { 471 newlen = (int) (LastChar - InputBuf); 472 for (idx = (int) (Cursor - InputBuf); 473 idx <= newlen; idx++) 474 InputBuf[idx - newlen + curlen] = 475 InputBuf[idx]; 476 LastChar = InputBuf + curlen; 477 Cursor = Cursor - newlen + curlen; 478 } 479 curlen = (int) (LastChar - InputBuf); 480 if (curchoice >= 0) 481 curchoice--; 482 483 fn = (retval == CC_LIST_ALL) ? LIST_ALL : LIST; 484 /* should catch ^C here... */ 485 if (tenematch(InputBuf, Cursor - InputBuf, fn) < 0) 486 SoundBeep(); 487 Refresh(); 488 Argument = 1; 489 DoingArg = 0; 490 break; 491 492 493 case CC_LIST_GLOB: 494 if (tenematch(InputBuf, Cursor - InputBuf, GLOB) < 0) 495 SoundBeep(); 496 curlen = Repair(); 497 break; 498 499 case CC_EXPAND_GLOB: 500 if (tenematch(InputBuf, Cursor - InputBuf, GLOB_EXPAND) <= 0) 501 SoundBeep(); /* Beep = No match */ 502 curlen = Repair(); 503 break; 504 505 case CC_NORMALIZE_PATH: 506 if (tenematch(InputBuf, Cursor - InputBuf, PATH_NORMALIZE) <= 0) 507 SoundBeep(); /* Beep = No match */ 508 curlen = Repair(); 509 break; 510 511 case CC_EXPAND_VARS: 512 if (tenematch(InputBuf, Cursor - InputBuf, VARS_EXPAND) <= 0) 513 SoundBeep(); /* Beep = No match */ 514 curlen = Repair(); 515 break; 516 517 case CC_NORMALIZE_COMMAND: 518 if (tenematch(InputBuf, Cursor - InputBuf, COMMAND_NORMALIZE) <= 0) 519 SoundBeep(); /* Beep = No match */ 520 curlen = Repair(); 521 break; 522 523 case CC_HELPME: 524 xputchar('\n'); 525 /* should catch ^C here... */ 526 (void) tenematch(InputBuf, LastChar - InputBuf, PRINT_HELP); 527 Refresh(); 528 Argument = 1; 529 DoingArg = 0; 530 curchoice = -1; 531 curlen = (int) (LastChar - InputBuf); 532 break; 533 534 case CC_FATAL: /* fatal error, reset to known state */ 535 #ifdef DEBUG_EDIT 536 xprintf(CGETS(7, 8, "*** editor fatal ERROR ***\r\n\n")); 537 #endif /* DEBUG_EDIT */ 538 /* put (real) cursor in a known place */ 539 ClearDisp(); /* reset the display stuff */ 540 ResetInLine(1); /* reset the input pointers */ 541 Refresh(); /* print the prompt again */ 542 Argument = 1; 543 DoingArg = 0; 544 curchoice = -1; 545 curlen = (int) (LastChar - InputBuf); 546 break; 547 548 case CC_ERROR: 549 default: /* functions we don't know about */ 550 if (adrof(STRhighlight)) { 551 ClearLines(); 552 ClearDisp(); 553 Refresh(); 554 } 555 DoingArg = 0; 556 Argument = 1; 557 SoundBeep(); 558 flush(); 559 curchoice = -1; 560 curlen = (int) (LastChar - InputBuf); 561 break; 562 } 563 } 564 (void) Cookedmode(); /* make sure the tty is set up correctly */ 565 GettingInput = 0; 566 flush(); /* flush any buffered output */ 567 return num; 568 } 569 570 void 571 PushMacro(Char *str) 572 { 573 if (str != NULL && MacroLvl + 1 < MAXMACROLEVELS) { 574 MacroLvl++; 575 KeyMacro[MacroLvl] = str; 576 } 577 else { 578 SoundBeep(); 579 flush(); 580 } 581 } 582 583 struct eval1_state 584 { 585 Char **evalvec, *evalp; 586 }; 587 588 static void 589 eval1_cleanup(void *xstate) 590 { 591 struct eval1_state *state; 592 593 state = xstate; 594 evalvec = state->evalvec; 595 evalp = state->evalp; 596 doneinp = 0; 597 } 598 599 /* 600 * Like eval, only using the current file descriptors 601 */ 602 static void 603 doeval1(Char **v) 604 { 605 struct eval1_state state; 606 Char **gv; 607 int gflag; 608 609 gflag = tglob(v); 610 if (gflag) { 611 gv = v = globall(v, gflag); 612 if (v == 0) 613 stderror(ERR_NOMATCH); 614 v = copyblk(v); 615 } 616 else { 617 gv = NULL; 618 v = copyblk(v); 619 trim(v); 620 } 621 if (gv) 622 cleanup_push(gv, blk_cleanup); 623 624 state.evalvec = evalvec; 625 state.evalp = evalp; 626 evalvec = v; 627 evalp = 0; 628 cleanup_push(&state, eval1_cleanup); 629 process(0); 630 cleanup_until(&state); 631 if (gv) 632 cleanup_until(gv); 633 } 634 635 static void 636 RunCommand(Char *str) 637 { 638 Char *cmd[2]; 639 640 xputchar('\n'); /* Start on a clean line */ 641 642 cmd[0] = str; 643 cmd[1] = NULL; 644 645 (void) Cookedmode(); 646 GettingInput = 0; 647 648 doeval1(cmd); 649 650 (void) Rawmode(); 651 GettingInput = 1; 652 653 ClearLines(); 654 ClearDisp(); 655 NeedsRedraw = 0; 656 Refresh(); 657 } 658 659 static int 660 GetNextCommand(KEYCMD *cmdnum, Char *ch) 661 { 662 KEYCMD cmd = 0; 663 int num; 664 665 while (cmd == 0 || cmd == F_XKEY) { 666 if ((num = GetNextChar(ch)) != 1) { /* if EOF or error */ 667 return num; 668 } 669 #ifdef KANJI 670 if ( 671 #ifdef DSPMBYTE 672 _enable_mbdisp && 673 #else 674 MB_LEN_MAX == 1 && 675 #endif 676 !adrof(STRnokanji) && (*ch & META)) { 677 MetaNext = 0; 678 cmd = F_INSERT; 679 break; 680 } 681 else 682 #endif /* KANJI */ 683 if (MetaNext) { 684 MetaNext = 0; 685 *ch |= META; 686 } 687 /* XXX: This needs to be fixed so that we don't just truncate 688 * the character, we unquote it. 689 */ 690 if (*ch < NT_NUM_KEYS) 691 cmd = CurrentKeyMap[*ch]; 692 else 693 #ifdef WINNT_NATIVE 694 cmd = CurrentKeyMap[(unsigned char) *ch]; 695 #else 696 cmd = F_INSERT; 697 #endif 698 if (cmd == F_XKEY) { 699 XmapVal val; 700 CStr cstr; 701 cstr.buf = ch; 702 cstr.len = 1; 703 switch (GetXkey(&cstr, &val)) { 704 case XK_CMD: 705 cmd = val.cmd; 706 break; 707 case XK_STR: 708 PushMacro(val.str.buf); 709 break; 710 case XK_EXE: 711 RunCommand(val.str.buf); 712 break; 713 default: 714 abort(); 715 break; 716 } 717 } 718 if (!AltKeyMap) 719 CurrentKeyMap = CcKeyMap; 720 } 721 *cmdnum = cmd; 722 return OKCMD; 723 } 724 725 static Char ungetchar; 726 static int haveungetchar; 727 728 void 729 UngetNextChar(Char cp) 730 { 731 ungetchar = cp; 732 haveungetchar = 1; 733 } 734 735 int 736 GetNextChar(Char *cp) 737 { 738 int num_read; 739 int tried = 0; 740 char cbuf[MB_LEN_MAX]; 741 size_t cbp; 742 743 if (haveungetchar) { 744 haveungetchar = 0; 745 *cp = ungetchar; 746 return 1; 747 } 748 for (;;) { 749 if (MacroLvl < 0) { 750 if (!Load_input_line()) 751 break; 752 } 753 if (*KeyMacro[MacroLvl] == 0) { 754 MacroLvl--; 755 continue; 756 } 757 *cp = *KeyMacro[MacroLvl]++ & CHAR; 758 if (*KeyMacro[MacroLvl] == 0) { /* Needed for QuoteMode On */ 759 MacroLvl--; 760 } 761 return (1); 762 } 763 764 if (Rawmode() < 0) /* make sure the tty is set up correctly */ 765 return 0; /* oops: SHIN was closed */ 766 767 #ifdef WINNT_NATIVE 768 __nt_want_vcode = 1; 769 #endif /* WINNT_NATIVE */ 770 #ifdef SIG_WINDOW 771 if (windowchg) 772 (void) check_window_size(0); /* for window systems */ 773 #endif /* SIG_WINDOW */ 774 cbp = 0; 775 for (;;) { 776 while ((num_read = xread(SHIN, cbuf + cbp, 1)) == -1) { 777 if (!tried && fixio(SHIN, errno) != -1) 778 tried = 1; 779 else { 780 # ifdef convex 781 /* need to print error message in case the file is migrated */ 782 stderror(ERR_SYSTEM, progname, strerror(errno)); 783 # endif /* convex */ 784 # ifdef WINNT_NATIVE 785 __nt_want_vcode = 0; 786 # endif /* WINNT_NATIVE */ 787 *cp = '\0'; /* Loses possible partial character */ 788 return -1; 789 } 790 } 791 if (AsciiOnly) { 792 *cp = (unsigned char)*cbuf; 793 } else { 794 cbp++; 795 if (normal_mbtowc(cp, cbuf, cbp) == -1) { 796 reset_mbtowc(); 797 if (cbp < MB_CUR_MAX) 798 continue; /* Maybe a partial character */ 799 /* And drop the following bytes, if any */ 800 *cp = (unsigned char)*cbuf | INVALID_BYTE; 801 } 802 } 803 break; 804 } 805 #ifdef WINNT_NATIVE 806 /* This is the part that doesn't work with WIDE_STRINGS */ 807 if (__nt_want_vcode == 2) 808 *cp = __nt_vcode; 809 __nt_want_vcode = 0; 810 #endif /* WINNT_NATIVE */ 811 return num_read; 812 } 813 814 /* 815 * SpellLine - do spelling correction on the entire command line 816 * (which may have trailing newline). 817 * If cmdonly is set, only check spelling of command words. 818 * Return value: 819 * -1: Something was incorrectible, and nothing was corrected 820 * 0: Everything was correct 821 * 1: Something was corrected 822 */ 823 static int 824 SpellLine(int cmdonly) 825 { 826 int endflag, matchval; 827 Char *argptr, *OldCursor, *OldLastChar; 828 829 OldLastChar = LastChar; 830 OldCursor = Cursor; 831 argptr = InputBuf; 832 endflag = 1; 833 matchval = 0; 834 do { 835 while (ismetahash(*argptr) || iscmdmeta(*argptr)) 836 argptr++; 837 for (Cursor = argptr; 838 *Cursor != '\0' && ((Cursor != argptr && Cursor[-1] == '\\') || 839 (!ismetahash(*Cursor) && !iscmdmeta(*Cursor))); 840 Cursor++) 841 continue; 842 if (*Cursor == '\0') { 843 Cursor = LastChar; 844 if (LastChar[-1] == '\n') 845 Cursor--; 846 endflag = 0; 847 } 848 /* Obey current history character settings */ 849 mismatch[0] = HIST; 850 mismatch[1] = HISTSUB; 851 if (!Strchr(mismatch, *argptr) && 852 (!cmdonly || starting_a_command(argptr, InputBuf))) { 853 #ifdef WINNT_NATIVE 854 /* 855 * This hack avoids correcting drive letter changes 856 */ 857 if((Cursor - InputBuf) != 2 || (char)InputBuf[1] != ':') 858 #endif /* WINNT_NATIVE */ 859 { 860 #ifdef HASH_SPELL_CHECK 861 Char save; 862 size_t len = Cursor - InputBuf; 863 864 save = InputBuf[len]; 865 InputBuf[len] = '\0'; 866 if (find_cmd(InputBuf, 0) != 0) { 867 InputBuf[len] = save; 868 argptr = Cursor; 869 continue; 870 } 871 InputBuf[len] = save; 872 #endif /* HASH_SPELL_CHECK */ 873 switch (tenematch(InputBuf, Cursor - InputBuf, SPELL)) { 874 case 1: /* corrected */ 875 matchval = 1; 876 break; 877 case -1: /* couldn't be corrected */ 878 if (!matchval) 879 matchval = -1; 880 break; 881 default: /* was correct */ 882 break; 883 } 884 } 885 if (LastChar != OldLastChar) { 886 if (argptr < OldCursor) 887 OldCursor += (LastChar - OldLastChar); 888 OldLastChar = LastChar; 889 } 890 } 891 argptr = Cursor; 892 } while (endflag); 893 Cursor = OldCursor; 894 return matchval; 895 } 896 897 /* 898 * CompleteLine - do command completion on the entire command line 899 * (which may have trailing newline). 900 * Return value: 901 * 0: No command matched or failure 902 * 1: One command matched 903 * 2: Several commands matched 904 */ 905 static int 906 CompleteLine(void) 907 { 908 int endflag, tmatch; 909 Char *argptr, *OldCursor, *OldLastChar; 910 911 OldLastChar = LastChar; 912 OldCursor = Cursor; 913 argptr = InputBuf; 914 endflag = 1; 915 do { 916 while (ismetahash(*argptr) || iscmdmeta(*argptr)) 917 argptr++; 918 for (Cursor = argptr; 919 *Cursor != '\0' && ((Cursor != argptr && Cursor[-1] == '\\') || 920 (!ismetahash(*Cursor) && !iscmdmeta(*Cursor))); 921 Cursor++) 922 continue; 923 if (*Cursor == '\0') { 924 Cursor = LastChar; 925 if (LastChar[-1] == '\n') 926 Cursor--; 927 endflag = 0; 928 } 929 if (!Strchr(mismatch, *argptr) && starting_a_command(argptr, InputBuf)) { 930 tmatch = tenematch(InputBuf, Cursor - InputBuf, RECOGNIZE); 931 if (tmatch <= 0) { 932 return 0; 933 } else if (tmatch > 1) { 934 return 2; 935 } 936 if (LastChar != OldLastChar) { 937 if (argptr < OldCursor) 938 OldCursor += (LastChar - OldLastChar); 939 OldLastChar = LastChar; 940 } 941 } 942 argptr = Cursor; 943 } while (endflag); 944 Cursor = OldCursor; 945 return 1; 946 } 947 948