OpenSolaris_b135/cmd/vi/port/ex_vops2.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/


/* Copyright (c) 1981 Regents of the University of California */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include "ex.h"
#include "ex_tty.h"
#include "ex_vis.h"
#ifndef PRESUNEUC
#include <wctype.h>
/* Undef putchar/getchar if they're defined. */
#ifdef putchar
#	undef putchar
#endif
#ifdef getchar
#	undef getchar
#endif
#endif /* PRESUNEUC */

extern size_t strlcpy(char *, const char *, size_t);

/*
 * Low level routines for operations sequences,
 * and mostly, insert mode (and a subroutine
 * to read an input line, including in the echo area.)
 */
extern unsigned char	*vUA1, *vUA2;		/* extern; also in ex_vops.c */
extern unsigned char	*vUD1, *vUD2;		/* extern; also in ex_vops.c */

#ifdef XPG6
/* XPG6 assertion 313 & 254 [count]r\n :  Also used in ex_vmain.c */
extern int redisplay;
#endif

int vmaxrep(unsigned char, int);
static void imultlinerep(int, line *, int, int);
static void omultlinerep(int, line *, int);
#ifdef XPG6
static void rmultlinerep(int, int);
#endif
void fixdisplay(void);

/*
 * Obleeperate characters in hardcopy
 * open with \'s.
 */
void
bleep(int i, unsigned char *cp)
{

	i -= lcolumn(nextchr(cp));
	do
		putchar('\\' | QUOTE);
	while (--i >= 0);
	rubble = 1;
}

/*
 * Common code for middle part of delete
 * and change operating on parts of lines.
 */
int
vdcMID(void)
{
	unsigned char *cp;

	squish();
	setLAST();
	if (FIXUNDO)
		vundkind = VCHNG, CP(vutmp, linebuf);
	if (wcursor < cursor)
		cp = wcursor, wcursor = cursor, cursor = cp;
	vUD1 = vUA1 = vUA2 = cursor; vUD2 = wcursor;
	/*
	 * XPG6 assertion 273: Set vmcurs so that undo positions the
	 * cursor column correctly when we've moved off the initial line
	 * that was changed, as with the C, c, and s commands,
	 * when G has moved us off the line, or when a
	 * multi-line change was done.
	 */
	fixundo();
	return (lcolumn(wcursor));
}

/*
 * Take text from linebuf and stick it
 * in the VBSIZE buffer BUF.  Used to save
 * deleted text of part of line.
 */
void
takeout(unsigned char *BUF)
{
	unsigned char *cp;

	if (wcursor < linebuf)
		wcursor = linebuf;
	if (cursor == wcursor) {
		(void) beep();
		return;
	}
	if (wcursor < cursor) {
		cp = wcursor;
		wcursor = cursor;
		cursor = cp;
	}
	setBUF(BUF);
	if ((unsigned char)BUF[128] == 0200)
		(void) beep();
}

/*
 * Are we at the end of the printed representation of the
 * line?  Used internally in hardcopy open.
 */
int
ateopr(void)
{
	wchar_t i, c;
	wchar_t *cp = vtube[destline] + destcol;

	for (i = WCOLS - destcol; i > 0; i--) {
		c = *cp++;
		if (c == 0) {
			/*
			 * Optimization to consider returning early, saving
			 * CPU time.  We have to make a special check that
			 * we aren't missing a mode indicator.
			 */
			if (destline == WECHO && destcol < WCOLS-11 && vtube[WECHO][WCOLS-20])
				return 0;
			return (1);
		}
		if (c != ' ' && (c & QUOTE) == 0)
			return (0);
	}
	return (1);
}

/*
 * Append.
 *
 * This routine handles the top level append, doing work
 * as each new line comes in, and arranging repeatability.
 * It also handles append with repeat counts, and calculation
 * of autoindents for new lines.
 */
bool	vaifirst;
bool	gobbled;
unsigned char	*ogcursor;

static int 	INSCDCNT; /* number of ^D's (backtabs) in insertion buffer */

static int 	inscdcnt; /* 
			   * count of ^D's (backtabs) not seen yet when doing
		 	   * repeat of insertion
			   */

