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