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