void
vappend(int ch, int cnt, int indent)
{
	int i;
	unsigned char *gcursor;
	bool escape;
	int repcnt, savedoomed;
	short oldhold = hold;
	int savecnt = cnt;
	line *startsrcline;
	int startsrccol, endsrccol;
	int gotNL = 0;
	int imultlinecnt = 0;
	int omultlinecnt = 0;

	if ((savecnt > 1) && (ch == 'o' || ch == 'O')) {
		omultlinecnt = 1;
	}
#ifdef XPG6
	if ((savecnt > 1) && (ch == 'a' || ch == 'A' || ch == 'i' || ch == 'I'))
		imultlinecnt = 1;
#endif /* XPG6 */

	/*
	 * Before a move in hardopen when the line is dirty
	 * or we are in the middle of the printed representation,
	 * we retype the line to the left of the cursor so the
	 * insert looks clean.
	 */

	if (ch != 'o' && state == HARDOPEN && (rubble || !ateopr())) {
		rubble = 1;
		gcursor = cursor;
		i = *gcursor;
		*gcursor = ' ';
		wcursor = gcursor;
		(void) vmove();
		*gcursor = i;
	}
	/*
	 * If vrep() passed indent = 0, this is the 'r' command,
	 * so don't autoindent until the last char.
	 */
	vaifirst = indent == 0;

	/*
	 * Handle replace character by (eventually)
	 * limiting the number of input characters allowed
	 * in the vgetline routine.
	 */
	if (ch == 'r')
		repcnt = 2;
	else
		repcnt = 0;

	/*
	 * If an autoindent is specified, then
	 * generate a mixture of blanks to tabs to implement
	 * it and place the cursor after the indent.
	 * Text read by the vgetline routine will be placed in genbuf,
	 * so the indent is generated there.
	 */
	if (value(vi_AUTOINDENT) && indent != 0) {
		unsigned char x;
		gcursor = genindent(indent);
		*gcursor = 0;
		vgotoCL(nqcolumn(lastchr(linebuf, cursor), genbuf)); 
	} else {
		gcursor = genbuf;
		*gcursor = 0;
		if (ch == 'o')
			vfixcurs();
	}

	/*
	 * Prepare for undo.  Pointers delimit inserted portion of line.
	 */
	vUA1 = vUA2 = cursor;

	/*
	 * If we are not in a repeated command and a ^@ comes in
	 * then this means the previous inserted text.
	 * If there is none or it was too long to be saved,
	 * then beep() and also arrange to undo any damage done
	 * so far (e.g. if we are a change.)
	 */
	switch (ch) {
	case 'r':
		break;
	case 'a':
		/*
		 * TRANSLATION_NOTE
		 *	"A" is a terse mode message corresponding to
		 *	"APPEND MODE".
		 *	Translated message of "A" must be 1 character (not byte).
		 *	Or, just leave it.
		 */
		if (value(vi_TERSE)) {
			vshowmode(gettext("A"));
		} else {
			vshowmode(gettext("APPEND MODE"));
		}
		break;
	case 's':
		/*
		 * TRANSLATION_NOTE
		 *	"S" is a terse mode message corresponding to
		 *	"SUBSTITUTE MODE".
		 *	Translated message of "S" must be 1 character (not byte).
		 *	Or, just leave it.
		 */
		if (value(vi_TERSE)) {
			vshowmode(gettext("S"));
		} else {
			vshowmode(gettext("SUBSTITUTE MODE"));
		}
		break;
	case 'c':
		/*
		 * TRANSLATION_NOTE
		 *	"C" is a terse mode message corresponding to
		 *	"CHANGE MODE".
		 *	Translated message of "C" must be 1 character (not byte).
		 *	Or, just leave it.
		 */
		if (value(vi_TERSE)) {
			vshowmode(gettext("C"));
		} else {
			vshowmode(gettext("CHANGE MODE"));
		}
		break;
	case 'R':
		/*
		 * TRANSLATION_NOTE
		 *	"R" is a terse mode message corresponding to
		 *	"REPLACE MODE".
		 *	Translated message of "R" must be 1 character (not byte).
		 *	Or, just leave it.
		 */
		if (value(vi_TERSE)) {
			vshowmode(gettext("R"));
		} else {
			vshowmode(gettext("REPLACE MODE"));
		}
		break;
	case 'o':
		/*
		 * TRANSLATION_NOTE
		 *	"O" is a terse mode message corresponding to
		 *	"OPEN MODE".
		 *	Translated message of "O" must be 1 character (not byte).
		 *	Or, just leave it.
		 */
		if (value(vi_TERSE)) {
			vshowmode(gettext("O"));
		} else {
			vshowmode(gettext("OPEN MODE"));
		}
		break;
	case 'i':
		/*
		 * TRANSLATION_NOTE
		 *	"I" is a terse mode message corresponding to
		 *	"INSERT MODE" and the following "INPUT MODE".
		 *	Translated message of "I" must be 1 character (not byte).
		 *	Or, just leave it.
		 */
		if (value(vi_TERSE)) {
			vshowmode(gettext("I"));
		} else {
			vshowmode(gettext("INSERT MODE"));
		}
		break;
	default:
		/*
		 * TRANSLATION_NOTE
		 *	"I" is a terse mode message corresponding to
		 *	"INPUT MODE" and the previous "INSERT MODE".
		 *	Translated message of "I" must be 1 character (not byte).
		 *	Or, just leave it.
		 */
		if (value(vi_TERSE)) {
			vshowmode(gettext("I"));
		} else {
			vshowmode(gettext("INPUT MODE"));
		}
	}
	ixlatctl(1);
	if ((vglobp && *vglobp == 0) || peekbr()) {
		if (INS[128] == 0200) {
			(void) beep();
			if (!splitw)
				ungetkey('u');
			doomed = 0;
			hold = oldhold;
			return;
		}
		/*
		 * Unread input from INS.
		 * An escape will be generated at end of string.
		 * Hold off n^^2 type update on dumb terminals.
		 */
		vglobp = INS;
		inscdcnt = INSCDCNT;
		hold |= HOLDQIK;
	} else if (vglobp == 0) {
		/*
		 * Not a repeated command, get
		 * a new inserted text for repeat.
		 */
		INS[0] = 0;
		INS[128] = 0;
		INSCDCNT = 0;
	}

	/*
	 * For wrapmargin to hack away second space after a '.'
	 * when the first space caused a line break we keep
	 * track that this happened in gobblebl, which says
	 * to gobble up a blank silently.
	 */
	gobblebl = 0;

	startsrcline = dot;
	startsrccol = cursor - linebuf;

	/*
	 * Text gathering loop.
	 * New text goes into genbuf starting at gcursor.
	 * cursor preserves place in linebuf where text will eventually go.
	 */
	if (*cursor == 0 || state == CRTOPEN)
		hold |= HOLDROL;
	for (;;) {
		if (ch == 'r' && repcnt == 0)
			escape = 0;
		else {
			ixlatctl(1);
			/*
			 * When vgetline() returns, gcursor is
			 * pointing to '\0' and vgetline() has
			 * read an ESCAPE or NL.
			 */
			gcursor = vgetline(repcnt, gcursor, &escape, ch);
			if (escape == '\n') {
				gotNL = 1;
#ifdef XPG6
				if (ch == 'r') {
					/*
					 * XPG6 assertion 313 [count]r\n :
					 * Arrange to set cursor correctly.
					 */
					endsrccol = gcursor - genbuf - 1;
				}
#endif /* XPG6 */
			} else {
				/*
				 * Upon escape, gcursor is pointing to '\0'
				 * terminating the string in genbuf.
				 */
				endsrccol = gcursor - genbuf - 1;
			}
			ixlatctl(0);

			/*
			 * After an append, stick information
			 * about the ^D's and ^^D's and 0^D's in
			 * the repeated text buffer so repeated
			 * inserts of stuff indented with ^D as backtab's
			 * can work.
			 */
			if (HADUP)
				addtext("^");
			else if (HADZERO)
				addtext("0");
			if(!vglobp)
				INSCDCNT = CDCNT;
			while (CDCNT > 0) {
				addtext("\004");
				CDCNT--;
			}
			if (gobbled)
				addtext(" ");
			addtext(ogcursor);
		}
		repcnt = 0;

		/*
		 * Smash the generated and preexisting indents together
		 * and generate one cleanly made out of tabs and spaces
		 * if we are using autoindent and this isn't 'r' command.
		 */
		if (!vaifirst && value(vi_AUTOINDENT)) {
			i = fixindent(indent);
			if (!HADUP)
				indent = i;
			gcursor = strend(genbuf);
		}

		/*
		 * Set cnt to 1 to avoid repeating the text on the same line.
		 * Do this for commands 'i', 'I', 'a', and 'A', if we're
		 * inserting anything with a newline for XPG6.  Always do this
		 * for commands 'o' and 'O'.
		 */
		if ((imultlinecnt && gotNL) || omultlinecnt) {
			cnt = 1;
		}

		/*
		 * Limit the repetition count based on maximum
		 * possible line length; do output implied
		 * by further count (> 1) and cons up the new line
		 * in linebuf.
		 */
		cnt = vmaxrep(ch, cnt);
		/*
		 * cursor points to linebuf
		 * Copy remaining old text (cursor) in original
		 * line to after new text (gcursor + 1) in genbuf.
		 */
		CP(gcursor + 1, cursor);
		/*
		 * For [count] r \n command, when replacing [count] chars
		 * with '\n', this loop replaces [count] chars with "".
		 */
		do {
			/* cp new text (genbuf) into linebuf (cursor) */
			CP(cursor, genbuf);
			if (cnt > 1) {
				int oldhold = hold;

				Outchar = vinschar;
				hold |= HOLDQIK;
				viprintf("%s", genbuf);
				hold = oldhold;
				Outchar = vputchar;
			}
			/* point cursor after new text in linebuf */
			cursor += gcursor - genbuf;
		} while (--cnt > 0);
		endim();
		vUA2 = cursor;
		/* add the remaining old text after the cursor */
		if (escape != '\n')
			CP(cursor, gcursor + 1);

		/*
		 * If doomed characters remain, clobber them,
		 * and reopen the line to get the display exact.
		 * eg. c$ to change to end of line
		 */
		if (state != HARDOPEN) {
			DEPTH(vcline) = 0;
			savedoomed = doomed;
			if (doomed > 0) {
				int cind = cindent();

				physdc(cind, cind + doomed);
				doomed = 0;
			}
			if(MB_CUR_MAX > 1)
				rewrite = _ON;
			i = vreopen(LINE(vcline), lineDOT(), vcline);
			if(MB_CUR_MAX > 1)
				rewrite = _OFF;
#ifdef TRACE
			if (trace)
				fprintf(trace, "restoring doomed from %d to %d\n", doomed, savedoomed);
#endif
			if (ch == 'R')
				doomed = savedoomed;
		}

		/*
		 * Unless we are continuing on to another line
		 * (got a NL), break out of the for loop (got
		 * an ESCAPE).
		 */
		if (escape != '\n') {
			vshowmode("");
			break;
		}

		/*
		 * Set up for the new line.
		 * First save the current line, then construct a new
		 * first image for the continuation line consisting
		 * of any new autoindent plus the pushed ahead text.
		 */
		killU();
		addtext(gobblebl ? " " : "\n");
		/* save vutmp (for undo state) into temp file */
		vsave();
		cnt = 1;
		if (value(vi_AUTOINDENT)) {
			if (value(vi_LISP))
				indent = lindent(dot + 1);
			else
			     if (!HADUP && vaifirst)
				indent = whitecnt(linebuf);
			vaifirst = 0;
			strcLIN(vpastwh(gcursor + 1));
			gcursor = genindent(indent);
			*gcursor = 0;
			if (gcursor + strlen(linebuf) > &genbuf[LBSIZE - 2])
				gcursor = genbuf;
			CP(gcursor, linebuf);
		} else {
			/*
			 * Put gcursor at start of genbuf to wipe
			 * out previous line in preparation for
			 * the next vgetline() loop.
			 */
			CP(genbuf, gcursor + 1);
			gcursor = genbuf;
		}

		/*
		 * If we started out as a single line operation and are now
		 * turning into a multi-line change, then we had better yank
		 * out dot before it changes so that undo will work
		 * correctly later.
		 */
		if (FIXUNDO && vundkind == VCHNG) {
			vremote(1, yank, 0);
			undap1--;
		}

		/*
		 * Now do the append of the new line in the buffer,
		 * and update the display, ie: append genbuf to
		 * the file after dot.  If slowopen
		 * we don't do very much.
		 */
		vdoappend(genbuf);
		vundkind = VMANYINS;
		vcline++;
		if (state != VISUAL)
			vshow(dot, NOLINE);
		else {
			i += LINE(vcline - 1);
			vopen(dot, i);
			if (value(vi_SLOWOPEN))
				vscrap();
			else
				vsync1(LINE(vcline));
		}
		switch (ch) {
		case 'r':
			break;
		case 'a':
			if (value(vi_TERSE)) {
				vshowmode(gettext("A"));
			} else {
				vshowmode(gettext("APPEND MODE"));
			}
			break;
		case 's':
			if (value(vi_TERSE)) {
				vshowmode(gettext("S"));
			} else {
				vshowmode(gettext("SUBSTITUTE MODE"));
			}
			break;
		case 'c':
			if (value(vi_TERSE)) {
				vshowmode(gettext("C"));
			} else {
				vshowmode(gettext("CHANGE MODE"));
			}
			break;
		case 'R':
			if (value(vi_TERSE)) {
				vshowmode(gettext("R"));
			} else {
				vshowmode(gettext("REPLACE MODE"));
			}
			break;
		case 'i':
			if (value(vi_TERSE)) {
				vshowmode(gettext("I"));
			} else {
				vshowmode(gettext("INSERT MODE"));
			}
			break;
		case 'o':
			if (value(vi_TERSE)) {
				vshowmode(gettext("O"));
			} else {
				vshowmode(gettext("OPEN MODE"));
			}
			break;
		default:
			if (value(vi_TERSE)) {
				vshowmode(gettext("I"));
			} else {
				vshowmode(gettext("INPUT MODE"));
			}
		}
		strcLIN(gcursor);
		/* zero genbuf */
		*gcursor = 0;
		cursor = linebuf;
		vgotoCL(nqcolumn(cursor - 1, genbuf));
	} /* end for (;;) loop in vappend() */

	if (imultlinecnt && gotNL) {
		imultlinerep(savecnt, startsrcline, startsrccol, endsrccol);
	} else if (omultlinecnt) {
		omultlinerep(savecnt, startsrcline, endsrccol);
#ifdef XPG6
	} else if (savecnt > 1 && ch == 'r' && gotNL) {
		/*
		 * XPG6 assertion 313 & 254 : Position cursor for [count]r\n
		 * then insert [count -1] newlines.
		 */
		endsrccol = gcursor - genbuf - 1;
		rmultlinerep(savecnt, endsrccol);
#endif /* XPG6 */
	}

	/*
	 * All done with insertion, position the cursor
	 * and sync the screen.
	 */
	hold = oldhold;
	if ((imultlinecnt && gotNL) || omultlinecnt) {
		fixdisplay();
#ifdef XPG6
	} else if (savecnt > 1 && ch == 'r' && gotNL) {
		fixdisplay();
		/*
		 * XPG6 assertion 313 & 254 [count]r\n : Set flag to call
		 * fixdisplay() after operate() has finished.  To be sure that
		 * the text (after the last \n followed by an indent) is always
		 * displayed, fixdisplay() is called right before getting
		 * the next command.
		 */
		redisplay = 1;
#endif /* XPG6 */
	} else if (cursor > linebuf) {
		cursor = lastchr(linebuf, cursor);
#ifdef XPG6
		/*
		 * XPG6 assertion 313 & 254 [count]r\n :
		 * For 'r' command, when the replacement char causes new
		 * lines to be created, point cursor to first non-blank.
		 * The old code, ie: cursor = lastchr(linebuf, cursor);
		 * set cursor to the blank before the first non-blank
		 * for r\n
		 */
		if (ch == 'r' && gotNL && isblank((int)*cursor))
			++cursor;
#endif /* XPG6 */
	}
	if (state != HARDOPEN)
		vsyncCL();
	else if (cursor > linebuf)
		back1();
	doomed = 0;
	wcursor = cursor;
	(void) vmove();
}

