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