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