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