119304Speter/*-
219304Speter * Copyright (c) 1992, 1993, 1994
319304Speter *	The Regents of the University of California.  All rights reserved.
419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996
519304Speter *	Keith Bostic.  All rights reserved.
619304Speter *
719304Speter * See the LICENSE file for redistribution information.
819304Speter */
919304Speter
1019304Speter#include "config.h"
1119304Speter
1219304Speter#ifndef lint
1319304Speterstatic const char sccsid[] = "@(#)vi.c	10.57 (Berkeley) 10/13/96";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/types.h>
1719304Speter#include <sys/queue.h>
1819304Speter#include <sys/time.h>
1919304Speter
2019304Speter#include <bitstring.h>
2119304Speter#include <ctype.h>
2219304Speter#include <errno.h>
2319304Speter#include <limits.h>
2419304Speter#include <stdio.h>
2519304Speter#include <stdlib.h>
2619304Speter#include <string.h>
2719304Speter#include <unistd.h>
2819304Speter
2919304Speter#include "../common/common.h"
3019304Speter#include "vi.h"
3119304Speter
3219304Spetertypedef enum {
3319304Speter	GC_ERR, GC_ERR_NOFLUSH, GC_EVENT, GC_FATAL, GC_INTERRUPT, GC_OK
3419304Speter} gcret_t;
3519304Speter
3619304Speterstatic VIKEYS const
3719304Speter	       *v_alias __P((SCR *, VICMD *, VIKEYS const *));
3819304Speterstatic gcret_t	v_cmd __P((SCR *, VICMD *, VICMD *, VICMD *, int *, int *));
3919304Speterstatic int	v_count __P((SCR *, ARG_CHAR_T, u_long *));
4019304Speterstatic void	v_dtoh __P((SCR *));
4119304Speterstatic int	v_init __P((SCR *));
4219304Speterstatic gcret_t	v_key __P((SCR *, int, EVENT *, u_int32_t));
4319304Speterstatic int	v_keyword __P((SCR *));
4419304Speterstatic int	v_motion __P((SCR *, VICMD *, VICMD *, int *));
4519304Speter
4619304Speter#if defined(DEBUG) && defined(COMLOG)
4719304Speterstatic void	v_comlog __P((SCR *, VICMD *));
4819304Speter#endif
4919304Speter
5019304Speter/*
5119304Speter * Side-effect:
5219304Speter *	The dot structure can be set by the underlying vi functions,
5319304Speter *	see v_Put() and v_put().
5419304Speter */
5519304Speter#define	DOT		(&VIP(sp)->sdot)
5619304Speter#define	DOTMOTION	(&VIP(sp)->sdotmotion)
5719304Speter
5819304Speter/*
5919304Speter * vi --
6019304Speter * 	Main vi command loop.
6119304Speter *
6219304Speter * PUBLIC: int vi __P((SCR **));
6319304Speter */
6419304Speterint
6519304Spetervi(spp)
6619304Speter	SCR **spp;
6719304Speter{
6819304Speter	GS *gp;
6919304Speter	MARK abs;
7019304Speter	SCR *next, *sp;
7119304Speter	VICMD cmd, *vp;
7219304Speter	VI_PRIVATE *vip;
7319304Speter	int comcount, mapped, rval;
7419304Speter
7519304Speter	/* Get the first screen. */
7619304Speter	sp = *spp;
7719304Speter	gp = sp->gp;
7819304Speter
7919304Speter	/* Initialize the command structure. */
8019304Speter	vp = &cmd;
8119304Speter	memset(vp, 0, sizeof(VICMD));
8219304Speter
8319304Speter	/* Reset strange attraction. */
8419304Speter	F_SET(vp, VM_RCM_SET);
8519304Speter
8619304Speter	/* Initialize the vi screen. */
8719304Speter	if (v_init(sp))
8819304Speter		return (1);
8919304Speter
9019304Speter	/* Set the focus. */
9119304Speter	(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
9219304Speter
9319304Speter	for (vip = VIP(sp), rval = 0;;) {
9419304Speter		/* Resolve messages. */
9519304Speter		if (!MAPPED_KEYS_WAITING(sp) && vs_resolve(sp, NULL, 0))
9619304Speter			goto ret;
9719304Speter
9819304Speter		/*
9919304Speter		 * If not skipping a refresh, return to command mode and
10019304Speter		 * refresh the screen.
10119304Speter		 */
10219304Speter		if (F_ISSET(vip, VIP_S_REFRESH))
10319304Speter			F_CLR(vip, VIP_S_REFRESH);
10419304Speter		else {
10519304Speter			sp->showmode = SM_COMMAND;
10619304Speter			if (vs_refresh(sp, 0))
10719304Speter				goto ret;
10819304Speter		}
10919304Speter
11019304Speter		/* Set the new favorite position. */
11119304Speter		if (F_ISSET(vp, VM_RCM_SET | VM_RCM_SETFNB | VM_RCM_SETNNB)) {
11219304Speter			F_CLR(vip, VIP_RCM_LAST);
11319304Speter			(void)vs_column(sp, &sp->rcm);
11419304Speter		}
11519304Speter
11619304Speter		/*
11719304Speter		 * If not currently in a map, log the cursor position,
11819304Speter		 * and set a flag so that this command can become the
11919304Speter		 * DOT command.
12019304Speter		 */
12119304Speter		if (MAPPED_KEYS_WAITING(sp))
12219304Speter			mapped = 1;
12319304Speter		else {
12419304Speter			if (log_cursor(sp))
12519304Speter				goto err;
12619304Speter			mapped = 0;
12719304Speter		}
12819304Speter
12919304Speter		/*
13019304Speter		 * There may be an ex command waiting, and we returned here
13119304Speter		 * only because we exited a screen or file.  In this case,
13219304Speter		 * we simply go back into the ex parser.
13319304Speter		 */
13419304Speter		if (EXCMD_RUNNING(gp)) {
13519304Speter			vp->kp = &vikeys[':'];
13619304Speter			goto ex_continue;
13719304Speter		}
13819304Speter
13919304Speter		/* Refresh the command structure. */
14019304Speter		memset(vp, 0, sizeof(VICMD));
14119304Speter
14219304Speter		/*
14319304Speter		 * We get a command, which may or may not have an associated
14419304Speter		 * motion.  If it does, we get it too, calling its underlying
14519304Speter		 * function to get the resulting mark.  We then call the
14619304Speter		 * command setting the cursor to the resulting mark.
14719304Speter		 *
14819304Speter		 * !!!
14919304Speter		 * Vi historically flushed mapped characters on error, but
15019304Speter		 * entering extra <escape> characters at the beginning of
15119304Speter		 * a map wasn't considered an error -- in fact, users would
15219304Speter		 * put leading <escape> characters in maps to clean up vi
15319304Speter		 * state before the map was interpreted.  Beauty!
15419304Speter		 */
15519304Speter		switch (v_cmd(sp, DOT, vp, NULL, &comcount, &mapped)) {
15619304Speter		case GC_ERR:
15719304Speter			goto err;
15819304Speter		case GC_ERR_NOFLUSH:
15919304Speter			goto gc_err_noflush;
16019304Speter		case GC_EVENT:
16119304Speter			if (v_event_exec(sp, vp))
16219304Speter				goto err;
16319304Speter			goto gc_event;
16419304Speter		case GC_FATAL:
16519304Speter			goto ret;
16619304Speter		case GC_INTERRUPT:
16719304Speter			goto intr;
16819304Speter		case GC_OK:
16919304Speter			break;
17019304Speter		}
17119304Speter
17219304Speter		/* Check for security setting. */
17319304Speter		if (F_ISSET(vp->kp, V_SECURE) && O_ISSET(sp, O_SECURE)) {
17419304Speter			ex_emsg(sp, KEY_NAME(sp, vp->key), EXM_SECURE);
17519304Speter			goto err;
17619304Speter		}
17719304Speter
17819304Speter		/*
17919304Speter		 * Historical practice: if a dot command gets a new count,
18019304Speter		 * any motion component goes away, i.e. "d3w2." deletes a
18119304Speter		 * total of 5 words.
18219304Speter		 */
18319304Speter		if (F_ISSET(vp, VC_ISDOT) && comcount)
18419304Speter			DOTMOTION->count = 1;
18519304Speter
18619304Speter		/* Copy the key flags into the local structure. */
18719304Speter		F_SET(vp, vp->kp->flags);
18819304Speter
18919304Speter		/* Prepare to set the previous context. */
19019304Speter		if (F_ISSET(vp, V_ABS | V_ABS_C | V_ABS_L)) {
19119304Speter			abs.lno = sp->lno;
19219304Speter			abs.cno = sp->cno;
19319304Speter		}
19419304Speter
19519304Speter		/*
19619304Speter		 * Set the three cursor locations to the current cursor.  The
19719304Speter		 * underlying routines don't bother if the cursor doesn't move.
19819304Speter		 * This also handles line commands (e.g. Y) defaulting to the
19919304Speter		 * current line.
20019304Speter		 */
20119304Speter		vp->m_start.lno = vp->m_stop.lno = vp->m_final.lno = sp->lno;
20219304Speter		vp->m_start.cno = vp->m_stop.cno = vp->m_final.cno = sp->cno;
20319304Speter
20419304Speter		/*
20519304Speter		 * Do any required motion; v_motion sets the from MARK and the
20619304Speter		 * line mode flag, as well as the VM_RCM flags.
20719304Speter		 */
20819304Speter		if (F_ISSET(vp, V_MOTION) &&
20919304Speter		    v_motion(sp, DOTMOTION, vp, &mapped)) {
21019304Speter			if (INTERRUPTED(sp))
21119304Speter				goto intr;
21219304Speter			goto err;
21319304Speter		}
21419304Speter
21519304Speter		/*
21619304Speter		 * If a count is set and the command is line oriented, set the
21719304Speter		 * to MARK here relative to the cursor/from MARK.  This is for
21819304Speter		 * commands that take both counts and motions, i.e. "4yy" and
21919304Speter		 * "y%".  As there's no way the command can know which the user
22019304Speter		 * did, we have to do it here.  (There are commands that are
22119304Speter		 * line oriented and that take counts ("#G", "#H"), for which
22219304Speter		 * this calculation is either completely meaningless or wrong.
22319304Speter		 * Each command must validate the value for itself.
22419304Speter		 */
22519304Speter		if (F_ISSET(vp, VC_C1SET) && F_ISSET(vp, VM_LMODE))
22619304Speter			vp->m_stop.lno += vp->count - 1;
22719304Speter
22819304Speter		/* Increment the command count. */
22919304Speter		++sp->ccnt;
23019304Speter
23119304Speter#if defined(DEBUG) && defined(COMLOG)
23219304Speter		v_comlog(sp, vp);
23319304Speter#endif
23419304Speter		/* Call the function. */
23519304Speterex_continue:	if (vp->kp->func(sp, vp))
23619304Speter			goto err;
23719304Spetergc_event:
23819304Speter#ifdef DEBUG
23919304Speter		/* Make sure no function left the temporary space locked. */
24019304Speter		if (F_ISSET(gp, G_TMP_INUSE)) {
24119304Speter			F_CLR(gp, G_TMP_INUSE);
24219304Speter			msgq(sp, M_ERR,
24319304Speter			    "232|vi: temporary buffer not released");
24419304Speter		}
24519304Speter#endif
24619304Speter		/*
24719304Speter		 * If we're exiting this screen, move to the next one, or, if
24819304Speter		 * there aren't any more, return to the main editor loop.  The
24919304Speter		 * ordering is careful, don't discard the contents of sp until
25019304Speter		 * the end.
25119304Speter		 */
25219304Speter		if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
25319304Speter			if (file_end(sp, NULL, F_ISSET(sp, SC_EXIT_FORCE)))
25419304Speter				goto ret;
25519304Speter			if (vs_discard(sp, &next))
25619304Speter				goto ret;
25719304Speter			if (next == NULL && vs_swap(sp, &next, NULL))
25819304Speter				goto ret;
25919304Speter			*spp = next;
26019304Speter			if (screen_end(sp))
26119304Speter				goto ret;
26219304Speter			if (next == NULL)
26319304Speter				break;
26419304Speter
26519304Speter			/* Switch screens, change focus. */
26619304Speter			sp = next;
26719304Speter			vip = VIP(sp);
26819304Speter			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
26919304Speter
27019304Speter			/* Don't trust the cursor. */
27119304Speter			F_SET(vip, VIP_CUR_INVALID);
27219304Speter
27319304Speter			continue;
27419304Speter		}
27519304Speter
27619304Speter		/*
27719304Speter		 * Set the dot command structure.
27819304Speter		 *
27919304Speter		 * !!!
28019304Speter		 * Historically, commands which used mapped keys did not
28119304Speter		 * set the dot command, with the exception of the text
28219304Speter		 * input commands.
28319304Speter		 */
28419304Speter		if (F_ISSET(vp, V_DOT) && !mapped) {
28519304Speter			*DOT = cmd;
28619304Speter			F_SET(DOT, VC_ISDOT);
28719304Speter
28819304Speter			/*
28919304Speter			 * If a count was supplied for both the command and
29019304Speter			 * its motion, the count was used only for the motion.
29119304Speter			 * Turn the count back on for the dot structure.
29219304Speter			 */
29319304Speter			if (F_ISSET(vp, VC_C1RESET))
29419304Speter				F_SET(DOT, VC_C1SET);
29519304Speter
29619304Speter			/* VM flags aren't retained. */
29719304Speter			F_CLR(DOT, VM_COMMASK | VM_RCM_MASK);
29819304Speter		}
29919304Speter
30019304Speter		/*
30119304Speter		 * Some vi row movements are "attracted" to the last position
30219304Speter		 * set, i.e. the VM_RCM commands are moths to the VM_RCM_SET
30319304Speter		 * commands' candle.  If the movement is to the EOL the vi
30419304Speter		 * command handles it.  If it's to the beginning, we handle it
30519304Speter		 * here.
30619304Speter		 *
30719304Speter		 * Note, some commands (e.g. _, ^) don't set the VM_RCM_SETFNB
30819304Speter		 * flag, but do the work themselves.  The reason is that they
30919304Speter		 * have to modify the column in case they're being used as a
31019304Speter		 * motion component.  Other similar commands (e.g. +, -) don't
31119304Speter		 * have to modify the column because they are always line mode
31219304Speter		 * operations when used as motions, so the column number isn't
31319304Speter		 * of any interest.
31419304Speter		 *
31519304Speter		 * Does this totally violate the screen and editor layering?
31619304Speter		 * You betcha.  As they say, if you think you understand it,
31719304Speter		 * you don't.
31819304Speter		 */
31919304Speter		switch (F_ISSET(vp, VM_RCM_MASK)) {
32019304Speter		case 0:
32119304Speter		case VM_RCM_SET:
32219304Speter			break;
32319304Speter		case VM_RCM:
32419304Speter			vp->m_final.cno = vs_rcm(sp,
32519304Speter			    vp->m_final.lno, F_ISSET(vip, VIP_RCM_LAST));
32619304Speter			break;
32719304Speter		case VM_RCM_SETLAST:
32819304Speter			F_SET(vip, VIP_RCM_LAST);
32919304Speter			break;
33019304Speter		case VM_RCM_SETFNB:
33119304Speter			vp->m_final.cno = 0;
33219304Speter			/* FALLTHROUGH */
33319304Speter		case VM_RCM_SETNNB:
33419304Speter			if (nonblank(sp, vp->m_final.lno, &vp->m_final.cno))
33519304Speter				goto err;
33619304Speter			break;
33719304Speter		default:
33819304Speter			abort();
33919304Speter		}
34019304Speter
34119304Speter		/* Update the cursor. */
34219304Speter		sp->lno = vp->m_final.lno;
34319304Speter		sp->cno = vp->m_final.cno;
34419304Speter
34519304Speter		/*
34619304Speter		 * Set the absolute mark -- set even if a tags or similar
34719304Speter		 * command, since the tag may be moving to the same file.
34819304Speter		 */
34919304Speter		if ((F_ISSET(vp, V_ABS) ||
35019304Speter		    F_ISSET(vp, V_ABS_L) && sp->lno != abs.lno ||
35119304Speter		    F_ISSET(vp, V_ABS_C) &&
35219304Speter		    (sp->lno != abs.lno || sp->cno != abs.cno)) &&
35319304Speter		    mark_set(sp, ABSMARK1, &abs, 1))
35419304Speter			goto err;
35519304Speter
35619304Speter		if (0) {
35719304Spetererr:			if (v_event_flush(sp, CH_MAPPED))
35819304Speter				msgq(sp, M_BERR,
35919304Speter			    "110|Vi command failed: mapped keys discarded");
36019304Speter		}
36119304Speter
36219304Speter		/*
36319304Speter		 * Check and clear interrupts.  There's an obvious race, but
36419304Speter		 * it's not worth fixing.
36519304Speter		 */
36619304Spetergc_err_noflush:	if (INTERRUPTED(sp)) {
36719304Speterintr:			CLR_INTERRUPT(sp);
36819304Speter			if (v_event_flush(sp, CH_MAPPED))
36919304Speter				msgq(sp, M_ERR,
37019304Speter				    "231|Interrupted: mapped keys discarded");
37119304Speter			else
37219304Speter				msgq(sp, M_ERR, "236|Interrupted");
37319304Speter		}
37419304Speter
37519304Speter		/* If the last command switched screens, update. */
37619304Speter		if (F_ISSET(sp, SC_SSWITCH)) {
37719304Speter			F_CLR(sp, SC_SSWITCH);
37819304Speter
37919304Speter			/*
38019304Speter			 * If the current screen is still displayed, it will
38119304Speter			 * need a new status line.
38219304Speter			 */
38319304Speter			F_SET(sp, SC_STATUS);
38419304Speter
38519304Speter			/* Switch screens, change focus. */
38619304Speter			sp = sp->nextdisp;
38719304Speter			vip = VIP(sp);
38819304Speter			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
38919304Speter
39019304Speter			/* Don't trust the cursor. */
39119304Speter			F_SET(vip, VIP_CUR_INVALID);
39219304Speter
39319304Speter			/* Refresh so we can display messages. */
39419304Speter			if (vs_refresh(sp, 1))
39519304Speter				return (1);
39619304Speter		}
39719304Speter
39819304Speter		/* If the last command switched files, change focus. */
39919304Speter		if (F_ISSET(sp, SC_FSWITCH)) {
40019304Speter			F_CLR(sp, SC_FSWITCH);
40119304Speter			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
40219304Speter		}
40319304Speter
40419304Speter		/* If leaving vi, return to the main editor loop. */
40519304Speter		if (F_ISSET(gp, G_SRESTART) || F_ISSET(sp, SC_EX)) {
40619304Speter			*spp = sp;
40719304Speter			v_dtoh(sp);
40819304Speter			break;
40919304Speter		}
41019304Speter	}
41119304Speter	if (0)
41219304Speterret:		rval = 1;
41319304Speter	return (rval);
41419304Speter}
41519304Speter
41619304Speter#define	KEY(key, ec_flags) {						\
41719304Speter	if ((gcret = v_key(sp, 0, &ev, ec_flags)) != GC_OK)		\
41819304Speter		return (gcret);						\
41919304Speter	if (ev.e_value == K_ESCAPE)					\
42019304Speter		goto esc;						\
42119304Speter	if (F_ISSET(&ev.e_ch, CH_MAPPED))				\
42219304Speter		*mappedp = 1;						\
42319304Speter	key = ev.e_c;							\
42419304Speter}
42519304Speter
42619304Speter/*
42719304Speter * The O_TILDEOP option makes the ~ command take a motion instead
42819304Speter * of a straight count.  This is the replacement structure we use
42919304Speter * instead of the one currently in the VIKEYS table.
43019304Speter *
43119304Speter * XXX
43219304Speter * This should probably be deleted -- it's not all that useful, and
43319304Speter * we get help messages wrong.
43419304Speter */
43519304SpeterVIKEYS const tmotion = {
43619304Speter	v_mulcase,	V_CNT|V_DOT|V_MOTION|VM_RCM_SET,
43719304Speter	"[count]~[count]motion",
43819304Speter	" ~ change case to motion"
43919304Speter};
44019304Speter
44119304Speter/*
44219304Speter * v_cmd --
44319304Speter *
44419304Speter * The command structure for vi is less complex than ex (and don't think
44519304Speter * I'm not grateful!)  The command syntax is:
44619304Speter *
44719304Speter *	[count] [buffer] [count] key [[motion] | [buffer] [character]]
44819304Speter *
44919304Speter * and there are several special cases.  The motion value is itself a vi
45019304Speter * command, with the syntax:
45119304Speter *
45219304Speter *	[count] key [character]
45319304Speter */
45419304Speterstatic gcret_t
45519304Speterv_cmd(sp, dp, vp, ismotion, comcountp, mappedp)
45619304Speter	SCR *sp;
45719304Speter	VICMD *dp, *vp;
45819304Speter	VICMD *ismotion;	/* Previous key if getting motion component. */
45919304Speter	int *comcountp, *mappedp;
46019304Speter{
46119304Speter	enum { COMMANDMODE, ISPARTIAL, NOTPARTIAL } cpart;
46219304Speter	EVENT ev;
46319304Speter	VIKEYS const *kp;
46419304Speter	gcret_t gcret;
46519304Speter	u_int flags;
46619304Speter	CHAR_T key;
46719304Speter	char *s;
46819304Speter
46919304Speter	/*
47019304Speter	 * Get a key.
47119304Speter	 *
47219304Speter	 * <escape> cancels partial commands, i.e. a command where at least
47319304Speter	 * one non-numeric character has been entered.  Otherwise, it beeps
47419304Speter	 * the terminal.
47519304Speter	 *
47619304Speter	 * !!!
47719304Speter	 * POSIX 1003.2-1992 explicitly disallows cancelling commands where
47819304Speter	 * all that's been entered is a number, requiring that the terminal
47919304Speter	 * be alerted.
48019304Speter	 */
48119304Speter	cpart = ismotion == NULL ? COMMANDMODE : ISPARTIAL;
48219304Speter	if ((gcret =
48319304Speter	    v_key(sp, ismotion == NULL, &ev, EC_MAPCOMMAND)) != GC_OK) {
48419304Speter		if (gcret == GC_EVENT)
48519304Speter			vp->ev = ev;
48619304Speter		return (gcret);
48719304Speter	}
48819304Speter	if (ev.e_value == K_ESCAPE)
48919304Speter		goto esc;
49019304Speter	if (F_ISSET(&ev.e_ch, CH_MAPPED))
49119304Speter		*mappedp = 1;
49219304Speter	key = ev.e_c;
49319304Speter
49419304Speter	if (ismotion == NULL)
49519304Speter		cpart = NOTPARTIAL;
49619304Speter
49719304Speter	/* Pick up optional buffer. */
49819304Speter	if (key == '"') {
49919304Speter		cpart = ISPARTIAL;
50019304Speter		if (ismotion != NULL) {
50119304Speter			v_emsg(sp, NULL, VIM_COMBUF);
50219304Speter			return (GC_ERR);
50319304Speter		}
50419304Speter		KEY(vp->buffer, 0);
50519304Speter		F_SET(vp, VC_BUFFER);
50619304Speter
50719304Speter		KEY(key, EC_MAPCOMMAND);
50819304Speter	}
50919304Speter
51019304Speter	/*
51119304Speter	 * Pick up optional count, where a leading 0 is not a count,
51219304Speter	 * it's a command.
51319304Speter	 */
51419304Speter	if (isdigit(key) && key != '0') {
51519304Speter		if (v_count(sp, key, &vp->count))
51619304Speter			return (GC_ERR);
51719304Speter		F_SET(vp, VC_C1SET);
51819304Speter		*comcountp = 1;
51919304Speter
52019304Speter		KEY(key, EC_MAPCOMMAND);
52119304Speter	} else
52219304Speter		*comcountp = 0;
52319304Speter
52419304Speter	/* Pick up optional buffer. */
52519304Speter	if (key == '"') {
52619304Speter		cpart = ISPARTIAL;
52719304Speter		if (F_ISSET(vp, VC_BUFFER)) {
52819304Speter			msgq(sp, M_ERR, "234|Only one buffer may be specified");
52919304Speter			return (GC_ERR);
53019304Speter		}
53119304Speter		if (ismotion != NULL) {
53219304Speter			v_emsg(sp, NULL, VIM_COMBUF);
53319304Speter			return (GC_ERR);
53419304Speter		}
53519304Speter		KEY(vp->buffer, 0);
53619304Speter		F_SET(vp, VC_BUFFER);
53719304Speter
53819304Speter		KEY(key, EC_MAPCOMMAND);
53919304Speter	}
54019304Speter
54119304Speter	/* Check for an OOB command key. */
54219304Speter	cpart = ISPARTIAL;
54319304Speter	if (key > MAXVIKEY) {
54419304Speter		v_emsg(sp, KEY_NAME(sp, key), VIM_NOCOM);
54519304Speter		return (GC_ERR);
54619304Speter	}
54719304Speter	kp = &vikeys[vp->key = key];
54819304Speter
54919304Speter	/*
55019304Speter	 * !!!
55119304Speter	 * Historically, D accepted and then ignored a count.  Match it.
55219304Speter	 */
55319304Speter	if (vp->key == 'D' && F_ISSET(vp, VC_C1SET)) {
55419304Speter		*comcountp = 0;
55519304Speter		vp->count = 0;
55619304Speter		F_CLR(vp, VC_C1SET);
55719304Speter	}
55819304Speter
55919304Speter	/* Check for command aliases. */
56019304Speter	if (kp->func == NULL && (kp = v_alias(sp, vp, kp)) == NULL)
56119304Speter		return (GC_ERR);
56219304Speter
56319304Speter	/* The tildeop option makes the ~ command take a motion. */
56419304Speter	if (key == '~' && O_ISSET(sp, O_TILDEOP))
56519304Speter		kp = &tmotion;
56619304Speter
56719304Speter	vp->kp = kp;
56819304Speter
56919304Speter	/*
57019304Speter	 * Find the command.  The only legal command with no underlying
57119304Speter	 * function is dot.  It's historic practice that <escape> doesn't
57219304Speter	 * just erase the preceding number, it beeps the terminal as well.
57319304Speter	 * It's a common problem, so just beep the terminal unless verbose
57419304Speter	 * was set.
57519304Speter	 */
57619304Speter	if (kp->func == NULL) {
57719304Speter		if (key != '.') {
57819304Speter			v_emsg(sp, KEY_NAME(sp, key),
57919304Speter			    ev.e_value == K_ESCAPE ? VIM_NOCOM_B : VIM_NOCOM);
58019304Speter			return (GC_ERR);
58119304Speter		}
58219304Speter
58319304Speter		/* If called for a motion command, stop now. */
58419304Speter		if (dp == NULL)
58519304Speter			goto usage;
58619304Speter
58719304Speter		/*
58819304Speter		 * !!!
58919304Speter		 * If a '.' is immediately entered after an undo command, we
59019304Speter		 * replay the log instead of redoing the last command.  This
59119304Speter		 * is necessary because 'u' can't set the dot command -- see
59219304Speter		 * vi/v_undo.c:v_undo for details.
59319304Speter		 */
59419304Speter		if (VIP(sp)->u_ccnt == sp->ccnt) {
59519304Speter			vp->kp = &vikeys['u'];
59619304Speter			F_SET(vp, VC_ISDOT);
59719304Speter			return (GC_OK);
59819304Speter		}
59919304Speter
60019304Speter		/* Otherwise, a repeatable command must have been executed. */
60119304Speter		if (!F_ISSET(dp, VC_ISDOT)) {
60219304Speter			msgq(sp, M_ERR, "208|No command to repeat");
60319304Speter			return (GC_ERR);
60419304Speter		}
60519304Speter
60619304Speter		/* Set new count/buffer, if any, and return. */
60719304Speter		if (F_ISSET(vp, VC_C1SET)) {
60819304Speter			F_SET(dp, VC_C1SET);
60919304Speter			dp->count = vp->count;
61019304Speter		}
61119304Speter		if (F_ISSET(vp, VC_BUFFER))
61219304Speter			dp->buffer = vp->buffer;
61319304Speter
61419304Speter		*vp = *dp;
61519304Speter		return (GC_OK);
61619304Speter	}
61719304Speter
61819304Speter	/* Set the flags based on the command flags. */
61919304Speter	flags = kp->flags;
62019304Speter
62119304Speter	/* Check for illegal count. */
62219304Speter	if (F_ISSET(vp, VC_C1SET) && !LF_ISSET(V_CNT))
62319304Speter		goto usage;
62419304Speter
62519304Speter	/* Illegal motion command. */
62619304Speter	if (ismotion == NULL) {
62719304Speter		/* Illegal buffer. */
62819304Speter		if (!LF_ISSET(V_OBUF) && F_ISSET(vp, VC_BUFFER))
62919304Speter			goto usage;
63019304Speter
63119304Speter		/* Required buffer. */
63219304Speter		if (LF_ISSET(V_RBUF)) {
63319304Speter			KEY(vp->buffer, 0);
63419304Speter			F_SET(vp, VC_BUFFER);
63519304Speter		}
63619304Speter	}
63719304Speter
63819304Speter	/*
63919304Speter	 * Special case: '[', ']' and 'Z' commands.  Doesn't the fact that
64019304Speter	 * the *single* characters don't mean anything but the *doubled*
64119304Speter	 * characters do, just frost your shorts?
64219304Speter	 */
64319304Speter	if (vp->key == '[' || vp->key == ']' || vp->key == 'Z') {
64419304Speter		/*
64519304Speter		 * Historically, half entered [[, ]] or Z commands weren't
64619304Speter		 * cancelled by <escape>, the terminal was beeped instead.
64719304Speter		 * POSIX.2-1992 probably didn't notice, and requires that
64819304Speter		 * they be cancelled instead of beeping.  Seems fine to me.
64919304Speter		 *
65019304Speter		 * Don't set the EC_MAPCOMMAND flag, apparently ] is a popular
65119304Speter		 * vi meta-character, and we don't want the user to wait while
65219304Speter		 * we time out a possible mapping.  This *appears* to match
65319304Speter		 * historic vi practice, but with mapping characters, you Just
65419304Speter		 * Never Know.
65519304Speter		 */
65619304Speter		KEY(key, 0);
65719304Speter
65819304Speter		if (vp->key != key) {
65919304Speterusage:			if (ismotion == NULL)
66019304Speter				s = kp->usage;
66119304Speter			else if (ismotion->key == '~' && O_ISSET(sp, O_TILDEOP))
66219304Speter				s = tmotion.usage;
66319304Speter			else
66419304Speter				s = vikeys[ismotion->key].usage;
66519304Speter			v_emsg(sp, s, VIM_USAGE);
66619304Speter			return (GC_ERR);
66719304Speter		}
66819304Speter	}
66919304Speter	/* Special case: 'z' command. */
67019304Speter	if (vp->key == 'z') {
67119304Speter		KEY(vp->character, 0);
67219304Speter		if (isdigit(vp->character)) {
67319304Speter			if (v_count(sp, vp->character, &vp->count2))
67419304Speter				return (GC_ERR);
67519304Speter			F_SET(vp, VC_C2SET);
67619304Speter			KEY(vp->character, 0);
67719304Speter		}
67819304Speter	}
67919304Speter
68019304Speter	/*
68119304Speter	 * Commands that have motion components can be doubled to
68219304Speter	 * imply the current line.
68319304Speter	 */
68419304Speter	if (ismotion != NULL && ismotion->key != key && !LF_ISSET(V_MOVE)) {
68519304Speter		msgq(sp, M_ERR, "210|%s may not be used as a motion command",
68619304Speter		    KEY_NAME(sp, key));
68719304Speter		return (GC_ERR);
68819304Speter	}
68919304Speter
69019304Speter	/* Required character. */
69119304Speter	if (LF_ISSET(V_CHAR))
69219304Speter		KEY(vp->character, 0);
69319304Speter
69419304Speter	/* Get any associated cursor word. */
69519304Speter	if (F_ISSET(kp, V_KEYW) && v_keyword(sp))
69619304Speter		return (GC_ERR);
69719304Speter
69819304Speter	return (GC_OK);
69919304Speter
70019304Speteresc:	switch (cpart) {
70119304Speter	case COMMANDMODE:
70219304Speter		msgq(sp, M_BERR, "211|Already in command mode");
70319304Speter		return (GC_ERR_NOFLUSH);
70419304Speter	case ISPARTIAL:
70519304Speter		break;
70619304Speter	case NOTPARTIAL:
70719304Speter		(void)sp->gp->scr_bell(sp);
70819304Speter		break;
70919304Speter	}
71019304Speter	return (GC_ERR);
71119304Speter}
71219304Speter
71319304Speter/*
71419304Speter * v_motion --
71519304Speter *
71619304Speter * Get resulting motion mark.
71719304Speter */
71819304Speterstatic int
71919304Speterv_motion(sp, dm, vp, mappedp)
72019304Speter	SCR *sp;
72119304Speter	VICMD *dm, *vp;
72219304Speter	int *mappedp;
72319304Speter{
72419304Speter	VICMD motion;
72519304Speter	size_t len;
72619304Speter	u_long cnt;
72719304Speter	u_int flags;
72819304Speter	int tilde_reset, notused;
72919304Speter
73019304Speter	/*
73119304Speter	 * If '.' command, use the dot motion, else get the motion command.
73219304Speter	 * Clear any line motion flags, the subsequent motion isn't always
73319304Speter	 * the same, i.e. "/aaa" may or may not be a line motion.
73419304Speter	 */
73519304Speter	if (F_ISSET(vp, VC_ISDOT)) {
73619304Speter		motion = *dm;
73719304Speter		F_SET(&motion, VC_ISDOT);
73819304Speter		F_CLR(&motion, VM_COMMASK);
73919304Speter	} else {
74019304Speter		memset(&motion, 0, sizeof(VICMD));
74119304Speter		if (v_cmd(sp, NULL, &motion, vp, &notused, mappedp) != GC_OK)
74219304Speter			return (1);
74319304Speter	}
74419304Speter
74519304Speter	/*
74619304Speter	 * A count may be provided both to the command and to the motion, in
74719304Speter	 * which case the count is multiplicative.  For example, "3y4y" is the
74819304Speter	 * same as "12yy".  This count is provided to the motion command and
74919304Speter	 * not to the regular function.
75019304Speter	 */
75119304Speter	cnt = motion.count = F_ISSET(&motion, VC_C1SET) ? motion.count : 1;
75219304Speter	if (F_ISSET(vp, VC_C1SET)) {
75319304Speter		motion.count *= vp->count;
75419304Speter		F_SET(&motion, VC_C1SET);
75519304Speter
75619304Speter		/*
75719304Speter		 * Set flags to restore the original values of the command
75819304Speter		 * structure so dot commands can change the count values,
75919304Speter		 * e.g. "2dw" "3." deletes a total of five words.
76019304Speter		 */
76119304Speter		F_CLR(vp, VC_C1SET);
76219304Speter		F_SET(vp, VC_C1RESET);
76319304Speter	}
76419304Speter
76519304Speter	/*
76619304Speter	 * Some commands can be repeated to indicate the current line.  In
76719304Speter	 * this case, or if the command is a "line command", set the flags
76819304Speter	 * appropriately.  If not a doubled command, run the function to get
76919304Speter	 * the resulting mark.
77019304Speter 	 */
77119304Speter	if (vp->key == motion.key) {
77219304Speter		F_SET(vp, VM_LDOUBLE | VM_LMODE);
77319304Speter
77419304Speter		/* Set the origin of the command. */
77519304Speter		vp->m_start.lno = sp->lno;
77619304Speter		vp->m_start.cno = 0;
77719304Speter
77819304Speter		/*
77919304Speter		 * Set the end of the command.
78019304Speter		 *
78119304Speter		 * If the current line is missing, i.e. the file is empty,
78219304Speter		 * historic vi permitted a "cc" or "!!" command to insert
78319304Speter		 * text.
78419304Speter		 */
78519304Speter		vp->m_stop.lno = sp->lno + motion.count - 1;
78619304Speter		if (db_get(sp, vp->m_stop.lno, 0, NULL, &len)) {
78719304Speter			if (vp->m_stop.lno != 1 ||
78819304Speter			   vp->key != 'c' && vp->key != '!') {
78919304Speter				v_emsg(sp, NULL, VIM_EMPTY);
79019304Speter				return (1);
79119304Speter			}
79219304Speter			vp->m_stop.cno = 0;
79319304Speter		} else
79419304Speter			vp->m_stop.cno = len ? len - 1 : 0;
79519304Speter	} else {
79619304Speter		/*
79719304Speter		 * Motion commands change the underlying movement (*snarl*).
79819304Speter		 * For example, "l" is illegal at the end of a line, but "dl"
79919304Speter		 * is not.  Set flags so the function knows the situation.
80019304Speter		 */
80119304Speter		motion.rkp = vp->kp;
80219304Speter
80319304Speter		/*
80419304Speter		 * XXX
80519304Speter		 * Use yank instead of creating a new motion command, it's a
80619304Speter		 * lot easier for now.
80719304Speter		 */
80819304Speter		if (vp->kp == &tmotion) {
80919304Speter			tilde_reset = 1;
81019304Speter			vp->kp = &vikeys['y'];
81119304Speter		} else
81219304Speter			tilde_reset = 0;
81319304Speter
81419304Speter		/*
81519304Speter		 * Copy the key flags into the local structure, except for the
81619304Speter		 * RCM flags -- the motion command will set the RCM flags in
81719304Speter		 * the vp structure if necessary.  This means that the motion
81819304Speter		 * command is expected to determine where the cursor ends up!
81919304Speter		 * However, we save off the current RCM mask and restore it if
82019304Speter		 * it no RCM flags are set by the motion command, with a small
82119304Speter		 * modification.
82219304Speter		 *
82319304Speter		 * We replace the VM_RCM_SET flag with the VM_RCM flag.  This
82419304Speter		 * is so that cursor movement doesn't set the relative position
82519304Speter		 * unless the motion command explicitly specified it.  This
82619304Speter		 * appears to match historic practice, but I've never been able
82719304Speter		 * to develop a hard-and-fast rule.
82819304Speter		 */
82919304Speter		flags = F_ISSET(vp, VM_RCM_MASK);
83019304Speter		if (LF_ISSET(VM_RCM_SET)) {
83119304Speter			LF_SET(VM_RCM);
83219304Speter			LF_CLR(VM_RCM_SET);
83319304Speter		}
83419304Speter		F_CLR(vp, VM_RCM_MASK);
83519304Speter		F_SET(&motion, motion.kp->flags & ~VM_RCM_MASK);
83619304Speter
83719304Speter		/*
83819304Speter		 * Set the three cursor locations to the current cursor.  This
83919304Speter		 * permits commands like 'j' and 'k', that are line oriented
84019304Speter		 * motions and have special cursor suck semantics when they are
84119304Speter		 * used as standalone commands, to ignore column positioning.
84219304Speter		 */
84319304Speter		motion.m_final.lno =
84419304Speter		    motion.m_stop.lno = motion.m_start.lno = sp->lno;
84519304Speter		motion.m_final.cno =
84619304Speter		    motion.m_stop.cno = motion.m_start.cno = sp->cno;
84719304Speter
84819304Speter		/* Run the function. */
84919304Speter		if ((motion.kp->func)(sp, &motion))
85019304Speter			return (1);
85119304Speter
85219304Speter		/*
85319304Speter		 * If the current line is missing, i.e. the file is empty,
85419304Speter		 * historic vi allowed "c<motion>" or "!<motion>" to insert
85519304Speter		 * text.  Otherwise fail -- most motion commands will have
85619304Speter		 * already failed, but some, e.g. G, succeed in empty files.
85719304Speter		 */
85819304Speter		if (!db_exist(sp, vp->m_stop.lno)) {
85919304Speter			if (vp->m_stop.lno != 1 ||
86019304Speter			   vp->key != 'c' && vp->key != '!') {
86119304Speter				v_emsg(sp, NULL, VIM_EMPTY);
86219304Speter				return (1);
86319304Speter			}
86419304Speter			vp->m_stop.cno = 0;
86519304Speter		}
86619304Speter
86719304Speter		/*
86819304Speter		 * XXX
86919304Speter		 * See above.
87019304Speter		 */
87119304Speter		if (tilde_reset)
87219304Speter			vp->kp = &tmotion;
87319304Speter
87419304Speter		/*
87519304Speter		 * Copy cut buffer, line mode and cursor position information
87619304Speter		 * from the motion command structure, i.e. anything that the
87719304Speter		 * motion command can set for us.  The commands can flag the
87819304Speter		 * movement as a line motion (see v_sentence) as well as set
87919304Speter		 * the VM_RCM_* flags explicitly.
88019304Speter		 */
88119304Speter		F_SET(vp, F_ISSET(&motion, VM_COMMASK | VM_RCM_MASK));
88219304Speter
88319304Speter		/*
88419304Speter		 * If the motion command set no relative motion flags, use
88519304Speter		 * the (slightly) modified previous values.
88619304Speter		 */
88719304Speter		if (!F_ISSET(vp, VM_RCM_MASK))
88819304Speter			F_SET(vp, flags);
88919304Speter
89019304Speter		/*
89119304Speter		 * Commands can change behaviors based on the motion command
89219304Speter		 * used, for example, the ! command repeated the last bang
89319304Speter		 * command if N or n was used as the motion.
89419304Speter		 */
89519304Speter		vp->rkp = motion.kp;
89619304Speter
89719304Speter		/*
89819304Speter		 * Motion commands can reset all of the cursor information.
89919304Speter		 * If the motion is in the reverse direction, switch the
90019304Speter		 * from and to MARK's so that it's in a forward direction.
90119304Speter		 * Motions are from the from MARK to the to MARK (inclusive).
90219304Speter		 */
90319304Speter		if (motion.m_start.lno > motion.m_stop.lno ||
90419304Speter		    motion.m_start.lno == motion.m_stop.lno &&
90519304Speter		    motion.m_start.cno > motion.m_stop.cno) {
90619304Speter			vp->m_start = motion.m_stop;
90719304Speter			vp->m_stop = motion.m_start;
90819304Speter		} else {
90919304Speter			vp->m_start = motion.m_start;
91019304Speter			vp->m_stop = motion.m_stop;
91119304Speter		}
91219304Speter		vp->m_final = motion.m_final;
91319304Speter	}
91419304Speter
91519304Speter	/*
91619304Speter	 * If the command sets dot, save the motion structure.  The motion
91719304Speter	 * count was changed above and needs to be reset, that's why this
91819304Speter	 * is done here, and not in the calling routine.
91919304Speter	 */
92019304Speter	if (F_ISSET(vp->kp, V_DOT)) {
92119304Speter		*dm = motion;
92219304Speter		dm->count = cnt;
92319304Speter	}
92419304Speter	return (0);
92519304Speter}
92619304Speter
92719304Speter/*
92819304Speter * v_init --
92919304Speter *	Initialize the vi screen.
93019304Speter */
93119304Speterstatic int
93219304Speterv_init(sp)
93319304Speter	SCR *sp;
93419304Speter{
93519304Speter	GS *gp;
93619304Speter	VI_PRIVATE *vip;
93719304Speter
93819304Speter	gp = sp->gp;
93919304Speter	vip = VIP(sp);
94019304Speter
94119304Speter	/* Switch into vi. */
94219304Speter	if (gp->scr_screen(sp, SC_VI))
94319304Speter		return (1);
94419304Speter	(void)gp->scr_attr(sp, SA_ALTERNATE, 1);
94519304Speter
94619304Speter	F_CLR(sp, SC_EX | SC_SCR_EX);
94719304Speter	F_SET(sp, SC_VI);
94819304Speter
94919304Speter	/*
95019304Speter	 * Initialize screen values.
95119304Speter	 *
95219304Speter	 * Small windows: see vs_refresh(), section 6a.
95319304Speter	 *
95419304Speter	 * Setup:
95519304Speter	 *	t_minrows is the minimum rows to display
95619304Speter	 *	t_maxrows is the maximum rows to display (rows - 1)
95719304Speter	 *	t_rows is the rows currently being displayed
95819304Speter	 */
95919304Speter	sp->rows = vip->srows = O_VAL(sp, O_LINES);
96019304Speter	sp->cols = O_VAL(sp, O_COLUMNS);
96119304Speter	sp->t_rows = sp->t_minrows = O_VAL(sp, O_WINDOW);
96219304Speter	if (sp->rows != 1) {
96319304Speter		if (sp->t_rows > sp->rows - 1) {
96419304Speter			sp->t_minrows = sp->t_rows = sp->rows - 1;
96519304Speter			msgq(sp, M_INFO,
96619304Speter			    "214|Windows option value is too large, max is %u",
96719304Speter			    sp->t_rows);
96819304Speter		}
96919304Speter		sp->t_maxrows = sp->rows - 1;
97019304Speter	} else
97119304Speter		sp->t_maxrows = 1;
97219304Speter	sp->woff = 0;
97319304Speter
97419304Speter	/* Create a screen map. */
97519304Speter	CALLOC_RET(sp, HMAP, SMAP *, SIZE_HMAP(sp), sizeof(SMAP));
97619304Speter	TMAP = HMAP + (sp->t_rows - 1);
97719304Speter	HMAP->lno = sp->lno;
97819304Speter	HMAP->coff = 0;
97919304Speter	HMAP->soff = 1;
98019304Speter
98119304Speter	/*
98219304Speter	 * Fill the screen map from scratch -- try and center the line.  That
98319304Speter	 * way if we're starting with a file we've seen before, we'll put the
98419304Speter	 * line in the middle, otherwise, it won't work and we'll end up with
98519304Speter	 * the line at the top.
98619304Speter	 */
98719304Speter	F_SET(sp, SC_SCR_REFORMAT | SC_SCR_CENTER);
98819304Speter
98919304Speter	/* Invalidate the cursor. */
99019304Speter	F_SET(vip, VIP_CUR_INVALID);
99119304Speter
99219304Speter	/* Paint the screen image from scratch. */
99319304Speter	F_SET(vip, VIP_N_EX_PAINT);
99419304Speter
99519304Speter	return (0);
99619304Speter}
99719304Speter
99819304Speter/*
99919304Speter * v_dtoh --
100019304Speter *	Move all but the current screen to the hidden queue.
100119304Speter */
100219304Speterstatic void
100319304Speterv_dtoh(sp)
100419304Speter	SCR *sp;
100519304Speter{
100619304Speter	GS *gp;
100719304Speter	SCR *tsp;
100819304Speter	int hidden;
100919304Speter
101019304Speter	/* Move all screens to the hidden queue, tossing screen maps. */
101119304Speter	for (hidden = 0, gp = sp->gp;
101219304Speter	    (tsp = gp->dq.cqh_first) != (void *)&gp->dq; ++hidden) {
101319304Speter		if (_HMAP(tsp) != NULL) {
101419304Speter			free(_HMAP(tsp));
101519304Speter			_HMAP(tsp) = NULL;
101619304Speter		}
101719304Speter		CIRCLEQ_REMOVE(&gp->dq, tsp, q);
101819304Speter		CIRCLEQ_INSERT_TAIL(&gp->hq, tsp, q);
101919304Speter	}
102019304Speter
102119304Speter	/* Move current screen back to the display queue. */
102219304Speter	CIRCLEQ_REMOVE(&gp->hq, sp, q);
102319304Speter	CIRCLEQ_INSERT_TAIL(&gp->dq, sp, q);
102419304Speter
102519304Speter	/*
102619304Speter	 * XXX
102719304Speter	 * Don't bother internationalizing this message, it's going to
102819304Speter	 * go away as soon as we have one-line screens.  --TK
102919304Speter	 */
103019304Speter	if (hidden > 1)
103119304Speter		msgq(sp, M_INFO,
103219304Speter		    "%d screens backgrounded; use :display to list them",
103319304Speter		    hidden - 1);
103419304Speter}
103519304Speter
103619304Speter/*
103719304Speter * v_keyword --
103819304Speter *	Get the word (or non-word) the cursor is on.
103919304Speter */
104019304Speterstatic int
104119304Speterv_keyword(sp)
104219304Speter	SCR *sp;
104319304Speter{
104419304Speter	VI_PRIVATE *vip;
104519304Speter	size_t beg, end, len;
104619304Speter	int moved, state;
104719304Speter	char *p;
104819304Speter
104919304Speter	if (db_get(sp, sp->lno, DBG_FATAL, &p, &len))
105019304Speter		return (1);
105119304Speter
105219304Speter	/*
105319304Speter	 * !!!
105419304Speter	 * Historically, tag commands skipped over any leading whitespace
105519304Speter	 * characters.  Make this true in general when using cursor words.
105619304Speter	 * If movement, getting a cursor word implies moving the cursor to
105719304Speter	 * its beginning.  Refresh now.
105819304Speter	 *
105919304Speter	 * !!!
106019304Speter	 * Find the beginning/end of the keyword.  Keywords are currently
106119304Speter	 * used for cursor-word searching and for tags.  Historical vi
106219304Speter	 * only used the word in a tag search from the cursor to the end
106319304Speter	 * of the word, i.e. if the cursor was on the 'b' in " abc ", the
106419304Speter	 * tag was "bc".  For consistency, we make cursor word searches
106519304Speter	 * follow the same rule.
106619304Speter	 */
106719304Speter	for (moved = 0,
106819304Speter	    beg = sp->cno; beg < len && isspace(p[beg]); moved = 1, ++beg);
106919304Speter	if (beg >= len) {
107019304Speter		msgq(sp, M_BERR, "212|Cursor not in a word");
107119304Speter		return (1);
107219304Speter	}
107319304Speter	if (moved) {
107419304Speter		sp->cno = beg;
107519304Speter		(void)vs_refresh(sp, 0);
107619304Speter	}
107719304Speter
107819304Speter	/* Find the end of the word. */
107919304Speter	for (state = inword(p[beg]),
108019304Speter	    end = beg; ++end < len && state == inword(p[end]););
108119304Speter
108219304Speter	vip = VIP(sp);
108319304Speter	len = (end - beg);
108419304Speter	BINC_RET(sp, vip->keyw, vip->klen, len);
108519304Speter	memmove(vip->keyw, p + beg, len);
108619304Speter	vip->keyw[len] = '\0';				/* XXX */
108719304Speter	return (0);
108819304Speter}
108919304Speter
109019304Speter/*
109119304Speter * v_alias --
109219304Speter *	Check for a command alias.
109319304Speter */
109419304Speterstatic VIKEYS const *
109519304Speterv_alias(sp, vp, kp)
109619304Speter	SCR *sp;
109719304Speter	VICMD *vp;
109819304Speter	VIKEYS const *kp;
109919304Speter{
110019304Speter	CHAR_T push;
110119304Speter
110219304Speter	switch (vp->key) {
110319304Speter	case 'C':			/* C -> c$ */
110419304Speter		push = '$';
110519304Speter		vp->key = 'c';
110619304Speter		break;
110719304Speter	case 'D':			/* D -> d$ */
110819304Speter		push = '$';
110919304Speter		vp->key = 'd';
111019304Speter		break;
111119304Speter	case 'S':			/* S -> c_ */
111219304Speter		push = '_';
111319304Speter		vp->key = 'c';
111419304Speter		break;
111519304Speter	case 'Y':			/* Y -> y_ */
111619304Speter		push = '_';
111719304Speter		vp->key = 'y';
111819304Speter		break;
111919304Speter	default:
112019304Speter		return (kp);
112119304Speter	}
112219304Speter	return (v_event_push(sp,
112319304Speter	    NULL, &push, 1, CH_NOMAP | CH_QUOTED) ? NULL : &vikeys[vp->key]);
112419304Speter}
112519304Speter
112619304Speter/*
112719304Speter * v_count --
112819304Speter *	Return the next count.
112919304Speter */
113019304Speterstatic int
113119304Speterv_count(sp, fkey, countp)
113219304Speter	SCR *sp;
113319304Speter	ARG_CHAR_T fkey;
113419304Speter	u_long *countp;
113519304Speter{
113619304Speter	EVENT ev;
113719304Speter	u_long count, tc;
113819304Speter
113919304Speter	ev.e_c = fkey;
114019304Speter	count = tc = 0;
114119304Speter	do {
114219304Speter		/*
114319304Speter		 * XXX
114419304Speter		 * Assume that overflow results in a smaller number.
114519304Speter		 */
114619304Speter		tc = count * 10 + ev.e_c - '0';
114719304Speter		if (count > tc) {
114819304Speter			/* Toss to the next non-digit. */
114919304Speter			do {
115019304Speter				if (v_key(sp, 0, &ev,
115119304Speter				    EC_MAPCOMMAND | EC_MAPNODIGIT) != GC_OK)
115219304Speter					return (1);
115319304Speter			} while (isdigit(ev.e_c));
115419304Speter			msgq(sp, M_ERR,
115519304Speter			    "235|Number larger than %lu", ULONG_MAX);
115619304Speter			return (1);
115719304Speter		}
115819304Speter		count = tc;
115919304Speter		if (v_key(sp, 0, &ev, EC_MAPCOMMAND | EC_MAPNODIGIT) != GC_OK)
116019304Speter			return (1);
116119304Speter	} while (isdigit(ev.e_c));
116219304Speter	*countp = count;
116319304Speter	return (0);
116419304Speter}
116519304Speter
116619304Speter/*
116719304Speter * v_key --
116819304Speter *	Return the next event.
116919304Speter */
117019304Speterstatic gcret_t
117119304Speterv_key(sp, command_events, evp, ec_flags)
117219304Speter	SCR *sp;
117319304Speter	int command_events;
117419304Speter	EVENT *evp;
117519304Speter	u_int32_t ec_flags;
117619304Speter{
117719304Speter	u_int32_t quote;
117819304Speter
117919304Speter	for (quote = 0;;) {
118019304Speter		if (v_event_get(sp, evp, 0, ec_flags | quote))
118119304Speter			return (GC_FATAL);
118219304Speter		quote = 0;
118319304Speter
118419304Speter		switch (evp->e_event) {
118519304Speter		case E_CHARACTER:
118619304Speter			/*
118719304Speter			 * !!!
118819304Speter			 * Historically, ^V was ignored in the command stream,
118919304Speter			 * although it had a useful side-effect of interrupting
119019304Speter			 * mappings.  Adding a quoting bit to the call probably
119119304Speter			 * extends historic practice, but it feels right.
119219304Speter			 */
119319304Speter			if (evp->e_value == K_VLNEXT) {
119419304Speter				quote = EC_QUOTED;
119519304Speter				break;
119619304Speter			}
119719304Speter			return (GC_OK);
119819304Speter		case E_ERR:
119919304Speter		case E_EOF:
120019304Speter			return (GC_FATAL);
120119304Speter		case E_INTERRUPT:
120219304Speter			/*
120319304Speter			 * !!!
120419304Speter			 * Historically, vi beeped on command level interrupts.
120519304Speter			 *
120619304Speter			 * Historically, vi exited to ex mode if no file was
120719304Speter			 * named on the command line, and two interrupts were
120819304Speter			 * generated in a row.  (Just figured you might want
120919304Speter			 * to know that.)
121019304Speter			 */
121119304Speter			(void)sp->gp->scr_bell(sp);
121219304Speter			return (GC_INTERRUPT);
121319304Speter		case E_REPAINT:
121419304Speter			if (vs_repaint(sp, evp))
121519304Speter				return (GC_FATAL);
121619304Speter			break;
121719304Speter		case E_WRESIZE:
121819304Speter			return (GC_ERR);
121919304Speter		case E_QUIT:
122019304Speter		case E_WRITE:
122119304Speter			if (command_events)
122219304Speter				return (GC_EVENT);
122319304Speter			/* FALLTHROUGH */
122419304Speter		default:
122519304Speter			v_event_err(sp, evp);
122619304Speter			return (GC_ERR);
122719304Speter		}
122819304Speter	}
122919304Speter	/* NOTREACHED */
123019304Speter}
123119304Speter
123219304Speter#if defined(DEBUG) && defined(COMLOG)
123319304Speter/*
123419304Speter * v_comlog --
123519304Speter *	Log the contents of the command structure.
123619304Speter */
123719304Speterstatic void
123819304Speterv_comlog(sp, vp)
123919304Speter	SCR *sp;
124019304Speter	VICMD *vp;
124119304Speter{
124219304Speter	TRACE(sp, "vcmd: %c", vp->key);
124319304Speter	if (F_ISSET(vp, VC_BUFFER))
124419304Speter		TRACE(sp, " buffer: %c", vp->buffer);
124519304Speter	if (F_ISSET(vp, VC_C1SET))
124619304Speter		TRACE(sp, " c1: %lu", vp->count);
124719304Speter	if (F_ISSET(vp, VC_C2SET))
124819304Speter		TRACE(sp, " c2: %lu", vp->count2);
124919304Speter	TRACE(sp, " flags: 0x%x\n", vp->flags);
125019304Speter}
125119304Speter#endif
1252