xref: /original-bsd/usr.bin/ex/ex_vops2.c (revision 3705696b)
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