119304Speter/*-
219304Speter * Copyright (c) 1993, 1994
319304Speter *	The Regents of the University of California.  All rights reserved.
419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996
519304Speter *	Keith Bostic.  All rights reserved.
619304Speter *
719304Speter * See the LICENSE file for redistribution information.
819304Speter */
919304Speter
1019304Speter#include "config.h"
1119304Speter
1219304Speter#ifndef lint
13254225Speterstatic const char sccsid[] = "$Id: v_txt.c,v 11.5 2013/05/19 20:37:45 bentley Exp $";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/types.h>
1719304Speter#include <sys/queue.h>
1819304Speter#include <sys/stat.h>
1919304Speter
2019304Speter#include <bitstring.h>
2119304Speter#include <ctype.h>
2219304Speter#include <errno.h>
2319304Speter#include <limits.h>
2419304Speter#include <stdio.h>
2519304Speter#include <stdlib.h>
2619304Speter#include <string.h>
2719304Speter#include <unistd.h>
2819304Speter
2919304Speter#include "../common/common.h"
3019304Speter#include "vi.h"
3119304Speter
3219304Speterstatic int	 txt_abbrev __P((SCR *, TEXT *, CHAR_T *, int, int *, int *));
3319304Speterstatic void	 txt_ai_resolve __P((SCR *, TEXT *, int *));
3419304Speterstatic TEXT	*txt_backup __P((SCR *, TEXTH *, TEXT *, u_int32_t *));
3519304Speterstatic int	 txt_dent __P((SCR *, TEXT *, int));
3619304Speterstatic int	 txt_emark __P((SCR *, TEXT *, size_t));
3719304Speterstatic void	 txt_err __P((SCR *, TEXTH *));
3819304Speterstatic int	 txt_fc __P((SCR *, TEXT *, int *));
3919304Speterstatic int	 txt_fc_col __P((SCR *, int, ARGS **));
4019304Speterstatic int	 txt_hex __P((SCR *, TEXT *));
4119304Speterstatic int	 txt_insch __P((SCR *, TEXT *, CHAR_T *, u_int));
4219304Speterstatic int	 txt_isrch __P((SCR *, VICMD *, TEXT *, u_int8_t *));
4319304Speterstatic int	 txt_map_end __P((SCR *));
4419304Speterstatic int	 txt_map_init __P((SCR *));
4519304Speterstatic int	 txt_margin __P((SCR *, TEXT *, TEXT *, int *, u_int32_t));
4619304Speterstatic void	 txt_nomorech __P((SCR *));
4719304Speterstatic void	 txt_Rresolve __P((SCR *, TEXTH *, TEXT *, const size_t));
4819304Speterstatic int	 txt_resolve __P((SCR *, TEXTH *, u_int32_t));
4919304Speterstatic int	 txt_showmatch __P((SCR *, TEXT *));
5019304Speterstatic void	 txt_unmap __P((SCR *, TEXT *, u_int32_t *));
5119304Speter
5219304Speter/* Cursor character (space is hard to track on the screen). */
5319304Speter#if defined(DEBUG) && 0
5419304Speter#undef	CH_CURSOR
5519304Speter#define	CH_CURSOR	'+'
5619304Speter#endif
5719304Speter
5819304Speter/*
5919304Speter * v_tcmd --
6019304Speter *	Fill a buffer from the terminal for vi.
6119304Speter *
6219304Speter * PUBLIC: int v_tcmd __P((SCR *, VICMD *, ARG_CHAR_T, u_int));
6319304Speter */
6419304Speterint
65254225Speterv_tcmd(SCR *sp, VICMD *vp, ARG_CHAR_T prompt, u_int flags)
6619304Speter{
6719304Speter	/* Normally, we end up where we started. */
6819304Speter	vp->m_final.lno = sp->lno;
6919304Speter	vp->m_final.cno = sp->cno;
7019304Speter
7119304Speter	/* Initialize the map. */
7219304Speter	if (txt_map_init(sp))
7319304Speter		return (1);
7419304Speter
7519304Speter	/* Move to the last line. */
7619304Speter	sp->lno = TMAP[0].lno;
7719304Speter	sp->cno = 0;
7819304Speter
7919304Speter	/* Don't update the modeline for now. */
8019304Speter	F_SET(sp, SC_TINPUT_INFO);
8119304Speter
8219304Speter	/* Set the input flags. */
8319304Speter	LF_SET(TXT_APPENDEOL |
8419304Speter	    TXT_CR | TXT_ESCAPE | TXT_INFOLINE | TXT_MAPINPUT);
8519304Speter	if (O_ISSET(sp, O_ALTWERASE))
8619304Speter		LF_SET(TXT_ALTWERASE);
8719304Speter	if (O_ISSET(sp, O_TTYWERASE))
8819304Speter		LF_SET(TXT_TTYWERASE);
8919304Speter
9019304Speter	/* Do the input thing. */
9119304Speter	if (v_txt(sp, vp, NULL, NULL, 0, prompt, 0, 1, flags))
9219304Speter		return (1);
9319304Speter
9419304Speter	/* Reenable the modeline updates. */
9519304Speter	F_CLR(sp, SC_TINPUT_INFO);
9619304Speter
9719304Speter	/* Clean up the map. */
9819304Speter	if (txt_map_end(sp))
9919304Speter		return (1);
10019304Speter
10119304Speter	if (IS_ONELINE(sp))
10219304Speter		F_SET(sp, SC_SCR_REDRAW);	/* XXX */
10319304Speter
10419304Speter	/* Set the cursor to the resulting position. */
10519304Speter	sp->lno = vp->m_final.lno;
10619304Speter	sp->cno = vp->m_final.cno;
10719304Speter
10819304Speter	return (0);
10919304Speter}
11019304Speter
11119304Speter/*
11219304Speter * txt_map_init
11319304Speter *	Initialize the screen map for colon command-line input.
11419304Speter */
11519304Speterstatic int
116254225Spetertxt_map_init(SCR *sp)
11719304Speter{
11819304Speter	SMAP *esmp;
11919304Speter	VI_PRIVATE *vip;
12019304Speter
12119304Speter	vip = VIP(sp);
12219304Speter	if (!IS_ONELINE(sp)) {
12319304Speter		/*
12419304Speter		 * Fake like the user is doing input on the last line of the
12519304Speter		 * screen.  This makes all of the scrolling work correctly,
12619304Speter		 * and allows us the use of the vi text editing routines, not
12719304Speter		 * to mention practically infinite length ex commands.
12819304Speter		 *
12919304Speter		 * Save the current location.
13019304Speter		 */
13119304Speter		vip->sv_tm_lno = TMAP->lno;
13219304Speter		vip->sv_tm_soff = TMAP->soff;
13319304Speter		vip->sv_tm_coff = TMAP->coff;
13419304Speter		vip->sv_t_maxrows = sp->t_maxrows;
13519304Speter		vip->sv_t_minrows = sp->t_minrows;
13619304Speter		vip->sv_t_rows = sp->t_rows;
13719304Speter
13819304Speter		/*
13919304Speter		 * If it's a small screen, TMAP may be small for the screen.
14019304Speter		 * Fix it, filling in fake lines as we go.
14119304Speter		 */
14219304Speter		if (IS_SMALL(sp))
14319304Speter			for (esmp =
14419304Speter			    HMAP + (sp->t_maxrows - 1); TMAP < esmp; ++TMAP) {
14519304Speter				TMAP[1].lno = TMAP[0].lno + 1;
14619304Speter				TMAP[1].coff = HMAP->coff;
14719304Speter				TMAP[1].soff = 1;
14819304Speter			}
14919304Speter
15019304Speter		/* Build the fake entry. */
15119304Speter		TMAP[1].lno = TMAP[0].lno + 1;
15219304Speter		TMAP[1].soff = 1;
15319304Speter		TMAP[1].coff = 0;
15419304Speter		SMAP_FLUSH(&TMAP[1]);
15519304Speter		++TMAP;
15619304Speter
15719304Speter		/* Reset the screen information. */
15819304Speter		sp->t_rows = sp->t_minrows = ++sp->t_maxrows;
15919304Speter	}
16019304Speter	return (0);
16119304Speter}
16219304Speter
16319304Speter/*
16419304Speter * txt_map_end
16519304Speter *	Reset the screen map for colon command-line input.
16619304Speter */
16719304Speterstatic int
168254225Spetertxt_map_end(SCR *sp)
16919304Speter{
17019304Speter	VI_PRIVATE *vip;
17119304Speter	size_t cnt;
17219304Speter
17319304Speter	vip = VIP(sp);
17419304Speter	if (!IS_ONELINE(sp)) {
17519304Speter		/* Restore the screen information. */
17619304Speter		sp->t_rows = vip->sv_t_rows;
17719304Speter		sp->t_minrows = vip->sv_t_minrows;
17819304Speter		sp->t_maxrows = vip->sv_t_maxrows;
17919304Speter
18019304Speter		/*
18119304Speter		 * If it's a small screen, TMAP may be wrong.  Clear any
18219304Speter		 * lines that might have been overwritten.
18319304Speter		 */
18419304Speter		if (IS_SMALL(sp)) {
18519304Speter			for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) {
18619304Speter				(void)sp->gp->scr_move(sp, cnt, 0);
18719304Speter				(void)sp->gp->scr_clrtoeol(sp);
18819304Speter			}
18919304Speter			TMAP = HMAP + (sp->t_rows - 1);
19019304Speter		} else
19119304Speter			--TMAP;
19219304Speter
19319304Speter		/*
19419304Speter		 * The map may be wrong if the user entered more than one
19519304Speter		 * (logical) line.  Fix it.  If the user entered a whole
19619304Speter		 * screen, this will be slow, but we probably don't care.
19719304Speter		 */
19819304Speter		if (!O_ISSET(sp, O_LEFTRIGHT))
19919304Speter			while (vip->sv_tm_lno != TMAP->lno ||
20019304Speter			    vip->sv_tm_soff != TMAP->soff)
20119304Speter				if (vs_sm_1down(sp))
20219304Speter					return (1);
20319304Speter	}
20419304Speter
20519304Speter	/*
20619304Speter	 * Invalidate the cursor and the line size cache, the line never
20719304Speter	 * really existed.  This fixes bugs where the user searches for
20819304Speter	 * the last line on the screen + 1 and the refresh routine thinks
20919304Speter	 * that's where we just were.
21019304Speter	 */
21119304Speter	VI_SCR_CFLUSH(vip);
21219304Speter	F_SET(vip, VIP_CUR_INVALID);
21319304Speter
21419304Speter	return (0);
21519304Speter}
21619304Speter
21719304Speter/*
21819304Speter * If doing input mapping on the colon command line, may need to unmap
21919304Speter * based on the command.
22019304Speter */
22119304Speter#define	UNMAP_TST							\
22219304Speter	FL_ISSET(ec_flags, EC_MAPINPUT) && LF_ISSET(TXT_INFOLINE)
22319304Speter
22419304Speter/*
22519304Speter * Internally, we maintain tp->lno and tp->cno, externally, everyone uses
22619304Speter * sp->lno and sp->cno.  Make them consistent as necessary.
22719304Speter */
22819304Speter#define	UPDATE_POSITION(sp, tp) {					\
22919304Speter	(sp)->lno = (tp)->lno;						\
23019304Speter	(sp)->cno = (tp)->cno;						\
23119304Speter}
23219304Speter
23319304Speter/*
23419304Speter * v_txt --
23519304Speter *	Vi text input.
23619304Speter *
23719304Speter * PUBLIC: int v_txt __P((SCR *, VICMD *, MARK *,
238254225Speter * PUBLIC:    const CHAR_T *, size_t, ARG_CHAR_T, recno_t, u_long, u_int32_t));
23919304Speter */
24019304Speterint
241254225Speterv_txt(
242254225Speter	SCR *sp,
243254225Speter	VICMD *vp,
244254225Speter	MARK *tm,		/* To MARK. */
245254225Speter	const CHAR_T *lp,	/* Input line. */
246254225Speter	size_t len,		/* Input line length. */
247254225Speter	ARG_CHAR_T prompt,	/* Prompt to display. */
248254225Speter	recno_t ai_line,	/* Line number to use for autoindent count. */
249254225Speter	u_long rcount,		/* Replay count. */
250254225Speter	u_int32_t flags)	/* TXT_* flags. */
25119304Speter{
252254225Speter	EVENT ev, *evp = NULL;	/* Current event. */
25319304Speter	EVENT fc;		/* File name completion event. */
25419304Speter	GS *gp;
25519304Speter	TEXT *ntp, *tp;		/* Input text structures. */
25619304Speter	TEXT ait;		/* Autoindent text structure. */
257254225Speter	TEXT wmt = {{ 0 }};	/* Wrapmargin text structure. */
25819304Speter	TEXTH *tiqh;
25919304Speter	VI_PRIVATE *vip;
26019304Speter	abb_t abb;		/* State of abbreviation checks. */
26119304Speter	carat_t carat;		/* State of the "[^0]^D" sequences. */
26219304Speter	quote_t quote;		/* State of quotation. */
26319304Speter	size_t owrite, insert;	/* Temporary copies of TEXT fields. */
26419304Speter	size_t margin;		/* Wrapmargin value. */
26519304Speter	size_t rcol;		/* 0-N: insert offset in the replay buffer. */
26619304Speter	size_t tcol;		/* Temporary column. */
26719304Speter	u_int32_t ec_flags;	/* Input mapping flags. */
26819304Speter#define	IS_RESTART	0x01	/* Reset the incremental search. */
26919304Speter#define	IS_RUNNING	0x02	/* Incremental search turned on. */
27019304Speter	u_int8_t is_flags;
27119304Speter	int abcnt, ab_turnoff;	/* Abbreviation character count, switch. */
27219304Speter	int filec_redraw;	/* Redraw after the file completion routine. */
27319304Speter	int hexcnt;		/* Hex character count. */
27419304Speter	int showmatch;		/* Showmatch set on this character. */
27519304Speter	int wm_set, wm_skip;	/* Wrapmargin happened, blank skip flags. */
27619304Speter	int max, tmp;
277254225Speter	int nochange;
278254225Speter	CHAR_T *p;
27919304Speter
28019304Speter	gp = sp->gp;
28119304Speter	vip = VIP(sp);
28219304Speter
28319304Speter	/*
28419304Speter	 * Set the input flag, so tabs get displayed correctly
28519304Speter	 * and everyone knows that the text buffer is in use.
28619304Speter	 */
28719304Speter	F_SET(sp, SC_TINPUT);
28819304Speter
28919304Speter	/*
29019304Speter	 * Get one TEXT structure with some initial buffer space, reusing
29119304Speter	 * the last one if it's big enough.  (All TEXT bookkeeping fields
29219304Speter	 * default to 0 -- text_init() handles this.)  If changing a line,
29319304Speter	 * copy it into the TEXT buffer.
29419304Speter	 */
295254225Speter	tiqh = sp->tiq;
296254225Speter	if (!TAILQ_EMPTY(tiqh)) {
297254225Speter		tp = TAILQ_FIRST(tiqh);
298258231Sgjb		if (TAILQ_NEXT(tp, q) != NULL ||
299258231Sgjb		    tp->lb_len < (len + 32) * sizeof(CHAR_T)) {
30019304Speter			text_lfree(tiqh);
30119304Speter			goto newtp;
30219304Speter		}
30319304Speter		tp->ai = tp->insert = tp->offset = tp->owrite = 0;
30419304Speter		if (lp != NULL) {
30519304Speter			tp->len = len;
306254225Speter			BINC_RETW(sp, tp->lb, tp->lb_len, len);
307254225Speter			MEMMOVE(tp->lb, lp, len);
30819304Speter		} else
30919304Speter			tp->len = 0;
31019304Speter	} else {
31119304Speternewtp:		if ((tp = text_init(sp, lp, len, len + 32)) == NULL)
31219304Speter			return (1);
313254225Speter		TAILQ_INSERT_HEAD(tiqh, tp, q);
31419304Speter	}
31519304Speter
31619304Speter	/* Set default termination condition. */
31719304Speter	tp->term = TERM_OK;
31819304Speter
31919304Speter	/* Set the starting line, column. */
32019304Speter	tp->lno = sp->lno;
32119304Speter	tp->cno = sp->cno;
32219304Speter
32319304Speter	/*
32419304Speter	 * Set the insert and overwrite counts.  If overwriting characters,
32519304Speter	 * do insertion afterward.  If not overwriting characters, assume
32619304Speter	 * doing insertion.  If change is to a mark, emphasize it with an
32719304Speter	 * CH_ENDMARK character.
32819304Speter	 */
32919304Speter	if (len) {
33019304Speter		if (LF_ISSET(TXT_OVERWRITE)) {
33119304Speter			tp->owrite = (tm->cno - tp->cno) + 1;
33219304Speter			tp->insert = (len - tm->cno) - 1;
33319304Speter		} else
33419304Speter			tp->insert = len - tp->cno;
33519304Speter
33619304Speter		if (LF_ISSET(TXT_EMARK) && txt_emark(sp, tp, tm->cno))
33719304Speter			return (1);
33819304Speter	}
33919304Speter
34019304Speter	/*
34119304Speter	 * Many of the special cases in text input are to handle autoindent
34219304Speter	 * support.  Somebody decided that it would be a good idea if "^^D"
34319304Speter	 * and "0^D" deleted all of the autoindented characters.  In an editor
34419304Speter	 * that takes single character input from the user, this beggars the
34519304Speter	 * imagination.  Note also, "^^D" resets the next lines' autoindent,
34619304Speter	 * but "0^D" doesn't.
34719304Speter	 *
34819304Speter	 * We assume that autoindent only happens on empty lines, so insert
34919304Speter	 * and overwrite will be zero.  If doing autoindent, figure out how
35019304Speter	 * much indentation we need and fill it in.  Update input column and
35119304Speter	 * screen cursor as necessary.
35219304Speter	 */
35319304Speter	if (LF_ISSET(TXT_AUTOINDENT) && ai_line != OOBLNO) {
35419304Speter		if (v_txt_auto(sp, ai_line, NULL, 0, tp))
35519304Speter			return (1);
35619304Speter		tp->cno = tp->ai;
35719304Speter	} else {
35819304Speter		/*
35919304Speter		 * The cc and S commands have a special feature -- leading
36019304Speter		 * <blank> characters are handled as autoindent characters.
36119304Speter		 * Beauty!
36219304Speter		 */
36319304Speter		if (LF_ISSET(TXT_AICHARS)) {
36419304Speter			tp->offset = 0;
36519304Speter			tp->ai = tp->cno;
36619304Speter		} else
36719304Speter			tp->offset = tp->cno;
36819304Speter	}
36919304Speter
37019304Speter	/* If getting a command buffer from the user, there may be a prompt. */
37119304Speter	if (LF_ISSET(TXT_PROMPT)) {
37219304Speter		tp->lb[tp->cno++] = prompt;
37319304Speter		++tp->len;
37419304Speter		++tp->offset;
37519304Speter	}
37619304Speter
37719304Speter	/*
37819304Speter	 * If appending after the end-of-line, add a space into the buffer
37919304Speter	 * and move the cursor right.  This space is inserted, i.e. pushed
38019304Speter	 * along, and then deleted when the line is resolved.  Assumes that
38119304Speter	 * the cursor is already positioned at the end of the line.  This
38219304Speter	 * avoids the nastiness of having the cursor reside on a magical
38319304Speter	 * column, i.e. a column that doesn't really exist.  The only down
38419304Speter	 * side is that we may wrap lines or scroll the screen before it's
38519304Speter	 * strictly necessary.  Not a big deal.
38619304Speter	 */
38719304Speter	if (LF_ISSET(TXT_APPENDEOL)) {
38819304Speter		tp->lb[tp->cno] = CH_CURSOR;
38919304Speter		++tp->len;
39019304Speter		++tp->insert;
39119304Speter		(void)vs_change(sp, tp->lno, LINE_RESET);
39219304Speter	}
39319304Speter
39419304Speter	/*
39519304Speter	 * Historic practice is that the wrapmargin value was a distance
39619304Speter	 * from the RIGHT-HAND margin, not the left.  It's more useful to
39719304Speter	 * us as a distance from the left-hand margin, i.e. the same as
39819304Speter	 * the wraplen value.  The wrapmargin option is historic practice.
39919304Speter	 * Nvi added the wraplen option so that it would be possible to
40019304Speter	 * edit files with consistent margins without knowing the number of
40119304Speter	 * columns in the window.
40219304Speter	 *
40319304Speter	 * XXX
40419304Speter	 * Setting margin causes a significant performance hit.  Normally
40519304Speter	 * we don't update the screen if there are keys waiting, but we
40619304Speter	 * have to if margin is set, otherwise the screen routines don't
40719304Speter	 * know where the cursor is.
40819304Speter	 *
40919304Speter	 * !!!
41019304Speter	 * Abbreviated keys were affected by the wrapmargin option in the
41119304Speter	 * historic 4BSD vi.  Mapped keys were usually, but sometimes not.
41219304Speter	 * See the comment in vi/v_text():set_txt_std for more information.
41319304Speter	 *
41419304Speter	 * !!!
41519304Speter	 * One more special case.  If an inserted <blank> character causes
41619304Speter	 * wrapmargin to split the line, the next user entered character is
41719304Speter	 * discarded if it's a <space> character.
41819304Speter	 */
41919304Speter	wm_set = wm_skip = 0;
42019304Speter	if (LF_ISSET(TXT_WRAPMARGIN))
42119304Speter		if ((margin = O_VAL(sp, O_WRAPMARGIN)) != 0)
42219304Speter			margin = sp->cols - margin;
42319304Speter		else
42419304Speter			margin = O_VAL(sp, O_WRAPLEN);
42519304Speter	else
42619304Speter		margin = 0;
42719304Speter
42819304Speter	/* Initialize abbreviation checks. */
42919304Speter	abcnt = ab_turnoff = 0;
43019304Speter	abb = F_ISSET(gp, G_ABBREV) &&
43119304Speter	    LF_ISSET(TXT_MAPINPUT) ? AB_INWORD : AB_NOTSET;
43219304Speter
43319304Speter	/*
43419304Speter	 * Set up the dot command.  Dot commands are done by saving the actual
43519304Speter	 * characters and then reevaluating them so that things like wrapmargin
43619304Speter	 * can change between the insert and the replay.
43719304Speter	 *
43819304Speter	 * !!!
43919304Speter	 * Historically, vi did not remap or reabbreviate replayed input.  (It
44019304Speter	 * did beep at you if you changed an abbreviation and then replayed the
44119304Speter	 * input.  We're not that compatible.)  We don't have to do anything to
44219304Speter	 * avoid remapping, as we're not getting characters from the terminal
44319304Speter	 * routines.  Turn the abbreviation check off.
44419304Speter	 *
44519304Speter	 * XXX
44619304Speter	 * It would be nice if we could swallow backspaces and such, but it's
44719304Speter	 * not all that easy to do.  What we can do is turn off the common
44819304Speter	 * error messages during the replay.  Otherwise, when the user enters
44919304Speter	 * an illegal command, e.g., "Ia<erase><erase><erase><erase>b<escape>",
45019304Speter	 * and then does a '.', they get a list of error messages after command
45119304Speter	 * completion.
45219304Speter	 */
45319304Speter	rcol = 0;
45419304Speter	if (LF_ISSET(TXT_REPLAY)) {
45519304Speter		abb = AB_NOTSET;
45619304Speter		LF_CLR(TXT_RECORD);
45719304Speter	}
45819304Speter
45919304Speter	/* Other text input mode setup. */
46019304Speter	quote = Q_NOTSET;
46119304Speter	carat = C_NOTSET;
462254225Speter	nochange = 0;
46319304Speter	FL_INIT(is_flags,
46419304Speter	    LF_ISSET(TXT_SEARCHINCR) ? IS_RESTART | IS_RUNNING : 0);
46519304Speter	filec_redraw = hexcnt = showmatch = 0;
46619304Speter
46719304Speter	/* Initialize input flags. */
46819304Speter	ec_flags = LF_ISSET(TXT_MAPINPUT) ? EC_MAPINPUT : 0;
46919304Speter
47019304Speter	/* Refresh the screen. */
47119304Speter	UPDATE_POSITION(sp, tp);
47219304Speter	if (vs_refresh(sp, 1))
47319304Speter		return (1);
47419304Speter
47519304Speter	/* If it's dot, just do it now. */
47619304Speter	if (F_ISSET(vp, VC_ISDOT))
47719304Speter		goto replay;
47819304Speter
47919304Speter	/* Get an event. */
48019304Speter	evp = &ev;
48119304Speternext:	if (v_event_get(sp, evp, 0, ec_flags))
48219304Speter		return (1);
48319304Speter
48419304Speter	/*
48519304Speter	 * If file completion overwrote part of the screen and nothing else has
48619304Speter	 * been displayed, clean up.  We don't do this as part of the normal
48719304Speter	 * message resolution because we know the user is on the colon command
48819304Speter	 * line and there's no reason to enter explicit characters to continue.
48919304Speter	 */
49019304Speter	if (filec_redraw && !F_ISSET(sp, SC_SCR_EXWROTE)) {
49119304Speter		filec_redraw = 0;
49219304Speter
49319304Speter		fc.e_event = E_REPAINT;
49419304Speter		fc.e_flno = vip->totalcount >=
49519304Speter		    sp->rows ? 1 : sp->rows - vip->totalcount;
49619304Speter		fc.e_tlno = sp->rows;
49719304Speter		vip->linecount = vip->lcontinue = vip->totalcount = 0;
49819304Speter		(void)vs_repaint(sp, &fc);
49919304Speter		(void)vs_refresh(sp, 1);
50019304Speter	}
50119304Speter
50219304Speter	/* Deal with all non-character events. */
50319304Speter	switch (evp->e_event) {
50419304Speter	case E_CHARACTER:
50519304Speter		break;
50619304Speter	case E_ERR:
50719304Speter	case E_EOF:
50819304Speter		F_SET(sp, SC_EXIT_FORCE);
50919304Speter		return (1);
510254225Speter	case E_INTERRUPT:
511254225Speter		/*
512254225Speter		 * !!!
513254225Speter		 * Historically, <interrupt> exited the user from text input
514254225Speter		 * mode or cancelled a colon command, and returned to command
515254225Speter		 * mode.  It also beeped the terminal, but that seems a bit
516254225Speter		 * excessive.
517254225Speter		 */
518254225Speter		goto k_escape;
51919304Speter	case E_REPAINT:
52019304Speter		if (vs_repaint(sp, &ev))
52119304Speter			return (1);
52219304Speter		goto next;
52319304Speter	case E_WRESIZE:
52419304Speter		/* <resize> interrupts the input mode. */
52519304Speter		v_emsg(sp, NULL, VIM_WRESIZE);
526254225Speter		goto k_escape;
52719304Speter	default:
528254225Speter		v_event_err(sp, evp);
529254225Speter		goto k_escape;
53019304Speter	}
53119304Speter
53219304Speter	/*
53319304Speter	 * !!!
53419304Speter	 * If the first character of the input is a nul, replay the previous
53519304Speter	 * input.  (Historically, it's okay to replay non-existent input.)
53619304Speter	 * This was not documented as far as I know, and is a great test of vi
53719304Speter	 * clones.
53819304Speter	 */
539208612Sjh	if (LF_ISSET(TXT_RECORD) && rcol == 0 && evp->e_c == '\0') {
54019304Speter		if (vip->rep == NULL)
54119304Speter			goto done;
54219304Speter
54319304Speter		abb = AB_NOTSET;
54419304Speter		LF_CLR(TXT_RECORD);
54519304Speter		LF_SET(TXT_REPLAY);
54619304Speter		goto replay;
54719304Speter	}
54819304Speter
54919304Speter	/*
55019304Speter	 * File name completion and colon command-line editing.   We don't
55119304Speter	 * have enough meta characters, so we expect people to overload
55219304Speter	 * them.  If the two characters are the same, then we do file name
55319304Speter	 * completion if the cursor is past the first column, and do colon
55419304Speter	 * command-line editing if it's not.
55519304Speter	 */
55619304Speter	if (quote == Q_NOTSET) {
55719304Speter		int L__cedit, L__filec;
55819304Speter
55919304Speter		L__cedit = L__filec = 0;
56019304Speter		if (LF_ISSET(TXT_CEDIT) && O_STR(sp, O_CEDIT) != NULL &&
56119304Speter		    O_STR(sp, O_CEDIT)[0] == evp->e_c)
56219304Speter			L__cedit = 1;
56319304Speter		if (LF_ISSET(TXT_FILEC) && O_STR(sp, O_FILEC) != NULL &&
56419304Speter		    O_STR(sp, O_FILEC)[0] == evp->e_c)
56519304Speter			L__filec = 1;
56619304Speter		if (L__cedit == 1 && (L__filec == 0 || tp->cno == tp->offset)) {
56719304Speter			tp->term = TERM_CEDIT;
56819304Speter			goto k_escape;
56919304Speter		}
57019304Speter		if (L__filec == 1) {
57119304Speter			if (txt_fc(sp, tp, &filec_redraw))
57219304Speter				goto err;
57319304Speter			goto resolve;
57419304Speter		}
57519304Speter	}
57619304Speter
57719304Speter	/* Abbreviation overflow check.  See comment in txt_abbrev(). */
57819304Speter#define	MAX_ABBREVIATION_EXPANSION	256
57919304Speter	if (F_ISSET(&evp->e_ch, CH_ABBREVIATED)) {
58019304Speter		if (++abcnt > MAX_ABBREVIATION_EXPANSION) {
58119304Speter			if (v_event_flush(sp, CH_ABBREVIATED))
58219304Speter				msgq(sp, M_ERR,
58319304Speter"191|Abbreviation exceeded expansion limit: characters discarded");
58419304Speter			abcnt = 0;
58519304Speter			if (LF_ISSET(TXT_REPLAY))
58619304Speter				goto done;
58719304Speter			goto resolve;
58819304Speter		}
58919304Speter	} else
59019304Speter		abcnt = 0;
59119304Speter
59219304Speter	/* Check to see if the character fits into the replay buffers. */
59319304Speter	if (LF_ISSET(TXT_RECORD)) {
594254225Speter		BINC_GOTO(sp, EVENT, vip->rep,
59519304Speter		    vip->rep_len, (rcol + 1) * sizeof(EVENT));
59619304Speter		vip->rep[rcol++] = *evp;
59719304Speter	}
59819304Speter
599254225Speterreplay:	if (LF_ISSET(TXT_REPLAY)) {
600254225Speter		if (rcol == vip->rep_cnt)
601254225Speter			goto k_escape;
60219304Speter		evp = vip->rep + rcol++;
603254225Speter	}
60419304Speter
60519304Speter	/* Wrapmargin check for leading space. */
60619304Speter	if (wm_skip) {
60719304Speter		wm_skip = 0;
60819304Speter		if (evp->e_c == ' ')
60919304Speter			goto resolve;
61019304Speter	}
61119304Speter
61219304Speter	/* If quoted by someone else, simply insert the character. */
61319304Speter	if (F_ISSET(&evp->e_ch, CH_QUOTED))
61419304Speter		goto insq_ch;
61519304Speter
61619304Speter	/*
61719304Speter	 * !!!
61819304Speter	 * If this character was quoted by a K_VLNEXT or a backslash, replace
61919304Speter	 * the placeholder (a carat or a backslash) with the new character.
62019304Speter	 * If it was quoted by a K_VLNEXT, we've already adjusted the cursor
62119304Speter	 * because it has to appear on top of the placeholder character.  If
62219304Speter	 * it was quoted by a backslash, adjust the cursor now, the cursor
62319304Speter	 * doesn't appear on top of it.  Historic practice in both cases.
62419304Speter	 *
62519304Speter	 * Skip tests for abbreviations; ":ab xa XA" followed by "ixa^V<space>"
62619304Speter	 * doesn't perform an abbreviation.  Special case, ^V^J (not ^V^M) is
62719304Speter	 * the same as ^J, historically.
62819304Speter	 */
62919304Speter	if (quote == Q_BTHIS || quote == Q_VTHIS) {
63019304Speter		FL_CLR(ec_flags, EC_QUOTED);
63119304Speter		if (LF_ISSET(TXT_MAPINPUT))
63219304Speter			FL_SET(ec_flags, EC_MAPINPUT);
63319304Speter
63419304Speter		if (quote == Q_BTHIS &&
63519304Speter		    (evp->e_value == K_VERASE || evp->e_value == K_VKILL)) {
63619304Speter			quote = Q_NOTSET;
63719304Speter			--tp->cno;
63819304Speter			++tp->owrite;
63919304Speter			goto insl_ch;
64019304Speter		}
64119304Speter		if (quote == Q_VTHIS && evp->e_value != K_NL) {
64219304Speter			quote = Q_NOTSET;
64319304Speter			goto insl_ch;
64419304Speter		}
64519304Speter		quote = Q_NOTSET;
64619304Speter	}
64719304Speter
64819304Speter	/*
64919304Speter	 * !!!
65019304Speter	 * Translate "<CH_HEX>[isxdigit()]*" to a character with a hex value:
65119304Speter	 * this test delimits the value by any non-hex character.  Offset by
65219304Speter	 * one, we use 0 to mean that we've found <CH_HEX>.
65319304Speter	 */
654254225Speter	if (hexcnt > 1 && !ISXDIGIT(evp->e_c)) {
65519304Speter		hexcnt = 0;
65619304Speter		if (txt_hex(sp, tp))
65719304Speter			goto err;
65819304Speter	}
65919304Speter
66019304Speter	switch (evp->e_value) {
66119304Speter	case K_CR:				/* Carriage return. */
66219304Speter	case K_NL:				/* New line. */
66319304Speter		/* Return in script windows and the command line. */
66419304Speterk_cr:		if (LF_ISSET(TXT_CR)) {
66519304Speter			/*
66619304Speter			 * If this was a map, we may have not displayed
66719304Speter			 * the line.  Display it, just in case.
66819304Speter			 *
66919304Speter			 * If a script window and not the colon line,
67019304Speter			 * push a <cr> so it gets executed.
67119304Speter			 */
67219304Speter			if (LF_ISSET(TXT_INFOLINE)) {
67319304Speter				if (vs_change(sp, tp->lno, LINE_RESET))
67419304Speter					goto err;
67519304Speter			} else if (F_ISSET(sp, SC_SCRIPT))
676254225Speter				(void)v_event_push(sp, NULL, L("\r"), 1, CH_NOMAP);
67719304Speter
67819304Speter			/* Set term condition: if empty. */
67919304Speter			if (tp->cno <= tp->offset)
68019304Speter				tp->term = TERM_CR;
68119304Speter			/*
68219304Speter			 * Set term condition: if searching incrementally and
68319304Speter			 * the user entered a pattern, return a completed
68419304Speter			 * search, regardless if the entire pattern was found.
68519304Speter			 */
68619304Speter			if (FL_ISSET(is_flags, IS_RUNNING) &&
68719304Speter			    tp->cno >= tp->offset + 1)
68819304Speter				tp->term = TERM_SEARCH;
68919304Speter
69019304Speter			goto k_escape;
69119304Speter		}
69219304Speter
69319304Speter#define	LINE_RESOLVE {							\
69419304Speter		/*							\
69519304Speter		 * Handle abbreviations.  If there was one, discard the	\
69619304Speter		 * replay characters.					\
69719304Speter		 */							\
69819304Speter		if (abb == AB_INWORD &&					\
69919304Speter		    !LF_ISSET(TXT_REPLAY) && F_ISSET(gp, G_ABBREV)) {	\
70019304Speter			if (txt_abbrev(sp, tp, &evp->e_c,		\
70119304Speter			    LF_ISSET(TXT_INFOLINE), &tmp,		\
70219304Speter			    &ab_turnoff))				\
70319304Speter				goto err;				\
70419304Speter			if (tmp) {					\
70519304Speter				if (LF_ISSET(TXT_RECORD))		\
70619304Speter					rcol -= tmp + 1;		\
70719304Speter				goto resolve;				\
70819304Speter			}						\
70919304Speter		}							\
71019304Speter		if (abb != AB_NOTSET)					\
71119304Speter			abb = AB_NOTWORD;				\
71219304Speter		if (UNMAP_TST)						\
71319304Speter			txt_unmap(sp, tp, &ec_flags);			\
71419304Speter		/*							\
71519304Speter		 * Delete any appended cursor.  It's possible to get in	\
71619304Speter		 * situations where TXT_APPENDEOL is set but tp->insert	\
71719304Speter		 * is 0 when using the R command and all the characters	\
71819304Speter		 * are tp->owrite characters.				\
71919304Speter		 */							\
72019304Speter		if (LF_ISSET(TXT_APPENDEOL) && tp->insert > 0) {	\
72119304Speter			--tp->len;					\
72219304Speter			--tp->insert;					\
72319304Speter		}							\
72419304Speter}
72519304Speter		LINE_RESOLVE;
72619304Speter
72719304Speter		/*
72819304Speter		 * Save the current line information for restoration in
72919304Speter		 * txt_backup(), and set the line final length.
73019304Speter		 */
73119304Speter		tp->sv_len = tp->len;
73219304Speter		tp->sv_cno = tp->cno;
73319304Speter		tp->len = tp->cno;
73419304Speter
73519304Speter		/* Update the old line. */
73619304Speter		if (vs_change(sp, tp->lno, LINE_RESET))
73719304Speter			goto err;
73819304Speter
73919304Speter		/*
74019304Speter		 * Historic practice, when the autoindent edit option was set,
74119304Speter		 * was to delete <blank> characters following the inserted
74219304Speter		 * newline.  This affected the 'R', 'c', and 's' commands; 'c'
74319304Speter		 * and 's' retained the insert characters only, 'R' moved the
74419304Speter		 * overwrite and insert characters into the next TEXT structure.
74519304Speter		 * We keep track of the number of characters erased for the 'R'
74619304Speter		 * command so that the final resolution of the line is correct.
74719304Speter		 */
74819304Speter		tp->R_erase = 0;
74919304Speter		owrite = tp->owrite;
75019304Speter		insert = tp->insert;
75119304Speter		if (LF_ISSET(TXT_REPLACE) && owrite != 0) {
75219304Speter			for (p = tp->lb + tp->cno; owrite > 0 && isblank(*p);
75319304Speter			    ++p, --owrite, ++tp->R_erase);
75419304Speter			if (owrite == 0)
75519304Speter				for (; insert > 0 && isblank(*p);
75619304Speter				    ++p, ++tp->R_erase, --insert);
75719304Speter		} else {
75819304Speter			p = tp->lb + tp->cno + owrite;
75919304Speter			if (O_ISSET(sp, O_AUTOINDENT))
76019304Speter				for (; insert > 0 &&
76119304Speter				    isblank(*p); ++p, --insert);
76219304Speter			owrite = 0;
76319304Speter		}
76419304Speter
76519304Speter		/*
76619304Speter		 * !!!
76719304Speter		 * Create a new line and insert the new TEXT into the queue.
76819304Speter		 * DON'T insert until the old line has been updated, or the
76919304Speter		 * inserted line count in line.c:db_get() will be wrong.
77019304Speter		 */
77119304Speter		if ((ntp = text_init(sp, p,
77219304Speter		    insert + owrite, insert + owrite + 32)) == NULL)
77319304Speter			goto err;
774254225Speter		TAILQ_INSERT_TAIL(sp->tiq, ntp, q);
77519304Speter
77619304Speter		/* Set up bookkeeping for the new line. */
77719304Speter		ntp->insert = insert;
77819304Speter		ntp->owrite = owrite;
77919304Speter		ntp->lno = tp->lno + 1;
78019304Speter
78119304Speter		/*
78219304Speter		 * Reset the autoindent line value.  0^D keeps the autoindent
78319304Speter		 * line from changing, ^D changes the level, even if there were
78419304Speter		 * no characters in the old line.  Note, if using the current
78519304Speter		 * tp structure, use the cursor as the length, the autoindent
78619304Speter		 * characters may have been erased.
78719304Speter		 */
78819304Speter		if (LF_ISSET(TXT_AUTOINDENT)) {
789254225Speter			if (nochange) {
790254225Speter				nochange = 0;
79119304Speter				if (v_txt_auto(sp, OOBLNO, &ait, ait.ai, ntp))
79219304Speter					goto err;
793254225Speter				FREE_SPACEW(sp, ait.lb, ait.lb_len);
79419304Speter			} else
79519304Speter				if (v_txt_auto(sp, OOBLNO, tp, tp->cno, ntp))
79619304Speter					goto err;
79719304Speter			carat = C_NOTSET;
79819304Speter		}
79919304Speter
80019304Speter		/* Reset the cursor. */
80119304Speter		ntp->cno = ntp->ai;
80219304Speter
80319304Speter		/*
80419304Speter		 * If we're here because wrapmargin was set and we've broken a
80519304Speter		 * line, there may be additional information (i.e. the start of
80619304Speter		 * a line) in the wmt structure.
80719304Speter		 */
80819304Speter		if (wm_set) {
80919304Speter			if (wmt.offset != 0 ||
81019304Speter			    wmt.owrite != 0 || wmt.insert != 0) {
81119304Speter#define	WMTSPACE	wmt.offset + wmt.owrite + wmt.insert
812254225Speter				BINC_GOTOW(sp, ntp->lb,
81319304Speter				    ntp->lb_len, ntp->len + WMTSPACE + 32);
814254225Speter				MEMMOVE(ntp->lb + ntp->cno, wmt.lb, WMTSPACE);
81519304Speter				ntp->len += WMTSPACE;
81619304Speter				ntp->cno += wmt.offset;
81719304Speter				ntp->owrite = wmt.owrite;
81819304Speter				ntp->insert = wmt.insert;
81919304Speter			}
82019304Speter			wm_set = 0;
82119304Speter		}
82219304Speter
82319304Speter		/* New lines are TXT_APPENDEOL. */
82419304Speter		if (ntp->owrite == 0 && ntp->insert == 0) {
825254225Speter			BINC_GOTOW(sp, ntp->lb, ntp->lb_len, ntp->len + 1);
82619304Speter			LF_SET(TXT_APPENDEOL);
82719304Speter			ntp->lb[ntp->cno] = CH_CURSOR;
82819304Speter			++ntp->insert;
82919304Speter			++ntp->len;
83019304Speter		}
83119304Speter
83219304Speter		/* Swap old and new TEXT's, and update the new line. */
83319304Speter		tp = ntp;
83419304Speter		if (vs_change(sp, tp->lno, LINE_INSERT))
83519304Speter			goto err;
83619304Speter
83719304Speter		goto resolve;
83819304Speter	case K_ESCAPE:				/* Escape. */
83919304Speter		if (!LF_ISSET(TXT_ESCAPE))
84019304Speter			goto ins_ch;
84119304Speter
84219304Speter		/* If we have a count, start replaying the input. */
84319304Speter		if (rcount > 1) {
84419304Speter			--rcount;
84519304Speter
846254225Speter			vip->rep_cnt = rcol;
84719304Speter			rcol = 0;
84819304Speter			abb = AB_NOTSET;
84919304Speter			LF_CLR(TXT_RECORD);
85019304Speter			LF_SET(TXT_REPLAY);
85119304Speter
85219304Speter			/*
85319304Speter			 * Some commands (e.g. 'o') need a <newline> for each
85419304Speter			 * repetition.
85519304Speter			 */
85619304Speter			if (LF_ISSET(TXT_ADDNEWLINE))
85719304Speter				goto k_cr;
85819304Speter
85919304Speter			/*
86019304Speter			 * The R command turns into the 'a' command after the
86119304Speter			 * first repetition.
86219304Speter			 */
86319304Speter			if (LF_ISSET(TXT_REPLACE)) {
86419304Speter				tp->insert = tp->owrite;
86519304Speter				tp->owrite = 0;
86619304Speter				LF_CLR(TXT_REPLACE);
86719304Speter			}
86819304Speter			goto replay;
86919304Speter		}
87019304Speter
87119304Speter		/* Set term condition: if empty. */
87219304Speter		if (tp->cno <= tp->offset)
87319304Speter			tp->term = TERM_ESC;
87419304Speter		/*
87519304Speter		 * Set term condition: if searching incrementally and the user
87619304Speter		 * entered a pattern, return a completed search, regardless if
87719304Speter		 * the entire pattern was found.
87819304Speter		 */
87919304Speter		if (FL_ISSET(is_flags, IS_RUNNING) && tp->cno >= tp->offset + 1)
88019304Speter			tp->term = TERM_SEARCH;
88119304Speter
88219304Speterk_escape:	LINE_RESOLVE;
88319304Speter
88419304Speter		/*
88519304Speter		 * Clean up for the 'R' command, restoring overwrite
88619304Speter		 * characters, and making them into insert characters.
88719304Speter		 */
88819304Speter		if (LF_ISSET(TXT_REPLACE))
889254225Speter			txt_Rresolve(sp, sp->tiq, tp, len);
89019304Speter
89119304Speter		/*
89219304Speter		 * If there are any overwrite characters, copy down
89319304Speter		 * any insert characters, and decrement the length.
89419304Speter		 */
89519304Speter		if (tp->owrite) {
89619304Speter			if (tp->insert)
897254225Speter				MEMMOVE(tp->lb + tp->cno,
89819304Speter				    tp->lb + tp->cno + tp->owrite, tp->insert);
89919304Speter			tp->len -= tp->owrite;
90019304Speter		}
90119304Speter
90219304Speter		/*
90319304Speter		 * Optionally resolve the lines into the file.  If not
90419304Speter		 * resolving the lines into the file, end the line with
90519304Speter		 * a nul.  If the line is empty, then set the length to
90619304Speter		 * 0, the termination condition has already been set.
90719304Speter		 *
90819304Speter		 * XXX
90919304Speter		 * This is wrong, should pass back a length.
91019304Speter		 */
91119304Speter		if (LF_ISSET(TXT_RESOLVE)) {
912254225Speter			if (txt_resolve(sp, sp->tiq, flags))
91319304Speter				goto err;
91419304Speter		} else {
915254225Speter			BINC_GOTOW(sp, tp->lb, tp->lb_len, tp->len + 1);
91619304Speter			tp->lb[tp->len] = '\0';
91719304Speter		}
91819304Speter
91919304Speter		/*
92019304Speter		 * Set the return cursor position to rest on the last
92119304Speter		 * inserted character.
92219304Speter		 */
92319304Speter		if (tp->cno != 0)
92419304Speter			--tp->cno;
92519304Speter
92619304Speter		/* Update the last line. */
92719304Speter		if (vs_change(sp, tp->lno, LINE_RESET))
92819304Speter			return (1);
92919304Speter		goto done;
93019304Speter	case K_CARAT:			/* Delete autoindent chars. */
93119304Speter		if (tp->cno <= tp->ai && LF_ISSET(TXT_AUTOINDENT))
93219304Speter			carat = C_CARATSET;
93319304Speter		goto ins_ch;
93419304Speter	case K_ZERO:			/* Delete autoindent chars. */
93519304Speter		if (tp->cno <= tp->ai && LF_ISSET(TXT_AUTOINDENT))
93619304Speter			carat = C_ZEROSET;
93719304Speter		goto ins_ch;
93819304Speter	case K_CNTRLD:			/* Delete autoindent char. */
93919304Speter		/*
94019304Speter		 * If in the first column or no characters to erase, ignore
94119304Speter		 * the ^D (this matches historic practice).  If not doing
94219304Speter		 * autoindent or already inserted non-ai characters, it's a
94319304Speter		 * literal.  The latter test is done in the switch, as the
94419304Speter		 * CARAT forms are N + 1, not N.
94519304Speter		 */
94619304Speter		if (!LF_ISSET(TXT_AUTOINDENT))
94719304Speter			goto ins_ch;
94819304Speter		if (tp->cno == 0)
94919304Speter			goto resolve;
95019304Speter
95119304Speter		switch (carat) {
95219304Speter		case C_CARATSET:	/* ^^D */
95319304Speter			if (tp->ai == 0 || tp->cno > tp->ai + tp->offset + 1)
95419304Speter				goto ins_ch;
95519304Speter
95619304Speter			/* Save the ai string for later. */
95719304Speter			ait.lb = NULL;
95819304Speter			ait.lb_len = 0;
959254225Speter			BINC_GOTOW(sp, ait.lb, ait.lb_len, tp->ai);
960254225Speter			MEMMOVE(ait.lb, tp->lb, tp->ai);
96119304Speter			ait.ai = ait.len = tp->ai;
96219304Speter
963254225Speter			carat = C_NOTSET;
964254225Speter			nochange = 1;
96519304Speter			goto leftmargin;
96619304Speter		case C_ZEROSET:		/* 0^D */
96719304Speter			if (tp->ai == 0 || tp->cno > tp->ai + tp->offset + 1)
96819304Speter				goto ins_ch;
96919304Speter
97019304Speter			carat = C_NOTSET;
97119304Speterleftmargin:		tp->lb[tp->cno - 1] = ' ';
97219304Speter			tp->owrite += tp->cno - tp->offset;
97319304Speter			tp->ai = 0;
97419304Speter			tp->cno = tp->offset;
97519304Speter			break;
97619304Speter		case C_NOTSET:		/* ^D */
97719304Speter			if (tp->ai == 0 || tp->cno > tp->ai + tp->offset)
97819304Speter				goto ins_ch;
97919304Speter
98019304Speter			(void)txt_dent(sp, tp, 0);
98119304Speter			break;
98219304Speter		default:
98319304Speter			abort();
98419304Speter		}
98519304Speter		break;
98619304Speter	case K_VERASE:			/* Erase the last character. */
98719304Speter		/* If can erase over the prompt, return. */
98819304Speter		if (tp->cno <= tp->offset && LF_ISSET(TXT_BS)) {
98919304Speter			tp->term = TERM_BS;
99019304Speter			goto done;
99119304Speter		}
99219304Speter
99319304Speter		/*
99419304Speter		 * If at the beginning of the line, try and drop back to a
99519304Speter		 * previously inserted line.
99619304Speter		 */
99719304Speter		if (tp->cno == 0) {
99819304Speter			if ((ntp =
999254225Speter			    txt_backup(sp, sp->tiq, tp, &flags)) == NULL)
100019304Speter				goto err;
100119304Speter			tp = ntp;
100219304Speter			break;
100319304Speter		}
100419304Speter
100519304Speter		/* If nothing to erase, bell the user. */
100619304Speter		if (tp->cno <= tp->offset) {
100719304Speter			if (!LF_ISSET(TXT_REPLAY))
100819304Speter				txt_nomorech(sp);
100919304Speter			break;
101019304Speter		}
101119304Speter
101219304Speter		/* Drop back one character. */
101319304Speter		--tp->cno;
101419304Speter
101519304Speter		/*
101619304Speter		 * Historically, vi didn't replace the erased characters with
101719304Speter		 * <blank>s, presumably because it's easier to fix a minor
101819304Speter		 * typing mistake and continue on if the previous letters are
101919304Speter		 * already there.  This is a problem for incremental searching,
102019304Speter		 * because the user can no longer tell where they are in the
102119304Speter		 * colon command line because the cursor is at the last search
102219304Speter		 * point in the screen.  So, if incrementally searching, erase
102319304Speter		 * the erased characters from the screen.
102419304Speter		 */
102519304Speter		if (FL_ISSET(is_flags, IS_RUNNING))
102619304Speter			tp->lb[tp->cno] = ' ';
102719304Speter
102819304Speter		/*
102919304Speter		 * Increment overwrite, decrement ai if deleted.
103019304Speter		 *
103119304Speter		 * !!!
103219304Speter		 * Historic vi did not permit users to use erase characters
103319304Speter		 * to delete autoindent characters.  We do.  Eat hot death,
103419304Speter		 * POSIX.
103519304Speter		 */
103619304Speter		++tp->owrite;
103719304Speter		if (tp->cno < tp->ai)
103819304Speter			--tp->ai;
103919304Speter
104019304Speter		/* Reset if we deleted an incremental search character. */
104119304Speter		if (FL_ISSET(is_flags, IS_RUNNING))
104219304Speter			FL_SET(is_flags, IS_RESTART);
104319304Speter		break;
104419304Speter	case K_VWERASE:			/* Skip back one word. */
104519304Speter		/*
104619304Speter		 * If at the beginning of the line, try and drop back to a
104719304Speter		 * previously inserted line.
104819304Speter		 */
104919304Speter		if (tp->cno == 0) {
105019304Speter			if ((ntp =
1051254225Speter			    txt_backup(sp, sp->tiq, tp, &flags)) == NULL)
105219304Speter				goto err;
105319304Speter			tp = ntp;
105419304Speter		}
105519304Speter
105619304Speter		/*
105719304Speter		 * If at offset, nothing to erase so bell the user.
105819304Speter		 */
105919304Speter		if (tp->cno <= tp->offset) {
106019304Speter			if (!LF_ISSET(TXT_REPLAY))
106119304Speter				txt_nomorech(sp);
106219304Speter			break;
106319304Speter		}
106419304Speter
106519304Speter		/*
106619304Speter		 * The first werase goes back to any autoindent column and the
106719304Speter		 * second werase goes back to the offset.
106819304Speter		 *
106919304Speter		 * !!!
107019304Speter		 * Historic vi did not permit users to use erase characters to
107119304Speter		 * delete autoindent characters.
107219304Speter		 */
107319304Speter		if (tp->ai && tp->cno > tp->ai)
107419304Speter			max = tp->ai;
107519304Speter		else {
107619304Speter			tp->ai = 0;
107719304Speter			max = tp->offset;
107819304Speter		}
107919304Speter
108019304Speter		/* Skip over trailing space characters. */
1081254225Speter		while (tp->cno > max && ISBLANK(tp->lb[tp->cno - 1])) {
108219304Speter			--tp->cno;
108319304Speter			++tp->owrite;
108419304Speter		}
108519304Speter		if (tp->cno == max)
108619304Speter			break;
108719304Speter		/*
108819304Speter		 * There are three types of word erase found on UNIX systems.
108919304Speter		 * They can be identified by how the string /a/b/c is treated
109019304Speter		 * -- as 1, 3, or 6 words.  Historic vi had two classes of
109119304Speter		 * characters, and strings were delimited by them and
109219304Speter		 * <blank>'s, so, 6 words.  The historic tty interface used
109319304Speter		 * <blank>'s to delimit strings, so, 1 word.  The algorithm
109419304Speter		 * offered in the 4.4BSD tty interface (as stty altwerase)
109519304Speter		 * treats it as 3 words -- there are two classes of
109619304Speter		 * characters, and strings are delimited by them and
109719304Speter		 * <blank>'s.  The difference is that the type of the first
109819304Speter		 * erased character erased is ignored, which is exactly right
109919304Speter		 * when erasing pathname components.  The edit options
110019304Speter		 * TXT_ALTWERASE and TXT_TTYWERASE specify the 4.4BSD tty
110119304Speter		 * interface and the historic tty driver behavior,
110219304Speter		 * respectively, and the default is the same as the historic
110319304Speter		 * vi behavior.
110419304Speter		 *
110519304Speter		 * Overwrite erased characters if doing incremental search;
110619304Speter		 * see comment above.
110719304Speter		 */
110819304Speter		if (LF_ISSET(TXT_TTYWERASE))
110919304Speter			while (tp->cno > max) {
1110254225Speter				if (ISBLANK(tp->lb[tp->cno - 1]))
1111254225Speter					break;
111219304Speter				--tp->cno;
111319304Speter				++tp->owrite;
111419304Speter				if (FL_ISSET(is_flags, IS_RUNNING))
111519304Speter					tp->lb[tp->cno] = ' ';
111619304Speter			}
111719304Speter		else {
111819304Speter			if (LF_ISSET(TXT_ALTWERASE)) {
111919304Speter				--tp->cno;
112019304Speter				++tp->owrite;
112119304Speter				if (FL_ISSET(is_flags, IS_RUNNING))
112219304Speter					tp->lb[tp->cno] = ' ';
112319304Speter			}
112419304Speter			if (tp->cno > max)
112519304Speter				tmp = inword(tp->lb[tp->cno - 1]);
112619304Speter			while (tp->cno > max) {
1127254225Speter				if (tmp != inword(tp->lb[tp->cno - 1])
1128254225Speter				    || ISBLANK(tp->lb[tp->cno - 1]))
1129254225Speter					break;
113019304Speter				--tp->cno;
113119304Speter				++tp->owrite;
113219304Speter				if (FL_ISSET(is_flags, IS_RUNNING))
113319304Speter					tp->lb[tp->cno] = ' ';
113419304Speter			}
113519304Speter		}
113619304Speter
113719304Speter		/* Reset if we deleted an incremental search character. */
113819304Speter		if (FL_ISSET(is_flags, IS_RUNNING))
113919304Speter			FL_SET(is_flags, IS_RESTART);
114019304Speter		break;
114119304Speter	case K_VKILL:			/* Restart this line. */
114219304Speter		/*
114319304Speter		 * !!!
114419304Speter		 * If at the beginning of the line, try and drop back to a
114519304Speter		 * previously inserted line.  Historic vi did not permit
114619304Speter		 * users to go back to previous lines.
114719304Speter		 */
114819304Speter		if (tp->cno == 0) {
114919304Speter			if ((ntp =
1150254225Speter			    txt_backup(sp, sp->tiq, tp, &flags)) == NULL)
115119304Speter				goto err;
115219304Speter			tp = ntp;
115319304Speter		}
115419304Speter
115519304Speter		/* If at offset, nothing to erase so bell the user. */
115619304Speter		if (tp->cno <= tp->offset) {
115719304Speter			if (!LF_ISSET(TXT_REPLAY))
115819304Speter				txt_nomorech(sp);
115919304Speter			break;
116019304Speter		}
116119304Speter
116219304Speter		/*
116319304Speter		 * First kill goes back to any autoindent and second kill goes
116419304Speter		 * back to the offset.
116519304Speter		 *
116619304Speter		 * !!!
116719304Speter		 * Historic vi did not permit users to use erase characters to
116819304Speter		 * delete autoindent characters.
116919304Speter		 */
117019304Speter		if (tp->ai && tp->cno > tp->ai)
117119304Speter			max = tp->ai;
117219304Speter		else {
117319304Speter			tp->ai = 0;
117419304Speter			max = tp->offset;
117519304Speter		}
117619304Speter		tp->owrite += tp->cno - max;
117719304Speter
117819304Speter		/*
117919304Speter		 * Overwrite erased characters if doing incremental search;
118019304Speter		 * see comment above.
118119304Speter		 */
118219304Speter		if (FL_ISSET(is_flags, IS_RUNNING))
118319304Speter			do {
118419304Speter				tp->lb[--tp->cno] = ' ';
118519304Speter			} while (tp->cno > max);
118619304Speter		else
118719304Speter			tp->cno = max;
118819304Speter
118919304Speter		/* Reset if we deleted an incremental search character. */
119019304Speter		if (FL_ISSET(is_flags, IS_RUNNING))
119119304Speter			FL_SET(is_flags, IS_RESTART);
119219304Speter		break;
119319304Speter	case K_CNTRLT:			/* Add autoindent characters. */
119419304Speter		if (!LF_ISSET(TXT_CNTRLT))
119519304Speter			goto ins_ch;
119619304Speter		if (txt_dent(sp, tp, 1))
119719304Speter			goto err;
119819304Speter		goto ebuf_chk;
119919304Speter	case K_BACKSLASH:		/* Quote next erase/kill. */
120019304Speter		/*
120119304Speter		 * !!!
120219304Speter		 * Historic vi tried to make abbreviations after a backslash
120319304Speter		 * escape work.  If you did ":ab x y", and inserted "x\^H",
120419304Speter		 * (assuming the erase character was ^H) you got "x^H", and
120519304Speter		 * no abbreviation was done.  If you inserted "x\z", however,
120619304Speter		 * it tried to back up and do the abbreviation, i.e. replace
120719304Speter		 * 'x' with 'y'.  The problem was it got it wrong, and you
120819304Speter		 * ended up with "zy\".
120919304Speter		 *
121019304Speter		 * This is really hard to do (you have to remember the
121119304Speter		 * word/non-word state, for example), and doesn't make any
121219304Speter		 * sense to me.  Both backslash and the characters it
121319304Speter		 * (usually) escapes will individually trigger the
121419304Speter		 * abbreviation, so I don't see why the combination of them
121519304Speter		 * wouldn't.  I don't expect to get caught on this one,
121619304Speter		 * particularly since it never worked right, but I've been
121719304Speter		 * wrong before.
121819304Speter		 *
121919304Speter		 * Do the tests for abbreviations, so ":ab xa XA",
122019304Speter		 * "ixa\<K_VERASE>" performs the abbreviation.
122119304Speter		 */
122219304Speter		quote = Q_BNEXT;
122319304Speter		goto insq_ch;
122419304Speter	case K_VLNEXT:			/* Quote next character. */
122519304Speter		evp->e_c = '^';
122619304Speter		quote = Q_VNEXT;
122719304Speter		/*
122819304Speter		 * Turn on the quote flag so that the underlying routines
122919304Speter		 * quote the next character where it's possible. Turn off
123019304Speter		 * the input mapbiting flag so that we don't remap the next
123119304Speter		 * character.
123219304Speter		 */
123319304Speter		FL_SET(ec_flags, EC_QUOTED);
123419304Speter		FL_CLR(ec_flags, EC_MAPINPUT);
123519304Speter
123619304Speter		/*
123719304Speter		 * !!!
123819304Speter		 * Skip the tests for abbreviations, so ":ab xa XA",
123919304Speter		 * "ixa^V<space>" doesn't perform the abbreviation.
124019304Speter		 */
124119304Speter		goto insl_ch;
124219304Speter	case K_HEXCHAR:
124319304Speter		hexcnt = 1;
124419304Speter		goto insq_ch;
124519304Speter	default:			/* Insert the character. */
1246254225Speter		if (LF_ISSET(TXT_SHOWMATCH)) {
1247254225Speter			CHAR_T *match_chars, *cp;
1248254225Speter
1249254225Speter			match_chars = VIP(sp)->mcs;
1250254225Speter			cp = STRCHR(match_chars, evp->e_c);
1251254225Speter			if (cp != NULL && (cp - match_chars) & 1)
1252254225Speter				showmatch = 1;
1253254225Speter		}
125419304Speterins_ch:		/*
125519304Speter		 * Historically, vi eliminated nul's out of hand.  If the
125619304Speter		 * beautify option was set, it also deleted any unknown
125719304Speter		 * ASCII value less than space (040) and the del character
125819304Speter		 * (0177), except for tabs.  Unknown is a key word here.
125919304Speter		 * Most vi documentation claims that it deleted everything
126019304Speter		 * but <tab>, <nl> and <ff>, as that's what the original
126119304Speter		 * 4BSD documentation said.  This is obviously wrong,
126219304Speter		 * however, as <esc> would be included in that list.  What
126319304Speter		 * we do is eliminate any unquoted, iscntrl() character that
126419304Speter		 * wasn't a replay and wasn't handled specially, except
126519304Speter		 * <tab> or <ff>.
126619304Speter		 */
1267254225Speter		if (LF_ISSET(TXT_BEAUTIFY) && ISCNTRL(evp->e_c) &&
126819304Speter		    evp->e_value != K_FORMFEED && evp->e_value != K_TAB) {
126919304Speter			msgq(sp, M_BERR,
127019304Speter			    "192|Illegal character; quote to enter");
127119304Speter			if (LF_ISSET(TXT_REPLAY))
127219304Speter				goto done;
127319304Speter			break;
127419304Speter		}
127519304Speter
127619304Speterinsq_ch:	/*
127719304Speter		 * If entering a non-word character after a word, check for
127819304Speter		 * abbreviations.  If there was one, discard replay characters.
127919304Speter		 * If entering a blank character, check for unmap commands,
128019304Speter		 * as well.
128119304Speter		 */
128219304Speter		if (!inword(evp->e_c)) {
128319304Speter			if (abb == AB_INWORD &&
128419304Speter			    !LF_ISSET(TXT_REPLAY) && F_ISSET(gp, G_ABBREV)) {
128519304Speter				if (txt_abbrev(sp, tp, &evp->e_c,
128619304Speter				    LF_ISSET(TXT_INFOLINE), &tmp, &ab_turnoff))
128719304Speter					goto err;
128819304Speter				if (tmp) {
128919304Speter					if (LF_ISSET(TXT_RECORD))
129019304Speter						rcol -= tmp + 1;
129119304Speter					goto resolve;
129219304Speter				}
129319304Speter			}
129419304Speter			if (isblank(evp->e_c) && UNMAP_TST)
129519304Speter				txt_unmap(sp, tp, &ec_flags);
129619304Speter		}
129719304Speter		if (abb != AB_NOTSET)
129819304Speter			abb = inword(evp->e_c) ? AB_INWORD : AB_NOTWORD;
129919304Speter
130019304Speterinsl_ch:	if (txt_insch(sp, tp, &evp->e_c, flags))
130119304Speter			goto err;
130219304Speter
130319304Speter		/*
130419304Speter		 * If we're using K_VLNEXT to quote the next character, then
130519304Speter		 * we want the cursor to position itself on the ^ placeholder
130619304Speter		 * we're displaying, to match historic practice.
130719304Speter		 */
130819304Speter		if (quote == Q_VNEXT) {
130919304Speter			--tp->cno;
131019304Speter			++tp->owrite;
131119304Speter		}
131219304Speter
131319304Speter		/*
131419304Speter		 * !!!
131519304Speter		 * Translate "<CH_HEX>[isxdigit()]*" to a character with
131619304Speter		 * a hex value: this test delimits the value by the max
131719304Speter		 * number of hex bytes.  Offset by one, we use 0 to mean
131819304Speter		 * that we've found <CH_HEX>.
131919304Speter		 */
1320254225Speter		if (hexcnt != 0 && hexcnt++ == 3) {
132119304Speter			hexcnt = 0;
132219304Speter			if (txt_hex(sp, tp))
132319304Speter				goto err;
132419304Speter		}
132519304Speter
132619304Speter		/*
132719304Speter		 * Check to see if we've crossed the margin.
132819304Speter		 *
132919304Speter		 * !!!
133019304Speter		 * In the historic vi, the wrapmargin value was figured out
133119304Speter		 * using the display widths of the characters, i.e. <tab>
133219304Speter		 * characters were counted as two characters if the list edit
133319304Speter		 * option is set, but as the tabstop edit option number of
133419304Speter		 * characters otherwise.  That's what the vs_column() function
133519304Speter		 * gives us, so we use it.
133619304Speter		 */
133719304Speter		if (margin != 0) {
133819304Speter			if (vs_column(sp, &tcol))
133919304Speter				goto err;
134019304Speter			if (tcol >= margin) {
134119304Speter				if (txt_margin(sp, tp, &wmt, &tmp, flags))
134219304Speter					goto err;
134319304Speter				if (tmp) {
134419304Speter					if (isblank(evp->e_c))
134519304Speter						wm_skip = 1;
134619304Speter					wm_set = 1;
134719304Speter					goto k_cr;
134819304Speter				}
134919304Speter			}
135019304Speter		}
135119304Speter
135219304Speter		/*
135319304Speter		 * If we've reached the end of the buffer, then we need to
135419304Speter		 * switch into insert mode.  This happens when there's a
135519304Speter		 * change to a mark and the user puts in more characters than
135619304Speter		 * the length of the motion.
135719304Speter		 */
135819304Speterebuf_chk:	if (tp->cno >= tp->len) {
1359254225Speter			BINC_GOTOW(sp, tp->lb, tp->lb_len, tp->len + 1);
136019304Speter			LF_SET(TXT_APPENDEOL);
136119304Speter
136219304Speter			tp->lb[tp->cno] = CH_CURSOR;
136319304Speter			++tp->insert;
136419304Speter			++tp->len;
136519304Speter		}
136619304Speter
136719304Speter		/* Step the quote state forward. */
136819304Speter		if (quote != Q_NOTSET) {
136919304Speter			if (quote == Q_BNEXT)
137019304Speter				quote = Q_BTHIS;
137119304Speter			if (quote == Q_VNEXT)
137219304Speter				quote = Q_VTHIS;
137319304Speter		}
137419304Speter		break;
137519304Speter	}
137619304Speter
137719304Speter#ifdef DEBUG
137819304Speter	if (tp->cno + tp->insert + tp->owrite != tp->len) {
137919304Speter		msgq(sp, M_ERR,
1380254225Speter		    "len %zu != cno: %zu ai: %zu insert %zu overwrite %zu",
138119304Speter		    tp->len, tp->cno, tp->ai, tp->insert, tp->owrite);
138219304Speter		if (LF_ISSET(TXT_REPLAY))
138319304Speter			goto done;
138419304Speter		tp->len = tp->cno + tp->insert + tp->owrite;
138519304Speter	}
138619304Speter#endif
138719304Speter
138819304Speterresolve:/*
138919304Speter	 * 1: If we don't need to know where the cursor really is and we're
139019304Speter	 *    replaying text, keep going.
139119304Speter	 */
139219304Speter	if (margin == 0 && LF_ISSET(TXT_REPLAY))
139319304Speter		goto replay;
139419304Speter
139519304Speter	/*
139619304Speter	 * 2: Reset the line.  Don't bother unless we're about to wait on
139719304Speter	 *    a character or we need to know where the cursor really is.
139819304Speter	 *    We have to do this before showing matching characters so the
139919304Speter	 *    user can see what they're matching.
140019304Speter	 */
140119304Speter	if ((margin != 0 || !KEYS_WAITING(sp)) &&
140219304Speter	    vs_change(sp, tp->lno, LINE_RESET))
140319304Speter		return (1);
140419304Speter
140519304Speter	/*
140619304Speter	 * 3: If there aren't keys waiting, display the matching character.
140719304Speter	 *    We have to do this before resolving any messages, otherwise
140819304Speter	 *    the error message from a missing match won't appear correctly.
140919304Speter	 */
141019304Speter	if (showmatch) {
141119304Speter		if (!KEYS_WAITING(sp) && txt_showmatch(sp, tp))
141219304Speter			return (1);
141319304Speter		showmatch = 0;
141419304Speter	}
141519304Speter
141619304Speter	/*
141719304Speter	 * 4: If there have been messages and we're not editing on the colon
141819304Speter	 *    command line or doing file name completion, resolve them.
141919304Speter	 */
142019304Speter	if ((vip->totalcount != 0 || F_ISSET(gp, G_BELLSCHED)) &&
142119304Speter	    !F_ISSET(sp, SC_TINPUT_INFO) && !filec_redraw &&
142219304Speter	    vs_resolve(sp, NULL, 0))
142319304Speter		return (1);
142419304Speter
142519304Speter	/*
142619304Speter	 * 5: Refresh the screen if we're about to wait on a character or we
142719304Speter	 *    need to know where the cursor really is.
142819304Speter	 */
142919304Speter	if (margin != 0 || !KEYS_WAITING(sp)) {
143019304Speter		UPDATE_POSITION(sp, tp);
143119304Speter		if (vs_refresh(sp, margin != 0))
143219304Speter			return (1);
143319304Speter	}
143419304Speter
143519304Speter	/* 6: Proceed with the incremental search. */
143619304Speter	if (FL_ISSET(is_flags, IS_RUNNING) && txt_isrch(sp, vp, tp, &is_flags))
143719304Speter		return (1);
143819304Speter
143919304Speter	/* 7: Next character... */
144019304Speter	if (LF_ISSET(TXT_REPLAY))
144119304Speter		goto replay;
144219304Speter	goto next;
144319304Speter
144419304Speterdone:	/* Leave input mode. */
144519304Speter	F_CLR(sp, SC_TINPUT);
144619304Speter
144719304Speter	/* If recording for playback, save it. */
144819304Speter	if (LF_ISSET(TXT_RECORD))
144919304Speter		vip->rep_cnt = rcol;
145019304Speter
145119304Speter	/*
145219304Speter	 * If not working on the colon command line, set the final cursor
145319304Speter	 * position.
145419304Speter	 */
145519304Speter	if (!F_ISSET(sp, SC_TINPUT_INFO)) {
145619304Speter		vp->m_final.lno = tp->lno;
145719304Speter		vp->m_final.cno = tp->cno;
145819304Speter	}
145919304Speter	return (0);
146019304Speter
146119304Spetererr:
146219304Speteralloc_err:
1463208612Sjh	F_CLR(sp, SC_TINPUT);
1464254225Speter	txt_err(sp, sp->tiq);
146519304Speter	return (1);
146619304Speter}
146719304Speter
146819304Speter/*
146919304Speter * txt_abbrev --
147019304Speter *	Handle abbreviations.
147119304Speter */
147219304Speterstatic int
1473254225Spetertxt_abbrev(SCR *sp, TEXT *tp, CHAR_T *pushcp, int isinfoline, int *didsubp, int *turnoffp)
147419304Speter{
147519304Speter	VI_PRIVATE *vip;
147619304Speter	CHAR_T ch, *p;
147719304Speter	SEQ *qp;
147819304Speter	size_t len, off;
147919304Speter
148019304Speter	/* Check to make sure we're not at the start of an append. */
148119304Speter	*didsubp = 0;
148219304Speter	if (tp->cno == tp->offset)
148319304Speter		return (0);
148419304Speter
148519304Speter	vip = VIP(sp);
148619304Speter
148719304Speter	/*
148819304Speter	 * Find the start of the "word".
148919304Speter	 *
149019304Speter	 * !!!
149119304Speter	 * We match historic practice, which, as far as I can tell, had an
149219304Speter	 * off-by-one error.  The way this worked was that when the inserted
149319304Speter	 * text switched from a "word" character to a non-word character,
149419304Speter	 * vi would check for possible abbreviations.  It would then take the
149519304Speter	 * type (i.e. word/non-word) of the character entered TWO characters
149619304Speter	 * ago, and move backward in the text until reaching a character that
149719304Speter	 * was not that type, or the beginning of the insert, the line, or
149819304Speter	 * the file.  For example, in the string "abc<space>", when the <space>
149919304Speter	 * character triggered the abbreviation check, the type of the 'b'
150019304Speter	 * character was used for moving through the string.  Maybe there's a
150119304Speter	 * reason for not using the first (i.e. 'c') character, but I can't
150219304Speter	 * think of one.
150319304Speter	 *
150419304Speter	 * Terminate at the beginning of the insert or the character after the
150519304Speter	 * offset character -- both can be tested for using tp->offset.
150619304Speter	 */
150719304Speter	off = tp->cno - 1;			/* Previous character. */
150819304Speter	p = tp->lb + off;
150919304Speter	len = 1;				/* One character test. */
151019304Speter	if (off == tp->offset || isblank(p[-1]))
151119304Speter		goto search;
151219304Speter	if (inword(p[-1]))			/* Move backward to change. */
151319304Speter		for (;;) {
151419304Speter			--off; --p; ++len;
151519304Speter			if (off == tp->offset || !inword(p[-1]))
151619304Speter				break;
151719304Speter		}
151819304Speter	else
151919304Speter		for (;;) {
152019304Speter			--off; --p; ++len;
152119304Speter			if (off == tp->offset ||
152219304Speter			    inword(p[-1]) || isblank(p[-1]))
152319304Speter				break;
152419304Speter		}
152519304Speter
152619304Speter	/*
152719304Speter	 * !!!
152819304Speter	 * Historic vi exploded abbreviations on the command line.  This has
152919304Speter	 * obvious problems in that unabbreviating the string can be extremely
153019304Speter	 * tricky, particularly if the string has, say, an embedded escape
153119304Speter	 * character.  Personally, I think it's a stunningly bad idea.  Other
153219304Speter	 * examples of problems this caused in historic vi are:
153319304Speter	 *	:ab foo bar
153419304Speter	 *	:ab foo baz
153519304Speter	 * results in "bar" being abbreviated to "baz", which wasn't what the
153619304Speter	 * user had in mind at all.  Also, the commands:
153719304Speter	 *	:ab foo bar
153819304Speter	 *	:unab foo<space>
153919304Speter	 * resulted in an error message that "bar" wasn't mapped.  Finally,
154019304Speter	 * since the string was already exploded by the time the unabbreviate
154119304Speter	 * command got it, all it knew was that an abbreviation had occurred.
154219304Speter	 * Cleverly, it checked the replacement string for its unabbreviation
154319304Speter	 * match, which meant that the commands:
154419304Speter	 *	:ab foo1 bar
154519304Speter	 *	:ab foo2 bar
154619304Speter	 *	:unab foo2
154719304Speter	 * unabbreviate "foo1", and the commands:
154819304Speter	 *	:ab foo bar
154919304Speter	 *	:ab bar baz
155019304Speter	 * unabbreviate "foo"!
155119304Speter	 *
155219304Speter	 * Anyway, people neglected to first ask my opinion before they wrote
155319304Speter	 * macros that depend on this stuff, so, we make this work as follows.
155419304Speter	 * When checking for an abbreviation on the command line, if we get a
155519304Speter	 * string which is <blank> terminated and which starts at the beginning
155619304Speter	 * of the line, we check to see it is the abbreviate or unabbreviate
155719304Speter	 * commands.  If it is, turn abbreviations off and return as if no
155819304Speter	 * abbreviation was found.  Note also, minor trickiness, so that if
155919304Speter	 * the user erases the line and starts another command, we turn the
156019304Speter	 * abbreviations back on.
156119304Speter	 *
156219304Speter	 * This makes the layering look like a Nachos Supreme.
156319304Speter	 */
156419304Spetersearch:	if (isinfoline)
156519304Speter		if (off == tp->ai || off == tp->offset)
156619304Speter			if (ex_is_abbrev(p, len)) {
156719304Speter				*turnoffp = 1;
156819304Speter				return (0);
156919304Speter			} else
157019304Speter				*turnoffp = 0;
157119304Speter		else
157219304Speter			if (*turnoffp)
157319304Speter				return (0);
157419304Speter
157519304Speter	/* Check for any abbreviations. */
157619304Speter	if ((qp = seq_find(sp, NULL, NULL, p, len, SEQ_ABBREV, NULL)) == NULL)
157719304Speter		return (0);
157819304Speter
157919304Speter	/*
158019304Speter	 * Push the abbreviation onto the tty stack.  Historically, characters
158119304Speter	 * resulting from an abbreviation expansion were themselves subject to
158219304Speter	 * map expansions, O_SHOWMATCH matching etc.  This means the expanded
158319304Speter	 * characters will be re-tested for abbreviations.  It's difficult to
158419304Speter	 * know what historic practice in this case was, since abbreviations
158519304Speter	 * were applied to :colon command lines, so entering abbreviations that
158619304Speter	 * looped was tricky, although possible.  In addition, obvious loops
158719304Speter	 * didn't work as expected.  (The command ':ab a b|ab b c|ab c a' will
158819304Speter	 * silently only implement and/or display the last abbreviation.)
158919304Speter	 *
159019304Speter	 * This implementation doesn't recover well from such abbreviations.
159119304Speter	 * The main input loop counts abbreviated characters, and, when it
159219304Speter	 * reaches a limit, discards any abbreviated characters on the queue.
159319304Speter	 * It's difficult to back up to the original position, as the replay
159419304Speter	 * queue would have to be adjusted, and the line state when an initial
159519304Speter	 * abbreviated character was received would have to be saved.
159619304Speter	 */
159719304Speter	ch = *pushcp;
159819304Speter	if (v_event_push(sp, NULL, &ch, 1, CH_ABBREVIATED))
159919304Speter		return (1);
160019304Speter	if (v_event_push(sp, NULL, qp->output, qp->olen, CH_ABBREVIATED))
160119304Speter		return (1);
160219304Speter
160319304Speter	/*
160419304Speter	 * If the size of the abbreviation is larger than or equal to the size
160519304Speter	 * of the original text, move to the start of the replaced characters,
160619304Speter	 * and add their length to the overwrite count.
160719304Speter	 *
160819304Speter	 * If the abbreviation is smaller than the original text, we have to
160919304Speter	 * delete the additional overwrite characters and copy down any insert
161019304Speter	 * characters.
161119304Speter	 */
161219304Speter	tp->cno -= len;
161319304Speter	if (qp->olen >= len)
161419304Speter		tp->owrite += len;
161519304Speter	else {
161619304Speter		if (tp->insert)
1617254225Speter			MEMMOVE(tp->lb + tp->cno + qp->olen,
161819304Speter			    tp->lb + tp->cno + tp->owrite + len, tp->insert);
161919304Speter		tp->owrite += qp->olen;
162019304Speter		tp->len -= len - qp->olen;
162119304Speter	}
162219304Speter
162319304Speter	/*
162419304Speter	 * We return the length of the abbreviated characters.  This is so
162519304Speter	 * the calling routine can replace the replay characters with the
162619304Speter	 * abbreviation.  This means that subsequent '.' commands will produce
162719304Speter	 * the same text, regardless of intervening :[un]abbreviate commands.
162819304Speter	 * This is historic practice.
162919304Speter	 */
163019304Speter	*didsubp = len;
163119304Speter	return (0);
163219304Speter}
163319304Speter
163419304Speter/*
163519304Speter * txt_unmap --
163619304Speter *	Handle the unmap command.
163719304Speter */
163819304Speterstatic void
1639254225Spetertxt_unmap(SCR *sp, TEXT *tp, u_int32_t *ec_flagsp)
164019304Speter{
164119304Speter	size_t len, off;
1642254225Speter	CHAR_T *p;
164319304Speter
164419304Speter	/* Find the beginning of this "word". */
164519304Speter	for (off = tp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) {
164619304Speter		if (isblank(*p)) {
164719304Speter			++p;
164819304Speter			break;
164919304Speter		}
165019304Speter		++len;
165119304Speter		if (off == tp->ai || off == tp->offset)
165219304Speter			break;
165319304Speter	}
165419304Speter
165519304Speter	/*
165619304Speter	 * !!!
165719304Speter	 * Historic vi exploded input mappings on the command line.  See the
165819304Speter	 * txt_abbrev() routine for an explanation of the problems inherent
165919304Speter	 * in this.
166019304Speter	 *
166119304Speter	 * We make this work as follows.  If we get a string which is <blank>
166219304Speter	 * terminated and which starts at the beginning of the line, we check
166319304Speter	 * to see it is the unmap command.  If it is, we return that the input
166419304Speter	 * mapping should be turned off.  Note also, minor trickiness, so that
166519304Speter	 * if the user erases the line and starts another command, we go ahead
166619304Speter	 * an turn mapping back on.
166719304Speter	 */
166819304Speter	if ((off == tp->ai || off == tp->offset) && ex_is_unmap(p, len))
166919304Speter		FL_CLR(*ec_flagsp, EC_MAPINPUT);
167019304Speter	else
167119304Speter		FL_SET(*ec_flagsp, EC_MAPINPUT);
167219304Speter}
167319304Speter
167419304Speter/*
167519304Speter * txt_ai_resolve --
167619304Speter *	When a line is resolved by <esc>, review autoindent characters.
167719304Speter */
167819304Speterstatic void
1679254225Spetertxt_ai_resolve(SCR *sp, TEXT *tp, int *changedp)
168019304Speter{
168119304Speter	u_long ts;
168219304Speter	int del;
168319304Speter	size_t cno, len, new, old, scno, spaces, tab_after_sp, tabs;
1684254225Speter	CHAR_T *p;
168519304Speter
168619304Speter	*changedp = 0;
168719304Speter
168819304Speter	/*
168919304Speter	 * If the line is empty, has an offset, or no autoindent
169019304Speter	 * characters, we're done.
169119304Speter	 */
169219304Speter	if (!tp->len || tp->offset || !tp->ai)
169319304Speter		return;
169419304Speter
169519304Speter	/*
169619304Speter	 * If the length is less than or equal to the autoindent
169719304Speter	 * characters, delete them.
169819304Speter	 */
169919304Speter	if (tp->len <= tp->ai) {
170019304Speter		tp->ai = tp->cno = tp->len = 0;
170119304Speter		return;
170219304Speter	}
170319304Speter
170419304Speter	/*
170519304Speter	 * The autoindent characters plus any leading <blank> characters
170619304Speter	 * in the line are resolved into the minimum number of characters.
170719304Speter	 * Historic practice.
170819304Speter	 */
170919304Speter	ts = O_VAL(sp, O_TABSTOP);
171019304Speter
171119304Speter	/* Figure out the last <blank> screen column. */
171219304Speter	for (p = tp->lb, scno = 0, len = tp->len,
171319304Speter	    spaces = tab_after_sp = 0; len-- && isblank(*p); ++p)
171419304Speter		if (*p == '\t') {
171519304Speter			if (spaces)
171619304Speter				tab_after_sp = 1;
171719304Speter			scno += COL_OFF(scno, ts);
171819304Speter		} else {
171919304Speter			++spaces;
172019304Speter			++scno;
172119304Speter		}
172219304Speter
172319304Speter	/*
172419304Speter	 * If there are no spaces, or no tabs after spaces and less than
172519304Speter	 * ts spaces, it's already minimal.
172619304Speter	 */
1727254225Speter	if (!spaces || (!tab_after_sp && spaces < ts))
172819304Speter		return;
172919304Speter
173019304Speter	/* Count up spaces/tabs needed to get to the target. */
173119304Speter	for (cno = 0, tabs = 0; cno + COL_OFF(cno, ts) <= scno; ++tabs)
173219304Speter		cno += COL_OFF(cno, ts);
173319304Speter	spaces = scno - cno;
173419304Speter
173519304Speter	/*
173619304Speter	 * Figure out how many characters we're dropping -- if we're not
173719304Speter	 * dropping any, it's already minimal, we're done.
173819304Speter	 */
173919304Speter	old = p - tp->lb;
174019304Speter	new = spaces + tabs;
174119304Speter	if (old == new)
174219304Speter		return;
174319304Speter
174419304Speter	/* Shift the rest of the characters down, adjust the counts. */
174519304Speter	del = old - new;
1746254225Speter	MEMMOVE(p - del, p, tp->len - old);
174719304Speter	tp->len -= del;
174819304Speter	tp->cno -= del;
174919304Speter
175019304Speter	/* Fill in space/tab characters. */
175119304Speter	for (p = tp->lb; tabs--;)
175219304Speter		*p++ = '\t';
175319304Speter	while (spaces--)
175419304Speter		*p++ = ' ';
175519304Speter	*changedp = 1;
175619304Speter}
175719304Speter
175819304Speter/*
175919304Speter * v_txt_auto --
176019304Speter *	Handle autoindent.  If aitp isn't NULL, use it, otherwise,
176119304Speter *	retrieve the line.
176219304Speter *
176319304Speter * PUBLIC: int v_txt_auto __P((SCR *, recno_t, TEXT *, size_t, TEXT *));
176419304Speter */
176519304Speterint
1766254225Speterv_txt_auto(SCR *sp, recno_t lno, TEXT *aitp, size_t len, TEXT *tp)
176719304Speter{
176819304Speter	size_t nlen;
1769254225Speter	CHAR_T *p, *t;
177019304Speter
177119304Speter	if (aitp == NULL) {
177219304Speter		/*
177319304Speter		 * If the ex append command is executed with an address of 0,
177419304Speter		 * it's possible to get here with a line number of 0.  Return
177519304Speter		 * an indent of 0.
177619304Speter		 */
177719304Speter		if (lno == 0) {
177819304Speter			tp->ai = 0;
177919304Speter			return (0);
178019304Speter		}
178119304Speter		if (db_get(sp, lno, DBG_FATAL, &t, &len))
178219304Speter			return (1);
178319304Speter	} else
178419304Speter		t = aitp->lb;
178519304Speter
178619304Speter	/* Count whitespace characters. */
178719304Speter	for (p = t; len > 0; ++p, --len)
178819304Speter		if (!isblank(*p))
178919304Speter			break;
179019304Speter
179119304Speter	/* Set count, check for no indentation. */
179219304Speter	if ((nlen = (p - t)) == 0)
179319304Speter		return (0);
179419304Speter
179519304Speter	/* Make sure the buffer's big enough. */
1796254225Speter	BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + nlen);
179719304Speter
179819304Speter	/* Copy the buffer's current contents up. */
179919304Speter	if (tp->len != 0)
1800254225Speter		MEMMOVE(tp->lb + nlen, tp->lb, tp->len);
180119304Speter	tp->len += nlen;
180219304Speter
180319304Speter	/* Copy the indentation into the new buffer. */
1804254225Speter	MEMMOVE(tp->lb, t, nlen);
180519304Speter
180619304Speter	/* Set the autoindent count. */
180719304Speter	tp->ai = nlen;
180819304Speter	return (0);
180919304Speter}
181019304Speter
181119304Speter/*
181219304Speter * txt_backup --
181319304Speter *	Back up to the previously edited line.
181419304Speter */
181519304Speterstatic TEXT *
1816254225Spetertxt_backup(SCR *sp, TEXTH *tiqh, TEXT *tp, u_int32_t *flagsp)
181719304Speter{
181819304Speter	VI_PRIVATE *vip;
181919304Speter	TEXT *ntp;
182019304Speter
182119304Speter	/* Get a handle on the previous TEXT structure. */
1822254225Speter	if ((ntp = TAILQ_PREV(tp, _texth, q)) == NULL) {
182319304Speter		if (!FL_ISSET(*flagsp, TXT_REPLAY))
182419304Speter			msgq(sp, M_BERR,
182519304Speter			    "193|Already at the beginning of the insert");
182619304Speter		return (tp);
182719304Speter	}
182819304Speter
182919304Speter	/* Bookkeeping. */
183019304Speter	ntp->len = ntp->sv_len;
183119304Speter
183219304Speter	/* Handle appending to the line. */
183319304Speter	vip = VIP(sp);
183419304Speter	if (ntp->owrite == 0 && ntp->insert == 0) {
183519304Speter		ntp->lb[ntp->len] = CH_CURSOR;
183619304Speter		++ntp->insert;
183719304Speter		++ntp->len;
183819304Speter		FL_SET(*flagsp, TXT_APPENDEOL);
183919304Speter	} else
184019304Speter		FL_CLR(*flagsp, TXT_APPENDEOL);
184119304Speter
184219304Speter	/* Release the current TEXT. */
1843254225Speter	TAILQ_REMOVE(tiqh, tp, q);
184419304Speter	text_free(tp);
184519304Speter
184619304Speter	/* Update the old line on the screen. */
184719304Speter	if (vs_change(sp, ntp->lno + 1, LINE_DELETE))
184819304Speter		return (NULL);
184919304Speter
185019304Speter	/* Return the new/current TEXT. */
185119304Speter	return (ntp);
185219304Speter}
185319304Speter
185419304Speter/*
185519304Speter * Text indentation is truly strange.  ^T and ^D do movements to the next or
185619304Speter * previous shiftwidth value, i.e. for a 1-based numbering, with shiftwidth=3,
185719304Speter * ^T moves a cursor on the 7th, 8th or 9th column to the 10th column, and ^D
185819304Speter * moves it back.
185919304Speter *
186019304Speter * !!!
186119304Speter * The ^T and ^D characters in historical vi had special meaning only when they
186219304Speter * were the first characters entered after entering text input mode.  As normal
186319304Speter * erase characters couldn't erase autoindent characters (^T in this case), it
186419304Speter * meant that inserting text into previously existing text was strange -- ^T
186519304Speter * only worked if it was the first keystroke(s), and then could only be erased
186619304Speter * using ^D.  This implementation treats ^T specially anywhere it occurs in the
186719304Speter * input, and permits the standard erase characters to erase the characters it
186819304Speter * inserts.
186919304Speter *
187019304Speter * !!!
187119304Speter * A fun test is to try:
187219304Speter *	:se sw=4 ai list
187319304Speter *	i<CR>^Tx<CR>^Tx<CR>^Tx<CR>^Dx<CR>^Dx<CR>^Dx<esc>
187419304Speter * Historic vi loses some of the '$' marks on the line ends, but otherwise gets
187519304Speter * it right.
187619304Speter *
187719304Speter * XXX
187819304Speter * Technically, txt_dent should be part of the screen interface, as it requires
187919304Speter * knowledge of character sizes, including <space>s, on the screen.  It's here
188019304Speter * because it's a complicated little beast, and I didn't want to shove it down
1881254225Speter * into the screen.  It's probable that KEY_COL will call into the screen once
188219304Speter * there are screens with different character representations.
188319304Speter *
188419304Speter * txt_dent --
188519304Speter *	Handle ^T indents, ^D outdents.
188619304Speter *
188719304Speter * If anything changes here, check the ex version to see if it needs similar
188819304Speter * changes.
188919304Speter */
189019304Speterstatic int
1891254225Spetertxt_dent(SCR *sp, TEXT *tp, int isindent)
189219304Speter{
189319304Speter	CHAR_T ch;
189419304Speter	u_long sw, ts;
1895254225Speter	size_t cno, current, spaces, target, tabs;
189619304Speter	int ai_reset;
189719304Speter
189819304Speter	ts = O_VAL(sp, O_TABSTOP);
189919304Speter	sw = O_VAL(sp, O_SHIFTWIDTH);
190019304Speter
190119304Speter	/*
190219304Speter	 * Since we don't know what precedes the character(s) being inserted
190319304Speter	 * (or deleted), the preceding whitespace characters must be resolved.
190419304Speter	 * An example is a <tab>, which doesn't need a full shiftwidth number
190519304Speter	 * of columns because it's preceded by <space>s.  This is easy to get
190619304Speter	 * if the user sets shiftwidth to a value less than tabstop (or worse,
190719304Speter	 * something for which tabstop isn't a multiple) and then uses ^T to
190819304Speter	 * indent, and ^D to outdent.
190919304Speter	 *
191019304Speter	 * Figure out the current and target screen columns.  In the historic
191119304Speter	 * vi, the autoindent column was NOT determined using display widths
191219304Speter	 * of characters as was the wrapmargin column.  For that reason, we
191319304Speter	 * can't use the vs_column() function, but have to calculate it here.
191419304Speter	 * This is slow, but it's normally only on the first few characters of
191519304Speter	 * a line.
191619304Speter	 */
191719304Speter	for (current = cno = 0; cno < tp->cno; ++cno)
191819304Speter		current += tp->lb[cno] == '\t' ?
1919254225Speter		    COL_OFF(current, ts) : KEY_COL(sp, tp->lb[cno]);
192019304Speter
192119304Speter	target = current;
192219304Speter	if (isindent)
192319304Speter		target += COL_OFF(target, sw);
1924246874Sdim	else {
1925246874Sdim		--target;
1926246874Sdim		target -= target % sw;
1927246874Sdim	}
192819304Speter
192919304Speter	/*
193019304Speter	 * The AI characters will be turned into overwrite characters if the
193119304Speter	 * cursor immediately follows them.  We test both the cursor position
193219304Speter	 * and the indent flag because there's no single test.  (^T can only
193319304Speter	 * be detected by the cursor position, and while we know that the test
193419304Speter	 * is always true for ^D, the cursor can be in more than one place, as
193519304Speter	 * "0^D" and "^D" are different.)
193619304Speter	 */
193719304Speter	ai_reset = !isindent || tp->cno == tp->ai + tp->offset;
193819304Speter
193919304Speter	/*
194019304Speter	 * Back up over any previous <blank> characters, changing them into
194119304Speter	 * overwrite characters (including any ai characters).  Then figure
194219304Speter	 * out the current screen column.
194319304Speter	 */
194419304Speter	for (; tp->cno > tp->offset &&
194519304Speter	    (tp->lb[tp->cno - 1] == ' ' || tp->lb[tp->cno - 1] == '\t');
194619304Speter	    --tp->cno, ++tp->owrite);
194719304Speter	for (current = cno = 0; cno < tp->cno; ++cno)
194819304Speter		current += tp->lb[cno] == '\t' ?
1949254225Speter		    COL_OFF(current, ts) : KEY_COL(sp, tp->lb[cno]);
195019304Speter
195119304Speter	/*
195219304Speter	 * If we didn't move up to or past the target, it's because there
195319304Speter	 * weren't enough characters to delete, e.g. the first character
195419304Speter	 * of the line was a tp->offset character, and the user entered
195519304Speter	 * ^D to move to the beginning of a line.  An example of this is:
195619304Speter	 *
195719304Speter	 *	:set ai sw=4<cr>i<space>a<esc>i^T^D
195819304Speter	 *
195919304Speter	 * Otherwise, count up the total spaces/tabs needed to get from the
196019304Speter	 * beginning of the line (or the last non-<blank> character) to the
196119304Speter	 * target.
196219304Speter	 */
196319304Speter	if (current >= target)
196419304Speter		spaces = tabs = 0;
196519304Speter	else {
196619304Speter		for (cno = current,
196719304Speter		    tabs = 0; cno + COL_OFF(cno, ts) <= target; ++tabs)
196819304Speter			cno += COL_OFF(cno, ts);
196919304Speter		spaces = target - cno;
197019304Speter	}
197119304Speter
197219304Speter	/* If we overwrote ai characters, reset the ai count. */
197319304Speter	if (ai_reset)
197419304Speter		tp->ai = tabs + spaces;
197519304Speter
197619304Speter	/*
197719304Speter	 * Call txt_insch() to insert each character, so that we get the
197819304Speter	 * correct effect when we add a <tab> to replace N <spaces>.
197919304Speter	 */
198019304Speter	for (ch = '\t'; tabs > 0; --tabs)
198119304Speter		(void)txt_insch(sp, tp, &ch, 0);
198219304Speter	for (ch = ' '; spaces > 0; --spaces)
198319304Speter		(void)txt_insch(sp, tp, &ch, 0);
198419304Speter	return (0);
198519304Speter}
198619304Speter
198719304Speter/*
198819304Speter * txt_fc --
1989254225Speter *	File name and ex command completion.
199019304Speter */
199119304Speterstatic int
1992254225Spetertxt_fc(SCR *sp, TEXT *tp, int *redrawp)
199319304Speter{
199419304Speter	struct stat sb;
199519304Speter	ARGS **argv;
199619304Speter	EXCMD cmd;
199719304Speter	size_t indx, len, nlen, off;
1998254225Speter	int argc;
1999254225Speter	CHAR_T *p, *t, *bp;
2000254225Speter	char *np, *epd = NULL;
2001254225Speter	size_t nplen;
2002254225Speter	int fstwd = 1;
200319304Speter
200419304Speter	*redrawp = 0;
2005254225Speter	ex_cinit(sp, &cmd, 0, 0, OOBLNO, OOBLNO, 0);
200619304Speter
200719304Speter	/*
200819304Speter	 * Find the beginning of this "word" -- if we're at the beginning
200919304Speter	 * of the line, it's a special case.
201019304Speter	 */
201119304Speter	if (tp->cno == 1) {
201219304Speter		len = 0;
201319304Speter		p = tp->lb;
2014254225Speter	} else {
2015254225Speter		CHAR_T *ap;
2016254225Speter
2017254225Speter		for (len = 0,
2018254225Speter		    off = MAX(tp->ai, tp->offset), ap = tp->lb + off, p = ap;
2019254225Speter		    off < tp->cno; ++off, ++ap) {
2020254225Speter			if (IS_ESCAPE(sp, &cmd, *ap)) {
2021254225Speter				if (++off == tp->cno)
2022254225Speter					break;
2023254225Speter				++ap;
2024254225Speter				len += 2;
2025254225Speter			} else if (cmdskip(*ap)) {
2026254225Speter				p = ap + 1;
2027254225Speter				if (len > 0)
2028254225Speter					fstwd = 0;
2029254225Speter				len = 0;
2030254225Speter			} else
2031254225Speter				++len;
203219304Speter		}
2033254225Speter	}
203419304Speter
203519304Speter	/*
2036254225Speter	 * If we are at the first word, do ex command completion instead of
2037254225Speter	 * file name completion.
203819304Speter	 */
2039254225Speter	if (fstwd)
2040254225Speter		(void)argv_flt_ex(sp, &cmd, p, len);
2041254225Speter	else {
2042254225Speter		if ((bp = argv_uesc(sp, &cmd, p, len)) == NULL)
2043254225Speter			return (1);
2044254225Speter		if (argv_flt_path(sp, &cmd, bp, STRLEN(bp))) {
2045254225Speter			FREE_SPACEW(sp, bp, 0);
2046254225Speter			return (0);
2047254225Speter		}
2048254225Speter		FREE_SPACEW(sp, bp, 0);
204919304Speter	}
205019304Speter	argc = cmd.argc;
205119304Speter	argv = cmd.argv;
205219304Speter
205319304Speter	switch (argc) {
205419304Speter	case 0:				/* No matches. */
2055254225Speter		(void)sp->gp->scr_bell(sp);
205619304Speter		return (0);
205719304Speter	case 1:				/* One match. */
2058254225Speter		/* Always overwrite the old text. */
2059254225Speter		nlen = STRLEN(cmd.argv[0]->bp);
2060254225Speter		break;
206119304Speter	default:			/* Multiple matches. */
206219304Speter		*redrawp = 1;
206319304Speter		if (txt_fc_col(sp, argc, argv))
206419304Speter			return (1);
206519304Speter
206619304Speter		/* Find the length of the shortest match. */
206719304Speter		for (nlen = cmd.argv[0]->len; --argc > 0;) {
206819304Speter			if (cmd.argv[argc]->len < nlen)
206919304Speter				nlen = cmd.argv[argc]->len;
207019304Speter			for (indx = 0; indx < nlen &&
207119304Speter			    cmd.argv[argc]->bp[indx] == cmd.argv[0]->bp[indx];
207219304Speter			    ++indx);
207319304Speter			nlen = indx;
207419304Speter		}
207519304Speter		break;
207619304Speter	}
207719304Speter
2078254225Speter	/* Escape the matched part of the path. */
2079254225Speter	if (fstwd)
2080254225Speter		bp = cmd.argv[0]->bp;
2081254225Speter	else {
2082254225Speter		if ((bp = argv_esc(sp, &cmd, cmd.argv[0]->bp, nlen)) == NULL)
2083254225Speter			return (1);
2084254225Speter		nlen = STRLEN(bp);
2085254225Speter	}
2086254225Speter
208719304Speter	/* Overwrite the expanded text first. */
2088254225Speter	for (t = bp; len > 0 && nlen > 0; --len, --nlen)
208919304Speter		*p++ = *t++;
209019304Speter
209119304Speter	/* If lost text, make the remaining old text overwrite characters. */
209219304Speter	if (len) {
209319304Speter		tp->cno -= len;
209419304Speter		tp->owrite += len;
209519304Speter	}
209619304Speter
209719304Speter	/* Overwrite any overwrite characters next. */
209819304Speter	for (; nlen > 0 && tp->owrite > 0; --nlen, --tp->owrite, ++tp->cno)
209919304Speter		*p++ = *t++;
210019304Speter
210119304Speter	/* Shift remaining text up, and move the cursor to the end. */
210219304Speter	if (nlen) {
210319304Speter		off = p - tp->lb;
2104254225Speter		BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + nlen);
210519304Speter		p = tp->lb + off;
210619304Speter
210719304Speter		tp->cno += nlen;
210819304Speter		tp->len += nlen;
210919304Speter
211019304Speter		if (tp->insert != 0)
2111254225Speter			(void)MEMMOVE(p + nlen, p, tp->insert);
211219304Speter		while (nlen--)
211319304Speter			*p++ = *t++;
211419304Speter	}
211519304Speter
2116254225Speter	if (!fstwd)
2117254225Speter		FREE_SPACEW(sp, bp, 0);
2118254225Speter
2119254225Speter	/* If not a single match of path, we've done. */
2120254225Speter	if (argc != 1 || fstwd)
2121254225Speter		return (0);
2122254225Speter
2123254225Speter	/* If a single match and it's a directory, append a '/'. */
2124254225Speter	INT2CHAR(sp, cmd.argv[0]->bp, cmd.argv[0]->len + 1, np, nplen);
2125254225Speter	if ((epd = expanduser(np)) != NULL)
2126254225Speter		np = epd;
2127254225Speter	if (!stat(np, &sb) && S_ISDIR(sb.st_mode)) {
2128254225Speter		if (tp->owrite == 0) {
212919304Speter			off = p - tp->lb;
2130254225Speter			BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + 1);
213119304Speter			p = tp->lb + off;
213219304Speter			if (tp->insert != 0)
2133254225Speter				(void)MEMMOVE(p + 1, p, tp->insert);
213419304Speter			++tp->len;
213519304Speter		} else
213619304Speter			--tp->owrite;
213719304Speter
213819304Speter		++tp->cno;
213919304Speter		*p++ = '/';
214019304Speter	}
2141254225Speter	free(epd);
214219304Speter	return (0);
214319304Speter}
214419304Speter
214519304Speter/*
214619304Speter * txt_fc_col --
214719304Speter *	Display file names for file name completion.
214819304Speter */
214919304Speterstatic int
2150254225Spetertxt_fc_col(SCR *sp, int argc, ARGS **argv)
215119304Speter{
215219304Speter	ARGS **av;
215319304Speter	CHAR_T *p;
215419304Speter	GS *gp;
215519304Speter	size_t base, cnt, col, colwidth, numrows, numcols, prefix, row;
215619304Speter	int ac, nf, reset;
2157254225Speter	char *np, *pp;
2158254225Speter	size_t nlen;
215919304Speter
216019304Speter	gp = sp->gp;
216119304Speter
216219304Speter	/* Trim any directory prefix common to all of the files. */
2163254225Speter	INT2CHAR(sp, argv[0]->bp, argv[0]->len + 1, np, nlen);
2164254225Speter	if ((pp = strrchr(np, '/')) == NULL)
216519304Speter		prefix = 0;
216619304Speter	else {
2167254225Speter		prefix = (pp - np) + 1;
216819304Speter		for (ac = argc - 1, av = argv + 1; ac > 0; --ac, ++av)
216919304Speter			if (av[0]->len < prefix ||
2170254225Speter			    MEMCMP(av[0]->bp, argv[0]->bp,
2171254225Speter				   prefix)) {
217219304Speter				prefix = 0;
217319304Speter				break;
217419304Speter			}
217519304Speter	}
217619304Speter
217719304Speter	/*
217819304Speter	 * Figure out the column width for the longest name.  Output is done on
217919304Speter	 * 6 character "tab" boundaries for no particular reason.  (Since we
218019304Speter	 * don't output tab characters, we ignore the terminal's tab settings.)
218119304Speter	 * Ignore the user's tab setting because we have no idea how reasonable
218219304Speter	 * it is.
218319304Speter	 */
218419304Speter	for (ac = argc, av = argv, colwidth = 0; ac > 0; --ac, ++av) {
218519304Speter		for (col = 0, p = av[0]->bp + prefix; *p != '\0'; ++p)
2186254225Speter			col += KEY_COL(sp, *p);
218719304Speter		if (col > colwidth)
218819304Speter			colwidth = col;
218919304Speter	}
219019304Speter	colwidth += COL_OFF(colwidth, 6);
219119304Speter
219219304Speter	/*
219319304Speter	 * Writing to the bottom line of the screen is always turned off when
219419304Speter	 * SC_TINPUT_INFO is set.  Turn it back on, we know what we're doing.
219519304Speter	 */
219619304Speter	if (F_ISSET(sp, SC_TINPUT_INFO)) {
219719304Speter		reset = 1;
219819304Speter		F_CLR(sp, SC_TINPUT_INFO);
219919304Speter	} else
220019304Speter		reset = 0;
220119304Speter
220219304Speter#define	CHK_INTR							\
220319304Speter	if (F_ISSET(gp, G_INTERRUPTED))					\
220419304Speter		goto intr;
220519304Speter
220619304Speter	/* If the largest file name is too large, just print them. */
2207254225Speter	if (colwidth >= sp->cols) {
220819304Speter		for (ac = argc, av = argv; ac > 0; --ac, ++av) {
2209254225Speter			INT2CHAR(sp, av[0]->bp+prefix, av[0]->len+1-prefix,
2210254225Speter				 np, nlen);
2211254225Speter			pp = msg_print(sp, np, &nf);
2212254225Speter			(void)ex_printf(sp, "%s\n", pp);
2213254225Speter			if (nf)
2214254225Speter				FREE_SPACE(sp, pp, 0);
221519304Speter			if (F_ISSET(gp, G_INTERRUPTED))
221619304Speter				break;
221719304Speter		}
221819304Speter		CHK_INTR;
221919304Speter	} else {
222019304Speter		/* Figure out the number of columns. */
222119304Speter		numcols = (sp->cols - 1) / colwidth;
222219304Speter		if (argc > numcols) {
222319304Speter			numrows = argc / numcols;
222419304Speter			if (argc % numcols)
222519304Speter				++numrows;
222619304Speter		} else
222719304Speter			numrows = 1;
222819304Speter
222919304Speter		/* Display the files in sorted order. */
223019304Speter		for (row = 0; row < numrows; ++row) {
223119304Speter			for (base = row, col = 0; col < numcols; ++col) {
2232254225Speter				INT2CHAR(sp, argv[base]->bp+prefix,
2233254225Speter					argv[base]->len+1-prefix, np, nlen);
2234254225Speter				pp = msg_print(sp, np, &nf);
2235254225Speter				cnt = ex_printf(sp, "%s", pp);
223619304Speter				if (nf)
2237254225Speter					FREE_SPACE(sp, pp, 0);
223819304Speter				CHK_INTR;
223919304Speter				if ((base += numrows) >= argc)
224019304Speter					break;
224119304Speter				(void)ex_printf(sp,
224219304Speter				    "%*s", (int)(colwidth - cnt), "");
224319304Speter				CHK_INTR;
224419304Speter			}
224519304Speter			(void)ex_puts(sp, "\n");
224619304Speter			CHK_INTR;
224719304Speter		}
224819304Speter		(void)ex_puts(sp, "\n");
224919304Speter		CHK_INTR;
225019304Speter	}
225119304Speter	(void)ex_fflush(sp);
225219304Speter
225319304Speter	if (0) {
225419304Speterintr:		F_CLR(gp, G_INTERRUPTED);
225519304Speter	}
225619304Speter	if (reset)
225719304Speter		F_SET(sp, SC_TINPUT_INFO);
225819304Speter
225919304Speter	return (0);
226019304Speter}
226119304Speter
226219304Speter/*
226319304Speter * txt_emark --
226419304Speter *	Set the end mark on the line.
226519304Speter */
226619304Speterstatic int
2267254225Spetertxt_emark(SCR *sp, TEXT *tp, size_t cno)
226819304Speter{
2269254225Speter	CHAR_T ch;
2270254225Speter	u_char *kp;
227119304Speter	size_t chlen, nlen, olen;
2272254225Speter	CHAR_T *p;
227319304Speter
227419304Speter	ch = CH_ENDMARK;
227519304Speter
227619304Speter	/*
227719304Speter	 * The end mark may not be the same size as the current character.
227819304Speter	 * Don't let the line shift.
227919304Speter	 */
2280254225Speter	nlen = KEY_COL(sp, ch);
228119304Speter	if (tp->lb[cno] == '\t')
228219304Speter		(void)vs_columns(sp, tp->lb, tp->lno, &cno, &olen);
228319304Speter	else
2284254225Speter		olen = KEY_COL(sp, tp->lb[cno]);
228519304Speter
228619304Speter	/*
228719304Speter	 * If the line got longer, well, it's weird, but it's easy.  If
228819304Speter	 * it's the same length, it's easy.  If it got shorter, we have
228919304Speter	 * to fix it up.
229019304Speter	 */
229119304Speter	if (olen > nlen) {
2292254225Speter		BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + olen);
229319304Speter		chlen = olen - nlen;
229419304Speter		if (tp->insert != 0)
2295254225Speter			MEMMOVE(tp->lb + cno + 1 + chlen,
229619304Speter			    tp->lb + cno + 1, tp->insert);
229719304Speter
229819304Speter		tp->len += chlen;
229919304Speter		tp->owrite += chlen;
230019304Speter		p = tp->lb + cno;
2301254225Speter		if (tp->lb[cno] == '\t' ||
2302254225Speter		    KEY_NEEDSWIDE(sp, tp->lb[cno]))
230319304Speter			for (cno += chlen; chlen--;)
230419304Speter				*p++ = ' ';
230519304Speter		else
2306254225Speter			for (kp = (u_char *)
2307254225Speter			    KEY_NAME(sp, tp->lb[cno]),
230819304Speter			    cno += chlen; chlen--;)
230919304Speter				*p++ = *kp++;
231019304Speter	}
231119304Speter	tp->lb[cno] = ch;
231219304Speter	return (vs_change(sp, tp->lno, LINE_RESET));
231319304Speter}
231419304Speter
231519304Speter/*
231619304Speter * txt_err --
231719304Speter *	Handle an error during input processing.
231819304Speter */
231919304Speterstatic void
2320254225Spetertxt_err(SCR *sp, TEXTH *tiqh)
232119304Speter{
232219304Speter	recno_t lno;
232319304Speter
232419304Speter	/*
232519304Speter	 * The problem with input processing is that the cursor is at an
232619304Speter	 * indeterminate position since some input may have been lost due
232719304Speter	 * to a malloc error.  So, try to go back to the place from which
232819304Speter	 * the cursor started, knowing that it may no longer be available.
232919304Speter	 *
233019304Speter	 * We depend on at least one line number being set in the text
233119304Speter	 * chain.
233219304Speter	 */
2333254225Speter	for (lno = TAILQ_FIRST(tiqh)->lno;
233419304Speter	    !db_exist(sp, lno) && lno > 0; --lno);
233519304Speter
233619304Speter	sp->lno = lno == 0 ? 1 : lno;
233719304Speter	sp->cno = 0;
233819304Speter
233919304Speter	/* Redraw the screen, just in case. */
234019304Speter	F_SET(sp, SC_SCR_REDRAW);
234119304Speter}
234219304Speter
234319304Speter/*
234419304Speter * txt_hex --
234519304Speter *	Let the user insert any character value they want.
234619304Speter *
234719304Speter * !!!
234819304Speter * This is an extension.  The pattern "^X[0-9a-fA-F]*" is a way
234919304Speter * for the user to specify a character value which their keyboard
235019304Speter * may not be able to enter.
235119304Speter */
235219304Speterstatic int
2353254225Spetertxt_hex(SCR *sp, TEXT *tp)
235419304Speter{
235519304Speter	CHAR_T savec;
235619304Speter	size_t len, off;
235719304Speter	u_long value;
2358254225Speter	CHAR_T *p, *wp;
235919304Speter
236019304Speter	/*
236119304Speter	 * Null-terminate the string.  Since nul isn't a legal hex value,
236219304Speter	 * this should be okay, and lets us use a local routine, which
236319304Speter	 * presumably understands the character set, to convert the value.
236419304Speter	 */
236519304Speter	savec = tp->lb[tp->cno];
236619304Speter	tp->lb[tp->cno] = 0;
236719304Speter
236819304Speter	/* Find the previous CH_HEX character. */
236919304Speter	for (off = tp->cno - 1, p = tp->lb + off, len = 0;; --p, --off, ++len) {
237019304Speter		if (*p == CH_HEX) {
237119304Speter			wp = p + 1;
237219304Speter			break;
237319304Speter		}
237419304Speter		/* Not on this line?  Shouldn't happen. */
237519304Speter		if (off == tp->ai || off == tp->offset)
237619304Speter			goto nothex;
237719304Speter	}
237819304Speter
237919304Speter	/* If length of 0, then it wasn't a hex value. */
238019304Speter	if (len == 0)
238119304Speter		goto nothex;
238219304Speter
238319304Speter	/* Get the value. */
238419304Speter	errno = 0;
2385254225Speter	value = STRTOL(wp, NULL, 16);
2386254225Speter	if (errno || value > UCHAR_MAX) {
238719304Speternothex:		tp->lb[tp->cno] = savec;
238819304Speter		return (0);
238919304Speter	}
239019304Speter
239119304Speter	/* Restore the original character. */
239219304Speter	tp->lb[tp->cno] = savec;
239319304Speter
239419304Speter	/* Adjust the bookkeeping. */
239519304Speter	tp->cno -= len;
239619304Speter	tp->len -= len;
239719304Speter	tp->lb[tp->cno - 1] = value;
239819304Speter
239919304Speter	/* Copy down any overwrite characters. */
240019304Speter	if (tp->owrite)
2401254225Speter		MEMMOVE(tp->lb + tp->cno, tp->lb + tp->cno + len,
2402254225Speter		    tp->owrite);
240319304Speter
240419304Speter	/* Copy down any insert characters. */
240519304Speter	if (tp->insert)
2406254225Speter		MEMMOVE(tp->lb + tp->cno + tp->owrite,
2407254225Speter		    tp->lb + tp->cno + tp->owrite + len,
2408254225Speter		    tp->insert);
240919304Speter
241019304Speter	return (0);
241119304Speter}
241219304Speter
241319304Speter/*
241419304Speter * txt_insch --
241519304Speter *
241619304Speter * !!!
241719304Speter * Historic vi did a special screen optimization for tab characters.  As an
241819304Speter * example, for the keystrokes "iabcd<esc>0C<tab>", the tab overwrote the
241919304Speter * rest of the string when it was displayed.
242019304Speter *
242119304Speter * Because early versions of this implementation redisplayed the entire line
242219304Speter * on each keystroke, the "bcd" was pushed to the right as it ignored that
242319304Speter * the user had "promised" to change the rest of the characters.  However,
242419304Speter * the historic vi implementation had an even worse bug: given the keystrokes
242519304Speter * "iabcd<esc>0R<tab><esc>", the "bcd" disappears, and magically reappears
242619304Speter * on the second <esc> key.
242719304Speter *
242819304Speter * POSIX 1003.2 requires (will require) that this be fixed, specifying that
242919304Speter * vi overwrite characters the user has committed to changing, on the basis
243019304Speter * of the screen space they require, but that it not overwrite other characters.
243119304Speter */
243219304Speterstatic int
2433254225Spetertxt_insch(SCR *sp, TEXT *tp, CHAR_T *chp, u_int flags)
243419304Speter{
2435254225Speter	u_char *kp;
2436254225Speter	CHAR_T savech;
243719304Speter	size_t chlen, cno, copydown, olen, nlen;
2438254225Speter	CHAR_T *p;
243919304Speter
244019304Speter	/*
244119304Speter	 * The 'R' command does one-for-one replacement, because there's
244219304Speter	 * no way to know how many characters the user intends to replace.
244319304Speter	 */
244419304Speter	if (LF_ISSET(TXT_REPLACE)) {
244519304Speter		if (tp->owrite) {
244619304Speter			--tp->owrite;
244719304Speter			tp->lb[tp->cno++] = *chp;
244819304Speter			return (0);
244919304Speter		}
245019304Speter	} else if (tp->owrite) {		/* Overwrite a character. */
245119304Speter		cno = tp->cno;
245219304Speter
245319304Speter		/*
245419304Speter		 * If the old or new characters are tabs, then the length of the
245519304Speter		 * display depends on the character position in the display.  We
245619304Speter		 * don't even try to handle this here, just ask the screen.
245719304Speter		 */
245819304Speter		if (*chp == '\t') {
245919304Speter			savech = tp->lb[cno];
246019304Speter			tp->lb[cno] = '\t';
246119304Speter			(void)vs_columns(sp, tp->lb, tp->lno, &cno, &nlen);
246219304Speter			tp->lb[cno] = savech;
246319304Speter		} else
2464254225Speter			nlen = KEY_COL(sp, *chp);
246519304Speter
246619304Speter		/*
246719304Speter		 * Eat overwrite characters until we run out of them or we've
246819304Speter		 * handled the length of the new character.  If we only eat
246919304Speter		 * part of an overwrite character, break it into its component
247019304Speter		 * elements and display the remaining components.
247119304Speter		 */
247219304Speter		for (copydown = 0; nlen != 0 && tp->owrite != 0;) {
247319304Speter			--tp->owrite;
247419304Speter
247519304Speter			if (tp->lb[cno] == '\t')
247619304Speter				(void)vs_columns(sp,
247719304Speter				    tp->lb, tp->lno, &cno, &olen);
247819304Speter			else
2479254225Speter				olen = KEY_COL(sp, tp->lb[cno]);
248019304Speter
248119304Speter			if (olen == nlen) {
248219304Speter				nlen = 0;
248319304Speter				break;
248419304Speter			}
248519304Speter			if (olen < nlen) {
248619304Speter				++copydown;
248719304Speter				nlen -= olen;
248819304Speter			} else {
2489254225Speter				BINC_RETW(sp,
249019304Speter				    tp->lb, tp->lb_len, tp->len + olen);
249119304Speter				chlen = olen - nlen;
2492254225Speter				MEMMOVE(tp->lb + cno + 1 + chlen,
2493254225Speter				    tp->lb + cno + 1,
2494254225Speter				    tp->owrite + tp->insert);
249519304Speter
249619304Speter				tp->len += chlen;
249719304Speter				tp->owrite += chlen;
2498254225Speter				if (tp->lb[cno] == '\t' ||
2499254225Speter				   KEY_NEEDSWIDE(sp, tp->lb[cno]))
250019304Speter					for (p = tp->lb + cno + 1; chlen--;)
250119304Speter						*p++ = ' ';
250219304Speter				else
2503254225Speter					for (kp = (u_char *)
250419304Speter					    KEY_NAME(sp, tp->lb[cno]) + nlen,
250519304Speter					    p = tp->lb + cno + 1; chlen--;)
250619304Speter						*p++ = *kp++;
250719304Speter				nlen = 0;
250819304Speter				break;
250919304Speter			}
251019304Speter		}
251119304Speter
251219304Speter		/*
251319304Speter		 * If had to erase several characters, we adjust the total
251419304Speter		 * count, and if there are any characters left, shift them
251519304Speter		 * into position.
251619304Speter		 */
251719304Speter		if (copydown != 0 && (tp->len -= copydown) != 0)
2518254225Speter			MEMMOVE(tp->lb + cno, tp->lb + cno + copydown,
251919304Speter			    tp->owrite + tp->insert + copydown);
252019304Speter
252119304Speter		/* If we had enough overwrite characters, we're done. */
252219304Speter		if (nlen == 0) {
252319304Speter			tp->lb[tp->cno++] = *chp;
252419304Speter			return (0);
252519304Speter		}
252619304Speter	}
252719304Speter
252819304Speter	/* Check to see if the character fits into the input buffer. */
2529254225Speter	BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + 1);
253019304Speter
253119304Speter	++tp->len;
253219304Speter	if (tp->insert) {			/* Insert a character. */
253319304Speter		if (tp->insert == 1)
253419304Speter			tp->lb[tp->cno + 1] = tp->lb[tp->cno];
253519304Speter		else
2536254225Speter			MEMMOVE(tp->lb + tp->cno + 1,
253719304Speter			    tp->lb + tp->cno, tp->owrite + tp->insert);
253819304Speter	}
253919304Speter	tp->lb[tp->cno++] = *chp;
254019304Speter	return (0);
254119304Speter}
254219304Speter
254319304Speter/*
254419304Speter * txt_isrch --
254519304Speter *	Do an incremental search.
254619304Speter */
254719304Speterstatic int
2548254225Spetertxt_isrch(SCR *sp, VICMD *vp, TEXT *tp, u_int8_t *is_flagsp)
254919304Speter{
255019304Speter	MARK start;
255119304Speter	recno_t lno;
255219304Speter	u_int sf;
255319304Speter
255419304Speter	/* If it's a one-line screen, we don't do incrementals. */
255519304Speter	if (IS_ONELINE(sp)) {
255619304Speter		FL_CLR(*is_flagsp, IS_RUNNING);
255719304Speter		return (0);
255819304Speter	}
255919304Speter
256019304Speter	/*
256119304Speter	 * If the user erases back to the beginning of the buffer, there's
256219304Speter	 * nothing to search for.  Reset the cursor to the starting point.
256319304Speter	 */
256419304Speter	if (tp->cno <= 1) {
256519304Speter		vp->m_final = vp->m_start;
256619304Speter		return (0);
256719304Speter	}
256819304Speter
256919304Speter	/*
257019304Speter	 * If it's an RE quote character, and not quoted, ignore it until
257119304Speter	 * we get another character.
257219304Speter	 */
257319304Speter	if (tp->lb[tp->cno - 1] == '\\' &&
257419304Speter	    (tp->cno == 2 || tp->lb[tp->cno - 2] != '\\'))
257519304Speter		return (0);
257619304Speter
257719304Speter	/*
257819304Speter	 * If it's a magic shell character, and not quoted, reset the cursor
257919304Speter	 * to the starting point.
258019304Speter	 */
2581254225Speter	if (IS_SHELLMETA(sp, tp->lb[tp->cno - 1]) &&
258219304Speter	    (tp->cno == 2 || tp->lb[tp->cno - 2] != '\\'))
258319304Speter		vp->m_final = vp->m_start;
258419304Speter
258519304Speter	/*
258619304Speter	 * If we see the search pattern termination character, then quit doing
258719304Speter	 * an incremental search.  There may be more, e.g., ":/foo/;/bar/",
258819304Speter	 * and we can't handle that incrementally.  Also, reset the cursor to
258919304Speter	 * the original location, the ex search routines don't know anything
259019304Speter	 * about incremental searches.
259119304Speter	 */
259219304Speter	if (tp->lb[0] == tp->lb[tp->cno - 1] &&
259319304Speter	    (tp->cno == 2 || tp->lb[tp->cno - 2] != '\\')) {
259419304Speter		vp->m_final = vp->m_start;
259519304Speter		FL_CLR(*is_flagsp, IS_RUNNING);
259619304Speter		return (0);
259719304Speter	}
259819304Speter
259919304Speter	/*
260019304Speter	 * Remember the input line and discard the special input map,
260119304Speter	 * but don't overwrite the input line on the screen.
260219304Speter	 */
260319304Speter	lno = tp->lno;
260419304Speter	F_SET(VIP(sp), VIP_S_MODELINE);
260519304Speter	F_CLR(sp, SC_TINPUT | SC_TINPUT_INFO);
260619304Speter	if (txt_map_end(sp))
260719304Speter		return (1);
260819304Speter
260919304Speter	/*
261019304Speter	 * Specify a starting point and search.  If we find a match, move to
261119304Speter	 * it and refresh the screen.  If we didn't find the match, then we
261219304Speter	 * beep the screen.  When searching from the original cursor position,
261319304Speter	 * we have to move the cursor, otherwise, we don't want to move the
261419304Speter	 * cursor in case the text at the current position continues to match.
261519304Speter	 */
261619304Speter	if (FL_ISSET(*is_flagsp, IS_RESTART)) {
261719304Speter		start = vp->m_start;
261819304Speter		sf = SEARCH_SET;
261919304Speter	} else {
262019304Speter		start = vp->m_final;
262119304Speter		sf = SEARCH_INCR | SEARCH_SET;
262219304Speter	}
262319304Speter
262419304Speter	if (tp->lb[0] == '/' ?
262519304Speter	    !f_search(sp,
262619304Speter	    &start, &vp->m_final, tp->lb + 1, tp->cno - 1, NULL, sf) :
262719304Speter	    !b_search(sp,
262819304Speter	    &start, &vp->m_final, tp->lb + 1, tp->cno - 1, NULL, sf)) {
262919304Speter		sp->lno = vp->m_final.lno;
263019304Speter		sp->cno = vp->m_final.cno;
263119304Speter		FL_CLR(*is_flagsp, IS_RESTART);
263219304Speter
263319304Speter		if (!KEYS_WAITING(sp) && vs_refresh(sp, 0))
263419304Speter			return (1);
263519304Speter	} else
263619304Speter		FL_SET(*is_flagsp, IS_RESTART);
263719304Speter
263819304Speter	/* Reinstantiate the special input map. */
263919304Speter	if (txt_map_init(sp))
264019304Speter		return (1);
264119304Speter	F_CLR(VIP(sp), VIP_S_MODELINE);
264219304Speter	F_SET(sp, SC_TINPUT | SC_TINPUT_INFO);
264319304Speter
264419304Speter	/* Reset the line number of the input line. */
264519304Speter	tp->lno = TMAP[0].lno;
264619304Speter
264719304Speter	/*
264819304Speter	 * If the colon command-line moved, i.e. the screen scrolled,
264919304Speter	 * refresh the input line.
265019304Speter	 *
265119304Speter	 * XXX
265219304Speter	 * We shouldn't be calling vs_line, here -- we need dirty bits
265319304Speter	 * on entries in the SMAP array.
265419304Speter	 */
265519304Speter	if (lno != TMAP[0].lno) {
265619304Speter		if (vs_line(sp, &TMAP[0], NULL, NULL))
265719304Speter			return (1);
265819304Speter		(void)sp->gp->scr_refresh(sp, 0);
265919304Speter	}
266019304Speter	return (0);
266119304Speter}
266219304Speter
266319304Speter/*
266419304Speter * txt_resolve --
266519304Speter *	Resolve the input text chain into the file.
266619304Speter */
266719304Speterstatic int
2668254225Spetertxt_resolve(SCR *sp, TEXTH *tiqh, u_int32_t flags)
266919304Speter{
267019304Speter	VI_PRIVATE *vip;
267119304Speter	TEXT *tp;
267219304Speter	recno_t lno;
267319304Speter	int changed;
267419304Speter
267519304Speter	/*
267619304Speter	 * The first line replaces a current line, and all subsequent lines
267719304Speter	 * are appended into the file.  Resolve autoindented characters for
267819304Speter	 * each line before committing it.  If the latter causes the line to
267919304Speter	 * change, we have to redisplay it, otherwise the information cached
268019304Speter	 * about the line will be wrong.
268119304Speter	 */
268219304Speter	vip = VIP(sp);
2683254225Speter	tp = TAILQ_FIRST(tiqh);
268419304Speter
268519304Speter	if (LF_ISSET(TXT_AUTOINDENT))
268619304Speter		txt_ai_resolve(sp, tp, &changed);
268719304Speter	else
268819304Speter		changed = 0;
268919304Speter	if (db_set(sp, tp->lno, tp->lb, tp->len) ||
2690254225Speter	    (changed && vs_change(sp, tp->lno, LINE_RESET)))
269119304Speter		return (1);
269219304Speter
2693254225Speter	for (lno = tp->lno; (tp = TAILQ_NEXT(tp, q)) != NULL; ++lno) {
269419304Speter		if (LF_ISSET(TXT_AUTOINDENT))
269519304Speter			txt_ai_resolve(sp, tp, &changed);
269619304Speter		else
269719304Speter			changed = 0;
269819304Speter		if (db_append(sp, 0, lno, tp->lb, tp->len) ||
2699254225Speter		    (changed && vs_change(sp, tp->lno, LINE_RESET)))
270019304Speter			return (1);
270119304Speter	}
270219304Speter
270319304Speter	/*
270419304Speter	 * Clear the input flag, the look-aside buffer is no longer valid.
270519304Speter	 * Has to be done as part of text resolution, or upon return we'll
270619304Speter	 * be looking at incorrect data.
270719304Speter	 */
270819304Speter	F_CLR(sp, SC_TINPUT);
270919304Speter
271019304Speter	return (0);
271119304Speter}
271219304Speter
271319304Speter/*
271419304Speter * txt_showmatch --
271519304Speter *	Show a character match.
271619304Speter *
271719304Speter * !!!
271819304Speter * Historic vi tried to display matches even in the :colon command line.
271919304Speter * I think not.
272019304Speter */
272119304Speterstatic int
2722254225Spetertxt_showmatch(SCR *sp, TEXT *tp)
272319304Speter{
272419304Speter	GS *gp;
272519304Speter	VCS cs;
272619304Speter	MARK m;
272719304Speter	int cnt, endc, startc;
272819304Speter
272919304Speter	gp = sp->gp;
273019304Speter
273119304Speter	/*
273219304Speter	 * Do a refresh first, in case we haven't done one in awhile,
273319304Speter	 * so the user can see what we're complaining about.
273419304Speter	 */
273519304Speter	UPDATE_POSITION(sp, tp);
273619304Speter	if (vs_refresh(sp, 1))
273719304Speter		return (1);
273819304Speter
273919304Speter	/*
274019304Speter	 * We don't display the match if it's not on the screen.  Find
274119304Speter	 * out what the first character on the screen is.
274219304Speter	 */
274319304Speter	if (vs_sm_position(sp, &m, 0, P_TOP))
274419304Speter		return (1);
274519304Speter
274619304Speter	/* Initialize the getc() interface. */
274719304Speter	cs.cs_lno = tp->lno;
274819304Speter	cs.cs_cno = tp->cno - 1;
274919304Speter	if (cs_init(sp, &cs))
275019304Speter		return (1);
2751254225Speter	startc = STRCHR(VIP(sp)->mcs, endc = cs.cs_ch)[-1];
275219304Speter
275319304Speter	/* Search for the match. */
275419304Speter	for (cnt = 1;;) {
275519304Speter		if (cs_prev(sp, &cs))
275619304Speter			return (1);
275719304Speter		if (cs.cs_flags != 0) {
275819304Speter			if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF) {
275919304Speter				msgq(sp, M_BERR,
276019304Speter				    "Unmatched %s", KEY_NAME(sp, endc));
276119304Speter				return (0);
276219304Speter			}
276319304Speter			continue;
276419304Speter		}
276519304Speter		if (cs.cs_ch == endc)
276619304Speter			++cnt;
276719304Speter		else if (cs.cs_ch == startc && --cnt == 0)
276819304Speter			break;
276919304Speter	}
277019304Speter
277119304Speter	/* If the match is on the screen, move to it. */
2772254225Speter	if (cs.cs_lno < m.lno || (cs.cs_lno == m.lno && cs.cs_cno < m.cno))
277319304Speter		return (0);
277419304Speter	sp->lno = cs.cs_lno;
277519304Speter	sp->cno = cs.cs_cno;
277619304Speter	if (vs_refresh(sp, 1))
277719304Speter		return (1);
277819304Speter
277919304Speter	/* Wait for timeout or character arrival. */
278019304Speter	return (v_event_get(sp,
278119304Speter	    NULL, O_VAL(sp, O_MATCHTIME) * 100, EC_TIMEOUT));
278219304Speter}
278319304Speter
278419304Speter/*
278519304Speter * txt_margin --
278619304Speter *	Handle margin wrap.
278719304Speter */
278819304Speterstatic int
2789254225Spetertxt_margin(SCR *sp, TEXT *tp, TEXT *wmtp, int *didbreak, u_int32_t flags)
279019304Speter{
279119304Speter	VI_PRIVATE *vip;
279219304Speter	size_t len, off;
2793254225Speter	CHAR_T *p, *wp;
279419304Speter
279519304Speter	/* Find the nearest previous blank. */
279619304Speter	for (off = tp->cno - 1, p = tp->lb + off, len = 0;; --off, --p, ++len) {
279719304Speter		if (isblank(*p)) {
279819304Speter			wp = p + 1;
279919304Speter			break;
280019304Speter		}
280119304Speter
280219304Speter		/*
280319304Speter		 * If reach the start of the line, there's nowhere to break.
280419304Speter		 *
280519304Speter		 * !!!
280619304Speter		 * Historic vi belled each time a character was entered after
280719304Speter		 * crossing the margin until a space was entered which could
280819304Speter		 * be used to break the line.  I don't as it tends to wake the
280919304Speter		 * cats.
281019304Speter		 */
281119304Speter		if (off == tp->ai || off == tp->offset) {
281219304Speter			*didbreak = 0;
281319304Speter			return (0);
281419304Speter		}
281519304Speter	}
281619304Speter
281719304Speter	/*
281819304Speter	 * Store saved information about the rest of the line in the
281919304Speter	 * wrapmargin TEXT structure.
282019304Speter	 *
282119304Speter	 * !!!
282219304Speter	 * The offset field holds the length of the current characters
282319304Speter	 * that the user entered, but which are getting split to the new
282419304Speter	 * line -- it's going to be used to set the cursor value when we
282519304Speter	 * move to the new line.
282619304Speter	 */
282719304Speter	vip = VIP(sp);
282819304Speter	wmtp->lb = p + 1;
282919304Speter	wmtp->offset = len;
283019304Speter	wmtp->insert = LF_ISSET(TXT_APPENDEOL) ?  tp->insert - 1 : tp->insert;
283119304Speter	wmtp->owrite = tp->owrite;
283219304Speter
283319304Speter	/* Correct current bookkeeping information. */
283419304Speter	tp->cno -= len;
283519304Speter	if (LF_ISSET(TXT_APPENDEOL)) {
283619304Speter		tp->len -= len + tp->owrite + (tp->insert - 1);
283719304Speter		tp->insert = 1;
283819304Speter	} else {
283919304Speter		tp->len -= len + tp->owrite + tp->insert;
284019304Speter		tp->insert = 0;
284119304Speter	}
284219304Speter	tp->owrite = 0;
284319304Speter
284419304Speter	/*
284519304Speter	 * !!!
284619304Speter	 * Delete any trailing whitespace from the current line.
284719304Speter	 */
284819304Speter	for (;; --p, --off) {
284919304Speter		if (!isblank(*p))
285019304Speter			break;
285119304Speter		--tp->cno;
285219304Speter		--tp->len;
285319304Speter		if (off == tp->ai || off == tp->offset)
285419304Speter			break;
285519304Speter	}
285619304Speter	*didbreak = 1;
285719304Speter	return (0);
285819304Speter}
285919304Speter
286019304Speter/*
286119304Speter * txt_Rresolve --
286219304Speter *	Resolve the input line for the 'R' command.
286319304Speter */
286419304Speterstatic void
2865254225Spetertxt_Rresolve(SCR *sp, TEXTH *tiqh, TEXT *tp, const size_t orig_len)
286619304Speter{
286719304Speter	TEXT *ttp;
286819304Speter	size_t input_len, retain;
2869254225Speter	CHAR_T *p;
287019304Speter
287119304Speter	/*
287219304Speter	 * Check to make sure that the cursor hasn't moved beyond
287319304Speter	 * the end of the line.
287419304Speter	 */
287519304Speter	if (tp->owrite == 0)
287619304Speter		return;
287719304Speter
287819304Speter	/*
287919304Speter	 * Calculate how many characters the user has entered,
288019304Speter	 * plus the blanks erased by <carriage-return>/<newline>s.
288119304Speter	 */
2882254225Speter	for (ttp = TAILQ_FIRST(tiqh), input_len = 0;;) {
288319304Speter		input_len += ttp == tp ? tp->cno : ttp->len + ttp->R_erase;
2884254225Speter		if ((ttp = TAILQ_NEXT(ttp, q)) == NULL)
288519304Speter			break;
288619304Speter	}
288719304Speter
288819304Speter	/*
288919304Speter	 * If the user has entered less characters than the original line
289019304Speter	 * was long, restore any overwriteable characters to the original
289119304Speter	 * characters.  These characters are entered as "insert characters",
289219304Speter	 * because they're after the cursor and we don't want to lose them.
289319304Speter	 * (This is okay because the R command has no insert characters.)
289419304Speter	 * We set owrite to 0 so that the insert characters don't get copied
289519304Speter	 * to somewhere else, which means that the line and the length have
289619304Speter	 * to be adjusted here as well.
289719304Speter	 *
289819304Speter	 * We have to retrieve the original line because the original pinned
289919304Speter	 * page has long since been discarded.  If it doesn't exist, that's
290019304Speter	 * okay, the user just extended the file.
290119304Speter	 */
290219304Speter	if (input_len < orig_len) {
290319304Speter		retain = MIN(tp->owrite, orig_len - input_len);
290419304Speter		if (db_get(sp,
2905254225Speter		    TAILQ_FIRST(tiqh)->lno, DBG_FATAL | DBG_NOCACHE, &p, NULL))
290619304Speter			return;
2907254225Speter		MEMCPY(tp->lb + tp->cno, p + input_len, retain);
290819304Speter		tp->len -= tp->owrite - retain;
290919304Speter		tp->owrite = 0;
291019304Speter		tp->insert += retain;
291119304Speter	}
291219304Speter}
291319304Speter
291419304Speter/*
291519304Speter * txt_nomorech --
291619304Speter *	No more characters message.
291719304Speter */
291819304Speterstatic void
2919254225Spetertxt_nomorech(SCR *sp)
292019304Speter{
292119304Speter	msgq(sp, M_BERR, "194|No more characters to erase");
292219304Speter}
2923