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