1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 31 /* Copyright (c) 1981 Regents of the University of California */ 32 33 #pragma ident "%Z%%M% %I% %E% SMI" 34 35 #include "ex.h" 36 #include "ex_tty.h" 37 #include "ex_vis.h" 38 #ifndef PRESUNEUC 39 #include <wctype.h> 40 /* Undef putchar/getchar if they're defined. */ 41 #ifdef putchar 42 # undef putchar 43 #endif 44 #ifdef getchar 45 # undef getchar 46 #endif 47 #endif /* PRESUNEUC */ 48 49 extern size_t strlcpy(char *, const char *, size_t); 50 51 /* 52 * Low level routines for operations sequences, 53 * and mostly, insert mode (and a subroutine 54 * to read an input line, including in the echo area.) 55 */ 56 extern unsigned char *vUA1, *vUA2; /* extern; also in ex_vops.c */ 57 extern unsigned char *vUD1, *vUD2; /* extern; also in ex_vops.c */ 58 59 int vmaxrep(unsigned char, int); 60 static void imultlinerep(int, line *, int, int); 61 static void omultlinerep(int, line *, int); 62 static void fixdisplay(void); 63 64 /* 65 * Obleeperate characters in hardcopy 66 * open with \'s. 67 */ 68 void 69 bleep(int i, unsigned char *cp) 70 { 71 72 i -= lcolumn(nextchr(cp)); 73 do 74 putchar('\\' | QUOTE); 75 while (--i >= 0); 76 rubble = 1; 77 } 78 79 /* 80 * Common code for middle part of delete 81 * and change operating on parts of lines. 82 */ 83 int 84 vdcMID(void) 85 { 86 unsigned char *cp; 87 88 squish(); 89 setLAST(); 90 if (FIXUNDO) 91 vundkind = VCHNG, CP(vutmp, linebuf); 92 if (wcursor < cursor) 93 cp = wcursor, wcursor = cursor, cursor = cp; 94 vUD1 = vUA1 = vUA2 = cursor; vUD2 = wcursor; 95 return (lcolumn(wcursor)); 96 } 97 98 /* 99 * Take text from linebuf and stick it 100 * in the VBSIZE buffer BUF. Used to save 101 * deleted text of part of line. 102 */ 103 void 104 takeout(unsigned char *BUF) 105 { 106 unsigned char *cp; 107 108 if (wcursor < linebuf) 109 wcursor = linebuf; 110 if (cursor == wcursor) { 111 (void) beep(); 112 return; 113 } 114 if (wcursor < cursor) { 115 cp = wcursor; 116 wcursor = cursor; 117 cursor = cp; 118 } 119 setBUF(BUF); 120 if ((unsigned char)BUF[128] == 0200) 121 (void) beep(); 122 } 123 124 /* 125 * Are we at the end of the printed representation of the 126 * line? Used internally in hardcopy open. 127 */ 128 int 129 ateopr(void) 130 { 131 wchar_t i, c; 132 wchar_t *cp = vtube[destline] + destcol; 133 134 for (i = WCOLS - destcol; i > 0; i--) { 135 c = *cp++; 136 if (c == 0) { 137 /* 138 * Optimization to consider returning early, saving 139 * CPU time. We have to make a special check that 140 * we aren't missing a mode indicator. 141 */ 142 if (destline == WECHO && destcol < WCOLS-11 && vtube[WECHO][WCOLS-20]) 143 return 0; 144 return (1); 145 } 146 if (c != ' ' && (c & QUOTE) == 0) 147 return (0); 148 } 149 return (1); 150 } 151 152 /* 153 * Append. 154 * 155 * This routine handles the top level append, doing work 156 * as each new line comes in, and arranging repeatability. 157 * It also handles append with repeat counts, and calculation 158 * of autoindents for new lines. 159 */ 160 bool vaifirst; 161 bool gobbled; 162 unsigned char *ogcursor; 163 164 static int INSCDCNT; /* number of ^D's (backtabs) in insertion buffer */ 165 166 static int inscdcnt; /* 167 * count of ^D's (backtabs) not seen yet when doing 168 * repeat of insertion 169 */ 170 171 void 172 vappend(int ch, int cnt, int indent) 173 { 174 int i; 175 unsigned char *gcursor; 176 bool escape; 177 int repcnt, savedoomed; 178 short oldhold = hold; 179 int savecnt = cnt; 180 line *startsrcline; 181 int startsrccol, endsrccol; 182 int gotNL = 0; 183 int imultlinecnt = 0; 184 int omultlinecnt = 0; 185 186 if ((savecnt > 1) && (ch == 'o' || ch == 'O')) { 187 omultlinecnt = 1; 188 } 189 #ifdef XPG6 190 if ((savecnt > 1) && (ch == 'a' || ch == 'A' || ch == 'i' || ch == 'I')) 191 imultlinecnt = 1; 192 #endif /* XPG6 */ 193 194 /* 195 * Before a move in hardopen when the line is dirty 196 * or we are in the middle of the printed representation, 197 * we retype the line to the left of the cursor so the 198 * insert looks clean. 199 */ 200 201 if (ch != 'o' && state == HARDOPEN && (rubble || !ateopr())) { 202 rubble = 1; 203 gcursor = cursor; 204 i = *gcursor; 205 *gcursor = ' '; 206 wcursor = gcursor; 207 (void) vmove(); 208 *gcursor = i; 209 } 210 vaifirst = indent == 0; 211 212 /* 213 * Handle replace character by (eventually) 214 * limiting the number of input characters allowed 215 * in the vgetline routine. 216 */ 217 if (ch == 'r') 218 repcnt = 2; 219 else 220 repcnt = 0; 221 222 /* 223 * If an autoindent is specified, then 224 * generate a mixture of blanks to tabs to implement 225 * it and place the cursor after the indent. 226 * Text read by the vgetline routine will be placed in genbuf, 227 * so the indent is generated there. 228 */ 229 if (value(vi_AUTOINDENT) && indent != 0) { 230 unsigned char x; 231 gcursor = genindent(indent); 232 *gcursor = 0; 233 vgotoCL(nqcolumn(lastchr(linebuf, cursor), genbuf)); 234 } else { 235 gcursor = genbuf; 236 *gcursor = 0; 237 if (ch == 'o') 238 vfixcurs(); 239 } 240 241 /* 242 * Prepare for undo. Pointers delimit inserted portion of line. 243 */ 244 vUA1 = vUA2 = cursor; 245 246 /* 247 * If we are not in a repeated command and a ^@ comes in 248 * then this means the previous inserted text. 249 * If there is none or it was too long to be saved, 250 * then beep() and also arrange to undo any damage done 251 * so far (e.g. if we are a change.) 252 */ 253 switch (ch) { 254 case 'r': 255 break; 256 case 'a': 257 /* 258 * TRANSLATION_NOTE 259 * "A" is a terse mode message corresponding to 260 * "APPEND MODE". 261 * Translated message of "A" must be 1 character (not byte). 262 * Or, just leave it. 263 */ 264 if (value(vi_TERSE)) { 265 vshowmode(gettext("A")); 266 } else { 267 vshowmode(gettext("APPEND MODE")); 268 } 269 break; 270 case 's': 271 /* 272 * TRANSLATION_NOTE 273 * "S" is a terse mode message corresponding to 274 * "SUBSTITUTE MODE". 275 * Translated message of "S" must be 1 character (not byte). 276 * Or, just leave it. 277 */ 278 if (value(vi_TERSE)) { 279 vshowmode(gettext("S")); 280 } else { 281 vshowmode(gettext("SUBSTITUTE MODE")); 282 } 283 break; 284 case 'c': 285 /* 286 * TRANSLATION_NOTE 287 * "C" is a terse mode message corresponding to 288 * "CHANGE MODE". 289 * Translated message of "C" must be 1 character (not byte). 290 * Or, just leave it. 291 */ 292 if (value(vi_TERSE)) { 293 vshowmode(gettext("C")); 294 } else { 295 vshowmode(gettext("CHANGE MODE")); 296 } 297 break; 298 case 'R': 299 /* 300 * TRANSLATION_NOTE 301 * "R" is a terse mode message corresponding to 302 * "REPLACE MODE". 303 * Translated message of "R" must be 1 character (not byte). 304 * Or, just leave it. 305 */ 306 if (value(vi_TERSE)) { 307 vshowmode(gettext("R")); 308 } else { 309 vshowmode(gettext("REPLACE MODE")); 310 } 311 break; 312 case 'o': 313 /* 314 * TRANSLATION_NOTE 315 * "O" is a terse mode message corresponding to 316 * "OPEN MODE". 317 * Translated message of "O" must be 1 character (not byte). 318 * Or, just leave it. 319 */ 320 if (value(vi_TERSE)) { 321 vshowmode(gettext("O")); 322 } else { 323 vshowmode(gettext("OPEN MODE")); 324 } 325 break; 326 case 'i': 327 /* 328 * TRANSLATION_NOTE 329 * "I" is a terse mode message corresponding to 330 * "INSERT MODE" and the following "INPUT MODE". 331 * Translated message of "I" must be 1 character (not byte). 332 * Or, just leave it. 333 */ 334 if (value(vi_TERSE)) { 335 vshowmode(gettext("I")); 336 } else { 337 vshowmode(gettext("INSERT MODE")); 338 } 339 break; 340 default: 341 /* 342 * TRANSLATION_NOTE 343 * "I" is a terse mode message corresponding to 344 * "INPUT MODE" and the previous "INSERT MODE". 345 * Translated message of "I" must be 1 character (not byte). 346 * Or, just leave it. 347 */ 348 if (value(vi_TERSE)) { 349 vshowmode(gettext("I")); 350 } else { 351 vshowmode(gettext("INPUT MODE")); 352 } 353 } 354 ixlatctl(1); 355 if ((vglobp && *vglobp == 0) || peekbr()) { 356 if (INS[128] == 0200) { 357 (void) beep(); 358 if (!splitw) 359 ungetkey('u'); 360 doomed = 0; 361 hold = oldhold; 362 return; 363 } 364 /* 365 * Unread input from INS. 366 * An escape will be generated at end of string. 367 * Hold off n^^2 type update on dumb terminals. 368 */ 369 vglobp = INS; 370 inscdcnt = INSCDCNT; 371 hold |= HOLDQIK; 372 } else if (vglobp == 0) { 373 /* 374 * Not a repeated command, get 375 * a new inserted text for repeat. 376 */ 377 INS[0] = 0; 378 INS[128] = 0; 379 INSCDCNT = 0; 380 } 381 382 /* 383 * For wrapmargin to hack away second space after a '.' 384 * when the first space caused a line break we keep 385 * track that this happened in gobblebl, which says 386 * to gobble up a blank silently. 387 */ 388 gobblebl = 0; 389 390 startsrcline = dot; 391 startsrccol = cursor - linebuf; 392 393 /* 394 * Text gathering loop. 395 * New text goes into genbuf starting at gcursor. 396 * cursor preserves place in linebuf where text will eventually go. 397 */ 398 if (*cursor == 0 || state == CRTOPEN) 399 hold |= HOLDROL; 400 for (;;) { 401 if (ch == 'r' && repcnt == 0) 402 escape = 0; 403 else { 404 ixlatctl(1); 405 gcursor = vgetline(repcnt, gcursor, &escape, ch); 406 407 /* vgetline() only returns when it got ESCAPE or NL */ 408 if (escape == '\n') { 409 gotNL = 1; 410 } else { 411 /* 412 * Upon escape, gcursor is pointing to '\0' 413 * terminating the string in genbuf. 414 */ 415 endsrccol = gcursor - genbuf - 1; 416 } 417 ixlatctl(0); 418 419 /* 420 * After an append, stick information 421 * about the ^D's and ^^D's and 0^D's in 422 * the repeated text buffer so repeated 423 * inserts of stuff indented with ^D as backtab's 424 * can work. 425 */ 426 if (HADUP) 427 addtext("^"); 428 else if (HADZERO) 429 addtext("0"); 430 if(!vglobp) 431 INSCDCNT = CDCNT; 432 while (CDCNT > 0) { 433 addtext("\004"); 434 CDCNT--; 435 } 436 if (gobbled) 437 addtext(" "); 438 addtext(ogcursor); 439 } 440 repcnt = 0; 441 442 /* 443 * Smash the generated and preexisting indents together 444 * and generate one cleanly made out of tabs and spaces 445 * if we are using autoindent. 446 */ 447 if (!vaifirst && value(vi_AUTOINDENT)) { 448 i = fixindent(indent); 449 if (!HADUP) 450 indent = i; 451 gcursor = strend(genbuf); 452 } 453 454 /* 455 * Set cnt to 1 to avoid repeating the text on the same line. 456 * Do this for commands 'i', 'I', 'a', and 'A', if we're 457 * inserting anything with a newline for XPG6. Always do this 458 * for commands 'o' and 'O'. 459 */ 460 if ((imultlinecnt && gotNL) || omultlinecnt) { 461 cnt = 1; 462 } 463 464 /* 465 * Limit the repetition count based on maximum 466 * possible line length; do output implied 467 * by further count (> 1) and cons up the new line 468 * in linebuf. 469 */ 470 cnt = vmaxrep(ch, cnt); 471 CP(gcursor + 1, cursor); 472 do { 473 CP(cursor, genbuf); 474 if (cnt > 1) { 475 int oldhold = hold; 476 477 Outchar = vinschar; 478 hold |= HOLDQIK; 479 viprintf("%s", genbuf); 480 hold = oldhold; 481 Outchar = vputchar; 482 } 483 cursor += gcursor - genbuf; 484 } while (--cnt > 0); 485 endim(); 486 vUA2 = cursor; 487 if (escape != '\n') 488 CP(cursor, gcursor + 1); 489 490 /* 491 * If doomed characters remain, clobber them, 492 * and reopen the line to get the display exact. 493 */ 494 if (state != HARDOPEN) { 495 DEPTH(vcline) = 0; 496 savedoomed = doomed; 497 if (doomed > 0) { 498 int cind = cindent(); 499 500 physdc(cind, cind + doomed); 501 doomed = 0; 502 } 503 if(MB_CUR_MAX > 1) 504 rewrite = _ON; 505 i = vreopen(LINE(vcline), lineDOT(), vcline); 506 if(MB_CUR_MAX > 1) 507 rewrite = _OFF; 508 #ifdef TRACE 509 if (trace) 510 fprintf(trace, "restoring doomed from %d to %d\n", doomed, savedoomed); 511 #endif 512 if (ch == 'R') 513 doomed = savedoomed; 514 } 515 516 /* 517 * All done unless we are continuing on to another line. 518 */ 519 if (escape != '\n') { 520 vshowmode(""); 521 break; 522 } 523 524 /* 525 * Set up for the new line. 526 * First save the current line, then construct a new 527 * first image for the continuation line consisting 528 * of any new autoindent plus the pushed ahead text. 529 */ 530 killU(); 531 addtext(gobblebl ? " " : "\n"); 532 vsave(); 533 cnt = 1; 534 if (value(vi_AUTOINDENT)) { 535 if (value(vi_LISP)) 536 indent = lindent(dot + 1); 537 else 538 if (!HADUP && vaifirst) 539 indent = whitecnt(linebuf); 540 vaifirst = 0; 541 strcLIN(vpastwh(gcursor + 1)); 542 gcursor = genindent(indent); 543 *gcursor = 0; 544 if (gcursor + strlen(linebuf) > &genbuf[LBSIZE - 2]) 545 gcursor = genbuf; 546 CP(gcursor, linebuf); 547 } else { 548 CP(genbuf, gcursor + 1); 549 gcursor = genbuf; 550 } 551 552 /* 553 * If we started out as a single line operation and are now 554 * turning into a multi-line change, then we had better yank 555 * out dot before it changes so that undo will work 556 * correctly later. 557 */ 558 if (FIXUNDO && vundkind == VCHNG) { 559 vremote(1, yank, 0); 560 undap1--; 561 } 562 563 /* 564 * Now do the append of the new line in the buffer, 565 * and update the display. If slowopen 566 * we don't do very much. 567 */ 568 vdoappend(genbuf); 569 vundkind = VMANYINS; 570 vcline++; 571 if (state != VISUAL) 572 vshow(dot, NOLINE); 573 else { 574 i += LINE(vcline - 1); 575 vopen(dot, i); 576 if (value(vi_SLOWOPEN)) 577 vscrap(); 578 else 579 vsync1(LINE(vcline)); 580 } 581 switch (ch) { 582 case 'r': 583 break; 584 case 'a': 585 if (value(vi_TERSE)) { 586 vshowmode(gettext("A")); 587 } else { 588 vshowmode(gettext("APPEND MODE")); 589 } 590 break; 591 case 's': 592 if (value(vi_TERSE)) { 593 vshowmode(gettext("S")); 594 } else { 595 vshowmode(gettext("SUBSTITUTE MODE")); 596 } 597 break; 598 case 'c': 599 if (value(vi_TERSE)) { 600 vshowmode(gettext("C")); 601 } else { 602 vshowmode(gettext("CHANGE MODE")); 603 } 604 break; 605 case 'R': 606 if (value(vi_TERSE)) { 607 vshowmode(gettext("R")); 608 } else { 609 vshowmode(gettext("REPLACE MODE")); 610 } 611 break; 612 case 'i': 613 if (value(vi_TERSE)) { 614 vshowmode(gettext("I")); 615 } else { 616 vshowmode(gettext("INSERT MODE")); 617 } 618 break; 619 case 'o': 620 if (value(vi_TERSE)) { 621 vshowmode(gettext("O")); 622 } else { 623 vshowmode(gettext("OPEN MODE")); 624 } 625 break; 626 default: 627 if (value(vi_TERSE)) { 628 vshowmode(gettext("I")); 629 } else { 630 vshowmode(gettext("INPUT MODE")); 631 } 632 } 633 strcLIN(gcursor); 634 *gcursor = 0; 635 cursor = linebuf; 636 vgotoCL(nqcolumn(cursor - 1, genbuf)); 637 } /* end for (;;) loop in vappend() */ 638 639 if (imultlinecnt && gotNL) { 640 imultlinerep(savecnt, startsrcline, startsrccol, endsrccol); 641 } else if (omultlinecnt) { 642 omultlinerep(savecnt, startsrcline, endsrccol); 643 } 644 645 /* 646 * All done with insertion, position the cursor 647 * and sync the screen. 648 */ 649 hold = oldhold; 650 if ((imultlinecnt && gotNL) || omultlinecnt) { 651 fixdisplay(); 652 } else if (cursor > linebuf) { 653 cursor = lastchr(linebuf, cursor); 654 } 655 if (state != HARDOPEN) 656 vsyncCL(); 657 else if (cursor > linebuf) 658 back1(); 659 doomed = 0; 660 wcursor = cursor; 661 (void) vmove(); 662 } 663 664 /* 665 * XPG6 666 * To repeat multi-line input for [count]a, [count]A, [count]i, [count]I, 667 * or a subsequent [count]. : 668 * insert input count-1 more times. 669 */ 670 671 static void 672 imultlinerep(int savecnt, line *startsrcline, int startsrccol, int endsrccol) 673 { 674 int tmpcnt = 2; /* 1st insert counts as 1 repeat */ 675 line *srcline, *endsrcline; 676 size_t destsize = LBSIZE - endsrccol - 1; 677 678 endsrcline = dot; 679 680 /* Save linebuf into temp file before moving off the line. */ 681 vsave(); 682 683 /* 684 * At this point the temp file contains the first iteration of 685 * a multi-line insert, and we need to repeat it savecnt - 1 686 * more times in the temp file. dot is the last line in the 687 * first iteration of the insert. Decrement dot so that 688 * vdoappend() will append each new line before the last line. 689 */ 690 --dot; 691 --vcline; 692 /* 693 * Use genbuf to rebuild the last line in the 1st iteration 694 * of the repeated insert, then copy this line to the temp file. 695 */ 696 (void) strlcpy((char *)genbuf, (char *)linebuf, sizeof (genbuf)); 697 getline(*startsrcline); 698 if (strlcpy((char *)(genbuf + endsrccol + 1), 699 (char *)(linebuf + startsrccol), destsize) >= destsize) { 700 error(gettext("Line too long")); 701 } 702 vdoappend(genbuf); 703 vcline++; 704 /* 705 * Loop from the second line of the first iteration 706 * through endsrcline, appending after dot. 707 */ 708 ++startsrcline; 709 710 while (tmpcnt <= savecnt) { 711 for (srcline = startsrcline; srcline <= endsrcline; 712 ++srcline) { 713 if ((tmpcnt == savecnt) && 714 (srcline == endsrcline)) { 715 /* 716 * The last line is already in place, 717 * just make it the current line. 718 */ 719 vcline++; 720 dot++; 721 getDOT(); 722 cursor = linebuf + endsrccol; 723 } else { 724 getline(*srcline); 725 /* copy linebuf to temp file */ 726 vdoappend(linebuf); 727 vcline++; 728 } 729 } 730 ++tmpcnt; 731 } 732 } 733 734 /* 735 * To repeat input for [count]o, [count]O, or a subsequent [count]. : 736 * append input count-1 more times to the end of the already added 737 * text, each time starting on a new line. 738 */ 739 740 static void 741 omultlinerep(int savecnt, line *startsrcline, int endsrccol) 742 { 743 int tmpcnt = 2; /* 1st insert counts as 1 repeat */ 744 line *srcline, *endsrcline; 745 746 endsrcline = dot; 747 /* Save linebuf into temp file before moving off the line. */ 748 vsave(); 749 750 /* 751 * Loop from the first line of the first iteration 752 * through endsrcline, appending after dot. 753 */ 754 while (tmpcnt <= savecnt) { 755 for (srcline = startsrcline; srcline <= endsrcline; ++srcline) { 756 getline(*srcline); 757 /* copy linebuf to temp file */ 758 vdoappend(linebuf); 759 vcline++; 760 } 761 ++tmpcnt; 762 } 763 cursor = linebuf + endsrccol; 764 } 765 766 /* 767 * Similiar to a ctrl-l, however always vrepaint() in case the last line 768 * of the repeat would exceed the bottom of the screen. 769 */ 770 771 static void 772 fixdisplay(void) 773 { 774 vclear(); 775 vdirty(0, vcnt); 776 if (state != VISUAL) { 777 vclean(); 778 vcnt = 0; 779 vmoveto(dot, cursor, 0); 780 } else { 781 vredraw(WTOP); 782 vrepaint(cursor); 783 vfixcurs(); 784 } 785 } 786 787 /* 788 * Subroutine for vgetline to back up a single character position, 789 * backwards around end of lines (vgoto can't hack columns which are 790 * less than 0 in general). 791 */ 792 void 793 back1(void) 794 { 795 796 vgoto(destline - 1, WCOLS + destcol - 1); 797 } 798 799 /* 800 * Get a line into genbuf after gcursor. 801 * Cnt limits the number of input characters 802 * accepted and is used for handling the replace 803 * single character command. Aescaped is the location 804 * where we stick a termination indicator (whether we 805 * ended with an ESCAPE or a newline/return. 806 * 807 * We do erase-kill type processing here and also 808 * are careful about the way we do this so that it is 809 * repeatable. (I.e. so that your kill doesn't happen, 810 * when you repeat an insert if it was escaped with \ the 811 * first time you did it. commch is the command character 812 * involved, including the prompt for readline. 813 */ 814 unsigned char * 815 vgetline(cnt, gcursor, aescaped, commch) 816 int cnt; 817 unsigned char *gcursor; 818 bool *aescaped; 819 unsigned char commch; 820 { 821 int c, ch; 822 unsigned char *cp, *pcp; 823 int x, y, iwhite, backsl=0; 824 unsigned char *iglobp; 825 int (*OO)() = Outchar; 826 int length, width; 827 unsigned char multic[MULTI_BYTE_MAX+1]; 828 wchar_t wchar = 0; 829 unsigned char *p; 830 int len; 831 832 833 /* 834 * Clear the output state and counters 835 * for autoindent backwards motion (counts of ^D, etc.) 836 * Remember how much white space at beginning of line so 837 * as not to allow backspace over autoindent. 838 */ 839 840 *aescaped = 0; 841 ogcursor = gcursor; 842 flusho(); 843 CDCNT = 0; 844 HADUP = 0; 845 HADZERO = 0; 846 gobbled = 0; 847 iwhite = whitecnt(genbuf); 848 iglobp = vglobp; 849 850 /* 851 * Clear abbreviation recursive-use count 852 */ 853 abbrepcnt = 0; 854 /* 855 * Carefully avoid using vinschar in the echo area. 856 */ 857 if (splitw) 858 Outchar = vputchar; 859 else { 860 Outchar = vinschar; 861 vprepins(); 862 } 863 for (;;) { 864 length = 0; 865 backsl = 0; 866 if (gobblebl) 867 gobblebl--; 868 if (cnt != 0) { 869 cnt--; 870 if (cnt == 0) 871 goto vadone; 872 } 873 c = getkey(); 874 if (c != ATTN) 875 c &= 0377; 876 ch = c; 877 maphopcnt = 0; 878 if (vglobp == 0 && Peekkey == 0 && commch != 'r') 879 while ((ch = map(c, immacs, commch)) != c) { 880 c = ch; 881 if (!value(vi_REMAP)) 882 break; 883 if (++maphopcnt > 256) 884 error(gettext("Infinite macro loop")); 885 } 886 if (!iglobp) { 887 888 /* 889 * Erase-kill type processing. 890 * Only happens if we were not reading 891 * from untyped input when we started. 892 * Map users erase to ^H, kill to -1 for switch. 893 */ 894 if (c == tty.c_cc[VERASE]) 895 c = CTRL('h'); 896 else if (c == tty.c_cc[VKILL]) 897 c = -1; 898 switch (c) { 899 900 /* 901 * ^? Interrupt drops you back to visual 902 * command mode with an unread interrupt 903 * still in the input buffer. 904 * 905 * ^\ Quit does the same as interrupt. 906 * If you are a ex command rather than 907 * a vi command this will drop you 908 * back to command mode for sure. 909 */ 910 case ATTN: 911 case QUIT: 912 ungetkey(c); 913 goto vadone; 914 915 /* 916 * ^H Backs up a character in the input. 917 * 918 * BUG: Can't back around line boundaries. 919 * This is hard because stuff has 920 * already been saved for repeat. 921 */ 922 case CTRL('h'): 923 bakchar: 924 cp = lastchr(ogcursor, gcursor); 925 if (cp < ogcursor) { 926 if (splitw) { 927 /* 928 * Backspacing over readecho 929 * prompt. Pretend delete but 930 * don't beep. 931 */ 932 ungetkey(c); 933 goto vadone; 934 } 935 (void) beep(); 936 continue; 937 } 938 goto vbackup; 939 940 /* 941 * ^W Back up a white/non-white word. 942 */ 943 case CTRL('w'): 944 wdkind = 1; 945 for (cp = gcursor; cp > ogcursor && isspace(cp[-1]); cp--) 946 continue; 947 pcp = lastchr(ogcursor, cp); 948 for (c = wordch(pcp); 949 cp > ogcursor && wordof(c, pcp); cp = pcp, pcp = lastchr(ogcursor, cp)) 950 continue; 951 goto vbackup; 952 953 /* 954 * users kill Kill input on this line, back to 955 * the autoindent. 956 */ 957 case -1: 958 cp = ogcursor; 959 vbackup: 960 if (cp == gcursor) { 961 (void) beep(); 962 continue; 963 } 964 endim(); 965 *cp = 0; 966 c = cindent(); 967 vgotoCL(nqcolumn(lastchr(linebuf, cursor), genbuf)); 968 969 if (doomed >= 0) 970 doomed += c - cindent(); 971 gcursor = cp; 972 continue; 973 974 /* 975 * \ Followed by erase or kill 976 * maps to just the erase or kill. 977 */ 978 case '\\': 979 x = destcol, y = destline; 980 putchar('\\'); 981 vcsync(); 982 c = getkey(); 983 if (c == tty.c_cc[VERASE] 984 || c == tty.c_cc[VKILL]) 985 { 986 vgoto(y, x); 987 if (doomed >= 0) 988 doomed++; 989 multic[0] = wchar = c; 990 length = 1; 991 goto def; 992 } 993 ungetkey(c), c = '\\'; 994 backsl = 1; 995 break; 996 997 /* 998 * ^Q Super quote following character 999 * Only ^@ is verboten (trapped at 1000 * a lower level) and \n forces a line 1001 * split so doesn't really go in. 1002 * 1003 * ^V Synonym for ^Q 1004 */ 1005 case CTRL('q'): 1006 case CTRL('v'): 1007 x = destcol, y = destline; 1008 putchar('^'); 1009 vgoto(y, x); 1010 c = getkey(); 1011 #ifdef USG 1012 if (c == ATTN) 1013 c = tty.c_cc[VINTR]; 1014 #endif 1015 if (c != NL) { 1016 if (doomed >= 0) 1017 doomed++; 1018 multic[0] = wchar = c; 1019 length = 1; 1020 goto def; 1021 } 1022 break; 1023 } 1024 } 1025 1026 /* 1027 * If we get a blank not in the echo area 1028 * consider splitting the window in the wrapmargin. 1029 */ 1030 if(!backsl) { 1031 ungetkey(c); 1032 if((length = _mbftowc((char *)multic, &wchar, getkey, &Peekkey)) <= 0) { 1033 (void) beep(); 1034 continue; 1035 } 1036 } else { 1037 length = 1; 1038 multic[0] = '\\'; 1039 } 1040 1041 if (c != NL && !splitw) { 1042 if (c == ' ' && gobblebl) { 1043 gobbled = 1; 1044 continue; 1045 } 1046 if ((width = wcwidth(wchar)) <= 0) 1047 width = (wchar <= 0177 ? 1 : 4); 1048 if (value(vi_WRAPMARGIN) && 1049 (outcol + width - 1 >= OCOLUMNS - value(vi_WRAPMARGIN) || 1050 backsl && outcol==0) && 1051 commch != 'r') { 1052 /* 1053 * At end of word and hit wrapmargin. 1054 * Move the word to next line and keep going. 1055 */ 1056 unsigned char *wp; 1057 int bytelength; 1058 #ifndef PRESUNEUC 1059 unsigned char *tgcursor; 1060 wchar_t wc1, wc2; 1061 tgcursor = gcursor; 1062 #endif /* PRESUNEUC */ 1063 wdkind = 1; 1064 strncpy(gcursor, multic, length); 1065 gcursor += length; 1066 if (backsl) { 1067 #ifdef PRESUNEUC 1068 if((length = mbftowc((char *)multic, &wchar, getkey, &Peekkey)) <= 0) { 1069 #else 1070 if((length = _mbftowc((char *)multic, &wchar, getkey, &Peekkey)) <= 0) { 1071 #endif /* PRESUNEUC */ 1072 (void) beep(); 1073 continue; 1074 } 1075 strncpy(gcursor, multic, length); 1076 gcursor += length; 1077 } 1078 *gcursor = 0; 1079 /* 1080 * Find end of previous word if we are past it. 1081 */ 1082 for (cp=gcursor; cp>ogcursor && isspace(cp[-1]); cp--) 1083 ; 1084 #ifdef PRESUNEUC 1085 /* find screen width of previous word */ 1086 width = 0; 1087 for(wp = cp; *wp; ) 1088 #else 1089 /* count screen width of pending characters */ 1090 width = 0; 1091 for(wp = tgcursor; wp < cp;) 1092 #endif /* PRESUNEUC */ 1093 if((bytelength = mbtowc(&wchar, (char *)wp, MULTI_BYTE_MAX)) < 0) { 1094 width+=4; 1095 wp++; 1096 } else { 1097 int curwidth = wcwidth(wchar); 1098 if(curwidth <= 0) 1099 width += (*wp < 0200 ? 2 : 4); 1100 else 1101 width += curwidth; 1102 wp += bytelength; 1103 } 1104 1105 #ifdef PRESUNEUC 1106 if (outcol+(backsl?OCOLUMNS:0) - width >= OCOLUMNS - value(vi_WRAPMARGIN)) { 1107 #else 1108 if (outcol+(backsl?OCOLUMNS:0) + width -1 >= OCOLUMNS - value(vi_WRAPMARGIN)) { 1109 #endif /* PRESUNEUC */ 1110 /* 1111 * Find beginning of previous word. 1112 */ 1113 #ifdef PRESUNEUC 1114 for (; cp>ogcursor && !isspace(cp[-1]); cp--) 1115 ; 1116 #else 1117 wc1 = wc2 = 0; 1118 while (cp>ogcursor) { 1119 if (isspace(cp[-1])) { 1120 break; 1121 } 1122 if (!multibyte) { 1123 cp--; 1124 continue; 1125 } 1126 wp = (unsigned char *)(cp - 1127 MB_CUR_MAX); 1128 if (wp < ogcursor) 1129 wp = ogcursor; 1130 while (cp > wp) { 1131 /* 7tabs */if (wc2) { 1132 /* 7tabs */ if ((bytelength = mbtowc(&wc1, (char *)wp, cp-wp)) != cp-wp) { 1133 /* 7tabs */ wp++; 1134 /* 7tabs */ wc1 = 0; 1135 /* 7tabs */ continue; 1136 /* 7tabs */ } 1137 /* 7tabs */} else { 1138 /* 7tabs */ if ((bytelength = mbtowc(&wc2, (char *)wp, cp-wp)) != cp-wp) { 1139 /* 7tabs */ wp++; 1140 /* 7tabs */ wc2 = 0; 1141 /* 7tabs */ continue; 1142 /* 7tabs */ } 1143 /* 7tabs */} 1144 /* 7tabs */if (wc1) { 1145 /* 7tabs */ if (wdbdg && (!iswascii(wc1) || !iswascii(wc2))) { 1146 /* 7tabs */ if ((*wdbdg)(wc1, wc2, 2) < 5) { 1147 /* 7tabs */ goto ws; 1148 /* 7tabs */ } 1149 /* 7tabs */ } 1150 /* 7tabs */ wc2 = wc1; 1151 /* 7tabs */ wc1 = 0; 1152 /* 7tabs */ cp -= bytelength - 1; 1153 /* 7tabs */ break; 1154 /* 7tabs */} else { 1155 /* 7tabs */ cp -= bytelength - 1; 1156 /* 7tabs */ break; 1157 /* 7tabs */} 1158 } 1159 cp--; 1160 } 1161 ws: 1162 #endif /* PRESUNEUC */ 1163 if (cp <= ogcursor) { 1164 /* 1165 * There is a single word that 1166 * is too long to fit. Just 1167 * let it pass, but beep for 1168 * each new letter to warn 1169 * the luser. 1170 */ 1171 gcursor -= length; 1172 c = *gcursor; 1173 *gcursor = 0; 1174 (void) beep(); 1175 goto dontbreak; 1176 } 1177 /* 1178 * Save it for next line. 1179 */ 1180 macpush(cp, 0); 1181 #ifdef PRESUNEUC 1182 cp--; 1183 #endif /* PRESUNEUC */ 1184 } 1185 macpush("\n", 0); 1186 /* 1187 * Erase white space before the word. 1188 */ 1189 while (cp > ogcursor && isspace(cp[-1])) 1190 cp--; /* skip blank */ 1191 gobblebl = 3; 1192 goto vbackup; 1193 } 1194 dontbreak:; 1195 } 1196 1197 /* 1198 * Word abbreviation mode. 1199 */ 1200 if (anyabbrs && gcursor > ogcursor && !wordch(multic) && wordch(lastchr(ogcursor, gcursor))) { 1201 int wdtype, abno; 1202 1203 multic[length] = 0; 1204 wdkind = 1; 1205 cp = lastchr(ogcursor, gcursor); 1206 pcp = lastchr(ogcursor, cp); 1207 for (wdtype = wordch(pcp); 1208 cp > ogcursor && wordof(wdtype, pcp); cp = pcp, pcp = lastchr(ogcursor, pcp)) 1209 ; 1210 *gcursor = 0; 1211 for (abno=0; abbrevs[abno].mapto; abno++) { 1212 if (eq(cp, abbrevs[abno].cap)) { 1213 if(abbrepcnt == 0) { 1214 if(reccnt(abbrevs[abno].cap, abbrevs[abno].mapto)) 1215 abbrepcnt = 1; 1216 macpush(multic, 0); 1217 macpush(abbrevs[abno].mapto); 1218 goto vbackup; 1219 } else 1220 abbrepcnt = 0; 1221 } 1222 } 1223 } 1224 1225 switch (c) { 1226 1227 /* 1228 * ^M Except in repeat maps to \n. 1229 */ 1230 case CR: 1231 if (vglobp) { 1232 multic[0] = wchar = c; 1233 length = 1; 1234 goto def; 1235 } 1236 c = '\n'; 1237 /* presto chango ... */ 1238 1239 /* 1240 * \n Start new line. 1241 */ 1242 case NL: 1243 *aescaped = c; 1244 goto vadone; 1245 1246 /* 1247 * escape End insert unless repeat and more to repeat. 1248 */ 1249 case ESCAPE: 1250 if (lastvgk) { 1251 multic[0] = wchar = c; 1252 length = 1; 1253 goto def; 1254 } 1255 goto vadone; 1256 1257 /* 1258 * ^D Backtab. 1259 * ^T Software forward tab. 1260 * 1261 * Unless in repeat where this means these 1262 * were superquoted in. 1263 */ 1264 case CTRL('t'): 1265 if (vglobp) { 1266 multic[0] = wchar = c; 1267 length = 1; 1268 goto def; 1269 } 1270 /* fall into ... */ 1271 1272 *gcursor = 0; 1273 cp = vpastwh(genbuf); 1274 c = whitecnt(genbuf); 1275 if (ch == CTRL('t')) { 1276 /* 1277 * ^t just generates new indent replacing 1278 * current white space rounded up to soft 1279 * tab stop increment. 1280 */ 1281 if (cp != gcursor) 1282 /* 1283 * BUG: Don't hack ^T except 1284 * right after initial 1285 * white space. 1286 */ 1287 continue; 1288 cp = genindent(iwhite = backtab(c + value(vi_SHIFTWIDTH) + 1)); 1289 ogcursor = cp; 1290 goto vbackup; 1291 } 1292 /* 1293 * ^D works only if we are at the (end of) the 1294 * generated autoindent. We count the ^D for repeat 1295 * purposes. 1296 */ 1297 case CTRL('d'): 1298 /* check if ^d was superquoted in */ 1299 if(vglobp && inscdcnt <= 0) { 1300 multic[0] = wchar = c; 1301 length = 1; 1302 goto def; 1303 } 1304 if(vglobp) 1305 inscdcnt--; 1306 *gcursor = 0; 1307 cp = vpastwh(genbuf); 1308 c = whitecnt(genbuf); 1309 if (c == iwhite && c != 0) 1310 if (cp == gcursor) { 1311 iwhite = backtab(c); 1312 CDCNT++; 1313 ogcursor = cp = genindent(iwhite); 1314 goto vbackup; 1315 } else if (&cp[1] == gcursor && 1316 (*cp == '^' || *cp == '0')) { 1317 /* 1318 * ^^D moves to margin, then back 1319 * to current indent on next line. 1320 * 1321 * 0^D moves to margin and then 1322 * stays there. 1323 */ 1324 HADZERO = *cp == '0'; 1325 ogcursor = cp = genbuf; 1326 HADUP = 1 - HADZERO; 1327 CDCNT = 1; 1328 endim(); 1329 back1(); 1330 (void) vputchar(' '); 1331 goto vbackup; 1332 } 1333 1334 if (vglobp && vglobp - iglobp >= 2) { 1335 if ((p = vglobp - MB_CUR_MAX) < iglobp) 1336 p = iglobp; 1337 for ( ; p < &vglobp[-2]; p += len) { 1338 if ((len = mblen((char *)p, MB_CUR_MAX)) <= 0) 1339 len = 1; 1340 } 1341 if ((p == &vglobp[-2]) && 1342 (*p == '^' || *p == '0') && 1343 gcursor == ogcursor + 1) 1344 goto bakchar; 1345 } 1346 continue; 1347 1348 default: 1349 /* 1350 * Possibly discard control inputs. 1351 */ 1352 if (!vglobp && junk(c)) { 1353 (void) beep(); 1354 continue; 1355 } 1356 def: 1357 if (!backsl) { 1358 putchar(wchar); 1359 flush(); 1360 } 1361 if (gcursor + length - 1 > &genbuf[LBSIZE - 2]) 1362 error(gettext("Line too long")); 1363 (void)strncpy(gcursor, multic, length); 1364 gcursor += length; 1365 vcsync(); 1366 if (value(vi_SHOWMATCH) && !iglobp) 1367 if (c == ')' || c == '}') 1368 lsmatch(gcursor); 1369 continue; 1370 } 1371 } 1372 vadone: 1373 *gcursor = 0; 1374 if (Outchar != termchar) 1375 Outchar = OO; 1376 endim(); 1377 return (gcursor); 1378 } 1379 1380 int vgetsplit(); 1381 unsigned char *vsplitpt; 1382 1383 /* 1384 * Append the line in buffer at lp 1385 * to the buffer after dot. 1386 */ 1387 void 1388 vdoappend(unsigned char *lp) 1389 { 1390 int oing = inglobal; 1391 1392 vsplitpt = lp; 1393 inglobal = 1; 1394 (void)append(vgetsplit, dot); 1395 inglobal = oing; 1396 } 1397 1398 /* 1399 * Subroutine for vdoappend to pass to append. 1400 */ 1401 int 1402 vgetsplit(void) 1403 { 1404 1405 if (vsplitpt == 0) 1406 return (EOF); 1407 strcLIN(vsplitpt); 1408 vsplitpt = 0; 1409 return (0); 1410 } 1411 1412 /* 1413 * Vmaxrep determines the maximum repetition factor 1414 * allowed that will yield total line length less than 1415 * LBSIZE characters and also does hacks for the R command. 1416 */ 1417 int 1418 vmaxrep(unsigned char ch, int cnt) 1419 { 1420 int len; 1421 unsigned char *cp; 1422 int repcnt, oldcnt, replen; 1423 if (cnt > LBSIZE - 2) 1424 cnt = LBSIZE - 2; 1425 if (ch == 'R') { 1426 len = strlen(cursor); 1427 oldcnt = 0; 1428 for(cp = cursor; *cp; ) { 1429 oldcnt++; 1430 cp = nextchr(cp); 1431 } 1432 repcnt = 0; 1433 for(cp = genbuf; *cp; ) { 1434 repcnt++; 1435 cp = nextchr(cp); 1436 } 1437 /* 1438 * if number of characters in replacement string 1439 * (repcnt) is less than number of characters following 1440 * cursor (oldcnt), find end of repcnt 1441 * characters after cursor 1442 */ 1443 if(repcnt < oldcnt) { 1444 for(cp = cursor; repcnt > 0; repcnt--) 1445 cp = nextchr(cp); 1446 len = cp - cursor; 1447 } 1448 CP(cursor, cursor + len); 1449 vUD2 += len; 1450 } 1451 len = strlen(linebuf); 1452 replen = strlen(genbuf); 1453 if (len + cnt * replen <= LBSIZE - 2) 1454 return (cnt); 1455 cnt = (LBSIZE - 2 - len) / replen; 1456 if (cnt == 0) { 1457 vsave(); 1458 error(gettext("Line too long")); 1459 } 1460 return (cnt); 1461 } 1462 1463 /* 1464 * Determine how many occurrences of word 'CAP' are in 'MAPTO'. To be 1465 * considered an occurrence there must be both a nonword-prefix, a 1466 * complete match of 'CAP' within 'MAPTO', and a nonword-suffix. 1467 * Note that the beginning and end of 'MAPTO' are considered to be 1468 * valid nonword delimiters. 1469 */ 1470 int 1471 reccnt(unsigned char *cap, unsigned char *mapto) 1472 { 1473 int i, cnt, final; 1474 1475 cnt = 0; 1476 final = strlen(mapto) - strlen(cap); 1477 1478 for (i=0; i <= final; i++) 1479 if ((strncmp(cap, mapto+i, strlen(cap)) == 0) /* match */ 1480 && (i == 0 || !wordch(&mapto[i-1])) /* prefix ok */ 1481 && (i == final || !wordch(&mapto[i+strlen(cap)]))) /* suffix ok */ 1482 cnt++; 1483 return (cnt); 1484 } 1485 1486