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 */
bleep(i,cp)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 */
vdcMID()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 */
takeout(BUF)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 */
ateopr()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
vappend(ch,cnt,indent)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 */
back1()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 *
vgetline(cnt,gcursor,aescaped,commch)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 */
vdoappend(lp)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 */
vgetsplit()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 */
vmaxrep(ch,cnt)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