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
13254225Speterstatic const char sccsid[] = "$Id: vi.c,v 10.61 2011/12/21 13:08:30 zy Exp $";
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_motion __P((SCR *, VICMD *, VICMD *, int *));
4419304Speter
4519304Speter#if defined(DEBUG) && defined(COMLOG)
4619304Speterstatic void	v_comlog __P((SCR *, VICMD *));
4719304Speter#endif
4819304Speter
4919304Speter/*
5019304Speter * Side-effect:
5119304Speter *	The dot structure can be set by the underlying vi functions,
5219304Speter *	see v_Put() and v_put().
5319304Speter */
5419304Speter#define	DOT		(&VIP(sp)->sdot)
5519304Speter#define	DOTMOTION	(&VIP(sp)->sdotmotion)
5619304Speter
5719304Speter/*
5819304Speter * vi --
5919304Speter * 	Main vi command loop.
6019304Speter *
6119304Speter * PUBLIC: int vi __P((SCR **));
6219304Speter */
6319304Speterint
64254225Spetervi(SCR **spp)
6519304Speter{
6619304Speter	GS *gp;
6719304Speter	MARK abs;
6819304Speter	SCR *next, *sp;
69254225Speter	VICMD cmd = { 0 }, *vp;
7019304Speter	VI_PRIVATE *vip;
7119304Speter	int comcount, mapped, rval;
7219304Speter
7319304Speter	/* Get the first screen. */
7419304Speter	sp = *spp;
7519304Speter	gp = sp->gp;
7619304Speter
77254225Speter	/* Point to the command structure. */
7819304Speter	vp = &cmd;
7919304Speter
8019304Speter	/* Reset strange attraction. */
8119304Speter	F_SET(vp, VM_RCM_SET);
8219304Speter
8319304Speter	/* Initialize the vi screen. */
8419304Speter	if (v_init(sp))
8519304Speter		return (1);
8619304Speter
8719304Speter	/* Set the focus. */
8819304Speter	(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
8919304Speter
9019304Speter	for (vip = VIP(sp), rval = 0;;) {
9119304Speter		/* Resolve messages. */
9219304Speter		if (!MAPPED_KEYS_WAITING(sp) && vs_resolve(sp, NULL, 0))
9319304Speter			goto ret;
9419304Speter
9519304Speter		/*
9619304Speter		 * If not skipping a refresh, return to command mode and
9719304Speter		 * refresh the screen.
9819304Speter		 */
9919304Speter		if (F_ISSET(vip, VIP_S_REFRESH))
10019304Speter			F_CLR(vip, VIP_S_REFRESH);
10119304Speter		else {
10219304Speter			sp->showmode = SM_COMMAND;
10319304Speter			if (vs_refresh(sp, 0))
10419304Speter				goto ret;
10519304Speter		}
10619304Speter
10719304Speter		/* Set the new favorite position. */
10819304Speter		if (F_ISSET(vp, VM_RCM_SET | VM_RCM_SETFNB | VM_RCM_SETNNB)) {
10919304Speter			F_CLR(vip, VIP_RCM_LAST);
11019304Speter			(void)vs_column(sp, &sp->rcm);
11119304Speter		}
11219304Speter
11319304Speter		/*
11419304Speter		 * If not currently in a map, log the cursor position,
11519304Speter		 * and set a flag so that this command can become the
11619304Speter		 * DOT command.
11719304Speter		 */
11819304Speter		if (MAPPED_KEYS_WAITING(sp))
11919304Speter			mapped = 1;
12019304Speter		else {
12119304Speter			if (log_cursor(sp))
12219304Speter				goto err;
12319304Speter			mapped = 0;
12419304Speter		}
12519304Speter
12619304Speter		/*
12719304Speter		 * There may be an ex command waiting, and we returned here
12819304Speter		 * only because we exited a screen or file.  In this case,
12919304Speter		 * we simply go back into the ex parser.
13019304Speter		 */
13119304Speter		if (EXCMD_RUNNING(gp)) {
13219304Speter			vp->kp = &vikeys[':'];
13319304Speter			goto ex_continue;
13419304Speter		}
13519304Speter
13619304Speter		/* Refresh the command structure. */
13719304Speter		memset(vp, 0, sizeof(VICMD));
13819304Speter
13919304Speter		/*
14019304Speter		 * We get a command, which may or may not have an associated
14119304Speter		 * motion.  If it does, we get it too, calling its underlying
14219304Speter		 * function to get the resulting mark.  We then call the
14319304Speter		 * command setting the cursor to the resulting mark.
14419304Speter		 *
14519304Speter		 * !!!
14619304Speter		 * Vi historically flushed mapped characters on error, but
14719304Speter		 * entering extra <escape> characters at the beginning of
14819304Speter		 * a map wasn't considered an error -- in fact, users would
14919304Speter		 * put leading <escape> characters in maps to clean up vi
15019304Speter		 * state before the map was interpreted.  Beauty!
15119304Speter		 */
15219304Speter		switch (v_cmd(sp, DOT, vp, NULL, &comcount, &mapped)) {
15319304Speter		case GC_ERR:
15419304Speter			goto err;
15519304Speter		case GC_ERR_NOFLUSH:
15619304Speter			goto gc_err_noflush;
15719304Speter		case GC_EVENT:
15819304Speter			goto gc_event;
15919304Speter		case GC_FATAL:
16019304Speter			goto ret;
16119304Speter		case GC_INTERRUPT:
16219304Speter			goto intr;
16319304Speter		case GC_OK:
16419304Speter			break;
16519304Speter		}
16619304Speter
16719304Speter		/* Check for security setting. */
16819304Speter		if (F_ISSET(vp->kp, V_SECURE) && O_ISSET(sp, O_SECURE)) {
16919304Speter			ex_emsg(sp, KEY_NAME(sp, vp->key), EXM_SECURE);
17019304Speter			goto err;
17119304Speter		}
17219304Speter
17319304Speter		/*
17419304Speter		 * Historical practice: if a dot command gets a new count,
17519304Speter		 * any motion component goes away, i.e. "d3w2." deletes a
17619304Speter		 * total of 5 words.
17719304Speter		 */
17819304Speter		if (F_ISSET(vp, VC_ISDOT) && comcount)
17919304Speter			DOTMOTION->count = 1;
18019304Speter
18119304Speter		/* Copy the key flags into the local structure. */
18219304Speter		F_SET(vp, vp->kp->flags);
18319304Speter
18419304Speter		/* Prepare to set the previous context. */
18519304Speter		if (F_ISSET(vp, V_ABS | V_ABS_C | V_ABS_L)) {
18619304Speter			abs.lno = sp->lno;
18719304Speter			abs.cno = sp->cno;
18819304Speter		}
18919304Speter
19019304Speter		/*
19119304Speter		 * Set the three cursor locations to the current cursor.  The
19219304Speter		 * underlying routines don't bother if the cursor doesn't move.
19319304Speter		 * This also handles line commands (e.g. Y) defaulting to the
19419304Speter		 * current line.
19519304Speter		 */
19619304Speter		vp->m_start.lno = vp->m_stop.lno = vp->m_final.lno = sp->lno;
19719304Speter		vp->m_start.cno = vp->m_stop.cno = vp->m_final.cno = sp->cno;
19819304Speter
19919304Speter		/*
20019304Speter		 * Do any required motion; v_motion sets the from MARK and the
20119304Speter		 * line mode flag, as well as the VM_RCM flags.
20219304Speter		 */
20319304Speter		if (F_ISSET(vp, V_MOTION) &&
20419304Speter		    v_motion(sp, DOTMOTION, vp, &mapped)) {
20519304Speter			if (INTERRUPTED(sp))
20619304Speter				goto intr;
20719304Speter			goto err;
20819304Speter		}
20919304Speter
21019304Speter		/*
21119304Speter		 * If a count is set and the command is line oriented, set the
21219304Speter		 * to MARK here relative to the cursor/from MARK.  This is for
21319304Speter		 * commands that take both counts and motions, i.e. "4yy" and
21419304Speter		 * "y%".  As there's no way the command can know which the user
21519304Speter		 * did, we have to do it here.  (There are commands that are
21619304Speter		 * line oriented and that take counts ("#G", "#H"), for which
21719304Speter		 * this calculation is either completely meaningless or wrong.
21819304Speter		 * Each command must validate the value for itself.
21919304Speter		 */
22019304Speter		if (F_ISSET(vp, VC_C1SET) && F_ISSET(vp, VM_LMODE))
22119304Speter			vp->m_stop.lno += vp->count - 1;
22219304Speter
22319304Speter		/* Increment the command count. */
22419304Speter		++sp->ccnt;
22519304Speter
22619304Speter#if defined(DEBUG) && defined(COMLOG)
22719304Speter		v_comlog(sp, vp);
22819304Speter#endif
22919304Speter		/* Call the function. */
23019304Speterex_continue:	if (vp->kp->func(sp, vp))
23119304Speter			goto err;
23219304Spetergc_event:
23319304Speter#ifdef DEBUG
23419304Speter		/* Make sure no function left the temporary space locked. */
23519304Speter		if (F_ISSET(gp, G_TMP_INUSE)) {
23619304Speter			F_CLR(gp, G_TMP_INUSE);
23719304Speter			msgq(sp, M_ERR,
23819304Speter			    "232|vi: temporary buffer not released");
23919304Speter		}
24019304Speter#endif
24119304Speter		/*
24219304Speter		 * If we're exiting this screen, move to the next one, or, if
24319304Speter		 * there aren't any more, return to the main editor loop.  The
24419304Speter		 * ordering is careful, don't discard the contents of sp until
24519304Speter		 * the end.
24619304Speter		 */
24719304Speter		if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
24819304Speter			if (file_end(sp, NULL, F_ISSET(sp, SC_EXIT_FORCE)))
24919304Speter				goto ret;
25019304Speter			if (vs_discard(sp, &next))
25119304Speter				goto ret;
25219304Speter			if (next == NULL && vs_swap(sp, &next, NULL))
25319304Speter				goto ret;
25419304Speter			*spp = next;
25519304Speter			if (screen_end(sp))
25619304Speter				goto ret;
25719304Speter			if (next == NULL)
25819304Speter				break;
25919304Speter
26019304Speter			/* Switch screens, change focus. */
26119304Speter			sp = next;
26219304Speter			vip = VIP(sp);
26319304Speter			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
26419304Speter
26519304Speter			/* Don't trust the cursor. */
26619304Speter			F_SET(vip, VIP_CUR_INVALID);
26719304Speter
26819304Speter			continue;
26919304Speter		}
27019304Speter
27119304Speter		/*
27219304Speter		 * Set the dot command structure.
27319304Speter		 *
27419304Speter		 * !!!
27519304Speter		 * Historically, commands which used mapped keys did not
27619304Speter		 * set the dot command, with the exception of the text
27719304Speter		 * input commands.
27819304Speter		 */
27919304Speter		if (F_ISSET(vp, V_DOT) && !mapped) {
28019304Speter			*DOT = cmd;
28119304Speter			F_SET(DOT, VC_ISDOT);
28219304Speter
28319304Speter			/*
28419304Speter			 * If a count was supplied for both the command and
28519304Speter			 * its motion, the count was used only for the motion.
28619304Speter			 * Turn the count back on for the dot structure.
28719304Speter			 */
28819304Speter			if (F_ISSET(vp, VC_C1RESET))
28919304Speter				F_SET(DOT, VC_C1SET);
29019304Speter
29119304Speter			/* VM flags aren't retained. */
29219304Speter			F_CLR(DOT, VM_COMMASK | VM_RCM_MASK);
29319304Speter		}
29419304Speter
29519304Speter		/*
29619304Speter		 * Some vi row movements are "attracted" to the last position
29719304Speter		 * set, i.e. the VM_RCM commands are moths to the VM_RCM_SET
29819304Speter		 * commands' candle.  If the movement is to the EOL the vi
29919304Speter		 * command handles it.  If it's to the beginning, we handle it
30019304Speter		 * here.
30119304Speter		 *
30219304Speter		 * Note, some commands (e.g. _, ^) don't set the VM_RCM_SETFNB
30319304Speter		 * flag, but do the work themselves.  The reason is that they
30419304Speter		 * have to modify the column in case they're being used as a
30519304Speter		 * motion component.  Other similar commands (e.g. +, -) don't
30619304Speter		 * have to modify the column because they are always line mode
30719304Speter		 * operations when used as motions, so the column number isn't
30819304Speter		 * of any interest.
30919304Speter		 *
31019304Speter		 * Does this totally violate the screen and editor layering?
31119304Speter		 * You betcha.  As they say, if you think you understand it,
31219304Speter		 * you don't.
31319304Speter		 */
31419304Speter		switch (F_ISSET(vp, VM_RCM_MASK)) {
31519304Speter		case 0:
31619304Speter		case VM_RCM_SET:
31719304Speter			break;
31819304Speter		case VM_RCM:
31919304Speter			vp->m_final.cno = vs_rcm(sp,
32019304Speter			    vp->m_final.lno, F_ISSET(vip, VIP_RCM_LAST));
32119304Speter			break;
32219304Speter		case VM_RCM_SETLAST:
32319304Speter			F_SET(vip, VIP_RCM_LAST);
32419304Speter			break;
32519304Speter		case VM_RCM_SETFNB:
32619304Speter			vp->m_final.cno = 0;
32719304Speter			/* FALLTHROUGH */
32819304Speter		case VM_RCM_SETNNB:
32919304Speter			if (nonblank(sp, vp->m_final.lno, &vp->m_final.cno))
33019304Speter				goto err;
33119304Speter			break;
33219304Speter		default:
33319304Speter			abort();
33419304Speter		}
33519304Speter
33619304Speter		/* Update the cursor. */
33719304Speter		sp->lno = vp->m_final.lno;
33819304Speter		sp->cno = vp->m_final.cno;
33919304Speter
34019304Speter		/*
34119304Speter		 * Set the absolute mark -- set even if a tags or similar
34219304Speter		 * command, since the tag may be moving to the same file.
34319304Speter		 */
34419304Speter		if ((F_ISSET(vp, V_ABS) ||
345254225Speter		    (F_ISSET(vp, V_ABS_L) && sp->lno != abs.lno) ||
346254225Speter		    (F_ISSET(vp, V_ABS_C) &&
347254225Speter		    (sp->lno != abs.lno || sp->cno != abs.cno))) &&
34819304Speter		    mark_set(sp, ABSMARK1, &abs, 1))
34919304Speter			goto err;
35019304Speter
35119304Speter		if (0) {
35219304Spetererr:			if (v_event_flush(sp, CH_MAPPED))
35319304Speter				msgq(sp, M_BERR,
35419304Speter			    "110|Vi command failed: mapped keys discarded");
35519304Speter		}
35619304Speter
35719304Speter		/*
35819304Speter		 * Check and clear interrupts.  There's an obvious race, but
35919304Speter		 * it's not worth fixing.
36019304Speter		 */
36119304Spetergc_err_noflush:	if (INTERRUPTED(sp)) {
36219304Speterintr:			CLR_INTERRUPT(sp);
36319304Speter			if (v_event_flush(sp, CH_MAPPED))
36419304Speter				msgq(sp, M_ERR,
36519304Speter				    "231|Interrupted: mapped keys discarded");
36619304Speter			else
36719304Speter				msgq(sp, M_ERR, "236|Interrupted");
36819304Speter		}
36919304Speter
37019304Speter		/* If the last command switched screens, update. */
37119304Speter		if (F_ISSET(sp, SC_SSWITCH)) {
37219304Speter			F_CLR(sp, SC_SSWITCH);
37319304Speter
37419304Speter			/*
37519304Speter			 * If the current screen is still displayed, it will
37619304Speter			 * need a new status line.
37719304Speter			 */
37819304Speter			F_SET(sp, SC_STATUS);
37919304Speter
38019304Speter			/* Switch screens, change focus. */
38119304Speter			sp = sp->nextdisp;
38219304Speter			vip = VIP(sp);
38319304Speter			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
38419304Speter
38519304Speter			/* Don't trust the cursor. */
38619304Speter			F_SET(vip, VIP_CUR_INVALID);
38719304Speter
38819304Speter			/* Refresh so we can display messages. */
38919304Speter			if (vs_refresh(sp, 1))
39019304Speter				return (1);
39119304Speter		}
39219304Speter
39319304Speter		/* If the last command switched files, change focus. */
39419304Speter		if (F_ISSET(sp, SC_FSWITCH)) {
39519304Speter			F_CLR(sp, SC_FSWITCH);
39619304Speter			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
39719304Speter		}
39819304Speter
39919304Speter		/* If leaving vi, return to the main editor loop. */
40019304Speter		if (F_ISSET(gp, G_SRESTART) || F_ISSET(sp, SC_EX)) {
40119304Speter			*spp = sp;
40219304Speter			v_dtoh(sp);
403254225Speter			gp->scr_discard(sp, NULL);
40419304Speter			break;
40519304Speter		}
40619304Speter	}
40719304Speter	if (0)
40819304Speterret:		rval = 1;
40919304Speter	return (rval);
41019304Speter}
41119304Speter
41219304Speter#define	KEY(key, ec_flags) {						\
41319304Speter	if ((gcret = v_key(sp, 0, &ev, ec_flags)) != GC_OK)		\
41419304Speter		return (gcret);						\
41519304Speter	if (ev.e_value == K_ESCAPE)					\
41619304Speter		goto esc;						\
41719304Speter	if (F_ISSET(&ev.e_ch, CH_MAPPED))				\
41819304Speter		*mappedp = 1;						\
41919304Speter	key = ev.e_c;							\
42019304Speter}
42119304Speter
42219304Speter/*
42319304Speter * The O_TILDEOP option makes the ~ command take a motion instead
42419304Speter * of a straight count.  This is the replacement structure we use
42519304Speter * instead of the one currently in the VIKEYS table.
42619304Speter *
42719304Speter * XXX
42819304Speter * This should probably be deleted -- it's not all that useful, and
42919304Speter * we get help messages wrong.
43019304Speter */
43119304SpeterVIKEYS const tmotion = {
43219304Speter	v_mulcase,	V_CNT|V_DOT|V_MOTION|VM_RCM_SET,
43319304Speter	"[count]~[count]motion",
43419304Speter	" ~ change case to motion"
43519304Speter};
43619304Speter
43719304Speter/*
43819304Speter * v_cmd --
43919304Speter *
44019304Speter * The command structure for vi is less complex than ex (and don't think
44119304Speter * I'm not grateful!)  The command syntax is:
44219304Speter *
44319304Speter *	[count] [buffer] [count] key [[motion] | [buffer] [character]]
44419304Speter *
44519304Speter * and there are several special cases.  The motion value is itself a vi
44619304Speter * command, with the syntax:
44719304Speter *
44819304Speter *	[count] key [character]
44919304Speter */
45019304Speterstatic gcret_t
451254225Speterv_cmd(
452254225Speter	SCR *sp,
453254225Speter	VICMD *dp,
454254225Speter	VICMD *vp,
455254225Speter	VICMD *ismotion,	/* Previous key if getting motion component. */
456254225Speter	int *comcountp,
457254225Speter	int *mappedp)
45819304Speter{
45919304Speter	enum { COMMANDMODE, ISPARTIAL, NOTPARTIAL } cpart;
46019304Speter	EVENT ev;
46119304Speter	VIKEYS const *kp;
46219304Speter	gcret_t gcret;
46319304Speter	u_int flags;
46419304Speter	CHAR_T key;
46519304Speter	char *s;
46619304Speter
46719304Speter	/*
46819304Speter	 * Get a key.
46919304Speter	 *
47019304Speter	 * <escape> cancels partial commands, i.e. a command where at least
47119304Speter	 * one non-numeric character has been entered.  Otherwise, it beeps
47219304Speter	 * the terminal.
47319304Speter	 *
47419304Speter	 * !!!
47519304Speter	 * POSIX 1003.2-1992 explicitly disallows cancelling commands where
47619304Speter	 * all that's been entered is a number, requiring that the terminal
47719304Speter	 * be alerted.
47819304Speter	 */
47919304Speter	cpart = ismotion == NULL ? COMMANDMODE : ISPARTIAL;
48019304Speter	if ((gcret =
48119304Speter	    v_key(sp, ismotion == NULL, &ev, EC_MAPCOMMAND)) != GC_OK) {
48219304Speter		if (gcret == GC_EVENT)
48319304Speter			vp->ev = ev;
48419304Speter		return (gcret);
48519304Speter	}
48619304Speter	if (ev.e_value == K_ESCAPE)
48719304Speter		goto esc;
48819304Speter	if (F_ISSET(&ev.e_ch, CH_MAPPED))
48919304Speter		*mappedp = 1;
49019304Speter	key = ev.e_c;
49119304Speter
49219304Speter	if (ismotion == NULL)
49319304Speter		cpart = NOTPARTIAL;
49419304Speter
495254225Speter	/* Pick up an optional buffer. */
49619304Speter	if (key == '"') {
49719304Speter		cpart = ISPARTIAL;
49819304Speter		if (ismotion != NULL) {
49919304Speter			v_emsg(sp, NULL, VIM_COMBUF);
50019304Speter			return (GC_ERR);
50119304Speter		}
50219304Speter		KEY(vp->buffer, 0);
50319304Speter		F_SET(vp, VC_BUFFER);
50419304Speter
50519304Speter		KEY(key, EC_MAPCOMMAND);
50619304Speter	}
50719304Speter
50819304Speter	/*
509254225Speter	 * Pick up an optional count, where a leading 0 is not a count,
51019304Speter	 * it's a command.
51119304Speter	 */
512254225Speter	if (ISDIGIT(key) && key != '0') {
51319304Speter		if (v_count(sp, key, &vp->count))
51419304Speter			return (GC_ERR);
51519304Speter		F_SET(vp, VC_C1SET);
51619304Speter		*comcountp = 1;
51719304Speter
51819304Speter		KEY(key, EC_MAPCOMMAND);
51919304Speter	} else
52019304Speter		*comcountp = 0;
52119304Speter
52219304Speter	/* Pick up optional buffer. */
52319304Speter	if (key == '"') {
52419304Speter		cpart = ISPARTIAL;
52519304Speter		if (F_ISSET(vp, VC_BUFFER)) {
52619304Speter			msgq(sp, M_ERR, "234|Only one buffer may be specified");
52719304Speter			return (GC_ERR);
52819304Speter		}
52919304Speter		if (ismotion != NULL) {
53019304Speter			v_emsg(sp, NULL, VIM_COMBUF);
53119304Speter			return (GC_ERR);
53219304Speter		}
53319304Speter		KEY(vp->buffer, 0);
53419304Speter		F_SET(vp, VC_BUFFER);
53519304Speter
53619304Speter		KEY(key, EC_MAPCOMMAND);
53719304Speter	}
53819304Speter
53919304Speter	/* Check for an OOB command key. */
54019304Speter	cpart = ISPARTIAL;
54119304Speter	if (key > MAXVIKEY) {
54219304Speter		v_emsg(sp, KEY_NAME(sp, key), VIM_NOCOM);
54319304Speter		return (GC_ERR);
54419304Speter	}
54519304Speter	kp = &vikeys[vp->key = key];
54619304Speter
54719304Speter	/*
54819304Speter	 * !!!
54919304Speter	 * Historically, D accepted and then ignored a count.  Match it.
55019304Speter	 */
55119304Speter	if (vp->key == 'D' && F_ISSET(vp, VC_C1SET)) {
55219304Speter		*comcountp = 0;
55319304Speter		vp->count = 0;
55419304Speter		F_CLR(vp, VC_C1SET);
55519304Speter	}
55619304Speter
55719304Speter	/* Check for command aliases. */
55819304Speter	if (kp->func == NULL && (kp = v_alias(sp, vp, kp)) == NULL)
55919304Speter		return (GC_ERR);
56019304Speter
56119304Speter	/* The tildeop option makes the ~ command take a motion. */
56219304Speter	if (key == '~' && O_ISSET(sp, O_TILDEOP))
56319304Speter		kp = &tmotion;
56419304Speter
56519304Speter	vp->kp = kp;
56619304Speter
56719304Speter	/*
56819304Speter	 * Find the command.  The only legal command with no underlying
56919304Speter	 * function is dot.  It's historic practice that <escape> doesn't
57019304Speter	 * just erase the preceding number, it beeps the terminal as well.
57119304Speter	 * It's a common problem, so just beep the terminal unless verbose
57219304Speter	 * was set.
57319304Speter	 */
57419304Speter	if (kp->func == NULL) {
57519304Speter		if (key != '.') {
57619304Speter			v_emsg(sp, KEY_NAME(sp, key),
57719304Speter			    ev.e_value == K_ESCAPE ? VIM_NOCOM_B : VIM_NOCOM);
57819304Speter			return (GC_ERR);
57919304Speter		}
58019304Speter
58119304Speter		/* If called for a motion command, stop now. */
58219304Speter		if (dp == NULL)
58319304Speter			goto usage;
58419304Speter
58519304Speter		/*
58619304Speter		 * !!!
58719304Speter		 * If a '.' is immediately entered after an undo command, we
58819304Speter		 * replay the log instead of redoing the last command.  This
58919304Speter		 * is necessary because 'u' can't set the dot command -- see
59019304Speter		 * vi/v_undo.c:v_undo for details.
59119304Speter		 */
59219304Speter		if (VIP(sp)->u_ccnt == sp->ccnt) {
59319304Speter			vp->kp = &vikeys['u'];
59419304Speter			F_SET(vp, VC_ISDOT);
59519304Speter			return (GC_OK);
59619304Speter		}
59719304Speter
59819304Speter		/* Otherwise, a repeatable command must have been executed. */
59919304Speter		if (!F_ISSET(dp, VC_ISDOT)) {
60019304Speter			msgq(sp, M_ERR, "208|No command to repeat");
60119304Speter			return (GC_ERR);
60219304Speter		}
60319304Speter
60419304Speter		/* Set new count/buffer, if any, and return. */
60519304Speter		if (F_ISSET(vp, VC_C1SET)) {
60619304Speter			F_SET(dp, VC_C1SET);
60719304Speter			dp->count = vp->count;
60819304Speter		}
60919304Speter		if (F_ISSET(vp, VC_BUFFER))
61019304Speter			dp->buffer = vp->buffer;
61119304Speter
61219304Speter		*vp = *dp;
61319304Speter		return (GC_OK);
61419304Speter	}
61519304Speter
61619304Speter	/* Set the flags based on the command flags. */
61719304Speter	flags = kp->flags;
61819304Speter
61919304Speter	/* Check for illegal count. */
62019304Speter	if (F_ISSET(vp, VC_C1SET) && !LF_ISSET(V_CNT))
62119304Speter		goto usage;
62219304Speter
62319304Speter	/* Illegal motion command. */
62419304Speter	if (ismotion == NULL) {
62519304Speter		/* Illegal buffer. */
62619304Speter		if (!LF_ISSET(V_OBUF) && F_ISSET(vp, VC_BUFFER))
62719304Speter			goto usage;
62819304Speter
62919304Speter		/* Required buffer. */
63019304Speter		if (LF_ISSET(V_RBUF)) {
63119304Speter			KEY(vp->buffer, 0);
63219304Speter			F_SET(vp, VC_BUFFER);
63319304Speter		}
63419304Speter	}
63519304Speter
63619304Speter	/*
63719304Speter	 * Special case: '[', ']' and 'Z' commands.  Doesn't the fact that
63819304Speter	 * the *single* characters don't mean anything but the *doubled*
63919304Speter	 * characters do, just frost your shorts?
64019304Speter	 */
64119304Speter	if (vp->key == '[' || vp->key == ']' || vp->key == 'Z') {
64219304Speter		/*
64319304Speter		 * Historically, half entered [[, ]] or Z commands weren't
64419304Speter		 * cancelled by <escape>, the terminal was beeped instead.
64519304Speter		 * POSIX.2-1992 probably didn't notice, and requires that
64619304Speter		 * they be cancelled instead of beeping.  Seems fine to me.
64719304Speter		 *
64819304Speter		 * Don't set the EC_MAPCOMMAND flag, apparently ] is a popular
64919304Speter		 * vi meta-character, and we don't want the user to wait while
65019304Speter		 * we time out a possible mapping.  This *appears* to match
651254225Speter		 * historic vi practice, but with mapping characters, You Just
65219304Speter		 * Never Know.
65319304Speter		 */
65419304Speter		KEY(key, 0);
65519304Speter
65619304Speter		if (vp->key != key) {
65719304Speterusage:			if (ismotion == NULL)
65819304Speter				s = kp->usage;
65919304Speter			else if (ismotion->key == '~' && O_ISSET(sp, O_TILDEOP))
66019304Speter				s = tmotion.usage;
66119304Speter			else
66219304Speter				s = vikeys[ismotion->key].usage;
66319304Speter			v_emsg(sp, s, VIM_USAGE);
66419304Speter			return (GC_ERR);
66519304Speter		}
66619304Speter	}
66719304Speter	/* Special case: 'z' command. */
66819304Speter	if (vp->key == 'z') {
66919304Speter		KEY(vp->character, 0);
670254225Speter		if (ISDIGIT(vp->character)) {
67119304Speter			if (v_count(sp, vp->character, &vp->count2))
67219304Speter				return (GC_ERR);
67319304Speter			F_SET(vp, VC_C2SET);
67419304Speter			KEY(vp->character, 0);
67519304Speter		}
67619304Speter	}
67719304Speter
67819304Speter	/*
679254225Speter	 * Commands that have motion components can be doubled to imply the
680254225Speter	 * current line.
68119304Speter	 */
68219304Speter	if (ismotion != NULL && ismotion->key != key && !LF_ISSET(V_MOVE)) {
68319304Speter		msgq(sp, M_ERR, "210|%s may not be used as a motion command",
68419304Speter		    KEY_NAME(sp, key));
68519304Speter		return (GC_ERR);
68619304Speter	}
68719304Speter
688254225Speter	/* Pick up required trailing character. */
68919304Speter	if (LF_ISSET(V_CHAR))
69019304Speter		KEY(vp->character, 0);
69119304Speter
69219304Speter	/* Get any associated cursor word. */
693254225Speter	if (F_ISSET(kp, V_KEYW) && v_curword(sp))
69419304Speter		return (GC_ERR);
69519304Speter
69619304Speter	return (GC_OK);
69719304Speter
69819304Speteresc:	switch (cpart) {
69919304Speter	case COMMANDMODE:
70019304Speter		msgq(sp, M_BERR, "211|Already in command mode");
70119304Speter		return (GC_ERR_NOFLUSH);
70219304Speter	case ISPARTIAL:
70319304Speter		break;
70419304Speter	case NOTPARTIAL:
70519304Speter		(void)sp->gp->scr_bell(sp);
70619304Speter		break;
70719304Speter	}
70819304Speter	return (GC_ERR);
70919304Speter}
71019304Speter
71119304Speter/*
71219304Speter * v_motion --
71319304Speter *
71419304Speter * Get resulting motion mark.
71519304Speter */
71619304Speterstatic int
717254225Speterv_motion(
718254225Speter	SCR *sp,
719254225Speter	VICMD *dm,
720254225Speter	VICMD *vp,
721254225Speter	int *mappedp)
72219304Speter{
72319304Speter	VICMD motion;
72419304Speter	size_t len;
72519304Speter	u_long cnt;
72619304Speter	u_int flags;
72719304Speter	int tilde_reset, notused;
72819304Speter
72919304Speter	/*
73019304Speter	 * If '.' command, use the dot motion, else get the motion command.
73119304Speter	 * Clear any line motion flags, the subsequent motion isn't always
73219304Speter	 * the same, i.e. "/aaa" may or may not be a line motion.
73319304Speter	 */
73419304Speter	if (F_ISSET(vp, VC_ISDOT)) {
73519304Speter		motion = *dm;
73619304Speter		F_SET(&motion, VC_ISDOT);
73719304Speter		F_CLR(&motion, VM_COMMASK);
73819304Speter	} else {
73919304Speter		memset(&motion, 0, sizeof(VICMD));
74019304Speter		if (v_cmd(sp, NULL, &motion, vp, &notused, mappedp) != GC_OK)
74119304Speter			return (1);
74219304Speter	}
74319304Speter
74419304Speter	/*
74519304Speter	 * A count may be provided both to the command and to the motion, in
74619304Speter	 * which case the count is multiplicative.  For example, "3y4y" is the
74719304Speter	 * same as "12yy".  This count is provided to the motion command and
74819304Speter	 * not to the regular function.
74919304Speter	 */
75019304Speter	cnt = motion.count = F_ISSET(&motion, VC_C1SET) ? motion.count : 1;
75119304Speter	if (F_ISSET(vp, VC_C1SET)) {
75219304Speter		motion.count *= vp->count;
75319304Speter		F_SET(&motion, VC_C1SET);
75419304Speter
75519304Speter		/*
75619304Speter		 * Set flags to restore the original values of the command
75719304Speter		 * structure so dot commands can change the count values,
75819304Speter		 * e.g. "2dw" "3." deletes a total of five words.
75919304Speter		 */
76019304Speter		F_CLR(vp, VC_C1SET);
76119304Speter		F_SET(vp, VC_C1RESET);
76219304Speter	}
76319304Speter
76419304Speter	/*
76519304Speter	 * Some commands can be repeated to indicate the current line.  In
76619304Speter	 * this case, or if the command is a "line command", set the flags
76719304Speter	 * appropriately.  If not a doubled command, run the function to get
76819304Speter	 * the resulting mark.
76919304Speter 	 */
77019304Speter	if (vp->key == motion.key) {
77119304Speter		F_SET(vp, VM_LDOUBLE | VM_LMODE);
77219304Speter
77319304Speter		/* Set the origin of the command. */
77419304Speter		vp->m_start.lno = sp->lno;
77519304Speter		vp->m_start.cno = 0;
77619304Speter
77719304Speter		/*
77819304Speter		 * Set the end of the command.
77919304Speter		 *
78019304Speter		 * If the current line is missing, i.e. the file is empty,
78119304Speter		 * historic vi permitted a "cc" or "!!" command to insert
78219304Speter		 * text.
78319304Speter		 */
78419304Speter		vp->m_stop.lno = sp->lno + motion.count - 1;
78519304Speter		if (db_get(sp, vp->m_stop.lno, 0, NULL, &len)) {
78619304Speter			if (vp->m_stop.lno != 1 ||
787254225Speter			   (vp->key != 'c' && vp->key != '!')) {
78819304Speter				v_emsg(sp, NULL, VIM_EMPTY);
78919304Speter				return (1);
79019304Speter			}
79119304Speter			vp->m_stop.cno = 0;
79219304Speter		} else
79319304Speter			vp->m_stop.cno = len ? len - 1 : 0;
79419304Speter	} else {
79519304Speter		/*
79619304Speter		 * Motion commands change the underlying movement (*snarl*).
79719304Speter		 * For example, "l" is illegal at the end of a line, but "dl"
79819304Speter		 * is not.  Set flags so the function knows the situation.
79919304Speter		 */
80019304Speter		motion.rkp = vp->kp;
80119304Speter
80219304Speter		/*
80319304Speter		 * XXX
80419304Speter		 * Use yank instead of creating a new motion command, it's a
80519304Speter		 * lot easier for now.
80619304Speter		 */
80719304Speter		if (vp->kp == &tmotion) {
80819304Speter			tilde_reset = 1;
80919304Speter			vp->kp = &vikeys['y'];
81019304Speter		} else
81119304Speter			tilde_reset = 0;
81219304Speter
81319304Speter		/*
81419304Speter		 * Copy the key flags into the local structure, except for the
81519304Speter		 * RCM flags -- the motion command will set the RCM flags in
81619304Speter		 * the vp structure if necessary.  This means that the motion
81719304Speter		 * command is expected to determine where the cursor ends up!
81819304Speter		 * However, we save off the current RCM mask and restore it if
81919304Speter		 * it no RCM flags are set by the motion command, with a small
82019304Speter		 * modification.
82119304Speter		 *
82219304Speter		 * We replace the VM_RCM_SET flag with the VM_RCM flag.  This
82319304Speter		 * is so that cursor movement doesn't set the relative position
82419304Speter		 * unless the motion command explicitly specified it.  This
82519304Speter		 * appears to match historic practice, but I've never been able
82619304Speter		 * to develop a hard-and-fast rule.
82719304Speter		 */
82819304Speter		flags = F_ISSET(vp, VM_RCM_MASK);
82919304Speter		if (LF_ISSET(VM_RCM_SET)) {
83019304Speter			LF_SET(VM_RCM);
83119304Speter			LF_CLR(VM_RCM_SET);
83219304Speter		}
83319304Speter		F_CLR(vp, VM_RCM_MASK);
83419304Speter		F_SET(&motion, motion.kp->flags & ~VM_RCM_MASK);
83519304Speter
83619304Speter		/*
83719304Speter		 * Set the three cursor locations to the current cursor.  This
83819304Speter		 * permits commands like 'j' and 'k', that are line oriented
83919304Speter		 * motions and have special cursor suck semantics when they are
84019304Speter		 * used as standalone commands, to ignore column positioning.
84119304Speter		 */
84219304Speter		motion.m_final.lno =
84319304Speter		    motion.m_stop.lno = motion.m_start.lno = sp->lno;
84419304Speter		motion.m_final.cno =
84519304Speter		    motion.m_stop.cno = motion.m_start.cno = sp->cno;
84619304Speter
84719304Speter		/* Run the function. */
84819304Speter		if ((motion.kp->func)(sp, &motion))
84919304Speter			return (1);
85019304Speter
85119304Speter		/*
85219304Speter		 * If the current line is missing, i.e. the file is empty,
85319304Speter		 * historic vi allowed "c<motion>" or "!<motion>" to insert
85419304Speter		 * text.  Otherwise fail -- most motion commands will have
85519304Speter		 * already failed, but some, e.g. G, succeed in empty files.
85619304Speter		 */
85719304Speter		if (!db_exist(sp, vp->m_stop.lno)) {
85819304Speter			if (vp->m_stop.lno != 1 ||
859254225Speter			   (vp->key != 'c' && vp->key != '!')) {
86019304Speter				v_emsg(sp, NULL, VIM_EMPTY);
86119304Speter				return (1);
86219304Speter			}
86319304Speter			vp->m_stop.cno = 0;
86419304Speter		}
86519304Speter
86619304Speter		/*
86719304Speter		 * XXX
86819304Speter		 * See above.
86919304Speter		 */
87019304Speter		if (tilde_reset)
87119304Speter			vp->kp = &tmotion;
87219304Speter
87319304Speter		/*
87419304Speter		 * Copy cut buffer, line mode and cursor position information
87519304Speter		 * from the motion command structure, i.e. anything that the
87619304Speter		 * motion command can set for us.  The commands can flag the
87719304Speter		 * movement as a line motion (see v_sentence) as well as set
87819304Speter		 * the VM_RCM_* flags explicitly.
87919304Speter		 */
88019304Speter		F_SET(vp, F_ISSET(&motion, VM_COMMASK | VM_RCM_MASK));
88119304Speter
88219304Speter		/*
88319304Speter		 * If the motion command set no relative motion flags, use
88419304Speter		 * the (slightly) modified previous values.
88519304Speter		 */
88619304Speter		if (!F_ISSET(vp, VM_RCM_MASK))
88719304Speter			F_SET(vp, flags);
88819304Speter
88919304Speter		/*
89019304Speter		 * Commands can change behaviors based on the motion command
89119304Speter		 * used, for example, the ! command repeated the last bang
89219304Speter		 * command if N or n was used as the motion.
89319304Speter		 */
89419304Speter		vp->rkp = motion.kp;
89519304Speter
89619304Speter		/*
89719304Speter		 * Motion commands can reset all of the cursor information.
89819304Speter		 * If the motion is in the reverse direction, switch the
89919304Speter		 * from and to MARK's so that it's in a forward direction.
90019304Speter		 * Motions are from the from MARK to the to MARK (inclusive).
90119304Speter		 */
90219304Speter		if (motion.m_start.lno > motion.m_stop.lno ||
903254225Speter		    (motion.m_start.lno == motion.m_stop.lno &&
904254225Speter		    motion.m_start.cno > motion.m_stop.cno)) {
90519304Speter			vp->m_start = motion.m_stop;
90619304Speter			vp->m_stop = motion.m_start;
90719304Speter		} else {
90819304Speter			vp->m_start = motion.m_start;
90919304Speter			vp->m_stop = motion.m_stop;
91019304Speter		}
91119304Speter		vp->m_final = motion.m_final;
91219304Speter	}
91319304Speter
91419304Speter	/*
91519304Speter	 * If the command sets dot, save the motion structure.  The motion
91619304Speter	 * count was changed above and needs to be reset, that's why this
91719304Speter	 * is done here, and not in the calling routine.
91819304Speter	 */
91919304Speter	if (F_ISSET(vp->kp, V_DOT)) {
92019304Speter		*dm = motion;
92119304Speter		dm->count = cnt;
92219304Speter	}
92319304Speter	return (0);
92419304Speter}
92519304Speter
92619304Speter/*
92719304Speter * v_init --
92819304Speter *	Initialize the vi screen.
92919304Speter */
93019304Speterstatic int
931254225Speterv_init(SCR *sp)
93219304Speter{
93319304Speter	GS *gp;
93419304Speter	VI_PRIVATE *vip;
93519304Speter
93619304Speter	gp = sp->gp;
93719304Speter	vip = VIP(sp);
93819304Speter
93919304Speter	/* Switch into vi. */
94019304Speter	if (gp->scr_screen(sp, SC_VI))
94119304Speter		return (1);
94219304Speter	(void)gp->scr_attr(sp, SA_ALTERNATE, 1);
94319304Speter
94419304Speter	F_CLR(sp, SC_EX | SC_SCR_EX);
94519304Speter	F_SET(sp, SC_VI);
94619304Speter
94719304Speter	/*
94819304Speter	 * Initialize screen values.
94919304Speter	 *
95019304Speter	 * Small windows: see vs_refresh(), section 6a.
95119304Speter	 *
95219304Speter	 * Setup:
95319304Speter	 *	t_minrows is the minimum rows to display
95419304Speter	 *	t_maxrows is the maximum rows to display (rows - 1)
95519304Speter	 *	t_rows is the rows currently being displayed
95619304Speter	 */
95719304Speter	sp->rows = vip->srows = O_VAL(sp, O_LINES);
95819304Speter	sp->cols = O_VAL(sp, O_COLUMNS);
95919304Speter	sp->t_rows = sp->t_minrows = O_VAL(sp, O_WINDOW);
96019304Speter	if (sp->rows != 1) {
96119304Speter		if (sp->t_rows > sp->rows - 1) {
96219304Speter			sp->t_minrows = sp->t_rows = sp->rows - 1;
96319304Speter			msgq(sp, M_INFO,
96419304Speter			    "214|Windows option value is too large, max is %u",
965254225Speter			    (u_int)sp->t_rows);
96619304Speter		}
96719304Speter		sp->t_maxrows = sp->rows - 1;
96819304Speter	} else
96919304Speter		sp->t_maxrows = 1;
970254225Speter	sp->roff = sp->coff = 0;
97119304Speter
97219304Speter	/* Create a screen map. */
97319304Speter	CALLOC_RET(sp, HMAP, SMAP *, SIZE_HMAP(sp), sizeof(SMAP));
97419304Speter	TMAP = HMAP + (sp->t_rows - 1);
97519304Speter	HMAP->lno = sp->lno;
97619304Speter	HMAP->coff = 0;
97719304Speter	HMAP->soff = 1;
97819304Speter
97919304Speter	/*
98019304Speter	 * Fill the screen map from scratch -- try and center the line.  That
98119304Speter	 * way if we're starting with a file we've seen before, we'll put the
98219304Speter	 * line in the middle, otherwise, it won't work and we'll end up with
98319304Speter	 * the line at the top.
98419304Speter	 */
98519304Speter	F_SET(sp, SC_SCR_REFORMAT | SC_SCR_CENTER);
98619304Speter
98719304Speter	/* Invalidate the cursor. */
98819304Speter	F_SET(vip, VIP_CUR_INVALID);
98919304Speter
99019304Speter	/* Paint the screen image from scratch. */
99119304Speter	F_SET(vip, VIP_N_EX_PAINT);
99219304Speter
99319304Speter	return (0);
99419304Speter}
99519304Speter
99619304Speter/*
99719304Speter * v_dtoh --
99819304Speter *	Move all but the current screen to the hidden queue.
99919304Speter */
100019304Speterstatic void
1001254225Speterv_dtoh(SCR *sp)
100219304Speter{
100319304Speter	GS *gp;
100419304Speter	SCR *tsp;
100519304Speter	int hidden;
100619304Speter
100719304Speter	/* Move all screens to the hidden queue, tossing screen maps. */
100819304Speter	for (hidden = 0, gp = sp->gp;
1009254225Speter	    (tsp = TAILQ_FIRST(gp->dq)) != NULL; ++hidden) {
101019304Speter		if (_HMAP(tsp) != NULL) {
101119304Speter			free(_HMAP(tsp));
101219304Speter			_HMAP(tsp) = NULL;
101319304Speter		}
1014254225Speter		TAILQ_REMOVE(gp->dq, tsp, q);
1015254225Speter		TAILQ_INSERT_TAIL(gp->hq, tsp, q);
1016254225Speter		/* XXXX Change if hidden screens per window */
1017254225Speter		gp->scr_discard(tsp, NULL);
101819304Speter	}
101919304Speter
102019304Speter	/* Move current screen back to the display queue. */
1021254225Speter	TAILQ_REMOVE(gp->hq, sp, q);
1022254225Speter	TAILQ_INSERT_TAIL(gp->dq, sp, q);
102319304Speter
102419304Speter	if (hidden > 1)
102519304Speter		msgq(sp, M_INFO,
1026254225Speter		    "319|%d screens backgrounded; use :display to list them",
102719304Speter		    hidden - 1);
102819304Speter}
102919304Speter
103019304Speter/*
1031254225Speter * v_curword --
1032254225Speter *	Get the word (tagstring, actually) the cursor is on.
1033254225Speter *
1034254225Speter * PUBLIC: int v_curword __P((SCR *));
103519304Speter */
1036254225Speterint
1037254225Speterv_curword(SCR *sp)
103819304Speter{
103919304Speter	VI_PRIVATE *vip;
104019304Speter	size_t beg, end, len;
1041254225Speter	int moved;
1042254225Speter	CHAR_T *p;
104319304Speter
104419304Speter	if (db_get(sp, sp->lno, DBG_FATAL, &p, &len))
104519304Speter		return (1);
104619304Speter
104719304Speter	/*
104819304Speter	 * !!!
104919304Speter	 * Historically, tag commands skipped over any leading whitespace
105019304Speter	 * characters.  Make this true in general when using cursor words.
105119304Speter	 * If movement, getting a cursor word implies moving the cursor to
105219304Speter	 * its beginning.  Refresh now.
105319304Speter	 *
105419304Speter	 * !!!
105519304Speter	 * Find the beginning/end of the keyword.  Keywords are currently
105619304Speter	 * used for cursor-word searching and for tags.  Historical vi
105719304Speter	 * only used the word in a tag search from the cursor to the end
105819304Speter	 * of the word, i.e. if the cursor was on the 'b' in " abc ", the
105919304Speter	 * tag was "bc".  For consistency, we make cursor word searches
106019304Speter	 * follow the same rule.
106119304Speter	 */
106219304Speter	for (moved = 0,
1063254225Speter	    beg = sp->cno; beg < len && ISSPACE(p[beg]); moved = 1, ++beg);
106419304Speter	if (beg >= len) {
106519304Speter		msgq(sp, M_BERR, "212|Cursor not in a word");
106619304Speter		return (1);
106719304Speter	}
106819304Speter	if (moved) {
106919304Speter		sp->cno = beg;
107019304Speter		(void)vs_refresh(sp, 0);
107119304Speter	}
107219304Speter
1073254225Speter	/*
1074254225Speter	 * Find the end of the word.
1075254225Speter	 *
1076254225Speter	 * !!!
1077254225Speter	 * Historically, vi accepted any non-blank as initial character
1078254225Speter	 * when building up a tagstring.  Required by IEEE 1003.1-2001.
1079254225Speter	 */
1080254225Speter	for (end = beg; ++end < len && inword(p[end]););
108119304Speter
108219304Speter	vip = VIP(sp);
1083254225Speter	vip->klen = len = (end - beg);
1084254225Speter	BINC_RETW(sp, vip->keyw, vip->keywlen, len+1);
1085254225Speter	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 *
1095254225Speterv_alias(
1096254225Speter	SCR *sp,
1097254225Speter	VICMD *vp,
1098254225Speter	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
1131254225Speterv_count(
1132254225Speter	SCR *sp,
1133254225Speter	ARG_CHAR_T fkey,
1134254225Speter	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);
1153254225Speter			} 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);
1161254225Speter	} 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
1171254225Speterv_key(
1172254225Speter	SCR *sp,
1173254225Speter	int command_events,
1174254225Speter	EVENT *evp,
1175254225Speter	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			/* FALLTHROUGH */
122019304Speter		default:
122119304Speter			v_event_err(sp, evp);
122219304Speter			return (GC_ERR);
122319304Speter		}
122419304Speter	}
122519304Speter	/* NOTREACHED */
122619304Speter}
122719304Speter
122819304Speter#if defined(DEBUG) && defined(COMLOG)
122919304Speter/*
123019304Speter * v_comlog --
123119304Speter *	Log the contents of the command structure.
123219304Speter */
123319304Speterstatic void
1234254225Speterv_comlog(
1235254225Speter	SCR *sp,
1236254225Speter	VICMD *vp)
123719304Speter{
1238254225Speter	TRACE(sp, "vcmd: "WC, vp->key);
123919304Speter	if (F_ISSET(vp, VC_BUFFER))
1240254225Speter		TRACE(sp, " buffer: "WC, vp->buffer);
124119304Speter	if (F_ISSET(vp, VC_C1SET))
124219304Speter		TRACE(sp, " c1: %lu", vp->count);
124319304Speter	if (F_ISSET(vp, VC_C2SET))
124419304Speter		TRACE(sp, " c2: %lu", vp->count2);
124519304Speter	TRACE(sp, " flags: 0x%x\n", vp->flags);
124619304Speter}
124719304Speter#endif
1248