1 /* Copyright (c) 1980 Regents of the University of California */ 2 static char *sccsid = "@(#)ex_vops2.c 6.2 10/23/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, savedoomed; 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, ch); 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 savedoomed = doomed; 281 if (doomed > 0) { 282 register int cind = cindent(); 283 284 physdc(cind, cind + doomed); 285 doomed = 0; 286 } 287 i = vreopen(LINE(vcline), lineDOT(), vcline); 288 #ifdef TRACE 289 if (trace) 290 fprintf(trace, "restoring doomed from %d to %d\n", doomed, savedoomed); 291 #endif 292 if (ch == 'R') 293 doomed = savedoomed; 294 } 295 296 /* 297 * All done unless we are continuing on to another line. 298 */ 299 if (escape != '\n') 300 break; 301 302 /* 303 * Set up for the new line. 304 * First save the current line, then construct a new 305 * first image for the continuation line consisting 306 * of any new autoindent plus the pushed ahead text. 307 */ 308 killU(); 309 addtext(gobblebl ? " " : "\n"); 310 vsave(); 311 cnt = 1; 312 if (value(AUTOINDENT)) { 313 #ifdef LISPCODE 314 if (value(LISP)) 315 indent = lindent(dot + 1); 316 else 317 #endif 318 if (!HADUP && vaifirst) 319 indent = whitecnt(linebuf); 320 vaifirst = 0; 321 strcLIN(vpastwh(gcursor + 1)); 322 gcursor = genindent(indent); 323 *gcursor = 0; 324 if (gcursor + strlen(linebuf) > &genbuf[LBSIZE - 2]) 325 gcursor = genbuf; 326 CP(gcursor, linebuf); 327 } else { 328 CP(genbuf, gcursor + 1); 329 gcursor = genbuf; 330 } 331 332 /* 333 * If we started out as a single line operation and are now 334 * turning into a multi-line change, then we had better yank 335 * out dot before it changes so that undo will work 336 * correctly later. 337 */ 338 if (FIXUNDO && vundkind == VCHNG) { 339 vremote(1, yank, 0); 340 undap1--; 341 } 342 343 /* 344 * Now do the append of the new line in the buffer, 345 * and update the display. If slowopen 346 * we don't do very much. 347 */ 348 vdoappend(genbuf); 349 vundkind = VMANYINS; 350 vcline++; 351 if (state != VISUAL) 352 vshow(dot, NOLINE); 353 else { 354 i += LINE(vcline - 1); 355 vopen(dot, i); 356 if (value(SLOWOPEN)) 357 vscrap(); 358 else 359 vsync1(LINE(vcline)); 360 } 361 strcLIN(gcursor); 362 *gcursor = 0; 363 cursor = linebuf; 364 vgotoCL(qcolumn(cursor - 1, genbuf)); 365 } 366 367 /* 368 * All done with insertion, position the cursor 369 * and sync the screen. 370 */ 371 hold = oldhold; 372 if (cursor > linebuf) 373 cursor--; 374 if (state != HARDOPEN) 375 vsyncCL(); 376 else if (cursor > linebuf) 377 back1(); 378 doomed = 0; 379 wcursor = cursor; 380 vmove(); 381 } 382 383 /* 384 * Subroutine for vgetline to back up a single character position, 385 * backwards around end of lines (vgoto can't hack columns which are 386 * less than 0 in general). 387 */ 388 back1() 389 { 390 391 vgoto(destline - 1, WCOLS + destcol - 1); 392 } 393 394 /* 395 * Get a line into genbuf after gcursor. 396 * Cnt limits the number of input characters 397 * accepted and is used for handling the replace 398 * single character command. Aescaped is the location 399 * where we stick a termination indicator (whether we 400 * ended with an ESCAPE or a newline/return. 401 * 402 * We do erase-kill type processing here and also 403 * are careful about the way we do this so that it is 404 * repeatable. (I.e. so that your kill doesn't happen, 405 * when you repeat an insert if it was escaped with \ the 406 * first time you did it. commch is the command character 407 * involved, including the prompt for readline. 408 */ 409 char * 410 vgetline(cnt, gcursor, aescaped, commch) 411 int cnt; 412 register char *gcursor; 413 bool *aescaped; 414 char commch; 415 { 416 register int c, ch; 417 register char *cp; 418 int x, y, iwhite, backsl=0; 419 char *iglobp; 420 char cstr[2]; 421 int (*OO)() = Outchar; 422 423 /* 424 * Clear the output state and counters 425 * for autoindent backwards motion (counts of ^D, etc.) 426 * Remember how much white space at beginning of line so 427 * as not to allow backspace over autoindent. 428 */ 429 *aescaped = 0; 430 ogcursor = gcursor; 431 flusho(); 432 CDCNT = 0; 433 HADUP = 0; 434 HADZERO = 0; 435 gobbled = 0; 436 iwhite = whitecnt(genbuf); 437 iglobp = vglobp; 438 439 /* 440 * Carefully avoid using vinschar in the echo area. 441 */ 442 if (splitw) 443 Outchar = vputchar; 444 else { 445 Outchar = vinschar; 446 vprepins(); 447 } 448 for (;;) { 449 backsl = 0; 450 if (gobblebl) 451 gobblebl--; 452 if (cnt != 0) { 453 cnt--; 454 if (cnt == 0) 455 goto vadone; 456 } 457 c = getkey(); 458 if (c != ATTN) 459 c &= (QUOTE|TRIM); 460 ch = c; 461 maphopcnt = 0; 462 if (vglobp == 0 && Peekkey == 0 && commch != 'r') 463 while ((ch = map(c, immacs)) != c) { 464 c = ch; 465 if (!value(REMAP)) 466 break; 467 if (++maphopcnt > 256) 468 error("Infinite macro loop"); 469 } 470 if (!iglobp) { 471 472 /* 473 * Erase-kill type processing. 474 * Only happens if we were not reading 475 * from untyped input when we started. 476 * Map users erase to ^H, kill to -1 for switch. 477 */ 478 #ifndef USG3TTY 479 if (c == tty.sg_erase) 480 c = CTRL(h); 481 else if (c == tty.sg_kill) 482 c = -1; 483 #else 484 if (c == tty.c_cc[VERASE]) 485 c = CTRL(h); 486 else if (c == tty.c_cc[VKILL]) 487 c = -1; 488 #endif 489 switch (c) { 490 491 /* 492 * ^? Interrupt drops you back to visual 493 * command mode with an unread interrupt 494 * still in the input buffer. 495 * 496 * ^\ Quit does the same as interrupt. 497 * If you are a ex command rather than 498 * a vi command this will drop you 499 * back to command mode for sure. 500 */ 501 case ATTN: 502 case QUIT: 503 ungetkey(c); 504 goto vadone; 505 506 /* 507 * ^H Backs up a character in the input. 508 * 509 * BUG: Can't back around line boundaries. 510 * This is hard because stuff has 511 * already been saved for repeat. 512 */ 513 case CTRL(h): 514 bakchar: 515 cp = gcursor - 1; 516 if (cp < ogcursor) { 517 if (splitw) { 518 /* 519 * Backspacing over readecho 520 * prompt. Pretend delete but 521 * don't beep. 522 */ 523 ungetkey(c); 524 goto vadone; 525 } 526 beep(); 527 continue; 528 } 529 goto vbackup; 530 531 /* 532 * ^W Back up a white/non-white word. 533 */ 534 case CTRL(w): 535 wdkind = 1; 536 for (cp = gcursor; cp > ogcursor && isspace(cp[-1]); cp--) 537 continue; 538 for (c = wordch(cp - 1); 539 cp > ogcursor && wordof(c, cp - 1); cp--) 540 continue; 541 goto vbackup; 542 543 /* 544 * users kill Kill input on this line, back to 545 * the autoindent. 546 */ 547 case -1: 548 cp = ogcursor; 549 vbackup: 550 if (cp == gcursor) { 551 beep(); 552 continue; 553 } 554 endim(); 555 *cp = 0; 556 c = cindent(); 557 vgotoCL(qcolumn(cursor - 1, genbuf)); 558 if (doomed >= 0) 559 doomed += c - cindent(); 560 gcursor = cp; 561 continue; 562 563 /* 564 * \ Followed by erase or kill 565 * maps to just the erase or kill. 566 */ 567 case '\\': 568 x = destcol, y = destline; 569 putchar('\\'); 570 vcsync(); 571 c = getkey(); 572 #ifndef USG3TTY 573 if (c == tty.sg_erase || c == tty.sg_kill) 574 #else 575 if (c == tty.c_cc[VERASE] 576 || c == tty.c_cc[VKILL]) 577 #endif 578 { 579 vgoto(y, x); 580 if (doomed >= 0) 581 doomed++; 582 goto def; 583 } 584 ungetkey(c), c = '\\'; 585 backsl = 1; 586 break; 587 588 /* 589 * ^Q Super quote following character 590 * Only ^@ is verboten (trapped at 591 * a lower level) and \n forces a line 592 * split so doesn't really go in. 593 * 594 * ^V Synonym for ^Q 595 */ 596 case CTRL(q): 597 case CTRL(v): 598 x = destcol, y = destline; 599 putchar('^'); 600 vgoto(y, x); 601 c = getkey(); 602 #ifdef TIOCSETC 603 if (c == ATTN) 604 c = nttyc.t_intrc; 605 #endif 606 if (c != NL) { 607 if (doomed >= 0) 608 doomed++; 609 goto def; 610 } 611 break; 612 } 613 } 614 615 /* 616 * If we get a blank not in the echo area 617 * consider splitting the window in the wrapmargin. 618 */ 619 if (c != NL && !splitw) { 620 if (c == ' ' && gobblebl) { 621 gobbled = 1; 622 continue; 623 } 624 if (value(WRAPMARGIN) && 625 (outcol >= OCOLUMNS - value(WRAPMARGIN) || 626 backsl && outcol==0) && 627 commch != 'r') { 628 /* 629 * At end of word and hit wrapmargin. 630 * Move the word to next line and keep going. 631 */ 632 wdkind = 1; 633 *gcursor++ = c; 634 if (backsl) 635 *gcursor++ = getkey(); 636 *gcursor = 0; 637 /* 638 * Find end of previous word if we are past it. 639 */ 640 for (cp=gcursor; cp>ogcursor && isspace(cp[-1]); cp--) 641 ; 642 if (outcol+(backsl?OCOLUMNS:0) - (gcursor-cp) >= OCOLUMNS - value(WRAPMARGIN)) { 643 /* 644 * Find beginning of previous word. 645 */ 646 for (; cp>ogcursor && !isspace(cp[-1]); cp--) 647 ; 648 if (cp <= ogcursor) { 649 /* 650 * There is a single word that 651 * is too long to fit. Just 652 * let it pass, but beep for 653 * each new letter to warn 654 * the luser. 655 */ 656 c = *--gcursor; 657 *gcursor = 0; 658 beep(); 659 goto dontbreak; 660 } 661 /* 662 * Save it for next line. 663 */ 664 macpush(cp, 0); 665 cp--; 666 } 667 macpush("\n", 0); 668 /* 669 * Erase white space before the word. 670 */ 671 while (cp > ogcursor && isspace(cp[-1])) 672 cp--; /* skip blank */ 673 gobblebl = 3; 674 goto vbackup; 675 } 676 dontbreak:; 677 } 678 679 /* 680 * Word abbreviation mode. 681 */ 682 cstr[0] = c; 683 if (anyabbrs && gcursor > ogcursor && !wordch(cstr) && wordch(gcursor-1)) { 684 int wdtype, abno; 685 686 cstr[1] = 0; 687 wdkind = 1; 688 cp = gcursor - 1; 689 for (wdtype = wordch(cp - 1); 690 cp > ogcursor && wordof(wdtype, cp - 1); cp--) 691 ; 692 *gcursor = 0; 693 for (abno=0; abbrevs[abno].mapto; abno++) { 694 if (eq(cp, abbrevs[abno].cap)) { 695 macpush(cstr, 0); 696 macpush(abbrevs[abno].mapto); 697 goto vbackup; 698 } 699 } 700 } 701 702 switch (c) { 703 704 /* 705 * ^M Except in repeat maps to \n. 706 */ 707 case CR: 708 if (vglobp) 709 goto def; 710 c = '\n'; 711 /* presto chango ... */ 712 713 /* 714 * \n Start new line. 715 */ 716 case NL: 717 *aescaped = c; 718 goto vadone; 719 720 /* 721 * escape End insert unless repeat and more to repeat. 722 */ 723 case ESCAPE: 724 if (lastvgk) 725 goto def; 726 goto vadone; 727 728 /* 729 * ^D Backtab. 730 * ^T Software forward tab. 731 * 732 * Unless in repeat where this means these 733 * were superquoted in. 734 */ 735 case CTRL(d): 736 case CTRL(t): 737 if (vglobp) 738 goto def; 739 /* fall into ... */ 740 741 /* 742 * ^D|QUOTE Is a backtab (in a repeated command). 743 */ 744 case CTRL(d) | QUOTE: 745 *gcursor = 0; 746 cp = vpastwh(genbuf); 747 c = whitecnt(genbuf); 748 if (ch == CTRL(t)) { 749 /* 750 * ^t just generates new indent replacing 751 * current white space rounded up to soft 752 * tab stop increment. 753 */ 754 if (cp != gcursor) 755 /* 756 * BUG: Don't hack ^T except 757 * right after initial 758 * white space. 759 */ 760 continue; 761 cp = genindent(iwhite = backtab(c + value(SHIFTWIDTH) + 1)); 762 ogcursor = cp; 763 goto vbackup; 764 } 765 /* 766 * ^D works only if we are at the (end of) the 767 * generated autoindent. We count the ^D for repeat 768 * purposes. 769 */ 770 if (c == iwhite && c != 0) 771 if (cp == gcursor) { 772 iwhite = backtab(c); 773 CDCNT++; 774 ogcursor = cp = genindent(iwhite); 775 goto vbackup; 776 } else if (&cp[1] == gcursor && 777 (*cp == '^' || *cp == '0')) { 778 /* 779 * ^^D moves to margin, then back 780 * to current indent on next line. 781 * 782 * 0^D moves to margin and then 783 * stays there. 784 */ 785 HADZERO = *cp == '0'; 786 ogcursor = cp = genbuf; 787 HADUP = 1 - HADZERO; 788 CDCNT = 1; 789 endim(); 790 back1(); 791 vputchar(' '); 792 goto vbackup; 793 } 794 if (vglobp && vglobp - iglobp >= 2 && 795 (vglobp[-2] == '^' || vglobp[-2] == '0') 796 && gcursor == ogcursor + 1) 797 goto bakchar; 798 continue; 799 800 default: 801 /* 802 * Possibly discard control inputs. 803 */ 804 if (!vglobp && junk(c)) { 805 beep(); 806 continue; 807 } 808 def: 809 if (!backsl) { 810 putchar(c); 811 flush(); 812 } 813 if (gcursor > &genbuf[LBSIZE - 2]) 814 error("Line too long"); 815 *gcursor++ = c & TRIM; 816 vcsync(); 817 if (value(SHOWMATCH) && !iglobp) 818 if (c == ')' || c == '}') 819 lsmatch(gcursor); 820 continue; 821 } 822 } 823 vadone: 824 *gcursor = 0; 825 if (Outchar != termchar) 826 Outchar = OO; 827 endim(); 828 return (gcursor); 829 } 830 831 int vgetsplit(); 832 char *vsplitpt; 833 834 /* 835 * Append the line in buffer at lp 836 * to the buffer after dot. 837 */ 838 vdoappend(lp) 839 char *lp; 840 { 841 register int oing = inglobal; 842 843 vsplitpt = lp; 844 inglobal = 1; 845 ignore(append(vgetsplit, dot)); 846 inglobal = oing; 847 } 848 849 /* 850 * Subroutine for vdoappend to pass to append. 851 */ 852 vgetsplit() 853 { 854 855 if (vsplitpt == 0) 856 return (EOF); 857 strcLIN(vsplitpt); 858 vsplitpt = 0; 859 return (0); 860 } 861 862 /* 863 * Vmaxrep determines the maximum repetitition factor 864 * allowed that will yield total line length less than 865 * LBSIZE characters and also does hacks for the R command. 866 */ 867 vmaxrep(ch, cnt) 868 char ch; 869 register int cnt; 870 { 871 register int len, replen; 872 873 if (cnt > LBSIZE - 2) 874 cnt = LBSIZE - 2; 875 replen = strlen(genbuf); 876 if (ch == 'R') { 877 len = strlen(cursor); 878 if (replen < len) 879 len = replen; 880 CP(cursor, cursor + len); 881 vUD2 += len; 882 } 883 len = strlen(linebuf); 884 if (len + cnt * replen <= LBSIZE - 2) 885 return (cnt); 886 cnt = (LBSIZE - 2 - len) / replen; 887 if (cnt == 0) { 888 vsave(); 889 error("Line too long"); 890 } 891 return (cnt); 892 } 893