xref: /freebsd/contrib/nvi/vi/v_txt.c (revision 755cc40c21ca63388c6a67ba848a908b429e9391)
1b8ba871bSPeter Wemm /*-
2b8ba871bSPeter Wemm  * Copyright (c) 1993, 1994
3b8ba871bSPeter Wemm  *	The Regents of the University of California.  All rights reserved.
4b8ba871bSPeter Wemm  * Copyright (c) 1992, 1993, 1994, 1995, 1996
5b8ba871bSPeter Wemm  *	Keith Bostic.  All rights reserved.
6b8ba871bSPeter Wemm  *
7b8ba871bSPeter Wemm  * See the LICENSE file for redistribution information.
8b8ba871bSPeter Wemm  */
9b8ba871bSPeter Wemm 
10b8ba871bSPeter Wemm #include "config.h"
11b8ba871bSPeter Wemm 
12b8ba871bSPeter Wemm #include <sys/types.h>
13b8ba871bSPeter Wemm #include <sys/queue.h>
14b8ba871bSPeter Wemm #include <sys/stat.h>
15b8ba871bSPeter Wemm 
16b8ba871bSPeter Wemm #include <bitstring.h>
17b8ba871bSPeter Wemm #include <ctype.h>
18b8ba871bSPeter Wemm #include <errno.h>
19b8ba871bSPeter Wemm #include <limits.h>
20b8ba871bSPeter Wemm #include <stdio.h>
21b8ba871bSPeter Wemm #include <stdlib.h>
22b8ba871bSPeter Wemm #include <string.h>
23b8ba871bSPeter Wemm #include <unistd.h>
24b8ba871bSPeter Wemm 
25b8ba871bSPeter Wemm #include "../common/common.h"
26b8ba871bSPeter Wemm #include "vi.h"
27b8ba871bSPeter Wemm 
28c271fa92SBaptiste Daroussin static int	 txt_abbrev(SCR *, TEXT *, CHAR_T *, int, int *, int *);
29c271fa92SBaptiste Daroussin static void	 txt_ai_resolve(SCR *, TEXT *, int *);
30c271fa92SBaptiste Daroussin static TEXT	*txt_backup(SCR *, TEXTH *, TEXT *, u_int32_t *);
31110d525eSBaptiste Daroussin static int	 txt_dent(SCR *, TEXT *, int, int);
32c271fa92SBaptiste Daroussin static int	 txt_emark(SCR *, TEXT *, size_t);
33c271fa92SBaptiste Daroussin static void	 txt_err(SCR *, TEXTH *);
34c271fa92SBaptiste Daroussin static int	 txt_fc(SCR *, TEXT *, int *);
35c271fa92SBaptiste Daroussin static int	 txt_fc_col(SCR *, int, ARGS **);
36c271fa92SBaptiste Daroussin static int	 txt_hex(SCR *, TEXT *);
37c271fa92SBaptiste Daroussin static int	 txt_insch(SCR *, TEXT *, CHAR_T *, u_int);
38c271fa92SBaptiste Daroussin static int	 txt_isrch(SCR *, VICMD *, TEXT *, u_int8_t *);
39c271fa92SBaptiste Daroussin static int	 txt_map_end(SCR *);
40c271fa92SBaptiste Daroussin static int	 txt_map_init(SCR *);
41c271fa92SBaptiste Daroussin static int	 txt_margin(SCR *, TEXT *, TEXT *, int *, u_int32_t);
42c271fa92SBaptiste Daroussin static void	 txt_nomorech(SCR *);
43c271fa92SBaptiste Daroussin static void	 txt_Rresolve(SCR *, TEXTH *, TEXT *, const size_t);
44c271fa92SBaptiste Daroussin static int	 txt_resolve(SCR *, TEXTH *, u_int32_t);
45c271fa92SBaptiste Daroussin static int	 txt_showmatch(SCR *, TEXT *);
46c271fa92SBaptiste Daroussin static void	 txt_unmap(SCR *, TEXT *, u_int32_t *);
47b8ba871bSPeter Wemm 
48b8ba871bSPeter Wemm /* Cursor character (space is hard to track on the screen). */
49b8ba871bSPeter Wemm #if defined(DEBUG) && 0
50b8ba871bSPeter Wemm #undef	CH_CURSOR
51b8ba871bSPeter Wemm #define	CH_CURSOR	'+'
52b8ba871bSPeter Wemm #endif
53b8ba871bSPeter Wemm 
54b8ba871bSPeter Wemm /*
55b8ba871bSPeter Wemm  * v_tcmd --
56b8ba871bSPeter Wemm  *	Fill a buffer from the terminal for vi.
57b8ba871bSPeter Wemm  *
58c271fa92SBaptiste Daroussin  * PUBLIC: int v_tcmd(SCR *, VICMD *, ARG_CHAR_T, u_int);
59b8ba871bSPeter Wemm  */
60b8ba871bSPeter Wemm int
v_tcmd(SCR * sp,VICMD * vp,ARG_CHAR_T prompt,u_int flags)61f0957ccaSPeter Wemm v_tcmd(SCR *sp, VICMD *vp, ARG_CHAR_T prompt, u_int flags)
62b8ba871bSPeter Wemm {
63b8ba871bSPeter Wemm 	/* Normally, we end up where we started. */
64b8ba871bSPeter Wemm 	vp->m_final.lno = sp->lno;
65b8ba871bSPeter Wemm 	vp->m_final.cno = sp->cno;
66b8ba871bSPeter Wemm 
67b8ba871bSPeter Wemm 	/* Initialize the map. */
68b8ba871bSPeter Wemm 	if (txt_map_init(sp))
69b8ba871bSPeter Wemm 		return (1);
70b8ba871bSPeter Wemm 
71b8ba871bSPeter Wemm 	/* Move to the last line. */
72b8ba871bSPeter Wemm 	sp->lno = TMAP[0].lno;
73b8ba871bSPeter Wemm 	sp->cno = 0;
74b8ba871bSPeter Wemm 
75b8ba871bSPeter Wemm 	/* Don't update the modeline for now. */
76b8ba871bSPeter Wemm 	F_SET(sp, SC_TINPUT_INFO);
77b8ba871bSPeter Wemm 
78b8ba871bSPeter Wemm 	/* Set the input flags. */
79b8ba871bSPeter Wemm 	LF_SET(TXT_APPENDEOL |
80b8ba871bSPeter Wemm 	    TXT_CR | TXT_ESCAPE | TXT_INFOLINE | TXT_MAPINPUT);
81b8ba871bSPeter Wemm 	if (O_ISSET(sp, O_ALTWERASE))
82b8ba871bSPeter Wemm 		LF_SET(TXT_ALTWERASE);
83b8ba871bSPeter Wemm 	if (O_ISSET(sp, O_TTYWERASE))
84b8ba871bSPeter Wemm 		LF_SET(TXT_TTYWERASE);
85b8ba871bSPeter Wemm 
86b8ba871bSPeter Wemm 	/* Do the input thing. */
87b8ba871bSPeter Wemm 	if (v_txt(sp, vp, NULL, NULL, 0, prompt, 0, 1, flags))
88b8ba871bSPeter Wemm 		return (1);
89b8ba871bSPeter Wemm 
90b8ba871bSPeter Wemm 	/* Reenable the modeline updates. */
91b8ba871bSPeter Wemm 	F_CLR(sp, SC_TINPUT_INFO);
92b8ba871bSPeter Wemm 
93b8ba871bSPeter Wemm 	/* Clean up the map. */
94b8ba871bSPeter Wemm 	if (txt_map_end(sp))
95b8ba871bSPeter Wemm 		return (1);
96b8ba871bSPeter Wemm 
97b8ba871bSPeter Wemm 	if (IS_ONELINE(sp))
98b8ba871bSPeter Wemm 		F_SET(sp, SC_SCR_REDRAW);	/* XXX */
99b8ba871bSPeter Wemm 
100b8ba871bSPeter Wemm 	/* Set the cursor to the resulting position. */
101b8ba871bSPeter Wemm 	sp->lno = vp->m_final.lno;
102b8ba871bSPeter Wemm 	sp->cno = vp->m_final.cno;
103b8ba871bSPeter Wemm 
104b8ba871bSPeter Wemm 	return (0);
105b8ba871bSPeter Wemm }
106b8ba871bSPeter Wemm 
107b8ba871bSPeter Wemm /*
108b8ba871bSPeter Wemm  * txt_map_init
109b8ba871bSPeter Wemm  *	Initialize the screen map for colon command-line input.
110b8ba871bSPeter Wemm  */
111b8ba871bSPeter Wemm static int
txt_map_init(SCR * sp)112f0957ccaSPeter Wemm txt_map_init(SCR *sp)
113b8ba871bSPeter Wemm {
114b8ba871bSPeter Wemm 	SMAP *esmp;
115b8ba871bSPeter Wemm 	VI_PRIVATE *vip;
116b8ba871bSPeter Wemm 
117b8ba871bSPeter Wemm 	vip = VIP(sp);
118b8ba871bSPeter Wemm 	if (!IS_ONELINE(sp)) {
119b8ba871bSPeter Wemm 		/*
120b8ba871bSPeter Wemm 		 * Fake like the user is doing input on the last line of the
121b8ba871bSPeter Wemm 		 * screen.  This makes all of the scrolling work correctly,
122b8ba871bSPeter Wemm 		 * and allows us the use of the vi text editing routines, not
123b8ba871bSPeter Wemm 		 * to mention practically infinite length ex commands.
124b8ba871bSPeter Wemm 		 *
125b8ba871bSPeter Wemm 		 * Save the current location.
126b8ba871bSPeter Wemm 		 */
127b8ba871bSPeter Wemm 		vip->sv_tm_lno = TMAP->lno;
128b8ba871bSPeter Wemm 		vip->sv_tm_soff = TMAP->soff;
129b8ba871bSPeter Wemm 		vip->sv_tm_coff = TMAP->coff;
130b8ba871bSPeter Wemm 		vip->sv_t_maxrows = sp->t_maxrows;
131b8ba871bSPeter Wemm 		vip->sv_t_minrows = sp->t_minrows;
132b8ba871bSPeter Wemm 		vip->sv_t_rows = sp->t_rows;
133b8ba871bSPeter Wemm 
134b8ba871bSPeter Wemm 		/*
135b8ba871bSPeter Wemm 		 * If it's a small screen, TMAP may be small for the screen.
136b8ba871bSPeter Wemm 		 * Fix it, filling in fake lines as we go.
137b8ba871bSPeter Wemm 		 */
138b8ba871bSPeter Wemm 		if (IS_SMALL(sp))
139b8ba871bSPeter Wemm 			for (esmp =
140b8ba871bSPeter Wemm 			    HMAP + (sp->t_maxrows - 1); TMAP < esmp; ++TMAP) {
141b8ba871bSPeter Wemm 				TMAP[1].lno = TMAP[0].lno + 1;
142b8ba871bSPeter Wemm 				TMAP[1].coff = HMAP->coff;
143b8ba871bSPeter Wemm 				TMAP[1].soff = 1;
144b8ba871bSPeter Wemm 			}
145b8ba871bSPeter Wemm 
146b8ba871bSPeter Wemm 		/* Build the fake entry. */
147b8ba871bSPeter Wemm 		TMAP[1].lno = TMAP[0].lno + 1;
148b8ba871bSPeter Wemm 		TMAP[1].soff = 1;
149b8ba871bSPeter Wemm 		TMAP[1].coff = 0;
150b8ba871bSPeter Wemm 		SMAP_FLUSH(&TMAP[1]);
151b8ba871bSPeter Wemm 		++TMAP;
152b8ba871bSPeter Wemm 
153b8ba871bSPeter Wemm 		/* Reset the screen information. */
154b8ba871bSPeter Wemm 		sp->t_rows = sp->t_minrows = ++sp->t_maxrows;
155b8ba871bSPeter Wemm 	}
156b8ba871bSPeter Wemm 	return (0);
157b8ba871bSPeter Wemm }
158b8ba871bSPeter Wemm 
159b8ba871bSPeter Wemm /*
160b8ba871bSPeter Wemm  * txt_map_end
161b8ba871bSPeter Wemm  *	Reset the screen map for colon command-line input.
162b8ba871bSPeter Wemm  */
163b8ba871bSPeter Wemm static int
txt_map_end(SCR * sp)164f0957ccaSPeter Wemm txt_map_end(SCR *sp)
165b8ba871bSPeter Wemm {
166b8ba871bSPeter Wemm 	VI_PRIVATE *vip;
167b8ba871bSPeter Wemm 	size_t cnt;
168b8ba871bSPeter Wemm 
169b8ba871bSPeter Wemm 	vip = VIP(sp);
170b8ba871bSPeter Wemm 	if (!IS_ONELINE(sp)) {
171b8ba871bSPeter Wemm 		/* Restore the screen information. */
172b8ba871bSPeter Wemm 		sp->t_rows = vip->sv_t_rows;
173b8ba871bSPeter Wemm 		sp->t_minrows = vip->sv_t_minrows;
174b8ba871bSPeter Wemm 		sp->t_maxrows = vip->sv_t_maxrows;
175b8ba871bSPeter Wemm 
176b8ba871bSPeter Wemm 		/*
177b8ba871bSPeter Wemm 		 * If it's a small screen, TMAP may be wrong.  Clear any
178b8ba871bSPeter Wemm 		 * lines that might have been overwritten.
179b8ba871bSPeter Wemm 		 */
180b8ba871bSPeter Wemm 		if (IS_SMALL(sp)) {
181b8ba871bSPeter Wemm 			for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) {
182b8ba871bSPeter Wemm 				(void)sp->gp->scr_move(sp, cnt, 0);
183b8ba871bSPeter Wemm 				(void)sp->gp->scr_clrtoeol(sp);
184b8ba871bSPeter Wemm 			}
185b8ba871bSPeter Wemm 			TMAP = HMAP + (sp->t_rows - 1);
186b8ba871bSPeter Wemm 		} else
187b8ba871bSPeter Wemm 			--TMAP;
188b8ba871bSPeter Wemm 
189b8ba871bSPeter Wemm 		/*
190b8ba871bSPeter Wemm 		 * The map may be wrong if the user entered more than one
191b8ba871bSPeter Wemm 		 * (logical) line.  Fix it.  If the user entered a whole
192b8ba871bSPeter Wemm 		 * screen, this will be slow, but we probably don't care.
193b8ba871bSPeter Wemm 		 */
194b8ba871bSPeter Wemm 		if (!O_ISSET(sp, O_LEFTRIGHT))
195b8ba871bSPeter Wemm 			while (vip->sv_tm_lno != TMAP->lno ||
196b8ba871bSPeter Wemm 			    vip->sv_tm_soff != TMAP->soff)
197b8ba871bSPeter Wemm 				if (vs_sm_1down(sp))
198b8ba871bSPeter Wemm 					return (1);
199b8ba871bSPeter Wemm 	}
200b8ba871bSPeter Wemm 
201b8ba871bSPeter Wemm 	/*
202b8ba871bSPeter Wemm 	 * Invalidate the cursor and the line size cache, the line never
203b8ba871bSPeter Wemm 	 * really existed.  This fixes bugs where the user searches for
204b8ba871bSPeter Wemm 	 * the last line on the screen + 1 and the refresh routine thinks
205b8ba871bSPeter Wemm 	 * that's where we just were.
206b8ba871bSPeter Wemm 	 */
207b8ba871bSPeter Wemm 	VI_SCR_CFLUSH(vip);
208b8ba871bSPeter Wemm 	F_SET(vip, VIP_CUR_INVALID);
209b8ba871bSPeter Wemm 
210b8ba871bSPeter Wemm 	return (0);
211b8ba871bSPeter Wemm }
212b8ba871bSPeter Wemm 
213b8ba871bSPeter Wemm /*
214b8ba871bSPeter Wemm  * If doing input mapping on the colon command line, may need to unmap
215b8ba871bSPeter Wemm  * based on the command.
216b8ba871bSPeter Wemm  */
217b8ba871bSPeter Wemm #define	UNMAP_TST							\
218b8ba871bSPeter Wemm 	FL_ISSET(ec_flags, EC_MAPINPUT) && LF_ISSET(TXT_INFOLINE)
219b8ba871bSPeter Wemm 
220b8ba871bSPeter Wemm /*
221b8ba871bSPeter Wemm  * Internally, we maintain tp->lno and tp->cno, externally, everyone uses
222b8ba871bSPeter Wemm  * sp->lno and sp->cno.  Make them consistent as necessary.
223b8ba871bSPeter Wemm  */
224*755cc40cSBaptiste Daroussin #define	UPDATE_POSITION(sp, tp) do {					\
225b8ba871bSPeter Wemm 	(sp)->lno = (tp)->lno;						\
226b8ba871bSPeter Wemm 	(sp)->cno = (tp)->cno;						\
227*755cc40cSBaptiste Daroussin } while (0)
228b8ba871bSPeter Wemm 
229b8ba871bSPeter Wemm /*
230b8ba871bSPeter Wemm  * v_txt --
231b8ba871bSPeter Wemm  *	Vi text input.
232b8ba871bSPeter Wemm  *
233c271fa92SBaptiste Daroussin  * PUBLIC: int v_txt(SCR *, VICMD *, MARK *,
234c271fa92SBaptiste Daroussin  * PUBLIC:    const CHAR_T *, size_t, ARG_CHAR_T, recno_t, u_long, u_int32_t);
235b8ba871bSPeter Wemm  */
236b8ba871bSPeter Wemm int
v_txt(SCR * sp,VICMD * vp,MARK * tm,const CHAR_T * lp,size_t len,ARG_CHAR_T prompt,recno_t ai_line,u_long rcount,u_int32_t flags)237f0957ccaSPeter Wemm v_txt(
238f0957ccaSPeter Wemm 	SCR *sp,
239f0957ccaSPeter Wemm 	VICMD *vp,
240f0957ccaSPeter Wemm 	MARK *tm,		/* To MARK. */
241f0957ccaSPeter Wemm 	const CHAR_T *lp,	/* Input line. */
242f0957ccaSPeter Wemm 	size_t len,		/* Input line length. */
243f0957ccaSPeter Wemm 	ARG_CHAR_T prompt,	/* Prompt to display. */
244f0957ccaSPeter Wemm 	recno_t ai_line,	/* Line number to use for autoindent count. */
245f0957ccaSPeter Wemm 	u_long rcount,		/* Replay count. */
246f0957ccaSPeter Wemm 	u_int32_t flags)	/* TXT_* flags. */
247b8ba871bSPeter Wemm {
248f0957ccaSPeter Wemm 	EVENT ev, *evp = NULL;	/* Current event. */
249b8ba871bSPeter Wemm 	EVENT fc;		/* File name completion event. */
250b8ba871bSPeter Wemm 	GS *gp;
251b8ba871bSPeter Wemm 	TEXT *ntp, *tp;		/* Input text structures. */
252b8ba871bSPeter Wemm 	TEXT ait;		/* Autoindent text structure. */
253f0957ccaSPeter Wemm 	TEXT wmt = {{ 0 }};	/* Wrapmargin text structure. */
254b8ba871bSPeter Wemm 	TEXTH *tiqh;
255b8ba871bSPeter Wemm 	VI_PRIVATE *vip;
256b8ba871bSPeter Wemm 	abb_t abb;		/* State of abbreviation checks. */
257b8ba871bSPeter Wemm 	carat_t carat;		/* State of the "[^0]^D" sequences. */
258b8ba871bSPeter Wemm 	quote_t quote;		/* State of quotation. */
259b8ba871bSPeter Wemm 	size_t owrite, insert;	/* Temporary copies of TEXT fields. */
260b8ba871bSPeter Wemm 	size_t margin;		/* Wrapmargin value. */
261b8ba871bSPeter Wemm 	size_t rcol;		/* 0-N: insert offset in the replay buffer. */
262b8ba871bSPeter Wemm 	size_t tcol;		/* Temporary column. */
263b8ba871bSPeter Wemm 	u_int32_t ec_flags;	/* Input mapping flags. */
264b8ba871bSPeter Wemm #define	IS_RESTART	0x01	/* Reset the incremental search. */
265b8ba871bSPeter Wemm #define	IS_RUNNING	0x02	/* Incremental search turned on. */
266b8ba871bSPeter Wemm 	u_int8_t is_flags;
267b8ba871bSPeter Wemm 	int abcnt, ab_turnoff;	/* Abbreviation character count, switch. */
268b8ba871bSPeter Wemm 	int filec_redraw;	/* Redraw after the file completion routine. */
269b8ba871bSPeter Wemm 	int hexcnt;		/* Hex character count. */
270b8ba871bSPeter Wemm 	int showmatch;		/* Showmatch set on this character. */
271b8ba871bSPeter Wemm 	int wm_set, wm_skip;	/* Wrapmargin happened, blank skip flags. */
272b8ba871bSPeter Wemm 	int max, tmp;
273f0957ccaSPeter Wemm 	int nochange;
274f0957ccaSPeter Wemm 	CHAR_T *p;
275b8ba871bSPeter Wemm 
276b8ba871bSPeter Wemm 	gp = sp->gp;
277b8ba871bSPeter Wemm 	vip = VIP(sp);
278b8ba871bSPeter Wemm 
279b8ba871bSPeter Wemm 	/*
280b8ba871bSPeter Wemm 	 * Set the input flag, so tabs get displayed correctly
281b8ba871bSPeter Wemm 	 * and everyone knows that the text buffer is in use.
282b8ba871bSPeter Wemm 	 */
283b8ba871bSPeter Wemm 	F_SET(sp, SC_TINPUT);
284b8ba871bSPeter Wemm 
285b8ba871bSPeter Wemm 	/*
286b8ba871bSPeter Wemm 	 * Get one TEXT structure with some initial buffer space, reusing
287b8ba871bSPeter Wemm 	 * the last one if it's big enough.  (All TEXT bookkeeping fields
288b8ba871bSPeter Wemm 	 * default to 0 -- text_init() handles this.)  If changing a line,
289b8ba871bSPeter Wemm 	 * copy it into the TEXT buffer.
290b8ba871bSPeter Wemm 	 */
291f0957ccaSPeter Wemm 	tiqh = sp->tiq;
292f0957ccaSPeter Wemm 	if (!TAILQ_EMPTY(tiqh)) {
293f0957ccaSPeter Wemm 		tp = TAILQ_FIRST(tiqh);
2949d9df6f4SPeter Wemm 		if (TAILQ_NEXT(tp, q) != NULL ||
2959d9df6f4SPeter Wemm 		    tp->lb_len < (len + 32) * sizeof(CHAR_T)) {
296b8ba871bSPeter Wemm 			text_lfree(tiqh);
297b8ba871bSPeter Wemm 			goto newtp;
298b8ba871bSPeter Wemm 		}
299b8ba871bSPeter Wemm 		tp->ai = tp->insert = tp->offset = tp->owrite = 0;
300b8ba871bSPeter Wemm 		if (lp != NULL) {
301b8ba871bSPeter Wemm 			tp->len = len;
302f0957ccaSPeter Wemm 			BINC_RETW(sp, tp->lb, tp->lb_len, len);
303f0957ccaSPeter Wemm 			MEMMOVE(tp->lb, lp, len);
304b8ba871bSPeter Wemm 		} else
305b8ba871bSPeter Wemm 			tp->len = 0;
306b8ba871bSPeter Wemm 	} else {
307b8ba871bSPeter Wemm newtp:		if ((tp = text_init(sp, lp, len, len + 32)) == NULL)
308b8ba871bSPeter Wemm 			return (1);
309f0957ccaSPeter Wemm 		TAILQ_INSERT_HEAD(tiqh, tp, q);
310b8ba871bSPeter Wemm 	}
311b8ba871bSPeter Wemm 
312b8ba871bSPeter Wemm 	/* Set default termination condition. */
313b8ba871bSPeter Wemm 	tp->term = TERM_OK;
314b8ba871bSPeter Wemm 
315b8ba871bSPeter Wemm 	/* Set the starting line, column. */
316b8ba871bSPeter Wemm 	tp->lno = sp->lno;
317b8ba871bSPeter Wemm 	tp->cno = sp->cno;
318b8ba871bSPeter Wemm 
319b8ba871bSPeter Wemm 	/*
320b8ba871bSPeter Wemm 	 * Set the insert and overwrite counts.  If overwriting characters,
321b8ba871bSPeter Wemm 	 * do insertion afterward.  If not overwriting characters, assume
322b8ba871bSPeter Wemm 	 * doing insertion.  If change is to a mark, emphasize it with an
323b8ba871bSPeter Wemm 	 * CH_ENDMARK character.
324b8ba871bSPeter Wemm 	 */
325b8ba871bSPeter Wemm 	if (len) {
326b8ba871bSPeter Wemm 		if (LF_ISSET(TXT_OVERWRITE)) {
327b8ba871bSPeter Wemm 			tp->owrite = (tm->cno - tp->cno) + 1;
328b8ba871bSPeter Wemm 			tp->insert = (len - tm->cno) - 1;
329b8ba871bSPeter Wemm 		} else
330b8ba871bSPeter Wemm 			tp->insert = len - tp->cno;
331b8ba871bSPeter Wemm 
332b8ba871bSPeter Wemm 		if (LF_ISSET(TXT_EMARK) && txt_emark(sp, tp, tm->cno))
333b8ba871bSPeter Wemm 			return (1);
334b8ba871bSPeter Wemm 	}
335b8ba871bSPeter Wemm 
336b8ba871bSPeter Wemm 	/*
337b8ba871bSPeter Wemm 	 * Many of the special cases in text input are to handle autoindent
338b8ba871bSPeter Wemm 	 * support.  Somebody decided that it would be a good idea if "^^D"
339b8ba871bSPeter Wemm 	 * and "0^D" deleted all of the autoindented characters.  In an editor
340b8ba871bSPeter Wemm 	 * that takes single character input from the user, this beggars the
341b8ba871bSPeter Wemm 	 * imagination.  Note also, "^^D" resets the next lines' autoindent,
342b8ba871bSPeter Wemm 	 * but "0^D" doesn't.
343b8ba871bSPeter Wemm 	 *
344b8ba871bSPeter Wemm 	 * We assume that autoindent only happens on empty lines, so insert
345b8ba871bSPeter Wemm 	 * and overwrite will be zero.  If doing autoindent, figure out how
346b8ba871bSPeter Wemm 	 * much indentation we need and fill it in.  Update input column and
347b8ba871bSPeter Wemm 	 * screen cursor as necessary.
348b8ba871bSPeter Wemm 	 */
349b8ba871bSPeter Wemm 	if (LF_ISSET(TXT_AUTOINDENT) && ai_line != OOBLNO) {
350b8ba871bSPeter Wemm 		if (v_txt_auto(sp, ai_line, NULL, 0, tp))
351b8ba871bSPeter Wemm 			return (1);
352b8ba871bSPeter Wemm 		tp->cno = tp->ai;
353b8ba871bSPeter Wemm 	} else {
354b8ba871bSPeter Wemm 		/*
355b8ba871bSPeter Wemm 		 * The cc and S commands have a special feature -- leading
356b8ba871bSPeter Wemm 		 * <blank> characters are handled as autoindent characters.
357b8ba871bSPeter Wemm 		 * Beauty!
358b8ba871bSPeter Wemm 		 */
359b8ba871bSPeter Wemm 		if (LF_ISSET(TXT_AICHARS)) {
360b8ba871bSPeter Wemm 			tp->offset = 0;
361b8ba871bSPeter Wemm 			tp->ai = tp->cno;
362b8ba871bSPeter Wemm 		} else
363b8ba871bSPeter Wemm 			tp->offset = tp->cno;
364b8ba871bSPeter Wemm 	}
365b8ba871bSPeter Wemm 
366b8ba871bSPeter Wemm 	/* If getting a command buffer from the user, there may be a prompt. */
367b8ba871bSPeter Wemm 	if (LF_ISSET(TXT_PROMPT)) {
368b8ba871bSPeter Wemm 		tp->lb[tp->cno++] = prompt;
369b8ba871bSPeter Wemm 		++tp->len;
370b8ba871bSPeter Wemm 		++tp->offset;
371b8ba871bSPeter Wemm 	}
372b8ba871bSPeter Wemm 
373b8ba871bSPeter Wemm 	/*
374b8ba871bSPeter Wemm 	 * If appending after the end-of-line, add a space into the buffer
375b8ba871bSPeter Wemm 	 * and move the cursor right.  This space is inserted, i.e. pushed
376b8ba871bSPeter Wemm 	 * along, and then deleted when the line is resolved.  Assumes that
377b8ba871bSPeter Wemm 	 * the cursor is already positioned at the end of the line.  This
378b8ba871bSPeter Wemm 	 * avoids the nastiness of having the cursor reside on a magical
379b8ba871bSPeter Wemm 	 * column, i.e. a column that doesn't really exist.  The only down
380b8ba871bSPeter Wemm 	 * side is that we may wrap lines or scroll the screen before it's
381b8ba871bSPeter Wemm 	 * strictly necessary.  Not a big deal.
382b8ba871bSPeter Wemm 	 */
383b8ba871bSPeter Wemm 	if (LF_ISSET(TXT_APPENDEOL)) {
384b8ba871bSPeter Wemm 		tp->lb[tp->cno] = CH_CURSOR;
385b8ba871bSPeter Wemm 		++tp->len;
386b8ba871bSPeter Wemm 		++tp->insert;
387b8ba871bSPeter Wemm 		(void)vs_change(sp, tp->lno, LINE_RESET);
388b8ba871bSPeter Wemm 	}
389b8ba871bSPeter Wemm 
390b8ba871bSPeter Wemm 	/*
391b8ba871bSPeter Wemm 	 * Historic practice is that the wrapmargin value was a distance
392b8ba871bSPeter Wemm 	 * from the RIGHT-HAND margin, not the left.  It's more useful to
393b8ba871bSPeter Wemm 	 * us as a distance from the left-hand margin, i.e. the same as
394b8ba871bSPeter Wemm 	 * the wraplen value.  The wrapmargin option is historic practice.
395b8ba871bSPeter Wemm 	 * Nvi added the wraplen option so that it would be possible to
396b8ba871bSPeter Wemm 	 * edit files with consistent margins without knowing the number of
397b8ba871bSPeter Wemm 	 * columns in the window.
398b8ba871bSPeter Wemm 	 *
399b8ba871bSPeter Wemm 	 * XXX
400b8ba871bSPeter Wemm 	 * Setting margin causes a significant performance hit.  Normally
401b8ba871bSPeter Wemm 	 * we don't update the screen if there are keys waiting, but we
402b8ba871bSPeter Wemm 	 * have to if margin is set, otherwise the screen routines don't
403b8ba871bSPeter Wemm 	 * know where the cursor is.
404b8ba871bSPeter Wemm 	 *
405b8ba871bSPeter Wemm 	 * !!!
406b8ba871bSPeter Wemm 	 * Abbreviated keys were affected by the wrapmargin option in the
407b8ba871bSPeter Wemm 	 * historic 4BSD vi.  Mapped keys were usually, but sometimes not.
408b8ba871bSPeter Wemm 	 * See the comment in vi/v_text():set_txt_std for more information.
409b8ba871bSPeter Wemm 	 *
410b8ba871bSPeter Wemm 	 * !!!
411b8ba871bSPeter Wemm 	 * One more special case.  If an inserted <blank> character causes
412b8ba871bSPeter Wemm 	 * wrapmargin to split the line, the next user entered character is
413b8ba871bSPeter Wemm 	 * discarded if it's a <space> character.
414b8ba871bSPeter Wemm 	 */
415b8ba871bSPeter Wemm 	wm_set = wm_skip = 0;
416b8ba871bSPeter Wemm 	if (LF_ISSET(TXT_WRAPMARGIN))
417b8ba871bSPeter Wemm 		if ((margin = O_VAL(sp, O_WRAPMARGIN)) != 0)
418b8ba871bSPeter Wemm 			margin = sp->cols - margin;
419b8ba871bSPeter Wemm 		else
420b8ba871bSPeter Wemm 			margin = O_VAL(sp, O_WRAPLEN);
421b8ba871bSPeter Wemm 	else
422b8ba871bSPeter Wemm 		margin = 0;
423b8ba871bSPeter Wemm 
424b8ba871bSPeter Wemm 	/* Initialize abbreviation checks. */
425b8ba871bSPeter Wemm 	abcnt = ab_turnoff = 0;
426b8ba871bSPeter Wemm 	abb = F_ISSET(gp, G_ABBREV) &&
427b8ba871bSPeter Wemm 	    LF_ISSET(TXT_MAPINPUT) ? AB_INWORD : AB_NOTSET;
428b8ba871bSPeter Wemm 
429b8ba871bSPeter Wemm 	/*
430b8ba871bSPeter Wemm 	 * Set up the dot command.  Dot commands are done by saving the actual
431b8ba871bSPeter Wemm 	 * characters and then reevaluating them so that things like wrapmargin
432b8ba871bSPeter Wemm 	 * can change between the insert and the replay.
433b8ba871bSPeter Wemm 	 *
434b8ba871bSPeter Wemm 	 * !!!
435b8ba871bSPeter Wemm 	 * Historically, vi did not remap or reabbreviate replayed input.  (It
436b8ba871bSPeter Wemm 	 * did beep at you if you changed an abbreviation and then replayed the
437b8ba871bSPeter Wemm 	 * input.  We're not that compatible.)  We don't have to do anything to
438b8ba871bSPeter Wemm 	 * avoid remapping, as we're not getting characters from the terminal
439b8ba871bSPeter Wemm 	 * routines.  Turn the abbreviation check off.
440b8ba871bSPeter Wemm 	 *
441b8ba871bSPeter Wemm 	 * XXX
442b8ba871bSPeter Wemm 	 * It would be nice if we could swallow backspaces and such, but it's
443b8ba871bSPeter Wemm 	 * not all that easy to do.  What we can do is turn off the common
444b8ba871bSPeter Wemm 	 * error messages during the replay.  Otherwise, when the user enters
445b8ba871bSPeter Wemm 	 * an illegal command, e.g., "Ia<erase><erase><erase><erase>b<escape>",
446b8ba871bSPeter Wemm 	 * and then does a '.', they get a list of error messages after command
447b8ba871bSPeter Wemm 	 * completion.
448b8ba871bSPeter Wemm 	 */
449b8ba871bSPeter Wemm 	rcol = 0;
450b8ba871bSPeter Wemm 	if (LF_ISSET(TXT_REPLAY)) {
451b8ba871bSPeter Wemm 		abb = AB_NOTSET;
452b8ba871bSPeter Wemm 		LF_CLR(TXT_RECORD);
453b8ba871bSPeter Wemm 	}
454b8ba871bSPeter Wemm 
455b8ba871bSPeter Wemm 	/* Other text input mode setup. */
456b8ba871bSPeter Wemm 	quote = Q_NOTSET;
457b8ba871bSPeter Wemm 	carat = C_NOTSET;
458f0957ccaSPeter Wemm 	nochange = 0;
459b8ba871bSPeter Wemm 	FL_INIT(is_flags,
460b8ba871bSPeter Wemm 	    LF_ISSET(TXT_SEARCHINCR) ? IS_RESTART | IS_RUNNING : 0);
461b8ba871bSPeter Wemm 	filec_redraw = hexcnt = showmatch = 0;
462b8ba871bSPeter Wemm 
463b8ba871bSPeter Wemm 	/* Initialize input flags. */
464b8ba871bSPeter Wemm 	ec_flags = LF_ISSET(TXT_MAPINPUT) ? EC_MAPINPUT : 0;
465b8ba871bSPeter Wemm 
466b8ba871bSPeter Wemm 	/* Refresh the screen. */
467b8ba871bSPeter Wemm 	UPDATE_POSITION(sp, tp);
468b8ba871bSPeter Wemm 	if (vs_refresh(sp, 1))
469b8ba871bSPeter Wemm 		return (1);
470b8ba871bSPeter Wemm 
471b8ba871bSPeter Wemm 	/* If it's dot, just do it now. */
472b8ba871bSPeter Wemm 	if (F_ISSET(vp, VC_ISDOT))
473b8ba871bSPeter Wemm 		goto replay;
474b8ba871bSPeter Wemm 
475b8ba871bSPeter Wemm 	/* Get an event. */
476b8ba871bSPeter Wemm 	evp = &ev;
477b8ba871bSPeter Wemm next:	if (v_event_get(sp, evp, 0, ec_flags))
478b8ba871bSPeter Wemm 		return (1);
479b8ba871bSPeter Wemm 
480b8ba871bSPeter Wemm 	/*
481b8ba871bSPeter Wemm 	 * If file completion overwrote part of the screen and nothing else has
482b8ba871bSPeter Wemm 	 * been displayed, clean up.  We don't do this as part of the normal
483b8ba871bSPeter Wemm 	 * message resolution because we know the user is on the colon command
484b8ba871bSPeter Wemm 	 * line and there's no reason to enter explicit characters to continue.
485b8ba871bSPeter Wemm 	 */
486b8ba871bSPeter Wemm 	if (filec_redraw && !F_ISSET(sp, SC_SCR_EXWROTE)) {
487b8ba871bSPeter Wemm 		filec_redraw = 0;
488b8ba871bSPeter Wemm 
489b8ba871bSPeter Wemm 		fc.e_event = E_REPAINT;
490b8ba871bSPeter Wemm 		fc.e_flno = vip->totalcount >=
491b8ba871bSPeter Wemm 		    sp->rows ? 1 : sp->rows - vip->totalcount;
492b8ba871bSPeter Wemm 		fc.e_tlno = sp->rows;
493b8ba871bSPeter Wemm 		vip->linecount = vip->lcontinue = vip->totalcount = 0;
494b8ba871bSPeter Wemm 		(void)vs_repaint(sp, &fc);
495b8ba871bSPeter Wemm 		(void)vs_refresh(sp, 1);
496b8ba871bSPeter Wemm 	}
497b8ba871bSPeter Wemm 
498b8ba871bSPeter Wemm 	/* Deal with all non-character events. */
499b8ba871bSPeter Wemm 	switch (evp->e_event) {
500b8ba871bSPeter Wemm 	case E_CHARACTER:
501b8ba871bSPeter Wemm 		break;
502b8ba871bSPeter Wemm 	case E_ERR:
503b8ba871bSPeter Wemm 	case E_EOF:
504b8ba871bSPeter Wemm 		F_SET(sp, SC_EXIT_FORCE);
505b8ba871bSPeter Wemm 		return (1);
506f0957ccaSPeter Wemm 	case E_INTERRUPT:
507725f5cdbSJaakko Heinonen 		/*
508725f5cdbSJaakko Heinonen 		 * !!!
509725f5cdbSJaakko Heinonen 		 * Historically, <interrupt> exited the user from text input
510725f5cdbSJaakko Heinonen 		 * mode or cancelled a colon command, and returned to command
511725f5cdbSJaakko Heinonen 		 * mode.  It also beeped the terminal, but that seems a bit
512725f5cdbSJaakko Heinonen 		 * excessive.
513725f5cdbSJaakko Heinonen 		 */
514b8ba871bSPeter Wemm 		goto k_escape;
515f0957ccaSPeter Wemm 	case E_REPAINT:
516f0957ccaSPeter Wemm 		if (vs_repaint(sp, &ev))
517f0957ccaSPeter Wemm 			return (1);
518f0957ccaSPeter Wemm 		goto next;
519f0957ccaSPeter Wemm 	case E_WRESIZE:
520f0957ccaSPeter Wemm 		/* <resize> interrupts the input mode. */
521f0957ccaSPeter Wemm 		v_emsg(sp, NULL, VIM_WRESIZE);
522f0957ccaSPeter Wemm 		goto k_escape;
523f0957ccaSPeter Wemm 	default:
524f0957ccaSPeter Wemm 		v_event_err(sp, evp);
525f0957ccaSPeter Wemm 		goto k_escape;
526725f5cdbSJaakko Heinonen 	}
527b8ba871bSPeter Wemm 
528b8ba871bSPeter Wemm 	/*
529b8ba871bSPeter Wemm 	 * !!!
530b8ba871bSPeter Wemm 	 * If the first character of the input is a nul, replay the previous
531b8ba871bSPeter Wemm 	 * input.  (Historically, it's okay to replay non-existent input.)
532b8ba871bSPeter Wemm 	 * This was not documented as far as I know, and is a great test of vi
533b8ba871bSPeter Wemm 	 * clones.
534b8ba871bSPeter Wemm 	 */
535725f5cdbSJaakko Heinonen 	if (LF_ISSET(TXT_RECORD) && rcol == 0 && evp->e_c == '\0') {
536b8ba871bSPeter Wemm 		if (vip->rep == NULL)
537b8ba871bSPeter Wemm 			goto done;
538b8ba871bSPeter Wemm 
539b8ba871bSPeter Wemm 		abb = AB_NOTSET;
540b8ba871bSPeter Wemm 		LF_CLR(TXT_RECORD);
541b8ba871bSPeter Wemm 		LF_SET(TXT_REPLAY);
542b8ba871bSPeter Wemm 		goto replay;
543b8ba871bSPeter Wemm 	}
544b8ba871bSPeter Wemm 
545b8ba871bSPeter Wemm 	/*
546b8ba871bSPeter Wemm 	 * File name completion and colon command-line editing.   We don't
547b8ba871bSPeter Wemm 	 * have enough meta characters, so we expect people to overload
548b8ba871bSPeter Wemm 	 * them.  If the two characters are the same, then we do file name
549b8ba871bSPeter Wemm 	 * completion if the cursor is past the first column, and do colon
550b8ba871bSPeter Wemm 	 * command-line editing if it's not.
551b8ba871bSPeter Wemm 	 */
552b8ba871bSPeter Wemm 	if (quote == Q_NOTSET) {
553b8ba871bSPeter Wemm 		int L__cedit, L__filec;
554b8ba871bSPeter Wemm 
555b8ba871bSPeter Wemm 		L__cedit = L__filec = 0;
556b8ba871bSPeter Wemm 		if (LF_ISSET(TXT_CEDIT) && O_STR(sp, O_CEDIT) != NULL &&
557b8ba871bSPeter Wemm 		    O_STR(sp, O_CEDIT)[0] == evp->e_c)
558b8ba871bSPeter Wemm 			L__cedit = 1;
559b8ba871bSPeter Wemm 		if (LF_ISSET(TXT_FILEC) && O_STR(sp, O_FILEC) != NULL &&
560b8ba871bSPeter Wemm 		    O_STR(sp, O_FILEC)[0] == evp->e_c)
561b8ba871bSPeter Wemm 			L__filec = 1;
562b8ba871bSPeter Wemm 		if (L__cedit == 1 && (L__filec == 0 || tp->cno == tp->offset)) {
563b8ba871bSPeter Wemm 			tp->term = TERM_CEDIT;
564b8ba871bSPeter Wemm 			goto k_escape;
565b8ba871bSPeter Wemm 		}
566b8ba871bSPeter Wemm 		if (L__filec == 1) {
567b8ba871bSPeter Wemm 			if (txt_fc(sp, tp, &filec_redraw))
568b8ba871bSPeter Wemm 				goto err;
569b8ba871bSPeter Wemm 			goto resolve;
570b8ba871bSPeter Wemm 		}
571b8ba871bSPeter Wemm 	}
572b8ba871bSPeter Wemm 
573b8ba871bSPeter Wemm 	/* Abbreviation overflow check.  See comment in txt_abbrev(). */
574b8ba871bSPeter Wemm #define	MAX_ABBREVIATION_EXPANSION	256
575b8ba871bSPeter Wemm 	if (F_ISSET(&evp->e_ch, CH_ABBREVIATED)) {
576b8ba871bSPeter Wemm 		if (++abcnt > MAX_ABBREVIATION_EXPANSION) {
577b8ba871bSPeter Wemm 			if (v_event_flush(sp, CH_ABBREVIATED))
578b8ba871bSPeter Wemm 				msgq(sp, M_ERR,
579b8ba871bSPeter Wemm "191|Abbreviation exceeded expansion limit: characters discarded");
580b8ba871bSPeter Wemm 			abcnt = 0;
581b8ba871bSPeter Wemm 			if (LF_ISSET(TXT_REPLAY))
582b8ba871bSPeter Wemm 				goto done;
583b8ba871bSPeter Wemm 			goto resolve;
584b8ba871bSPeter Wemm 		}
585b8ba871bSPeter Wemm 	} else
586b8ba871bSPeter Wemm 		abcnt = 0;
587b8ba871bSPeter Wemm 
588b8ba871bSPeter Wemm 	/* Check to see if the character fits into the replay buffers. */
589b8ba871bSPeter Wemm 	if (LF_ISSET(TXT_RECORD)) {
590f0957ccaSPeter Wemm 		BINC_GOTO(sp, EVENT, vip->rep,
591b8ba871bSPeter Wemm 		    vip->rep_len, (rcol + 1) * sizeof(EVENT));
592b8ba871bSPeter Wemm 		vip->rep[rcol++] = *evp;
593b8ba871bSPeter Wemm 	}
594b8ba871bSPeter Wemm 
595f0957ccaSPeter Wemm replay:	if (LF_ISSET(TXT_REPLAY)) {
596f0957ccaSPeter Wemm 		if (rcol == vip->rep_cnt)
597f0957ccaSPeter Wemm 			goto k_escape;
598b8ba871bSPeter Wemm 		evp = vip->rep + rcol++;
599f0957ccaSPeter Wemm 	}
600b8ba871bSPeter Wemm 
601b8ba871bSPeter Wemm 	/* Wrapmargin check for leading space. */
602b8ba871bSPeter Wemm 	if (wm_skip) {
603b8ba871bSPeter Wemm 		wm_skip = 0;
604b8ba871bSPeter Wemm 		if (evp->e_c == ' ')
605b8ba871bSPeter Wemm 			goto resolve;
606b8ba871bSPeter Wemm 	}
607b8ba871bSPeter Wemm 
608b8ba871bSPeter Wemm 	/* If quoted by someone else, simply insert the character. */
609b8ba871bSPeter Wemm 	if (F_ISSET(&evp->e_ch, CH_QUOTED))
610b8ba871bSPeter Wemm 		goto insq_ch;
611b8ba871bSPeter Wemm 
612b8ba871bSPeter Wemm 	/*
613b8ba871bSPeter Wemm 	 * !!!
614110d525eSBaptiste Daroussin 	 * If this character was quoted by a K_VLNEXT, replace the placeholder
615110d525eSBaptiste Daroussin 	 * (a carat) with the new character.  We've already adjusted the cursor
616110d525eSBaptiste Daroussin 	 * because it has to appear on top of the placeholder character.
617110d525eSBaptiste Daroussin 	 * Historic practice.
618b8ba871bSPeter Wemm 	 *
619b8ba871bSPeter Wemm 	 * Skip tests for abbreviations; ":ab xa XA" followed by "ixa^V<space>"
620b8ba871bSPeter Wemm 	 * doesn't perform an abbreviation.  Special case, ^V^J (not ^V^M) is
621b8ba871bSPeter Wemm 	 * the same as ^J, historically.
622b8ba871bSPeter Wemm 	 */
623110d525eSBaptiste Daroussin 	if (quote == Q_VTHIS) {
624b8ba871bSPeter Wemm 		FL_CLR(ec_flags, EC_QUOTED);
625b8ba871bSPeter Wemm 		if (LF_ISSET(TXT_MAPINPUT))
626b8ba871bSPeter Wemm 			FL_SET(ec_flags, EC_MAPINPUT);
627b8ba871bSPeter Wemm 
628110d525eSBaptiste Daroussin 		if (evp->e_value != K_NL) {
629b8ba871bSPeter Wemm 			quote = Q_NOTSET;
630b8ba871bSPeter Wemm 			goto insl_ch;
631b8ba871bSPeter Wemm 		}
632b8ba871bSPeter Wemm 		quote = Q_NOTSET;
633b8ba871bSPeter Wemm 	}
634b8ba871bSPeter Wemm 
635b8ba871bSPeter Wemm 	/*
636b8ba871bSPeter Wemm 	 * !!!
637b8ba871bSPeter Wemm 	 * Translate "<CH_HEX>[isxdigit()]*" to a character with a hex value:
638b8ba871bSPeter Wemm 	 * this test delimits the value by any non-hex character.  Offset by
639b8ba871bSPeter Wemm 	 * one, we use 0 to mean that we've found <CH_HEX>.
640b8ba871bSPeter Wemm 	 */
641f0957ccaSPeter Wemm 	if (hexcnt > 1 && !ISXDIGIT(evp->e_c)) {
642b8ba871bSPeter Wemm 		hexcnt = 0;
643b8ba871bSPeter Wemm 		if (txt_hex(sp, tp))
644b8ba871bSPeter Wemm 			goto err;
645b8ba871bSPeter Wemm 	}
646b8ba871bSPeter Wemm 
647b8ba871bSPeter Wemm 	switch (evp->e_value) {
648b8ba871bSPeter Wemm 	case K_CR:				/* Carriage return. */
649b8ba871bSPeter Wemm 	case K_NL:				/* New line. */
650b8ba871bSPeter Wemm 		/* Return in script windows and the command line. */
651b8ba871bSPeter Wemm k_cr:		if (LF_ISSET(TXT_CR)) {
652b8ba871bSPeter Wemm 			/*
653b8ba871bSPeter Wemm 			 * If this was a map, we may have not displayed
654b8ba871bSPeter Wemm 			 * the line.  Display it, just in case.
655b8ba871bSPeter Wemm 			 *
656b8ba871bSPeter Wemm 			 * If a script window and not the colon line,
657b8ba871bSPeter Wemm 			 * push a <cr> so it gets executed.
658b8ba871bSPeter Wemm 			 */
659b8ba871bSPeter Wemm 			if (LF_ISSET(TXT_INFOLINE)) {
660b8ba871bSPeter Wemm 				if (vs_change(sp, tp->lno, LINE_RESET))
661b8ba871bSPeter Wemm 					goto err;
662b8ba871bSPeter Wemm 			} else if (F_ISSET(sp, SC_SCRIPT))
663f0957ccaSPeter Wemm 				(void)v_event_push(sp, NULL, L("\r"), 1, CH_NOMAP);
664b8ba871bSPeter Wemm 
665b8ba871bSPeter Wemm 			/* Set term condition: if empty. */
666b8ba871bSPeter Wemm 			if (tp->cno <= tp->offset)
667b8ba871bSPeter Wemm 				tp->term = TERM_CR;
668b8ba871bSPeter Wemm 			/*
669b8ba871bSPeter Wemm 			 * Set term condition: if searching incrementally and
670b8ba871bSPeter Wemm 			 * the user entered a pattern, return a completed
671b8ba871bSPeter Wemm 			 * search, regardless if the entire pattern was found.
672b8ba871bSPeter Wemm 			 */
673b8ba871bSPeter Wemm 			if (FL_ISSET(is_flags, IS_RUNNING) &&
674b8ba871bSPeter Wemm 			    tp->cno >= tp->offset + 1)
675b8ba871bSPeter Wemm 				tp->term = TERM_SEARCH;
676b8ba871bSPeter Wemm 
677b8ba871bSPeter Wemm 			goto k_escape;
678b8ba871bSPeter Wemm 		}
679b8ba871bSPeter Wemm 
680*755cc40cSBaptiste Daroussin #define	LINE_RESOLVE do {						\
681b8ba871bSPeter Wemm 		/*							\
682b8ba871bSPeter Wemm 		 * Handle abbreviations.  If there was one, discard the	\
683b8ba871bSPeter Wemm 		 * replay characters.					\
684b8ba871bSPeter Wemm 		 */							\
685b8ba871bSPeter Wemm 		if (abb == AB_INWORD &&					\
686b8ba871bSPeter Wemm 		    !LF_ISSET(TXT_REPLAY) && F_ISSET(gp, G_ABBREV)) {	\
687b8ba871bSPeter Wemm 			if (txt_abbrev(sp, tp, &evp->e_c,		\
688b8ba871bSPeter Wemm 			    LF_ISSET(TXT_INFOLINE), &tmp,		\
689b8ba871bSPeter Wemm 			    &ab_turnoff))				\
690b8ba871bSPeter Wemm 				goto err;				\
691b8ba871bSPeter Wemm 			if (tmp) {					\
692b8ba871bSPeter Wemm 				if (LF_ISSET(TXT_RECORD))		\
693b8ba871bSPeter Wemm 					rcol -= tmp + 1;		\
694b8ba871bSPeter Wemm 				goto resolve;				\
695b8ba871bSPeter Wemm 			}						\
696b8ba871bSPeter Wemm 		}							\
697b8ba871bSPeter Wemm 		if (abb != AB_NOTSET)					\
698b8ba871bSPeter Wemm 			abb = AB_NOTWORD;				\
699b8ba871bSPeter Wemm 		if (UNMAP_TST)						\
700b8ba871bSPeter Wemm 			txt_unmap(sp, tp, &ec_flags);			\
701b8ba871bSPeter Wemm 		/*							\
702b8ba871bSPeter Wemm 		 * Delete any appended cursor.  It's possible to get in	\
703b8ba871bSPeter Wemm 		 * situations where TXT_APPENDEOL is set but tp->insert	\
704b8ba871bSPeter Wemm 		 * is 0 when using the R command and all the characters	\
705b8ba871bSPeter Wemm 		 * are tp->owrite characters.				\
706b8ba871bSPeter Wemm 		 */							\
707b8ba871bSPeter Wemm 		if (LF_ISSET(TXT_APPENDEOL) && tp->insert > 0) {	\
708b8ba871bSPeter Wemm 			--tp->len;					\
709b8ba871bSPeter Wemm 			--tp->insert;					\
710b8ba871bSPeter Wemm 		}							\
711*755cc40cSBaptiste Daroussin } while (0)
712b8ba871bSPeter Wemm 		LINE_RESOLVE;
713b8ba871bSPeter Wemm 
714b8ba871bSPeter Wemm 		/*
715b8ba871bSPeter Wemm 		 * Save the current line information for restoration in
716b8ba871bSPeter Wemm 		 * txt_backup(), and set the line final length.
717b8ba871bSPeter Wemm 		 */
718b8ba871bSPeter Wemm 		tp->sv_len = tp->len;
719b8ba871bSPeter Wemm 		tp->sv_cno = tp->cno;
720b8ba871bSPeter Wemm 		tp->len = tp->cno;
721b8ba871bSPeter Wemm 
722b8ba871bSPeter Wemm 		/* Update the old line. */
723b8ba871bSPeter Wemm 		if (vs_change(sp, tp->lno, LINE_RESET))
724b8ba871bSPeter Wemm 			goto err;
725b8ba871bSPeter Wemm 
726b8ba871bSPeter Wemm 		/*
727b8ba871bSPeter Wemm 		 * Historic practice, when the autoindent edit option was set,
728b8ba871bSPeter Wemm 		 * was to delete <blank> characters following the inserted
729b8ba871bSPeter Wemm 		 * newline.  This affected the 'R', 'c', and 's' commands; 'c'
730b8ba871bSPeter Wemm 		 * and 's' retained the insert characters only, 'R' moved the
731b8ba871bSPeter Wemm 		 * overwrite and insert characters into the next TEXT structure.
732b8ba871bSPeter Wemm 		 * We keep track of the number of characters erased for the 'R'
733b8ba871bSPeter Wemm 		 * command so that the final resolution of the line is correct.
734b8ba871bSPeter Wemm 		 */
735b8ba871bSPeter Wemm 		tp->R_erase = 0;
736b8ba871bSPeter Wemm 		owrite = tp->owrite;
737b8ba871bSPeter Wemm 		insert = tp->insert;
738b8ba871bSPeter Wemm 		if (LF_ISSET(TXT_REPLACE) && owrite != 0) {
739b8ba871bSPeter Wemm 			for (p = tp->lb + tp->cno; owrite > 0 && isblank(*p);
740b8ba871bSPeter Wemm 			    ++p, --owrite, ++tp->R_erase);
741b8ba871bSPeter Wemm 			if (owrite == 0)
742b8ba871bSPeter Wemm 				for (; insert > 0 && isblank(*p);
743b8ba871bSPeter Wemm 				    ++p, ++tp->R_erase, --insert);
744b8ba871bSPeter Wemm 		} else {
745b8ba871bSPeter Wemm 			p = tp->lb + tp->cno + owrite;
746b8ba871bSPeter Wemm 			if (O_ISSET(sp, O_AUTOINDENT))
747b8ba871bSPeter Wemm 				for (; insert > 0 &&
748b8ba871bSPeter Wemm 				    isblank(*p); ++p, --insert);
749b8ba871bSPeter Wemm 			owrite = 0;
750b8ba871bSPeter Wemm 		}
751b8ba871bSPeter Wemm 
752b8ba871bSPeter Wemm 		/*
753b8ba871bSPeter Wemm 		 * !!!
754b8ba871bSPeter Wemm 		 * Create a new line and insert the new TEXT into the queue.
755b8ba871bSPeter Wemm 		 * DON'T insert until the old line has been updated, or the
756b8ba871bSPeter Wemm 		 * inserted line count in line.c:db_get() will be wrong.
757b8ba871bSPeter Wemm 		 */
758b8ba871bSPeter Wemm 		if ((ntp = text_init(sp, p,
759b8ba871bSPeter Wemm 		    insert + owrite, insert + owrite + 32)) == NULL)
760b8ba871bSPeter Wemm 			goto err;
761f0957ccaSPeter Wemm 		TAILQ_INSERT_TAIL(sp->tiq, ntp, q);
762b8ba871bSPeter Wemm 
763b8ba871bSPeter Wemm 		/* Set up bookkeeping for the new line. */
764b8ba871bSPeter Wemm 		ntp->insert = insert;
765b8ba871bSPeter Wemm 		ntp->owrite = owrite;
766b8ba871bSPeter Wemm 		ntp->lno = tp->lno + 1;
767b8ba871bSPeter Wemm 
768b8ba871bSPeter Wemm 		/*
769b8ba871bSPeter Wemm 		 * Reset the autoindent line value.  0^D keeps the autoindent
770b8ba871bSPeter Wemm 		 * line from changing, ^D changes the level, even if there were
771b8ba871bSPeter Wemm 		 * no characters in the old line.  Note, if using the current
772b8ba871bSPeter Wemm 		 * tp structure, use the cursor as the length, the autoindent
773b8ba871bSPeter Wemm 		 * characters may have been erased.
774b8ba871bSPeter Wemm 		 */
775b8ba871bSPeter Wemm 		if (LF_ISSET(TXT_AUTOINDENT)) {
776f0957ccaSPeter Wemm 			if (nochange) {
777f0957ccaSPeter Wemm 				nochange = 0;
778b8ba871bSPeter Wemm 				if (v_txt_auto(sp, OOBLNO, &ait, ait.ai, ntp))
779b8ba871bSPeter Wemm 					goto err;
780f0957ccaSPeter Wemm 				FREE_SPACEW(sp, ait.lb, ait.lb_len);
781b8ba871bSPeter Wemm 			} else
782b8ba871bSPeter Wemm 				if (v_txt_auto(sp, OOBLNO, tp, tp->cno, ntp))
783b8ba871bSPeter Wemm 					goto err;
784b8ba871bSPeter Wemm 			carat = C_NOTSET;
785b8ba871bSPeter Wemm 		}
786b8ba871bSPeter Wemm 
787b8ba871bSPeter Wemm 		/* Reset the cursor. */
788b8ba871bSPeter Wemm 		ntp->cno = ntp->ai;
789b8ba871bSPeter Wemm 
790b8ba871bSPeter Wemm 		/*
791b8ba871bSPeter Wemm 		 * If we're here because wrapmargin was set and we've broken a
792b8ba871bSPeter Wemm 		 * line, there may be additional information (i.e. the start of
793b8ba871bSPeter Wemm 		 * a line) in the wmt structure.
794b8ba871bSPeter Wemm 		 */
795b8ba871bSPeter Wemm 		if (wm_set) {
796b8ba871bSPeter Wemm 			if (wmt.offset != 0 ||
797b8ba871bSPeter Wemm 			    wmt.owrite != 0 || wmt.insert != 0) {
798b8ba871bSPeter Wemm #define	WMTSPACE	wmt.offset + wmt.owrite + wmt.insert
799f0957ccaSPeter Wemm 				BINC_GOTOW(sp, ntp->lb,
800b8ba871bSPeter Wemm 				    ntp->lb_len, ntp->len + WMTSPACE + 32);
801f0957ccaSPeter Wemm 				MEMMOVE(ntp->lb + ntp->cno, wmt.lb, WMTSPACE);
802b8ba871bSPeter Wemm 				ntp->len += WMTSPACE;
803b8ba871bSPeter Wemm 				ntp->cno += wmt.offset;
804b8ba871bSPeter Wemm 				ntp->owrite = wmt.owrite;
805b8ba871bSPeter Wemm 				ntp->insert = wmt.insert;
806b8ba871bSPeter Wemm 			}
807b8ba871bSPeter Wemm 			wm_set = 0;
808b8ba871bSPeter Wemm 		}
809b8ba871bSPeter Wemm 
810b8ba871bSPeter Wemm 		/* New lines are TXT_APPENDEOL. */
811b8ba871bSPeter Wemm 		if (ntp->owrite == 0 && ntp->insert == 0) {
812f0957ccaSPeter Wemm 			BINC_GOTOW(sp, ntp->lb, ntp->lb_len, ntp->len + 1);
813b8ba871bSPeter Wemm 			LF_SET(TXT_APPENDEOL);
814b8ba871bSPeter Wemm 			ntp->lb[ntp->cno] = CH_CURSOR;
815b8ba871bSPeter Wemm 			++ntp->insert;
816b8ba871bSPeter Wemm 			++ntp->len;
817b8ba871bSPeter Wemm 		}
818b8ba871bSPeter Wemm 
819b8ba871bSPeter Wemm 		/* Swap old and new TEXT's, and update the new line. */
820b8ba871bSPeter Wemm 		tp = ntp;
821b8ba871bSPeter Wemm 		if (vs_change(sp, tp->lno, LINE_INSERT))
822b8ba871bSPeter Wemm 			goto err;
823b8ba871bSPeter Wemm 
824b8ba871bSPeter Wemm 		goto resolve;
825b8ba871bSPeter Wemm 	case K_ESCAPE:				/* Escape. */
826b8ba871bSPeter Wemm 		if (!LF_ISSET(TXT_ESCAPE))
827b8ba871bSPeter Wemm 			goto ins_ch;
828b8ba871bSPeter Wemm 
829b8ba871bSPeter Wemm 		/* If we have a count, start replaying the input. */
830b8ba871bSPeter Wemm 		if (rcount > 1) {
831b8ba871bSPeter Wemm 			--rcount;
832b8ba871bSPeter Wemm 
833f0957ccaSPeter Wemm 			vip->rep_cnt = rcol;
834b8ba871bSPeter Wemm 			rcol = 0;
835b8ba871bSPeter Wemm 			abb = AB_NOTSET;
836b8ba871bSPeter Wemm 			LF_CLR(TXT_RECORD);
837b8ba871bSPeter Wemm 			LF_SET(TXT_REPLAY);
838b8ba871bSPeter Wemm 
839b8ba871bSPeter Wemm 			/*
840b8ba871bSPeter Wemm 			 * Some commands (e.g. 'o') need a <newline> for each
841b8ba871bSPeter Wemm 			 * repetition.
842b8ba871bSPeter Wemm 			 */
843b8ba871bSPeter Wemm 			if (LF_ISSET(TXT_ADDNEWLINE))
844b8ba871bSPeter Wemm 				goto k_cr;
845b8ba871bSPeter Wemm 
846b8ba871bSPeter Wemm 			/*
847b8ba871bSPeter Wemm 			 * The R command turns into the 'a' command after the
848b8ba871bSPeter Wemm 			 * first repetition.
849b8ba871bSPeter Wemm 			 */
850b8ba871bSPeter Wemm 			if (LF_ISSET(TXT_REPLACE)) {
851b8ba871bSPeter Wemm 				tp->insert = tp->owrite;
852b8ba871bSPeter Wemm 				tp->owrite = 0;
853b8ba871bSPeter Wemm 				LF_CLR(TXT_REPLACE);
854b8ba871bSPeter Wemm 			}
855b8ba871bSPeter Wemm 			goto replay;
856b8ba871bSPeter Wemm 		}
857b8ba871bSPeter Wemm 
858b8ba871bSPeter Wemm 		/* Set term condition: if empty. */
859b8ba871bSPeter Wemm 		if (tp->cno <= tp->offset)
860b8ba871bSPeter Wemm 			tp->term = TERM_ESC;
861b8ba871bSPeter Wemm 		/*
862b8ba871bSPeter Wemm 		 * Set term condition: if searching incrementally and the user
863b8ba871bSPeter Wemm 		 * entered a pattern, return a completed search, regardless if
864b8ba871bSPeter Wemm 		 * the entire pattern was found.
865b8ba871bSPeter Wemm 		 */
866b8ba871bSPeter Wemm 		if (FL_ISSET(is_flags, IS_RUNNING) && tp->cno >= tp->offset + 1)
867b8ba871bSPeter Wemm 			tp->term = TERM_SEARCH;
868b8ba871bSPeter Wemm 
869b8ba871bSPeter Wemm k_escape:	LINE_RESOLVE;
870b8ba871bSPeter Wemm 
871b8ba871bSPeter Wemm 		/*
872b8ba871bSPeter Wemm 		 * Clean up for the 'R' command, restoring overwrite
873b8ba871bSPeter Wemm 		 * characters, and making them into insert characters.
874b8ba871bSPeter Wemm 		 */
875b8ba871bSPeter Wemm 		if (LF_ISSET(TXT_REPLACE))
876f0957ccaSPeter Wemm 			txt_Rresolve(sp, sp->tiq, tp, len);
877b8ba871bSPeter Wemm 
878b8ba871bSPeter Wemm 		/*
879b8ba871bSPeter Wemm 		 * If there are any overwrite characters, copy down
880b8ba871bSPeter Wemm 		 * any insert characters, and decrement the length.
881b8ba871bSPeter Wemm 		 */
882b8ba871bSPeter Wemm 		if (tp->owrite) {
883b8ba871bSPeter Wemm 			if (tp->insert)
884f0957ccaSPeter Wemm 				MEMMOVE(tp->lb + tp->cno,
885b8ba871bSPeter Wemm 				    tp->lb + tp->cno + tp->owrite, tp->insert);
886b8ba871bSPeter Wemm 			tp->len -= tp->owrite;
887b8ba871bSPeter Wemm 		}
888b8ba871bSPeter Wemm 
889b8ba871bSPeter Wemm 		/*
890b8ba871bSPeter Wemm 		 * Optionally resolve the lines into the file.  If not
891b8ba871bSPeter Wemm 		 * resolving the lines into the file, end the line with
892b8ba871bSPeter Wemm 		 * a nul.  If the line is empty, then set the length to
893b8ba871bSPeter Wemm 		 * 0, the termination condition has already been set.
894b8ba871bSPeter Wemm 		 *
895b8ba871bSPeter Wemm 		 * XXX
896b8ba871bSPeter Wemm 		 * This is wrong, should pass back a length.
897b8ba871bSPeter Wemm 		 */
898b8ba871bSPeter Wemm 		if (LF_ISSET(TXT_RESOLVE)) {
899f0957ccaSPeter Wemm 			if (txt_resolve(sp, sp->tiq, flags))
900b8ba871bSPeter Wemm 				goto err;
901b8ba871bSPeter Wemm 		} else {
902f0957ccaSPeter Wemm 			BINC_GOTOW(sp, tp->lb, tp->lb_len, tp->len + 1);
903b8ba871bSPeter Wemm 			tp->lb[tp->len] = '\0';
904b8ba871bSPeter Wemm 		}
905b8ba871bSPeter Wemm 
906b8ba871bSPeter Wemm 		/*
907b8ba871bSPeter Wemm 		 * Set the return cursor position to rest on the last
908b8ba871bSPeter Wemm 		 * inserted character.
909b8ba871bSPeter Wemm 		 */
910b8ba871bSPeter Wemm 		if (tp->cno != 0)
911b8ba871bSPeter Wemm 			--tp->cno;
912b8ba871bSPeter Wemm 
913b8ba871bSPeter Wemm 		/* Update the last line. */
914b8ba871bSPeter Wemm 		if (vs_change(sp, tp->lno, LINE_RESET))
915b8ba871bSPeter Wemm 			return (1);
916b8ba871bSPeter Wemm 		goto done;
917b8ba871bSPeter Wemm 	case K_CARAT:			/* Delete autoindent chars. */
918b8ba871bSPeter Wemm 		if (tp->cno <= tp->ai && LF_ISSET(TXT_AUTOINDENT))
919b8ba871bSPeter Wemm 			carat = C_CARATSET;
920b8ba871bSPeter Wemm 		goto ins_ch;
921b8ba871bSPeter Wemm 	case K_ZERO:			/* Delete autoindent chars. */
922b8ba871bSPeter Wemm 		if (tp->cno <= tp->ai && LF_ISSET(TXT_AUTOINDENT))
923b8ba871bSPeter Wemm 			carat = C_ZEROSET;
924b8ba871bSPeter Wemm 		goto ins_ch;
925b8ba871bSPeter Wemm 	case K_CNTRLD:			/* Delete autoindent char. */
926b8ba871bSPeter Wemm 		/*
927b8ba871bSPeter Wemm 		 * If in the first column or no characters to erase, ignore
928b8ba871bSPeter Wemm 		 * the ^D (this matches historic practice).  If not doing
929b8ba871bSPeter Wemm 		 * autoindent or already inserted non-ai characters, it's a
930b8ba871bSPeter Wemm 		 * literal.  The latter test is done in the switch, as the
931b8ba871bSPeter Wemm 		 * CARAT forms are N + 1, not N.
932b8ba871bSPeter Wemm 		 */
933b8ba871bSPeter Wemm 		if (!LF_ISSET(TXT_AUTOINDENT))
934b8ba871bSPeter Wemm 			goto ins_ch;
935b8ba871bSPeter Wemm 		if (tp->cno == 0)
936b8ba871bSPeter Wemm 			goto resolve;
937b8ba871bSPeter Wemm 
938b8ba871bSPeter Wemm 		switch (carat) {
939b8ba871bSPeter Wemm 		case C_CARATSET:	/* ^^D */
940b8ba871bSPeter Wemm 			if (tp->ai == 0 || tp->cno > tp->ai + tp->offset + 1)
941b8ba871bSPeter Wemm 				goto ins_ch;
942b8ba871bSPeter Wemm 
943b8ba871bSPeter Wemm 			/* Save the ai string for later. */
944b8ba871bSPeter Wemm 			ait.lb = NULL;
945b8ba871bSPeter Wemm 			ait.lb_len = 0;
946f0957ccaSPeter Wemm 			BINC_GOTOW(sp, ait.lb, ait.lb_len, tp->ai);
947f0957ccaSPeter Wemm 			MEMMOVE(ait.lb, tp->lb, tp->ai);
948b8ba871bSPeter Wemm 			ait.ai = ait.len = tp->ai;
949b8ba871bSPeter Wemm 
950f0957ccaSPeter Wemm 			carat = C_NOTSET;
951f0957ccaSPeter Wemm 			nochange = 1;
952b8ba871bSPeter Wemm 			goto leftmargin;
953b8ba871bSPeter Wemm 		case C_ZEROSET:		/* 0^D */
954b8ba871bSPeter Wemm 			if (tp->ai == 0 || tp->cno > tp->ai + tp->offset + 1)
955b8ba871bSPeter Wemm 				goto ins_ch;
956b8ba871bSPeter Wemm 
957b8ba871bSPeter Wemm 			carat = C_NOTSET;
958b8ba871bSPeter Wemm leftmargin:		tp->lb[tp->cno - 1] = ' ';
959b8ba871bSPeter Wemm 			tp->owrite += tp->cno - tp->offset;
960b8ba871bSPeter Wemm 			tp->ai = 0;
961b8ba871bSPeter Wemm 			tp->cno = tp->offset;
962b8ba871bSPeter Wemm 			break;
963b8ba871bSPeter Wemm 		case C_NOTSET:		/* ^D */
964b8ba871bSPeter Wemm 			if (tp->ai == 0 || tp->cno > tp->ai + tp->offset)
965b8ba871bSPeter Wemm 				goto ins_ch;
966b8ba871bSPeter Wemm 
967110d525eSBaptiste Daroussin 			(void)txt_dent(sp, tp, O_SHIFTWIDTH, 0);
968b8ba871bSPeter Wemm 			break;
969b8ba871bSPeter Wemm 		default:
970b8ba871bSPeter Wemm 			abort();
971b8ba871bSPeter Wemm 		}
972b8ba871bSPeter Wemm 		break;
973b8ba871bSPeter Wemm 	case K_VERASE:			/* Erase the last character. */
974b8ba871bSPeter Wemm 		/* If can erase over the prompt, return. */
975b8ba871bSPeter Wemm 		if (tp->cno <= tp->offset && LF_ISSET(TXT_BS)) {
976b8ba871bSPeter Wemm 			tp->term = TERM_BS;
977b8ba871bSPeter Wemm 			goto done;
978b8ba871bSPeter Wemm 		}
979b8ba871bSPeter Wemm 
980b8ba871bSPeter Wemm 		/*
981b8ba871bSPeter Wemm 		 * If at the beginning of the line, try and drop back to a
982b8ba871bSPeter Wemm 		 * previously inserted line.
983b8ba871bSPeter Wemm 		 */
984b8ba871bSPeter Wemm 		if (tp->cno == 0) {
985b8ba871bSPeter Wemm 			if ((ntp =
986f0957ccaSPeter Wemm 			    txt_backup(sp, sp->tiq, tp, &flags)) == NULL)
987b8ba871bSPeter Wemm 				goto err;
988b8ba871bSPeter Wemm 			tp = ntp;
989b8ba871bSPeter Wemm 			break;
990b8ba871bSPeter Wemm 		}
991b8ba871bSPeter Wemm 
992b8ba871bSPeter Wemm 		/* If nothing to erase, bell the user. */
993b8ba871bSPeter Wemm 		if (tp->cno <= tp->offset) {
994b8ba871bSPeter Wemm 			if (!LF_ISSET(TXT_REPLAY))
995b8ba871bSPeter Wemm 				txt_nomorech(sp);
996b8ba871bSPeter Wemm 			break;
997b8ba871bSPeter Wemm 		}
998b8ba871bSPeter Wemm 
999b8ba871bSPeter Wemm 		/* Drop back one character. */
1000b8ba871bSPeter Wemm 		--tp->cno;
1001b8ba871bSPeter Wemm 
1002b8ba871bSPeter Wemm 		/*
1003b8ba871bSPeter Wemm 		 * Historically, vi didn't replace the erased characters with
1004b8ba871bSPeter Wemm 		 * <blank>s, presumably because it's easier to fix a minor
1005b8ba871bSPeter Wemm 		 * typing mistake and continue on if the previous letters are
1006b8ba871bSPeter Wemm 		 * already there.  This is a problem for incremental searching,
1007b8ba871bSPeter Wemm 		 * because the user can no longer tell where they are in the
1008b8ba871bSPeter Wemm 		 * colon command line because the cursor is at the last search
1009b8ba871bSPeter Wemm 		 * point in the screen.  So, if incrementally searching, erase
1010b8ba871bSPeter Wemm 		 * the erased characters from the screen.
1011b8ba871bSPeter Wemm 		 */
1012b8ba871bSPeter Wemm 		if (FL_ISSET(is_flags, IS_RUNNING))
1013b8ba871bSPeter Wemm 			tp->lb[tp->cno] = ' ';
1014b8ba871bSPeter Wemm 
1015b8ba871bSPeter Wemm 		/*
1016b8ba871bSPeter Wemm 		 * Increment overwrite, decrement ai if deleted.
1017b8ba871bSPeter Wemm 		 *
1018b8ba871bSPeter Wemm 		 * !!!
1019b8ba871bSPeter Wemm 		 * Historic vi did not permit users to use erase characters
1020b8ba871bSPeter Wemm 		 * to delete autoindent characters.  We do.  Eat hot death,
1021b8ba871bSPeter Wemm 		 * POSIX.
1022b8ba871bSPeter Wemm 		 */
1023b8ba871bSPeter Wemm 		++tp->owrite;
1024b8ba871bSPeter Wemm 		if (tp->cno < tp->ai)
1025b8ba871bSPeter Wemm 			--tp->ai;
1026b8ba871bSPeter Wemm 
1027b8ba871bSPeter Wemm 		/* Reset if we deleted an incremental search character. */
1028b8ba871bSPeter Wemm 		if (FL_ISSET(is_flags, IS_RUNNING))
1029b8ba871bSPeter Wemm 			FL_SET(is_flags, IS_RESTART);
1030b8ba871bSPeter Wemm 		break;
1031b8ba871bSPeter Wemm 	case K_VWERASE:			/* Skip back one word. */
1032b8ba871bSPeter Wemm 		/*
1033b8ba871bSPeter Wemm 		 * If at the beginning of the line, try and drop back to a
1034b8ba871bSPeter Wemm 		 * previously inserted line.
1035b8ba871bSPeter Wemm 		 */
1036b8ba871bSPeter Wemm 		if (tp->cno == 0) {
1037b8ba871bSPeter Wemm 			if ((ntp =
1038f0957ccaSPeter Wemm 			    txt_backup(sp, sp->tiq, tp, &flags)) == NULL)
1039b8ba871bSPeter Wemm 				goto err;
1040b8ba871bSPeter Wemm 			tp = ntp;
1041b8ba871bSPeter Wemm 		}
1042b8ba871bSPeter Wemm 
1043b8ba871bSPeter Wemm 		/*
1044b8ba871bSPeter Wemm 		 * If at offset, nothing to erase so bell the user.
1045b8ba871bSPeter Wemm 		 */
1046b8ba871bSPeter Wemm 		if (tp->cno <= tp->offset) {
1047b8ba871bSPeter Wemm 			if (!LF_ISSET(TXT_REPLAY))
1048b8ba871bSPeter Wemm 				txt_nomorech(sp);
1049b8ba871bSPeter Wemm 			break;
1050b8ba871bSPeter Wemm 		}
1051b8ba871bSPeter Wemm 
1052b8ba871bSPeter Wemm 		/*
1053b8ba871bSPeter Wemm 		 * The first werase goes back to any autoindent column and the
1054b8ba871bSPeter Wemm 		 * second werase goes back to the offset.
1055b8ba871bSPeter Wemm 		 *
1056b8ba871bSPeter Wemm 		 * !!!
1057b8ba871bSPeter Wemm 		 * Historic vi did not permit users to use erase characters to
1058b8ba871bSPeter Wemm 		 * delete autoindent characters.
1059b8ba871bSPeter Wemm 		 */
1060b8ba871bSPeter Wemm 		if (tp->ai && tp->cno > tp->ai)
1061b8ba871bSPeter Wemm 			max = tp->ai;
1062b8ba871bSPeter Wemm 		else {
1063b8ba871bSPeter Wemm 			tp->ai = 0;
1064b8ba871bSPeter Wemm 			max = tp->offset;
1065b8ba871bSPeter Wemm 		}
1066b8ba871bSPeter Wemm 
1067b8ba871bSPeter Wemm 		/* Skip over trailing space characters. */
1068f0957ccaSPeter Wemm 		while (tp->cno > max && ISBLANK(tp->lb[tp->cno - 1])) {
1069b8ba871bSPeter Wemm 			--tp->cno;
1070b8ba871bSPeter Wemm 			++tp->owrite;
1071b8ba871bSPeter Wemm 		}
1072b8ba871bSPeter Wemm 		if (tp->cno == max)
1073b8ba871bSPeter Wemm 			break;
1074b8ba871bSPeter Wemm 		/*
1075b8ba871bSPeter Wemm 		 * There are three types of word erase found on UNIX systems.
1076b8ba871bSPeter Wemm 		 * They can be identified by how the string /a/b/c is treated
1077b8ba871bSPeter Wemm 		 * -- as 1, 3, or 6 words.  Historic vi had two classes of
1078b8ba871bSPeter Wemm 		 * characters, and strings were delimited by them and
1079b8ba871bSPeter Wemm 		 * <blank>'s, so, 6 words.  The historic tty interface used
1080b8ba871bSPeter Wemm 		 * <blank>'s to delimit strings, so, 1 word.  The algorithm
1081b8ba871bSPeter Wemm 		 * offered in the 4.4BSD tty interface (as stty altwerase)
1082b8ba871bSPeter Wemm 		 * treats it as 3 words -- there are two classes of
1083b8ba871bSPeter Wemm 		 * characters, and strings are delimited by them and
1084b8ba871bSPeter Wemm 		 * <blank>'s.  The difference is that the type of the first
1085b8ba871bSPeter Wemm 		 * erased character erased is ignored, which is exactly right
1086b8ba871bSPeter Wemm 		 * when erasing pathname components.  The edit options
1087b8ba871bSPeter Wemm 		 * TXT_ALTWERASE and TXT_TTYWERASE specify the 4.4BSD tty
1088b8ba871bSPeter Wemm 		 * interface and the historic tty driver behavior,
1089b8ba871bSPeter Wemm 		 * respectively, and the default is the same as the historic
1090b8ba871bSPeter Wemm 		 * vi behavior.
1091b8ba871bSPeter Wemm 		 *
1092b8ba871bSPeter Wemm 		 * Overwrite erased characters if doing incremental search;
1093b8ba871bSPeter Wemm 		 * see comment above.
1094b8ba871bSPeter Wemm 		 */
1095b8ba871bSPeter Wemm 		if (LF_ISSET(TXT_TTYWERASE))
1096b8ba871bSPeter Wemm 			while (tp->cno > max) {
1097f0957ccaSPeter Wemm 				if (ISBLANK(tp->lb[tp->cno - 1]))
1098f0957ccaSPeter Wemm 					break;
1099b8ba871bSPeter Wemm 				--tp->cno;
1100b8ba871bSPeter Wemm 				++tp->owrite;
1101b8ba871bSPeter Wemm 				if (FL_ISSET(is_flags, IS_RUNNING))
1102b8ba871bSPeter Wemm 					tp->lb[tp->cno] = ' ';
1103b8ba871bSPeter Wemm 			}
1104b8ba871bSPeter Wemm 		else {
1105b8ba871bSPeter Wemm 			if (LF_ISSET(TXT_ALTWERASE)) {
1106b8ba871bSPeter Wemm 				--tp->cno;
1107b8ba871bSPeter Wemm 				++tp->owrite;
1108b8ba871bSPeter Wemm 				if (FL_ISSET(is_flags, IS_RUNNING))
1109b8ba871bSPeter Wemm 					tp->lb[tp->cno] = ' ';
1110b8ba871bSPeter Wemm 			}
1111b8ba871bSPeter Wemm 			if (tp->cno > max)
1112b8ba871bSPeter Wemm 				tmp = inword(tp->lb[tp->cno - 1]);
1113b8ba871bSPeter Wemm 			while (tp->cno > max) {
1114f0957ccaSPeter Wemm 				if (tmp != inword(tp->lb[tp->cno - 1])
1115f0957ccaSPeter Wemm 				    || ISBLANK(tp->lb[tp->cno - 1]))
1116f0957ccaSPeter Wemm 					break;
1117b8ba871bSPeter Wemm 				--tp->cno;
1118b8ba871bSPeter Wemm 				++tp->owrite;
1119b8ba871bSPeter Wemm 				if (FL_ISSET(is_flags, IS_RUNNING))
1120b8ba871bSPeter Wemm 					tp->lb[tp->cno] = ' ';
1121b8ba871bSPeter Wemm 			}
1122b8ba871bSPeter Wemm 		}
1123b8ba871bSPeter Wemm 
1124b8ba871bSPeter Wemm 		/* Reset if we deleted an incremental search character. */
1125b8ba871bSPeter Wemm 		if (FL_ISSET(is_flags, IS_RUNNING))
1126b8ba871bSPeter Wemm 			FL_SET(is_flags, IS_RESTART);
1127b8ba871bSPeter Wemm 		break;
1128b8ba871bSPeter Wemm 	case K_VKILL:			/* Restart this line. */
1129b8ba871bSPeter Wemm 		/*
1130b8ba871bSPeter Wemm 		 * !!!
1131b8ba871bSPeter Wemm 		 * If at the beginning of the line, try and drop back to a
1132b8ba871bSPeter Wemm 		 * previously inserted line.  Historic vi did not permit
1133b8ba871bSPeter Wemm 		 * users to go back to previous lines.
1134b8ba871bSPeter Wemm 		 */
1135b8ba871bSPeter Wemm 		if (tp->cno == 0) {
1136b8ba871bSPeter Wemm 			if ((ntp =
1137f0957ccaSPeter Wemm 			    txt_backup(sp, sp->tiq, tp, &flags)) == NULL)
1138b8ba871bSPeter Wemm 				goto err;
1139b8ba871bSPeter Wemm 			tp = ntp;
1140b8ba871bSPeter Wemm 		}
1141b8ba871bSPeter Wemm 
1142b8ba871bSPeter Wemm 		/* If at offset, nothing to erase so bell the user. */
1143b8ba871bSPeter Wemm 		if (tp->cno <= tp->offset) {
1144b8ba871bSPeter Wemm 			if (!LF_ISSET(TXT_REPLAY))
1145b8ba871bSPeter Wemm 				txt_nomorech(sp);
1146b8ba871bSPeter Wemm 			break;
1147b8ba871bSPeter Wemm 		}
1148b8ba871bSPeter Wemm 
1149b8ba871bSPeter Wemm 		/*
1150b8ba871bSPeter Wemm 		 * First kill goes back to any autoindent and second kill goes
1151b8ba871bSPeter Wemm 		 * back to the offset.
1152b8ba871bSPeter Wemm 		 *
1153b8ba871bSPeter Wemm 		 * !!!
1154b8ba871bSPeter Wemm 		 * Historic vi did not permit users to use erase characters to
1155b8ba871bSPeter Wemm 		 * delete autoindent characters.
1156b8ba871bSPeter Wemm 		 */
1157b8ba871bSPeter Wemm 		if (tp->ai && tp->cno > tp->ai)
1158b8ba871bSPeter Wemm 			max = tp->ai;
1159b8ba871bSPeter Wemm 		else {
1160b8ba871bSPeter Wemm 			tp->ai = 0;
1161b8ba871bSPeter Wemm 			max = tp->offset;
1162b8ba871bSPeter Wemm 		}
1163b8ba871bSPeter Wemm 		tp->owrite += tp->cno - max;
1164b8ba871bSPeter Wemm 
1165b8ba871bSPeter Wemm 		/*
1166b8ba871bSPeter Wemm 		 * Overwrite erased characters if doing incremental search;
1167b8ba871bSPeter Wemm 		 * see comment above.
1168b8ba871bSPeter Wemm 		 */
1169b8ba871bSPeter Wemm 		if (FL_ISSET(is_flags, IS_RUNNING))
1170b8ba871bSPeter Wemm 			do {
1171b8ba871bSPeter Wemm 				tp->lb[--tp->cno] = ' ';
1172b8ba871bSPeter Wemm 			} while (tp->cno > max);
1173b8ba871bSPeter Wemm 		else
1174b8ba871bSPeter Wemm 			tp->cno = max;
1175b8ba871bSPeter Wemm 
1176b8ba871bSPeter Wemm 		/* Reset if we deleted an incremental search character. */
1177b8ba871bSPeter Wemm 		if (FL_ISSET(is_flags, IS_RUNNING))
1178b8ba871bSPeter Wemm 			FL_SET(is_flags, IS_RESTART);
1179b8ba871bSPeter Wemm 		break;
1180b8ba871bSPeter Wemm 	case K_CNTRLT:			/* Add autoindent characters. */
1181b8ba871bSPeter Wemm 		if (!LF_ISSET(TXT_CNTRLT))
1182b8ba871bSPeter Wemm 			goto ins_ch;
1183110d525eSBaptiste Daroussin 		if (txt_dent(sp, tp, O_SHIFTWIDTH, 1))
1184b8ba871bSPeter Wemm 			goto err;
1185b8ba871bSPeter Wemm 		goto ebuf_chk;
1186b8ba871bSPeter Wemm 	case K_VLNEXT:			/* Quote next character. */
1187b8ba871bSPeter Wemm 		evp->e_c = '^';
1188b8ba871bSPeter Wemm 		quote = Q_VNEXT;
1189b8ba871bSPeter Wemm 		/*
1190b8ba871bSPeter Wemm 		 * Turn on the quote flag so that the underlying routines
1191b8ba871bSPeter Wemm 		 * quote the next character where it's possible. Turn off
1192b8ba871bSPeter Wemm 		 * the input mapbiting flag so that we don't remap the next
1193b8ba871bSPeter Wemm 		 * character.
1194b8ba871bSPeter Wemm 		 */
1195b8ba871bSPeter Wemm 		FL_SET(ec_flags, EC_QUOTED);
1196b8ba871bSPeter Wemm 		FL_CLR(ec_flags, EC_MAPINPUT);
1197b8ba871bSPeter Wemm 
1198b8ba871bSPeter Wemm 		/*
1199b8ba871bSPeter Wemm 		 * !!!
1200b8ba871bSPeter Wemm 		 * Skip the tests for abbreviations, so ":ab xa XA",
1201b8ba871bSPeter Wemm 		 * "ixa^V<space>" doesn't perform the abbreviation.
1202b8ba871bSPeter Wemm 		 */
1203b8ba871bSPeter Wemm 		goto insl_ch;
1204b8ba871bSPeter Wemm 	case K_HEXCHAR:
1205b8ba871bSPeter Wemm 		hexcnt = 1;
1206b8ba871bSPeter Wemm 		goto insq_ch;
1207110d525eSBaptiste Daroussin 	case K_TAB:
1208110d525eSBaptiste Daroussin 		if (sp->showmode != SM_COMMAND && quote != Q_VTHIS &&
1209110d525eSBaptiste Daroussin 		    O_ISSET(sp, O_EXPANDTAB)) {
1210110d525eSBaptiste Daroussin 			if (txt_dent(sp, tp, O_TABSTOP, 1))
1211110d525eSBaptiste Daroussin 				goto err;
1212110d525eSBaptiste Daroussin 			goto ebuf_chk;
1213110d525eSBaptiste Daroussin 		}
1214110d525eSBaptiste Daroussin 		goto insq_ch;
1215b8ba871bSPeter Wemm 	default:			/* Insert the character. */
1216f0957ccaSPeter Wemm 		if (LF_ISSET(TXT_SHOWMATCH)) {
1217f0957ccaSPeter Wemm 			CHAR_T *match_chars, *cp;
1218f0957ccaSPeter Wemm 
1219f0957ccaSPeter Wemm 			match_chars = VIP(sp)->mcs;
1220f0957ccaSPeter Wemm 			cp = STRCHR(match_chars, evp->e_c);
1221f0957ccaSPeter Wemm 			if (cp != NULL && (cp - match_chars) & 1)
1222f0957ccaSPeter Wemm 				showmatch = 1;
1223f0957ccaSPeter Wemm 		}
1224b8ba871bSPeter Wemm ins_ch:		/*
1225b8ba871bSPeter Wemm 		 * Historically, vi eliminated nul's out of hand.  If the
1226b8ba871bSPeter Wemm 		 * beautify option was set, it also deleted any unknown
1227b8ba871bSPeter Wemm 		 * ASCII value less than space (040) and the del character
1228b8ba871bSPeter Wemm 		 * (0177), except for tabs.  Unknown is a key word here.
1229b8ba871bSPeter Wemm 		 * Most vi documentation claims that it deleted everything
1230b8ba871bSPeter Wemm 		 * but <tab>, <nl> and <ff>, as that's what the original
1231b8ba871bSPeter Wemm 		 * 4BSD documentation said.  This is obviously wrong,
1232b8ba871bSPeter Wemm 		 * however, as <esc> would be included in that list.  What
1233b8ba871bSPeter Wemm 		 * we do is eliminate any unquoted, iscntrl() character that
1234b8ba871bSPeter Wemm 		 * wasn't a replay and wasn't handled specially, except
1235b8ba871bSPeter Wemm 		 * <tab> or <ff>.
1236b8ba871bSPeter Wemm 		 */
1237f0957ccaSPeter Wemm 		if (LF_ISSET(TXT_BEAUTIFY) && ISCNTRL(evp->e_c) &&
1238b8ba871bSPeter Wemm 		    evp->e_value != K_FORMFEED && evp->e_value != K_TAB) {
1239b8ba871bSPeter Wemm 			msgq(sp, M_BERR,
1240b8ba871bSPeter Wemm 			    "192|Illegal character; quote to enter");
1241b8ba871bSPeter Wemm 			if (LF_ISSET(TXT_REPLAY))
1242b8ba871bSPeter Wemm 				goto done;
1243b8ba871bSPeter Wemm 			break;
1244b8ba871bSPeter Wemm 		}
1245b8ba871bSPeter Wemm 
1246b8ba871bSPeter Wemm insq_ch:	/*
1247b8ba871bSPeter Wemm 		 * If entering a non-word character after a word, check for
1248b8ba871bSPeter Wemm 		 * abbreviations.  If there was one, discard replay characters.
1249b8ba871bSPeter Wemm 		 * If entering a blank character, check for unmap commands,
1250b8ba871bSPeter Wemm 		 * as well.
1251b8ba871bSPeter Wemm 		 */
1252b8ba871bSPeter Wemm 		if (!inword(evp->e_c)) {
1253b8ba871bSPeter Wemm 			if (abb == AB_INWORD &&
1254b8ba871bSPeter Wemm 			    !LF_ISSET(TXT_REPLAY) && F_ISSET(gp, G_ABBREV)) {
1255b8ba871bSPeter Wemm 				if (txt_abbrev(sp, tp, &evp->e_c,
1256b8ba871bSPeter Wemm 				    LF_ISSET(TXT_INFOLINE), &tmp, &ab_turnoff))
1257b8ba871bSPeter Wemm 					goto err;
1258b8ba871bSPeter Wemm 				if (tmp) {
1259b8ba871bSPeter Wemm 					if (LF_ISSET(TXT_RECORD))
1260b8ba871bSPeter Wemm 						rcol -= tmp + 1;
1261b8ba871bSPeter Wemm 					goto resolve;
1262b8ba871bSPeter Wemm 				}
1263b8ba871bSPeter Wemm 			}
1264b8ba871bSPeter Wemm 			if (isblank(evp->e_c) && UNMAP_TST)
1265b8ba871bSPeter Wemm 				txt_unmap(sp, tp, &ec_flags);
1266b8ba871bSPeter Wemm 		}
1267b8ba871bSPeter Wemm 		if (abb != AB_NOTSET)
1268b8ba871bSPeter Wemm 			abb = inword(evp->e_c) ? AB_INWORD : AB_NOTWORD;
1269b8ba871bSPeter Wemm 
1270b8ba871bSPeter Wemm insl_ch:	if (txt_insch(sp, tp, &evp->e_c, flags))
1271b8ba871bSPeter Wemm 			goto err;
1272b8ba871bSPeter Wemm 
1273b8ba871bSPeter Wemm 		/*
1274b8ba871bSPeter Wemm 		 * If we're using K_VLNEXT to quote the next character, then
1275b8ba871bSPeter Wemm 		 * we want the cursor to position itself on the ^ placeholder
1276b8ba871bSPeter Wemm 		 * we're displaying, to match historic practice.
1277b8ba871bSPeter Wemm 		 */
1278b8ba871bSPeter Wemm 		if (quote == Q_VNEXT) {
1279b8ba871bSPeter Wemm 			--tp->cno;
1280b8ba871bSPeter Wemm 			++tp->owrite;
1281b8ba871bSPeter Wemm 		}
1282b8ba871bSPeter Wemm 
1283b8ba871bSPeter Wemm 		/*
1284b8ba871bSPeter Wemm 		 * !!!
1285b8ba871bSPeter Wemm 		 * Translate "<CH_HEX>[isxdigit()]*" to a character with
1286b8ba871bSPeter Wemm 		 * a hex value: this test delimits the value by the max
1287b8ba871bSPeter Wemm 		 * number of hex bytes.  Offset by one, we use 0 to mean
1288b8ba871bSPeter Wemm 		 * that we've found <CH_HEX>.
1289b8ba871bSPeter Wemm 		 */
1290f0957ccaSPeter Wemm 		if (hexcnt != 0 && hexcnt++ == 3) {
1291b8ba871bSPeter Wemm 			hexcnt = 0;
1292b8ba871bSPeter Wemm 			if (txt_hex(sp, tp))
1293b8ba871bSPeter Wemm 				goto err;
1294b8ba871bSPeter Wemm 		}
1295b8ba871bSPeter Wemm 
1296b8ba871bSPeter Wemm 		/*
1297b8ba871bSPeter Wemm 		 * Check to see if we've crossed the margin.
1298b8ba871bSPeter Wemm 		 *
1299b8ba871bSPeter Wemm 		 * !!!
1300b8ba871bSPeter Wemm 		 * In the historic vi, the wrapmargin value was figured out
1301b8ba871bSPeter Wemm 		 * using the display widths of the characters, i.e. <tab>
1302b8ba871bSPeter Wemm 		 * characters were counted as two characters if the list edit
1303b8ba871bSPeter Wemm 		 * option is set, but as the tabstop edit option number of
1304b8ba871bSPeter Wemm 		 * characters otherwise.  That's what the vs_column() function
1305b8ba871bSPeter Wemm 		 * gives us, so we use it.
1306b8ba871bSPeter Wemm 		 */
1307b8ba871bSPeter Wemm 		if (margin != 0) {
1308b8ba871bSPeter Wemm 			if (vs_column(sp, &tcol))
1309b8ba871bSPeter Wemm 				goto err;
1310b8ba871bSPeter Wemm 			if (tcol >= margin) {
1311b8ba871bSPeter Wemm 				if (txt_margin(sp, tp, &wmt, &tmp, flags))
1312b8ba871bSPeter Wemm 					goto err;
1313b8ba871bSPeter Wemm 				if (tmp) {
1314b8ba871bSPeter Wemm 					if (isblank(evp->e_c))
1315b8ba871bSPeter Wemm 						wm_skip = 1;
1316b8ba871bSPeter Wemm 					wm_set = 1;
1317b8ba871bSPeter Wemm 					goto k_cr;
1318b8ba871bSPeter Wemm 				}
1319b8ba871bSPeter Wemm 			}
1320b8ba871bSPeter Wemm 		}
1321b8ba871bSPeter Wemm 
1322b8ba871bSPeter Wemm 		/*
1323b8ba871bSPeter Wemm 		 * If we've reached the end of the buffer, then we need to
1324b8ba871bSPeter Wemm 		 * switch into insert mode.  This happens when there's a
1325b8ba871bSPeter Wemm 		 * change to a mark and the user puts in more characters than
1326b8ba871bSPeter Wemm 		 * the length of the motion.
1327b8ba871bSPeter Wemm 		 */
1328b8ba871bSPeter Wemm ebuf_chk:	if (tp->cno >= tp->len) {
1329f0957ccaSPeter Wemm 			BINC_GOTOW(sp, tp->lb, tp->lb_len, tp->len + 1);
1330b8ba871bSPeter Wemm 			LF_SET(TXT_APPENDEOL);
1331b8ba871bSPeter Wemm 
1332b8ba871bSPeter Wemm 			tp->lb[tp->cno] = CH_CURSOR;
1333b8ba871bSPeter Wemm 			++tp->insert;
1334b8ba871bSPeter Wemm 			++tp->len;
1335b8ba871bSPeter Wemm 		}
1336b8ba871bSPeter Wemm 
1337b8ba871bSPeter Wemm 		/* Step the quote state forward. */
1338b8ba871bSPeter Wemm 		if (quote == Q_VNEXT)
1339b8ba871bSPeter Wemm 			quote = Q_VTHIS;
1340b8ba871bSPeter Wemm 		break;
1341b8ba871bSPeter Wemm 	}
1342b8ba871bSPeter Wemm 
1343b8ba871bSPeter Wemm #ifdef DEBUG
1344b8ba871bSPeter Wemm 	if (tp->cno + tp->insert + tp->owrite != tp->len) {
1345b8ba871bSPeter Wemm 		msgq(sp, M_ERR,
1346f0957ccaSPeter Wemm 		    "len %zu != cno: %zu ai: %zu insert %zu overwrite %zu",
1347b8ba871bSPeter Wemm 		    tp->len, tp->cno, tp->ai, tp->insert, tp->owrite);
1348b8ba871bSPeter Wemm 		if (LF_ISSET(TXT_REPLAY))
1349b8ba871bSPeter Wemm 			goto done;
1350b8ba871bSPeter Wemm 		tp->len = tp->cno + tp->insert + tp->owrite;
1351b8ba871bSPeter Wemm 	}
1352b8ba871bSPeter Wemm #endif
1353b8ba871bSPeter Wemm 
1354b8ba871bSPeter Wemm resolve:/*
1355b8ba871bSPeter Wemm 	 * 1: If we don't need to know where the cursor really is and we're
1356b8ba871bSPeter Wemm 	 *    replaying text, keep going.
1357b8ba871bSPeter Wemm 	 */
1358b8ba871bSPeter Wemm 	if (margin == 0 && LF_ISSET(TXT_REPLAY))
1359b8ba871bSPeter Wemm 		goto replay;
1360b8ba871bSPeter Wemm 
1361b8ba871bSPeter Wemm 	/*
1362b8ba871bSPeter Wemm 	 * 2: Reset the line.  Don't bother unless we're about to wait on
1363b8ba871bSPeter Wemm 	 *    a character or we need to know where the cursor really is.
1364b8ba871bSPeter Wemm 	 *    We have to do this before showing matching characters so the
1365b8ba871bSPeter Wemm 	 *    user can see what they're matching.
1366b8ba871bSPeter Wemm 	 */
1367b8ba871bSPeter Wemm 	if ((margin != 0 || !KEYS_WAITING(sp)) &&
1368b8ba871bSPeter Wemm 	    vs_change(sp, tp->lno, LINE_RESET))
1369b8ba871bSPeter Wemm 		return (1);
1370b8ba871bSPeter Wemm 
1371b8ba871bSPeter Wemm 	/*
1372b8ba871bSPeter Wemm 	 * 3: If there aren't keys waiting, display the matching character.
1373b8ba871bSPeter Wemm 	 *    We have to do this before resolving any messages, otherwise
1374b8ba871bSPeter Wemm 	 *    the error message from a missing match won't appear correctly.
1375b8ba871bSPeter Wemm 	 */
1376b8ba871bSPeter Wemm 	if (showmatch) {
1377b8ba871bSPeter Wemm 		if (!KEYS_WAITING(sp) && txt_showmatch(sp, tp))
1378b8ba871bSPeter Wemm 			return (1);
1379b8ba871bSPeter Wemm 		showmatch = 0;
1380b8ba871bSPeter Wemm 	}
1381b8ba871bSPeter Wemm 
1382b8ba871bSPeter Wemm 	/*
1383b8ba871bSPeter Wemm 	 * 4: If there have been messages and we're not editing on the colon
1384b8ba871bSPeter Wemm 	 *    command line or doing file name completion, resolve them.
1385b8ba871bSPeter Wemm 	 */
1386b8ba871bSPeter Wemm 	if ((vip->totalcount != 0 || F_ISSET(gp, G_BELLSCHED)) &&
1387b8ba871bSPeter Wemm 	    !F_ISSET(sp, SC_TINPUT_INFO) && !filec_redraw &&
1388b8ba871bSPeter Wemm 	    vs_resolve(sp, NULL, 0))
1389b8ba871bSPeter Wemm 		return (1);
1390b8ba871bSPeter Wemm 
1391b8ba871bSPeter Wemm 	/*
1392b8ba871bSPeter Wemm 	 * 5: Refresh the screen if we're about to wait on a character or we
1393b8ba871bSPeter Wemm 	 *    need to know where the cursor really is.
1394b8ba871bSPeter Wemm 	 */
1395b8ba871bSPeter Wemm 	if (margin != 0 || !KEYS_WAITING(sp)) {
1396b8ba871bSPeter Wemm 		UPDATE_POSITION(sp, tp);
1397b8ba871bSPeter Wemm 		if (vs_refresh(sp, margin != 0))
1398b8ba871bSPeter Wemm 			return (1);
1399b8ba871bSPeter Wemm 	}
1400b8ba871bSPeter Wemm 
1401b8ba871bSPeter Wemm 	/* 6: Proceed with the incremental search. */
1402b8ba871bSPeter Wemm 	if (FL_ISSET(is_flags, IS_RUNNING) && txt_isrch(sp, vp, tp, &is_flags))
1403b8ba871bSPeter Wemm 		return (1);
1404b8ba871bSPeter Wemm 
1405b8ba871bSPeter Wemm 	/* 7: Next character... */
1406b8ba871bSPeter Wemm 	if (LF_ISSET(TXT_REPLAY))
1407b8ba871bSPeter Wemm 		goto replay;
1408b8ba871bSPeter Wemm 	goto next;
1409b8ba871bSPeter Wemm 
1410b8ba871bSPeter Wemm done:	/* Leave input mode. */
1411b8ba871bSPeter Wemm 	F_CLR(sp, SC_TINPUT);
1412b8ba871bSPeter Wemm 
1413b8ba871bSPeter Wemm 	/* If recording for playback, save it. */
1414b8ba871bSPeter Wemm 	if (LF_ISSET(TXT_RECORD))
1415b8ba871bSPeter Wemm 		vip->rep_cnt = rcol;
1416b8ba871bSPeter Wemm 
1417b8ba871bSPeter Wemm 	/*
1418b8ba871bSPeter Wemm 	 * If not working on the colon command line, set the final cursor
1419b8ba871bSPeter Wemm 	 * position.
1420b8ba871bSPeter Wemm 	 */
1421b8ba871bSPeter Wemm 	if (!F_ISSET(sp, SC_TINPUT_INFO)) {
1422b8ba871bSPeter Wemm 		vp->m_final.lno = tp->lno;
1423b8ba871bSPeter Wemm 		vp->m_final.cno = tp->cno;
1424b8ba871bSPeter Wemm 	}
1425b8ba871bSPeter Wemm 	return (0);
1426b8ba871bSPeter Wemm 
1427b8ba871bSPeter Wemm err:
1428b8ba871bSPeter Wemm alloc_err:
1429725f5cdbSJaakko Heinonen 	F_CLR(sp, SC_TINPUT);
1430f0957ccaSPeter Wemm 	txt_err(sp, sp->tiq);
1431b8ba871bSPeter Wemm 	return (1);
1432b8ba871bSPeter Wemm }
1433b8ba871bSPeter Wemm 
1434b8ba871bSPeter Wemm /*
1435b8ba871bSPeter Wemm  * txt_abbrev --
1436b8ba871bSPeter Wemm  *	Handle abbreviations.
1437b8ba871bSPeter Wemm  */
1438b8ba871bSPeter Wemm static int
txt_abbrev(SCR * sp,TEXT * tp,CHAR_T * pushcp,int isinfoline,int * didsubp,int * turnoffp)1439f0957ccaSPeter Wemm txt_abbrev(SCR *sp, TEXT *tp, CHAR_T *pushcp, int isinfoline, int *didsubp, int *turnoffp)
1440b8ba871bSPeter Wemm {
1441b8ba871bSPeter Wemm 	VI_PRIVATE *vip;
1442b8ba871bSPeter Wemm 	CHAR_T ch, *p;
1443b8ba871bSPeter Wemm 	SEQ *qp;
1444b8ba871bSPeter Wemm 	size_t len, off;
1445b8ba871bSPeter Wemm 
1446b8ba871bSPeter Wemm 	/* Check to make sure we're not at the start of an append. */
1447b8ba871bSPeter Wemm 	*didsubp = 0;
1448b8ba871bSPeter Wemm 	if (tp->cno == tp->offset)
1449b8ba871bSPeter Wemm 		return (0);
1450b8ba871bSPeter Wemm 
1451b8ba871bSPeter Wemm 	vip = VIP(sp);
1452b8ba871bSPeter Wemm 
1453b8ba871bSPeter Wemm 	/*
1454b8ba871bSPeter Wemm 	 * Find the start of the "word".
1455b8ba871bSPeter Wemm 	 *
1456b8ba871bSPeter Wemm 	 * !!!
1457b8ba871bSPeter Wemm 	 * We match historic practice, which, as far as I can tell, had an
1458b8ba871bSPeter Wemm 	 * off-by-one error.  The way this worked was that when the inserted
1459b8ba871bSPeter Wemm 	 * text switched from a "word" character to a non-word character,
1460b8ba871bSPeter Wemm 	 * vi would check for possible abbreviations.  It would then take the
1461b8ba871bSPeter Wemm 	 * type (i.e. word/non-word) of the character entered TWO characters
1462b8ba871bSPeter Wemm 	 * ago, and move backward in the text until reaching a character that
1463b8ba871bSPeter Wemm 	 * was not that type, or the beginning of the insert, the line, or
1464b8ba871bSPeter Wemm 	 * the file.  For example, in the string "abc<space>", when the <space>
1465b8ba871bSPeter Wemm 	 * character triggered the abbreviation check, the type of the 'b'
1466b8ba871bSPeter Wemm 	 * character was used for moving through the string.  Maybe there's a
1467b8ba871bSPeter Wemm 	 * reason for not using the first (i.e. 'c') character, but I can't
1468b8ba871bSPeter Wemm 	 * think of one.
1469b8ba871bSPeter Wemm 	 *
1470b8ba871bSPeter Wemm 	 * Terminate at the beginning of the insert or the character after the
1471b8ba871bSPeter Wemm 	 * offset character -- both can be tested for using tp->offset.
1472b8ba871bSPeter Wemm 	 */
1473b8ba871bSPeter Wemm 	off = tp->cno - 1;			/* Previous character. */
1474b8ba871bSPeter Wemm 	p = tp->lb + off;
1475b8ba871bSPeter Wemm 	len = 1;				/* One character test. */
1476b8ba871bSPeter Wemm 	if (off == tp->offset || isblank(p[-1]))
1477b8ba871bSPeter Wemm 		goto search;
1478b8ba871bSPeter Wemm 	if (inword(p[-1]))			/* Move backward to change. */
1479b8ba871bSPeter Wemm 		for (;;) {
1480b8ba871bSPeter Wemm 			--off; --p; ++len;
1481b8ba871bSPeter Wemm 			if (off == tp->offset || !inword(p[-1]))
1482b8ba871bSPeter Wemm 				break;
1483b8ba871bSPeter Wemm 		}
1484b8ba871bSPeter Wemm 	else
1485b8ba871bSPeter Wemm 		for (;;) {
1486b8ba871bSPeter Wemm 			--off; --p; ++len;
1487b8ba871bSPeter Wemm 			if (off == tp->offset ||
1488b8ba871bSPeter Wemm 			    inword(p[-1]) || isblank(p[-1]))
1489b8ba871bSPeter Wemm 				break;
1490b8ba871bSPeter Wemm 		}
1491b8ba871bSPeter Wemm 
1492b8ba871bSPeter Wemm 	/*
1493b8ba871bSPeter Wemm 	 * !!!
1494b8ba871bSPeter Wemm 	 * Historic vi exploded abbreviations on the command line.  This has
1495b8ba871bSPeter Wemm 	 * obvious problems in that unabbreviating the string can be extremely
1496b8ba871bSPeter Wemm 	 * tricky, particularly if the string has, say, an embedded escape
1497b8ba871bSPeter Wemm 	 * character.  Personally, I think it's a stunningly bad idea.  Other
1498b8ba871bSPeter Wemm 	 * examples of problems this caused in historic vi are:
1499b8ba871bSPeter Wemm 	 *	:ab foo bar
1500b8ba871bSPeter Wemm 	 *	:ab foo baz
1501b8ba871bSPeter Wemm 	 * results in "bar" being abbreviated to "baz", which wasn't what the
1502b8ba871bSPeter Wemm 	 * user had in mind at all.  Also, the commands:
1503b8ba871bSPeter Wemm 	 *	:ab foo bar
1504b8ba871bSPeter Wemm 	 *	:unab foo<space>
1505b8ba871bSPeter Wemm 	 * resulted in an error message that "bar" wasn't mapped.  Finally,
1506b8ba871bSPeter Wemm 	 * since the string was already exploded by the time the unabbreviate
1507b8ba871bSPeter Wemm 	 * command got it, all it knew was that an abbreviation had occurred.
1508b8ba871bSPeter Wemm 	 * Cleverly, it checked the replacement string for its unabbreviation
1509b8ba871bSPeter Wemm 	 * match, which meant that the commands:
1510b8ba871bSPeter Wemm 	 *	:ab foo1 bar
1511b8ba871bSPeter Wemm 	 *	:ab foo2 bar
1512b8ba871bSPeter Wemm 	 *	:unab foo2
1513b8ba871bSPeter Wemm 	 * unabbreviate "foo1", and the commands:
1514b8ba871bSPeter Wemm 	 *	:ab foo bar
1515b8ba871bSPeter Wemm 	 *	:ab bar baz
1516b8ba871bSPeter Wemm 	 * unabbreviate "foo"!
1517b8ba871bSPeter Wemm 	 *
1518b8ba871bSPeter Wemm 	 * Anyway, people neglected to first ask my opinion before they wrote
1519b8ba871bSPeter Wemm 	 * macros that depend on this stuff, so, we make this work as follows.
1520b8ba871bSPeter Wemm 	 * When checking for an abbreviation on the command line, if we get a
1521b8ba871bSPeter Wemm 	 * string which is <blank> terminated and which starts at the beginning
1522b8ba871bSPeter Wemm 	 * of the line, we check to see it is the abbreviate or unabbreviate
1523b8ba871bSPeter Wemm 	 * commands.  If it is, turn abbreviations off and return as if no
1524b8ba871bSPeter Wemm 	 * abbreviation was found.  Note also, minor trickiness, so that if
1525b8ba871bSPeter Wemm 	 * the user erases the line and starts another command, we turn the
1526b8ba871bSPeter Wemm 	 * abbreviations back on.
1527b8ba871bSPeter Wemm 	 *
1528b8ba871bSPeter Wemm 	 * This makes the layering look like a Nachos Supreme.
1529b8ba871bSPeter Wemm 	 */
1530*755cc40cSBaptiste Daroussin search:	if (isinfoline) {
1531b8ba871bSPeter Wemm 		if (off == tp->ai || off == tp->offset)
1532b8ba871bSPeter Wemm 			if (ex_is_abbrev(p, len)) {
1533b8ba871bSPeter Wemm 				*turnoffp = 1;
1534b8ba871bSPeter Wemm 				return (0);
1535b8ba871bSPeter Wemm 			} else
1536b8ba871bSPeter Wemm 				*turnoffp = 0;
1537b8ba871bSPeter Wemm 		else
1538b8ba871bSPeter Wemm 			if (*turnoffp)
1539b8ba871bSPeter Wemm 				return (0);
1540*755cc40cSBaptiste Daroussin 	}
1541b8ba871bSPeter Wemm 
1542b8ba871bSPeter Wemm 	/* Check for any abbreviations. */
1543b8ba871bSPeter Wemm 	if ((qp = seq_find(sp, NULL, NULL, p, len, SEQ_ABBREV, NULL)) == NULL)
1544b8ba871bSPeter Wemm 		return (0);
1545b8ba871bSPeter Wemm 
1546b8ba871bSPeter Wemm 	/*
1547b8ba871bSPeter Wemm 	 * Push the abbreviation onto the tty stack.  Historically, characters
1548b8ba871bSPeter Wemm 	 * resulting from an abbreviation expansion were themselves subject to
1549b8ba871bSPeter Wemm 	 * map expansions, O_SHOWMATCH matching etc.  This means the expanded
1550b8ba871bSPeter Wemm 	 * characters will be re-tested for abbreviations.  It's difficult to
1551b8ba871bSPeter Wemm 	 * know what historic practice in this case was, since abbreviations
1552b8ba871bSPeter Wemm 	 * were applied to :colon command lines, so entering abbreviations that
1553b8ba871bSPeter Wemm 	 * looped was tricky, although possible.  In addition, obvious loops
1554b8ba871bSPeter Wemm 	 * didn't work as expected.  (The command ':ab a b|ab b c|ab c a' will
1555b8ba871bSPeter Wemm 	 * silently only implement and/or display the last abbreviation.)
1556b8ba871bSPeter Wemm 	 *
1557b8ba871bSPeter Wemm 	 * This implementation doesn't recover well from such abbreviations.
1558b8ba871bSPeter Wemm 	 * The main input loop counts abbreviated characters, and, when it
1559b8ba871bSPeter Wemm 	 * reaches a limit, discards any abbreviated characters on the queue.
1560b8ba871bSPeter Wemm 	 * It's difficult to back up to the original position, as the replay
1561b8ba871bSPeter Wemm 	 * queue would have to be adjusted, and the line state when an initial
1562b8ba871bSPeter Wemm 	 * abbreviated character was received would have to be saved.
1563b8ba871bSPeter Wemm 	 */
1564b8ba871bSPeter Wemm 	ch = *pushcp;
1565b8ba871bSPeter Wemm 	if (v_event_push(sp, NULL, &ch, 1, CH_ABBREVIATED))
1566b8ba871bSPeter Wemm 		return (1);
1567b8ba871bSPeter Wemm 	if (v_event_push(sp, NULL, qp->output, qp->olen, CH_ABBREVIATED))
1568b8ba871bSPeter Wemm 		return (1);
1569b8ba871bSPeter Wemm 
1570b8ba871bSPeter Wemm 	/*
1571b8ba871bSPeter Wemm 	 * If the size of the abbreviation is larger than or equal to the size
1572b8ba871bSPeter Wemm 	 * of the original text, move to the start of the replaced characters,
1573b8ba871bSPeter Wemm 	 * and add their length to the overwrite count.
1574b8ba871bSPeter Wemm 	 *
1575b8ba871bSPeter Wemm 	 * If the abbreviation is smaller than the original text, we have to
1576b8ba871bSPeter Wemm 	 * delete the additional overwrite characters and copy down any insert
1577b8ba871bSPeter Wemm 	 * characters.
1578b8ba871bSPeter Wemm 	 */
1579b8ba871bSPeter Wemm 	tp->cno -= len;
1580b8ba871bSPeter Wemm 	if (qp->olen >= len)
1581b8ba871bSPeter Wemm 		tp->owrite += len;
1582b8ba871bSPeter Wemm 	else {
1583b8ba871bSPeter Wemm 		if (tp->insert)
1584f0957ccaSPeter Wemm 			MEMMOVE(tp->lb + tp->cno + qp->olen,
1585b8ba871bSPeter Wemm 			    tp->lb + tp->cno + tp->owrite + len, tp->insert);
1586b8ba871bSPeter Wemm 		tp->owrite += qp->olen;
1587b8ba871bSPeter Wemm 		tp->len -= len - qp->olen;
1588b8ba871bSPeter Wemm 	}
1589b8ba871bSPeter Wemm 
1590b8ba871bSPeter Wemm 	/*
1591b8ba871bSPeter Wemm 	 * We return the length of the abbreviated characters.  This is so
1592b8ba871bSPeter Wemm 	 * the calling routine can replace the replay characters with the
1593b8ba871bSPeter Wemm 	 * abbreviation.  This means that subsequent '.' commands will produce
1594b8ba871bSPeter Wemm 	 * the same text, regardless of intervening :[un]abbreviate commands.
1595b8ba871bSPeter Wemm 	 * This is historic practice.
1596b8ba871bSPeter Wemm 	 */
1597b8ba871bSPeter Wemm 	*didsubp = len;
1598b8ba871bSPeter Wemm 	return (0);
1599b8ba871bSPeter Wemm }
1600b8ba871bSPeter Wemm 
1601b8ba871bSPeter Wemm /*
1602b8ba871bSPeter Wemm  * txt_unmap --
1603b8ba871bSPeter Wemm  *	Handle the unmap command.
1604b8ba871bSPeter Wemm  */
1605b8ba871bSPeter Wemm static void
txt_unmap(SCR * sp,TEXT * tp,u_int32_t * ec_flagsp)1606f0957ccaSPeter Wemm txt_unmap(SCR *sp, TEXT *tp, u_int32_t *ec_flagsp)
1607b8ba871bSPeter Wemm {
1608b8ba871bSPeter Wemm 	size_t len, off;
1609f0957ccaSPeter Wemm 	CHAR_T *p;
1610b8ba871bSPeter Wemm 
1611b8ba871bSPeter Wemm 	/* Find the beginning of this "word". */
1612b8ba871bSPeter Wemm 	for (off = tp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) {
1613b8ba871bSPeter Wemm 		if (isblank(*p)) {
1614b8ba871bSPeter Wemm 			++p;
1615b8ba871bSPeter Wemm 			break;
1616b8ba871bSPeter Wemm 		}
1617b8ba871bSPeter Wemm 		++len;
1618b8ba871bSPeter Wemm 		if (off == tp->ai || off == tp->offset)
1619b8ba871bSPeter Wemm 			break;
1620b8ba871bSPeter Wemm 	}
1621b8ba871bSPeter Wemm 
1622b8ba871bSPeter Wemm 	/*
1623b8ba871bSPeter Wemm 	 * !!!
1624b8ba871bSPeter Wemm 	 * Historic vi exploded input mappings on the command line.  See the
1625b8ba871bSPeter Wemm 	 * txt_abbrev() routine for an explanation of the problems inherent
1626b8ba871bSPeter Wemm 	 * in this.
1627b8ba871bSPeter Wemm 	 *
1628b8ba871bSPeter Wemm 	 * We make this work as follows.  If we get a string which is <blank>
1629b8ba871bSPeter Wemm 	 * terminated and which starts at the beginning of the line, we check
1630b8ba871bSPeter Wemm 	 * to see it is the unmap command.  If it is, we return that the input
1631b8ba871bSPeter Wemm 	 * mapping should be turned off.  Note also, minor trickiness, so that
1632b8ba871bSPeter Wemm 	 * if the user erases the line and starts another command, we go ahead
1633b8ba871bSPeter Wemm 	 * an turn mapping back on.
1634b8ba871bSPeter Wemm 	 */
1635b8ba871bSPeter Wemm 	if ((off == tp->ai || off == tp->offset) && ex_is_unmap(p, len))
1636b8ba871bSPeter Wemm 		FL_CLR(*ec_flagsp, EC_MAPINPUT);
1637b8ba871bSPeter Wemm 	else
1638b8ba871bSPeter Wemm 		FL_SET(*ec_flagsp, EC_MAPINPUT);
1639b8ba871bSPeter Wemm }
1640b8ba871bSPeter Wemm 
1641b8ba871bSPeter Wemm /*
1642b8ba871bSPeter Wemm  * txt_ai_resolve --
1643b8ba871bSPeter Wemm  *	When a line is resolved by <esc>, review autoindent characters.
1644b8ba871bSPeter Wemm  */
1645b8ba871bSPeter Wemm static void
txt_ai_resolve(SCR * sp,TEXT * tp,int * changedp)1646f0957ccaSPeter Wemm txt_ai_resolve(SCR *sp, TEXT *tp, int *changedp)
1647b8ba871bSPeter Wemm {
1648b8ba871bSPeter Wemm 	u_long ts;
1649b8ba871bSPeter Wemm 	int del;
1650b8ba871bSPeter Wemm 	size_t cno, len, new, old, scno, spaces, tab_after_sp, tabs;
1651f0957ccaSPeter Wemm 	CHAR_T *p;
1652b8ba871bSPeter Wemm 
1653b8ba871bSPeter Wemm 	*changedp = 0;
1654b8ba871bSPeter Wemm 
1655b8ba871bSPeter Wemm 	/*
1656b8ba871bSPeter Wemm 	 * If the line is empty, has an offset, or no autoindent
1657b8ba871bSPeter Wemm 	 * characters, we're done.
1658b8ba871bSPeter Wemm 	 */
1659b8ba871bSPeter Wemm 	if (!tp->len || tp->offset || !tp->ai)
1660b8ba871bSPeter Wemm 		return;
1661b8ba871bSPeter Wemm 
1662b8ba871bSPeter Wemm 	/*
1663b8ba871bSPeter Wemm 	 * If the length is less than or equal to the autoindent
1664b8ba871bSPeter Wemm 	 * characters, delete them.
1665b8ba871bSPeter Wemm 	 */
1666b8ba871bSPeter Wemm 	if (tp->len <= tp->ai) {
1667b8ba871bSPeter Wemm 		tp->ai = tp->cno = tp->len = 0;
1668b8ba871bSPeter Wemm 		return;
1669b8ba871bSPeter Wemm 	}
1670b8ba871bSPeter Wemm 
1671b8ba871bSPeter Wemm 	/*
1672b8ba871bSPeter Wemm 	 * The autoindent characters plus any leading <blank> characters
1673b8ba871bSPeter Wemm 	 * in the line are resolved into the minimum number of characters.
1674b8ba871bSPeter Wemm 	 * Historic practice.
1675b8ba871bSPeter Wemm 	 */
1676b8ba871bSPeter Wemm 	ts = O_VAL(sp, O_TABSTOP);
1677b8ba871bSPeter Wemm 
1678b8ba871bSPeter Wemm 	/* Figure out the last <blank> screen column. */
1679b8ba871bSPeter Wemm 	for (p = tp->lb, scno = 0, len = tp->len,
1680b8ba871bSPeter Wemm 	    spaces = tab_after_sp = 0; len-- && isblank(*p); ++p)
1681b8ba871bSPeter Wemm 		if (*p == '\t') {
1682b8ba871bSPeter Wemm 			if (spaces)
1683b8ba871bSPeter Wemm 				tab_after_sp = 1;
1684b8ba871bSPeter Wemm 			scno += COL_OFF(scno, ts);
1685b8ba871bSPeter Wemm 		} else {
1686b8ba871bSPeter Wemm 			++spaces;
1687b8ba871bSPeter Wemm 			++scno;
1688b8ba871bSPeter Wemm 		}
1689b8ba871bSPeter Wemm 
1690b8ba871bSPeter Wemm 	/*
1691b8ba871bSPeter Wemm 	 * If there are no spaces, or no tabs after spaces and less than
1692b8ba871bSPeter Wemm 	 * ts spaces, it's already minimal.
1693110d525eSBaptiste Daroussin 	 * Keep analysing if expandtab is set.
1694b8ba871bSPeter Wemm 	 */
1695110d525eSBaptiste Daroussin 	if ((!spaces || (!tab_after_sp && spaces < ts)) &&
1696110d525eSBaptiste Daroussin 	    !O_ISSET(sp, O_EXPANDTAB))
1697b8ba871bSPeter Wemm 		return;
1698b8ba871bSPeter Wemm 
1699b8ba871bSPeter Wemm 	/* Count up spaces/tabs needed to get to the target. */
1700110d525eSBaptiste Daroussin 	cno = 0;
1701110d525eSBaptiste Daroussin 	tabs = 0;
1702110d525eSBaptiste Daroussin 	if (!O_ISSET(sp, O_EXPANDTAB)) {
1703110d525eSBaptiste Daroussin 		for (; cno + COL_OFF(cno, ts) <= scno; ++tabs)
1704b8ba871bSPeter Wemm 			cno += COL_OFF(cno, ts);
1705110d525eSBaptiste Daroussin 	}
1706b8ba871bSPeter Wemm 	spaces = scno - cno;
1707b8ba871bSPeter Wemm 
1708b8ba871bSPeter Wemm 	/*
1709b8ba871bSPeter Wemm 	 * Figure out how many characters we're dropping -- if we're not
1710b8ba871bSPeter Wemm 	 * dropping any, it's already minimal, we're done.
1711b8ba871bSPeter Wemm 	 */
1712b8ba871bSPeter Wemm 	old = p - tp->lb;
1713b8ba871bSPeter Wemm 	new = spaces + tabs;
1714b8ba871bSPeter Wemm 	if (old == new)
1715b8ba871bSPeter Wemm 		return;
1716b8ba871bSPeter Wemm 
1717b8ba871bSPeter Wemm 	/* Shift the rest of the characters down, adjust the counts. */
1718b8ba871bSPeter Wemm 	del = old - new;
1719f0957ccaSPeter Wemm 	MEMMOVE(p - del, p, tp->len - old);
1720b8ba871bSPeter Wemm 	tp->len -= del;
1721b8ba871bSPeter Wemm 	tp->cno -= del;
1722b8ba871bSPeter Wemm 
1723b8ba871bSPeter Wemm 	/* Fill in space/tab characters. */
1724b8ba871bSPeter Wemm 	for (p = tp->lb; tabs--;)
1725b8ba871bSPeter Wemm 		*p++ = '\t';
1726b8ba871bSPeter Wemm 	while (spaces--)
1727b8ba871bSPeter Wemm 		*p++ = ' ';
1728b8ba871bSPeter Wemm 	*changedp = 1;
1729b8ba871bSPeter Wemm }
1730b8ba871bSPeter Wemm 
1731b8ba871bSPeter Wemm /*
1732b8ba871bSPeter Wemm  * v_txt_auto --
1733b8ba871bSPeter Wemm  *	Handle autoindent.  If aitp isn't NULL, use it, otherwise,
1734b8ba871bSPeter Wemm  *	retrieve the line.
1735b8ba871bSPeter Wemm  *
1736c271fa92SBaptiste Daroussin  * PUBLIC: int v_txt_auto(SCR *, recno_t, TEXT *, size_t, TEXT *);
1737b8ba871bSPeter Wemm  */
1738b8ba871bSPeter Wemm int
v_txt_auto(SCR * sp,recno_t lno,TEXT * aitp,size_t len,TEXT * tp)1739f0957ccaSPeter Wemm v_txt_auto(SCR *sp, recno_t lno, TEXT *aitp, size_t len, TEXT *tp)
1740b8ba871bSPeter Wemm {
1741b8ba871bSPeter Wemm 	size_t nlen;
1742f0957ccaSPeter Wemm 	CHAR_T *p, *t;
1743b8ba871bSPeter Wemm 
1744b8ba871bSPeter Wemm 	if (aitp == NULL) {
1745b8ba871bSPeter Wemm 		/*
1746b8ba871bSPeter Wemm 		 * If the ex append command is executed with an address of 0,
1747b8ba871bSPeter Wemm 		 * it's possible to get here with a line number of 0.  Return
1748b8ba871bSPeter Wemm 		 * an indent of 0.
1749b8ba871bSPeter Wemm 		 */
1750b8ba871bSPeter Wemm 		if (lno == 0) {
1751b8ba871bSPeter Wemm 			tp->ai = 0;
1752b8ba871bSPeter Wemm 			return (0);
1753b8ba871bSPeter Wemm 		}
1754b8ba871bSPeter Wemm 		if (db_get(sp, lno, DBG_FATAL, &t, &len))
1755b8ba871bSPeter Wemm 			return (1);
1756b8ba871bSPeter Wemm 	} else
1757b8ba871bSPeter Wemm 		t = aitp->lb;
1758b8ba871bSPeter Wemm 
1759b8ba871bSPeter Wemm 	/* Count whitespace characters. */
1760b8ba871bSPeter Wemm 	for (p = t; len > 0; ++p, --len)
1761b8ba871bSPeter Wemm 		if (!isblank(*p))
1762b8ba871bSPeter Wemm 			break;
1763b8ba871bSPeter Wemm 
1764b8ba871bSPeter Wemm 	/* Set count, check for no indentation. */
1765b8ba871bSPeter Wemm 	if ((nlen = (p - t)) == 0)
1766b8ba871bSPeter Wemm 		return (0);
1767b8ba871bSPeter Wemm 
1768b8ba871bSPeter Wemm 	/* Make sure the buffer's big enough. */
1769f0957ccaSPeter Wemm 	BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + nlen);
1770b8ba871bSPeter Wemm 
1771b8ba871bSPeter Wemm 	/* Copy the buffer's current contents up. */
1772b8ba871bSPeter Wemm 	if (tp->len != 0)
1773f0957ccaSPeter Wemm 		MEMMOVE(tp->lb + nlen, tp->lb, tp->len);
1774b8ba871bSPeter Wemm 	tp->len += nlen;
1775b8ba871bSPeter Wemm 
1776b8ba871bSPeter Wemm 	/* Copy the indentation into the new buffer. */
1777f0957ccaSPeter Wemm 	MEMMOVE(tp->lb, t, nlen);
1778b8ba871bSPeter Wemm 
1779b8ba871bSPeter Wemm 	/* Set the autoindent count. */
1780b8ba871bSPeter Wemm 	tp->ai = nlen;
1781b8ba871bSPeter Wemm 	return (0);
1782b8ba871bSPeter Wemm }
1783b8ba871bSPeter Wemm 
1784b8ba871bSPeter Wemm /*
1785b8ba871bSPeter Wemm  * txt_backup --
1786b8ba871bSPeter Wemm  *	Back up to the previously edited line.
1787b8ba871bSPeter Wemm  */
1788b8ba871bSPeter Wemm static TEXT *
txt_backup(SCR * sp,TEXTH * tiqh,TEXT * tp,u_int32_t * flagsp)1789f0957ccaSPeter Wemm txt_backup(SCR *sp, TEXTH *tiqh, TEXT *tp, u_int32_t *flagsp)
1790b8ba871bSPeter Wemm {
1791b8ba871bSPeter Wemm 	VI_PRIVATE *vip;
1792b8ba871bSPeter Wemm 	TEXT *ntp;
1793b8ba871bSPeter Wemm 
1794b8ba871bSPeter Wemm 	/* Get a handle on the previous TEXT structure. */
1795f0957ccaSPeter Wemm 	if ((ntp = TAILQ_PREV(tp, _texth, q)) == NULL) {
1796b8ba871bSPeter Wemm 		if (!FL_ISSET(*flagsp, TXT_REPLAY))
1797b8ba871bSPeter Wemm 			msgq(sp, M_BERR,
1798b8ba871bSPeter Wemm 			    "193|Already at the beginning of the insert");
1799b8ba871bSPeter Wemm 		return (tp);
1800b8ba871bSPeter Wemm 	}
1801b8ba871bSPeter Wemm 
1802b8ba871bSPeter Wemm 	/* Bookkeeping. */
1803b8ba871bSPeter Wemm 	ntp->len = ntp->sv_len;
1804b8ba871bSPeter Wemm 
1805b8ba871bSPeter Wemm 	/* Handle appending to the line. */
1806b8ba871bSPeter Wemm 	vip = VIP(sp);
1807b8ba871bSPeter Wemm 	if (ntp->owrite == 0 && ntp->insert == 0) {
1808b8ba871bSPeter Wemm 		ntp->lb[ntp->len] = CH_CURSOR;
1809b8ba871bSPeter Wemm 		++ntp->insert;
1810b8ba871bSPeter Wemm 		++ntp->len;
1811b8ba871bSPeter Wemm 		FL_SET(*flagsp, TXT_APPENDEOL);
1812b8ba871bSPeter Wemm 	} else
1813b8ba871bSPeter Wemm 		FL_CLR(*flagsp, TXT_APPENDEOL);
1814b8ba871bSPeter Wemm 
1815b8ba871bSPeter Wemm 	/* Release the current TEXT. */
1816f0957ccaSPeter Wemm 	TAILQ_REMOVE(tiqh, tp, q);
1817b8ba871bSPeter Wemm 	text_free(tp);
1818b8ba871bSPeter Wemm 
1819b8ba871bSPeter Wemm 	/* Update the old line on the screen. */
1820b8ba871bSPeter Wemm 	if (vs_change(sp, ntp->lno + 1, LINE_DELETE))
1821b8ba871bSPeter Wemm 		return (NULL);
1822b8ba871bSPeter Wemm 
1823b8ba871bSPeter Wemm 	/* Return the new/current TEXT. */
1824b8ba871bSPeter Wemm 	return (ntp);
1825b8ba871bSPeter Wemm }
1826b8ba871bSPeter Wemm 
1827b8ba871bSPeter Wemm /*
1828b8ba871bSPeter Wemm  * Text indentation is truly strange.  ^T and ^D do movements to the next or
1829b8ba871bSPeter Wemm  * previous shiftwidth value, i.e. for a 1-based numbering, with shiftwidth=3,
1830b8ba871bSPeter Wemm  * ^T moves a cursor on the 7th, 8th or 9th column to the 10th column, and ^D
1831b8ba871bSPeter Wemm  * moves it back.
1832b8ba871bSPeter Wemm  *
1833b8ba871bSPeter Wemm  * !!!
1834b8ba871bSPeter Wemm  * The ^T and ^D characters in historical vi had special meaning only when they
1835b8ba871bSPeter Wemm  * were the first characters entered after entering text input mode.  As normal
1836b8ba871bSPeter Wemm  * erase characters couldn't erase autoindent characters (^T in this case), it
1837b8ba871bSPeter Wemm  * meant that inserting text into previously existing text was strange -- ^T
1838b8ba871bSPeter Wemm  * only worked if it was the first keystroke(s), and then could only be erased
1839b8ba871bSPeter Wemm  * using ^D.  This implementation treats ^T specially anywhere it occurs in the
1840b8ba871bSPeter Wemm  * input, and permits the standard erase characters to erase the characters it
1841b8ba871bSPeter Wemm  * inserts.
1842b8ba871bSPeter Wemm  *
1843b8ba871bSPeter Wemm  * !!!
1844b8ba871bSPeter Wemm  * A fun test is to try:
1845b8ba871bSPeter Wemm  *	:se sw=4 ai list
1846b8ba871bSPeter Wemm  *	i<CR>^Tx<CR>^Tx<CR>^Tx<CR>^Dx<CR>^Dx<CR>^Dx<esc>
1847b8ba871bSPeter Wemm  * Historic vi loses some of the '$' marks on the line ends, but otherwise gets
1848b8ba871bSPeter Wemm  * it right.
1849b8ba871bSPeter Wemm  *
1850b8ba871bSPeter Wemm  * XXX
1851b8ba871bSPeter Wemm  * Technically, txt_dent should be part of the screen interface, as it requires
1852b8ba871bSPeter Wemm  * knowledge of character sizes, including <space>s, on the screen.  It's here
1853b8ba871bSPeter Wemm  * because it's a complicated little beast, and I didn't want to shove it down
1854f0957ccaSPeter Wemm  * into the screen.  It's probable that KEY_COL will call into the screen once
1855b8ba871bSPeter Wemm  * there are screens with different character representations.
1856b8ba871bSPeter Wemm  *
1857b8ba871bSPeter Wemm  * txt_dent --
1858b8ba871bSPeter Wemm  *	Handle ^T indents, ^D outdents.
1859b8ba871bSPeter Wemm  *
1860b8ba871bSPeter Wemm  * If anything changes here, check the ex version to see if it needs similar
1861b8ba871bSPeter Wemm  * changes.
1862b8ba871bSPeter Wemm  */
1863b8ba871bSPeter Wemm static int
txt_dent(SCR * sp,TEXT * tp,int swopt,int isindent)1864110d525eSBaptiste Daroussin txt_dent(SCR *sp, TEXT *tp, int swopt, int isindent)
1865b8ba871bSPeter Wemm {
1866b8ba871bSPeter Wemm 	CHAR_T ch;
1867b8ba871bSPeter Wemm 	u_long sw, ts;
1868f0957ccaSPeter Wemm 	size_t cno, current, spaces, target, tabs;
1869b8ba871bSPeter Wemm 	int ai_reset;
1870b8ba871bSPeter Wemm 
1871b8ba871bSPeter Wemm 	ts = O_VAL(sp, O_TABSTOP);
1872110d525eSBaptiste Daroussin 	sw = O_VAL(sp, swopt);
1873b8ba871bSPeter Wemm 
1874b8ba871bSPeter Wemm 	/*
1875b8ba871bSPeter Wemm 	 * Since we don't know what precedes the character(s) being inserted
1876b8ba871bSPeter Wemm 	 * (or deleted), the preceding whitespace characters must be resolved.
1877b8ba871bSPeter Wemm 	 * An example is a <tab>, which doesn't need a full shiftwidth number
1878b8ba871bSPeter Wemm 	 * of columns because it's preceded by <space>s.  This is easy to get
1879b8ba871bSPeter Wemm 	 * if the user sets shiftwidth to a value less than tabstop (or worse,
1880b8ba871bSPeter Wemm 	 * something for which tabstop isn't a multiple) and then uses ^T to
1881b8ba871bSPeter Wemm 	 * indent, and ^D to outdent.
1882b8ba871bSPeter Wemm 	 *
1883b8ba871bSPeter Wemm 	 * Figure out the current and target screen columns.  In the historic
1884b8ba871bSPeter Wemm 	 * vi, the autoindent column was NOT determined using display widths
1885b8ba871bSPeter Wemm 	 * of characters as was the wrapmargin column.  For that reason, we
1886b8ba871bSPeter Wemm 	 * can't use the vs_column() function, but have to calculate it here.
1887b8ba871bSPeter Wemm 	 * This is slow, but it's normally only on the first few characters of
1888b8ba871bSPeter Wemm 	 * a line.
1889b8ba871bSPeter Wemm 	 */
1890b8ba871bSPeter Wemm 	for (current = cno = 0; cno < tp->cno; ++cno)
1891b8ba871bSPeter Wemm 		current += tp->lb[cno] == '\t' ?
1892f0957ccaSPeter Wemm 		    COL_OFF(current, ts) : KEY_COL(sp, tp->lb[cno]);
1893b8ba871bSPeter Wemm 
1894b8ba871bSPeter Wemm 	target = current;
1895b8ba871bSPeter Wemm 	if (isindent)
1896b8ba871bSPeter Wemm 		target += COL_OFF(target, sw);
1897d21f31a1SDimitry Andric 	else {
1898d21f31a1SDimitry Andric 		--target;
1899d21f31a1SDimitry Andric 		target -= target % sw;
1900d21f31a1SDimitry Andric 	}
1901b8ba871bSPeter Wemm 
1902b8ba871bSPeter Wemm 	/*
1903b8ba871bSPeter Wemm 	 * The AI characters will be turned into overwrite characters if the
1904b8ba871bSPeter Wemm 	 * cursor immediately follows them.  We test both the cursor position
1905b8ba871bSPeter Wemm 	 * and the indent flag because there's no single test.  (^T can only
1906b8ba871bSPeter Wemm 	 * be detected by the cursor position, and while we know that the test
1907b8ba871bSPeter Wemm 	 * is always true for ^D, the cursor can be in more than one place, as
1908b8ba871bSPeter Wemm 	 * "0^D" and "^D" are different.)
1909b8ba871bSPeter Wemm 	 */
1910b8ba871bSPeter Wemm 	ai_reset = !isindent || tp->cno == tp->ai + tp->offset;
1911b8ba871bSPeter Wemm 
1912b8ba871bSPeter Wemm 	/*
1913b8ba871bSPeter Wemm 	 * Back up over any previous <blank> characters, changing them into
1914b8ba871bSPeter Wemm 	 * overwrite characters (including any ai characters).  Then figure
1915b8ba871bSPeter Wemm 	 * out the current screen column.
1916b8ba871bSPeter Wemm 	 */
1917b8ba871bSPeter Wemm 	for (; tp->cno > tp->offset &&
1918b8ba871bSPeter Wemm 	    (tp->lb[tp->cno - 1] == ' ' || tp->lb[tp->cno - 1] == '\t');
1919b8ba871bSPeter Wemm 	    --tp->cno, ++tp->owrite);
1920b8ba871bSPeter Wemm 	for (current = cno = 0; cno < tp->cno; ++cno)
1921b8ba871bSPeter Wemm 		current += tp->lb[cno] == '\t' ?
1922f0957ccaSPeter Wemm 		    COL_OFF(current, ts) : KEY_COL(sp, tp->lb[cno]);
1923b8ba871bSPeter Wemm 
1924b8ba871bSPeter Wemm 	/*
1925b8ba871bSPeter Wemm 	 * If we didn't move up to or past the target, it's because there
1926b8ba871bSPeter Wemm 	 * weren't enough characters to delete, e.g. the first character
1927b8ba871bSPeter Wemm 	 * of the line was a tp->offset character, and the user entered
1928b8ba871bSPeter Wemm 	 * ^D to move to the beginning of a line.  An example of this is:
1929b8ba871bSPeter Wemm 	 *
1930b8ba871bSPeter Wemm 	 *	:set ai sw=4<cr>i<space>a<esc>i^T^D
1931b8ba871bSPeter Wemm 	 *
1932b8ba871bSPeter Wemm 	 * Otherwise, count up the total spaces/tabs needed to get from the
1933b8ba871bSPeter Wemm 	 * beginning of the line (or the last non-<blank> character) to the
1934b8ba871bSPeter Wemm 	 * target.
1935b8ba871bSPeter Wemm 	 */
1936b8ba871bSPeter Wemm 	if (current >= target)
1937b8ba871bSPeter Wemm 		spaces = tabs = 0;
1938b8ba871bSPeter Wemm 	else {
1939110d525eSBaptiste Daroussin 		cno = current;
1940110d525eSBaptiste Daroussin 		tabs = 0;
1941110d525eSBaptiste Daroussin 		if (!O_ISSET(sp, O_EXPANDTAB)) {
1942110d525eSBaptiste Daroussin 			for (; cno + COL_OFF(cno, ts) <= target; ++tabs)
1943b8ba871bSPeter Wemm 				cno += COL_OFF(cno, ts);
1944110d525eSBaptiste Daroussin 		}
1945b8ba871bSPeter Wemm 		spaces = target - cno;
1946b8ba871bSPeter Wemm 	}
1947b8ba871bSPeter Wemm 
1948b8ba871bSPeter Wemm 	/* If we overwrote ai characters, reset the ai count. */
1949b8ba871bSPeter Wemm 	if (ai_reset)
1950b8ba871bSPeter Wemm 		tp->ai = tabs + spaces;
1951b8ba871bSPeter Wemm 
1952b8ba871bSPeter Wemm 	/*
1953b8ba871bSPeter Wemm 	 * Call txt_insch() to insert each character, so that we get the
1954b8ba871bSPeter Wemm 	 * correct effect when we add a <tab> to replace N <spaces>.
1955b8ba871bSPeter Wemm 	 */
1956b8ba871bSPeter Wemm 	for (ch = '\t'; tabs > 0; --tabs)
1957b8ba871bSPeter Wemm 		(void)txt_insch(sp, tp, &ch, 0);
1958b8ba871bSPeter Wemm 	for (ch = ' '; spaces > 0; --spaces)
1959b8ba871bSPeter Wemm 		(void)txt_insch(sp, tp, &ch, 0);
1960b8ba871bSPeter Wemm 	return (0);
1961b8ba871bSPeter Wemm }
1962b8ba871bSPeter Wemm 
1963b8ba871bSPeter Wemm /*
1964b8ba871bSPeter Wemm  * txt_fc --
1965f0957ccaSPeter Wemm  *	File name and ex command completion.
1966b8ba871bSPeter Wemm  */
1967b8ba871bSPeter Wemm static int
txt_fc(SCR * sp,TEXT * tp,int * redrawp)1968f0957ccaSPeter Wemm txt_fc(SCR *sp, TEXT *tp, int *redrawp)
1969b8ba871bSPeter Wemm {
1970b8ba871bSPeter Wemm 	struct stat sb;
1971b8ba871bSPeter Wemm 	ARGS **argv;
1972b8ba871bSPeter Wemm 	EXCMD cmd;
1973b8ba871bSPeter Wemm 	size_t indx, len, nlen, off;
1974f0957ccaSPeter Wemm 	int argc;
1975f0957ccaSPeter Wemm 	CHAR_T *p, *t, *bp;
1976f0957ccaSPeter Wemm 	char *np, *epd = NULL;
1977f0957ccaSPeter Wemm 	size_t nplen;
1978f0957ccaSPeter Wemm 	int fstwd = 1;
1979b8ba871bSPeter Wemm 
1980b8ba871bSPeter Wemm 	*redrawp = 0;
1981f0957ccaSPeter Wemm 	ex_cinit(sp, &cmd, 0, 0, OOBLNO, OOBLNO, 0);
1982b8ba871bSPeter Wemm 
1983b8ba871bSPeter Wemm 	/*
1984b8ba871bSPeter Wemm 	 * Find the beginning of this "word" -- if we're at the beginning
1985b8ba871bSPeter Wemm 	 * of the line, it's a special case.
1986b8ba871bSPeter Wemm 	 */
1987b8ba871bSPeter Wemm 	if (tp->cno == 1) {
1988b8ba871bSPeter Wemm 		len = 0;
1989b8ba871bSPeter Wemm 		p = tp->lb;
1990f0957ccaSPeter Wemm 	} else {
1991f0957ccaSPeter Wemm 		CHAR_T *ap;
1992f0957ccaSPeter Wemm 
1993f0957ccaSPeter Wemm 		for (len = 0,
1994f0957ccaSPeter Wemm 		    off = MAX(tp->ai, tp->offset), ap = tp->lb + off, p = ap;
1995f0957ccaSPeter Wemm 		    off < tp->cno; ++off, ++ap) {
1996f0957ccaSPeter Wemm 			if (IS_ESCAPE(sp, &cmd, *ap)) {
1997f0957ccaSPeter Wemm 				if (++off == tp->cno)
1998f0957ccaSPeter Wemm 					break;
1999f0957ccaSPeter Wemm 				++ap;
2000f0957ccaSPeter Wemm 				len += 2;
2001f0957ccaSPeter Wemm 			} else if (cmdskip(*ap)) {
2002f0957ccaSPeter Wemm 				p = ap + 1;
2003f0957ccaSPeter Wemm 				if (len > 0)
2004f0957ccaSPeter Wemm 					fstwd = 0;
2005f0957ccaSPeter Wemm 				len = 0;
2006b8ba871bSPeter Wemm 			} else
2007b8ba871bSPeter Wemm 				++len;
2008f0957ccaSPeter Wemm 		}
2009b8ba871bSPeter Wemm 	}
2010b8ba871bSPeter Wemm 
2011b8ba871bSPeter Wemm 	/*
2012f0957ccaSPeter Wemm 	 * If we are at the first word, do ex command completion instead of
2013f0957ccaSPeter Wemm 	 * file name completion.
2014b8ba871bSPeter Wemm 	 */
2015f0957ccaSPeter Wemm 	if (fstwd)
2016f0957ccaSPeter Wemm 		(void)argv_flt_ex(sp, &cmd, p, len);
2017f0957ccaSPeter Wemm 	else {
2018f0957ccaSPeter Wemm 		if ((bp = argv_uesc(sp, &cmd, p, len)) == NULL)
2019b8ba871bSPeter Wemm 			return (1);
2020f0957ccaSPeter Wemm 		if (argv_flt_path(sp, &cmd, bp, STRLEN(bp))) {
2021f0957ccaSPeter Wemm 			FREE_SPACEW(sp, bp, 0);
2022b8ba871bSPeter Wemm 			return (0);
2023b8ba871bSPeter Wemm 		}
2024f0957ccaSPeter Wemm 		FREE_SPACEW(sp, bp, 0);
2025f0957ccaSPeter Wemm 	}
2026b8ba871bSPeter Wemm 	argc = cmd.argc;
2027b8ba871bSPeter Wemm 	argv = cmd.argv;
2028b8ba871bSPeter Wemm 
2029b8ba871bSPeter Wemm 	switch (argc) {
2030b8ba871bSPeter Wemm 	case 0:				/* No matches. */
2031b8ba871bSPeter Wemm 		(void)sp->gp->scr_bell(sp);
2032b8ba871bSPeter Wemm 		return (0);
2033b8ba871bSPeter Wemm 	case 1:				/* One match. */
2034f0957ccaSPeter Wemm 		/* Always overwrite the old text. */
2035f0957ccaSPeter Wemm 		nlen = STRLEN(cmd.argv[0]->bp);
2036b8ba871bSPeter Wemm 		break;
2037b8ba871bSPeter Wemm 	default:			/* Multiple matches. */
2038b8ba871bSPeter Wemm 		*redrawp = 1;
2039b8ba871bSPeter Wemm 		if (txt_fc_col(sp, argc, argv))
2040b8ba871bSPeter Wemm 			return (1);
2041b8ba871bSPeter Wemm 
2042b8ba871bSPeter Wemm 		/* Find the length of the shortest match. */
2043b8ba871bSPeter Wemm 		for (nlen = cmd.argv[0]->len; --argc > 0;) {
2044b8ba871bSPeter Wemm 			if (cmd.argv[argc]->len < nlen)
2045b8ba871bSPeter Wemm 				nlen = cmd.argv[argc]->len;
2046b8ba871bSPeter Wemm 			for (indx = 0; indx < nlen &&
2047b8ba871bSPeter Wemm 			    cmd.argv[argc]->bp[indx] == cmd.argv[0]->bp[indx];
2048b8ba871bSPeter Wemm 			    ++indx);
2049b8ba871bSPeter Wemm 			nlen = indx;
2050b8ba871bSPeter Wemm 		}
2051b8ba871bSPeter Wemm 		break;
2052b8ba871bSPeter Wemm 	}
2053b8ba871bSPeter Wemm 
2054f0957ccaSPeter Wemm 	/* Escape the matched part of the path. */
2055f0957ccaSPeter Wemm 	if (fstwd)
2056f0957ccaSPeter Wemm 		bp = cmd.argv[0]->bp;
2057f0957ccaSPeter Wemm 	else {
2058f0957ccaSPeter Wemm 		if ((bp = argv_esc(sp, &cmd, cmd.argv[0]->bp, nlen)) == NULL)
2059f0957ccaSPeter Wemm 			return (1);
2060f0957ccaSPeter Wemm 		nlen = STRLEN(bp);
2061f0957ccaSPeter Wemm 	}
2062f0957ccaSPeter Wemm 
2063b8ba871bSPeter Wemm 	/* Overwrite the expanded text first. */
2064f0957ccaSPeter Wemm 	for (t = bp; len > 0 && nlen > 0; --len, --nlen)
2065b8ba871bSPeter Wemm 		*p++ = *t++;
2066b8ba871bSPeter Wemm 
2067b8ba871bSPeter Wemm 	/* If lost text, make the remaining old text overwrite characters. */
2068b8ba871bSPeter Wemm 	if (len) {
2069b8ba871bSPeter Wemm 		tp->cno -= len;
2070b8ba871bSPeter Wemm 		tp->owrite += len;
2071b8ba871bSPeter Wemm 	}
2072b8ba871bSPeter Wemm 
2073b8ba871bSPeter Wemm 	/* Overwrite any overwrite characters next. */
2074b8ba871bSPeter Wemm 	for (; nlen > 0 && tp->owrite > 0; --nlen, --tp->owrite, ++tp->cno)
2075b8ba871bSPeter Wemm 		*p++ = *t++;
2076b8ba871bSPeter Wemm 
2077b8ba871bSPeter Wemm 	/* Shift remaining text up, and move the cursor to the end. */
2078b8ba871bSPeter Wemm 	if (nlen) {
2079b8ba871bSPeter Wemm 		off = p - tp->lb;
2080f0957ccaSPeter Wemm 		BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + nlen);
2081b8ba871bSPeter Wemm 		p = tp->lb + off;
2082b8ba871bSPeter Wemm 
2083b8ba871bSPeter Wemm 		tp->cno += nlen;
2084b8ba871bSPeter Wemm 		tp->len += nlen;
2085b8ba871bSPeter Wemm 
2086b8ba871bSPeter Wemm 		if (tp->insert != 0)
2087f0957ccaSPeter Wemm 			(void)MEMMOVE(p + nlen, p, tp->insert);
2088b8ba871bSPeter Wemm 		while (nlen--)
2089b8ba871bSPeter Wemm 			*p++ = *t++;
2090b8ba871bSPeter Wemm 	}
2091b8ba871bSPeter Wemm 
2092f0957ccaSPeter Wemm 	if (!fstwd)
2093f0957ccaSPeter Wemm 		FREE_SPACEW(sp, bp, 0);
2094f0957ccaSPeter Wemm 
2095f0957ccaSPeter Wemm 	/* If not a single match of path, we've done. */
2096f0957ccaSPeter Wemm 	if (argc != 1 || fstwd)
2097f0957ccaSPeter Wemm 		return (0);
2098f0957ccaSPeter Wemm 
2099f0957ccaSPeter Wemm 	/* If a single match and it's a directory, append a '/'. */
2100f0957ccaSPeter Wemm 	INT2CHAR(sp, cmd.argv[0]->bp, cmd.argv[0]->len + 1, np, nplen);
2101f0957ccaSPeter Wemm 	if ((epd = expanduser(np)) != NULL)
2102f0957ccaSPeter Wemm 		np = epd;
2103f0957ccaSPeter Wemm 	if (!stat(np, &sb) && S_ISDIR(sb.st_mode)) {
2104f0957ccaSPeter Wemm 		if (tp->owrite == 0) {
2105b8ba871bSPeter Wemm 			off = p - tp->lb;
2106f0957ccaSPeter Wemm 			BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + 1);
2107b8ba871bSPeter Wemm 			p = tp->lb + off;
2108b8ba871bSPeter Wemm 			if (tp->insert != 0)
2109f0957ccaSPeter Wemm 				(void)MEMMOVE(p + 1, p, tp->insert);
2110b8ba871bSPeter Wemm 			++tp->len;
2111b8ba871bSPeter Wemm 		} else
2112b8ba871bSPeter Wemm 			--tp->owrite;
2113b8ba871bSPeter Wemm 
2114b8ba871bSPeter Wemm 		++tp->cno;
2115b8ba871bSPeter Wemm 		*p++ = '/';
2116b8ba871bSPeter Wemm 	}
2117f0957ccaSPeter Wemm 	free(epd);
2118b8ba871bSPeter Wemm 	return (0);
2119b8ba871bSPeter Wemm }
2120b8ba871bSPeter Wemm 
2121b8ba871bSPeter Wemm /*
2122b8ba871bSPeter Wemm  * txt_fc_col --
2123b8ba871bSPeter Wemm  *	Display file names for file name completion.
2124b8ba871bSPeter Wemm  */
2125b8ba871bSPeter Wemm static int
txt_fc_col(SCR * sp,int argc,ARGS ** argv)2126f0957ccaSPeter Wemm txt_fc_col(SCR *sp, int argc, ARGS **argv)
2127b8ba871bSPeter Wemm {
2128b8ba871bSPeter Wemm 	ARGS **av;
2129b8ba871bSPeter Wemm 	CHAR_T *p;
2130b8ba871bSPeter Wemm 	GS *gp;
2131b8ba871bSPeter Wemm 	size_t base, cnt, col, colwidth, numrows, numcols, prefix, row;
2132b8ba871bSPeter Wemm 	int ac, nf, reset;
2133f0957ccaSPeter Wemm 	char *np, *pp;
2134f0957ccaSPeter Wemm 	size_t nlen;
2135b8ba871bSPeter Wemm 
2136b8ba871bSPeter Wemm 	gp = sp->gp;
2137b8ba871bSPeter Wemm 
2138b8ba871bSPeter Wemm 	/* Trim any directory prefix common to all of the files. */
2139f0957ccaSPeter Wemm 	INT2CHAR(sp, argv[0]->bp, argv[0]->len + 1, np, nlen);
2140f0957ccaSPeter Wemm 	if ((pp = strrchr(np, '/')) == NULL)
2141b8ba871bSPeter Wemm 		prefix = 0;
2142b8ba871bSPeter Wemm 	else {
2143f0957ccaSPeter Wemm 		prefix = (pp - np) + 1;
2144b8ba871bSPeter Wemm 		for (ac = argc - 1, av = argv + 1; ac > 0; --ac, ++av)
2145b8ba871bSPeter Wemm 			if (av[0]->len < prefix ||
2146f0957ccaSPeter Wemm 			    MEMCMP(av[0]->bp, argv[0]->bp,
2147f0957ccaSPeter Wemm 				   prefix)) {
2148b8ba871bSPeter Wemm 				prefix = 0;
2149b8ba871bSPeter Wemm 				break;
2150b8ba871bSPeter Wemm 			}
2151b8ba871bSPeter Wemm 	}
2152b8ba871bSPeter Wemm 
2153b8ba871bSPeter Wemm 	/*
2154b8ba871bSPeter Wemm 	 * Figure out the column width for the longest name.  Output is done on
2155b8ba871bSPeter Wemm 	 * 6 character "tab" boundaries for no particular reason.  (Since we
2156b8ba871bSPeter Wemm 	 * don't output tab characters, we ignore the terminal's tab settings.)
2157b8ba871bSPeter Wemm 	 * Ignore the user's tab setting because we have no idea how reasonable
2158b8ba871bSPeter Wemm 	 * it is.
2159b8ba871bSPeter Wemm 	 */
2160b8ba871bSPeter Wemm 	for (ac = argc, av = argv, colwidth = 0; ac > 0; --ac, ++av) {
2161b8ba871bSPeter Wemm 		for (col = 0, p = av[0]->bp + prefix; *p != '\0'; ++p)
2162f0957ccaSPeter Wemm 			col += KEY_COL(sp, *p);
2163b8ba871bSPeter Wemm 		if (col > colwidth)
2164b8ba871bSPeter Wemm 			colwidth = col;
2165b8ba871bSPeter Wemm 	}
2166b8ba871bSPeter Wemm 	colwidth += COL_OFF(colwidth, 6);
2167b8ba871bSPeter Wemm 
2168b8ba871bSPeter Wemm 	/*
2169b8ba871bSPeter Wemm 	 * Writing to the bottom line of the screen is always turned off when
2170b8ba871bSPeter Wemm 	 * SC_TINPUT_INFO is set.  Turn it back on, we know what we're doing.
2171b8ba871bSPeter Wemm 	 */
2172b8ba871bSPeter Wemm 	if (F_ISSET(sp, SC_TINPUT_INFO)) {
2173b8ba871bSPeter Wemm 		reset = 1;
2174b8ba871bSPeter Wemm 		F_CLR(sp, SC_TINPUT_INFO);
2175b8ba871bSPeter Wemm 	} else
2176b8ba871bSPeter Wemm 		reset = 0;
2177b8ba871bSPeter Wemm 
2178b8ba871bSPeter Wemm #define	CHK_INTR							\
2179b8ba871bSPeter Wemm 	if (F_ISSET(gp, G_INTERRUPTED))					\
2180b8ba871bSPeter Wemm 		goto intr;
2181b8ba871bSPeter Wemm 
2182b8ba871bSPeter Wemm 	/* If the largest file name is too large, just print them. */
2183f0957ccaSPeter Wemm 	if (colwidth >= sp->cols) {
2184b8ba871bSPeter Wemm 		for (ac = argc, av = argv; ac > 0; --ac, ++av) {
2185f0957ccaSPeter Wemm 			INT2CHAR(sp, av[0]->bp+prefix, av[0]->len+1-prefix,
2186f0957ccaSPeter Wemm 				 np, nlen);
2187f0957ccaSPeter Wemm 			pp = msg_print(sp, np, &nf);
2188f0957ccaSPeter Wemm 			(void)ex_printf(sp, "%s\n", pp);
2189f0957ccaSPeter Wemm 			if (nf)
2190f0957ccaSPeter Wemm 				FREE_SPACE(sp, pp, 0);
2191b8ba871bSPeter Wemm 			if (F_ISSET(gp, G_INTERRUPTED))
2192b8ba871bSPeter Wemm 				break;
2193b8ba871bSPeter Wemm 		}
2194b8ba871bSPeter Wemm 		CHK_INTR;
2195b8ba871bSPeter Wemm 	} else {
2196b8ba871bSPeter Wemm 		/* Figure out the number of columns. */
2197b8ba871bSPeter Wemm 		numcols = (sp->cols - 1) / colwidth;
2198b8ba871bSPeter Wemm 		if (argc > numcols) {
2199b8ba871bSPeter Wemm 			numrows = argc / numcols;
2200b8ba871bSPeter Wemm 			if (argc % numcols)
2201b8ba871bSPeter Wemm 				++numrows;
2202b8ba871bSPeter Wemm 		} else
2203b8ba871bSPeter Wemm 			numrows = 1;
2204b8ba871bSPeter Wemm 
2205b8ba871bSPeter Wemm 		/* Display the files in sorted order. */
2206b8ba871bSPeter Wemm 		for (row = 0; row < numrows; ++row) {
2207b8ba871bSPeter Wemm 			for (base = row, col = 0; col < numcols; ++col) {
2208f0957ccaSPeter Wemm 				INT2CHAR(sp, argv[base]->bp+prefix,
2209f0957ccaSPeter Wemm 					argv[base]->len+1-prefix, np, nlen);
2210f0957ccaSPeter Wemm 				pp = msg_print(sp, np, &nf);
2211f0957ccaSPeter Wemm 				cnt = ex_printf(sp, "%s", pp);
2212b8ba871bSPeter Wemm 				if (nf)
2213f0957ccaSPeter Wemm 					FREE_SPACE(sp, pp, 0);
2214b8ba871bSPeter Wemm 				CHK_INTR;
2215b8ba871bSPeter Wemm 				if ((base += numrows) >= argc)
2216b8ba871bSPeter Wemm 					break;
2217b8ba871bSPeter Wemm 				(void)ex_printf(sp,
2218b8ba871bSPeter Wemm 				    "%*s", (int)(colwidth - cnt), "");
2219b8ba871bSPeter Wemm 				CHK_INTR;
2220b8ba871bSPeter Wemm 			}
2221b8ba871bSPeter Wemm 			(void)ex_puts(sp, "\n");
2222b8ba871bSPeter Wemm 			CHK_INTR;
2223b8ba871bSPeter Wemm 		}
2224b8ba871bSPeter Wemm 		(void)ex_puts(sp, "\n");
2225b8ba871bSPeter Wemm 		CHK_INTR;
2226b8ba871bSPeter Wemm 	}
2227b8ba871bSPeter Wemm 	(void)ex_fflush(sp);
2228b8ba871bSPeter Wemm 
2229b8ba871bSPeter Wemm 	if (0) {
2230b8ba871bSPeter Wemm intr:		F_CLR(gp, G_INTERRUPTED);
2231b8ba871bSPeter Wemm 	}
2232b8ba871bSPeter Wemm 	if (reset)
2233b8ba871bSPeter Wemm 		F_SET(sp, SC_TINPUT_INFO);
2234b8ba871bSPeter Wemm 
2235b8ba871bSPeter Wemm 	return (0);
2236b8ba871bSPeter Wemm }
2237b8ba871bSPeter Wemm 
2238b8ba871bSPeter Wemm /*
2239b8ba871bSPeter Wemm  * txt_emark --
2240b8ba871bSPeter Wemm  *	Set the end mark on the line.
2241b8ba871bSPeter Wemm  */
2242b8ba871bSPeter Wemm static int
txt_emark(SCR * sp,TEXT * tp,size_t cno)2243f0957ccaSPeter Wemm txt_emark(SCR *sp, TEXT *tp, size_t cno)
2244b8ba871bSPeter Wemm {
2245f0957ccaSPeter Wemm 	CHAR_T ch;
2246f0957ccaSPeter Wemm 	u_char *kp;
2247b8ba871bSPeter Wemm 	size_t chlen, nlen, olen;
2248f0957ccaSPeter Wemm 	CHAR_T *p;
2249b8ba871bSPeter Wemm 
2250b8ba871bSPeter Wemm 	ch = CH_ENDMARK;
2251b8ba871bSPeter Wemm 
2252b8ba871bSPeter Wemm 	/*
2253b8ba871bSPeter Wemm 	 * The end mark may not be the same size as the current character.
2254b8ba871bSPeter Wemm 	 * Don't let the line shift.
2255b8ba871bSPeter Wemm 	 */
2256f0957ccaSPeter Wemm 	nlen = KEY_COL(sp, ch);
2257b8ba871bSPeter Wemm 	if (tp->lb[cno] == '\t')
2258b8ba871bSPeter Wemm 		(void)vs_columns(sp, tp->lb, tp->lno, &cno, &olen);
2259b8ba871bSPeter Wemm 	else
2260f0957ccaSPeter Wemm 		olen = KEY_COL(sp, tp->lb[cno]);
2261b8ba871bSPeter Wemm 
2262b8ba871bSPeter Wemm 	/*
2263b8ba871bSPeter Wemm 	 * If the line got longer, well, it's weird, but it's easy.  If
2264b8ba871bSPeter Wemm 	 * it's the same length, it's easy.  If it got shorter, we have
2265b8ba871bSPeter Wemm 	 * to fix it up.
2266b8ba871bSPeter Wemm 	 */
2267b8ba871bSPeter Wemm 	if (olen > nlen) {
2268f0957ccaSPeter Wemm 		BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + olen);
2269b8ba871bSPeter Wemm 		chlen = olen - nlen;
2270b8ba871bSPeter Wemm 		if (tp->insert != 0)
2271f0957ccaSPeter Wemm 			MEMMOVE(tp->lb + cno + 1 + chlen,
2272b8ba871bSPeter Wemm 			    tp->lb + cno + 1, tp->insert);
2273b8ba871bSPeter Wemm 
2274b8ba871bSPeter Wemm 		tp->len += chlen;
2275b8ba871bSPeter Wemm 		tp->owrite += chlen;
2276b8ba871bSPeter Wemm 		p = tp->lb + cno;
2277f0957ccaSPeter Wemm 		if (tp->lb[cno] == '\t' ||
2278f0957ccaSPeter Wemm 		    KEY_NEEDSWIDE(sp, tp->lb[cno]))
2279b8ba871bSPeter Wemm 			for (cno += chlen; chlen--;)
2280b8ba871bSPeter Wemm 				*p++ = ' ';
2281b8ba871bSPeter Wemm 		else
2282f0957ccaSPeter Wemm 			for (kp = (u_char *)
2283f0957ccaSPeter Wemm 			    KEY_NAME(sp, tp->lb[cno]),
2284b8ba871bSPeter Wemm 			    cno += chlen; chlen--;)
2285b8ba871bSPeter Wemm 				*p++ = *kp++;
2286b8ba871bSPeter Wemm 	}
2287b8ba871bSPeter Wemm 	tp->lb[cno] = ch;
2288b8ba871bSPeter Wemm 	return (vs_change(sp, tp->lno, LINE_RESET));
2289b8ba871bSPeter Wemm }
2290b8ba871bSPeter Wemm 
2291b8ba871bSPeter Wemm /*
2292b8ba871bSPeter Wemm  * txt_err --
2293b8ba871bSPeter Wemm  *	Handle an error during input processing.
2294b8ba871bSPeter Wemm  */
2295b8ba871bSPeter Wemm static void
txt_err(SCR * sp,TEXTH * tiqh)2296f0957ccaSPeter Wemm txt_err(SCR *sp, TEXTH *tiqh)
2297b8ba871bSPeter Wemm {
2298b8ba871bSPeter Wemm 	recno_t lno;
2299b8ba871bSPeter Wemm 
2300b8ba871bSPeter Wemm 	/*
2301b8ba871bSPeter Wemm 	 * The problem with input processing is that the cursor is at an
2302b8ba871bSPeter Wemm 	 * indeterminate position since some input may have been lost due
2303b8ba871bSPeter Wemm 	 * to a malloc error.  So, try to go back to the place from which
2304b8ba871bSPeter Wemm 	 * the cursor started, knowing that it may no longer be available.
2305b8ba871bSPeter Wemm 	 *
2306b8ba871bSPeter Wemm 	 * We depend on at least one line number being set in the text
2307b8ba871bSPeter Wemm 	 * chain.
2308b8ba871bSPeter Wemm 	 */
2309f0957ccaSPeter Wemm 	for (lno = TAILQ_FIRST(tiqh)->lno;
2310b8ba871bSPeter Wemm 	    !db_exist(sp, lno) && lno > 0; --lno);
2311b8ba871bSPeter Wemm 
2312b8ba871bSPeter Wemm 	sp->lno = lno == 0 ? 1 : lno;
2313b8ba871bSPeter Wemm 	sp->cno = 0;
2314b8ba871bSPeter Wemm 
2315b8ba871bSPeter Wemm 	/* Redraw the screen, just in case. */
2316b8ba871bSPeter Wemm 	F_SET(sp, SC_SCR_REDRAW);
2317b8ba871bSPeter Wemm }
2318b8ba871bSPeter Wemm 
2319b8ba871bSPeter Wemm /*
2320b8ba871bSPeter Wemm  * txt_hex --
2321b8ba871bSPeter Wemm  *	Let the user insert any character value they want.
2322b8ba871bSPeter Wemm  *
2323b8ba871bSPeter Wemm  * !!!
2324b8ba871bSPeter Wemm  * This is an extension.  The pattern "^X[0-9a-fA-F]*" is a way
2325b8ba871bSPeter Wemm  * for the user to specify a character value which their keyboard
2326b8ba871bSPeter Wemm  * may not be able to enter.
2327b8ba871bSPeter Wemm  */
2328b8ba871bSPeter Wemm static int
txt_hex(SCR * sp,TEXT * tp)2329f0957ccaSPeter Wemm txt_hex(SCR *sp, TEXT *tp)
2330b8ba871bSPeter Wemm {
2331b8ba871bSPeter Wemm 	CHAR_T savec;
2332b8ba871bSPeter Wemm 	size_t len, off;
2333b8ba871bSPeter Wemm 	u_long value;
2334f0957ccaSPeter Wemm 	CHAR_T *p, *wp;
2335b8ba871bSPeter Wemm 
2336b8ba871bSPeter Wemm 	/*
2337b8ba871bSPeter Wemm 	 * Null-terminate the string.  Since nul isn't a legal hex value,
2338b8ba871bSPeter Wemm 	 * this should be okay, and lets us use a local routine, which
2339b8ba871bSPeter Wemm 	 * presumably understands the character set, to convert the value.
2340b8ba871bSPeter Wemm 	 */
2341b8ba871bSPeter Wemm 	savec = tp->lb[tp->cno];
2342b8ba871bSPeter Wemm 	tp->lb[tp->cno] = 0;
2343b8ba871bSPeter Wemm 
2344b8ba871bSPeter Wemm 	/* Find the previous CH_HEX character. */
2345b8ba871bSPeter Wemm 	for (off = tp->cno - 1, p = tp->lb + off, len = 0;; --p, --off, ++len) {
2346b8ba871bSPeter Wemm 		if (*p == CH_HEX) {
2347b8ba871bSPeter Wemm 			wp = p + 1;
2348b8ba871bSPeter Wemm 			break;
2349b8ba871bSPeter Wemm 		}
2350b8ba871bSPeter Wemm 		/* Not on this line?  Shouldn't happen. */
2351b8ba871bSPeter Wemm 		if (off == tp->ai || off == tp->offset)
2352b8ba871bSPeter Wemm 			goto nothex;
2353b8ba871bSPeter Wemm 	}
2354b8ba871bSPeter Wemm 
2355b8ba871bSPeter Wemm 	/* If length of 0, then it wasn't a hex value. */
2356b8ba871bSPeter Wemm 	if (len == 0)
2357b8ba871bSPeter Wemm 		goto nothex;
2358b8ba871bSPeter Wemm 
2359b8ba871bSPeter Wemm 	/* Get the value. */
2360b8ba871bSPeter Wemm 	errno = 0;
2361f0957ccaSPeter Wemm 	value = STRTOL(wp, NULL, 16);
2362f0957ccaSPeter Wemm 	if (errno || value > UCHAR_MAX) {
2363b8ba871bSPeter Wemm nothex:		tp->lb[tp->cno] = savec;
2364b8ba871bSPeter Wemm 		return (0);
2365b8ba871bSPeter Wemm 	}
2366b8ba871bSPeter Wemm 
2367b8ba871bSPeter Wemm 	/* Restore the original character. */
2368b8ba871bSPeter Wemm 	tp->lb[tp->cno] = savec;
2369b8ba871bSPeter Wemm 
2370b8ba871bSPeter Wemm 	/* Adjust the bookkeeping. */
2371b8ba871bSPeter Wemm 	tp->cno -= len;
2372b8ba871bSPeter Wemm 	tp->len -= len;
2373b8ba871bSPeter Wemm 	tp->lb[tp->cno - 1] = value;
2374b8ba871bSPeter Wemm 
2375b8ba871bSPeter Wemm 	/* Copy down any overwrite characters. */
2376b8ba871bSPeter Wemm 	if (tp->owrite)
2377f0957ccaSPeter Wemm 		MEMMOVE(tp->lb + tp->cno, tp->lb + tp->cno + len,
2378f0957ccaSPeter Wemm 		    tp->owrite);
2379b8ba871bSPeter Wemm 
2380b8ba871bSPeter Wemm 	/* Copy down any insert characters. */
2381b8ba871bSPeter Wemm 	if (tp->insert)
2382f0957ccaSPeter Wemm 		MEMMOVE(tp->lb + tp->cno + tp->owrite,
2383f0957ccaSPeter Wemm 		    tp->lb + tp->cno + tp->owrite + len,
2384f0957ccaSPeter Wemm 		    tp->insert);
2385b8ba871bSPeter Wemm 
2386b8ba871bSPeter Wemm 	return (0);
2387b8ba871bSPeter Wemm }
2388b8ba871bSPeter Wemm 
2389b8ba871bSPeter Wemm /*
2390b8ba871bSPeter Wemm  * txt_insch --
2391b8ba871bSPeter Wemm  *
2392b8ba871bSPeter Wemm  * !!!
2393b8ba871bSPeter Wemm  * Historic vi did a special screen optimization for tab characters.  As an
2394b8ba871bSPeter Wemm  * example, for the keystrokes "iabcd<esc>0C<tab>", the tab overwrote the
2395b8ba871bSPeter Wemm  * rest of the string when it was displayed.
2396b8ba871bSPeter Wemm  *
2397b8ba871bSPeter Wemm  * Because early versions of this implementation redisplayed the entire line
2398b8ba871bSPeter Wemm  * on each keystroke, the "bcd" was pushed to the right as it ignored that
2399b8ba871bSPeter Wemm  * the user had "promised" to change the rest of the characters.  However,
2400b8ba871bSPeter Wemm  * the historic vi implementation had an even worse bug: given the keystrokes
2401b8ba871bSPeter Wemm  * "iabcd<esc>0R<tab><esc>", the "bcd" disappears, and magically reappears
2402b8ba871bSPeter Wemm  * on the second <esc> key.
2403b8ba871bSPeter Wemm  *
2404b8ba871bSPeter Wemm  * POSIX 1003.2 requires (will require) that this be fixed, specifying that
2405b8ba871bSPeter Wemm  * vi overwrite characters the user has committed to changing, on the basis
2406b8ba871bSPeter Wemm  * of the screen space they require, but that it not overwrite other characters.
2407b8ba871bSPeter Wemm  */
2408b8ba871bSPeter Wemm static int
txt_insch(SCR * sp,TEXT * tp,CHAR_T * chp,u_int flags)2409f0957ccaSPeter Wemm txt_insch(SCR *sp, TEXT *tp, CHAR_T *chp, u_int flags)
2410b8ba871bSPeter Wemm {
2411f0957ccaSPeter Wemm 	u_char *kp;
2412f0957ccaSPeter Wemm 	CHAR_T savech;
2413b8ba871bSPeter Wemm 	size_t chlen, cno, copydown, olen, nlen;
2414f0957ccaSPeter Wemm 	CHAR_T *p;
2415b8ba871bSPeter Wemm 
2416b8ba871bSPeter Wemm 	/*
2417b8ba871bSPeter Wemm 	 * The 'R' command does one-for-one replacement, because there's
2418b8ba871bSPeter Wemm 	 * no way to know how many characters the user intends to replace.
2419b8ba871bSPeter Wemm 	 */
2420b8ba871bSPeter Wemm 	if (LF_ISSET(TXT_REPLACE)) {
2421b8ba871bSPeter Wemm 		if (tp->owrite) {
2422b8ba871bSPeter Wemm 			--tp->owrite;
2423b8ba871bSPeter Wemm 			tp->lb[tp->cno++] = *chp;
2424b8ba871bSPeter Wemm 			return (0);
2425b8ba871bSPeter Wemm 		}
2426b8ba871bSPeter Wemm 	} else if (tp->owrite) {		/* Overwrite a character. */
2427b8ba871bSPeter Wemm 		cno = tp->cno;
2428b8ba871bSPeter Wemm 
2429b8ba871bSPeter Wemm 		/*
2430b8ba871bSPeter Wemm 		 * If the old or new characters are tabs, then the length of the
2431b8ba871bSPeter Wemm 		 * display depends on the character position in the display.  We
2432b8ba871bSPeter Wemm 		 * don't even try to handle this here, just ask the screen.
2433b8ba871bSPeter Wemm 		 */
2434b8ba871bSPeter Wemm 		if (*chp == '\t') {
2435b8ba871bSPeter Wemm 			savech = tp->lb[cno];
2436b8ba871bSPeter Wemm 			tp->lb[cno] = '\t';
2437b8ba871bSPeter Wemm 			(void)vs_columns(sp, tp->lb, tp->lno, &cno, &nlen);
2438b8ba871bSPeter Wemm 			tp->lb[cno] = savech;
2439b8ba871bSPeter Wemm 		} else
2440f0957ccaSPeter Wemm 			nlen = KEY_COL(sp, *chp);
2441b8ba871bSPeter Wemm 
2442b8ba871bSPeter Wemm 		/*
2443b8ba871bSPeter Wemm 		 * Eat overwrite characters until we run out of them or we've
2444b8ba871bSPeter Wemm 		 * handled the length of the new character.  If we only eat
2445b8ba871bSPeter Wemm 		 * part of an overwrite character, break it into its component
2446b8ba871bSPeter Wemm 		 * elements and display the remaining components.
2447b8ba871bSPeter Wemm 		 */
2448b8ba871bSPeter Wemm 		for (copydown = 0; nlen != 0 && tp->owrite != 0;) {
2449b8ba871bSPeter Wemm 			--tp->owrite;
2450b8ba871bSPeter Wemm 
2451b8ba871bSPeter Wemm 			if (tp->lb[cno] == '\t')
2452b8ba871bSPeter Wemm 				(void)vs_columns(sp,
2453b8ba871bSPeter Wemm 				    tp->lb, tp->lno, &cno, &olen);
2454b8ba871bSPeter Wemm 			else
2455f0957ccaSPeter Wemm 				olen = KEY_COL(sp, tp->lb[cno]);
2456b8ba871bSPeter Wemm 
2457b8ba871bSPeter Wemm 			if (olen == nlen) {
2458b8ba871bSPeter Wemm 				nlen = 0;
2459b8ba871bSPeter Wemm 				break;
2460b8ba871bSPeter Wemm 			}
2461b8ba871bSPeter Wemm 			if (olen < nlen) {
2462b8ba871bSPeter Wemm 				++copydown;
2463b8ba871bSPeter Wemm 				nlen -= olen;
2464b8ba871bSPeter Wemm 			} else {
2465f0957ccaSPeter Wemm 				BINC_RETW(sp,
2466b8ba871bSPeter Wemm 				    tp->lb, tp->lb_len, tp->len + olen);
2467b8ba871bSPeter Wemm 				chlen = olen - nlen;
2468f0957ccaSPeter Wemm 				MEMMOVE(tp->lb + cno + 1 + chlen,
2469f0957ccaSPeter Wemm 				    tp->lb + cno + 1,
2470f0957ccaSPeter Wemm 				    tp->owrite + tp->insert);
2471b8ba871bSPeter Wemm 
2472b8ba871bSPeter Wemm 				tp->len += chlen;
2473b8ba871bSPeter Wemm 				tp->owrite += chlen;
2474f0957ccaSPeter Wemm 				if (tp->lb[cno] == '\t' ||
2475f0957ccaSPeter Wemm 				   KEY_NEEDSWIDE(sp, tp->lb[cno]))
2476b8ba871bSPeter Wemm 					for (p = tp->lb + cno + 1; chlen--;)
2477b8ba871bSPeter Wemm 						*p++ = ' ';
2478b8ba871bSPeter Wemm 				else
2479f0957ccaSPeter Wemm 					for (kp = (u_char *)
2480b8ba871bSPeter Wemm 					    KEY_NAME(sp, tp->lb[cno]) + nlen,
2481b8ba871bSPeter Wemm 					    p = tp->lb + cno + 1; chlen--;)
2482b8ba871bSPeter Wemm 						*p++ = *kp++;
2483b8ba871bSPeter Wemm 				nlen = 0;
2484b8ba871bSPeter Wemm 				break;
2485b8ba871bSPeter Wemm 			}
2486b8ba871bSPeter Wemm 		}
2487b8ba871bSPeter Wemm 
2488b8ba871bSPeter Wemm 		/*
2489b8ba871bSPeter Wemm 		 * If had to erase several characters, we adjust the total
2490b8ba871bSPeter Wemm 		 * count, and if there are any characters left, shift them
2491b8ba871bSPeter Wemm 		 * into position.
2492b8ba871bSPeter Wemm 		 */
2493b8ba871bSPeter Wemm 		if (copydown != 0 && (tp->len -= copydown) != 0)
2494f0957ccaSPeter Wemm 			MEMMOVE(tp->lb + cno, tp->lb + cno + copydown,
2495b8ba871bSPeter Wemm 			    tp->owrite + tp->insert + copydown);
2496b8ba871bSPeter Wemm 
2497b8ba871bSPeter Wemm 		/* If we had enough overwrite characters, we're done. */
2498b8ba871bSPeter Wemm 		if (nlen == 0) {
2499b8ba871bSPeter Wemm 			tp->lb[tp->cno++] = *chp;
2500b8ba871bSPeter Wemm 			return (0);
2501b8ba871bSPeter Wemm 		}
2502b8ba871bSPeter Wemm 	}
2503b8ba871bSPeter Wemm 
2504b8ba871bSPeter Wemm 	/* Check to see if the character fits into the input buffer. */
2505f0957ccaSPeter Wemm 	BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + 1);
2506b8ba871bSPeter Wemm 
2507b8ba871bSPeter Wemm 	++tp->len;
2508b8ba871bSPeter Wemm 	if (tp->insert) {			/* Insert a character. */
2509b8ba871bSPeter Wemm 		if (tp->insert == 1)
2510b8ba871bSPeter Wemm 			tp->lb[tp->cno + 1] = tp->lb[tp->cno];
2511b8ba871bSPeter Wemm 		else
2512f0957ccaSPeter Wemm 			MEMMOVE(tp->lb + tp->cno + 1,
2513b8ba871bSPeter Wemm 			    tp->lb + tp->cno, tp->owrite + tp->insert);
2514b8ba871bSPeter Wemm 	}
2515b8ba871bSPeter Wemm 	tp->lb[tp->cno++] = *chp;
2516b8ba871bSPeter Wemm 	return (0);
2517b8ba871bSPeter Wemm }
2518b8ba871bSPeter Wemm 
2519b8ba871bSPeter Wemm /*
2520b8ba871bSPeter Wemm  * txt_isrch --
2521b8ba871bSPeter Wemm  *	Do an incremental search.
2522b8ba871bSPeter Wemm  */
2523b8ba871bSPeter Wemm static int
txt_isrch(SCR * sp,VICMD * vp,TEXT * tp,u_int8_t * is_flagsp)2524f0957ccaSPeter Wemm txt_isrch(SCR *sp, VICMD *vp, TEXT *tp, u_int8_t *is_flagsp)
2525b8ba871bSPeter Wemm {
2526b8ba871bSPeter Wemm 	MARK start;
2527b8ba871bSPeter Wemm 	recno_t lno;
2528b8ba871bSPeter Wemm 	u_int sf;
2529b8ba871bSPeter Wemm 
2530b8ba871bSPeter Wemm 	/* If it's a one-line screen, we don't do incrementals. */
2531b8ba871bSPeter Wemm 	if (IS_ONELINE(sp)) {
2532b8ba871bSPeter Wemm 		FL_CLR(*is_flagsp, IS_RUNNING);
2533b8ba871bSPeter Wemm 		return (0);
2534b8ba871bSPeter Wemm 	}
2535b8ba871bSPeter Wemm 
2536b8ba871bSPeter Wemm 	/*
2537b8ba871bSPeter Wemm 	 * If the user erases back to the beginning of the buffer, there's
2538b8ba871bSPeter Wemm 	 * nothing to search for.  Reset the cursor to the starting point.
2539b8ba871bSPeter Wemm 	 */
2540b8ba871bSPeter Wemm 	if (tp->cno <= 1) {
2541b8ba871bSPeter Wemm 		vp->m_final = vp->m_start;
2542b8ba871bSPeter Wemm 		return (0);
2543b8ba871bSPeter Wemm 	}
2544b8ba871bSPeter Wemm 
2545b8ba871bSPeter Wemm 	/*
2546b8ba871bSPeter Wemm 	 * If it's an RE quote character, and not quoted, ignore it until
2547b8ba871bSPeter Wemm 	 * we get another character.
2548b8ba871bSPeter Wemm 	 */
2549b8ba871bSPeter Wemm 	if (tp->lb[tp->cno - 1] == '\\' &&
2550b8ba871bSPeter Wemm 	    (tp->cno == 2 || tp->lb[tp->cno - 2] != '\\'))
2551b8ba871bSPeter Wemm 		return (0);
2552b8ba871bSPeter Wemm 
2553b8ba871bSPeter Wemm 	/*
2554b8ba871bSPeter Wemm 	 * If it's a magic shell character, and not quoted, reset the cursor
2555b8ba871bSPeter Wemm 	 * to the starting point.
2556b8ba871bSPeter Wemm 	 */
2557f0957ccaSPeter Wemm 	if (IS_SHELLMETA(sp, tp->lb[tp->cno - 1]) &&
2558b8ba871bSPeter Wemm 	    (tp->cno == 2 || tp->lb[tp->cno - 2] != '\\'))
2559b8ba871bSPeter Wemm 		vp->m_final = vp->m_start;
2560b8ba871bSPeter Wemm 
2561b8ba871bSPeter Wemm 	/*
2562b8ba871bSPeter Wemm 	 * If we see the search pattern termination character, then quit doing
2563b8ba871bSPeter Wemm 	 * an incremental search.  There may be more, e.g., ":/foo/;/bar/",
2564b8ba871bSPeter Wemm 	 * and we can't handle that incrementally.  Also, reset the cursor to
2565b8ba871bSPeter Wemm 	 * the original location, the ex search routines don't know anything
2566b8ba871bSPeter Wemm 	 * about incremental searches.
2567b8ba871bSPeter Wemm 	 */
2568b8ba871bSPeter Wemm 	if (tp->lb[0] == tp->lb[tp->cno - 1] &&
2569b8ba871bSPeter Wemm 	    (tp->cno == 2 || tp->lb[tp->cno - 2] != '\\')) {
2570b8ba871bSPeter Wemm 		vp->m_final = vp->m_start;
2571b8ba871bSPeter Wemm 		FL_CLR(*is_flagsp, IS_RUNNING);
2572b8ba871bSPeter Wemm 		return (0);
2573b8ba871bSPeter Wemm 	}
2574b8ba871bSPeter Wemm 
2575b8ba871bSPeter Wemm 	/*
2576b8ba871bSPeter Wemm 	 * Remember the input line and discard the special input map,
2577b8ba871bSPeter Wemm 	 * but don't overwrite the input line on the screen.
2578b8ba871bSPeter Wemm 	 */
2579b8ba871bSPeter Wemm 	lno = tp->lno;
2580b8ba871bSPeter Wemm 	F_SET(VIP(sp), VIP_S_MODELINE);
2581b8ba871bSPeter Wemm 	F_CLR(sp, SC_TINPUT | SC_TINPUT_INFO);
2582b8ba871bSPeter Wemm 	if (txt_map_end(sp))
2583b8ba871bSPeter Wemm 		return (1);
2584b8ba871bSPeter Wemm 
2585b8ba871bSPeter Wemm 	/*
2586b8ba871bSPeter Wemm 	 * Specify a starting point and search.  If we find a match, move to
2587b8ba871bSPeter Wemm 	 * it and refresh the screen.  If we didn't find the match, then we
2588b8ba871bSPeter Wemm 	 * beep the screen.  When searching from the original cursor position,
2589b8ba871bSPeter Wemm 	 * we have to move the cursor, otherwise, we don't want to move the
2590b8ba871bSPeter Wemm 	 * cursor in case the text at the current position continues to match.
2591b8ba871bSPeter Wemm 	 */
2592b8ba871bSPeter Wemm 	if (FL_ISSET(*is_flagsp, IS_RESTART)) {
2593b8ba871bSPeter Wemm 		start = vp->m_start;
2594b8ba871bSPeter Wemm 		sf = SEARCH_SET;
2595b8ba871bSPeter Wemm 	} else {
2596b8ba871bSPeter Wemm 		start = vp->m_final;
2597b8ba871bSPeter Wemm 		sf = SEARCH_INCR | SEARCH_SET;
2598b8ba871bSPeter Wemm 	}
2599b8ba871bSPeter Wemm 
2600b8ba871bSPeter Wemm 	if (tp->lb[0] == '/' ?
2601b8ba871bSPeter Wemm 	    !f_search(sp,
2602b8ba871bSPeter Wemm 	    &start, &vp->m_final, tp->lb + 1, tp->cno - 1, NULL, sf) :
2603b8ba871bSPeter Wemm 	    !b_search(sp,
2604b8ba871bSPeter Wemm 	    &start, &vp->m_final, tp->lb + 1, tp->cno - 1, NULL, sf)) {
2605b8ba871bSPeter Wemm 		sp->lno = vp->m_final.lno;
2606b8ba871bSPeter Wemm 		sp->cno = vp->m_final.cno;
2607b8ba871bSPeter Wemm 		FL_CLR(*is_flagsp, IS_RESTART);
2608b8ba871bSPeter Wemm 
2609b8ba871bSPeter Wemm 		if (!KEYS_WAITING(sp) && vs_refresh(sp, 0))
2610b8ba871bSPeter Wemm 			return (1);
2611b8ba871bSPeter Wemm 	} else
2612b8ba871bSPeter Wemm 		FL_SET(*is_flagsp, IS_RESTART);
2613b8ba871bSPeter Wemm 
2614b8ba871bSPeter Wemm 	/* Reinstantiate the special input map. */
2615b8ba871bSPeter Wemm 	if (txt_map_init(sp))
2616b8ba871bSPeter Wemm 		return (1);
2617b8ba871bSPeter Wemm 	F_CLR(VIP(sp), VIP_S_MODELINE);
2618b8ba871bSPeter Wemm 	F_SET(sp, SC_TINPUT | SC_TINPUT_INFO);
2619b8ba871bSPeter Wemm 
2620b8ba871bSPeter Wemm 	/* Reset the line number of the input line. */
2621b8ba871bSPeter Wemm 	tp->lno = TMAP[0].lno;
2622b8ba871bSPeter Wemm 
2623b8ba871bSPeter Wemm 	/*
2624b8ba871bSPeter Wemm 	 * If the colon command-line moved, i.e. the screen scrolled,
2625b8ba871bSPeter Wemm 	 * refresh the input line.
2626b8ba871bSPeter Wemm 	 *
2627b8ba871bSPeter Wemm 	 * XXX
2628b8ba871bSPeter Wemm 	 * We shouldn't be calling vs_line, here -- we need dirty bits
2629b8ba871bSPeter Wemm 	 * on entries in the SMAP array.
2630b8ba871bSPeter Wemm 	 */
2631b8ba871bSPeter Wemm 	if (lno != TMAP[0].lno) {
2632b8ba871bSPeter Wemm 		if (vs_line(sp, &TMAP[0], NULL, NULL))
2633b8ba871bSPeter Wemm 			return (1);
2634b8ba871bSPeter Wemm 		(void)sp->gp->scr_refresh(sp, 0);
2635b8ba871bSPeter Wemm 	}
2636b8ba871bSPeter Wemm 	return (0);
2637b8ba871bSPeter Wemm }
2638b8ba871bSPeter Wemm 
2639b8ba871bSPeter Wemm /*
2640b8ba871bSPeter Wemm  * txt_resolve --
2641b8ba871bSPeter Wemm  *	Resolve the input text chain into the file.
2642b8ba871bSPeter Wemm  */
2643b8ba871bSPeter Wemm static int
txt_resolve(SCR * sp,TEXTH * tiqh,u_int32_t flags)2644f0957ccaSPeter Wemm txt_resolve(SCR *sp, TEXTH *tiqh, u_int32_t flags)
2645b8ba871bSPeter Wemm {
2646b8ba871bSPeter Wemm 	VI_PRIVATE *vip;
2647b8ba871bSPeter Wemm 	TEXT *tp;
2648b8ba871bSPeter Wemm 	recno_t lno;
2649b8ba871bSPeter Wemm 	int changed;
2650b8ba871bSPeter Wemm 
2651b8ba871bSPeter Wemm 	/*
2652b8ba871bSPeter Wemm 	 * The first line replaces a current line, and all subsequent lines
2653b8ba871bSPeter Wemm 	 * are appended into the file.  Resolve autoindented characters for
2654b8ba871bSPeter Wemm 	 * each line before committing it.  If the latter causes the line to
2655b8ba871bSPeter Wemm 	 * change, we have to redisplay it, otherwise the information cached
2656b8ba871bSPeter Wemm 	 * about the line will be wrong.
2657b8ba871bSPeter Wemm 	 */
2658b8ba871bSPeter Wemm 	vip = VIP(sp);
2659f0957ccaSPeter Wemm 	tp = TAILQ_FIRST(tiqh);
2660b8ba871bSPeter Wemm 
2661b8ba871bSPeter Wemm 	if (LF_ISSET(TXT_AUTOINDENT))
2662b8ba871bSPeter Wemm 		txt_ai_resolve(sp, tp, &changed);
2663b8ba871bSPeter Wemm 	else
2664b8ba871bSPeter Wemm 		changed = 0;
2665b8ba871bSPeter Wemm 	if (db_set(sp, tp->lno, tp->lb, tp->len) ||
2666f0957ccaSPeter Wemm 	    (changed && vs_change(sp, tp->lno, LINE_RESET)))
2667b8ba871bSPeter Wemm 		return (1);
2668b8ba871bSPeter Wemm 
2669f0957ccaSPeter Wemm 	for (lno = tp->lno; (tp = TAILQ_NEXT(tp, q)) != NULL; ++lno) {
2670b8ba871bSPeter Wemm 		if (LF_ISSET(TXT_AUTOINDENT))
2671b8ba871bSPeter Wemm 			txt_ai_resolve(sp, tp, &changed);
2672b8ba871bSPeter Wemm 		else
2673b8ba871bSPeter Wemm 			changed = 0;
2674b8ba871bSPeter Wemm 		if (db_append(sp, 0, lno, tp->lb, tp->len) ||
2675f0957ccaSPeter Wemm 		    (changed && vs_change(sp, tp->lno, LINE_RESET)))
2676b8ba871bSPeter Wemm 			return (1);
2677b8ba871bSPeter Wemm 	}
2678b8ba871bSPeter Wemm 
2679b8ba871bSPeter Wemm 	/*
2680b8ba871bSPeter Wemm 	 * Clear the input flag, the look-aside buffer is no longer valid.
2681b8ba871bSPeter Wemm 	 * Has to be done as part of text resolution, or upon return we'll
2682b8ba871bSPeter Wemm 	 * be looking at incorrect data.
2683b8ba871bSPeter Wemm 	 */
2684b8ba871bSPeter Wemm 	F_CLR(sp, SC_TINPUT);
2685b8ba871bSPeter Wemm 
2686b8ba871bSPeter Wemm 	return (0);
2687b8ba871bSPeter Wemm }
2688b8ba871bSPeter Wemm 
2689b8ba871bSPeter Wemm /*
2690b8ba871bSPeter Wemm  * txt_showmatch --
2691b8ba871bSPeter Wemm  *	Show a character match.
2692b8ba871bSPeter Wemm  *
2693b8ba871bSPeter Wemm  * !!!
2694b8ba871bSPeter Wemm  * Historic vi tried to display matches even in the :colon command line.
2695b8ba871bSPeter Wemm  * I think not.
2696b8ba871bSPeter Wemm  */
2697b8ba871bSPeter Wemm static int
txt_showmatch(SCR * sp,TEXT * tp)2698f0957ccaSPeter Wemm txt_showmatch(SCR *sp, TEXT *tp)
2699b8ba871bSPeter Wemm {
2700b8ba871bSPeter Wemm 	GS *gp;
2701b8ba871bSPeter Wemm 	VCS cs;
2702b8ba871bSPeter Wemm 	MARK m;
2703b8ba871bSPeter Wemm 	int cnt, endc, startc;
2704b8ba871bSPeter Wemm 
2705b8ba871bSPeter Wemm 	gp = sp->gp;
2706b8ba871bSPeter Wemm 
2707b8ba871bSPeter Wemm 	/*
2708b8ba871bSPeter Wemm 	 * Do a refresh first, in case we haven't done one in awhile,
2709b8ba871bSPeter Wemm 	 * so the user can see what we're complaining about.
2710b8ba871bSPeter Wemm 	 */
2711b8ba871bSPeter Wemm 	UPDATE_POSITION(sp, tp);
2712b8ba871bSPeter Wemm 	if (vs_refresh(sp, 1))
2713b8ba871bSPeter Wemm 		return (1);
2714b8ba871bSPeter Wemm 
2715b8ba871bSPeter Wemm 	/*
2716b8ba871bSPeter Wemm 	 * We don't display the match if it's not on the screen.  Find
2717b8ba871bSPeter Wemm 	 * out what the first character on the screen is.
2718b8ba871bSPeter Wemm 	 */
2719b8ba871bSPeter Wemm 	if (vs_sm_position(sp, &m, 0, P_TOP))
2720b8ba871bSPeter Wemm 		return (1);
2721b8ba871bSPeter Wemm 
2722b8ba871bSPeter Wemm 	/* Initialize the getc() interface. */
2723b8ba871bSPeter Wemm 	cs.cs_lno = tp->lno;
2724b8ba871bSPeter Wemm 	cs.cs_cno = tp->cno - 1;
2725b8ba871bSPeter Wemm 	if (cs_init(sp, &cs))
2726b8ba871bSPeter Wemm 		return (1);
2727f0957ccaSPeter Wemm 	startc = STRCHR(VIP(sp)->mcs, endc = cs.cs_ch)[-1];
2728b8ba871bSPeter Wemm 
2729b8ba871bSPeter Wemm 	/* Search for the match. */
2730b8ba871bSPeter Wemm 	for (cnt = 1;;) {
2731b8ba871bSPeter Wemm 		if (cs_prev(sp, &cs))
2732b8ba871bSPeter Wemm 			return (1);
2733b8ba871bSPeter Wemm 		if (cs.cs_flags != 0) {
2734b8ba871bSPeter Wemm 			if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF) {
2735b8ba871bSPeter Wemm 				msgq(sp, M_BERR,
2736b8ba871bSPeter Wemm 				    "Unmatched %s", KEY_NAME(sp, endc));
2737b8ba871bSPeter Wemm 				return (0);
2738b8ba871bSPeter Wemm 			}
2739b8ba871bSPeter Wemm 			continue;
2740b8ba871bSPeter Wemm 		}
2741b8ba871bSPeter Wemm 		if (cs.cs_ch == endc)
2742b8ba871bSPeter Wemm 			++cnt;
2743b8ba871bSPeter Wemm 		else if (cs.cs_ch == startc && --cnt == 0)
2744b8ba871bSPeter Wemm 			break;
2745b8ba871bSPeter Wemm 	}
2746b8ba871bSPeter Wemm 
2747b8ba871bSPeter Wemm 	/* If the match is on the screen, move to it. */
2748f0957ccaSPeter Wemm 	if (cs.cs_lno < m.lno || (cs.cs_lno == m.lno && cs.cs_cno < m.cno))
2749b8ba871bSPeter Wemm 		return (0);
2750b8ba871bSPeter Wemm 	sp->lno = cs.cs_lno;
2751b8ba871bSPeter Wemm 	sp->cno = cs.cs_cno;
2752b8ba871bSPeter Wemm 	if (vs_refresh(sp, 1))
2753b8ba871bSPeter Wemm 		return (1);
2754b8ba871bSPeter Wemm 
2755b8ba871bSPeter Wemm 	/* Wait for timeout or character arrival. */
2756b8ba871bSPeter Wemm 	return (v_event_get(sp,
2757b8ba871bSPeter Wemm 	    NULL, O_VAL(sp, O_MATCHTIME) * 100, EC_TIMEOUT));
2758b8ba871bSPeter Wemm }
2759b8ba871bSPeter Wemm 
2760b8ba871bSPeter Wemm /*
2761b8ba871bSPeter Wemm  * txt_margin --
2762b8ba871bSPeter Wemm  *	Handle margin wrap.
2763b8ba871bSPeter Wemm  */
2764b8ba871bSPeter Wemm static int
txt_margin(SCR * sp,TEXT * tp,TEXT * wmtp,int * didbreak,u_int32_t flags)2765f0957ccaSPeter Wemm txt_margin(SCR *sp, TEXT *tp, TEXT *wmtp, int *didbreak, u_int32_t flags)
2766b8ba871bSPeter Wemm {
2767b8ba871bSPeter Wemm 	VI_PRIVATE *vip;
2768b8ba871bSPeter Wemm 	size_t len, off;
2769f0957ccaSPeter Wemm 	CHAR_T *p, *wp;
2770b8ba871bSPeter Wemm 
2771b8ba871bSPeter Wemm 	/* Find the nearest previous blank. */
2772b8ba871bSPeter Wemm 	for (off = tp->cno - 1, p = tp->lb + off, len = 0;; --off, --p, ++len) {
2773b8ba871bSPeter Wemm 		if (isblank(*p)) {
2774b8ba871bSPeter Wemm 			wp = p + 1;
2775b8ba871bSPeter Wemm 			break;
2776b8ba871bSPeter Wemm 		}
2777b8ba871bSPeter Wemm 
2778b8ba871bSPeter Wemm 		/*
2779b8ba871bSPeter Wemm 		 * If reach the start of the line, there's nowhere to break.
2780b8ba871bSPeter Wemm 		 *
2781b8ba871bSPeter Wemm 		 * !!!
2782b8ba871bSPeter Wemm 		 * Historic vi belled each time a character was entered after
2783b8ba871bSPeter Wemm 		 * crossing the margin until a space was entered which could
2784b8ba871bSPeter Wemm 		 * be used to break the line.  I don't as it tends to wake the
2785b8ba871bSPeter Wemm 		 * cats.
2786b8ba871bSPeter Wemm 		 */
2787b8ba871bSPeter Wemm 		if (off == tp->ai || off == tp->offset) {
2788b8ba871bSPeter Wemm 			*didbreak = 0;
2789b8ba871bSPeter Wemm 			return (0);
2790b8ba871bSPeter Wemm 		}
2791b8ba871bSPeter Wemm 	}
2792b8ba871bSPeter Wemm 
2793b8ba871bSPeter Wemm 	/*
2794b8ba871bSPeter Wemm 	 * Store saved information about the rest of the line in the
2795b8ba871bSPeter Wemm 	 * wrapmargin TEXT structure.
2796b8ba871bSPeter Wemm 	 *
2797b8ba871bSPeter Wemm 	 * !!!
2798b8ba871bSPeter Wemm 	 * The offset field holds the length of the current characters
2799b8ba871bSPeter Wemm 	 * that the user entered, but which are getting split to the new
2800b8ba871bSPeter Wemm 	 * line -- it's going to be used to set the cursor value when we
2801b8ba871bSPeter Wemm 	 * move to the new line.
2802b8ba871bSPeter Wemm 	 */
2803b8ba871bSPeter Wemm 	vip = VIP(sp);
2804b8ba871bSPeter Wemm 	wmtp->lb = p + 1;
2805b8ba871bSPeter Wemm 	wmtp->offset = len;
2806b8ba871bSPeter Wemm 	wmtp->insert = LF_ISSET(TXT_APPENDEOL) ?  tp->insert - 1 : tp->insert;
2807b8ba871bSPeter Wemm 	wmtp->owrite = tp->owrite;
2808b8ba871bSPeter Wemm 
2809b8ba871bSPeter Wemm 	/* Correct current bookkeeping information. */
2810b8ba871bSPeter Wemm 	tp->cno -= len;
2811b8ba871bSPeter Wemm 	if (LF_ISSET(TXT_APPENDEOL)) {
2812b8ba871bSPeter Wemm 		tp->len -= len + tp->owrite + (tp->insert - 1);
2813b8ba871bSPeter Wemm 		tp->insert = 1;
2814b8ba871bSPeter Wemm 	} else {
2815b8ba871bSPeter Wemm 		tp->len -= len + tp->owrite + tp->insert;
2816b8ba871bSPeter Wemm 		tp->insert = 0;
2817b8ba871bSPeter Wemm 	}
2818b8ba871bSPeter Wemm 	tp->owrite = 0;
2819b8ba871bSPeter Wemm 
2820b8ba871bSPeter Wemm 	/*
2821b8ba871bSPeter Wemm 	 * !!!
2822b8ba871bSPeter Wemm 	 * Delete any trailing whitespace from the current line.
2823b8ba871bSPeter Wemm 	 */
2824b8ba871bSPeter Wemm 	for (;; --p, --off) {
2825b8ba871bSPeter Wemm 		if (!isblank(*p))
2826b8ba871bSPeter Wemm 			break;
2827b8ba871bSPeter Wemm 		--tp->cno;
2828b8ba871bSPeter Wemm 		--tp->len;
2829b8ba871bSPeter Wemm 		if (off == tp->ai || off == tp->offset)
2830b8ba871bSPeter Wemm 			break;
2831b8ba871bSPeter Wemm 	}
2832b8ba871bSPeter Wemm 	*didbreak = 1;
2833b8ba871bSPeter Wemm 	return (0);
2834b8ba871bSPeter Wemm }
2835b8ba871bSPeter Wemm 
2836b8ba871bSPeter Wemm /*
2837b8ba871bSPeter Wemm  * txt_Rresolve --
2838b8ba871bSPeter Wemm  *	Resolve the input line for the 'R' command.
2839b8ba871bSPeter Wemm  */
2840b8ba871bSPeter Wemm static void
txt_Rresolve(SCR * sp,TEXTH * tiqh,TEXT * tp,const size_t orig_len)2841f0957ccaSPeter Wemm txt_Rresolve(SCR *sp, TEXTH *tiqh, TEXT *tp, const size_t orig_len)
2842b8ba871bSPeter Wemm {
2843b8ba871bSPeter Wemm 	TEXT *ttp;
2844b8ba871bSPeter Wemm 	size_t input_len, retain;
2845f0957ccaSPeter Wemm 	CHAR_T *p;
2846b8ba871bSPeter Wemm 
2847b8ba871bSPeter Wemm 	/*
2848b8ba871bSPeter Wemm 	 * Check to make sure that the cursor hasn't moved beyond
2849b8ba871bSPeter Wemm 	 * the end of the line.
2850b8ba871bSPeter Wemm 	 */
2851b8ba871bSPeter Wemm 	if (tp->owrite == 0)
2852b8ba871bSPeter Wemm 		return;
2853b8ba871bSPeter Wemm 
2854b8ba871bSPeter Wemm 	/*
2855b8ba871bSPeter Wemm 	 * Calculate how many characters the user has entered,
2856b8ba871bSPeter Wemm 	 * plus the blanks erased by <carriage-return>/<newline>s.
2857b8ba871bSPeter Wemm 	 */
2858f0957ccaSPeter Wemm 	for (ttp = TAILQ_FIRST(tiqh), input_len = 0;;) {
2859b8ba871bSPeter Wemm 		input_len += ttp == tp ? tp->cno : ttp->len + ttp->R_erase;
2860f0957ccaSPeter Wemm 		if ((ttp = TAILQ_NEXT(ttp, q)) == NULL)
2861b8ba871bSPeter Wemm 			break;
2862b8ba871bSPeter Wemm 	}
2863b8ba871bSPeter Wemm 
2864b8ba871bSPeter Wemm 	/*
2865b8ba871bSPeter Wemm 	 * If the user has entered less characters than the original line
2866b8ba871bSPeter Wemm 	 * was long, restore any overwriteable characters to the original
2867b8ba871bSPeter Wemm 	 * characters.  These characters are entered as "insert characters",
2868b8ba871bSPeter Wemm 	 * because they're after the cursor and we don't want to lose them.
2869b8ba871bSPeter Wemm 	 * (This is okay because the R command has no insert characters.)
2870b8ba871bSPeter Wemm 	 * We set owrite to 0 so that the insert characters don't get copied
2871b8ba871bSPeter Wemm 	 * to somewhere else, which means that the line and the length have
2872b8ba871bSPeter Wemm 	 * to be adjusted here as well.
2873b8ba871bSPeter Wemm 	 *
2874b8ba871bSPeter Wemm 	 * We have to retrieve the original line because the original pinned
2875b8ba871bSPeter Wemm 	 * page has long since been discarded.  If it doesn't exist, that's
2876b8ba871bSPeter Wemm 	 * okay, the user just extended the file.
2877b8ba871bSPeter Wemm 	 */
2878b8ba871bSPeter Wemm 	if (input_len < orig_len) {
2879b8ba871bSPeter Wemm 		retain = MIN(tp->owrite, orig_len - input_len);
2880b8ba871bSPeter Wemm 		if (db_get(sp,
2881f0957ccaSPeter Wemm 		    TAILQ_FIRST(tiqh)->lno, DBG_FATAL | DBG_NOCACHE, &p, NULL))
2882b8ba871bSPeter Wemm 			return;
2883f0957ccaSPeter Wemm 		MEMCPY(tp->lb + tp->cno, p + input_len, retain);
2884b8ba871bSPeter Wemm 		tp->len -= tp->owrite - retain;
2885b8ba871bSPeter Wemm 		tp->owrite = 0;
2886b8ba871bSPeter Wemm 		tp->insert += retain;
2887b8ba871bSPeter Wemm 	}
2888b8ba871bSPeter Wemm }
2889b8ba871bSPeter Wemm 
2890b8ba871bSPeter Wemm /*
2891b8ba871bSPeter Wemm  * txt_nomorech --
2892b8ba871bSPeter Wemm  *	No more characters message.
2893b8ba871bSPeter Wemm  */
2894b8ba871bSPeter Wemm static void
txt_nomorech(SCR * sp)2895f0957ccaSPeter Wemm txt_nomorech(SCR *sp)
2896b8ba871bSPeter Wemm {
2897b8ba871bSPeter Wemm 	msgq(sp, M_BERR, "194|No more characters to erase");
2898b8ba871bSPeter Wemm }
2899