1: /* 2: * Copyright (c) 1980 Regents of the University of California. 3: * All rights reserved. The Berkeley software License Agreement 4: * specifies the terms and conditions for redistribution. 5: */ 6: 7: #ifndef lint 8: static char *sccsid = "@(#)ex_vget.c 6.8 (Berkeley) 6/7/85"; 9: #endif not lint 10: 11: #include "ex.h" 12: #include "ex_tty.h" 13: #include "ex_vis.h" 14: 15: /* 16: * Input routines for open/visual. 17: * We handle upper case only terminals in visual and reading from the 18: * echo area here as well as notification on large changes 19: * which appears in the echo area. 20: */ 21: 22: /* 23: * Return the key. 24: */ 25: ungetkey(c) 26: int c; /* mjm: char --> int */ 27: { 28: 29: if (Peekkey != ATTN) 30: Peekkey = c; 31: } 32: 33: /* 34: * Return a keystroke, but never a ^@. 35: */ 36: getkey() 37: { 38: register int c; /* mjm: char --> int */ 39: 40: do { 41: c = getbr(); 42: if (c==0) 43: beep(); 44: } while (c == 0); 45: return (c); 46: } 47: 48: /* 49: * Tell whether next keystroke would be a ^@. 50: */ 51: peekbr() 52: { 53: 54: Peekkey = getbr(); 55: return (Peekkey == 0); 56: } 57: 58: short precbksl; 59: jmp_buf readbuf; 60: int doingread = 0; 61: 62: /* 63: * Get a keystroke, including a ^@. 64: * If an key was returned with ungetkey, that 65: * comes back first. Next comes unread input (e.g. 66: * from repeating commands with .), and finally new 67: * keystrokes. 68: * 69: * The hard work here is in mapping of \ escaped 70: * characters on upper case only terminals. 71: */ 72: getbr() 73: { 74: char ch; 75: register int c, d; 76: register char *colp; 77: int cnt; 78: #define BEEHIVE 79: #ifdef BEEHIVE 80: static char Peek2key; 81: #endif 82: extern short slevel, ttyindes; 83: 84: getATTN: 85: if (Peekkey) { 86: c = Peekkey; 87: Peekkey = 0; 88: return (c); 89: } 90: #ifdef BEEHIVE 91: if (Peek2key) { 92: c = Peek2key; 93: Peek2key = 0; 94: return (c); 95: } 96: #endif 97: if (vglobp) { 98: if (*vglobp) 99: return (lastvgk = *vglobp++); 100: lastvgk = 0; 101: return (ESCAPE); 102: } 103: if (vmacp) { 104: if (*vmacp) 105: return(*vmacp++); 106: /* End of a macro or set of nested macros */ 107: vmacp = 0; 108: if (inopen == -1) /* don't screw up undo for esc esc */ 109: vundkind = VMANY; 110: inopen = 1; /* restore old setting now that macro done */ 111: vch_mac = VC_NOTINMAC; 112: } 113: flusho(); 114: again: 115: if (setjmp(readbuf)) 116: goto getATTN; 117: doingread = 1; 118: c = read(slevel == 0 ? 0 : ttyindes, &ch, 1); 119: doingread = 0; 120: if (c != 1) { 121: if (errno == EINTR) 122: goto getATTN; 123: error("Input read error"); 124: } 125: c = ch & TRIM; 126: #ifdef BEEHIVE 127: if (XB && slevel==0 && c == ESCAPE) { 128: if (read(0, &Peek2key, 1) != 1) 129: goto getATTN; 130: Peek2key &= TRIM; 131: switch (Peek2key) { 132: case 'C': /* SPOW mode sometimes sends \EC for space */ 133: c = ' '; 134: Peek2key = 0; 135: break; 136: case 'q': /* f2 -> ^C */ 137: c = CTRL(c); 138: Peek2key = 0; 139: break; 140: case 'p': /* f1 -> esc */ 141: Peek2key = 0; 142: break; 143: } 144: } 145: #endif 146: 147: #ifdef UCVISUAL 148: /* 149: * The algorithm here is that of the UNIX kernel. 150: * See the description in the programmers manual. 151: */ 152: if (UPPERCASE) { 153: if (isupper(c)) 154: c = tolower(c); 155: if (c == '\\') { 156: if (precbksl < 2) 157: precbksl++; 158: if (precbksl == 1) 159: goto again; 160: } else if (precbksl) { 161: d = 0; 162: if (islower(c)) 163: d = toupper(c); 164: else { 165: colp = "({)}!|^~'~"; 166: while (d = *colp++) 167: if (d == c) { 168: d = *colp++; 169: break; 170: } else 171: colp++; 172: } 173: if (precbksl == 2) { 174: if (!d) { 175: Peekkey = c; 176: precbksl = 0; 177: c = '\\'; 178: } 179: } else if (d) 180: c = d; 181: else { 182: Peekkey = c; 183: precbksl = 0; 184: c = '\\'; 185: } 186: } 187: if (c != '\\') 188: precbksl = 0; 189: } 190: #endif 191: #ifdef TRACE 192: if (trace) { 193: if (!techoin) { 194: tfixnl(); 195: techoin = 1; 196: fprintf(trace, "*** Input: "); 197: } 198: tracec(c); 199: } 200: #endif 201: lastvgk = 0; 202: return (c); 203: } 204: 205: /* 206: * Get a key, but if a delete, quit or attention 207: * is typed return 0 so we will abort a partial command. 208: */ 209: getesc() 210: { 211: register int c; 212: 213: c = getkey(); 214: switch (c) { 215: 216: case CTRL(v): 217: case CTRL(q): 218: c = getkey(); 219: return (c); 220: 221: case ATTN: 222: case QUIT: 223: ungetkey(c); 224: return (0); 225: 226: case ESCAPE: 227: return (0); 228: } 229: return (c); 230: } 231: 232: /* 233: * Peek at the next keystroke. 234: */ 235: peekkey() 236: { 237: 238: Peekkey = getkey(); 239: return (Peekkey); 240: } 241: 242: /* 243: * Read a line from the echo area, with single character prompt c. 244: * A return value of 1 means the user blewit or blewit away. 245: */ 246: readecho(c) 247: char c; 248: { 249: register char *sc = cursor; 250: register int (*OP)(); 251: bool waste; 252: register int OPeek; 253: 254: if (WBOT == WECHO) 255: vclean(); 256: else 257: vclrech(0); 258: splitw++; 259: vgoto(WECHO, 0); 260: putchar(c); 261: vclreol(); 262: vgoto(WECHO, 1); 263: cursor = linebuf; linebuf[0] = 0; genbuf[0] = c; 264: if (peekbr()) { 265: if (!INS[0] || (INS[0] & (QUOTE|TRIM)) == OVERBUF) 266: goto blewit; 267: vglobp = INS; 268: } 269: OP = Pline; Pline = normline; 270: ignore(vgetline(0, genbuf + 1, &waste, c)); 271: if (Outchar == termchar) 272: putchar('\n'); 273: vscrap(); 274: Pline = OP; 275: if (Peekkey != ATTN && Peekkey != QUIT && Peekkey != CTRL(h)) { 276: cursor = sc; 277: vclreol(); 278: return (0); 279: } 280: blewit: 281: OPeek = Peekkey==CTRL(h) ? 0 : Peekkey; Peekkey = 0; 282: splitw = 0; 283: vclean(); 284: vshow(dot, NOLINE); 285: vnline(sc); 286: Peekkey = OPeek; 287: return (1); 288: } 289: 290: /* 291: * A complete command has been defined for 292: * the purposes of repeat, so copy it from 293: * the working to the previous command buffer. 294: */ 295: setLAST() 296: { 297: 298: if (vglobp || vmacp) 299: return; 300: lastreg = vreg; 301: lasthad = Xhadcnt; 302: lastcnt = Xcnt; 303: *lastcp = 0; 304: CP(lastcmd, workcmd); 305: } 306: 307: /* 308: * Gather up some more text from an insert. 309: * If the insertion buffer oveflows, then destroy 310: * the repeatability of the insert. 311: */ 312: addtext(cp) 313: char *cp; 314: { 315: 316: if (vglobp) 317: return; 318: addto(INS, cp); 319: if ((INS[0] & (QUOTE|TRIM)) == OVERBUF) 320: lastcmd[0] = 0; 321: } 322: 323: setDEL() 324: { 325: 326: setBUF(DEL); 327: } 328: 329: /* 330: * Put text from cursor upto wcursor in BUF. 331: */ 332: setBUF(BUF) 333: register char *BUF; 334: { 335: register int c; 336: register char *wp = wcursor; 337: 338: c = *wp; 339: *wp = 0; 340: BUF[0] = 0; 341: addto(BUF, cursor); 342: *wp = c; 343: } 344: 345: addto(buf, str) 346: register char *buf, *str; 347: { 348: 349: if ((buf[0] & (QUOTE|TRIM)) == OVERBUF) 350: return; 351: if (strlen(buf) + strlen(str) + 1 >= VBSIZE) { 352: buf[0] = OVERBUF; 353: return; 354: } 355: ignore(strcat(buf, str)); 356: } 357: 358: /* 359: * Note a change affecting a lot of lines, or non-visible 360: * lines. If the parameter must is set, then we only want 361: * to do this for open modes now; return and save for later 362: * notification in visual. 363: */ 364: noteit(must) 365: bool must; 366: { 367: register int sdl = destline, sdc = destcol; 368: 369: if (notecnt < 2 || !must && state == VISUAL) 370: return (0); 371: splitw++; 372: if (WBOT == WECHO) 373: vmoveitup(1, 1); 374: vigoto(WECHO, 0); 375: printf("%d %sline", notecnt, notesgn); 376: if (notecnt > 1) 377: putchar('s'); 378: if (*notenam) { 379: printf(" %s", notenam); 380: if (*(strend(notenam) - 1) != 'e') 381: putchar('e'); 382: putchar('d'); 383: } 384: vclreol(); 385: notecnt = 0; 386: if (state != VISUAL) 387: vcnt = vcline = 0; 388: splitw = 0; 389: if (state == ONEOPEN || state == CRTOPEN) 390: vup1(); 391: destline = sdl; destcol = sdc; 392: return (1); 393: } 394: 395: /* 396: * Rrrrringgggggg. 397: * If possible, use flash (VB). 398: */ 399: beep() 400: { 401: 402: if (VB) 403: vputp(VB, 0); 404: else 405: vputc(CTRL(g)); 406: } 407: 408: /* 409: * Map the command input character c, 410: * for keypads and labelled keys which do cursor 411: * motions. I.e. on an adm3a we might map ^K to ^P. 412: * DM1520 for example has a lot of mappable characters. 413: */ 414: 415: map(c,maps) 416: register int c; 417: register struct maps *maps; 418: { 419: register int d; 420: register char *p, *q; 421: char b[10]; /* Assumption: no keypad sends string longer than 10 */ 422: 423: /* 424: * Mapping for special keys on the terminal only. 425: * BUG: if there's a long sequence and it matches 426: * some chars and then misses, we lose some chars. 427: * 428: * For this to work, some conditions must be met. 429: * 1) Keypad sends SHORT (2 or 3 char) strings 430: * 2) All strings sent are same length & similar 431: * 3) The user is unlikely to type the first few chars of 432: * one of these strings very fast. 433: * Note: some code has been fixed up since the above was laid out, 434: * so conditions 1 & 2 are probably not required anymore. 435: * However, this hasn't been tested with any first char 436: * that means anything else except escape. 437: */ 438: #ifdef MDEBUG 439: if (trace) 440: fprintf(trace,"map(%c): ",c); 441: #endif 442: /* 443: * If c==0, the char came from getesc typing escape. Pass it through 444: * unchanged. 0 messes up the following code anyway. 445: */ 446: if (c==0) 447: return(0); 448: 449: b[0] = c; 450: b[1] = 0; 451: for (d=0; maps[d].mapto; d++) { 452: #ifdef MDEBUG 453: if (trace) 454: fprintf(trace,"\ntry '%s', ",maps[d].cap); 455: #endif 456: if (p = maps[d].cap) { 457: for (q=b; *p; p++, q++) { 458: #ifdef MDEBUG 459: if (trace) 460: fprintf(trace,"q->b[%d], ",q-b); 461: #endif 462: if (*q==0) { 463: /* 464: * Is there another char waiting? 465: * 466: * This test is oversimplified, but 467: * should work mostly. It handles the 468: * case where we get an ESCAPE that 469: * wasn't part of a keypad string. 470: */ 471: if ((c=='#' ? peekkey() : fastpeekkey()) == 0) { 472: #ifdef MDEBUG 473: if (trace) 474: fprintf(trace,"fpk=0: will return '%c'",c); 475: #endif 476: /* 477: * Nothing waiting. Push back 478: * what we peeked at & return 479: * failure (c). 480: * 481: * We want to be able to undo 482: * commands, but it's nonsense 483: * to undo part of an insertion 484: * so if in input mode don't. 485: */ 486: #ifdef MDEBUG 487: if (trace) 488: fprintf(trace, "Call macpush, b %d %d %d\n", b[0], b[1], b[2]); 489: #endif 490: macpush(&b[1],maps == arrows); 491: #ifdef MDEBUG 492: if (trace) 493: fprintf(trace, "return %d\n", c); 494: #endif 495: return(c); 496: } 497: *q = getkey(); 498: q[1] = 0; 499: } 500: if (*p != *q) 501: goto contin; 502: } 503: macpush(maps[d].mapto,maps == arrows); 504: c = getkey(); 505: #ifdef MDEBUG 506: if (trace) 507: fprintf(trace,"Success: push(%s), return %c",maps[d].mapto, c); 508: #endif 509: return(c); /* first char of map string */ 510: contin:; 511: } 512: } 513: #ifdef MDEBUG 514: if (trace) 515: fprintf(trace,"Fail: push(%s), return %c", &b[1], c); 516: #endif 517: macpush(&b[1],0); 518: return(c); 519: } 520: 521: /* 522: * Push st onto the front of vmacp. This is tricky because we have to 523: * worry about where vmacp was previously pointing. We also have to 524: * check for overflow (which is typically from a recursive macro) 525: * Finally we have to set a flag so the whole thing can be undone. 526: * canundo is 1 iff we want to be able to undo the macro. This 527: * is false for, for example, pushing back lookahead from fastpeekkey(), 528: * since otherwise two fast escapes can clobber our undo. 529: */ 530: macpush(st, canundo) 531: char *st; 532: int canundo; 533: { 534: char tmpbuf[BUFSIZ]; 535: 536: if (st==0 || *st==0) 537: return; 538: #ifdef MDEBUG 539: if (trace) 540: fprintf(trace, "macpush(%s), canundo=%d\n",st,canundo); 541: #endif 542: if ((vmacp ? strlen(vmacp) : 0) + strlen(st) > BUFSIZ) 543: error("Macro too long@ - maybe recursive?"); 544: if (vmacp) { 545: strcpy(tmpbuf, vmacp); 546: if (!FIXUNDO) 547: canundo = 0; /* can't undo inside a macro anyway */ 548: } 549: strcpy(vmacbuf, st); 550: if (vmacp) 551: strcat(vmacbuf, tmpbuf); 552: vmacp = vmacbuf; 553: /* arrange to be able to undo the whole macro */ 554: if (canundo) { 555: #ifdef notdef 556: otchng = tchng; 557: vsave(); 558: saveall(); 559: inopen = -1; /* no need to save since it had to be 1 or -1 before */ 560: vundkind = VMANY; 561: #endif 562: vch_mac = VC_NOCHANGE; 563: } 564: } 565: 566: #ifdef TRACE 567: visdump(s) 568: char *s; 569: { 570: register int i; 571: 572: if (!trace) return; 573: 574: fprintf(trace, "\n%s: basWTOP=%d, basWLINES=%d, WTOP=%d, WBOT=%d, WLINES=%d, WCOLS=%d, WECHO=%d\n", 575: s, basWTOP, basWLINES, WTOP, WBOT, WLINES, WCOLS, WECHO); 576: fprintf(trace, " vcnt=%d, vcline=%d, cursor=%d, wcursor=%d, wdot=%d\n", 577: vcnt, vcline, cursor-linebuf, wcursor-linebuf, wdot-zero); 578: for (i=0; i<TUBELINES; i++) 579: if (vtube[i] && *vtube[i]) 580: fprintf(trace, "%d: '%s'\n", i, vtube[i]); 581: tvliny(); 582: } 583: 584: vudump(s) 585: char *s; 586: { 587: register line *p; 588: char savelb[1024]; 589: 590: if (!trace) return; 591: 592: fprintf(trace, "\n%s: undkind=%d, vundkind=%d, unddel=%d, undap1=%d, undap2=%d,\n", 593: s, undkind, vundkind, lineno(unddel), lineno(undap1), lineno(undap2)); 594: fprintf(trace, " undadot=%d, dot=%d, dol=%d, unddol=%d, truedol=%d\n", 595: lineno(undadot), lineno(dot), lineno(dol), lineno(unddol), lineno(truedol)); 596: fprintf(trace, " [\n"); 597: CP(savelb, linebuf); 598: fprintf(trace, "linebuf = '%s'\n", linebuf); 599: for (p=zero+1; p<=truedol; p++) { 600: fprintf(trace, "%o ", *p); 601: getline(*p); 602: fprintf(trace, "'%s'\n", linebuf); 603: } 604: fprintf(trace, "]\n"); 605: CP(linebuf, savelb); 606: } 607: #endif 608: 609: /* 610: * Get a count from the keyed input stream. 611: * A zero count is indistinguishable from no count. 612: */ 613: vgetcnt() 614: { 615: register int c, cnt; 616: 617: cnt = 0; 618: for (;;) { 619: c = getkey(); 620: if (!isdigit(c)) 621: break; 622: cnt *= 10, cnt += c - '0'; 623: } 624: ungetkey(c); 625: Xhadcnt = 1; 626: Xcnt = cnt; 627: return(cnt); 628: } 629: 630: /* 631: * fastpeekkey is just like peekkey but insists the character come in 632: * fast (within 1 second). This will succeed if it is the 2nd char of 633: * a machine generated sequence (such as a function pad from an escape 634: * flavor terminal) but fail for a human hitting escape then waiting. 635: */ 636: fastpeekkey() 637: { 638: int trapalarm(); 639: int (*Oint)(); 640: register int c; 641: 642: /* 643: * If the user has set notimeout, we wait forever for a key. 644: * If we are in a macro we do too, but since it's already 645: * buffered internally it will return immediately. 646: * In other cases we force this to die in 1 second. 647: * This is pretty reliable (VMUNIX rounds it to .5 - 1.5 secs, 648: * but UNIX truncates it to 0 - 1 secs) but due to system delays 649: * there are times when arrow keys or very fast typing get counted 650: * as separate. notimeout is provided for people who dislike such 651: * nondeterminism. 652: */ 653: #ifdef MDEBUG 654: if (trace) 655: fprintf(trace,"\nfastpeekkey: ",c); 656: #endif 657: Oint = signal(SIGINT, trapalarm); 658: if (value(TIMEOUT) && inopen >= 0) { 659: signal(SIGALRM, trapalarm); 660: #ifdef MDEBUG 661: alarm(10); 662: if (trace) 663: fprintf(trace, "set alarm "); 664: #else 665: alarm(1); 666: #endif 667: } 668: CATCH 669: c = peekkey(); 670: #ifdef MDEBUG 671: if (trace) 672: fprintf(trace,"[OK]",c); 673: #endif 674: alarm(0); 675: ONERR 676: c = 0; 677: #ifdef MDEBUG 678: if (trace) 679: fprintf(trace,"[TIMEOUT]",c); 680: #endif 681: ENDCATCH 682: #ifdef MDEBUG 683: if (trace) 684: fprintf(trace,"[fpk:%o]",c); 685: #endif 686: signal(SIGINT,Oint); 687: return(c); 688: } 689: 690: trapalarm() { 691: alarm(0); 692: if (vcatch) 693: longjmp(vreslab,1); 694: }