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