/*
 * XPG6
 * To repeat multi-line input for [count]a, [count]A, [count]i, [count]I,
 * or a subsequent [count]. :
 * insert input count-1 more times.
 */

static void
imultlinerep(int savecnt, line *startsrcline, int startsrccol, int endsrccol)
{
	int tmpcnt = 2;	/* 1st insert counts as 1 repeat */
	line *srcline, *endsrcline;
	size_t destsize = LBSIZE - endsrccol - 1;

	endsrcline = dot;

	/* Save linebuf into temp file before moving off the line. */
	vsave();

	/*
	 * At this point the temp file contains the first iteration of
	 * a multi-line insert, and we need to repeat it savecnt - 1
	 * more times in the temp file.  dot is the last line in the
	 * first iteration of the insert.  Decrement dot so that
	 * vdoappend() will append each new line before the last line.
	 */
	--dot;
	--vcline;
	/*
	 * Use genbuf to rebuild the last line in the 1st iteration
	 * of the repeated insert, then copy this line to the temp file.
	 */
	(void) strlcpy((char *)genbuf, (char *)linebuf, sizeof (genbuf));
	getline(*startsrcline);
	if (strlcpy((char *)(genbuf + endsrccol + 1),
	    (char *)(linebuf + startsrccol), destsize) >= destsize) {
		error(gettext("Line too long"));
	}
	vdoappend(genbuf);
	vcline++;
	/*
	 * Loop from the second line of the first iteration
	 * through endsrcline, appending after dot.
	 */
	++startsrcline;

	while (tmpcnt <= savecnt) {
		for (srcline = startsrcline; srcline <= endsrcline;
		    ++srcline) {
			if ((tmpcnt == savecnt) &&
			    (srcline == endsrcline)) {
				/*
				 * The last line is already in place,
				 * just make it the current line.
				 */
				vcline++;
				dot++;
				getDOT();
				cursor = linebuf + endsrccol;
			} else {
				getline(*srcline);
				/* copy linebuf to temp file */
				vdoappend(linebuf);
				vcline++;
			}
		}
		++tmpcnt;
	}
}

