xref: /freebsd/contrib/nvi/vi/v_ex.c (revision 725f5cdb)
1b8ba871bSPeter Wemm /*-
2b8ba871bSPeter Wemm  * Copyright (c) 1992, 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/time.h>
15b8ba871bSPeter Wemm 
16b8ba871bSPeter Wemm #include <bitstring.h>
17b8ba871bSPeter Wemm #include <limits.h>
18b8ba871bSPeter Wemm #include <stdio.h>
19b8ba871bSPeter Wemm #include <stdlib.h>
20b8ba871bSPeter Wemm #include <string.h>
21b8ba871bSPeter Wemm #include <unistd.h>
22b8ba871bSPeter Wemm 
23b8ba871bSPeter Wemm #include "../common/common.h"
24b8ba871bSPeter Wemm #include "vi.h"
25b8ba871bSPeter Wemm 
26b8ba871bSPeter Wemm static int v_ecl(SCR *);
27b8ba871bSPeter Wemm static int v_ecl_init(SCR *);
28b8ba871bSPeter Wemm static int v_ecl_log(SCR *, TEXT *);
29b8ba871bSPeter Wemm static int v_ex_done(SCR *, VICMD *);
30b8ba871bSPeter Wemm static int v_exec_ex(SCR *, VICMD *, EXCMD *);
31b8ba871bSPeter Wemm 
32b8ba871bSPeter Wemm /*
33b8ba871bSPeter Wemm  * v_again -- &
34b8ba871bSPeter Wemm  *	Repeat the previous substitution.
35b8ba871bSPeter Wemm  *
36b8ba871bSPeter Wemm  * PUBLIC: int v_again(SCR *, VICMD *);
37b8ba871bSPeter Wemm  */
38b8ba871bSPeter Wemm int
v_again(SCR * sp,VICMD * vp)39b8ba871bSPeter Wemm v_again(SCR *sp, VICMD *vp)
40b8ba871bSPeter Wemm {
41b8ba871bSPeter Wemm 	EXCMD cmd;
42b8ba871bSPeter Wemm 
43b8ba871bSPeter Wemm 	ex_cinit(sp, &cmd, C_SUBAGAIN, 2, vp->m_start.lno, vp->m_start.lno, 1);
44b8ba871bSPeter Wemm 	argv_exp0(sp, &cmd, L(""), 1);
45b8ba871bSPeter Wemm 	return (v_exec_ex(sp, vp, &cmd));
46b8ba871bSPeter Wemm }
47b8ba871bSPeter Wemm 
48b8ba871bSPeter Wemm /*
49b8ba871bSPeter Wemm  * v_exmode -- Q
50b8ba871bSPeter Wemm  *	Switch the editor into EX mode.
51b8ba871bSPeter Wemm  *
52b8ba871bSPeter Wemm  * PUBLIC: int v_exmode(SCR *, VICMD *);
53b8ba871bSPeter Wemm  */
54b8ba871bSPeter Wemm int
v_exmode(SCR * sp,VICMD * vp)55b8ba871bSPeter Wemm v_exmode(SCR *sp, VICMD *vp)
56b8ba871bSPeter Wemm {
57b8ba871bSPeter Wemm 	GS *gp;
58b8ba871bSPeter Wemm 
59b8ba871bSPeter Wemm 	gp = sp->gp;
60b8ba871bSPeter Wemm 
61b8ba871bSPeter Wemm 	/* Try and switch screens -- the screen may not permit it. */
62b8ba871bSPeter Wemm 	if (gp->scr_screen(sp, SC_EX)) {
63b8ba871bSPeter Wemm 		msgq(sp, M_ERR,
64b8ba871bSPeter Wemm 		    "207|The Q command requires the ex terminal interface");
65b8ba871bSPeter Wemm 		return (1);
66b8ba871bSPeter Wemm 	}
67b8ba871bSPeter Wemm 	(void)gp->scr_attr(sp, SA_ALTERNATE, 0);
68b8ba871bSPeter Wemm 
69b8ba871bSPeter Wemm 	/* Save the current cursor position. */
70b8ba871bSPeter Wemm 	sp->frp->lno = sp->lno;
71b8ba871bSPeter Wemm 	sp->frp->cno = sp->cno;
72b8ba871bSPeter Wemm 	F_SET(sp->frp, FR_CURSORSET);
73b8ba871bSPeter Wemm 
74b8ba871bSPeter Wemm 	/* Switch to ex mode. */
75b8ba871bSPeter Wemm 	F_CLR(sp, SC_VI | SC_SCR_VI);
76b8ba871bSPeter Wemm 	F_SET(sp, SC_EX);
77b8ba871bSPeter Wemm 
78b8ba871bSPeter Wemm 	/* Move out of the vi screen. */
79b8ba871bSPeter Wemm 	(void)ex_puts(sp, "\n");
80b8ba871bSPeter Wemm 
81b8ba871bSPeter Wemm 	return (0);
82b8ba871bSPeter Wemm }
83b8ba871bSPeter Wemm 
84b8ba871bSPeter Wemm /*
85b8ba871bSPeter Wemm  * v_join -- [count]J
86b8ba871bSPeter Wemm  *	Join lines together.
87b8ba871bSPeter Wemm  *
88b8ba871bSPeter Wemm  * PUBLIC: int v_join(SCR *, VICMD *);
89b8ba871bSPeter Wemm  */
90b8ba871bSPeter Wemm int
v_join(SCR * sp,VICMD * vp)91b8ba871bSPeter Wemm v_join(SCR *sp, VICMD *vp)
92b8ba871bSPeter Wemm {
93b8ba871bSPeter Wemm 	EXCMD cmd;
94b8ba871bSPeter Wemm 	int lno;
95b8ba871bSPeter Wemm 
96b8ba871bSPeter Wemm 	/*
97b8ba871bSPeter Wemm 	 * YASC.
98b8ba871bSPeter Wemm 	 * The general rule is that '#J' joins # lines, counting the current
99b8ba871bSPeter Wemm 	 * line.  However, 'J' and '1J' are the same as '2J', i.e. join the
100b8ba871bSPeter Wemm 	 * current and next lines.  This doesn't map well into the ex command
101b8ba871bSPeter Wemm 	 * (which takes two line numbers), so we handle it here.  Note that
102b8ba871bSPeter Wemm 	 * we never test for EOF -- historically going past the end of file
103b8ba871bSPeter Wemm 	 * worked just fine.
104b8ba871bSPeter Wemm 	 */
105b8ba871bSPeter Wemm 	lno = vp->m_start.lno + 1;
106b8ba871bSPeter Wemm 	if (F_ISSET(vp, VC_C1SET) && vp->count > 2)
107b8ba871bSPeter Wemm 		lno = vp->m_start.lno + (vp->count - 1);
108b8ba871bSPeter Wemm 
109b8ba871bSPeter Wemm 	ex_cinit(sp, &cmd, C_JOIN, 2, vp->m_start.lno, lno, 0);
110b8ba871bSPeter Wemm 	return (v_exec_ex(sp, vp, &cmd));
111b8ba871bSPeter Wemm }
112b8ba871bSPeter Wemm 
113b8ba871bSPeter Wemm /*
114b8ba871bSPeter Wemm  * v_shiftl -- [count]<motion
115b8ba871bSPeter Wemm  *	Shift lines left.
116b8ba871bSPeter Wemm  *
117b8ba871bSPeter Wemm  * PUBLIC: int v_shiftl(SCR *, VICMD *);
118b8ba871bSPeter Wemm  */
119b8ba871bSPeter Wemm int
v_shiftl(SCR * sp,VICMD * vp)120b8ba871bSPeter Wemm v_shiftl(SCR *sp, VICMD *vp)
121b8ba871bSPeter Wemm {
122b8ba871bSPeter Wemm 	EXCMD cmd;
123b8ba871bSPeter Wemm 
124b8ba871bSPeter Wemm 	ex_cinit(sp, &cmd, C_SHIFTL, 2, vp->m_start.lno, vp->m_stop.lno, 0);
125b8ba871bSPeter Wemm 	argv_exp0(sp, &cmd, L("<"), 2);
126b8ba871bSPeter Wemm 	return (v_exec_ex(sp, vp, &cmd));
127b8ba871bSPeter Wemm }
128b8ba871bSPeter Wemm 
129b8ba871bSPeter Wemm /*
130b8ba871bSPeter Wemm  * v_shiftr -- [count]>motion
131b8ba871bSPeter Wemm  *	Shift lines right.
132b8ba871bSPeter Wemm  *
133b8ba871bSPeter Wemm  * PUBLIC: int v_shiftr(SCR *, VICMD *);
134b8ba871bSPeter Wemm  */
135b8ba871bSPeter Wemm int
v_shiftr(SCR * sp,VICMD * vp)136b8ba871bSPeter Wemm v_shiftr(SCR *sp, VICMD *vp)
137b8ba871bSPeter Wemm {
138b8ba871bSPeter Wemm 	EXCMD cmd;
139b8ba871bSPeter Wemm 
140b8ba871bSPeter Wemm 	ex_cinit(sp, &cmd, C_SHIFTR, 2, vp->m_start.lno, vp->m_stop.lno, 0);
141b8ba871bSPeter Wemm 	argv_exp0(sp, &cmd, L(">"), 2);
142b8ba871bSPeter Wemm 	return (v_exec_ex(sp, vp, &cmd));
143b8ba871bSPeter Wemm }
144b8ba871bSPeter Wemm 
145b8ba871bSPeter Wemm /*
146b8ba871bSPeter Wemm  * v_suspend -- ^Z
147b8ba871bSPeter Wemm  *	Suspend vi.
148b8ba871bSPeter Wemm  *
149b8ba871bSPeter Wemm  * PUBLIC: int v_suspend(SCR *, VICMD *);
150b8ba871bSPeter Wemm  */
151b8ba871bSPeter Wemm int
v_suspend(SCR * sp,VICMD * vp)152b8ba871bSPeter Wemm v_suspend(SCR *sp, VICMD *vp)
153b8ba871bSPeter Wemm {
154b8ba871bSPeter Wemm 	EXCMD cmd;
155b8ba871bSPeter Wemm 
156b8ba871bSPeter Wemm 	ex_cinit(sp, &cmd, C_STOP, 0, OOBLNO, OOBLNO, 0);
157b8ba871bSPeter Wemm 	argv_exp0(sp, &cmd, L("suspend"), SIZE(L("suspend")));
158b8ba871bSPeter Wemm 	return (v_exec_ex(sp, vp, &cmd));
159b8ba871bSPeter Wemm }
160b8ba871bSPeter Wemm 
161b8ba871bSPeter Wemm /*
162b8ba871bSPeter Wemm  * v_switch -- ^^
163b8ba871bSPeter Wemm  *	Switch to the previous file.
164b8ba871bSPeter Wemm  *
165b8ba871bSPeter Wemm  * PUBLIC: int v_switch(SCR *, VICMD *);
166b8ba871bSPeter Wemm  */
167b8ba871bSPeter Wemm int
v_switch(SCR * sp,VICMD * vp)168b8ba871bSPeter Wemm v_switch(SCR *sp, VICMD *vp)
169b8ba871bSPeter Wemm {
170b8ba871bSPeter Wemm 	EXCMD cmd;
171b8ba871bSPeter Wemm 	char *name;
172b8ba871bSPeter Wemm 	CHAR_T *wp;
173b8ba871bSPeter Wemm 	size_t wlen;
174b8ba871bSPeter Wemm 
175b8ba871bSPeter Wemm 	/*
176b8ba871bSPeter Wemm 	 * Try the alternate file name, then the previous file
177b8ba871bSPeter Wemm 	 * name.  Use the real name, not the user's current name.
178b8ba871bSPeter Wemm 	 */
179b8ba871bSPeter Wemm 	if ((name = sp->alt_name) == NULL) {
180b8ba871bSPeter Wemm 		msgq(sp, M_ERR, "180|No previous file to edit");
181b8ba871bSPeter Wemm 		return (1);
182b8ba871bSPeter Wemm 	}
183b8ba871bSPeter Wemm 
184b8ba871bSPeter Wemm 	/* If autowrite is set, write out the file. */
185b8ba871bSPeter Wemm 	if (file_m1(sp, 0, FS_ALL))
186b8ba871bSPeter Wemm 		return (1);
187b8ba871bSPeter Wemm 
188b8ba871bSPeter Wemm 	ex_cinit(sp, &cmd, C_EDIT, 0, OOBLNO, OOBLNO, 0);
189b8ba871bSPeter Wemm 	CHAR2INT(sp, name, strlen(name) + 1, wp, wlen);
190b8ba871bSPeter Wemm 	argv_exp0(sp, &cmd, wp, wlen);
191b8ba871bSPeter Wemm 	return (v_exec_ex(sp, vp, &cmd));
192b8ba871bSPeter Wemm }
193b8ba871bSPeter Wemm 
194b8ba871bSPeter Wemm /*
195b8ba871bSPeter Wemm  * v_tagpush -- ^[
196b8ba871bSPeter Wemm  *	Do a tag search on the cursor keyword.
197b8ba871bSPeter Wemm  *
198b8ba871bSPeter Wemm  * PUBLIC: int v_tagpush(SCR *, VICMD *);
199b8ba871bSPeter Wemm  */
200b8ba871bSPeter Wemm int
v_tagpush(SCR * sp,VICMD * vp)201b8ba871bSPeter Wemm v_tagpush(SCR *sp, VICMD *vp)
202b8ba871bSPeter Wemm {
203b8ba871bSPeter Wemm 	EXCMD cmd;
204b8ba871bSPeter Wemm 
205b8ba871bSPeter Wemm 	ex_cinit(sp, &cmd, C_TAG, 0, OOBLNO, 0, 0);
206b8ba871bSPeter Wemm 	argv_exp0(sp, &cmd, VIP(sp)->keyw, STRLEN(VIP(sp)->keyw) + 1);
207b8ba871bSPeter Wemm 	return (v_exec_ex(sp, vp, &cmd));
208b8ba871bSPeter Wemm }
209b8ba871bSPeter Wemm 
210b8ba871bSPeter Wemm /*
211b8ba871bSPeter Wemm  * v_tagpop -- ^T
212b8ba871bSPeter Wemm  *	Pop the tags stack.
213b8ba871bSPeter Wemm  *
214b8ba871bSPeter Wemm  * PUBLIC: int v_tagpop(SCR *, VICMD *);
215b8ba871bSPeter Wemm  */
216b8ba871bSPeter Wemm int
v_tagpop(SCR * sp,VICMD * vp)217b8ba871bSPeter Wemm v_tagpop(SCR *sp, VICMD *vp)
218b8ba871bSPeter Wemm {
219b8ba871bSPeter Wemm 	EXCMD cmd;
220b8ba871bSPeter Wemm 
221b8ba871bSPeter Wemm 	ex_cinit(sp, &cmd, C_TAGPOP, 0, OOBLNO, 0, 0);
222b8ba871bSPeter Wemm 	return (v_exec_ex(sp, vp, &cmd));
223b8ba871bSPeter Wemm }
224b8ba871bSPeter Wemm 
225b8ba871bSPeter Wemm /*
226b8ba871bSPeter Wemm  * v_filter -- [count]!motion command(s)
227b8ba871bSPeter Wemm  *	Run range through shell commands, replacing text.
228b8ba871bSPeter Wemm  *
229e56a7205SJordan K. Hubbard  * PUBLIC: int v_filter(SCR *, VICMD *);
230e56a7205SJordan K. Hubbard  */
231e56a7205SJordan K. Hubbard int
v_filter(SCR * sp,VICMD * vp)232e56a7205SJordan K. Hubbard v_filter(SCR *sp, VICMD *vp)
233e56a7205SJordan K. Hubbard {
234b8ba871bSPeter Wemm 	EXCMD cmd;
235b8ba871bSPeter Wemm 	TEXT *tp;
236b8ba871bSPeter Wemm 
237b8ba871bSPeter Wemm 	/*
238b8ba871bSPeter Wemm 	 * !!!
239b8ba871bSPeter Wemm 	 * Historical vi permitted "!!" in an empty file, and it's handled
240b8ba871bSPeter Wemm 	 * as a special case in the ex_bang routine.  Don't modify this setup
241b8ba871bSPeter Wemm 	 * without understanding that one.  In particular, note that we're
242b8ba871bSPeter Wemm 	 * manipulating the ex argument structures behind ex's back.
243b8ba871bSPeter Wemm 	 *
244b8ba871bSPeter Wemm 	 * !!!
245b8ba871bSPeter Wemm 	 * Historical vi did not permit the '!' command to be associated with
246b8ba871bSPeter Wemm 	 * a non-line oriented motion command, in general, although it did
247b8ba871bSPeter Wemm 	 * with search commands.  So, !f; and !w would fail, but !/;<CR>
248b8ba871bSPeter Wemm 	 * would succeed, even if they all moved to the same location in the
249b8ba871bSPeter Wemm 	 * current line.  I don't see any reason to disallow '!' using any of
250b8ba871bSPeter Wemm 	 * the possible motion commands.
251b8ba871bSPeter Wemm 	 *
252b8ba871bSPeter Wemm 	 * !!!
253b8ba871bSPeter Wemm 	 * Historical vi ran the last bang command if N or n was used as the
254b8ba871bSPeter Wemm 	 * search motion.
255b8ba871bSPeter Wemm 	 */
256b8ba871bSPeter Wemm 	if (F_ISSET(vp, VC_ISDOT) ||
257b8ba871bSPeter Wemm 	    ISCMD(vp->rkp, 'N') || ISCMD(vp->rkp, 'n')) {
258b8ba871bSPeter Wemm 		ex_cinit(sp,
259b8ba871bSPeter Wemm 		    &cmd, C_BANG, 2, vp->m_start.lno, vp->m_stop.lno, 0);
260b8ba871bSPeter Wemm 		EXP(sp)->argsoff = 0;			/* XXX */
261b8ba871bSPeter Wemm 
262b8ba871bSPeter Wemm 		if (argv_exp1(sp, &cmd, L("!"), 1, 1))
263b8ba871bSPeter Wemm 			return (1);
264b8ba871bSPeter Wemm 		cmd.argc = EXP(sp)->argsoff;		/* XXX */
265b8ba871bSPeter Wemm 		cmd.argv = EXP(sp)->args;		/* XXX */
266b8ba871bSPeter Wemm 		return (v_exec_ex(sp, vp, &cmd));
267b8ba871bSPeter Wemm 	}
268b8ba871bSPeter Wemm 
269b8ba871bSPeter Wemm 	/* Get the command from the user. */
270b8ba871bSPeter Wemm 	if (v_tcmd(sp, vp,
271b8ba871bSPeter Wemm 	    '!', TXT_BS | TXT_CR | TXT_ESCAPE | TXT_FILEC | TXT_PROMPT))
272b8ba871bSPeter Wemm 		return (1);
273b8ba871bSPeter Wemm 
274b8ba871bSPeter Wemm 	/*
275b8ba871bSPeter Wemm 	 * Check to see if the user changed their mind.
276b8ba871bSPeter Wemm 	 *
277b8ba871bSPeter Wemm 	 * !!!
278b8ba871bSPeter Wemm 	 * Entering <escape> on an empty line was historically an error,
279b8ba871bSPeter Wemm 	 * this implementation doesn't bother.
280b8ba871bSPeter Wemm 	 */
281b8ba871bSPeter Wemm 	tp = TAILQ_FIRST(sp->tiq);
282b8ba871bSPeter Wemm 	if (tp->term != TERM_OK) {
283b8ba871bSPeter Wemm 		vp->m_final.lno = sp->lno;
284b8ba871bSPeter Wemm 		vp->m_final.cno = sp->cno;
285b8ba871bSPeter Wemm 		return (0);
286b8ba871bSPeter Wemm 	}
287b8ba871bSPeter Wemm 
288b8ba871bSPeter Wemm 	/* Home the cursor. */
289b8ba871bSPeter Wemm 	vs_home(sp);
290b8ba871bSPeter Wemm 
291b8ba871bSPeter Wemm 	ex_cinit(sp, &cmd, C_BANG, 2, vp->m_start.lno, vp->m_stop.lno, 0);
292b8ba871bSPeter Wemm 	EXP(sp)->argsoff = 0;			/* XXX */
293b8ba871bSPeter Wemm 
294b8ba871bSPeter Wemm 	if (argv_exp1(sp, &cmd, tp->lb + 1, tp->len - 1, 1))
295b8ba871bSPeter Wemm 		return (1);
296b8ba871bSPeter Wemm 	cmd.argc = EXP(sp)->argsoff;		/* XXX */
297b8ba871bSPeter Wemm 	cmd.argv = EXP(sp)->args;		/* XXX */
298b8ba871bSPeter Wemm 	return (v_exec_ex(sp, vp, &cmd));
299b8ba871bSPeter Wemm }
300b8ba871bSPeter Wemm 
301b8ba871bSPeter Wemm /*
302b8ba871bSPeter Wemm  * v_exec_ex --
303b8ba871bSPeter Wemm  *	Execute an ex command.
304b8ba871bSPeter Wemm  */
305b8ba871bSPeter Wemm static int
v_exec_ex(SCR * sp,VICMD * vp,EXCMD * exp)306b8ba871bSPeter Wemm v_exec_ex(SCR *sp, VICMD *vp, EXCMD *exp)
307b8ba871bSPeter Wemm {
308b8ba871bSPeter Wemm 	int rval;
309b8ba871bSPeter Wemm 
310b8ba871bSPeter Wemm 	rval = exp->cmd->fn(sp, exp);
311b8ba871bSPeter Wemm 	return (v_ex_done(sp, vp) || rval);
312b8ba871bSPeter Wemm }
313b8ba871bSPeter Wemm 
314b8ba871bSPeter Wemm /*
315b8ba871bSPeter Wemm  * v_ex -- :
316b8ba871bSPeter Wemm  *	Execute a colon command line.
317b8ba871bSPeter Wemm  *
318b8ba871bSPeter Wemm  * PUBLIC: int v_ex(SCR *, VICMD *);
319b8ba871bSPeter Wemm  */
320b8ba871bSPeter Wemm int
v_ex(SCR * sp,VICMD * vp)321b8ba871bSPeter Wemm v_ex(SCR *sp, VICMD *vp)
322b8ba871bSPeter Wemm {
323b8ba871bSPeter Wemm 	GS *gp;
324b8ba871bSPeter Wemm 	TEXT *tp;
325b8ba871bSPeter Wemm 	int do_cedit, do_resolution, ifcontinue;
326b8ba871bSPeter Wemm 
327b8ba871bSPeter Wemm 	gp = sp->gp;
328b8ba871bSPeter Wemm 
329b8ba871bSPeter Wemm 	/*
330b8ba871bSPeter Wemm 	 * !!!
331b8ba871bSPeter Wemm 	 * If we put out more than a single line of messages, or ex trashes
332b8ba871bSPeter Wemm 	 * the screen, the user may continue entering ex commands.  We find
333b8ba871bSPeter Wemm 	 * this out when we do the screen/message resolution.  We can't enter
334b8ba871bSPeter Wemm 	 * completely into ex mode however, because the user can elect to
335b8ba871bSPeter Wemm 	 * return into vi mode by entering any key, i.e. we have to be in raw
336b8ba871bSPeter Wemm 	 * mode.
337b8ba871bSPeter Wemm 	 */
338b8ba871bSPeter Wemm 	for (do_cedit = do_resolution = 0;;) {
339b8ba871bSPeter Wemm 		/*
340b8ba871bSPeter Wemm 		 * !!!
341b8ba871bSPeter Wemm 		 * There may already be an ex command waiting to run.  If
342b8ba871bSPeter Wemm 		 * so, we continue with it.
343b8ba871bSPeter Wemm 		 */
344b8ba871bSPeter Wemm 		if (!EXCMD_RUNNING(gp)) {
345b8ba871bSPeter Wemm 			/* Get a command. */
346b8ba871bSPeter Wemm 			if (v_tcmd(sp, vp, ':',
347b8ba871bSPeter Wemm 			    TXT_BS | TXT_CEDIT | TXT_FILEC | TXT_PROMPT))
348b8ba871bSPeter Wemm 				return (1);
349b8ba871bSPeter Wemm 			tp = TAILQ_FIRST(sp->tiq);
350b8ba871bSPeter Wemm 
351b8ba871bSPeter Wemm 			/*
352b8ba871bSPeter Wemm 			 * If the user entered a single <esc>, they want to
353b8ba871bSPeter Wemm 			 * edit their colon command history.  If they already
354b8ba871bSPeter Wemm 			 * entered some text, move it into the edit history.
355b8ba871bSPeter Wemm 			 */
356b8ba871bSPeter Wemm 			if (tp->term == TERM_CEDIT) {
357b8ba871bSPeter Wemm 				if (tp->len > 1 && v_ecl_log(sp, tp))
358b8ba871bSPeter Wemm 					return (1);
359b8ba871bSPeter Wemm 				do_cedit = 1;
360b8ba871bSPeter Wemm 				break;
361b8ba871bSPeter Wemm 			}
362b8ba871bSPeter Wemm 
363b8ba871bSPeter Wemm 			/* If the user didn't enter anything, return. */
364b8ba871bSPeter Wemm 			if (tp->term == TERM_BS)
365b8ba871bSPeter Wemm 				break;
366b8ba871bSPeter Wemm 
367b8ba871bSPeter Wemm 			/* If the user changed their mind, return. */
368b8ba871bSPeter Wemm 			if (tp->term != TERM_OK)
369b8ba871bSPeter Wemm 				break;
370b8ba871bSPeter Wemm 
371b8ba871bSPeter Wemm 			/* Log the command. */
372b8ba871bSPeter Wemm 			if (O_STR(sp, O_CEDIT) != NULL && v_ecl_log(sp, tp))
373b8ba871bSPeter Wemm 				return (1);
374b8ba871bSPeter Wemm 
375b8ba871bSPeter Wemm 			/* Push a command on the command stack. */
376b8ba871bSPeter Wemm 			if (ex_run_str(sp, NULL, tp->lb, tp->len, 0, 1))
377b8ba871bSPeter Wemm 				return (1);
378b8ba871bSPeter Wemm 		}
379b8ba871bSPeter Wemm 
380b8ba871bSPeter Wemm 		/* Home the cursor. */
381b8ba871bSPeter Wemm 		vs_home(sp);
382b8ba871bSPeter Wemm 
383b8ba871bSPeter Wemm 		/*
384b8ba871bSPeter Wemm 		 * !!!
385b8ba871bSPeter Wemm 		 * If the editor wrote the screen behind curses back, put out
386b8ba871bSPeter Wemm 		 * a <newline> so that we don't overwrite the user's command
387b8ba871bSPeter Wemm 		 * with its output or the next want-to-continue? message.  This
388b8ba871bSPeter Wemm 		 * doesn't belong here, but I can't find another place to put
389b8ba871bSPeter Wemm 		 * it.  See, we resolved the output from the last ex command,
390b8ba871bSPeter Wemm 		 * and the user entered another one.  This is the only place
391b8ba871bSPeter Wemm 		 * where we have control before the ex command writes output.
392b8ba871bSPeter Wemm 		 * We could get control in vs_msg(), but we have no way to know
393b8ba871bSPeter Wemm 		 * if command didn't put out any output when we try and resolve
394b8ba871bSPeter Wemm 		 * this command.  This fixes a bug where combinations of ex
395b8ba871bSPeter Wemm 		 * commands, e.g. ":set<CR>:!date<CR>:set" didn't look right.
396b8ba871bSPeter Wemm 		 */
397b8ba871bSPeter Wemm 		if (F_ISSET(sp, SC_SCR_EXWROTE))
398b8ba871bSPeter Wemm 			(void)putchar('\n');
399b8ba871bSPeter Wemm 
400b8ba871bSPeter Wemm 		/* Call the ex parser. */
401b8ba871bSPeter Wemm 		(void)ex_cmd(sp);
402b8ba871bSPeter Wemm 
403b8ba871bSPeter Wemm 		/* Flush ex messages. */
404b8ba871bSPeter Wemm 		(void)ex_fflush(sp);
405b8ba871bSPeter Wemm 
406b8ba871bSPeter Wemm 		/* Resolve any messages. */
407b8ba871bSPeter Wemm 		if (vs_ex_resolve(sp, &ifcontinue))
408b8ba871bSPeter Wemm 			return (1);
409b8ba871bSPeter Wemm 
410b8ba871bSPeter Wemm 		/*
411b8ba871bSPeter Wemm 		 * Continue or return.  If continuing, make sure that we
412b8ba871bSPeter Wemm 		 * eventually do resolution.
413b8ba871bSPeter Wemm 		 */
414b8ba871bSPeter Wemm 		if (!ifcontinue)
415b8ba871bSPeter Wemm 			break;
416b8ba871bSPeter Wemm 		do_resolution = 1;
417b8ba871bSPeter Wemm 
418b8ba871bSPeter Wemm 		/* If we're continuing, it's a new command. */
419b8ba871bSPeter Wemm 		++sp->ccnt;
420b8ba871bSPeter Wemm 	}
421b8ba871bSPeter Wemm 
422b8ba871bSPeter Wemm 	/*
423b8ba871bSPeter Wemm 	 * If the user previously continued an ex command, we have to do
424b8ba871bSPeter Wemm 	 * resolution to clean up the screen.  Don't wait, we already did
425b8ba871bSPeter Wemm 	 * that.
426b8ba871bSPeter Wemm 	 */
427b8ba871bSPeter Wemm 	if (do_resolution) {
428b8ba871bSPeter Wemm 		F_SET(sp, SC_EX_WAIT_NO);
429b8ba871bSPeter Wemm 		if (vs_ex_resolve(sp, &ifcontinue))
430b8ba871bSPeter Wemm 			return (1);
431725f5cdbSJaakko Heinonen 	}
432725f5cdbSJaakko Heinonen 
433725f5cdbSJaakko Heinonen 	/* Cleanup from the ex command. */
434725f5cdbSJaakko Heinonen 	if (v_ex_done(sp, vp))
435b8ba871bSPeter Wemm 		return (1);
436b8ba871bSPeter Wemm 
437b8ba871bSPeter Wemm 	/* The user may want to edit their colon command history. */
438b8ba871bSPeter Wemm 	if (do_cedit)
439b8ba871bSPeter Wemm 		return (v_ecl(sp));
440b8ba871bSPeter Wemm 
441b8ba871bSPeter Wemm 	return (0);
442b8ba871bSPeter Wemm }
443b8ba871bSPeter Wemm 
444b8ba871bSPeter Wemm /*
445b8ba871bSPeter Wemm  * v_ex_done --
446b8ba871bSPeter Wemm  *	Cleanup from an ex command.
447b8ba871bSPeter Wemm  */
448b8ba871bSPeter Wemm static int
v_ex_done(SCR * sp,VICMD * vp)449b8ba871bSPeter Wemm v_ex_done(SCR *sp, VICMD *vp)
450b8ba871bSPeter Wemm {
451b8ba871bSPeter Wemm 	size_t len;
452b8ba871bSPeter Wemm 
453b8ba871bSPeter Wemm 	/*
454b8ba871bSPeter Wemm 	 * The only cursor modifications are real, however, the underlying
455b8ba871bSPeter Wemm 	 * line may have changed; don't trust anything.  This code has been
456b8ba871bSPeter Wemm 	 * a remarkably fertile place for bugs.  Do a reality check on a
457b8ba871bSPeter Wemm 	 * cursor value, and make sure it's okay.  If necessary, change it.
458b8ba871bSPeter Wemm 	 * Ex keeps track of the line number, but it cares less about the
459b8ba871bSPeter Wemm 	 * column and it may have disappeared.
460b8ba871bSPeter Wemm 	 *
461b8ba871bSPeter Wemm 	 * Don't trust ANYTHING.
462b8ba871bSPeter Wemm 	 *
463b8ba871bSPeter Wemm 	 * XXX
464b8ba871bSPeter Wemm 	 * Ex will soon have to start handling the column correctly; see
465b8ba871bSPeter Wemm 	 * the POSIX 1003.2 standard.
466b8ba871bSPeter Wemm 	 */
467b8ba871bSPeter Wemm 	if (db_eget(sp, sp->lno, NULL, &len, NULL)) {
468b8ba871bSPeter Wemm 		sp->lno = 1;
469b8ba871bSPeter Wemm 		sp->cno = 0;
470b8ba871bSPeter Wemm 	} else if (sp->cno >= len)
471b8ba871bSPeter Wemm 		sp->cno = len ? len - 1 : 0;
472b8ba871bSPeter Wemm 
473b8ba871bSPeter Wemm 	vp->m_final.lno = sp->lno;
474b8ba871bSPeter Wemm 	vp->m_final.cno = sp->cno;
475b8ba871bSPeter Wemm 
476b8ba871bSPeter Wemm 	/*
477b8ba871bSPeter Wemm 	 * Don't re-adjust the cursor after executing an ex command,
478b8ba871bSPeter Wemm 	 * and ex movements are permanent.
479b8ba871bSPeter Wemm 	 */
480b8ba871bSPeter Wemm 	F_CLR(vp, VM_RCM_MASK);
481b8ba871bSPeter Wemm 	F_SET(vp, VM_RCM_SET);
482b8ba871bSPeter Wemm 
483b8ba871bSPeter Wemm 	return (0);
484b8ba871bSPeter Wemm }
485b8ba871bSPeter Wemm 
486b8ba871bSPeter Wemm /*
487b8ba871bSPeter Wemm  * v_ecl --
488b8ba871bSPeter Wemm  *	Start an edit window on the colon command-line commands.
489b8ba871bSPeter Wemm  */
490b8ba871bSPeter Wemm static int
v_ecl(SCR * sp)491b8ba871bSPeter Wemm v_ecl(SCR *sp)
492b8ba871bSPeter Wemm {
493b8ba871bSPeter Wemm 	GS *gp;
494b8ba871bSPeter Wemm 	SCR *new;
495b8ba871bSPeter Wemm 
496b8ba871bSPeter Wemm 	/* Initialize the screen, if necessary. */
497b8ba871bSPeter Wemm 	gp = sp->gp;
498b8ba871bSPeter Wemm 	if (gp->ccl_sp == NULL && v_ecl_init(sp))
499b8ba871bSPeter Wemm 		return (1);
500b8ba871bSPeter Wemm 
501b8ba871bSPeter Wemm 	/* Get a new screen. */
502b8ba871bSPeter Wemm 	if (screen_init(gp, sp, &new))
503b8ba871bSPeter Wemm 		return (1);
504b8ba871bSPeter Wemm 	if (vs_split(sp, new, 1)) {
505b8ba871bSPeter Wemm 		(void)screen_end(new);
506b8ba871bSPeter Wemm 		return (1);
507b8ba871bSPeter Wemm 	}
508b8ba871bSPeter Wemm 
509b8ba871bSPeter Wemm 	/* Attach to the screen. */
510b8ba871bSPeter Wemm 	new->ep = gp->ccl_sp->ep;
511b8ba871bSPeter Wemm 	++new->ep->refcnt;
512b8ba871bSPeter Wemm 
513b8ba871bSPeter Wemm 	new->frp = gp->ccl_sp->frp;
514b8ba871bSPeter Wemm 	new->frp->flags = sp->frp->flags;
515b8ba871bSPeter Wemm 
516b8ba871bSPeter Wemm 	/* Move the cursor to the end. */
517b8ba871bSPeter Wemm 	(void)db_last(new, &new->lno);
518b8ba871bSPeter Wemm 	if (new->lno == 0)
519b8ba871bSPeter Wemm 		new->lno = 1;
520b8ba871bSPeter Wemm 
521b8ba871bSPeter Wemm 	/* Remember the originating window. */
522b8ba871bSPeter Wemm 	sp->ccl_parent = sp;
523b8ba871bSPeter Wemm 
524b8ba871bSPeter Wemm 	/* It's a special window. */
525b8ba871bSPeter Wemm 	F_SET(new, SC_COMEDIT);
526b8ba871bSPeter Wemm 
527b8ba871bSPeter Wemm #if defined(USE_WIDECHAR) && defined(USE_ICONV)
528b8ba871bSPeter Wemm 	/* Bypass iconv on writing to DB. */
529b8ba871bSPeter Wemm 	o_set(new, O_FILEENCODING, OS_STRDUP, codeset(), 0);
530b8ba871bSPeter Wemm #endif
531b8ba871bSPeter Wemm 
532b8ba871bSPeter Wemm 	/* Set up the switch. */
533b8ba871bSPeter Wemm 	sp->nextdisp = new;
534b8ba871bSPeter Wemm 	F_SET(sp, SC_SSWITCH);
535b8ba871bSPeter Wemm 	return (0);
536b8ba871bSPeter Wemm }
537b8ba871bSPeter Wemm 
538b8ba871bSPeter Wemm /*
539b8ba871bSPeter Wemm  * v_ecl_exec --
540b8ba871bSPeter Wemm  *	Execute a command from a colon command-line window.
541b8ba871bSPeter Wemm  *
542b8ba871bSPeter Wemm  * PUBLIC: int v_ecl_exec(SCR *);
543b8ba871bSPeter Wemm  */
544b8ba871bSPeter Wemm int
v_ecl_exec(SCR * sp)545b8ba871bSPeter Wemm v_ecl_exec(SCR *sp)
546b8ba871bSPeter Wemm {
547b8ba871bSPeter Wemm 	size_t len;
548b8ba871bSPeter Wemm 	CHAR_T *p;
549b8ba871bSPeter Wemm 
550b8ba871bSPeter Wemm 	if (db_get(sp, sp->lno, 0, &p, &len) && sp->lno == 1) {
551b8ba871bSPeter Wemm 		v_emsg(sp, NULL, VIM_EMPTY);
552b8ba871bSPeter Wemm 		return (1);
553b8ba871bSPeter Wemm 	}
554b8ba871bSPeter Wemm 	if (len == 0) {
555b8ba871bSPeter Wemm 		msgq(sp, M_BERR, "307|No ex command to execute");
556b8ba871bSPeter Wemm 		return (1);
557b8ba871bSPeter Wemm 	}
558b8ba871bSPeter Wemm 
559b8ba871bSPeter Wemm 	/* Push the command on the command stack. */
560b8ba871bSPeter Wemm 	if (ex_run_str(sp, NULL, p, len, 0, 0))
561b8ba871bSPeter Wemm 		return (1);
562b8ba871bSPeter Wemm 
563b8ba871bSPeter Wemm 	/* Set up the switch. */
564b8ba871bSPeter Wemm 	sp->nextdisp = sp->ccl_parent;
565b8ba871bSPeter Wemm 	F_SET(sp, SC_EXIT);
566b8ba871bSPeter Wemm 	return (0);
567b8ba871bSPeter Wemm }
568b8ba871bSPeter Wemm 
569b8ba871bSPeter Wemm /*
570b8ba871bSPeter Wemm  * v_ecl_log --
571b8ba871bSPeter Wemm  *	Log a command into the colon command-line log file.
572b8ba871bSPeter Wemm  */
573b8ba871bSPeter Wemm static int
v_ecl_log(SCR * sp,TEXT * tp)574b8ba871bSPeter Wemm v_ecl_log(SCR *sp, TEXT *tp)
575b8ba871bSPeter Wemm {
576b8ba871bSPeter Wemm 	recno_t lno;
577b8ba871bSPeter Wemm 	int rval;
578b8ba871bSPeter Wemm 	CHAR_T *p;
579b8ba871bSPeter Wemm 	size_t len;
580b8ba871bSPeter Wemm 	SCR *ccl_sp;
581b8ba871bSPeter Wemm 
582b8ba871bSPeter Wemm 	/* Initialize the screen, if necessary. */
583b8ba871bSPeter Wemm 	if (sp->gp->ccl_sp == NULL && v_ecl_init(sp))
584b8ba871bSPeter Wemm 		return (1);
585b8ba871bSPeter Wemm 
586b8ba871bSPeter Wemm 	ccl_sp = sp->gp->ccl_sp;
587b8ba871bSPeter Wemm 
588b8ba871bSPeter Wemm 	/*
589b8ba871bSPeter Wemm 	 * Don't log colon command window commands into the colon command
590b8ba871bSPeter Wemm 	 * window...
591b8ba871bSPeter Wemm 	 */
592b8ba871bSPeter Wemm 	if (sp->ep == ccl_sp->ep)
593b8ba871bSPeter Wemm 		return (0);
594b8ba871bSPeter Wemm 
595b8ba871bSPeter Wemm 	if (db_last(ccl_sp, &lno)) {
596b8ba871bSPeter Wemm 		return (1);
597b8ba871bSPeter Wemm 	}
598b8ba871bSPeter Wemm 	/* Don't log line that is identical to previous one */
599b8ba871bSPeter Wemm 	if (lno > 0 &&
600b8ba871bSPeter Wemm 	    !db_get(ccl_sp, lno, 0, &p, &len) &&
601b8ba871bSPeter Wemm 	    len == tp->len &&
602b8ba871bSPeter Wemm 	    !MEMCMP(tp->lb, p, len))
603b8ba871bSPeter Wemm 		rval = 0;
604b8ba871bSPeter Wemm 	else {
605b8ba871bSPeter Wemm 		rval = db_append(ccl_sp, 0, lno, tp->lb, tp->len);
606b8ba871bSPeter Wemm 		/* XXXX end "transaction" on ccl */
607b8ba871bSPeter Wemm 		/* Is this still necessary now that we no longer hijack sp ? */
608b8ba871bSPeter Wemm 		log_cursor(ccl_sp);
609b8ba871bSPeter Wemm 	}
610b8ba871bSPeter Wemm 
611b8ba871bSPeter Wemm 	return (rval);
612b8ba871bSPeter Wemm }
613b8ba871bSPeter Wemm 
614b8ba871bSPeter Wemm /*
615b8ba871bSPeter Wemm  * v_ecl_init --
616b8ba871bSPeter Wemm  *	Initialize the colon command-line log file.
617b8ba871bSPeter Wemm  */
618b8ba871bSPeter Wemm static int
v_ecl_init(SCR * sp)619b8ba871bSPeter Wemm v_ecl_init(SCR *sp)
620b8ba871bSPeter Wemm {
621b8ba871bSPeter Wemm 	FREF *frp;
622b8ba871bSPeter Wemm 	GS *gp;
623b8ba871bSPeter Wemm 
624b8ba871bSPeter Wemm 	gp = sp->gp;
625b8ba871bSPeter Wemm 
626b8ba871bSPeter Wemm 	/* Get a temporary file. */
627b8ba871bSPeter Wemm 	if ((frp = file_add(sp, NULL)) == NULL)
628b8ba871bSPeter Wemm 		return (1);
629b8ba871bSPeter Wemm 
630b8ba871bSPeter Wemm 	/*
631b8ba871bSPeter Wemm 	 * XXX
632b8ba871bSPeter Wemm 	 * Create a screen -- the file initialization code wants one.
633b8ba871bSPeter Wemm 	 */
634b8ba871bSPeter Wemm 	if (screen_init(gp, sp, &gp->ccl_sp))
635b8ba871bSPeter Wemm 		return (1);
636b8ba871bSPeter Wemm 	if (file_init(gp->ccl_sp, frp, NULL, 0)) {
637b8ba871bSPeter Wemm 		(void)screen_end(gp->ccl_sp);
638b8ba871bSPeter Wemm 		gp->ccl_sp = NULL;
639b8ba871bSPeter Wemm 		return (1);
640b8ba871bSPeter Wemm 	}
641b8ba871bSPeter Wemm 
642b8ba871bSPeter Wemm 	/* The underlying file isn't recoverable. */
643b8ba871bSPeter Wemm 	F_CLR(gp->ccl_sp->ep, F_RCV_ON);
644b8ba871bSPeter Wemm 
645b8ba871bSPeter Wemm 	return (0);
646b8ba871bSPeter Wemm }
647b8ba871bSPeter Wemm