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