/*
 * To repeat input for [count]o, [count]O, or a subsequent [count]. :
 * append input count-1 more times to the end of the already added
 * text, each time starting on a new line.
 */

static void
omultlinerep(int savecnt, line *startsrcline, int endsrccol)
{
	int tmpcnt = 2;	/* 1st insert counts as 1 repeat */
	line *srcline, *endsrcline;

	endsrcline = dot;
	/* Save linebuf into temp file before moving off the line. */
	vsave();

	/*
	 * Loop from the first line of the first iteration
	 * through endsrcline, appending after dot.
	 */
	while (tmpcnt <= savecnt) {
		for (srcline = startsrcline; srcline <= endsrcline; ++srcline) {
			getline(*srcline);
			/* copy linebuf to temp file */
			vdoappend(linebuf);
			vcline++;
		}
		++tmpcnt;
	}
	cursor = linebuf + endsrccol;
}

#ifdef XPG6
/*
 * XPG6 assertion 313 & 254 : To repeat '\n' for [count]r\n
 * insert '\n' savecnt-1 more times before the already added '\n'.
 */

static void
rmultlinerep(int savecnt, int endsrccol)
{
	int tmpcnt = 2;	/* 1st replacement counts as 1 repeat */

	/* Save linebuf into temp file before moving off the line. */
	vsave();
	/*
	 * At this point the temp file contains the line followed by '\n',
	 * which is preceded by indentation if autoindent is set.
	 * '\n' must be repeated [savecnt - 1] more times in the temp file.
	 * dot is the current line containing the '\n'.  Decrement dot so that
	 * vdoappend() will append each '\n' before the current '\n'.
	 * This will allow only the last line to contain any autoindent
	 * characters.
	 */
	--dot;
	--vcline;

	/*
	 * Append after dot.
	 */
	while (tmpcnt <= savecnt) {
		linebuf[0] = '\0';
		/* append linebuf below current line in temp file */
		vdoappend(linebuf);
		vcline++;
		++tmpcnt;
	}
	/* set the current line to the line after the last '\n' */
	++dot;
	++vcline;
	/* point cursor after (linebuf + endsrccol) */
	vcursaft(linebuf + endsrccol);
}
#endif /* XPG6 */

