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