vi.c revision 19304
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#ifndef lint
13static const char sccsid[] = "@(#)vi.c	10.57 (Berkeley) 10/13/96";
14#endif /* not lint */
15
16#include <sys/types.h>
17#include <sys/queue.h>
18#include <sys/time.h>
19
20#include <bitstring.h>
21#include <ctype.h>
22#include <errno.h>
23#include <limits.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <unistd.h>
28
29#include "../common/common.h"
30#include "vi.h"
31
32typedef enum {
33	GC_ERR, GC_ERR_NOFLUSH, GC_EVENT, GC_FATAL, GC_INTERRUPT, GC_OK
34} gcret_t;
35
36static VIKEYS const
37	       *v_alias __P((SCR *, VICMD *, VIKEYS const *));
38static gcret_t	v_cmd __P((SCR *, VICMD *, VICMD *, VICMD *, int *, int *));
39static int	v_count __P((SCR *, ARG_CHAR_T, u_long *));
40static void	v_dtoh __P((SCR *));
41static int	v_init __P((SCR *));
42static gcret_t	v_key __P((SCR *, int, EVENT *, u_int32_t));
43static int	v_keyword __P((SCR *));
44static int	v_motion __P((SCR *, VICMD *, VICMD *, int *));
45
46#if defined(DEBUG) && defined(COMLOG)
47static void	v_comlog __P((SCR *, VICMD *));
48#endif
49
50/*
51 * Side-effect:
52 *	The dot structure can be set by the underlying vi functions,
53 *	see v_Put() and v_put().
54 */
55#define	DOT		(&VIP(sp)->sdot)
56#define	DOTMOTION	(&VIP(sp)->sdotmotion)
57
58/*
59 * vi --
60 * 	Main vi command loop.
61 *
62 * PUBLIC: int vi __P((SCR **));
63 */
64int
65vi(spp)
66	SCR **spp;
67{
68	GS *gp;
69	MARK abs;
70	SCR *next, *sp;
71	VICMD cmd, *vp;
72	VI_PRIVATE *vip;
73	int comcount, mapped, rval;
74
75	/* Get the first screen. */
76	sp = *spp;
77	gp = sp->gp;
78
79	/* Initialize the command structure. */
80	vp = &cmd;
81	memset(vp, 0, sizeof(VICMD));
82
83	/* Reset strange attraction. */
84	F_SET(vp, VM_RCM_SET);
85
86	/* Initialize the vi screen. */
87	if (v_init(sp))
88		return (1);
89
90	/* Set the focus. */
91	(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
92
93	for (vip = VIP(sp), rval = 0;;) {
94		/* Resolve messages. */
95		if (!MAPPED_KEYS_WAITING(sp) && vs_resolve(sp, NULL, 0))
96			goto ret;
97
98		/*
99		 * If not skipping a refresh, return to command mode and
100		 * refresh the screen.
101		 */
102		if (F_ISSET(vip, VIP_S_REFRESH))
103			F_CLR(vip, VIP_S_REFRESH);
104		else {
105			sp->showmode = SM_COMMAND;
106			if (vs_refresh(sp, 0))
107				goto ret;
108		}
109
110		/* Set the new favorite position. */
111		if (F_ISSET(vp, VM_RCM_SET | VM_RCM_SETFNB | VM_RCM_SETNNB)) {
112			F_CLR(vip, VIP_RCM_LAST);
113			(void)vs_column(sp, &sp->rcm);
114		}
115
116		/*
117		 * If not currently in a map, log the cursor position,
118		 * and set a flag so that this command can become the
119		 * DOT command.
120		 */
121		if (MAPPED_KEYS_WAITING(sp))
122			mapped = 1;
123		else {
124			if (log_cursor(sp))
125				goto err;
126			mapped = 0;
127		}
128
129		/*
130		 * There may be an ex command waiting, and we returned here
131		 * only because we exited a screen or file.  In this case,
132		 * we simply go back into the ex parser.
133		 */
134		if (EXCMD_RUNNING(gp)) {
135			vp->kp = &vikeys[':'];
136			goto ex_continue;
137		}
138
139		/* Refresh the command structure. */
140		memset(vp, 0, sizeof(VICMD));
141
142		/*
143		 * We get a command, which may or may not have an associated
144		 * motion.  If it does, we get it too, calling its underlying
145		 * function to get the resulting mark.  We then call the
146		 * command setting the cursor to the resulting mark.
147		 *
148		 * !!!
149		 * Vi historically flushed mapped characters on error, but
150		 * entering extra <escape> characters at the beginning of
151		 * a map wasn't considered an error -- in fact, users would
152		 * put leading <escape> characters in maps to clean up vi
153		 * state before the map was interpreted.  Beauty!
154		 */
155		switch (v_cmd(sp, DOT, vp, NULL, &comcount, &mapped)) {
156		case GC_ERR:
157			goto err;
158		case GC_ERR_NOFLUSH:
159			goto gc_err_noflush;
160		case GC_EVENT:
161			if (v_event_exec(sp, vp))
162				goto err;
163			goto gc_event;
164		case GC_FATAL:
165			goto ret;
166		case GC_INTERRUPT:
167			goto intr;
168		case GC_OK:
169			break;
170		}
171
172		/* Check for security setting. */
173		if (F_ISSET(vp->kp, V_SECURE) && O_ISSET(sp, O_SECURE)) {
174			ex_emsg(sp, KEY_NAME(sp, vp->key), EXM_SECURE);
175			goto err;
176		}
177
178		/*
179		 * Historical practice: if a dot command gets a new count,
180		 * any motion component goes away, i.e. "d3w2." deletes a
181		 * total of 5 words.
182		 */
183		if (F_ISSET(vp, VC_ISDOT) && comcount)
184			DOTMOTION->count = 1;
185
186		/* Copy the key flags into the local structure. */
187		F_SET(vp, vp->kp->flags);
188
189		/* Prepare to set the previous context. */
190		if (F_ISSET(vp, V_ABS | V_ABS_C | V_ABS_L)) {
191			abs.lno = sp->lno;
192			abs.cno = sp->cno;
193		}
194
195		/*
196		 * Set the three cursor locations to the current cursor.  The
197		 * underlying routines don't bother if the cursor doesn't move.
198		 * This also handles line commands (e.g. Y) defaulting to the
199		 * current line.
200		 */
201		vp->m_start.lno = vp->m_stop.lno = vp->m_final.lno = sp->lno;
202		vp->m_start.cno = vp->m_stop.cno = vp->m_final.cno = sp->cno;
203
204		/*
205		 * Do any required motion; v_motion sets the from MARK and the
206		 * line mode flag, as well as the VM_RCM flags.
207		 */
208		if (F_ISSET(vp, V_MOTION) &&
209		    v_motion(sp, DOTMOTION, vp, &mapped)) {
210			if (INTERRUPTED(sp))
211				goto intr;
212			goto err;
213		}
214
215		/*
216		 * If a count is set and the command is line oriented, set the
217		 * to MARK here relative to the cursor/from MARK.  This is for
218		 * commands that take both counts and motions, i.e. "4yy" and
219		 * "y%".  As there's no way the command can know which the user
220		 * did, we have to do it here.  (There are commands that are
221		 * line oriented and that take counts ("#G", "#H"), for which
222		 * this calculation is either completely meaningless or wrong.
223		 * Each command must validate the value for itself.
224		 */
225		if (F_ISSET(vp, VC_C1SET) && F_ISSET(vp, VM_LMODE))
226			vp->m_stop.lno += vp->count - 1;
227
228		/* Increment the command count. */
229		++sp->ccnt;
230
231#if defined(DEBUG) && defined(COMLOG)
232		v_comlog(sp, vp);
233#endif
234		/* Call the function. */
235ex_continue:	if (vp->kp->func(sp, vp))
236			goto err;
237gc_event:
238#ifdef DEBUG
239		/* Make sure no function left the temporary space locked. */
240		if (F_ISSET(gp, G_TMP_INUSE)) {
241			F_CLR(gp, G_TMP_INUSE);
242			msgq(sp, M_ERR,
243			    "232|vi: temporary buffer not released");
244		}
245#endif
246		/*
247		 * If we're exiting this screen, move to the next one, or, if
248		 * there aren't any more, return to the main editor loop.  The
249		 * ordering is careful, don't discard the contents of sp until
250		 * the end.
251		 */
252		if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
253			if (file_end(sp, NULL, F_ISSET(sp, SC_EXIT_FORCE)))
254				goto ret;
255			if (vs_discard(sp, &next))
256				goto ret;
257			if (next == NULL && vs_swap(sp, &next, NULL))
258				goto ret;
259			*spp = next;
260			if (screen_end(sp))
261				goto ret;
262			if (next == NULL)
263				break;
264
265			/* Switch screens, change focus. */
266			sp = next;
267			vip = VIP(sp);
268			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
269
270			/* Don't trust the cursor. */
271			F_SET(vip, VIP_CUR_INVALID);
272
273			continue;
274		}
275
276		/*
277		 * Set the dot command structure.
278		 *
279		 * !!!
280		 * Historically, commands which used mapped keys did not
281		 * set the dot command, with the exception of the text
282		 * input commands.
283		 */
284		if (F_ISSET(vp, V_DOT) && !mapped) {
285			*DOT = cmd;
286			F_SET(DOT, VC_ISDOT);
287
288			/*
289			 * If a count was supplied for both the command and
290			 * its motion, the count was used only for the motion.
291			 * Turn the count back on for the dot structure.
292			 */
293			if (F_ISSET(vp, VC_C1RESET))
294				F_SET(DOT, VC_C1SET);
295
296			/* VM flags aren't retained. */
297			F_CLR(DOT, VM_COMMASK | VM_RCM_MASK);
298		}
299
300		/*
301		 * Some vi row movements are "attracted" to the last position
302		 * set, i.e. the VM_RCM commands are moths to the VM_RCM_SET
303		 * commands' candle.  If the movement is to the EOL the vi
304		 * command handles it.  If it's to the beginning, we handle it
305		 * here.
306		 *
307		 * Note, some commands (e.g. _, ^) don't set the VM_RCM_SETFNB
308		 * flag, but do the work themselves.  The reason is that they
309		 * have to modify the column in case they're being used as a
310		 * motion component.  Other similar commands (e.g. +, -) don't
311		 * have to modify the column because they are always line mode
312		 * operations when used as motions, so the column number isn't
313		 * of any interest.
314		 *
315		 * Does this totally violate the screen and editor layering?
316		 * You betcha.  As they say, if you think you understand it,
317		 * you don't.
318		 */
319		switch (F_ISSET(vp, VM_RCM_MASK)) {
320		case 0:
321		case VM_RCM_SET:
322			break;
323		case VM_RCM:
324			vp->m_final.cno = vs_rcm(sp,
325			    vp->m_final.lno, F_ISSET(vip, VIP_RCM_LAST));
326			break;
327		case VM_RCM_SETLAST:
328			F_SET(vip, VIP_RCM_LAST);
329			break;
330		case VM_RCM_SETFNB:
331			vp->m_final.cno = 0;
332			/* FALLTHROUGH */
333		case VM_RCM_SETNNB:
334			if (nonblank(sp, vp->m_final.lno, &vp->m_final.cno))
335				goto err;
336			break;
337		default:
338			abort();
339		}
340
341		/* Update the cursor. */
342		sp->lno = vp->m_final.lno;
343		sp->cno = vp->m_final.cno;
344
345		/*
346		 * Set the absolute mark -- set even if a tags or similar
347		 * command, since the tag may be moving to the same file.
348		 */
349		if ((F_ISSET(vp, V_ABS) ||
350		    F_ISSET(vp, V_ABS_L) && sp->lno != abs.lno ||
351		    F_ISSET(vp, V_ABS_C) &&
352		    (sp->lno != abs.lno || sp->cno != abs.cno)) &&
353		    mark_set(sp, ABSMARK1, &abs, 1))
354			goto err;
355
356		if (0) {
357err:			if (v_event_flush(sp, CH_MAPPED))
358				msgq(sp, M_BERR,
359			    "110|Vi command failed: mapped keys discarded");
360		}
361
362		/*
363		 * Check and clear interrupts.  There's an obvious race, but
364		 * it's not worth fixing.
365		 */
366gc_err_noflush:	if (INTERRUPTED(sp)) {
367intr:			CLR_INTERRUPT(sp);
368			if (v_event_flush(sp, CH_MAPPED))
369				msgq(sp, M_ERR,
370				    "231|Interrupted: mapped keys discarded");
371			else
372				msgq(sp, M_ERR, "236|Interrupted");
373		}
374
375		/* If the last command switched screens, update. */
376		if (F_ISSET(sp, SC_SSWITCH)) {
377			F_CLR(sp, SC_SSWITCH);
378
379			/*
380			 * If the current screen is still displayed, it will
381			 * need a new status line.
382			 */
383			F_SET(sp, SC_STATUS);
384
385			/* Switch screens, change focus. */
386			sp = sp->nextdisp;
387			vip = VIP(sp);
388			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
389
390			/* Don't trust the cursor. */
391			F_SET(vip, VIP_CUR_INVALID);
392
393			/* Refresh so we can display messages. */
394			if (vs_refresh(sp, 1))
395				return (1);
396		}
397
398		/* If the last command switched files, change focus. */
399		if (F_ISSET(sp, SC_FSWITCH)) {
400			F_CLR(sp, SC_FSWITCH);
401			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
402		}
403
404		/* If leaving vi, return to the main editor loop. */
405		if (F_ISSET(gp, G_SRESTART) || F_ISSET(sp, SC_EX)) {
406			*spp = sp;
407			v_dtoh(sp);
408			break;
409		}
410	}
411	if (0)
412ret:		rval = 1;
413	return (rval);
414}
415
416#define	KEY(key, ec_flags) {						\
417	if ((gcret = v_key(sp, 0, &ev, ec_flags)) != GC_OK)		\
418		return (gcret);						\
419	if (ev.e_value == K_ESCAPE)					\
420		goto esc;						\
421	if (F_ISSET(&ev.e_ch, CH_MAPPED))				\
422		*mappedp = 1;						\
423	key = ev.e_c;							\
424}
425
426/*
427 * The O_TILDEOP option makes the ~ command take a motion instead
428 * of a straight count.  This is the replacement structure we use
429 * instead of the one currently in the VIKEYS table.
430 *
431 * XXX
432 * This should probably be deleted -- it's not all that useful, and
433 * we get help messages wrong.
434 */
435VIKEYS const tmotion = {
436	v_mulcase,	V_CNT|V_DOT|V_MOTION|VM_RCM_SET,
437	"[count]~[count]motion",
438	" ~ change case to motion"
439};
440
441/*
442 * v_cmd --
443 *
444 * The command structure for vi is less complex than ex (and don't think
445 * I'm not grateful!)  The command syntax is:
446 *
447 *	[count] [buffer] [count] key [[motion] | [buffer] [character]]
448 *
449 * and there are several special cases.  The motion value is itself a vi
450 * command, with the syntax:
451 *
452 *	[count] key [character]
453 */
454static gcret_t
455v_cmd(sp, dp, vp, ismotion, comcountp, mappedp)
456	SCR *sp;
457	VICMD *dp, *vp;
458	VICMD *ismotion;	/* Previous key if getting motion component. */
459	int *comcountp, *mappedp;
460{
461	enum { COMMANDMODE, ISPARTIAL, NOTPARTIAL } cpart;
462	EVENT ev;
463	VIKEYS const *kp;
464	gcret_t gcret;
465	u_int flags;
466	CHAR_T key;
467	char *s;
468
469	/*
470	 * Get a key.
471	 *
472	 * <escape> cancels partial commands, i.e. a command where at least
473	 * one non-numeric character has been entered.  Otherwise, it beeps
474	 * the terminal.
475	 *
476	 * !!!
477	 * POSIX 1003.2-1992 explicitly disallows cancelling commands where
478	 * all that's been entered is a number, requiring that the terminal
479	 * be alerted.
480	 */
481	cpart = ismotion == NULL ? COMMANDMODE : ISPARTIAL;
482	if ((gcret =
483	    v_key(sp, ismotion == NULL, &ev, EC_MAPCOMMAND)) != GC_OK) {
484		if (gcret == GC_EVENT)
485			vp->ev = ev;
486		return (gcret);
487	}
488	if (ev.e_value == K_ESCAPE)
489		goto esc;
490	if (F_ISSET(&ev.e_ch, CH_MAPPED))
491		*mappedp = 1;
492	key = ev.e_c;
493
494	if (ismotion == NULL)
495		cpart = NOTPARTIAL;
496
497	/* Pick up optional buffer. */
498	if (key == '"') {
499		cpart = ISPARTIAL;
500		if (ismotion != NULL) {
501			v_emsg(sp, NULL, VIM_COMBUF);
502			return (GC_ERR);
503		}
504		KEY(vp->buffer, 0);
505		F_SET(vp, VC_BUFFER);
506
507		KEY(key, EC_MAPCOMMAND);
508	}
509
510	/*
511	 * Pick up optional count, where a leading 0 is not a count,
512	 * it's a command.
513	 */
514	if (isdigit(key) && key != '0') {
515		if (v_count(sp, key, &vp->count))
516			return (GC_ERR);
517		F_SET(vp, VC_C1SET);
518		*comcountp = 1;
519
520		KEY(key, EC_MAPCOMMAND);
521	} else
522		*comcountp = 0;
523
524	/* Pick up optional buffer. */
525	if (key == '"') {
526		cpart = ISPARTIAL;
527		if (F_ISSET(vp, VC_BUFFER)) {
528			msgq(sp, M_ERR, "234|Only one buffer may be specified");
529			return (GC_ERR);
530		}
531		if (ismotion != NULL) {
532			v_emsg(sp, NULL, VIM_COMBUF);
533			return (GC_ERR);
534		}
535		KEY(vp->buffer, 0);
536		F_SET(vp, VC_BUFFER);
537
538		KEY(key, EC_MAPCOMMAND);
539	}
540
541	/* Check for an OOB command key. */
542	cpart = ISPARTIAL;
543	if (key > MAXVIKEY) {
544		v_emsg(sp, KEY_NAME(sp, key), VIM_NOCOM);
545		return (GC_ERR);
546	}
547	kp = &vikeys[vp->key = key];
548
549	/*
550	 * !!!
551	 * Historically, D accepted and then ignored a count.  Match it.
552	 */
553	if (vp->key == 'D' && F_ISSET(vp, VC_C1SET)) {
554		*comcountp = 0;
555		vp->count = 0;
556		F_CLR(vp, VC_C1SET);
557	}
558
559	/* Check for command aliases. */
560	if (kp->func == NULL && (kp = v_alias(sp, vp, kp)) == NULL)
561		return (GC_ERR);
562
563	/* The tildeop option makes the ~ command take a motion. */
564	if (key == '~' && O_ISSET(sp, O_TILDEOP))
565		kp = &tmotion;
566
567	vp->kp = kp;
568
569	/*
570	 * Find the command.  The only legal command with no underlying
571	 * function is dot.  It's historic practice that <escape> doesn't
572	 * just erase the preceding number, it beeps the terminal as well.
573	 * It's a common problem, so just beep the terminal unless verbose
574	 * was set.
575	 */
576	if (kp->func == NULL) {
577		if (key != '.') {
578			v_emsg(sp, KEY_NAME(sp, key),
579			    ev.e_value == K_ESCAPE ? VIM_NOCOM_B : VIM_NOCOM);
580			return (GC_ERR);
581		}
582
583		/* If called for a motion command, stop now. */
584		if (dp == NULL)
585			goto usage;
586
587		/*
588		 * !!!
589		 * If a '.' is immediately entered after an undo command, we
590		 * replay the log instead of redoing the last command.  This
591		 * is necessary because 'u' can't set the dot command -- see
592		 * vi/v_undo.c:v_undo for details.
593		 */
594		if (VIP(sp)->u_ccnt == sp->ccnt) {
595			vp->kp = &vikeys['u'];
596			F_SET(vp, VC_ISDOT);
597			return (GC_OK);
598		}
599
600		/* Otherwise, a repeatable command must have been executed. */
601		if (!F_ISSET(dp, VC_ISDOT)) {
602			msgq(sp, M_ERR, "208|No command to repeat");
603			return (GC_ERR);
604		}
605
606		/* Set new count/buffer, if any, and return. */
607		if (F_ISSET(vp, VC_C1SET)) {
608			F_SET(dp, VC_C1SET);
609			dp->count = vp->count;
610		}
611		if (F_ISSET(vp, VC_BUFFER))
612			dp->buffer = vp->buffer;
613
614		*vp = *dp;
615		return (GC_OK);
616	}
617
618	/* Set the flags based on the command flags. */
619	flags = kp->flags;
620
621	/* Check for illegal count. */
622	if (F_ISSET(vp, VC_C1SET) && !LF_ISSET(V_CNT))
623		goto usage;
624
625	/* Illegal motion command. */
626	if (ismotion == NULL) {
627		/* Illegal buffer. */
628		if (!LF_ISSET(V_OBUF) && F_ISSET(vp, VC_BUFFER))
629			goto usage;
630
631		/* Required buffer. */
632		if (LF_ISSET(V_RBUF)) {
633			KEY(vp->buffer, 0);
634			F_SET(vp, VC_BUFFER);
635		}
636	}
637
638	/*
639	 * Special case: '[', ']' and 'Z' commands.  Doesn't the fact that
640	 * the *single* characters don't mean anything but the *doubled*
641	 * characters do, just frost your shorts?
642	 */
643	if (vp->key == '[' || vp->key == ']' || vp->key == 'Z') {
644		/*
645		 * Historically, half entered [[, ]] or Z commands weren't
646		 * cancelled by <escape>, the terminal was beeped instead.
647		 * POSIX.2-1992 probably didn't notice, and requires that
648		 * they be cancelled instead of beeping.  Seems fine to me.
649		 *
650		 * Don't set the EC_MAPCOMMAND flag, apparently ] is a popular
651		 * vi meta-character, and we don't want the user to wait while
652		 * we time out a possible mapping.  This *appears* to match
653		 * historic vi practice, but with mapping characters, you Just
654		 * Never Know.
655		 */
656		KEY(key, 0);
657
658		if (vp->key != key) {
659usage:			if (ismotion == NULL)
660				s = kp->usage;
661			else if (ismotion->key == '~' && O_ISSET(sp, O_TILDEOP))
662				s = tmotion.usage;
663			else
664				s = vikeys[ismotion->key].usage;
665			v_emsg(sp, s, VIM_USAGE);
666			return (GC_ERR);
667		}
668	}
669	/* Special case: 'z' command. */
670	if (vp->key == 'z') {
671		KEY(vp->character, 0);
672		if (isdigit(vp->character)) {
673			if (v_count(sp, vp->character, &vp->count2))
674				return (GC_ERR);
675			F_SET(vp, VC_C2SET);
676			KEY(vp->character, 0);
677		}
678	}
679
680	/*
681	 * Commands that have motion components can be doubled to
682	 * imply the current line.
683	 */
684	if (ismotion != NULL && ismotion->key != key && !LF_ISSET(V_MOVE)) {
685		msgq(sp, M_ERR, "210|%s may not be used as a motion command",
686		    KEY_NAME(sp, key));
687		return (GC_ERR);
688	}
689
690	/* Required character. */
691	if (LF_ISSET(V_CHAR))
692		KEY(vp->character, 0);
693
694	/* Get any associated cursor word. */
695	if (F_ISSET(kp, V_KEYW) && v_keyword(sp))
696		return (GC_ERR);
697
698	return (GC_OK);
699
700esc:	switch (cpart) {
701	case COMMANDMODE:
702		msgq(sp, M_BERR, "211|Already in command mode");
703		return (GC_ERR_NOFLUSH);
704	case ISPARTIAL:
705		break;
706	case NOTPARTIAL:
707		(void)sp->gp->scr_bell(sp);
708		break;
709	}
710	return (GC_ERR);
711}
712
713/*
714 * v_motion --
715 *
716 * Get resulting motion mark.
717 */
718static int
719v_motion(sp, dm, vp, mappedp)
720	SCR *sp;
721	VICMD *dm, *vp;
722	int *mappedp;
723{
724	VICMD motion;
725	size_t len;
726	u_long cnt;
727	u_int flags;
728	int tilde_reset, notused;
729
730	/*
731	 * If '.' command, use the dot motion, else get the motion command.
732	 * Clear any line motion flags, the subsequent motion isn't always
733	 * the same, i.e. "/aaa" may or may not be a line motion.
734	 */
735	if (F_ISSET(vp, VC_ISDOT)) {
736		motion = *dm;
737		F_SET(&motion, VC_ISDOT);
738		F_CLR(&motion, VM_COMMASK);
739	} else {
740		memset(&motion, 0, sizeof(VICMD));
741		if (v_cmd(sp, NULL, &motion, vp, &notused, mappedp) != GC_OK)
742			return (1);
743	}
744
745	/*
746	 * A count may be provided both to the command and to the motion, in
747	 * which case the count is multiplicative.  For example, "3y4y" is the
748	 * same as "12yy".  This count is provided to the motion command and
749	 * not to the regular function.
750	 */
751	cnt = motion.count = F_ISSET(&motion, VC_C1SET) ? motion.count : 1;
752	if (F_ISSET(vp, VC_C1SET)) {
753		motion.count *= vp->count;
754		F_SET(&motion, VC_C1SET);
755
756		/*
757		 * Set flags to restore the original values of the command
758		 * structure so dot commands can change the count values,
759		 * e.g. "2dw" "3." deletes a total of five words.
760		 */
761		F_CLR(vp, VC_C1SET);
762		F_SET(vp, VC_C1RESET);
763	}
764
765	/*
766	 * Some commands can be repeated to indicate the current line.  In
767	 * this case, or if the command is a "line command", set the flags
768	 * appropriately.  If not a doubled command, run the function to get
769	 * the resulting mark.
770 	 */
771	if (vp->key == motion.key) {
772		F_SET(vp, VM_LDOUBLE | VM_LMODE);
773
774		/* Set the origin of the command. */
775		vp->m_start.lno = sp->lno;
776		vp->m_start.cno = 0;
777
778		/*
779		 * Set the end of the command.
780		 *
781		 * If the current line is missing, i.e. the file is empty,
782		 * historic vi permitted a "cc" or "!!" command to insert
783		 * text.
784		 */
785		vp->m_stop.lno = sp->lno + motion.count - 1;
786		if (db_get(sp, vp->m_stop.lno, 0, NULL, &len)) {
787			if (vp->m_stop.lno != 1 ||
788			   vp->key != 'c' && vp->key != '!') {
789				v_emsg(sp, NULL, VIM_EMPTY);
790				return (1);
791			}
792			vp->m_stop.cno = 0;
793		} else
794			vp->m_stop.cno = len ? len - 1 : 0;
795	} else {
796		/*
797		 * Motion commands change the underlying movement (*snarl*).
798		 * For example, "l" is illegal at the end of a line, but "dl"
799		 * is not.  Set flags so the function knows the situation.
800		 */
801		motion.rkp = vp->kp;
802
803		/*
804		 * XXX
805		 * Use yank instead of creating a new motion command, it's a
806		 * lot easier for now.
807		 */
808		if (vp->kp == &tmotion) {
809			tilde_reset = 1;
810			vp->kp = &vikeys['y'];
811		} else
812			tilde_reset = 0;
813
814		/*
815		 * Copy the key flags into the local structure, except for the
816		 * RCM flags -- the motion command will set the RCM flags in
817		 * the vp structure if necessary.  This means that the motion
818		 * command is expected to determine where the cursor ends up!
819		 * However, we save off the current RCM mask and restore it if
820		 * it no RCM flags are set by the motion command, with a small
821		 * modification.
822		 *
823		 * We replace the VM_RCM_SET flag with the VM_RCM flag.  This
824		 * is so that cursor movement doesn't set the relative position
825		 * unless the motion command explicitly specified it.  This
826		 * appears to match historic practice, but I've never been able
827		 * to develop a hard-and-fast rule.
828		 */
829		flags = F_ISSET(vp, VM_RCM_MASK);
830		if (LF_ISSET(VM_RCM_SET)) {
831			LF_SET(VM_RCM);
832			LF_CLR(VM_RCM_SET);
833		}
834		F_CLR(vp, VM_RCM_MASK);
835		F_SET(&motion, motion.kp->flags & ~VM_RCM_MASK);
836
837		/*
838		 * Set the three cursor locations to the current cursor.  This
839		 * permits commands like 'j' and 'k', that are line oriented
840		 * motions and have special cursor suck semantics when they are
841		 * used as standalone commands, to ignore column positioning.
842		 */
843		motion.m_final.lno =
844		    motion.m_stop.lno = motion.m_start.lno = sp->lno;
845		motion.m_final.cno =
846		    motion.m_stop.cno = motion.m_start.cno = sp->cno;
847
848		/* Run the function. */
849		if ((motion.kp->func)(sp, &motion))
850			return (1);
851
852		/*
853		 * If the current line is missing, i.e. the file is empty,
854		 * historic vi allowed "c<motion>" or "!<motion>" to insert
855		 * text.  Otherwise fail -- most motion commands will have
856		 * already failed, but some, e.g. G, succeed in empty files.
857		 */
858		if (!db_exist(sp, vp->m_stop.lno)) {
859			if (vp->m_stop.lno != 1 ||
860			   vp->key != 'c' && vp->key != '!') {
861				v_emsg(sp, NULL, VIM_EMPTY);
862				return (1);
863			}
864			vp->m_stop.cno = 0;
865		}
866
867		/*
868		 * XXX
869		 * See above.
870		 */
871		if (tilde_reset)
872			vp->kp = &tmotion;
873
874		/*
875		 * Copy cut buffer, line mode and cursor position information
876		 * from the motion command structure, i.e. anything that the
877		 * motion command can set for us.  The commands can flag the
878		 * movement as a line motion (see v_sentence) as well as set
879		 * the VM_RCM_* flags explicitly.
880		 */
881		F_SET(vp, F_ISSET(&motion, VM_COMMASK | VM_RCM_MASK));
882
883		/*
884		 * If the motion command set no relative motion flags, use
885		 * the (slightly) modified previous values.
886		 */
887		if (!F_ISSET(vp, VM_RCM_MASK))
888			F_SET(vp, flags);
889
890		/*
891		 * Commands can change behaviors based on the motion command
892		 * used, for example, the ! command repeated the last bang
893		 * command if N or n was used as the motion.
894		 */
895		vp->rkp = motion.kp;
896
897		/*
898		 * Motion commands can reset all of the cursor information.
899		 * If the motion is in the reverse direction, switch the
900		 * from and to MARK's so that it's in a forward direction.
901		 * Motions are from the from MARK to the to MARK (inclusive).
902		 */
903		if (motion.m_start.lno > motion.m_stop.lno ||
904		    motion.m_start.lno == motion.m_stop.lno &&
905		    motion.m_start.cno > motion.m_stop.cno) {
906			vp->m_start = motion.m_stop;
907			vp->m_stop = motion.m_start;
908		} else {
909			vp->m_start = motion.m_start;
910			vp->m_stop = motion.m_stop;
911		}
912		vp->m_final = motion.m_final;
913	}
914
915	/*
916	 * If the command sets dot, save the motion structure.  The motion
917	 * count was changed above and needs to be reset, that's why this
918	 * is done here, and not in the calling routine.
919	 */
920	if (F_ISSET(vp->kp, V_DOT)) {
921		*dm = motion;
922		dm->count = cnt;
923	}
924	return (0);
925}
926
927/*
928 * v_init --
929 *	Initialize the vi screen.
930 */
931static int
932v_init(sp)
933	SCR *sp;
934{
935	GS *gp;
936	VI_PRIVATE *vip;
937
938	gp = sp->gp;
939	vip = VIP(sp);
940
941	/* Switch into vi. */
942	if (gp->scr_screen(sp, SC_VI))
943		return (1);
944	(void)gp->scr_attr(sp, SA_ALTERNATE, 1);
945
946	F_CLR(sp, SC_EX | SC_SCR_EX);
947	F_SET(sp, SC_VI);
948
949	/*
950	 * Initialize screen values.
951	 *
952	 * Small windows: see vs_refresh(), section 6a.
953	 *
954	 * Setup:
955	 *	t_minrows is the minimum rows to display
956	 *	t_maxrows is the maximum rows to display (rows - 1)
957	 *	t_rows is the rows currently being displayed
958	 */
959	sp->rows = vip->srows = O_VAL(sp, O_LINES);
960	sp->cols = O_VAL(sp, O_COLUMNS);
961	sp->t_rows = sp->t_minrows = O_VAL(sp, O_WINDOW);
962	if (sp->rows != 1) {
963		if (sp->t_rows > sp->rows - 1) {
964			sp->t_minrows = sp->t_rows = sp->rows - 1;
965			msgq(sp, M_INFO,
966			    "214|Windows option value is too large, max is %u",
967			    sp->t_rows);
968		}
969		sp->t_maxrows = sp->rows - 1;
970	} else
971		sp->t_maxrows = 1;
972	sp->woff = 0;
973
974	/* Create a screen map. */
975	CALLOC_RET(sp, HMAP, SMAP *, SIZE_HMAP(sp), sizeof(SMAP));
976	TMAP = HMAP + (sp->t_rows - 1);
977	HMAP->lno = sp->lno;
978	HMAP->coff = 0;
979	HMAP->soff = 1;
980
981	/*
982	 * Fill the screen map from scratch -- try and center the line.  That
983	 * way if we're starting with a file we've seen before, we'll put the
984	 * line in the middle, otherwise, it won't work and we'll end up with
985	 * the line at the top.
986	 */
987	F_SET(sp, SC_SCR_REFORMAT | SC_SCR_CENTER);
988
989	/* Invalidate the cursor. */
990	F_SET(vip, VIP_CUR_INVALID);
991
992	/* Paint the screen image from scratch. */
993	F_SET(vip, VIP_N_EX_PAINT);
994
995	return (0);
996}
997
998/*
999 * v_dtoh --
1000 *	Move all but the current screen to the hidden queue.
1001 */
1002static void
1003v_dtoh(sp)
1004	SCR *sp;
1005{
1006	GS *gp;
1007	SCR *tsp;
1008	int hidden;
1009
1010	/* Move all screens to the hidden queue, tossing screen maps. */
1011	for (hidden = 0, gp = sp->gp;
1012	    (tsp = gp->dq.cqh_first) != (void *)&gp->dq; ++hidden) {
1013		if (_HMAP(tsp) != NULL) {
1014			free(_HMAP(tsp));
1015			_HMAP(tsp) = NULL;
1016		}
1017		CIRCLEQ_REMOVE(&gp->dq, tsp, q);
1018		CIRCLEQ_INSERT_TAIL(&gp->hq, tsp, q);
1019	}
1020
1021	/* Move current screen back to the display queue. */
1022	CIRCLEQ_REMOVE(&gp->hq, sp, q);
1023	CIRCLEQ_INSERT_TAIL(&gp->dq, sp, q);
1024
1025	/*
1026	 * XXX
1027	 * Don't bother internationalizing this message, it's going to
1028	 * go away as soon as we have one-line screens.  --TK
1029	 */
1030	if (hidden > 1)
1031		msgq(sp, M_INFO,
1032		    "%d screens backgrounded; use :display to list them",
1033		    hidden - 1);
1034}
1035
1036/*
1037 * v_keyword --
1038 *	Get the word (or non-word) the cursor is on.
1039 */
1040static int
1041v_keyword(sp)
1042	SCR *sp;
1043{
1044	VI_PRIVATE *vip;
1045	size_t beg, end, len;
1046	int moved, state;
1047	char *p;
1048
1049	if (db_get(sp, sp->lno, DBG_FATAL, &p, &len))
1050		return (1);
1051
1052	/*
1053	 * !!!
1054	 * Historically, tag commands skipped over any leading whitespace
1055	 * characters.  Make this true in general when using cursor words.
1056	 * If movement, getting a cursor word implies moving the cursor to
1057	 * its beginning.  Refresh now.
1058	 *
1059	 * !!!
1060	 * Find the beginning/end of the keyword.  Keywords are currently
1061	 * used for cursor-word searching and for tags.  Historical vi
1062	 * only used the word in a tag search from the cursor to the end
1063	 * of the word, i.e. if the cursor was on the 'b' in " abc ", the
1064	 * tag was "bc".  For consistency, we make cursor word searches
1065	 * follow the same rule.
1066	 */
1067	for (moved = 0,
1068	    beg = sp->cno; beg < len && isspace(p[beg]); moved = 1, ++beg);
1069	if (beg >= len) {
1070		msgq(sp, M_BERR, "212|Cursor not in a word");
1071		return (1);
1072	}
1073	if (moved) {
1074		sp->cno = beg;
1075		(void)vs_refresh(sp, 0);
1076	}
1077
1078	/* Find the end of the word. */
1079	for (state = inword(p[beg]),
1080	    end = beg; ++end < len && state == inword(p[end]););
1081
1082	vip = VIP(sp);
1083	len = (end - beg);
1084	BINC_RET(sp, vip->keyw, vip->klen, len);
1085	memmove(vip->keyw, p + beg, len);
1086	vip->keyw[len] = '\0';				/* XXX */
1087	return (0);
1088}
1089
1090/*
1091 * v_alias --
1092 *	Check for a command alias.
1093 */
1094static VIKEYS const *
1095v_alias(sp, vp, kp)
1096	SCR *sp;
1097	VICMD *vp;
1098	VIKEYS const *kp;
1099{
1100	CHAR_T push;
1101
1102	switch (vp->key) {
1103	case 'C':			/* C -> c$ */
1104		push = '$';
1105		vp->key = 'c';
1106		break;
1107	case 'D':			/* D -> d$ */
1108		push = '$';
1109		vp->key = 'd';
1110		break;
1111	case 'S':			/* S -> c_ */
1112		push = '_';
1113		vp->key = 'c';
1114		break;
1115	case 'Y':			/* Y -> y_ */
1116		push = '_';
1117		vp->key = 'y';
1118		break;
1119	default:
1120		return (kp);
1121	}
1122	return (v_event_push(sp,
1123	    NULL, &push, 1, CH_NOMAP | CH_QUOTED) ? NULL : &vikeys[vp->key]);
1124}
1125
1126/*
1127 * v_count --
1128 *	Return the next count.
1129 */
1130static int
1131v_count(sp, fkey, countp)
1132	SCR *sp;
1133	ARG_CHAR_T fkey;
1134	u_long *countp;
1135{
1136	EVENT ev;
1137	u_long count, tc;
1138
1139	ev.e_c = fkey;
1140	count = tc = 0;
1141	do {
1142		/*
1143		 * XXX
1144		 * Assume that overflow results in a smaller number.
1145		 */
1146		tc = count * 10 + ev.e_c - '0';
1147		if (count > tc) {
1148			/* Toss to the next non-digit. */
1149			do {
1150				if (v_key(sp, 0, &ev,
1151				    EC_MAPCOMMAND | EC_MAPNODIGIT) != GC_OK)
1152					return (1);
1153			} while (isdigit(ev.e_c));
1154			msgq(sp, M_ERR,
1155			    "235|Number larger than %lu", ULONG_MAX);
1156			return (1);
1157		}
1158		count = tc;
1159		if (v_key(sp, 0, &ev, EC_MAPCOMMAND | EC_MAPNODIGIT) != GC_OK)
1160			return (1);
1161	} while (isdigit(ev.e_c));
1162	*countp = count;
1163	return (0);
1164}
1165
1166/*
1167 * v_key --
1168 *	Return the next event.
1169 */
1170static gcret_t
1171v_key(sp, command_events, evp, ec_flags)
1172	SCR *sp;
1173	int command_events;
1174	EVENT *evp;
1175	u_int32_t ec_flags;
1176{
1177	u_int32_t quote;
1178
1179	for (quote = 0;;) {
1180		if (v_event_get(sp, evp, 0, ec_flags | quote))
1181			return (GC_FATAL);
1182		quote = 0;
1183
1184		switch (evp->e_event) {
1185		case E_CHARACTER:
1186			/*
1187			 * !!!
1188			 * Historically, ^V was ignored in the command stream,
1189			 * although it had a useful side-effect of interrupting
1190			 * mappings.  Adding a quoting bit to the call probably
1191			 * extends historic practice, but it feels right.
1192			 */
1193			if (evp->e_value == K_VLNEXT) {
1194				quote = EC_QUOTED;
1195				break;
1196			}
1197			return (GC_OK);
1198		case E_ERR:
1199		case E_EOF:
1200			return (GC_FATAL);
1201		case E_INTERRUPT:
1202			/*
1203			 * !!!
1204			 * Historically, vi beeped on command level interrupts.
1205			 *
1206			 * Historically, vi exited to ex mode if no file was
1207			 * named on the command line, and two interrupts were
1208			 * generated in a row.  (Just figured you might want
1209			 * to know that.)
1210			 */
1211			(void)sp->gp->scr_bell(sp);
1212			return (GC_INTERRUPT);
1213		case E_REPAINT:
1214			if (vs_repaint(sp, evp))
1215				return (GC_FATAL);
1216			break;
1217		case E_WRESIZE:
1218			return (GC_ERR);
1219		case E_QUIT:
1220		case E_WRITE:
1221			if (command_events)
1222				return (GC_EVENT);
1223			/* FALLTHROUGH */
1224		default:
1225			v_event_err(sp, evp);
1226			return (GC_ERR);
1227		}
1228	}
1229	/* NOTREACHED */
1230}
1231
1232#if defined(DEBUG) && defined(COMLOG)
1233/*
1234 * v_comlog --
1235 *	Log the contents of the command structure.
1236 */
1237static void
1238v_comlog(sp, vp)
1239	SCR *sp;
1240	VICMD *vp;
1241{
1242	TRACE(sp, "vcmd: %c", vp->key);
1243	if (F_ISSET(vp, VC_BUFFER))
1244		TRACE(sp, " buffer: %c", vp->buffer);
1245	if (F_ISSET(vp, VC_C1SET))
1246		TRACE(sp, " c1: %lu", vp->count);
1247	if (F_ISSET(vp, VC_C2SET))
1248		TRACE(sp, " c2: %lu", vp->count2);
1249	TRACE(sp, " flags: 0x%x\n", vp->flags);
1250}
1251#endif
1252