xref: /illumos-gate/usr/src/cmd/vi/port/ex_vops2.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 
30 /* Copyright (c) 1981 Regents of the University of California */
31 
32 #pragma ident	"%Z%%M%	%I%	%E% SMI"
33 
34 #include "ex.h"
35 #include "ex_tty.h"
36 #include "ex_vis.h"
37 #ifndef PRESUNEUC
38 #include <wctype.h>
39 /* Undef putchar/getchar if they're defined. */
40 #ifdef putchar
41 #	undef putchar
42 #endif
43 #ifdef getchar
44 #	undef getchar
45 #endif
46 #endif /* PRESUNEUC */
47 
48 extern size_t strlcpy(char *, const char *, size_t);
49 
50 /*
51  * Low level routines for operations sequences,
52  * and mostly, insert mode (and a subroutine
53  * to read an input line, including in the echo area.)
54  */
55 extern unsigned char	*vUA1, *vUA2;		/* extern; also in ex_vops.c */
56 extern unsigned char	*vUD1, *vUD2;		/* extern; also in ex_vops.c */
57 
58 #ifdef XPG6
59 /* XPG6 assertion 313 & 254 [count]r\n :  Also used in ex_vmain.c */
60 extern int redisplay;
61 #endif
62 
63 int vmaxrep(unsigned char, int);
64 static void imultlinerep(int, line *, int, int);
65 static void omultlinerep(int, line *, int);
66 #ifdef XPG6
67 static void rmultlinerep(int, int);
68 #endif
69 void fixdisplay(void);
70 
71 /*
72  * Obleeperate characters in hardcopy
73  * open with \'s.
74  */
75 void
76 bleep(int i, unsigned char *cp)
77 {
78 
79 	i -= lcolumn(nextchr(cp));
80 	do
81 		putchar('\\' | QUOTE);
82 	while (--i >= 0);
83 	rubble = 1;
84 }
85 
86 /*
87  * Common code for middle part of delete
88  * and change operating on parts of lines.
89  */
90 int
91 vdcMID(void)
92 {
93 	unsigned char *cp;
94 
95 	squish();
96 	setLAST();
97 	if (FIXUNDO)
98 		vundkind = VCHNG, CP(vutmp, linebuf);
99 	if (wcursor < cursor)
100 		cp = wcursor, wcursor = cursor, cursor = cp;
101 	vUD1 = vUA1 = vUA2 = cursor; vUD2 = wcursor;
102 	/*
103 	 * XPG6 assertion 273: Set vmcurs so that undo positions the
104 	 * cursor column correctly when we've moved off the initial line
105 	 * that was changed, as with the C, c, and s commands,
106 	 * when G has moved us off the line, or when a
107 	 * multi-line change was done.
108 	 */
109 	fixundo();
110 	return (lcolumn(wcursor));
111 }
112 
113 /*
114  * Take text from linebuf and stick it
115  * in the VBSIZE buffer BUF.  Used to save
116  * deleted text of part of line.
117  */
118 void
119 takeout(unsigned char *BUF)
120 {
121 	unsigned char *cp;
122 
123 	if (wcursor < linebuf)
124 		wcursor = linebuf;
125 	if (cursor == wcursor) {
126 		(void) beep();
127 		return;
128 	}
129 	if (wcursor < cursor) {
130 		cp = wcursor;
131 		wcursor = cursor;
132 		cursor = cp;
133 	}
134 	setBUF(BUF);
135 	if ((unsigned char)BUF[128] == 0200)
136 		(void) beep();
137 }
138 
139 /*
140  * Are we at the end of the printed representation of the
141  * line?  Used internally in hardcopy open.
142  */
143 int
144 ateopr(void)
145 {
146 	wchar_t i, c;
147 	wchar_t *cp = vtube[destline] + destcol;
148 
149 	for (i = WCOLS - destcol; i > 0; i--) {
150 		c = *cp++;
151 		if (c == 0) {
152 			/*
153 			 * Optimization to consider returning early, saving
154 			 * CPU time.  We have to make a special check that
155 			 * we aren't missing a mode indicator.
156 			 */
157 			if (destline == WECHO && destcol < WCOLS-11 && vtube[WECHO][WCOLS-20])
158 				return 0;
159 			return (1);
160 		}
161 		if (c != ' ' && (c & QUOTE) == 0)
162 			return (0);
163 	}
164 	return (1);
165 }
166 
167 /*
168  * Append.
169  *
170  * This routine handles the top level append, doing work
171  * as each new line comes in, and arranging repeatability.
172  * It also handles append with repeat counts, and calculation
173  * of autoindents for new lines.
174  */
175 bool	vaifirst;
176 bool	gobbled;
177 unsigned char	*ogcursor;
178 
179 static int 	INSCDCNT; /* number of ^D's (backtabs) in insertion buffer */
180 
181 static int 	inscdcnt; /*
182 			   * count of ^D's (backtabs) not seen yet when doing
183 		 	   * repeat of insertion
184 			   */
185 
186 void
187 vappend(int ch, int cnt, int indent)
188 {
189 	int i;
190 	unsigned char *gcursor;
191 	bool escape;
192 	int repcnt, savedoomed;
193 	short oldhold = hold;
194 	int savecnt = cnt;
195 	line *startsrcline;
196 	int startsrccol, endsrccol;
197 	int gotNL = 0;
198 	int imultlinecnt = 0;
199 	int omultlinecnt = 0;
200 
201 	if ((savecnt > 1) && (ch == 'o' || ch == 'O')) {
202 		omultlinecnt = 1;
203 	}
204 #ifdef XPG6
205 	if ((savecnt > 1) && (ch == 'a' || ch == 'A' || ch == 'i' || ch == 'I'))
206 		imultlinecnt = 1;
207 #endif /* XPG6 */
208 
209 	/*
210 	 * Before a move in hardopen when the line is dirty
211 	 * or we are in the middle of the printed representation,
212 	 * we retype the line to the left of the cursor so the
213 	 * insert looks clean.
214 	 */
215 
216 	if (ch != 'o' && state == HARDOPEN && (rubble || !ateopr())) {
217 		rubble = 1;
218 		gcursor = cursor;
219 		i = *gcursor;
220 		*gcursor = ' ';
221 		wcursor = gcursor;
222 		(void) vmove();
223 		*gcursor = i;
224 	}
225 	/*
226 	 * If vrep() passed indent = 0, this is the 'r' command,
227 	 * so don't autoindent until the last char.
228 	 */
229 	vaifirst = indent == 0;
230 
231 	/*
232 	 * Handle replace character by (eventually)
233 	 * limiting the number of input characters allowed
234 	 * in the vgetline routine.
235 	 */
236 	if (ch == 'r')
237 		repcnt = 2;
238 	else
239 		repcnt = 0;
240 
241 	/*
242 	 * If an autoindent is specified, then
243 	 * generate a mixture of blanks to tabs to implement
244 	 * it and place the cursor after the indent.
245 	 * Text read by the vgetline routine will be placed in genbuf,
246 	 * so the indent is generated there.
247 	 */
248 	if (value(vi_AUTOINDENT) && indent != 0) {
249 		unsigned char x;
250 		gcursor = genindent(indent);
251 		*gcursor = 0;
252 		vgotoCL(nqcolumn(lastchr(linebuf, cursor), genbuf));
253 	} else {
254 		gcursor = genbuf;
255 		*gcursor = 0;
256 		if (ch == 'o')
257 			vfixcurs();
258 	}
259 
260 	/*
261 	 * Prepare for undo.  Pointers delimit inserted portion of line.
262 	 */
263 	vUA1 = vUA2 = cursor;
264 
265 	/*
266 	 * If we are not in a repeated command and a ^@ comes in
267 	 * then this means the previous inserted text.
268 	 * If there is none or it was too long to be saved,
269 	 * then beep() and also arrange to undo any damage done
270 	 * so far (e.g. if we are a change.)
271 	 */
272 	switch (ch) {
273 	case 'r':
274 		break;
275 	case 'a':
276 		/*
277 		 * TRANSLATION_NOTE
278 		 *	"A" is a terse mode message corresponding to
279 		 *	"APPEND MODE".
280 		 *	Translated message of "A" must be 1 character (not byte).
281 		 *	Or, just leave it.
282 		 */
283 		if (value(vi_TERSE)) {
284 			vshowmode(gettext("A"));
285 		} else {
286 			vshowmode(gettext("APPEND MODE"));
287 		}
288 		break;
289 	case 's':
290 		/*
291 		 * TRANSLATION_NOTE
292 		 *	"S" is a terse mode message corresponding to
293 		 *	"SUBSTITUTE MODE".
294 		 *	Translated message of "S" must be 1 character (not byte).
295 		 *	Or, just leave it.
296 		 */
297 		if (value(vi_TERSE)) {
298 			vshowmode(gettext("S"));
299 		} else {
300 			vshowmode(gettext("SUBSTITUTE MODE"));
301 		}
302 		break;
303 	case 'c':
304 		/*
305 		 * TRANSLATION_NOTE
306 		 *	"C" is a terse mode message corresponding to
307 		 *	"CHANGE MODE".
308 		 *	Translated message of "C" must be 1 character (not byte).
309 		 *	Or, just leave it.
310 		 */
311 		if (value(vi_TERSE)) {
312 			vshowmode(gettext("C"));
313 		} else {
314 			vshowmode(gettext("CHANGE MODE"));
315 		}
316 		break;
317 	case 'R':
318 		/*
319 		 * TRANSLATION_NOTE
320 		 *	"R" is a terse mode message corresponding to
321 		 *	"REPLACE MODE".
322 		 *	Translated message of "R" must be 1 character (not byte).
323 		 *	Or, just leave it.
324 		 */
325 		if (value(vi_TERSE)) {
326 			vshowmode(gettext("R"));
327 		} else {
328 			vshowmode(gettext("REPLACE MODE"));
329 		}
330 		break;
331 	case 'o':
332 		/*
333 		 * TRANSLATION_NOTE
334 		 *	"O" is a terse mode message corresponding to
335 		 *	"OPEN MODE".
336 		 *	Translated message of "O" must be 1 character (not byte).
337 		 *	Or, just leave it.
338 		 */
339 		if (value(vi_TERSE)) {
340 			vshowmode(gettext("O"));
341 		} else {
342 			vshowmode(gettext("OPEN MODE"));
343 		}
344 		break;
345 	case 'i':
346 		/*
347 		 * TRANSLATION_NOTE
348 		 *	"I" is a terse mode message corresponding to
349 		 *	"INSERT MODE" and the following "INPUT MODE".
350 		 *	Translated message of "I" must be 1 character (not byte).
351 		 *	Or, just leave it.
352 		 */
353 		if (value(vi_TERSE)) {
354 			vshowmode(gettext("I"));
355 		} else {
356 			vshowmode(gettext("INSERT MODE"));
357 		}
358 		break;
359 	default:
360 		/*
361 		 * TRANSLATION_NOTE
362 		 *	"I" is a terse mode message corresponding to
363 		 *	"INPUT MODE" and the previous "INSERT MODE".
364 		 *	Translated message of "I" must be 1 character (not byte).
365 		 *	Or, just leave it.
366 		 */
367 		if (value(vi_TERSE)) {
368 			vshowmode(gettext("I"));
369 		} else {
370 			vshowmode(gettext("INPUT MODE"));
371 		}
372 	}
373 	ixlatctl(1);
374 	if ((vglobp && *vglobp == 0) || peekbr()) {
375 		if (INS[128] == 0200) {
376 			(void) beep();
377 			if (!splitw)
378 				ungetkey('u');
379 			doomed = 0;
380 			hold = oldhold;
381 			return;
382 		}
383 		/*
384 		 * Unread input from INS.
385 		 * An escape will be generated at end of string.
386 		 * Hold off n^^2 type update on dumb terminals.
387 		 */
388 		vglobp = INS;
389 		inscdcnt = INSCDCNT;
390 		hold |= HOLDQIK;
391 	} else if (vglobp == 0) {
392 		/*
393 		 * Not a repeated command, get
394 		 * a new inserted text for repeat.
395 		 */
396 		INS[0] = 0;
397 		INS[128] = 0;
398 		INSCDCNT = 0;
399 	}
400 
401 	/*
402 	 * For wrapmargin to hack away second space after a '.'
403 	 * when the first space caused a line break we keep
404 	 * track that this happened in gobblebl, which says
405 	 * to gobble up a blank silently.
406 	 */
407 	gobblebl = 0;
408 
409 	startsrcline = dot;
410 	startsrccol = cursor - linebuf;
411 
412 	/*
413 	 * Text gathering loop.
414 	 * New text goes into genbuf starting at gcursor.
415 	 * cursor preserves place in linebuf where text will eventually go.
416 	 */
417 	if (*cursor == 0 || state == CRTOPEN)
418 		hold |= HOLDROL;
419 	for (;;) {
420 		if (ch == 'r' && repcnt == 0)
421 			escape = 0;
422 		else {
423 			ixlatctl(1);
424 			/*
425 			 * When vgetline() returns, gcursor is
426 			 * pointing to '\0' and vgetline() has
427 			 * read an ESCAPE or NL.
428 			 */
429 			gcursor = vgetline(repcnt, gcursor, &escape, ch);
430 			if (escape == '\n') {
431 				gotNL = 1;
432 #ifdef XPG6
433 				if (ch == 'r') {
434 					/*
435 					 * XPG6 assertion 313 [count]r\n :
436 					 * Arrange to set cursor correctly.
437 					 */
438 					endsrccol = gcursor - genbuf - 1;
439 				}
440 #endif /* XPG6 */
441 			} else {
442 				/*
443 				 * Upon escape, gcursor is pointing to '\0'
444 				 * terminating the string in genbuf.
445 				 */
446 				endsrccol = gcursor - genbuf - 1;
447 			}
448 			ixlatctl(0);
449 
450 			/*
451 			 * After an append, stick information
452 			 * about the ^D's and ^^D's and 0^D's in
453 			 * the repeated text buffer so repeated
454 			 * inserts of stuff indented with ^D as backtab's
455 			 * can work.
456 			 */
457 			if (HADUP)
458 				addtext("^");
459 			else if (HADZERO)
460 				addtext("0");
461 			if(!vglobp)
462 				INSCDCNT = CDCNT;
463 			while (CDCNT > 0) {
464 				addtext("\004");
465 				CDCNT--;
466 			}
467 			if (gobbled)
468 				addtext(" ");
469 			addtext(ogcursor);
470 		}
471 		repcnt = 0;
472 
473 		/*
474 		 * Smash the generated and preexisting indents together
475 		 * and generate one cleanly made out of tabs and spaces
476 		 * if we are using autoindent and this isn't 'r' command.
477 		 */
478 		if (!vaifirst && value(vi_AUTOINDENT)) {
479 			i = fixindent(indent);
480 			if (!HADUP)
481 				indent = i;
482 			gcursor = strend(genbuf);
483 		}
484 
485 		/*
486 		 * Set cnt to 1 to avoid repeating the text on the same line.
487 		 * Do this for commands 'i', 'I', 'a', and 'A', if we're
488 		 * inserting anything with a newline for XPG6.  Always do this
489 		 * for commands 'o' and 'O'.
490 		 */
491 		if ((imultlinecnt && gotNL) || omultlinecnt) {
492 			cnt = 1;
493 		}
494 
495 		/*
496 		 * Limit the repetition count based on maximum
497 		 * possible line length; do output implied
498 		 * by further count (> 1) and cons up the new line
499 		 * in linebuf.
500 		 */
501 		cnt = vmaxrep(ch, cnt);
502 		/*
503 		 * cursor points to linebuf
504 		 * Copy remaining old text (cursor) in original
505 		 * line to after new text (gcursor + 1) in genbuf.
506 		 */
507 		CP(gcursor + 1, cursor);
508 		/*
509 		 * For [count] r \n command, when replacing [count] chars
510 		 * with '\n', this loop replaces [count] chars with "".
511 		 */
512 		do {
513 			/* cp new text (genbuf) into linebuf (cursor) */
514 			CP(cursor, genbuf);
515 			if (cnt > 1) {
516 				int oldhold = hold;
517 
518 				Outchar = vinschar;
519 				hold |= HOLDQIK;
520 				viprintf("%s", genbuf);
521 				hold = oldhold;
522 				Outchar = vputchar;
523 			}
524 			/* point cursor after new text in linebuf */
525 			cursor += gcursor - genbuf;
526 		} while (--cnt > 0);
527 		endim();
528 		vUA2 = cursor;
529 		/* add the remaining old text after the cursor */
530 		if (escape != '\n')
531 			CP(cursor, gcursor + 1);
532 
533 		/*
534 		 * If doomed characters remain, clobber them,
535 		 * and reopen the line to get the display exact.
536 		 * eg. c$ to change to end of line
537 		 */
538 		if (state != HARDOPEN) {
539 			DEPTH(vcline) = 0;
540 			savedoomed = doomed;
541 			if (doomed > 0) {
542 				int cind = cindent();
543 
544 				physdc(cind, cind + doomed);
545 				doomed = 0;
546 			}
547 			if(MB_CUR_MAX > 1)
548 				rewrite = _ON;
549 			i = vreopen(LINE(vcline), lineDOT(), vcline);
550 			if(MB_CUR_MAX > 1)
551 				rewrite = _OFF;
552 #ifdef TRACE
553 			if (trace)
554 				fprintf(trace, "restoring doomed from %d to %d\n", doomed, savedoomed);
555 #endif
556 			if (ch == 'R')
557 				doomed = savedoomed;
558 		}
559 
560 		/*
561 		 * Unless we are continuing on to another line
562 		 * (got a NL), break out of the for loop (got
563 		 * an ESCAPE).
564 		 */
565 		if (escape != '\n') {
566 			vshowmode("");
567 			break;
568 		}
569 
570 		/*
571 		 * Set up for the new line.
572 		 * First save the current line, then construct a new
573 		 * first image for the continuation line consisting
574 		 * of any new autoindent plus the pushed ahead text.
575 		 */
576 		killU();
577 		addtext(gobblebl ? " " : "\n");
578 		/* save vutmp (for undo state) into temp file */
579 		vsave();
580 		cnt = 1;
581 		if (value(vi_AUTOINDENT)) {
582 			if (value(vi_LISP))
583 				indent = lindent(dot + 1);
584 			else
585 			     if (!HADUP && vaifirst)
586 				indent = whitecnt(linebuf);
587 			vaifirst = 0;
588 			strcLIN(vpastwh(gcursor + 1));
589 			gcursor = genindent(indent);
590 			*gcursor = 0;
591 			if (gcursor + strlen(linebuf) > &genbuf[LBSIZE - 2])
592 				gcursor = genbuf;
593 			CP(gcursor, linebuf);
594 		} else {
595 			/*
596 			 * Put gcursor at start of genbuf to wipe
597 			 * out previous line in preparation for
598 			 * the next vgetline() loop.
599 			 */
600 			CP(genbuf, gcursor + 1);
601 			gcursor = genbuf;
602 		}
603 
604 		/*
605 		 * If we started out as a single line operation and are now
606 		 * turning into a multi-line change, then we had better yank
607 		 * out dot before it changes so that undo will work
608 		 * correctly later.
609 		 */
610 		if (FIXUNDO && vundkind == VCHNG) {
611 			vremote(1, yank, 0);
612 			undap1--;
613 		}
614 
615 		/*
616 		 * Now do the append of the new line in the buffer,
617 		 * and update the display, ie: append genbuf to
618 		 * the file after dot.  If slowopen
619 		 * we don't do very much.
620 		 */
621 		vdoappend(genbuf);
622 		vundkind = VMANYINS;
623 		vcline++;
624 		if (state != VISUAL)
625 			vshow(dot, NOLINE);
626 		else {
627 			i += LINE(vcline - 1);
628 			vopen(dot, i);
629 			if (value(vi_SLOWOPEN))
630 				vscrap();
631 			else
632 				vsync1(LINE(vcline));
633 		}
634 		switch (ch) {
635 		case 'r':
636 			break;
637 		case 'a':
638 			if (value(vi_TERSE)) {
639 				vshowmode(gettext("A"));
640 			} else {
641 				vshowmode(gettext("APPEND MODE"));
642 			}
643 			break;
644 		case 's':
645 			if (value(vi_TERSE)) {
646 				vshowmode(gettext("S"));
647 			} else {
648 				vshowmode(gettext("SUBSTITUTE MODE"));
649 			}
650 			break;
651 		case 'c':
652 			if (value(vi_TERSE)) {
653 				vshowmode(gettext("C"));
654 			} else {
655 				vshowmode(gettext("CHANGE MODE"));
656 			}
657 			break;
658 		case 'R':
659 			if (value(vi_TERSE)) {
660 				vshowmode(gettext("R"));
661 			} else {
662 				vshowmode(gettext("REPLACE MODE"));
663 			}
664 			break;
665 		case 'i':
666 			if (value(vi_TERSE)) {
667 				vshowmode(gettext("I"));
668 			} else {
669 				vshowmode(gettext("INSERT MODE"));
670 			}
671 			break;
672 		case 'o':
673 			if (value(vi_TERSE)) {
674 				vshowmode(gettext("O"));
675 			} else {
676 				vshowmode(gettext("OPEN MODE"));
677 			}
678 			break;
679 		default:
680 			if (value(vi_TERSE)) {
681 				vshowmode(gettext("I"));
682 			} else {
683 				vshowmode(gettext("INPUT MODE"));
684 			}
685 		}
686 		strcLIN(gcursor);
687 		/* zero genbuf */
688 		*gcursor = 0;
689 		cursor = linebuf;
690 		vgotoCL(nqcolumn(cursor - 1, genbuf));
691 	} /* end for (;;) loop in vappend() */
692 
693 	if (imultlinecnt && gotNL) {
694 		imultlinerep(savecnt, startsrcline, startsrccol, endsrccol);
695 	} else if (omultlinecnt) {
696 		omultlinerep(savecnt, startsrcline, endsrccol);
697 #ifdef XPG6
698 	} else if (savecnt > 1 && ch == 'r' && gotNL) {
699 		/*
700 		 * XPG6 assertion 313 & 254 : Position cursor for [count]r\n
701 		 * then insert [count -1] newlines.
702 		 */
703 		endsrccol = gcursor - genbuf - 1;
704 		rmultlinerep(savecnt, endsrccol);
705 #endif /* XPG6 */
706 	}
707 
708 	/*
709 	 * All done with insertion, position the cursor
710 	 * and sync the screen.
711 	 */
712 	hold = oldhold;
713 	if ((imultlinecnt && gotNL) || omultlinecnt) {
714 		fixdisplay();
715 #ifdef XPG6
716 	} else if (savecnt > 1 && ch == 'r' && gotNL) {
717 		fixdisplay();
718 		/*
719 		 * XPG6 assertion 313 & 254 [count]r\n : Set flag to call
720 		 * fixdisplay() after operate() has finished.  To be sure that
721 		 * the text (after the last \n followed by an indent) is always
722 		 * displayed, fixdisplay() is called right before getting
723 		 * the next command.
724 		 */
725 		redisplay = 1;
726 #endif /* XPG6 */
727 	} else if (cursor > linebuf) {
728 		cursor = lastchr(linebuf, cursor);
729 #ifdef XPG6
730 		/*
731 		 * XPG6 assertion 313 & 254 [count]r\n :
732 		 * For 'r' command, when the replacement char causes new
733 		 * lines to be created, point cursor to first non-blank.
734 		 * The old code, ie: cursor = lastchr(linebuf, cursor);
735 		 * set cursor to the blank before the first non-blank
736 		 * for r\n
737 		 */
738 		if (ch == 'r' && gotNL && isblank((int)*cursor))
739 			++cursor;
740 #endif /* XPG6 */
741 	}
742 	if (state != HARDOPEN)
743 		vsyncCL();
744 	else if (cursor > linebuf)
745 		back1();
746 	doomed = 0;
747 	wcursor = cursor;
748 	(void) vmove();
749 }
750 
751 /*
752  * XPG6
753  * To repeat multi-line input for [count]a, [count]A, [count]i, [count]I,
754  * or a subsequent [count]. :
755  * insert input count-1 more times.
756  */
757 
758 static void
759 imultlinerep(int savecnt, line *startsrcline, int startsrccol, int endsrccol)
760 {
761 	int tmpcnt = 2;	/* 1st insert counts as 1 repeat */
762 	line *srcline, *endsrcline;
763 	size_t destsize = LBSIZE - endsrccol - 1;
764 
765 	endsrcline = dot;
766 
767 	/* Save linebuf into temp file before moving off the line. */
768 	vsave();
769 
770 	/*
771 	 * At this point the temp file contains the first iteration of
772 	 * a multi-line insert, and we need to repeat it savecnt - 1
773 	 * more times in the temp file.  dot is the last line in the
774 	 * first iteration of the insert.  Decrement dot so that
775 	 * vdoappend() will append each new line before the last line.
776 	 */
777 	--dot;
778 	--vcline;
779 	/*
780 	 * Use genbuf to rebuild the last line in the 1st iteration
781 	 * of the repeated insert, then copy this line to the temp file.
782 	 */
783 	(void) strlcpy((char *)genbuf, (char *)linebuf, sizeof (genbuf));
784 	getline(*startsrcline);
785 	if (strlcpy((char *)(genbuf + endsrccol + 1),
786 	    (char *)(linebuf + startsrccol), destsize) >= destsize) {
787 		error(gettext("Line too long"));
788 	}
789 	vdoappend(genbuf);
790 	vcline++;
791 	/*
792 	 * Loop from the second line of the first iteration
793 	 * through endsrcline, appending after dot.
794 	 */
795 	++startsrcline;
796 
797 	while (tmpcnt <= savecnt) {
798 		for (srcline = startsrcline; srcline <= endsrcline;
799 		    ++srcline) {
800 			if ((tmpcnt == savecnt) &&
801 			    (srcline == endsrcline)) {
802 				/*
803 				 * The last line is already in place,
804 				 * just make it the current line.
805 				 */
806 				vcline++;
807 				dot++;
808 				getDOT();
809 				cursor = linebuf + endsrccol;
810 			} else {
811 				getline(*srcline);
812 				/* copy linebuf to temp file */
813 				vdoappend(linebuf);
814 				vcline++;
815 			}
816 		}
817 		++tmpcnt;
818 	}
819 }
820 
821 /*
822  * To repeat input for [count]o, [count]O, or a subsequent [count]. :
823  * append input count-1 more times to the end of the already added
824  * text, each time starting on a new line.
825  */
826 
827 static void
828 omultlinerep(int savecnt, line *startsrcline, int endsrccol)
829 {
830 	int tmpcnt = 2;	/* 1st insert counts as 1 repeat */
831 	line *srcline, *endsrcline;
832 
833 	endsrcline = dot;
834 	/* Save linebuf into temp file before moving off the line. */
835 	vsave();
836 
837 	/*
838 	 * Loop from the first line of the first iteration
839 	 * through endsrcline, appending after dot.
840 	 */
841 	while (tmpcnt <= savecnt) {
842 		for (srcline = startsrcline; srcline <= endsrcline; ++srcline) {
843 			getline(*srcline);
844 			/* copy linebuf to temp file */
845 			vdoappend(linebuf);
846 			vcline++;
847 		}
848 		++tmpcnt;
849 	}
850 	cursor = linebuf + endsrccol;
851 }
852 
853 #ifdef XPG6
854 /*
855  * XPG6 assertion 313 & 254 : To repeat '\n' for [count]r\n
856  * insert '\n' savecnt-1 more times before the already added '\n'.
857  */
858 
859 static void
860 rmultlinerep(int savecnt, int endsrccol)
861 {
862 	int tmpcnt = 2;	/* 1st replacement counts as 1 repeat */
863 
864 	/* Save linebuf into temp file before moving off the line. */
865 	vsave();
866 	/*
867 	 * At this point the temp file contains the line followed by '\n',
868 	 * which is preceded by indentation if autoindent is set.
869 	 * '\n' must be repeated [savecnt - 1] more times in the temp file.
870 	 * dot is the current line containing the '\n'.  Decrement dot so that
871 	 * vdoappend() will append each '\n' before the current '\n'.
872 	 * This will allow only the last line to contain any autoindent
873 	 * characters.
874 	 */
875 	--dot;
876 	--vcline;
877 
878 	/*
879 	 * Append after dot.
880 	 */
881 	while (tmpcnt <= savecnt) {
882 		linebuf[0] = '\0';
883 		/* append linebuf below current line in temp file */
884 		vdoappend(linebuf);
885 		vcline++;
886 		++tmpcnt;
887 	}
888 	/* set the current line to the line after the last '\n' */
889 	++dot;
890 	++vcline;
891 	/* point cursor after (linebuf + endsrccol) */
892 	vcursaft(linebuf + endsrccol);
893 }
894 #endif /* XPG6 */
895 
896 /*
897  * Similiar to a ctrl-l, however always vrepaint() in case the last line
898  * of the repeat would exceed the bottom of the screen.
899  */
900 
901 void
902 fixdisplay(void)
903 {
904 	vclear();
905 	vdirty(0, vcnt);
906 	if (state != VISUAL) {
907 		vclean();
908 		vcnt = 0;
909 		vmoveto(dot, cursor, 0);
910 	} else {
911 		vredraw(WTOP);
912 		vrepaint(cursor);
913 		vfixcurs();
914 	}
915 }
916 
917 /*
918  * Subroutine for vgetline to back up a single character position,
919  * backwards around end of lines (vgoto can't hack columns which are
920  * less than 0 in general).
921  */
922 void
923 back1(void)
924 {
925 
926 	vgoto(destline - 1, WCOLS + destcol - 1);
927 }
928 
929 /*
930  * Get a line into genbuf after gcursor.
931  * Cnt limits the number of input characters
932  * accepted and is used for handling the replace
933  * single character command.  Aescaped is the location
934  * where we stick a termination indicator (whether we
935  * ended with an ESCAPE or a newline/return.
936  *
937  * We do erase-kill type processing here and also
938  * are careful about the way we do this so that it is
939  * repeatable.  (I.e. so that your kill doesn't happen,
940  * when you repeat an insert if it was escaped with \ the
941  * first time you did it.  commch is the command character
942  * involved, including the prompt for readline.
943  */
944 unsigned char *
945 vgetline(cnt, gcursor, aescaped, commch)
946 	int cnt;
947 	unsigned char *gcursor;
948 	bool *aescaped;
949 	unsigned char commch;
950 {
951 	int c, ch;
952 	unsigned char *cp, *pcp;
953 	int x, y, iwhite, backsl=0;
954 	unsigned char *iglobp;
955 	int (*OO)() = Outchar;
956 	int length, width;
957 	unsigned char multic[MULTI_BYTE_MAX+1];
958 	wchar_t wchar = 0;
959 	unsigned char	*p;
960 	int	len;
961 
962 
963 	/*
964 	 * Clear the output state and counters
965 	 * for autoindent backwards motion (counts of ^D, etc.)
966 	 * Remember how much white space at beginning of line so
967 	 * as not to allow backspace over autoindent.
968 	 */
969 
970 	*aescaped = 0;
971 	ogcursor = gcursor;
972 	flusho();
973 	CDCNT = 0;
974 	HADUP = 0;
975 	HADZERO = 0;
976 	gobbled = 0;
977 	iwhite = whitecnt(genbuf);
978 	iglobp = vglobp;
979 
980 	/*
981 	 * Clear abbreviation recursive-use count
982 	 */
983 	abbrepcnt = 0;
984 	/*
985 	 * Carefully avoid using vinschar in the echo area.
986 	 */
987 	if (splitw)
988 		Outchar = vputchar;
989 	else {
990 		Outchar = vinschar;
991 		vprepins();
992 	}
993 	for (;;) {
994 		length = 0;
995 		backsl = 0;
996 		if (gobblebl)
997 			gobblebl--;
998 		if (cnt != 0) {
999 			cnt--;
1000 			if (cnt == 0)
1001 				goto vadone;
1002 		}
1003 		c = getkey();
1004 		if (c != ATTN)
1005 			c &= 0377;
1006 		ch = c;
1007 		maphopcnt = 0;
1008 		if (vglobp == 0 && Peekkey == 0 && commch != 'r')
1009 			while ((ch = map(c, immacs, commch)) != c) {
1010 				c = ch;
1011 				if (!value(vi_REMAP))
1012 					break;
1013 				if (++maphopcnt > 256)
1014 					error(gettext("Infinite macro loop"));
1015 			}
1016 		if (!iglobp) {
1017 
1018 			/*
1019 			 * Erase-kill type processing.
1020 			 * Only happens if we were not reading
1021 			 * from untyped input when we started.
1022 			 * Map users erase to ^H, kill to -1 for switch.
1023 			 */
1024 			if (c == tty.c_cc[VERASE])
1025 				c = CTRL('h');
1026 			else if (c == tty.c_cc[VKILL])
1027 				c = -1;
1028 			switch (c) {
1029 
1030 			/*
1031 			 * ^?		Interrupt drops you back to visual
1032 			 *		command mode with an unread interrupt
1033 			 *		still in the input buffer.
1034 			 *
1035 			 * ^\		Quit does the same as interrupt.
1036 			 *		If you are a ex command rather than
1037 			 *		a vi command this will drop you
1038 			 *		back to command mode for sure.
1039 			 */
1040 			case ATTN:
1041 			case QUIT:
1042 				ungetkey(c);
1043 				goto vadone;
1044 
1045 			/*
1046 			 * ^H		Backs up a character in the input.
1047 			 *
1048 			 * BUG:		Can't back around line boundaries.
1049 			 *		This is hard because stuff has
1050 			 *		already been saved for repeat.
1051 			 */
1052 			case CTRL('h'):
1053 bakchar:
1054 				cp = lastchr(ogcursor, gcursor);
1055 				if (cp < ogcursor) {
1056 					if (splitw) {
1057 						/*
1058 						 * Backspacing over readecho
1059 						 * prompt. Pretend delete but
1060 						 * don't beep.
1061 						 */
1062 						ungetkey(c);
1063 						goto vadone;
1064 					}
1065 					(void) beep();
1066 					continue;
1067 				}
1068 				goto vbackup;
1069 
1070 			/*
1071 			 * ^W		Back up a white/non-white word.
1072 			 */
1073 			case CTRL('w'):
1074 				wdkind = 1;
1075 				for (cp = gcursor; cp > ogcursor && isspace(cp[-1]); cp--)
1076 					continue;
1077 				pcp = lastchr(ogcursor, cp);
1078 				for (c = wordch(pcp);
1079 				    cp > ogcursor && wordof(c, pcp); cp = pcp, pcp = lastchr(ogcursor, cp))
1080 					continue;
1081 				goto vbackup;
1082 
1083 			/*
1084 			 * users kill	Kill input on this line, back to
1085 			 *		the autoindent.
1086 			 */
1087 			case -1:
1088 				cp = ogcursor;
1089 vbackup:
1090 				if (cp == gcursor) {
1091 					(void) beep();
1092 					continue;
1093 				}
1094 				endim();
1095 				*cp = 0;
1096 				c = cindent();
1097 				vgotoCL(nqcolumn(lastchr(linebuf, cursor), genbuf));
1098 
1099 				if (doomed >= 0)
1100 					doomed += c - cindent();
1101 				gcursor = cp;
1102 				continue;
1103 
1104 			/*
1105 			 * \		Followed by erase or kill
1106 			 *		maps to just the erase or kill.
1107 			 */
1108 			case '\\':
1109 				x = destcol, y = destline;
1110 				putchar('\\');
1111 				vcsync();
1112 				c = getkey();
1113 				if (c == tty.c_cc[VERASE]
1114 				    || c == tty.c_cc[VKILL])
1115 				{
1116 					vgoto(y, x);
1117 					if (doomed >= 0)
1118 						doomed++;
1119 					multic[0] = wchar = c;
1120 					length = 1;
1121 					goto def;
1122 				}
1123 				ungetkey(c), c = '\\';
1124 				backsl = 1;
1125 				break;
1126 
1127 			/*
1128 			 * ^Q		Super quote following character
1129 			 *		Only ^@ is verboten (trapped at
1130 			 *		a lower level) and \n forces a line
1131 			 *		split so doesn't really go in.
1132 			 *
1133 			 * ^V		Synonym for ^Q
1134 			 */
1135 			case CTRL('q'):
1136 			case CTRL('v'):
1137 				x = destcol, y = destline;
1138 				putchar('^');
1139 				vgoto(y, x);
1140 				c = getkey();
1141 #ifdef USG
1142 				if (c == ATTN)
1143 					c = tty.c_cc[VINTR];
1144 #endif
1145 				if (c != NL) {
1146 					if (doomed >= 0)
1147 						doomed++;
1148 					multic[0] = wchar = c;
1149 					length = 1;
1150 					goto def;
1151 				}
1152 				break;
1153 			}
1154 		}
1155 
1156 		/*
1157 		 * If we get a blank not in the echo area
1158 		 * consider splitting the window in the wrapmargin.
1159 		 */
1160 		if(!backsl) {
1161 			ungetkey(c);
1162 			if((length = _mbftowc((char *)multic, &wchar, getkey, &Peekkey)) <= 0) {
1163 				(void) beep();
1164 				continue;
1165 			}
1166 		} else {
1167 			length = 1;
1168 			multic[0] = '\\';
1169 		}
1170 
1171 		if (c != NL && !splitw) {
1172 			if (c == ' ' && gobblebl) {
1173 				gobbled = 1;
1174 				continue;
1175 			}
1176 			if ((width = wcwidth(wchar)) <= 0)
1177 				width = (wchar <= 0177 ? 1 : 4);
1178 			if (value(vi_WRAPMARGIN) &&
1179 				(outcol + width - 1 >= OCOLUMNS - value(vi_WRAPMARGIN) ||
1180 				 backsl && outcol==0) &&
1181 				commch != 'r') {
1182 				/*
1183 				 * At end of word and hit wrapmargin.
1184 				 * Move the word to next line and keep going.
1185 				 */
1186 				unsigned char *wp;
1187 				int bytelength;
1188 #ifndef PRESUNEUC
1189 				unsigned char *tgcursor;
1190 				wchar_t wc1, wc2;
1191 				tgcursor = gcursor;
1192 #endif /* PRESUNEUC */
1193 				wdkind = 1;
1194 				strncpy(gcursor, multic, length);
1195 				gcursor += length;
1196 				if (backsl) {
1197 #ifdef PRESUNEUC
1198 					if((length = mbftowc((char *)multic, &wchar, getkey, &Peekkey)) <= 0) {
1199 #else
1200 					if((length = _mbftowc((char *)multic, &wchar, getkey, &Peekkey)) <= 0) {
1201 #endif /* PRESUNEUC */
1202 						(void) beep();
1203 						continue;
1204 					}
1205 					strncpy(gcursor, multic, length);
1206 					gcursor += length;
1207 				}
1208 				*gcursor = 0;
1209 				/*
1210 				 * Find end of previous word if we are past it.
1211 				 */
1212 				for (cp=gcursor; cp>ogcursor && isspace(cp[-1]); cp--)
1213 					;
1214 #ifdef PRESUNEUC
1215 				/* find screen width of previous word */
1216 				width = 0;
1217 				for(wp = cp; *wp; )
1218 #else
1219 				/* count screen width of pending characters */
1220 				width = 0;
1221 				for(wp = tgcursor; wp < cp;)
1222 #endif /* PRESUNEUC */
1223 					if((bytelength = mbtowc(&wchar, (char *)wp, MULTI_BYTE_MAX)) < 0) {
1224 						width+=4;
1225 						wp++;
1226 					} else {
1227 						int curwidth = wcwidth(wchar);
1228 						if(curwidth <= 0)
1229 							width += (*wp < 0200 ? 2 : 4);
1230 						else
1231 							width += curwidth;
1232 						wp += bytelength;
1233 					}
1234 
1235 #ifdef PRESUNEUC
1236 				if (outcol+(backsl?OCOLUMNS:0) - width >= OCOLUMNS - value(vi_WRAPMARGIN)) {
1237 #else
1238 				if (outcol+(backsl?OCOLUMNS:0) + width -1 >= OCOLUMNS - value(vi_WRAPMARGIN)) {
1239 #endif /* PRESUNEUC */
1240 					/*
1241 					 * Find beginning of previous word.
1242 					 */
1243 #ifdef PRESUNEUC
1244 					for (; cp>ogcursor && !isspace(cp[-1]); cp--)
1245 						;
1246 #else
1247 					wc1 = wc2 = 0;
1248 					while (cp>ogcursor) {
1249 						if (isspace(cp[-1])) {
1250 							break;
1251 						}
1252 						if (!multibyte) {
1253 							cp--;
1254 							continue;
1255 						}
1256 						wp = (unsigned char *)(cp -
1257 							MB_CUR_MAX);
1258 						if (wp < ogcursor)
1259 							wp = ogcursor;
1260 						while (cp > wp) {
1261 /* 7tabs */if (wc2) {
1262 /* 7tabs */	if ((bytelength = mbtowc(&wc1, (char *)wp, cp-wp)) != cp-wp) {
1263 /* 7tabs */		wp++;
1264 /* 7tabs */		wc1 = 0;
1265 /* 7tabs */		continue;
1266 /* 7tabs */	}
1267 /* 7tabs */} else {
1268 /* 7tabs */	if ((bytelength = mbtowc(&wc2, (char *)wp, cp-wp)) != cp-wp) {
1269 /* 7tabs */		wp++;
1270 /* 7tabs */		wc2 = 0;
1271 /* 7tabs */		continue;
1272 /* 7tabs */	}
1273 /* 7tabs */}
1274 /* 7tabs */if (wc1) {
1275 /* 7tabs */	if (wdbdg && (!iswascii(wc1) || !iswascii(wc2))) {
1276 /* 7tabs */		if ((*wdbdg)(wc1, wc2, 2) < 5) {
1277 /* 7tabs */			goto ws;
1278 /* 7tabs */		}
1279 /* 7tabs */	}
1280 /* 7tabs */	wc2 = wc1;
1281 /* 7tabs */	wc1 = 0;
1282 /* 7tabs */	cp -= bytelength - 1;
1283 /* 7tabs */	break;
1284 /* 7tabs */} else {
1285 /* 7tabs */	cp -= bytelength - 1;
1286 /* 7tabs */	break;
1287 /* 7tabs */}
1288 						}
1289 						cp--;
1290 					}
1291 ws:
1292 #endif /* PRESUNEUC */
1293 					if (cp <= ogcursor) {
1294 						/*
1295 						 * There is a single word that
1296 						 * is too long to fit.  Just
1297 						 * let it pass, but beep for
1298 						 * each new letter to warn
1299 						 * the luser.
1300 						 */
1301 						gcursor -= length;
1302 						c = *gcursor;
1303 						*gcursor = 0;
1304 						(void) beep();
1305 						goto dontbreak;
1306 					}
1307 					/*
1308 					 * Save it for next line.
1309 					 */
1310 					macpush(cp, 0);
1311 #ifdef PRESUNEUC
1312 					cp--;
1313 #endif /* PRESUNEUC */
1314 				}
1315 				macpush("\n", 0);
1316 				/*
1317 				 * Erase white space before the word.
1318 				 */
1319 				while (cp > ogcursor && isspace(cp[-1]))
1320 					cp--;	/* skip blank */
1321 				gobblebl = 3;
1322 				goto vbackup;
1323 			}
1324 		dontbreak:;
1325 		}
1326 
1327 		/*
1328 		 * Word abbreviation mode.
1329 		 */
1330 		if (anyabbrs && gcursor > ogcursor && !wordch(multic) && wordch(lastchr(ogcursor, gcursor))) {
1331 				int wdtype, abno;
1332 
1333 				multic[length] = 0;
1334 				wdkind = 1;
1335 				cp = lastchr(ogcursor, gcursor);
1336 				pcp = lastchr(ogcursor, cp);
1337 				for (wdtype = wordch(pcp);
1338 				    cp > ogcursor && wordof(wdtype, pcp); cp = pcp, pcp = lastchr(ogcursor, pcp))
1339 					;
1340 				*gcursor = 0;
1341 				for (abno=0; abbrevs[abno].mapto; abno++) {
1342 					if (eq(cp, abbrevs[abno].cap)) {
1343 						if(abbrepcnt == 0) {
1344 							if(reccnt(abbrevs[abno].cap, abbrevs[abno].mapto))
1345 								abbrepcnt = 1;
1346 							macpush(multic, 0);
1347 							macpush(abbrevs[abno].mapto);
1348 							goto vbackup;
1349 						} else
1350 							abbrepcnt = 0;
1351 					}
1352 				}
1353 		}
1354 
1355 		switch (c) {
1356 
1357 		/*
1358 		 * ^M		Except in repeat maps to \n.
1359 		 */
1360 		case CR:
1361 			if (vglobp) {
1362 				multic[0] = wchar = c;
1363 				length = 1;
1364 				goto def;
1365 			}
1366 			c = '\n';
1367 			/* presto chango ... */
1368 
1369 		/*
1370 		 * \n		Start new line.
1371 		 */
1372 		case NL:
1373 			*aescaped = c;
1374 			goto vadone;
1375 
1376 		/*
1377 		 * escape	End insert unless repeat and more to repeat.
1378 		 */
1379 		case ESCAPE:
1380 			if (lastvgk) {
1381 				multic[0] = wchar = c;
1382 				length = 1;
1383 				goto def;
1384 			}
1385 			goto vadone;
1386 
1387 		/*
1388 		 * ^D		Backtab.
1389 		 * ^T		Software forward tab.
1390 		 *
1391 		 *		Unless in repeat where this means these
1392 		 *		were superquoted in.
1393 		 */
1394 		case CTRL('t'):
1395 			if (vglobp) {
1396 				multic[0] = wchar = c;
1397 				length = 1;
1398 				goto def;
1399 			}
1400 			/* fall into ... */
1401 
1402 			*gcursor = 0;
1403 			cp = vpastwh(genbuf);
1404 			c = whitecnt(genbuf);
1405 			if (ch == CTRL('t')) {
1406 				/*
1407 				 * ^t just generates new indent replacing
1408 				 * current white space rounded up to soft
1409 				 * tab stop increment.
1410 				 */
1411 				if (cp != gcursor)
1412 					/*
1413 					 * BUG:		Don't hack ^T except
1414 					 *		right after initial
1415 					 *		white space.
1416 					 */
1417 					continue;
1418 				cp = genindent(iwhite = backtab(c + value(vi_SHIFTWIDTH) + 1));
1419 				ogcursor = cp;
1420 				goto vbackup;
1421 			}
1422 			/*
1423 			 * ^D works only if we are at the (end of) the
1424 			 * generated autoindent.  We count the ^D for repeat
1425 			 * purposes.
1426 			 */
1427 		case CTRL('d'):
1428 			/* check if ^d was superquoted in */
1429 			if(vglobp && inscdcnt <= 0) {
1430 				multic[0] = wchar = c;
1431 				length = 1;
1432 				goto def;
1433 			}
1434 			if(vglobp)
1435 				inscdcnt--;
1436 			*gcursor = 0;
1437 			cp = vpastwh(genbuf);
1438 			c = whitecnt(genbuf);
1439 			if (c == iwhite && c != 0)
1440 				if (cp == gcursor) {
1441 					iwhite = backtab(c);
1442 					CDCNT++;
1443 					ogcursor = cp = genindent(iwhite);
1444 					goto vbackup;
1445 				} else if (&cp[1] == gcursor &&
1446 				    (*cp == '^' || *cp == '0')) {
1447 					/*
1448 					 * ^^D moves to margin, then back
1449 					 * to current indent on next line.
1450 					 *
1451 					 * 0^D moves to margin and then
1452 					 * stays there.
1453 					 */
1454 					HADZERO = *cp == '0';
1455 					ogcursor = cp = genbuf;
1456 					HADUP = 1 - HADZERO;
1457 					CDCNT = 1;
1458 					endim();
1459 					back1();
1460 					(void) vputchar(' ');
1461 					goto vbackup;
1462 				}
1463 
1464 			if (vglobp && vglobp - iglobp >= 2) {
1465 				if ((p = vglobp - MB_CUR_MAX) < iglobp)
1466 					p = iglobp;
1467 				for ( ; p < &vglobp[-2]; p += len) {
1468 					if ((len = mblen((char *)p, MB_CUR_MAX)) <= 0)
1469 						len = 1;
1470 				}
1471 				if ((p == &vglobp[-2]) &&
1472 			            (*p == '^' || *p == '0') &&
1473 			            gcursor == ogcursor + 1)
1474 					goto bakchar;
1475 			}
1476 			continue;
1477 
1478 		default:
1479 			/*
1480 			 * Possibly discard control inputs.
1481 			 */
1482 			if (!vglobp && junk(c)) {
1483 				(void) beep();
1484 				continue;
1485 			}
1486 def:
1487 			if (!backsl) {
1488 				putchar(wchar);
1489 				flush();
1490 			}
1491 			if (gcursor + length - 1 > &genbuf[LBSIZE - 2])
1492 				error(gettext("Line too long"));
1493 			(void)strncpy(gcursor, multic, length);
1494 			gcursor += length;
1495 			vcsync();
1496 			if (value(vi_SHOWMATCH) && !iglobp)
1497 				if (c == ')' || c == '}')
1498 					lsmatch(gcursor);
1499 			continue;
1500 		}
1501 	}
1502 vadone:
1503 	*gcursor = 0;
1504 	if (Outchar != termchar)
1505 		Outchar = OO;
1506 	endim();
1507 	return (gcursor);
1508 }
1509 
1510 int	vgetsplit();
1511 unsigned char	*vsplitpt;
1512 
1513 /*
1514  * Append the line in buffer at lp
1515  * to the buffer after dot.
1516  */
1517 void
1518 vdoappend(unsigned char *lp)
1519 {
1520 	int oing = inglobal;
1521 
1522 	vsplitpt = lp;
1523 	inglobal = 1;
1524 	(void)append(vgetsplit, dot);
1525 	inglobal = oing;
1526 }
1527 
1528 /*
1529  * Subroutine for vdoappend to pass to append.
1530  */
1531 int
1532 vgetsplit(void)
1533 {
1534 
1535 	if (vsplitpt == 0)
1536 		return (EOF);
1537 	strcLIN(vsplitpt);
1538 	vsplitpt = 0;
1539 	return (0);
1540 }
1541 
1542 /*
1543  * Vmaxrep determines the maximum repetition factor
1544  * allowed that will yield total line length less than
1545  * LBSIZE characters and also does hacks for the R command.
1546  */
1547 int
1548 vmaxrep(unsigned char ch, int cnt)
1549 {
1550 	int len;
1551 	unsigned char *cp;
1552 	int repcnt, oldcnt, replen;
1553 	if (cnt > LBSIZE - 2)
1554 		cnt = LBSIZE - 2;
1555 	if (ch == 'R') {
1556 		len = strlen(cursor);
1557 		oldcnt = 0;
1558 		for(cp = cursor; *cp; ) {
1559 			oldcnt++;
1560 			cp = nextchr(cp);
1561 		}
1562 		repcnt = 0;
1563 		for(cp = genbuf; *cp; ) {
1564 			repcnt++;
1565 			cp = nextchr(cp);
1566 		}
1567 		/*
1568 		 * if number of characters in replacement string
1569 		 * (repcnt) is less than number of characters following
1570 		 * cursor (oldcnt), find end of repcnt
1571 		 * characters after cursor
1572 		 */
1573 		if(repcnt < oldcnt) {
1574 			for(cp = cursor; repcnt > 0; repcnt--)
1575 				cp = nextchr(cp);
1576 			len = cp - cursor;
1577 		}
1578 		CP(cursor, cursor + len);
1579 		vUD2 += len;
1580 	}
1581 	len = strlen(linebuf);
1582 	replen = strlen(genbuf);
1583 	if (len + cnt * replen <= LBSIZE - 2)
1584 		return (cnt);
1585 	cnt = (LBSIZE - 2 - len) / replen;
1586 	if (cnt == 0) {
1587 		vsave();
1588 		error(gettext("Line too long"));
1589 	}
1590 	return (cnt);
1591 }
1592 
1593 /*
1594  * Determine how many occurrences of word 'CAP' are in 'MAPTO'.  To be
1595  * considered an occurrence there must be both a nonword-prefix, a
1596  * complete match of 'CAP' within 'MAPTO', and a nonword-suffix.
1597  * Note that the beginning and end of 'MAPTO' are considered to be
1598  * valid nonword delimiters.
1599  */
1600 int
1601 reccnt(unsigned char *cap, unsigned char *mapto)
1602 {
1603 	int i, cnt, final;
1604 
1605 	cnt = 0;
1606 	final = strlen(mapto) - strlen(cap);
1607 
1608 	for (i=0; i <= final; i++)
1609 	  if ((strncmp(cap, mapto+i, strlen(cap)) == 0)       /* match */
1610 	  && (i == 0     || !wordch(&mapto[i-1]))	      /* prefix ok */
1611 	  && (i == final || !wordch(&mapto[i+strlen(cap)])))  /* suffix ok */
1612 		cnt++;
1613 	return (cnt);
1614 }
1615 
1616