1 /* Copyright (c) 1980 Regents of the University of California */ 2 static char *sccsid = "@(#)ex_vops2.c 5.1 08/20/80"; 3 #include "ex.h" 4 #include "ex_tty.h" 5 #include "ex_vis.h" 6 7 /* 8 * Low level routines for operations sequences, 9 * and mostly, insert mode (and a subroutine 10 * to read an input line, including in the echo area.) 11 */ 12 char *vUA1, *vUA2; 13 char *vUD1, *vUD2; 14 15 /* 16 * Obleeperate characters in hardcopy 17 * open with \'s. 18 */ 19 bleep(i, cp) 20 register int i; 21 char *cp; 22 { 23 24 i -= column(cp); 25 do 26 putchar('\\' | QUOTE); 27 while (--i >= 0); 28 rubble = 1; 29 } 30 31 /* 32 * Common code for middle part of delete 33 * and change operating on parts of lines. 34 */ 35 vdcMID() 36 { 37 register char *cp; 38 39 squish(); 40 setLAST(); 41 if (FIXUNDO) 42 vundkind = VCHNG, CP(vutmp, linebuf); 43 if (wcursor < cursor) 44 cp = wcursor, wcursor = cursor, cursor = cp; 45 vUD1 = vUA1 = vUA2 = cursor; vUD2 = wcursor; 46 return (column(wcursor - 1)); 47 } 48 49 /* 50 * Take text from linebuf and stick it 51 * in the VBSIZE buffer BUF. Used to save 52 * deleted text of part of line. 53 */ 54 takeout(BUF) 55 char *BUF; 56 { 57 register char *cp; 58 59 if (wcursor < linebuf) 60 wcursor = linebuf; 61 if (cursor == wcursor) { 62 beep(); 63 return; 64 } 65 if (wcursor < cursor) { 66 cp = wcursor; 67 wcursor = cursor; 68 cursor = cp; 69 } 70 setBUF(BUF); 71 if ((BUF[0] & (QUOTE|TRIM)) == OVERBUF) 72 beep(); 73 } 74 75 /* 76 * Are we at the end of the printed representation of the 77 * line? Used internally in hardcopy open. 78 */ 79 ateopr() 80 { 81 register int i, c; 82 register char *cp = vtube[destline] + destcol; 83 84 for (i = WCOLS - destcol; i > 0; i--) { 85 c = *cp++; 86 if (c == 0) 87 return (1); 88 if (c != ' ' && (c & QUOTE) == 0) 89 return (0); 90 } 91 return (1); 92 } 93 94 /* 95 * Append. 96 * 97 * This routine handles the top level append, doing work 98 * as each new line comes in, and arranging repeatability. 99 * It also handles append with repeat counts, and calculation 100 * of autoindents for new lines. 101 */ 102 bool vaifirst; 103 bool gobbled; 104 char *ogcursor; 105 106 vappend(ch, cnt, indent) 107 char ch; 108 int cnt, indent; 109 { 110 register int i; 111 register char *gcursor; 112 bool escape; 113 int repcnt; 114 short oldhold = hold; 115 116 /* 117 * Before a move in hardopen when the line is dirty 118 * or we are in the middle of the printed representation, 119 * we retype the line to the left of the cursor so the 120 * insert looks clean. 121 */ 122 if (ch != 'o' && state == HARDOPEN && (rubble || !ateopr())) { 123 rubble = 1; 124 gcursor = cursor; 125 i = *gcursor; 126 *gcursor = ' '; 127 wcursor = gcursor; 128 vmove(); 129 *gcursor = i; 130 } 131 vaifirst = indent == 0; 132 133 /* 134 * Handle replace character by (eventually) 135 * limiting the number of input characters allowed 136 * in the vgetline routine. 137 */ 138 if (ch == 'r') 139 repcnt = 2; 140 else 141 repcnt = 0; 142 143 /* 144 * If an autoindent is specified, then 145 * generate a mixture of blanks to tabs to implement 146 * it and place the cursor after the indent. 147 * Text read by the vgetline routine will be placed in genbuf, 148 * so the indent is generated there. 149 */ 150 if (value(AUTOINDENT) && indent != 0) { 151 gcursor = genindent(indent); 152 *gcursor = 0; 153 vgotoCL(qcolumn(cursor - 1, genbuf)); 154 } else { 155 gcursor = genbuf; 156 *gcursor = 0; 157 if (ch == 'o') 158 vfixcurs(); 159 } 160 161 /* 162 * Prepare for undo. Pointers delimit inserted portion of line. 163 */ 164 vUA1 = vUA2 = cursor; 165 166 /* 167 * If we are not in a repeated command and a ^@ comes in 168 * then this means the previous inserted text. 169 * If there is none or it was too long to be saved, 170 * then beep() and also arrange to undo any damage done 171 * so far (e.g. if we are a change.) 172 */ 173 if ((vglobp && *vglobp == 0) || peekbr()) { 174 if ((INS[0] & (QUOTE|TRIM)) == OVERBUF) { 175 beep(); 176 if (!splitw) 177 ungetkey('u'); 178 doomed = 0; 179 hold = oldhold; 180 return; 181 } 182 /* 183 * Unread input from INS. 184 * An escape will be generated at end of string. 185 * Hold off n^^2 type update on dumb terminals. 186 */ 187 vglobp = INS; 188 hold |= HOLDQIK; 189 } else if (vglobp == 0) 190 /* 191 * Not a repeated command, get 192 * a new inserted text for repeat. 193 */ 194 INS[0] = 0; 195 196 /* 197 * For wrapmargin to hack away second space after a '.' 198 * when the first space caused a line break we keep 199 * track that this happened in gobblebl, which says 200 * to gobble up a blank silently. 201 */ 202 gobblebl = 0; 203 204 /* 205 * Text gathering loop. 206 * New text goes into genbuf starting at gcursor. 207 * cursor preserves place in linebuf where text will eventually go. 208 */ 209 if (*cursor == 0 || state == CRTOPEN) 210 hold |= HOLDROL; 211 for (;;) { 212 if (ch == 'r' && repcnt == 0) 213 escape = 0; 214 else { 215 gcursor = vgetline(repcnt, gcursor, &escape); 216 217 /* 218 * After an append, stick information 219 * about the ^D's and ^^D's and 0^D's in 220 * the repeated text buffer so repeated 221 * inserts of stuff indented with ^D as backtab's 222 * can work. 223 */ 224 if (HADUP) 225 addtext("^"); 226 else if (HADZERO) 227 addtext("0"); 228 while (CDCNT > 0) 229 addtext("\204"), CDCNT--; 230 if (gobbled) 231 addtext(" "); 232 addtext(ogcursor); 233 } 234 repcnt = 0; 235 236 /* 237 * Smash the generated and preexisting indents together 238 * and generate one cleanly made out of tabs and spaces 239 * if we are using autoindent. 240 */ 241 if (!vaifirst && value(AUTOINDENT)) { 242 i = fixindent(indent); 243 if (!HADUP) 244 indent = i; 245 gcursor = strend(genbuf); 246 } 247 248 /* 249 * Limit the repetition count based on maximum 250 * possible line length; do output implied 251 * by further count (> 1) and cons up the new line 252 * in linebuf. 253 */ 254 cnt = vmaxrep(ch, cnt); 255 CP(gcursor + 1, cursor); 256 do { 257 CP(cursor, genbuf); 258 if (cnt > 1) { 259 int oldhold = hold; 260 261 Outchar = vinschar; 262 hold |= HOLDQIK; 263 printf("%s", genbuf); 264 hold = oldhold; 265 Outchar = vputchar; 266 } 267 cursor += gcursor - genbuf; 268 } while (--cnt > 0); 269 endim(); 270 vUA2 = cursor; 271 if (escape != '\n') 272 CP(cursor, gcursor + 1); 273 274 /* 275 * If doomed characters remain, clobber them, 276 * and reopen the line to get the display exact. 277 */ 278 if (state != HARDOPEN) { 279 DEPTH(vcline) = 0; 280 if (doomed > 0) { 281 register int cind = cindent(); 282 283 physdc(cind, cind + doomed); 284 doomed = 0; 285 } 286 i = vreopen(LINE(vcline), lineDOT(), vcline); 287 } 288 289 /* 290 * All done unless we are continuing on to another line. 291 */ 292 if (escape != '\n') 293 break; 294 295 /* 296 * Set up for the new line. 297 * First save the current line, then construct a new 298 * first image for the continuation line consisting 299 * of any new autoindent plus the pushed ahead text. 300 */ 301 killU(); 302 addtext(gobblebl ? " " : "\n"); 303 vsave(); 304 cnt = 1; 305 if (value(AUTOINDENT)) { 306 #ifdef LISPCODE 307 if (value(LISP)) 308 indent = lindent(dot + 1); 309 else 310 #endif 311 if (!HADUP && vaifirst) 312 indent = whitecnt(linebuf); 313 vaifirst = 0; 314 strcLIN(vpastwh(gcursor + 1)); 315 gcursor = genindent(indent); 316 *gcursor = 0; 317 if (gcursor + strlen(linebuf) > &genbuf[LBSIZE - 2]) 318 gcursor = genbuf; 319 CP(gcursor, linebuf); 320 } else { 321 CP(genbuf, gcursor + 1); 322 gcursor = genbuf; 323 } 324 325 /* 326 * If we started out as a single line operation and are now 327 * turning into a multi-line change, then we had better yank 328 * out dot before it changes so that undo will work 329 * correctly later. 330 */ 331 if (FIXUNDO && vundkind == VCHNG) { 332 vremote(1, yank, 0); 333 undap1--; 334 } 335 336 /* 337 * Now do the append of the new line in the buffer, 338 * and update the display. If slowopen 339 * we don't do very much. 340 */ 341 vdoappend(genbuf); 342 vundkind = VMANYINS; 343 vcline++; 344 if (state != VISUAL) 345 vshow(dot, NOLINE); 346 else { 347 i += LINE(vcline - 1); 348 vopen(dot, i); 349 if (value(SLOWOPEN)) 350 vscrap(); 351 else 352 vsync1(LINE(vcline)); 353 } 354 strcLIN(gcursor); 355 *gcursor = 0; 356 cursor = linebuf; 357 vgotoCL(qcolumn(cursor - 1, genbuf)); 358 } 359 360 /* 361 * All done with insertion, position the cursor 362 * and sync the screen. 363 */ 364 hold = oldhold; 365 if (cursor > linebuf) 366 cursor--; 367 if (state != HARDOPEN) 368 vsyncCL(); 369 else if (cursor > linebuf) 370 back1(); 371 doomed = 0; 372 wcursor = cursor; 373 vmove(); 374 } 375 376 /* 377 * Subroutine for vgetline to back up a single character position, 378 * backwards around end of lines (vgoto can't hack columns which are 379 * less than 0 in general). 380 */ 381 back1() 382 { 383 384 vgoto(destline - 1, WCOLS + destcol - 1); 385 } 386 387 /* 388 * Get a line into genbuf after gcursor. 389 * Cnt limits the number of input characters 390 * accepted and is used for handling the replace 391 * single character command. Aescaped is the location 392 * where we stick a termination indicator (whether we 393 * ended with an ESCAPE or a newline/return. 394 * 395 * We do erase-kill type processing here and also 396 * are careful about the way we do this so that it is 397 * repeatable. (I.e. so that your kill doesn't happen, 398 * when you repeat an insert if it was escaped with \ the 399 * first time you did it. 400 */ 401 char * 402 vgetline(cnt, gcursor, aescaped) 403 int cnt; 404 register char *gcursor; 405 bool *aescaped; 406 { 407 register int c, ch; 408 register char *cp; 409 int x, y, iwhite; 410 char *iglobp; 411 char cstr[2]; 412 int (*OO)() = Outchar; 413 414 /* 415 * Clear the output state and counters 416 * for autoindent backwards motion (counts of ^D, etc.) 417 * Remember how much white space at beginning of line so 418 * as not to allow backspace over autoindent. 419 */ 420 *aescaped = 0; 421 ogcursor = gcursor; 422 flusho(); 423 CDCNT = 0; 424 HADUP = 0; 425 HADZERO = 0; 426 gobbled = 0; 427 iwhite = whitecnt(genbuf); 428 iglobp = vglobp; 429 430 /* 431 * Carefully avoid using vinschar in the echo area. 432 */ 433 if (splitw) 434 Outchar = vputchar; 435 else { 436 Outchar = vinschar; 437 vprepins(); 438 } 439 for (;;) { 440 if (gobblebl) 441 gobblebl--; 442 if (cnt != 0) { 443 cnt--; 444 if (cnt == 0) 445 goto vadone; 446 } 447 c = getkey(); 448 if (c != ATTN) 449 c &= (QUOTE|TRIM); 450 ch = c; 451 maphopcnt = 0; 452 if (vglobp == 0 && Peekkey == 0) 453 while ((ch = map(c, immacs)) != c) { 454 c = ch; 455 if (!value(REMAP)) 456 break; 457 if (++maphopcnt > 256) 458 error("Infinite macro loop"); 459 } 460 if (!iglobp) { 461 462 /* 463 * Erase-kill type processing. 464 * Only happens if we were not reading 465 * from untyped input when we started. 466 * Map users erase to ^H, kill to -1 for switch. 467 */ 468 #ifndef USG3TTY 469 if (c == tty.sg_erase) 470 c = CTRL(h); 471 else if (c == tty.sg_kill) 472 c = -1; 473 #else 474 if (c == tty.c_cc[VERASE]) 475 c = CTRL(h); 476 else if (c == tty.c_cc[VKILL]) 477 c = -1; 478 #endif 479 switch (c) { 480 481 /* 482 * ^? Interrupt drops you back to visual 483 * command mode with an unread interrupt 484 * still in the input buffer. 485 * 486 * ^\ Quit does the same as interrupt. 487 * If you are a ex command rather than 488 * a vi command this will drop you 489 * back to command mode for sure. 490 */ 491 case ATTN: 492 case QUIT: 493 ungetkey(c); 494 goto vadone; 495 496 /* 497 * ^H Backs up a character in the input. 498 * 499 * BUG: Can't back around line boundaries. 500 * This is hard because stuff has 501 * already been saved for repeat. 502 */ 503 case CTRL(h): 504 bakchar: 505 cp = gcursor - 1; 506 if (cp < ogcursor) { 507 if (splitw) { 508 /* 509 * Backspacing over readecho 510 * prompt. Pretend delete but 511 * don't beep. 512 */ 513 ungetkey(c); 514 goto vadone; 515 } 516 beep(); 517 continue; 518 } 519 goto vbackup; 520 521 /* 522 * ^W Back up a white/non-white word. 523 */ 524 case CTRL(w): 525 wdkind = 1; 526 for (cp = gcursor; cp > ogcursor && isspace(cp[-1]); cp--) 527 continue; 528 for (c = wordch(cp - 1); 529 cp > ogcursor && wordof(c, cp - 1); cp--) 530 continue; 531 goto vbackup; 532 533 /* 534 * users kill Kill input on this line, back to 535 * the autoindent. 536 */ 537 case -1: 538 cp = ogcursor; 539 vbackup: 540 if (cp == gcursor) { 541 beep(); 542 continue; 543 } 544 endim(); 545 *cp = 0; 546 c = cindent(); 547 vgotoCL(qcolumn(cursor - 1, genbuf)); 548 if (doomed >= 0) 549 doomed += c - cindent(); 550 gcursor = cp; 551 continue; 552 553 /* 554 * \ Followed by erase or kill 555 * maps to just the erase or kill. 556 */ 557 case '\\': 558 x = destcol, y = destline; 559 putchar('\\'); 560 vcsync(); 561 c = getkey(); 562 #ifndef USG3TTY 563 if (c == tty.sg_erase || c == tty.sg_kill) { 564 #else 565 if (c == tty.c_cc[VERASE] 566 || c == tty.c_cc[VKILL]) { 567 #endif 568 vgoto(y, x); 569 if (doomed >= 0) 570 doomed++; 571 goto def; 572 } 573 ungetkey(c), c = '\\'; 574 goto noput; 575 576 /* 577 * ^Q Super quote following character 578 * Only ^@ is verboten (trapped at 579 * a lower level) and \n forces a line 580 * split so doesn't really go in. 581 * 582 * ^V Synonym for ^Q 583 */ 584 case CTRL(q): 585 case CTRL(v): 586 x = destcol, y = destline; 587 putchar('^'); 588 vgoto(y, x); 589 c = getkey(); 590 #ifdef TIOCSETC 591 if (c == ATTN) 592 c = nttyc.t_intrc; 593 #endif 594 if (c != NL) { 595 if (doomed >= 0) 596 doomed++; 597 goto def; 598 } 599 break; 600 } 601 } 602 603 /* 604 * If we get a blank not in the echo area 605 * consider splitting the window in the wrapmargin. 606 */ 607 if (c != NL && !splitw) { 608 if (c == ' ' && gobblebl) { 609 gobbled = 1; 610 continue; 611 } 612 if (/* c <= ' ' && */ value(WRAPMARGIN) && 613 outcol >= OCOLUMNS - value(WRAPMARGIN)) { 614 /* 615 * At end of word and hit wrapmargin. 616 * Move the word to next line and keep going. 617 */ 618 wdkind = 1; 619 *gcursor++ = c; 620 *gcursor = 0; 621 /* 622 * Find end of previous word if we are past it. 623 */ 624 for (cp=gcursor; cp>ogcursor && isspace(cp[-1]); cp--) 625 ; 626 if (outcol - (gcursor-cp) >= OCOLUMNS - value(WRAPMARGIN)) { 627 /* 628 * Find beginning of previous word. 629 */ 630 for (; cp>ogcursor && !isspace(cp[-1]); cp--) 631 ; 632 if (cp <= ogcursor) { 633 /* 634 * There is a single word that 635 * is too long to fit. Just 636 * let it pass, but beep for 637 * each new letter to warn 638 * the luser. 639 */ 640 c = *--gcursor; 641 *gcursor = 0; 642 beep(); 643 goto dontbreak; 644 } 645 /* 646 * Save it for next line. 647 */ 648 macpush(cp, 0); 649 cp--; 650 } 651 macpush("\n", 0); 652 /* 653 * Erase white space before the word. 654 */ 655 while (cp > ogcursor && isspace(cp[-1])) 656 cp--; /* skip blank */ 657 gobblebl = 3; 658 goto vbackup; 659 } 660 dontbreak:; 661 } 662 663 /* 664 * Word abbreviation mode. 665 */ 666 cstr[0] = c; 667 if (anyabbrs && gcursor > ogcursor && !wordch(cstr) && wordch(gcursor-1)) { 668 int wdtype, abno; 669 670 cstr[1] = 0; 671 wdkind = 1; 672 cp = gcursor - 1; 673 for (wdtype = wordch(cp - 1); 674 cp > ogcursor && wordof(wdtype, cp - 1); cp--) 675 ; 676 *gcursor = 0; 677 for (abno=0; abbrevs[abno].mapto; abno++) { 678 if (eq(cp, abbrevs[abno].cap)) { 679 macpush(cstr, 0); 680 macpush(abbrevs[abno].mapto); 681 goto vbackup; 682 } 683 } 684 } 685 686 switch (c) { 687 688 /* 689 * ^M Except in repeat maps to \n. 690 */ 691 case CR: 692 if (vglobp) 693 goto def; 694 c = '\n'; 695 /* presto chango ... */ 696 697 /* 698 * \n Start new line. 699 */ 700 case NL: 701 *aescaped = c; 702 goto vadone; 703 704 /* 705 * escape End insert unless repeat and more to repeat. 706 */ 707 case ESCAPE: 708 if (lastvgk) 709 goto def; 710 goto vadone; 711 712 /* 713 * ^D Backtab. 714 * ^T Software forward tab. 715 * 716 * Unless in repeat where this means these 717 * were superquoted in. 718 */ 719 case CTRL(d): 720 case CTRL(t): 721 if (vglobp) 722 goto def; 723 /* fall into ... */ 724 725 /* 726 * ^D|QUOTE Is a backtab (in a repeated command). 727 */ 728 case CTRL(d) | QUOTE: 729 *gcursor = 0; 730 cp = vpastwh(genbuf); 731 c = whitecnt(genbuf); 732 if (ch == CTRL(t)) { 733 /* 734 * ^t just generates new indent replacing 735 * current white space rounded up to soft 736 * tab stop increment. 737 */ 738 if (cp != gcursor) 739 /* 740 * BUG: Don't hack ^T except 741 * right after initial 742 * white space. 743 */ 744 continue; 745 cp = genindent(iwhite = backtab(c + value(SHIFTWIDTH) + 1)); 746 ogcursor = cp; 747 goto vbackup; 748 } 749 /* 750 * ^D works only if we are at the (end of) the 751 * generated autoindent. We count the ^D for repeat 752 * purposes. 753 */ 754 if (c == iwhite && c != 0) 755 if (cp == gcursor) { 756 iwhite = backtab(c); 757 CDCNT++; 758 ogcursor = cp = genindent(iwhite); 759 goto vbackup; 760 } else if (&cp[1] == gcursor && 761 (*cp == '^' || *cp == '0')) { 762 /* 763 * ^^D moves to margin, then back 764 * to current indent on next line. 765 * 766 * 0^D moves to margin and then 767 * stays there. 768 */ 769 HADZERO = *cp == '0'; 770 ogcursor = cp = genbuf; 771 HADUP = 1 - HADZERO; 772 CDCNT = 1; 773 endim(); 774 back1(); 775 vputchar(' '); 776 goto vbackup; 777 } 778 if (vglobp && vglobp - iglobp >= 2 && 779 (vglobp[-2] == '^' || vglobp[-2] == '0') 780 && gcursor == ogcursor + 1) 781 goto bakchar; 782 continue; 783 784 default: 785 /* 786 * Possibly discard control inputs. 787 */ 788 if (!vglobp && junk(c)) { 789 beep(); 790 continue; 791 } 792 def: 793 putchar(c); 794 flush(); 795 noput: 796 if (gcursor > &genbuf[LBSIZE - 2]) 797 error("Line too long"); 798 *gcursor++ = c & TRIM; 799 vcsync(); 800 if (value(SHOWMATCH) && !iglobp) 801 if (c == ')' || c == '}') 802 lsmatch(gcursor); 803 continue; 804 } 805 } 806 vadone: 807 *gcursor = 0; 808 if (Outchar != termchar) 809 Outchar = OO; 810 endim(); 811 return (gcursor); 812 } 813 814 int vgetsplit(); 815 char *vsplitpt; 816 817 /* 818 * Append the line in buffer at lp 819 * to the buffer after dot. 820 */ 821 vdoappend(lp) 822 char *lp; 823 { 824 register int oing = inglobal; 825 826 vsplitpt = lp; 827 inglobal = 1; 828 ignore(append(vgetsplit, dot)); 829 inglobal = oing; 830 } 831 832 /* 833 * Subroutine for vdoappend to pass to append. 834 */ 835 vgetsplit() 836 { 837 838 if (vsplitpt == 0) 839 return (EOF); 840 strcLIN(vsplitpt); 841 vsplitpt = 0; 842 return (0); 843 } 844 845 /* 846 * Vmaxrep determines the maximum repetitition factor 847 * allowed that will yield total line length less than 848 * LBSIZE characters and also does hacks for the R command. 849 */ 850 vmaxrep(ch, cnt) 851 char ch; 852 register int cnt; 853 { 854 register int len, replen; 855 856 if (cnt > LBSIZE - 2) 857 cnt = LBSIZE - 2; 858 replen = strlen(genbuf); 859 if (ch == 'R') { 860 len = strlen(cursor); 861 if (replen < len) 862 len = replen; 863 CP(cursor, cursor + len); 864 vUD2 += len; 865 } 866 len = strlen(linebuf); 867 if (len + cnt * replen <= LBSIZE - 2) 868 return (cnt); 869 cnt = (LBSIZE - 2 - len) / replen; 870 if (cnt == 0) { 871 vsave(); 872 error("Line too long"); 873 } 874 return (cnt); 875 } 876