1 /* Copyright (c) 1981 Regents of the University of California */ 2 static char *sccsid = "@(#)ex_vget.c 6.3 07/08/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 } 676