/*
 * Similiar to a ctrl-l, however always vrepaint() in case the last line
 * of the repeat would exceed the bottom of the screen.
 */

void
fixdisplay(void)
{
	vclear();
	vdirty(0, vcnt);
	if (state != VISUAL) {
		vclean();
		vcnt = 0;
		vmoveto(dot, cursor, 0);
	} else {
		vredraw(WTOP);
		vrepaint(cursor);
		vfixcurs();
	}
}

/*
 * Subroutine for vgetline to back up a single character position,
 * backwards around end of lines (vgoto can't hack columns which are
 * less than 0 in general).
 */
void
back1(void)
{

	vgoto(destline - 1, WCOLS + destcol - 1);
}

/*
 * Get a line into genbuf after gcursor.
 * Cnt limits the number of input characters
 * accepted and is used for handling the replace
 * single character command.  Aescaped is the location
 * where we stick a termination indicator (whether we
 * ended with an ESCAPE or a newline/return.
 *
 * We do erase-kill type processing here and also
 * are careful about the way we do this so that it is
 * repeatable.  (I.e. so that your kill doesn't happen,
 * when you repeat an insert if it was escaped with \ the
 * first time you did it.  commch is the command character
 * involved, including the prompt for readline.
 */
unsigned char *
vgetline(cnt, gcursor, aescaped, commch)
	int cnt;
	unsigned char *gcursor;
	bool *aescaped;
	unsigned char commch;
{
	int c, ch;
	unsigned char *cp, *pcp;
	int x, y, iwhite, backsl=0;
	unsigned char *iglobp;
	int (*OO)() = Outchar;
	int length, width;
	unsigned char multic[MULTI_BYTE_MAX+1];
	wchar_t wchar = 0;
	unsigned char	*p;
	int	len;
	

	/*
	 * Clear the output state and counters
	 * for autoindent backwards motion (counts of ^D, etc.)
	 * Remember how much white space at beginning of line so
	 * as not to allow backspace over autoindent.
	 */

	*aescaped = 0;
	ogcursor = gcursor;
	flusho();
	CDCNT = 0;
	HADUP = 0;
	HADZERO = 0;
	gobbled = 0;
	iwhite = whitecnt(genbuf);
	iglobp = vglobp;

	/*
	 * Clear abbreviation recursive-use count
	 */
	abbrepcnt = 0;
	/*
	 * Carefully avoid using vinschar in the echo area.
	 */
	if (splitw)
		Outchar = vputchar;
	else {
		Outchar = vinschar;
		vprepins();
	}
	for (;;) {
		length = 0;
		backsl = 0;
		if (gobblebl)
			gobblebl--;
		if (cnt != 0) {
			cnt--;
			if (cnt == 0)
				goto vadone;
		}
		c = getkey();
		if (c != ATTN)
			c &= 0377;
		ch = c;
		maphopcnt = 0;
		if (vglobp == 0 && Peekkey == 0 && commch != 'r')
			while ((ch = map(c, immacs, commch)) != c) {
				c = ch;
				if (!value(vi_REMAP))
					break;
				if (++maphopcnt > 256)
					error(gettext("Infinite macro loop"));
			}
		if (!iglobp) {

			/*
			 * Erase-kill type processing.
			 * Only happens if we were not reading
			 * from untyped input when we started.
			 * Map users erase to ^H, kill to -1 for switch.
			 */
			if (c == tty.c_cc[VERASE])
				c = CTRL('h');
			else if (c == tty.c_cc[VKILL])
				c = -1;
			switch (c) {

			/*
			 * ^?		Interrupt drops you back to visual
			 *		command mode with an unread interrupt
			 *		still in the input buffer.
			 *
			 * ^\		Quit does the same as interrupt.
			 *		If you are a ex command rather than
			 *		a vi command this will drop you
			 *		back to command mode for sure.
			 */
			case ATTN:
			case QUIT:
				ungetkey(c);
				goto vadone;

			/*
			 * ^H		Backs up a character in the input.
			 *
			 * BUG:		Can't back around line boundaries.
			 *		This is hard because stuff has
			 *		already been saved for repeat.
			 */
			case CTRL('h'):
bakchar:
				cp = lastchr(ogcursor, gcursor);
				if (cp < ogcursor) {
					if (splitw) {
						/*
						 * Backspacing over readecho
						 * prompt. Pretend delete but
						 * don't beep.
						 */
						ungetkey(c);
						goto vadone;
					}
					(void) beep();
					continue;
				}
				goto vbackup;

			/*
			 * ^W		Back up a white/non-white word.
			 */
			case CTRL('w'):
				wdkind = 1;
				for (cp = gcursor; cp > ogcursor && isspace(cp[-1]); cp--)
					continue;
				pcp = lastchr(ogcursor, cp);
				for (c = wordch(pcp);
				    cp > ogcursor && wordof(c, pcp); cp = pcp, pcp = lastchr(ogcursor, cp))
					continue;
				goto vbackup;

			/*
			 * users kill	Kill input on this line, back to
			 *		the autoindent.
			 */
			case -1:
				cp = ogcursor;
vbackup:
				if (cp == gcursor) {
					(void) beep();
					continue;
				}
				endim();
				*cp = 0;
				c = cindent();
				vgotoCL(nqcolumn(lastchr(linebuf, cursor), genbuf));
					
				if (doomed >= 0)
					doomed += c - cindent();
				gcursor = cp;
				continue;

			/*
			 * \		Followed by erase or kill
			 *		maps to just the erase or kill.
			 */
			case '\\':
				x = destcol, y = destline;
				putchar('\\');
				vcsync();
				c = getkey();
				if (c == tty.c_cc[VERASE]
				    || c == tty.c_cc[VKILL])
				{
					vgoto(y, x);
					if (doomed >= 0)
						doomed++;
					multic[0] = wchar = c;
					length = 1;
					goto def;
				}
				ungetkey(c), c = '\\';
				backsl = 1;
				break;

			/*
			 * ^Q		Super quote following character
			 *		Only ^@ is verboten (trapped at
			 *		a lower level) and \n forces a line
			 *		split so doesn't really go in.
			 *
			 * ^V		Synonym for ^Q
			 */
			case CTRL('q'):
			case CTRL('v'):
				x = destcol, y = destline;
				putchar('^');
				vgoto(y, x);
				c = getkey();
#ifdef USG
				if (c == ATTN)
					c = tty.c_cc[VINTR];
#endif
				if (c != NL) {
					if (doomed >= 0)
						doomed++;
					multic[0] = wchar = c;
					length = 1;
					goto def;
				}
				break;
			}
		}

		/*
		 * If we get a blank not in the echo area
		 * consider splitting the window in the wrapmargin.
		 */
		if(!backsl) {
			ungetkey(c);
			if((length = _mbftowc((char *)multic, &wchar, getkey, &Peekkey)) <= 0) {
				(void) beep();
				continue;
			} 
		} else {
			length = 1;
			multic[0] = '\\';
		}

		if (c != NL && !splitw) {
			if (c == ' ' && gobblebl) {
				gobbled = 1;
				continue;
			}
			if ((width = wcwidth(wchar)) <= 0)
				width = (wchar <= 0177 ? 1 : 4);
			if (value(vi_WRAPMARGIN) &&
				(outcol + width - 1 >= OCOLUMNS - value(vi_WRAPMARGIN) ||
				 backsl && outcol==0) &&
				commch != 'r') {
				/*
				 * At end of word and hit wrapmargin.
				 * Move the word to next line and keep going.
				 */
				unsigned char *wp;
				int bytelength;
#ifndef PRESUNEUC
				unsigned char *tgcursor;
				wchar_t wc1, wc2;
				tgcursor = gcursor;
#endif /* PRESUNEUC */
				wdkind = 1;
				strncpy(gcursor, multic, length);
				gcursor += length;
				if (backsl) {
#ifdef PRESUNEUC
					if((length = mbftowc((char *)multic, &wchar, getkey, &Peekkey)) <= 0) {
#else
					if((length = _mbftowc((char *)multic, &wchar, getkey, &Peekkey)) <= 0) {
#endif /* PRESUNEUC */
						(void) beep();
						continue;
					} 
					strncpy(gcursor, multic, length);
					gcursor += length;
				}
				*gcursor = 0;
				/*
				 * Find end of previous word if we are past it.
				 */
				for (cp=gcursor; cp>ogcursor && isspace(cp[-1]); cp--)
					;
#ifdef PRESUNEUC
				/* find screen width of previous word */
				width = 0;
				for(wp = cp; *wp; ) 
#else
				/* count screen width of pending characters */
				width = 0;
				for(wp = tgcursor; wp < cp;)
#endif /* PRESUNEUC */
					if((bytelength = mbtowc(&wchar, (char *)wp, MULTI_BYTE_MAX)) < 0) {
						width+=4;
						wp++;
					} else {
						int curwidth = wcwidth(wchar);
						if(curwidth <= 0)
							width += (*wp < 0200 ? 2 : 4);
						else
							width += curwidth;
						wp += bytelength;
					}	
						
#ifdef PRESUNEUC
				if (outcol+(backsl?OCOLUMNS:0) - width >= OCOLUMNS - value(vi_WRAPMARGIN)) {
#else
				if (outcol+(backsl?OCOLUMNS:0) + width -1 >= OCOLUMNS - value(vi_WRAPMARGIN)) {
#endif /* PRESUNEUC */
					/*
					 * Find beginning of previous word.
					 */
#ifdef PRESUNEUC
					for (; cp>ogcursor && !isspace(cp[-1]); cp--)
						;
#else
					wc1 = wc2 = 0;
					while (cp>ogcursor) {
						if (isspace(cp[-1])) {
							break;
						}
						if (!multibyte) {
							cp--;
							continue;
						}
						wp = (unsigned char *)(cp -
							MB_CUR_MAX);
						if (wp < ogcursor)
							wp = ogcursor;
						while (cp > wp) {
/* 7tabs */if (wc2) {
/* 7tabs */	if ((bytelength = mbtowc(&wc1, (char *)wp, cp-wp)) != cp-wp) {
/* 7tabs */		wp++;
/* 7tabs */		wc1 = 0;
/* 7tabs */		continue;
/* 7tabs */	}
/* 7tabs */} else {
/* 7tabs */	if ((bytelength = mbtowc(&wc2, (char *)wp, cp-wp)) != cp-wp) {
/* 7tabs */		wp++;
/* 7tabs */		wc2 = 0;
/* 7tabs */		continue;
/* 7tabs */	}
/* 7tabs */}
/* 7tabs */if (wc1) {
/* 7tabs */	if (wdbdg && (!iswascii(wc1) || !iswascii(wc2))) {
/* 7tabs */		if ((*wdbdg)(wc1, wc2, 2) < 5) {
/* 7tabs */			goto ws;
/* 7tabs */		}	
/* 7tabs */	}
/* 7tabs */	wc2 = wc1;
/* 7tabs */	wc1 = 0;
/* 7tabs */	cp -= bytelength - 1;
/* 7tabs */	break;
/* 7tabs */} else {
/* 7tabs */	cp -= bytelength - 1;
/* 7tabs */	break;
/* 7tabs */}
						}
						cp--;
					}
ws:
#endif /* PRESUNEUC */
					if (cp <= ogcursor) {
						/*
						 * There is a single word that
						 * is too long to fit.  Just
						 * let it pass, but beep for
						 * each new letter to warn
						 * the luser.
						 */
						gcursor -= length;
						c = *gcursor;
						*gcursor = 0;
						(void) beep();
						goto dontbreak;
					}
					/*
					 * Save it for next line.
					 */
					macpush(cp, 0);
#ifdef PRESUNEUC
					cp--;
#endif /* PRESUNEUC */
				}
				macpush("\n", 0);
				/*
				 * Erase white space before the word.
				 */
				while (cp > ogcursor && isspace(cp[-1]))
					cp--;	/* skip blank */
				gobblebl = 3;
				goto vbackup;
			}
		dontbreak:;
		}

		/*
		 * Word abbreviation mode.
		 */
		if (anyabbrs && gcursor > ogcursor && !wordch(multic) && wordch(lastchr(ogcursor, gcursor))) {
				int wdtype, abno;

				multic[length] = 0;
				wdkind = 1;
				cp = lastchr(ogcursor, gcursor);
				pcp = lastchr(ogcursor, cp);
				for (wdtype = wordch(pcp);
				    cp > ogcursor && wordof(wdtype, pcp); cp = pcp, pcp = lastchr(ogcursor, pcp))
					;
				*gcursor = 0;
				for (abno=0; abbrevs[abno].mapto; abno++) {
					if (eq(cp, abbrevs[abno].cap)) {
						if(abbrepcnt == 0) {
							if(reccnt(abbrevs[abno].cap, abbrevs[abno].mapto))
								abbrepcnt = 1;
							macpush(multic, 0);
							macpush(abbrevs[abno].mapto);
							goto vbackup;
						} else
							abbrepcnt = 0;
					}
				}
		}

		switch (c) {

		/*
		 * ^M		Except in repeat maps to \n.
		 */
		case CR:
			if (vglobp) {
				multic[0] = wchar = c;
				length = 1;
				goto def;
			}
			c = '\n';
			/* presto chango ... */

		/*
		 * \n		Start new line.
		 */
		case NL:
			*aescaped = c;
			goto vadone;

		/*
		 * escape	End insert unless repeat and more to repeat.
		 */
		case ESCAPE:
			if (lastvgk) {
				multic[0] = wchar = c;
				length = 1;
				goto def;
			}
			goto vadone;

		/*
		 * ^D		Backtab.
		 * ^T		Software forward tab.
		 *
		 *		Unless in repeat where this means these
		 *		were superquoted in.
		 */
		case CTRL('t'):
			if (vglobp) {
				multic[0] = wchar = c;
				length = 1;
				goto def;
			}
			/* fall into ... */

			*gcursor = 0;
			cp = vpastwh(genbuf);
			c = whitecnt(genbuf);
			if (ch == CTRL('t')) {
				/*
				 * ^t just generates new indent replacing
				 * current white space rounded up to soft
				 * tab stop increment.
				 */
				if (cp != gcursor)
					/*
					 * BUG:		Don't hack ^T except
					 *		right after initial
					 *		white space.
					 */
					continue;
				cp = genindent(iwhite = backtab(c + value(vi_SHIFTWIDTH) + 1));
				ogcursor = cp;
				goto vbackup;
			}
			/*
			 * ^D works only if we are at the (end of) the
			 * generated autoindent.  We count the ^D for repeat
			 * purposes.
			 */
		case CTRL('d'):
			/* check if ^d was superquoted in */
			if(vglobp && inscdcnt <= 0) {
				multic[0] = wchar = c;
				length = 1;
				goto def;
			}
			if(vglobp)
				inscdcnt--;
			*gcursor = 0;
			cp = vpastwh(genbuf);
			c = whitecnt(genbuf);
			if (c == iwhite && c != 0)
				if (cp == gcursor) {
					iwhite = backtab(c);
					CDCNT++;
					ogcursor = cp = genindent(iwhite);
					goto vbackup;
				} else if (&cp[1] == gcursor &&
				    (*cp == '^' || *cp == '0')) {
					/*
					 * ^^D moves to margin, then back
					 * to current indent on next line.
					 *
					 * 0^D moves to margin and then
					 * stays there.
					 */
					HADZERO = *cp == '0';
					ogcursor = cp = genbuf;
					HADUP = 1 - HADZERO;
					CDCNT = 1;
					endim();
					back1();
					(void) vputchar(' ');
					goto vbackup;
				}

			if (vglobp && vglobp - iglobp >= 2) {
				if ((p = vglobp - MB_CUR_MAX) < iglobp)
					p = iglobp;
				for ( ; p < &vglobp[-2]; p += len) {
					if ((len = mblen((char *)p, MB_CUR_MAX)) <= 0)
						len = 1;
				}
				if ((p == &vglobp[-2]) &&
			            (*p == '^' || *p == '0') &&
			            gcursor == ogcursor + 1)
					goto bakchar;
			}
			continue;

		default:
			/*
			 * Possibly discard control inputs.
			 */
			if (!vglobp && junk(c)) {
				(void) beep();
				continue;
			}
def:
			if (!backsl) {
				putchar(wchar);
				flush();
			}
			if (gcursor + length - 1 > &genbuf[LBSIZE - 2])
				error(gettext("Line too long"));
			(void)strncpy(gcursor, multic, length);
			gcursor += length;
			vcsync();
			if (value(vi_SHOWMATCH) && !iglobp)
				if (c == ')' || c == '}')
					lsmatch(gcursor);
			continue;
		}
	}
vadone:
	*gcursor = 0;
	if (Outchar != termchar)
		Outchar = OO;
	endim();
	return (gcursor);
}

int	vgetsplit();
unsigned char	*vsplitpt;

/*
 * Append the line in buffer at lp
 * to the buffer after dot.
 */
void
vdoappend(unsigned char *lp)
{
	int oing = inglobal;

	vsplitpt = lp;
	inglobal = 1;
	(void)append(vgetsplit, dot);
	inglobal = oing;
}

/*
 * Subroutine for vdoappend to pass to append.
 */
int
vgetsplit(void)
{

	if (vsplitpt == 0)
		return (EOF);
	strcLIN(vsplitpt);
	vsplitpt = 0;
	return (0);
}

/*
 * Vmaxrep determines the maximum repetition factor
 * allowed that will yield total line length less than
 * LBSIZE characters and also does hacks for the R command.
 */
int
vmaxrep(unsigned char ch, int cnt)
{
	int len;
	unsigned char *cp;
	int repcnt, oldcnt, replen;
	if (cnt > LBSIZE - 2)
		cnt = LBSIZE - 2;
	if (ch == 'R') {
		len = strlen(cursor);
		oldcnt = 0;
		for(cp = cursor; *cp; ) {
			oldcnt++;
			cp = nextchr(cp);
		}
		repcnt = 0;
		for(cp = genbuf; *cp; ) {
			repcnt++;
			cp = nextchr(cp);
		}
		/*
		 * if number of characters in replacement string
		 * (repcnt) is less than number of characters following 
		 * cursor (oldcnt), find end of repcnt
		 * characters after cursor
		 */
		if(repcnt < oldcnt) {
			for(cp = cursor; repcnt > 0; repcnt--)
				cp = nextchr(cp);
			len = cp - cursor;
		}
		CP(cursor, cursor + len);
		vUD2 += len;
	}
	len = strlen(linebuf);
	replen = strlen(genbuf);
	if (len + cnt * replen <= LBSIZE - 2)
		return (cnt);
	cnt = (LBSIZE - 2 - len) / replen;
	if (cnt == 0) {
		vsave();
		error(gettext("Line too long"));
	}
	return (cnt);
}

/*
 * Determine how many occurrences of word 'CAP' are in 'MAPTO'.  To be
 * considered an occurrence there must be both a nonword-prefix, a 
 * complete match of 'CAP' within 'MAPTO', and a nonword-suffix. 
 * Note that the beginning and end of 'MAPTO' are considered to be
 * valid nonword delimiters.
 */
int
reccnt(unsigned char *cap, unsigned char *mapto)
{
	int i, cnt, final;

	cnt = 0;
	final = strlen(mapto) - strlen(cap);

	for (i=0; i <= final; i++) 
	  if ((strncmp(cap, mapto+i, strlen(cap)) == 0)       /* match */
	  && (i == 0     || !wordch(&mapto[i-1]))	      /* prefix ok */
	  && (i == final || !wordch(&mapto[i+strlen(cap)])))  /* suffix ok */
		cnt++;
	return (cnt);
}