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