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) 06/07/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 } 695