xref: /freebsd/contrib/nvi/vi/v_ex.c (revision 02e9120893770924227138ba49df1edb3896112a)
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 
26 static int v_ecl(SCR *);
27 static int v_ecl_init(SCR *);
28 static int v_ecl_log(SCR *, TEXT *);
29 static int v_ex_done(SCR *, VICMD *);
30 static 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  */
38 int
39 v_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  */
54 int
55 v_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  */
90 int
91 v_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  */
119 int
120 v_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  */
135 int
136 v_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  */
151 int
152 v_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  */
167 int
168 v_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  */
200 int
201 v_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  */
216 int
217 v_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  */
231 int
232 v_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  */
305 static int
306 v_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  */
320 int
321 v_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  */
448 static int
449 v_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  */
490 static int
491 v_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  */
544 int
545 v_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  */
573 static int
574 v_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  */
618 static int
619 v_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