* txt_backup().  Set the new line length.
* txt_backup(), and set the line final length.
v_txt(sp, vp, tm, lp, len, prompt, ai_line, rcount, flags)	SCR *sp;	VICMD *vp;	MARK *tm;		/* To MARK. */	const char *lp;		/* Input line. */	size_t len;		/* Input line length. */	ARG_CHAR_T prompt;	/* Prompt to display. */	recno_t ai_line;	/* Line number to use for autoindent count. */	u_long rcount;		/* Replay count. */	u_int32_t flags;	/* TXT_* flags. */{	EVENT ev, *evp;		/* Current event. */	EVENT fc;		/* File name completion event. */	GS *gp;	TEXT *ntp, *tp;		/* Input text structures. */	TEXT ait;		/* Autoindent text structure. */	TEXT wmt;		/* Wrapmargin text structure. */	TEXTH *tiqh;	VI_PRIVATE *vip;	abb_t abb;		/* State of abbreviation checks. */	carat_t carat;		/* State of the "[^0]^D" sequences. */	quote_t quote;		/* State of quotation. */	size_t owrite, insert;	/* Temporary copies of TEXT fields. */	size_t margin;		/* Wrapmargin value. */	size_t rcol;		/* 0-N: insert offset in the replay buffer. */	size_t tcol;		/* Temporary column. */	u_int32_t ec_flags;	/* Input mapping flags. */#define	IS_RESTART	0x01	/* Reset the incremental search. */#define	IS_RUNNING	0x02	/* Incremental search turned on. */	u_int8_t is_flags;	int abcnt, ab_turnoff;	/* Abbreviation character count, switch. */	int filec_redraw;	/* Redraw after the file completion routine. */	int hexcnt;		/* Hex character count. */	int showmatch;		/* Showmatch set on this character. */	int wm_set, wm_skip;	/* Wrapmargin happened, blank skip flags. */	int max, tmp;	char *p;	gp = sp->gp;	vip = VIP(sp);	/*	 * Set the input flag, so tabs get displayed correctly	 * and everyone knows that the text buffer is in use.	 */	F_SET(sp, SC_TINPUT);	/*	 * Get one TEXT structure with some initial buffer space, reusing	 * the last one if it's big enough.  (All TEXT bookkeeping fields	 * default to 0 -- text_init() handles this.)  If changing a line,	 * copy it into the TEXT buffer.	 */	tiqh = &sp->tiq;	if (tiqh->cqh_first != (void *)tiqh) {		tp = tiqh->cqh_first;		if (tp->q.cqe_next != (void *)tiqh || tp->lb_len < len + 32) {			text_lfree(tiqh);			goto newtp;		}		tp->ai = tp->insert = tp->offset = tp->owrite = 0;		if (lp != NULL) {			tp->len = len;			memmove(tp->lb, lp, len);		} else			tp->len = 0;	} else {newtp:		if ((tp = text_init(sp, lp, len, len + 32)) == NULL)			return (1);		CIRCLEQ_INSERT_HEAD(tiqh, tp, q);	}	/* Set default termination condition. */	tp->term = TERM_OK;	/* Set the starting line, column. */	tp->lno = sp->lno;	tp->cno = sp->cno;	/*	 * Set the insert and overwrite counts.  If overwriting characters,	 * do insertion afterward.  If not overwriting characters, assume	 * doing insertion.  If change is to a mark, emphasize it with an	 * CH_ENDMARK character.	 */	if (len) {		if (LF_ISSET(TXT_OVERWRITE)) {			tp->owrite = (tm->cno - tp->cno) + 1;			tp->insert = (len - tm->cno) - 1;		} else			tp->insert = len - tp->cno;		if (LF_ISSET(TXT_EMARK) && txt_emark(sp, tp, tm->cno))			return (1);	}	/*	 * Many of the special cases in text input are to handle autoindent	 * support.  Somebody decided that it would be a good idea if "^^D"	 * and "0^D" deleted all of the autoindented characters.  In an editor	 * that takes single character input from the user, this beggars the	 * imagination.  Note also, "^^D" resets the next lines' autoindent,	 * but "0^D" doesn't.	 *	 * We assume that autoindent only happens on empty lines, so insert	 * and overwrite will be zero.  If doing autoindent, figure out how	 * much indentation we need and fill it in.  Update input column and	 * screen cursor as necessary.	 */	if (LF_ISSET(TXT_AUTOINDENT) && ai_line != OOBLNO) {		if (v_txt_auto(sp, ai_line, NULL, 0, tp))			return (1);		tp->cno = tp->ai;	} else {		/*		 * The cc and S commands have a special feature -- leading		 * <blank> characters are handled as autoindent characters.		 * Beauty!		 */		if (LF_ISSET(TXT_AICHARS)) {			tp->offset = 0;			tp->ai = tp->cno;		} else			tp->offset = tp->cno;	}	/* If getting a command buffer from the user, there may be a prompt. */	if (LF_ISSET(TXT_PROMPT)) {		tp->lb[tp->cno++] = prompt;		++tp->len;		++tp->offset;	}	/*	 * If appending after the end-of-line, add a space into the buffer	 * and move the cursor right.  This space is inserted, i.e. pushed	 * along, and then deleted when the line is resolved.  Assumes that	 * the cursor is already positioned at the end of the line.  This	 * avoids the nastiness of having the cursor reside on a magical	 * column, i.e. a column that doesn't really exist.  The only down	 * side is that we may wrap lines or scroll the screen before it's	 * strictly necessary.  Not a big deal.	 */	if (LF_ISSET(TXT_APPENDEOL)) {		tp->lb[tp->cno] = CH_CURSOR;		++tp->len;		++tp->insert;		(void)vs_change(sp, tp->lno, LINE_RESET);	}	/*	 * Historic practice is that the wrapmargin value was a distance	 * from the RIGHT-HAND margin, not the left.  It's more useful to	 * us as a distance from the left-hand margin, i.e. the same as	 * the wraplen value.  The wrapmargin option is historic practice.	 * Nvi added the wraplen option so that it would be possible to	 * edit files with consistent margins without knowing the number of	 * columns in the window.	 *	 * XXX	 * Setting margin causes a significant performance hit.  Normally	 * we don't update the screen if there are keys waiting, but we	 * have to if margin is set, otherwise the screen routines don't	 * know where the cursor is.	 *	 * !!!	 * Abbreviated keys were affected by the wrapmargin option in the	 * historic 4BSD vi.  Mapped keys were usually, but sometimes not.	 * See the comment in vi/v_text():set_txt_std for more information.	 *	 * !!!	 * One more special case.  If an inserted <blank> character causes	 * wrapmargin to split the line, the next user entered character is	 * discarded if it's a <space> character.	 */	wm_set = wm_skip = 0;	if (LF_ISSET(TXT_WRAPMARGIN))		if ((margin = O_VAL(sp, O_WRAPMARGIN)) != 0)			margin = sp->cols - margin;		else			margin = O_VAL(sp, O_WRAPLEN);	else		margin = 0;	/* Initialize abbreviation checks. */	abcnt = ab_turnoff = 0;	abb = F_ISSET(gp, G_ABBREV) &&	    LF_ISSET(TXT_MAPINPUT) ? AB_INWORD : AB_NOTSET;	/*	 * Set up the dot command.  Dot commands are done by saving the actual	 * characters and then reevaluating them so that things like wrapmargin	 * can change between the insert and the replay.	 *	 * !!!	 * Historically, vi did not remap or reabbreviate replayed input.  (It	 * did beep at you if you changed an abbreviation and then replayed the	 * input.  We're not that compatible.)  We don't have to do anything to	 * avoid remapping, as we're not getting characters from the terminal	 * routines.  Turn the abbreviation check off.	 *	 * XXX	 * It would be nice if we could swallow backspaces and such, but it's	 * not all that easy to do.  What we can do is turn off the common	 * error messages during the replay.  Otherwise, when the user enters	 * an illegal command, e.g., "Ia<erase><erase><erase><erase>b<escape>",	 * and then does a '.', they get a list of error messages after command	 * completion.	 */	rcol = 0;	if (LF_ISSET(TXT_REPLAY)) {		abb = AB_NOTSET;		LF_CLR(TXT_RECORD);	}	/* Other text input mode setup. */	quote = Q_NOTSET;	carat = C_NOTSET;	FL_INIT(is_flags,	    LF_ISSET(TXT_SEARCHINCR) ? IS_RESTART | IS_RUNNING : 0);	filec_redraw = hexcnt = showmatch = 0;	/* Initialize input flags. */	ec_flags = LF_ISSET(TXT_MAPINPUT) ? EC_MAPINPUT : 0;	/* Refresh the screen. */	UPDATE_POSITION(sp, tp);	if (vs_refresh(sp, 1))		return (1);	/* If it's dot, just do it now. */	if (F_ISSET(vp, VC_ISDOT))		goto replay;	/* Get an event. */	evp = &ev;next:	if (v_event_get(sp, evp, 0, ec_flags))		return (1);	/*	 * If file completion overwrote part of the screen and nothing else has	 * been displayed, clean up.  We don't do this as part of the normal	 * message resolution because we know the user is on the colon command	 * line and there's no reason to enter explicit characters to continue.	 */	if (filec_redraw && !F_ISSET(sp, SC_SCR_EXWROTE)) {		filec_redraw = 0;		fc.e_event = E_REPAINT;		fc.e_flno = vip->totalcount >=		    sp->rows ? 1 : sp->rows - vip->totalcount;		fc.e_tlno = sp->rows;		vip->linecount = vip->lcontinue = vip->totalcount = 0;		(void)vs_repaint(sp, &fc);		(void)vs_refresh(sp, 1);	}	/* Deal with all non-character events. */	switch (evp->e_event) {	case E_CHARACTER:		break;	case E_ERR:	case E_EOF:		F_SET(sp, SC_EXIT_FORCE);		return (1);	case E_INTERRUPT:		/*		 * !!!		 * Historically, <interrupt> exited the user from text input		 * mode or cancelled a colon command, and returned to command		 * mode.  It also beeped the terminal, but that seems a bit		 * excessive.		 */		goto k_escape;	case E_REPAINT:		if (vs_repaint(sp, &ev))			return (1);		goto next;	case E_WRESIZE:		/* <resize> interrupts the input mode. */		v_emsg(sp, NULL, VIM_WRESIZE);		goto k_escape;	default:		v_event_err(sp, evp);		goto k_escape;	}	/*	 * !!!	 * If the first character of the input is a nul, replay the previous	 * input.  Note, it was okay to replay non-existent input.  This was	 * not documented as far as I know, and is a great test of vi clones.	 */	if (!LF_ISSET(TXT_REPLAY) && evp->e_c == '\0') {		if (vip->rep == NULL)			goto done;		rcol = 0;		abb = AB_NOTSET;		LF_CLR(TXT_RECORD);		LF_SET(TXT_REPLAY);		goto replay;	}	/*	 * File name completion and colon command-line editing.   We don't	 * have enough meta characters, so we expect people to overload	 * them.  If the two characters are the same, then we do file name	 * completion if the cursor is past the first column, and do colon	 * command-line editing if it's not.	 */	if (quote == Q_NOTSET) {		int L__cedit, L__filec;		L__cedit = L__filec = 0;		if (LF_ISSET(TXT_CEDIT) && O_STR(sp, O_CEDIT) != NULL &&		    O_STR(sp, O_CEDIT)[0] == evp->e_c)			L__cedit = 1;		if (LF_ISSET(TXT_FILEC) && O_STR(sp, O_FILEC) != NULL &&		    O_STR(sp, O_FILEC)[0] == evp->e_c)			L__filec = 1;		if (L__cedit == 1 && (L__filec == 0 || tp->cno == tp->offset)) {			tp->term = TERM_CEDIT;			goto k_escape;		}		if (L__filec == 1) {			if (txt_fc(sp, tp, &filec_redraw))				goto err;			goto resolve;		}	}	/* Abbreviation overflow check.  See comment in txt_abbrev(). */#define	MAX_ABBREVIATION_EXPANSION	256	if (F_ISSET(&evp->e_ch, CH_ABBREVIATED)) {		if (++abcnt > MAX_ABBREVIATION_EXPANSION) {			if (v_event_flush(sp, CH_ABBREVIATED))				msgq(sp, M_ERR,"191|Abbreviation exceeded expansion limit: characters discarded");			abcnt = 0;			if (LF_ISSET(TXT_REPLAY))				goto done;			goto resolve;		}	} else		abcnt = 0;	/* Check to see if the character fits into the replay buffers. */	if (LF_ISSET(TXT_RECORD)) {		BINC_GOTO(sp, vip->rep,		    vip->rep_len, (rcol + 1) * sizeof(EVENT));		vip->rep[rcol++] = *evp;	}replay:	if (LF_ISSET(TXT_REPLAY))		evp = vip->rep + rcol++;	/* Wrapmargin check for leading space. */	if (wm_skip) {		wm_skip = 0;		if (evp->e_c == ' ')			goto resolve;	}	/* If quoted by someone else, simply insert the character. */	if (F_ISSET(&evp->e_ch, CH_QUOTED))		goto insq_ch;	/*	 * !!!	 * If this character was quoted by a K_VLNEXT or a backslash, replace	 * the placeholder (a carat or a backslash) with the new character.	 * If it was quoted by a K_VLNEXT, we've already adjusted the cursor	 * because it has to appear on top of the placeholder character.  If	 * it was quoted by a backslash, adjust the cursor now, the cursor	 * doesn't appear on top of it.  Historic practice in both cases.	 *	 * Skip tests for abbreviations; ":ab xa XA" followed by "ixa^V<space>"	 * doesn't perform an abbreviation.  Special case, ^V^J (not ^V^M) is	 * the same as ^J, historically.	 */	if (quote == Q_BTHIS || quote == Q_VTHIS) {		FL_CLR(ec_flags, EC_QUOTED);		if (quote == Q_BTHIS &&		    (evp->e_value == K_VERASE || evp->e_value == K_VKILL)) {			quote = Q_NOTSET;			--tp->cno;			++tp->owrite;			goto insl_ch;		}		if (quote == Q_VTHIS && evp->e_value != K_NL) {			quote = Q_NOTSET;			goto insl_ch;		}		quote = Q_NOTSET;	}	/*	 * !!!	 * Translate "<CH_HEX>[isxdigit()]*" to a character with a hex value:	 * this test delimits the value by any non-hex character.  Offset by	 * one, we use 0 to mean that we've found <CH_HEX>.	 */	if (hexcnt > 1 && !isxdigit(evp->e_c)) {		hexcnt = 0;		if (txt_hex(sp, tp))			goto err;	}	switch (evp->e_value) {	case K_CR:				/* Carriage return. */	case K_NL:				/* New line. */		/* Return in script windows and the command line. */k_cr:		if (LF_ISSET(TXT_CR)) {			/*			 * If this was a map, we may have not displayed			 * the line.  Display it, just in case.			 *			 * If a script window and not the colon line,			 * push a <cr> so it gets executed.			 */			if (LF_ISSET(TXT_INFOLINE)) {				if (vs_change(sp, tp->lno, LINE_RESET))					goto err;			} else if (F_ISSET(sp, SC_SCRIPT))				(void)v_event_push(sp, NULL, "\r", 1, CH_NOMAP);			/* Set term condition: if empty. */			if (tp->cno <= tp->offset)				tp->term = TERM_CR;			/*			 * Set term condition: if searching incrementally and			 * the user entered a pattern, return a completed			 * search, regardless if the entire pattern was found.			 */			if (FL_ISSET(is_flags, IS_RUNNING) &&			    tp->cno >= tp->offset + 1)				tp->term = TERM_SEARCH;			goto k_escape;		}#define	LINE_RESOLVE {							\		/*							\		 * Handle abbreviations.  If there was one, discard the	\		 * replay characters.					\		 */							\		if (abb == AB_INWORD &&					\		    !LF_ISSET(TXT_REPLAY) && F_ISSET(gp, G_ABBREV)) {	\			if (txt_abbrev(sp, tp, &evp->e_c,		\			    LF_ISSET(TXT_INFOLINE), &tmp,		\			    &ab_turnoff))				\				goto err;				\			if (tmp) {					\				if (LF_ISSET(TXT_RECORD))		\					rcol -= tmp + 1;		\				goto resolve;				\			}						\		}							\		if (abb != AB_NOTSET)					\			abb = AB_NOTWORD;				\		if (UNMAP_TST)						\			txt_unmap(sp, tp, &ec_flags);			\		/* Delete any appended cursor. */			\		if (LF_ISSET(TXT_APPENDEOL)) {				\			--tp->len;					\			--tp->insert;					\		}							\}		LINE_RESOLVE;		/*		 * Save the current line information for restoration in		 * txt_backup().  Set the new line length.		 */		tp->sv_len = tp->len;		tp->sv_cno = tp->cno;		tp->len = tp->cno;		/* Update the old line. */		if (vs_change(sp, tp->lno, LINE_RESET))			goto err;		/*		 * Historic practice, when the autoindent edit option was set,		 * was to delete <blank> characters following the inserted		 * newline.  This affected the 'R', 'c', and 's' commands; 'c'		 * and 's' retained the insert characters only, 'R' moved the		 * overwrite and insert characters into the next TEXT structure.		 * We keep track of the number of characters erased for the 'R'		 * command so that the final resolution of the line is correct.		 */		tp->R_erase = 0;		owrite = tp->owrite;		insert = tp->insert;		if (LF_ISSET(TXT_REPLACE) && owrite != 0) {			for (p = tp->lb + tp->cno; owrite > 0 && isblank(*p);			    ++p, --owrite, ++tp->R_erase);			if (owrite == 0)				for (; insert > 0 && isblank(*p);				    ++p, ++tp->R_erase, --insert);		} else {			p = tp->lb + tp->cno + owrite;			if (O_ISSET(sp, O_AUTOINDENT))				for (; insert > 0 &&				    isblank(*p); ++p, --insert);			owrite = 0;		}		/* Set up bookkeeping for the new line. */		if ((ntp = text_init(sp, p,		    insert + owrite, insert + owrite + 32)) == NULL)			goto err;		ntp->insert = insert;		ntp->owrite = owrite;		ntp->lno = tp->lno + 1;		/*		 * Reset the autoindent line value.  0^D keeps the autoindent		 * line from changing, ^D changes the level, even if there were		 * no characters in the old line.  Note, if using the current		 * tp structure, use the cursor as the length, the autoindent		 * characters may have been erased.		 */		if (LF_ISSET(TXT_AUTOINDENT)) {			if (carat == C_NOCHANGE) {				if (v_txt_auto(sp, OOBLNO, &ait, ait.ai, ntp))					goto err;				FREE_SPACE(sp, ait.lb, ait.lb_len);			} else				if (v_txt_auto(sp, OOBLNO, tp, tp->cno, ntp))					goto err;			carat = C_NOTSET;		}		/* Reset the cursor. */		ntp->cno = ntp->ai;		/*		 * If we're here because wrapmargin was set and we've broken a		 * line, there may be additional information (i.e. the start of		 * a line) in the wmt structure.		 */		if (wm_set) {			if (wmt.offset != 0 ||			    wmt.owrite != 0 || wmt.insert != 0) {#define	WMTSPACE	wmt.offset + wmt.owrite + wmt.insert				BINC_GOTO(sp, ntp->lb,				    ntp->lb_len, ntp->len + WMTSPACE + 32);				memmove(ntp->lb + ntp->cno, wmt.lb, WMTSPACE);				ntp->len += WMTSPACE;				ntp->cno += wmt.offset;				ntp->owrite = wmt.owrite;				ntp->insert = wmt.insert;			}			wm_set = 0;		}		/* New lines are TXT_APPENDEOL. */		if (ntp->owrite == 0 && ntp->insert == 0) {			BINC_GOTO(sp, ntp->lb, ntp->lb_len, ntp->len + 1);			LF_SET(TXT_APPENDEOL);			ntp->lb[ntp->cno] = CH_CURSOR;			++ntp->insert;			++ntp->len;		}		/*		 * Swap old and new TEXT's, and insert the new TEXT into the		 * queue.		 *		 * !!!		 * DON'T insert until the old line has been updated, or the		 * inserted line count in line.c:db_get() will be wrong.		 */		tp = ntp;		CIRCLEQ_INSERT_TAIL(&sp->tiq, tp, q);		/* Update the new line. */		if (vs_change(sp, tp->lno, LINE_INSERT))			goto err;		goto resolve;	case K_ESCAPE:				/* Escape. */		if (!LF_ISSET(TXT_ESCAPE))			goto ins_ch;		/* If we have a count, start replaying the input. */		if (rcount > 1) {			--rcount;			rcol = 0;			abb = AB_NOTSET;			LF_CLR(TXT_RECORD);			LF_SET(TXT_REPLAY);			/*			 * Some commands (e.g. 'o') need a <newline> for each			 * repetition.			 */			if (LF_ISSET(TXT_ADDNEWLINE))				goto k_cr;			/*			 * The R command turns into the 'a' command after the			 * first repetition.			 */			if (LF_ISSET(TXT_REPLACE)) {				tp->insert = tp->owrite;				tp->owrite = 0;				LF_CLR(TXT_REPLACE);			}			goto replay;		}		/* Set term condition: if empty. */		if (tp->cno <= tp->offset)			tp->term = TERM_ESC;		/*		 * Set term condition: if searching incrementally and the user		 * entered a pattern, return a completed search, regardless if		 * the entire pattern was found.		 */		if (FL_ISSET(is_flags, IS_RUNNING) && tp->cno >= tp->offset + 1)			tp->term = TERM_SEARCH;k_escape:	LINE_RESOLVE;		/*		 * Clean up for the 'R' command, restoring overwrite		 * characters, and making them into insert characters.		 */		if (LF_ISSET(TXT_REPLACE))			txt_Rresolve(sp, &sp->tiq, tp, len);		/*		 * If there are any overwrite characters, copy down		 * any insert characters, and decrement the length.		 */		if (tp->owrite) {			if (tp->insert)				memmove(tp->lb + tp->cno,				    tp->lb + tp->cno + tp->owrite, tp->insert);			tp->len -= tp->owrite;		}		/*		 * Optionally resolve the lines into the file.  If not		 * resolving the lines into the file, end the line with		 * a nul.  If the line is empty, then set the length to		 * 0, the termination condition has already been set.		 *		 * XXX		 * This is wrong, should pass back a length.		 */		if (LF_ISSET(TXT_RESOLVE)) {			if (txt_resolve(sp, &sp->tiq, flags))				goto err;		} else {			BINC_GOTO(sp, tp->lb, tp->lb_len, tp->len + 1);			tp->lb[tp->len] = '\0';		}		/*		 * Set the return cursor position to rest on the last		 * inserted character.		 */		if (tp->cno != 0)			--tp->cno;		/* Update the last line. */		if (vs_change(sp, tp->lno, LINE_RESET))			return (1);		goto done;	case K_CARAT:			/* Delete autoindent chars. */		if (tp->cno <= tp->ai && LF_ISSET(TXT_AUTOINDENT))			carat = C_CARATSET;		goto ins_ch;	case K_ZERO:			/* Delete autoindent chars. */		if (tp->cno <= tp->ai && LF_ISSET(TXT_AUTOINDENT))			carat = C_ZEROSET;		goto ins_ch;	case K_CNTRLD:			/* Delete autoindent char. */		/*		 * If in the first column or no characters to erase, ignore		 * the ^D (this matches historic practice).  If not doing		 * autoindent or already inserted non-ai characters, it's a		 * literal.  The latter test is done in the switch, as the		 * CARAT forms are N + 1, not N.		 */		if (!LF_ISSET(TXT_AUTOINDENT))			goto ins_ch;		if (tp->cno == 0)			goto resolve;		switch (carat) {		case C_CARATSET:	/* ^^D */			if (tp->ai == 0 || tp->cno > tp->ai + tp->offset + 1)				goto ins_ch;			/* Save the ai string for later. */			ait.lb = NULL;			ait.lb_len = 0;			BINC_GOTO(sp, ait.lb, ait.lb_len, tp->ai);			memmove(ait.lb, tp->lb, tp->ai);			ait.ai = ait.len = tp->ai;			carat = C_NOCHANGE;			goto leftmargin;		case C_ZEROSET:		/* 0^D */			if (tp->ai == 0 || tp->cno > tp->ai + tp->offset + 1)				goto ins_ch;			carat = C_NOTSET;leftmargin:		tp->lb[tp->cno - 1] = ' ';			tp->owrite += tp->cno - tp->offset;			tp->ai = 0;			tp->cno = tp->offset;			break;		case C_NOTSET:		/* ^D */			if (tp->ai == 0 || tp->cno > tp->ai + tp->offset)				goto ins_ch;			(void)txt_dent(sp, tp, 0);			break;		default:			abort();		}		break;	case K_VERASE:			/* Erase the last character. */		/* If can erase over the prompt, return. */		if (tp->cno <= tp->offset && LF_ISSET(TXT_BS)) {			tp->term = TERM_BS;			goto done;		}		/*		 * If at the beginning of the line, try and drop back to a		 * previously inserted line.		 */		if (tp->cno == 0) {			if ((ntp =			    txt_backup(sp, &sp->tiq, tp, &flags)) == NULL)				goto err;			tp = ntp;			break;		}		/* If nothing to erase, bell the user. */		if (tp->cno <= tp->offset) {			if (!LF_ISSET(TXT_REPLAY))				txt_nomorech(sp);			break;		}		/* Drop back one character. */		--tp->cno;		/*		 * Historically, vi didn't replace the erased characters with		 * <blank>s, presumably because it's easier to fix a minor		 * typing mistake and continue on if the previous letters are		 * already there.  This is a problem for incremental searching,		 * because the user can no longer tell where they are in the		 * colon command line because the cursor is at the last search		 * point in the screen.  So, if incrementally searching, erase		 * the erased characters from the screen.		 */		if (FL_ISSET(is_flags, IS_RUNNING))			tp->lb[tp->cno] = ' ';		/*		 * Increment overwrite, decrement ai if deleted.		 *		 * !!!		 * Historic vi did not permit users to use erase characters		 * to delete autoindent characters.  We do.  Eat hot death,		 * POSIX.		 */		++tp->owrite;		if (tp->cno < tp->ai)			--tp->ai;		/* Reset if we deleted an incremental search character. */		if (FL_ISSET(is_flags, IS_RUNNING))			FL_SET(is_flags, IS_RESTART);		break;	case K_VWERASE:			/* Skip back one word. */		/*		 * If at the beginning of the line, try and drop back to a		 * previously inserted line.		 */		if (tp->cno == 0) {			if ((ntp =			    txt_backup(sp, &sp->tiq, tp, &flags)) == NULL)				goto err;			tp = ntp;		}		/*		 * If at offset, nothing to erase so bell the user.		 */		if (tp->cno <= tp->offset) {			if (!LF_ISSET(TXT_REPLAY))				txt_nomorech(sp);			break;		}		/*		 * The first werase goes back to any autoindent column and the		 * second werase goes back to the offset.		 *		 * !!!		 * Historic vi did not permit users to use erase characters to		 * delete autoindent characters.		 */		if (tp->ai && tp->cno > tp->ai)			max = tp->ai;		else {			tp->ai = 0;			max = tp->offset;		}		/* Skip over trailing space characters. */		while (tp->cno > max && isblank(tp->lb[tp->cno - 1])) {			--tp->cno;			++tp->owrite;		}		if (tp->cno == max)			break;		/*		 * There are three types of word erase found on UNIX systems.		 * They can be identified by how the string /a/b/c is treated		 * -- as 1, 3, or 6 words.  Historic vi had two classes of		 * characters, and strings were delimited by them and		 * <blank>'s, so, 6 words.  The historic tty interface used		 * <blank>'s to delimit strings, so, 1 word.  The algorithm		 * offered in the 4.4BSD tty interface (as stty altwerase)		 * treats it as 3 words -- there are two classes of		 * characters, and strings are delimited by them and		 * <blank>'s.  The difference is that the type of the first		 * erased character erased is ignored, which is exactly right		 * when erasing pathname components.  The edit options		 * TXT_ALTWERASE and TXT_TTYWERASE specify the 4.4BSD tty		 * interface and the historic tty driver behavior,		 * respectively, and the default is the same as the historic		 * vi behavior.		 *		 * Overwrite erased characters if doing incremental search;		 * see comment above.		 */		if (LF_ISSET(TXT_TTYWERASE))			while (tp->cno > max) {				--tp->cno;				++tp->owrite;				if (FL_ISSET(is_flags, IS_RUNNING))					tp->lb[tp->cno] = ' ';				if (isblank(tp->lb[tp->cno - 1]))					break;			}		else {			if (LF_ISSET(TXT_ALTWERASE)) {				--tp->cno;				++tp->owrite;				if (FL_ISSET(is_flags, IS_RUNNING))					tp->lb[tp->cno] = ' ';				if (isblank(tp->lb[tp->cno - 1]))					break;			}			if (tp->cno > max)				tmp = inword(tp->lb[tp->cno - 1]);			while (tp->cno > max) {				--tp->cno;				++tp->owrite;				if (FL_ISSET(is_flags, IS_RUNNING))					tp->lb[tp->cno] = ' ';				if (tmp != inword(tp->lb[tp->cno - 1])				    || isblank(tp->lb[tp->cno - 1]))					break;			}		}		/* Reset if we deleted an incremental search character. */		if (FL_ISSET(is_flags, IS_RUNNING))			FL_SET(is_flags, IS_RESTART);		break;	case K_VKILL:			/* Restart this line. */		/*		 * !!!		 * If at the beginning of the line, try and drop back to a		 * previously inserted line.  Historic vi did not permit		 * users to go back to previous lines.		 */		if (tp->cno == 0) {			if ((ntp =			    txt_backup(sp, &sp->tiq, tp, &flags)) == NULL)				goto err;			tp = ntp;		}		/* If at offset, nothing to erase so bell the user. */		if (tp->cno <= tp->offset) {			if (!LF_ISSET(TXT_REPLAY))				txt_nomorech(sp);			break;		}		/*		 * First kill goes back to any autoindent and second kill goes		 * back to the offset.		 *		 * !!!		 * Historic vi did not permit users to use erase characters to		 * delete autoindent characters.		 */		if (tp->ai && tp->cno > tp->ai)			max = tp->ai;		else {			tp->ai = 0;			max = tp->offset;		}		tp->owrite += tp->cno - max;		/*		 * Overwrite erased characters if doing incremental search;		 * see comment above.		 */		if (FL_ISSET(is_flags, IS_RUNNING))			do {				tp->lb[--tp->cno] = ' ';			} while (tp->cno > max);		else			tp->cno = max;		/* Reset if we deleted an incremental search character. */		if (FL_ISSET(is_flags, IS_RUNNING))			FL_SET(is_flags, IS_RESTART);		break;	case K_CNTRLT:			/* Add autoindent characters. */		if (!LF_ISSET(TXT_CNTRLT))			goto ins_ch;		if (txt_dent(sp, tp, 1))			goto err;		goto ebuf_chk;	case K_RIGHTBRACE:	case K_RIGHTPAREN:		if (LF_ISSET(TXT_SHOWMATCH))			showmatch = 1;		goto ins_ch;	case K_BACKSLASH:		/* Quote next erase/kill. */		/*		 * !!!		 * Historic vi tried to make abbreviations after a backslash		 * escape work.  If you did ":ab x y", and inserted "x\^H",		 * (assuming the erase character was ^H) you got "x^H", and		 * no abbreviation was done.  If you inserted "x\z", however,		 * it tried to back up and do the abbreviation, i.e. replace		 * 'x' with 'y'.  The problem was it got it wrong, and you		 * ended up with "zy\".		 *		 * This is really hard to do (you have to remember the		 * word/non-word state, for example), and doesn't make any		 * sense to me.  Both backslash and the characters it		 * (usually) escapes will individually trigger the		 * abbreviation, so I don't see why the combination of them		 * wouldn't.  I don't expect to get caught on this one,		 * particularly since it never worked right, but I've been		 * wrong before.		 *		 * Do the tests for abbreviations, so ":ab xa XA",		 * "ixa\<K_VERASE>" performs the abbreviation.		 */		quote = Q_BNEXT;		goto insq_ch;	case K_VLNEXT:			/* Quote next character. */		evp->e_c = '^';		quote = Q_VNEXT;		FL_SET(ec_flags, EC_QUOTED);		/*		 * !!!		 * Skip the tests for abbreviations, so ":ab xa XA",		 * "ixa^V<space>" doesn't perform the abbreviation.		 */		goto insl_ch;	case K_HEXCHAR:		hexcnt = 1;		goto insq_ch;	default:			/* Insert the character. */ins_ch:		/*		 * Historically, vi eliminated nul's out of hand.  If the		 * beautify option was set, it also deleted any unknown		 * ASCII value less than space (040) and the del character		 * (0177), except for tabs.  Unknown is a key word here.		 * Most vi documentation claims that it deleted everything		 * but <tab>, <nl> and <ff>, as that's what the original		 * 4BSD documentation said.  This is obviously wrong,		 * however, as <esc> would be included in that list.  What		 * we do is eliminate any unquoted, iscntrl() character that		 * wasn't a replay and wasn't handled specially, except		 * <tab> or <ff>.		 */		if (LF_ISSET(TXT_BEAUTIFY) && iscntrl(evp->e_c) &&		    evp->e_value != K_FORMFEED && evp->e_value != K_TAB) {			msgq(sp, M_BERR,			    "192|Illegal character; quote to enter");			if (LF_ISSET(TXT_REPLAY))				goto done;			break;		}insq_ch:	/*		 * If entering a non-word character after a word, check for		 * abbreviations.  If there was one, discard replay characters.		 * If entering a blank character, check for unmap commands,		 * as well.		 */		if (!inword(evp->e_c)) {			if (abb == AB_INWORD &&			    !LF_ISSET(TXT_REPLAY) && F_ISSET(gp, G_ABBREV)) {				if (txt_abbrev(sp, tp, &evp->e_c,				    LF_ISSET(TXT_INFOLINE), &tmp, &ab_turnoff))					goto err;				if (tmp) {					if (LF_ISSET(TXT_RECORD))						rcol -= tmp + 1;					goto resolve;				}			}			if (isblank(evp->e_c) && UNMAP_TST)				txt_unmap(sp, tp, &ec_flags);		}		if (abb != AB_NOTSET)			abb = inword(evp->e_c) ? AB_INWORD : AB_NOTWORD;insl_ch:	if (txt_insch(sp, tp, &evp->e_c, flags))			goto err;		/*		 * If we're using K_VLNEXT to quote the next character, then		 * we want the cursor to position itself on the ^ placeholder		 * we're displaying, to match historic practice.		 */		if (quote == Q_VNEXT) {			--tp->cno;			++tp->owrite;		}		/*		 * !!!		 * Translate "<CH_HEX>[isxdigit()]*" to a character with		 * a hex value: this test delimits the value by the max		 * number of hex bytes.  Offset by one, we use 0 to mean		 * that we've found <CH_HEX>.		 */		if (hexcnt != 0 && hexcnt++ == sizeof(CHAR_T) * 2 + 1) {			hexcnt = 0;			if (txt_hex(sp, tp))				goto err;		}		/*		 * Check to see if we've crossed the margin.		 *		 * !!!		 * In the historic vi, the wrapmargin value was figured out		 * using the display widths of the characters, i.e. <tab>		 * characters were counted as two characters if the list edit		 * option is set, but as the tabstop edit option number of		 * characters otherwise.  That's what the vs_column() function		 * gives us, so we use it.		 */		if (margin != 0) {			if (vs_column(sp, &tcol))				goto err;			if (tcol >= margin) {				if (txt_margin(sp, tp, &wmt, &tmp, flags))					goto err;				if (tmp) {					if (isblank(evp->e_c))						wm_skip = 1;					wm_set = 1;					goto k_cr;				}			}		}		/*		 * If we've reached the end of the buffer, then we need to		 * switch into insert mode.  This happens when there's a		 * change to a mark and the user puts in more characters than		 * the length of the motion.		 */ebuf_chk:	if (tp->cno >= tp->len) {			BINC_GOTO(sp, tp->lb, tp->lb_len, tp->len + 1);			LF_SET(TXT_APPENDEOL);			tp->lb[tp->cno] = CH_CURSOR;			++tp->insert;			++tp->len;		}		/* Step the quote state forward. */		if (quote != Q_NOTSET) {			if (quote == Q_BNEXT)				quote = Q_BTHIS;			if (quote == Q_VNEXT)				quote = Q_VTHIS;		}		break;	}#ifdef DEBUG	if (tp->cno + tp->insert + tp->owrite != tp->len) {		msgq(sp, M_ERR,		    "len %u != cno: %u ai: %u insert %u overwrite %u",		    tp->len, tp->cno, tp->ai, tp->insert, tp->owrite);		if (LF_ISSET(TXT_REPLAY))			goto done;		tp->len = tp->cno + tp->insert + tp->owrite;	}#endifresolve:/*	 * 1: If we don't need to know where the cursor really is and we're	 *    replaying text, keep going.	 */	if (margin == 0 && LF_ISSET(TXT_REPLAY))		goto replay;	/*	 * 2: Reset the line.  Don't bother unless we're about to wait on	 *    a character or we need to know where the cursor really is.	 *    We have to do this before showing matching characters so the	 *    user can see what they're matching.	 */	if ((margin != 0 || !KEYS_WAITING(sp)) &&	    vs_change(sp, tp->lno, LINE_RESET))		return (1);	/*	 * 3: If there aren't keys waiting, display the matching character.	 *    We have to do this before resolving any messages, otherwise	 *    the error message from a missing match won't appear correctly.	 */	if (showmatch) {		if (!KEYS_WAITING(sp) && txt_showmatch(sp, tp))			return (1);		showmatch = 0;	}	/*	 * 4: If there have been messages and we're not editing on the colon	 *    command line or doing file name completion, resolve them.	 */	if ((vip->totalcount != 0 || F_ISSET(gp, G_BELLSCHED)) &&	    !F_ISSET(sp, SC_TINPUT_INFO) && !filec_redraw && vs_resolve(sp, 0))		return (1);	/*	 * 5: Refresh the screen if we're about to wait on a character or we	 *    need to know where the cursor really is.	 */	if (margin != 0 || !KEYS_WAITING(sp)) {		UPDATE_POSITION(sp, tp);		if (vs_refresh(sp, margin != 0))			return (1);	}	/* 6: Proceed with the incremental search. */	if (FL_ISSET(is_flags, IS_RUNNING) && txt_isrch(sp, vp, tp, &is_flags))		return (1);	/* 7: Next character... */	if (LF_ISSET(TXT_REPLAY))		goto replay;	goto next;done:	/* Leave input mode. */	F_CLR(sp, SC_TINPUT);	/* If recording for playback, save it. */	if (LF_ISSET(TXT_RECORD))		vip->rep_cnt = rcol;	/*	 * If not working on the colon command line, set the final cursor	 * position.	 */	if (!F_ISSET(sp, SC_TINPUT_INFO)) {		vp->m_final.lno = tp->lno;		vp->m_final.cno = tp->cno;	}	return (0);err:alloc_err:	txt_err(sp, &sp->tiq);	return (1);}