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