1: /* $Header: /home/hyperion/mu/christos/src/sys/tcsh-6.00/RCS/ed.inputl.c,v 3.0 1991/07/04 21:49:28 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. All advertising materials mentioning features or use of this software 18: * must display the following acknowledgement: 19: * This product includes software developed by the University of 20: * California, Berkeley and its contributors. 21: * 4. Neither the name of the University nor the names of its contributors 22: * may be used to endorse or promote products derived from this software 23: * without specific prior written permission. 24: * 25: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35: * SUCH DAMAGE. 36: */ 37: #include "config.h" 38: #if !defined(lint) && !defined(pdp11) 39: static char *rcsid() 40: { return "$Id: ed.inputl.c,v 3.0 1991/07/04 21:49:28 christos Exp $"; } 41: #endif 42: 43: #include "sh.h" 44: #include "ed.h" 45: #include "ed.defns.h" /* for the function names */ 46: #include "tw.h" /* for twenex stuff */ 47: 48: #define OKCMD (INBUFSIZ+INBUFSIZ) 49: extern CCRETVAL e_up_hist(); 50: extern CCRETVAL e_ex_history(); 51: 52: /* ed.inputl -- routines to get a single line from the input. */ 53: 54: extern bool tellwhat; 55: extern bool MapsAreInited; 56: extern bool Tty_raw_mode; 57: 58: /* mismatched first character */ 59: static Char mismatch[] = {'!', '\\', '^', '-', '%', '\0'}; 60: 61: static int G_N_Command __P((KEYCMD *, Char *)); 62: static int SpellLine __P((int)); 63: 64: /* CCRETVAL */ 65: int 66: Inputl() 67: { 68: CCRETVAL retval; 69: KEYCMD cmdnum = 0; 70: extern KEYCMD NumFuns; 71: unsigned char tch; /* the place where read() goes */ 72: Char ch; 73: int num; /* how many chars we have read at NL */ 74: struct varent *crct = adrof(STRcorrect); 75: struct varent *matchbeep = adrof(STRmatchbeep); 76: Char *SaveChar, *CorrChar; 77: Char Origin[INBUFSIZ], Change[INBUFSIZ]; 78: int matchval; /* from tenematch() */ 79: 80: if (!MapsAreInited) /* double extra just in case */ 81: ed_IMaps(); 82: 83: ClearDisp(); /* reset the display stuff */ 84: ResetInLine(); /* reset the input pointers */ 85: if (GettingInput) 86: MacroLvl = -1; /* editor was interrupted during input */ 87: 88: #ifdef FIONREAD 89: if (!Tty_raw_mode && MacroLvl < 0) { 90: long chrs = 0; 91: 92: (void) ioctl(SHIN, FIONREAD, (ioctl_t) & chrs); 93: if (chrs == 0) { 94: if (Rawmode() < 0) 95: return 0; 96: } 97: } 98: #endif 99: 100: GettingInput = 1; 101: NeedsRedraw = 0; 102: 103: if (tellwhat) { 104: copyn(InputBuf, WhichBuf, INBUFSIZ); 105: LastChar = InputBuf + (LastWhich - WhichBuf); 106: Cursor = InputBuf + (CursWhich - WhichBuf); 107: tellwhat = 0; 108: Hist_num = HistWhich; 109: } 110: if (Expand) { 111: (void) e_up_hist(); 112: Expand = 0; 113: } 114: Refresh(); /* print the prompt */ 115: 116: for (num = OKCMD; num == OKCMD;) { /* while still editing this line */ 117: #ifdef DEBUG_EDIT 118: if (Cursor > LastChar) 119: xprintf("Cursor > LastChar\r\n"); 120: if (Cursor < InputBuf) 121: xprintf("Cursor < InputBuf\r\n"); 122: if (Cursor > InputLim) 123: xprintf("Cursor > InputLim\r\n"); 124: if (LastChar > InputLim) 125: xprintf("LastChar > InputLim\r\n"); 126: if (InputLim != &InputBuf[INBUFSIZ - 2]) 127: xprintf("InputLim != &InputBuf[INBUFSIZ-2]\r\n"); 128: if ((!DoingArg) && (Argument != 1)) 129: xprintf("(!DoingArg) && (Argument != 1)\r\n"); 130: if (CcKeyMap[0] == 0) 131: xprintf("CcKeyMap[0] == 0 (maybe not inited)\r\n"); 132: #endif 133: 134: /* if EOF or error */ 135: if ((num = G_N_Command(&cmdnum, &ch)) != OKCMD) { 136: break; 137: } 138: 139: if (cmdnum >= NumFuns) {/* BUG CHECK command */ 140: #ifdef DEBUG_EDIT 141: xprintf("ERROR: illegal command from key 0%o\r\n", ch); 142: #endif 143: continue; /* try again */ 144: } 145: 146: /* now do the real command */ 147: retval = (*CcFuncTbl[cmdnum]) (ch); 148: 149: /* save the last command here */ 150: LastCmd = cmdnum; 151: 152: /* use any return value */ 153: switch (retval) { 154: 155: case CC_REFRESH: 156: Refresh(); 157: /* fall through */ 158: case CC_NORM: /* normal char */ 159: Argument = 1; 160: DoingArg = 0; 161: /* fall through */ 162: case CC_ARGHACK: /* Suggested by Rich Salz */ 163: /* <rsalz@pineapple.bbn.com> */ 164: break; /* keep going... */ 165: 166: case CC_EOF: /* end of file typed */ 167: #ifdef notdef 168: PromptBuf[0] = '\0'; 169: #endif 170: num = 0; 171: break; 172: 173: case CC_WHICH: /* tell what this command does */ 174: tellwhat = 1; 175: copyn(WhichBuf, InputBuf, INBUFSIZ); 176: LastWhich = WhichBuf + (LastChar - InputBuf); 177: CursWhich = WhichBuf + (Cursor - InputBuf); 178: *LastChar++ = '\n'; /* for the benifit of CSH */ 179: HistWhich = Hist_num; 180: Hist_num = 0; /* for the history commands */ 181: num = LastChar - InputBuf; /* number characters read */ 182: #ifdef notdef 183: ResetInLine(); /* reset the input pointers */ 184: #endif 185: break; 186: 187: case CC_NEWLINE: /* normal end of line */ 188: if (crct && (!Strcmp(*(crct->vec), STRcmd) || 189: !Strcmp(*(crct->vec), STRall))) { 190: (void) Strcpy(Origin, InputBuf); 191: SaveChar = LastChar; 192: if (SpellLine(!Strcmp(*(crct->vec), STRcmd)) == 1) { 193: (void) Strcpy(Change, InputBuf); 194: *Strchr(Change, '\n') = '\0'; 195: CorrChar = LastChar; /* Save the corrected end */ 196: LastChar = InputBuf; /* Null the current line */ 197: Beep(); 198: printprompt(2, Change); 199: Refresh(); 200: (void) read(SHIN, (char *) &tch, 1); 201: ch = tch; 202: if (ch != 'y' && ch != ' ') { 203: (void) Strcpy(InputBuf, Origin); 204: LastChar = SaveChar; 205: xprintf("no\n"); 206: } 207: else { 208: LastChar = CorrChar; /* Restore the corrected end */ 209: xprintf("yes\n"); 210: } 211: flush(); 212: } 213: } /* end CORRECT code */ 214: tellwhat = 0; /* just in case */ 215: Hist_num = 0; /* for the history commands */ 216: num = LastChar - InputBuf; /* return the number of chars read */ 217: /* 218: * For continuation lines, we set the prompt to prompt 2 219: */ 220: printprompt(1, NULL); 221: #ifdef notdef 222: ResetInLine(); /* reset the input pointers */ 223: PromptBuf[0] = '\0'; 224: #endif 225: break; 226: 227: case CC_CORRECT: 228: if (tenematch(InputBuf, INBUFSIZ, Cursor - InputBuf, 229: SPELL) < 0) 230: Beep(); /* Beep = No match/ambiguous */ 231: if (NeedsRedraw) { 232: ClearLines(); 233: ClearDisp(); 234: NeedsRedraw = 0; 235: } 236: Refresh(); 237: Argument = 1; 238: DoingArg = 0; 239: break; 240: 241: case CC_CORRECT_L: 242: if (SpellLine(FALSE) < 0) 243: Beep(); /* Beep = No match/ambiguous */ 244: if (NeedsRedraw) { 245: ClearLines(); 246: ClearDisp(); 247: NeedsRedraw = 0; 248: } 249: Refresh(); 250: Argument = 1; 251: DoingArg = 0; 252: break; 253: 254: 255: case CC_COMPLETE: 256: if (adrof(STRa_expand)) 257: (void) e_ex_history(); 258: /* 259: * Modified by Martin Boyer (gamin@ireq-robot.hydro.qc.ca): 260: * A separate variable now controls beeping after 261: * completion, independently of autolisting. 262: */ 263: switch (matchval = 264: tenematch(InputBuf, INBUFSIZ, Cursor-InputBuf, RECOGNIZE)) { 265: case 1: 266: if (non_unique_match && matchbeep && 267: (Strcmp(*(matchbeep->vec), STRnotunique) == 0)) 268: Beep(); 269: break; 270: case 0: 271: if (matchbeep) { 272: if (Strcmp(*(matchbeep->vec), STRnomatch) == 0 || 273: Strcmp(*(matchbeep->vec), STRambiguous) == 0 || 274: Strcmp(*(matchbeep->vec), STRnotunique) == 0) 275: Beep(); 276: } 277: else 278: Beep(); 279: break; 280: default: 281: if (matchval < 0) /* Error from tenematch */ 282: Beep(); 283: else if (matchbeep) { 284: if ((Strcmp(*(matchbeep->vec), STRambiguous) == 0 || 285: Strcmp(*(matchbeep->vec), STRnotunique) == 0)) 286: Beep(); 287: } 288: else 289: Beep(); 290: /* 291: * Addition by David C Lawrence <tale@pawl.rpi.edu>: If an 292: * attempted completion is ambiguous, list the choices. 293: * (PWP: this is the best feature addition to tcsh I have 294: * seen in many months.) 295: */ 296: if (adrof(STRa_list)) { 297: PastBottom(); 298: (void) tenematch(InputBuf, INBUFSIZ, Cursor-InputBuf, LIST); 299: } 300: break; 301: } 302: if (NeedsRedraw) { 303: PastBottom(); 304: ClearLines(); 305: ClearDisp(); 306: NeedsRedraw = 0; 307: } 308: Refresh(); 309: Argument = 1; 310: DoingArg = 0; 311: break; 312: 313: case CC_LIST_CHOICES: 314: /* should catch ^C here... */ 315: (void) tenematch(InputBuf, INBUFSIZ, Cursor - InputBuf, LIST); 316: Refresh(); 317: Argument = 1; 318: DoingArg = 0; 319: break; 320: 321: case CC_LIST_GLOB: 322: (void) tenematch(InputBuf, INBUFSIZ, Cursor - InputBuf, GLOB); 323: Refresh(); 324: Argument = 1; 325: DoingArg = 0; 326: break; 327: 328: case CC_EXPAND_GLOB: 329: if (tenematch(InputBuf, INBUFSIZ, Cursor - InputBuf, 330: GLOB_EXPAND) <= 0) 331: Beep(); /* Beep = No match */ 332: if (NeedsRedraw) { 333: ClearLines(); 334: ClearDisp(); 335: NeedsRedraw = 0; 336: } 337: Refresh(); 338: Argument = 1; 339: DoingArg = 0; 340: break; 341: 342: case CC_EXPAND_VARS: 343: if (tenematch(InputBuf, INBUFSIZ, Cursor - InputBuf, 344: VARS_EXPAND) <= 0) 345: Beep(); /* Beep = No match */ 346: if (NeedsRedraw) { 347: ClearLines(); 348: ClearDisp(); 349: NeedsRedraw = 0; 350: } 351: Refresh(); 352: Argument = 1; 353: DoingArg = 0; 354: break; 355: 356: case CC_HELPME: 357: xputchar('\n'); 358: /* should catch ^C here... */ 359: (void) tenematch(InputBuf, INBUFSIZ, LastChar - InputBuf, 360: PRINT_HELP); 361: Refresh(); 362: Argument = 1; 363: DoingArg = 0; 364: break; 365: 366: case CC_FATAL: /* fatal error, reset to known state */ 367: #ifdef DEBUG_EDIT 368: xprintf("*** editor fatal ERROR ***\r\n\n"); 369: #endif /* DEBUG_EDIT */ 370: /* put (real) cursor in a known place */ 371: ClearDisp(); /* reset the display stuff */ 372: ResetInLine(); /* reset the input pointers */ 373: Refresh(); /* print the prompt again */ 374: Argument = 1; 375: DoingArg = 0; 376: break; 377: 378: case CC_ERROR: 379: default: /* functions we don't know about */ 380: DoingArg = 0; 381: Argument = 1; 382: Beep(); 383: flush(); 384: break; 385: } 386: } 387: (void) Cookedmode(); /* make sure the tty is set up correctly */ 388: GettingInput = 0; 389: flush(); /* flush any buffered output */ 390: return num; 391: } 392: 393: void 394: PushMacro(str) 395: Char *str; 396: { 397: if (str != NULL && MacroLvl + 1 < MAXMACROLEVELS) { 398: MacroLvl++; 399: KeyMacro[MacroLvl] = str; 400: } 401: else { 402: Beep(); 403: flush(); 404: } 405: } 406: 407: static int 408: G_N_Command(cmdnum, ch) 409: KEYCMD *cmdnum; 410: register Char *ch; 411: { 412: KEYCMD cmd = 0; 413: int num; 414: Char *str; 415: 416: for (; cmd == 0 || cmd == F_XKEY;) { 417: if ((num = G_N_Char(ch)) != 1) { /* if EOF or error */ 418: return num; 419: } 420: #ifdef KANJI 421: if (*ch & META) { 422: MetaNext = 0; 423: cmd = CcViMap[' ']; 424: break; 425: } 426: else 427: #endif /* KANJI */ 428: if (MetaNext) { 429: MetaNext = 0; 430: *ch |= META; 431: } 432: cmd = Cur_KeyMap[(unsigned char) *ch]; 433: if (cmd == F_XKEY) { 434: if (GetXkey(ch, &str)) 435: cmd = (KEYCMD) * str; 436: else 437: PushMacro(str); 438: } 439: if (!AltKeyMap) { 440: Cur_KeyMap = CcKeyMap; 441: } 442: } 443: *cmdnum = cmd; 444: return OKCMD; 445: } 446: 447: int 448: G_N_Char(cp) 449: register Char *cp; 450: { 451: register int num_read; 452: 453: #if defined(EWOULDBLOCK) || (defined(POSIX) && defined(EAGAIN)) 454: # if defined(FIONBIO) || (defined(F_SETFL) && defined(O_NDELAY)) 455: # define TRY_AGAIN 456: int tried = 0; 457: 458: # endif /* FIONBIO || (F_SETFL && O_NDELAY) */ 459: #endif /* EWOULDBLOCK || (POSIX && EAGAIN) */ 460: unsigned char tcp; 461: 462: for (;;) { 463: if (MacroLvl < 0) { 464: if (!Load_input_line()) 465: break; 466: } 467: if (*KeyMacro[MacroLvl] == 0) { 468: MacroLvl--; 469: continue; 470: } 471: *cp = *KeyMacro[MacroLvl]++ & CHAR; 472: if (*KeyMacro[MacroLvl] == 0) { /* Needed for QMode On */ 473: MacroLvl--; 474: } 475: return (1); 476: } 477: 478: if (Rawmode() < 0) /* make sure the tty is set up correctly */ 479: return 0; /* oops: SHIN was closed */ 480: 481: while ((num_read = read(SHIN, (char *) &tcp, 1)) == -1) 482: switch (errno) { 483: /* 484: * Someone might have set our file descriptor to non blocking From 485: * Gray Watson (gray%antr.uucp@med.pitt.edu), Thanks!!! 486: */ 487: #ifdef EWOULDBLOCK 488: case EWOULDBLOCK: 489: #endif /* EWOULDBLOCK */ 490: #if defined(POSIX) && defined(EAGAIN) 491: # if defined(EWOULDBLOCK) && EAGAIN != EWOULDBLOCK 492: case EAGAIN: 493: # endif /* EWOULDBLOCK && EAGAIN != EWOULDBLOCK */ 494: #endif /* POSIX && EAGAIN */ 495: #ifdef TRY_AGAIN 496: if (!tried) { 497: # if defined(F_SETFL) && defined(O_NDELAY) 498: (void) fcntl(SHIN, F_SETFL, 499: fcntl(SHIN, F_GETFL, 0) & ~O_NDELAY); 500: # endif /* F_SETFL && O_NDELAY */ 501: # ifdef FIONBIO 502: (void) ioctl(SHIN, FIONBIO, (ioctl_t) & tried); 503: # endif /* FIONBIO */ 504: tried = 1; 505: break; 506: } 507: #endif 508: *cp = tcp; 509: return (num_read); 510: #ifdef _SEQUENT_ 511: case EBADF: 512: #endif /* _SEQUENT_ */ 513: case EINTR: 514: break; 515: default: 516: xprintf("G_N_Char(): errno == %d\n", errno); 517: *cp = tcp; 518: return num_read; 519: } 520: *cp = tcp; 521: return num_read; 522: } 523: 524: /* 525: * SpellLine - do spelling correction on the entire command line 526: * (which may have trailing newline). 527: * If cmdonly is set, only check spelling of command words. 528: * Return value: 529: * -1: Something was incorrectible, and nothing was corrected 530: * 0: Everything was correct 531: * 1: Something was corrected 532: */ 533: static int 534: SpellLine(cmdonly) 535: int cmdonly; 536: { 537: int endflag, matchval; 538: Char *argptr, *OldCursor, *OldLastChar; 539: 540: OldLastChar = LastChar; 541: OldCursor = Cursor; 542: argptr = InputBuf; 543: endflag = 1; 544: matchval = 0; 545: do { 546: while (ismeta(*argptr) || iscmdmeta(*argptr)) 547: argptr++; 548: for (Cursor = argptr; 549: *Cursor != '\0' && !ismeta(*Cursor) && !iscmdmeta(*Cursor); 550: Cursor++); 551: if (*Cursor == '\0') { 552: Cursor = LastChar; 553: if (LastChar[-1] == '\n') 554: Cursor--; 555: endflag = 0; 556: } 557: if (!Strchr(mismatch, *argptr) && 558: (!cmdonly || starting_a_command(argptr, InputBuf))) { 559: switch (tenematch(InputBuf, INBUFSIZ, Cursor - InputBuf, SPELL)) { 560: case 1: /* corrected */ 561: matchval = 1; 562: break; 563: case -1: /* couldn't be corrected */ 564: if (!matchval) 565: matchval = -1; 566: break; 567: default: /* was correct */ 568: break; 569: } 570: if (LastChar != OldLastChar) { 571: if (argptr < OldCursor) 572: OldCursor += (LastChar - OldLastChar); 573: OldLastChar = LastChar; 574: } 575: } 576: argptr = Cursor; 577: } while (endflag); 578: Cursor = OldCursor; 579: return matchval; 580: }