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