/*
 * v_cfirst -- [count]_
 *	Move to the first non-blank character in a line.
 *
 * PUBLIC: int v_cfirst __P((SCR *, VICMD *));
 */
int
v_cfirst(SCR *sp, VICMD *vp)
{
	db_recno_t cnt, lno;

	/*
	 * !!!
	 * If the _ is a motion component, it makes the command a line motion
	 * e.g. "d_" deletes the line.  It also means that the cursor doesn't
	 * move.
	 *
	 * The _ command never failed in the first column.
	 */
	if (ISMOTION(vp))
		F_SET(vp, VM_LMODE);
	/*
	 * !!!
	 * Historically a specified count makes _ move down count - 1
	 * rows, so, "3_" is the same as "2j_".
	 */
	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
	if (cnt != 1) {
		--vp->count;
		return (v_down(sp, vp));
	}

	/*
	 * Move to the first non-blank.
	 *
	 * Can't just use RCM_SET_FNB, in case _ is used as the motion
	 * component of another command.
	 */
	vp->m_stop.cno = 0;
	if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
		return (1);

	/*
	 * !!!
	 * The _ command has to fail if the file is empty and we're doing
	 * a delete.  If deleting line 1, and 0 is the first nonblank,
	 * make the check.
	 */
	if (vp->m_stop.lno == 1 &&
	    vp->m_stop.cno == 0 && ISCMD(vp->rkp, 'd')) {
		if (db_last(sp, &lno))
			return (1);
		if (lno == 0) {
			v_sol(sp);
			return (1);
		}
	}

	/*
	 * Delete and non-motion commands move to the end of the range,
	 * yank stays at the start.  Ignore others.
	 */
	vp->m_final =
	    ISMOTION(vp) && ISCMD(vp->rkp, 'y') ? vp->m_start : vp->m_stop;
	return (0);
}
Ejemplo n.º 2
0
/*
 * v_dollar -- [count]$
 *	Move to the last column.
 *
 * PUBLIC: int v_dollar __P((SCR *, VICMD *));
 */
int
v_dollar(SCR *sp, VICMD *vp)
{
	size_t len;
	int isempty;

	/*
	 * !!!
	 * A count moves down count - 1 rows, so, "3$" is the same as "2j$".
	 */
	if ((F_ISSET(vp, VC_C1SET) ? vp->count : 1) != 1) {
		/*
		 * !!!
		 * Historically, if the $ is a motion, and deleting from
		 * at or before the first non-blank of the line, it's a
		 * line motion, and the line motion flag is set.
		 */
		vp->m_stop.cno = 0;
		if (nonblank(sp, vp->m_start.lno, &vp->m_stop.cno))
			return (1);
		if (ISMOTION(vp) && vp->m_start.cno <= vp->m_stop.cno)
			F_SET(vp, VM_LMODE);

		--vp->count;
		if (v_down(sp, vp))
			return (1);
	}

	/*
	 * !!!
	 * Historically, it was illegal to use $ as a motion command on
	 * an empty line.  Unfortunately, even though C was historically
	 * aliased to c$, it (and not c$) was special cased to work on
	 * empty lines.  Since we alias C to c$ too, we have a problem.
	 * To fix it, we let c$ go through, on the assumption that it's
	 * not a problem for it to work.
	 */
	if (db_eget(sp, vp->m_stop.lno, NULL, &len, &isempty)) {
		if (!isempty)
			return (1);
		len = 0;
	}

	if (len == 0) {
		if (ISMOTION(vp) && !ISCMD(vp->rkp, 'c')) {
			v_eol(sp, NULL);
			return (1);
		}
		return (0);
	}

	/*
	 * Non-motion commands move to the end of the range.  Delete
	 * and yank stay at the start.  Ignore others.
	 */
	vp->m_stop.cno = len ? len - 1 : 0;
	vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
	return (0);
}
/*
 * v_first -- ^
 *	Move to the first non-blank character in this line.
 *
 * PUBLIC: int v_first __P((SCR *, VICMD *));
 */
int
v_first(SCR *sp, VICMD *vp)
{
	/*
	 * !!!
	 * Yielding to none in our quest for compatibility with every
	 * historical blemish of vi, no matter how strange it might be,
	 * we permit the user to enter a count and then ignore it.
	 */

	/*
	 * Move to the first non-blank.
	 *
	 * Can't just use RCM_SET_FNB, in case ^ is used as the motion
	 * component of another command.
	 */
	vp->m_stop.cno = 0;
	if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
		return (1);

	/*
	 * !!!
	 * The ^ command succeeded if used as a command when the cursor was
	 * on the first non-blank in the line, but failed if used as a motion
	 * component in the same situation.
	 */
	if (ISMOTION(vp) && vp->m_start.cno == vp->m_stop.cno) {
		v_sol(sp);
		return (1);
	}

	/*
	 * If moving right, non-motion commands move to the end of the range.
	 * Delete and yank stay at the start.  Motion commands adjust the
	 * ending point to the character before the current ending charcter.
	 *
	 * If moving left, all commands move to the end of the range.  Motion
	 * commands adjust the starting point to the character before the
	 * current starting character.
	 */
	if (vp->m_start.cno < vp->m_stop.cno)
		if (ISMOTION(vp)) {
			--vp->m_stop.cno;
			vp->m_final = vp->m_start;
		} else
			vp->m_final = vp->m_stop;
	else {
		if (ISMOTION(vp))
			--vp->m_start.cno;
		vp->m_final = vp->m_stop;
	}
	return (0);
}
/*
 * v_ncol -- [count]|
 *	Move to column count or the first column on this line.  If the
 *	requested column is past EOL, move to EOL.  The nasty part is
 *	that we have to know character column widths to make this work.
 *
 * PUBLIC: int v_ncol __P((SCR *, VICMD *));
 */
int
v_ncol(SCR *sp, VICMD *vp)
{
	if (F_ISSET(vp, VC_C1SET) && vp->count > 1) {
		--vp->count;
		vp->m_stop.cno =
		    vs_colpos(sp, vp->m_start.lno, (size_t)vp->count);
		/*
		 * !!!
		 * The | command succeeded if used as a command and the cursor
		 * didn't move, but failed if used as a motion component in the
		 * same situation.
		 */
		if (ISMOTION(vp) && vp->m_stop.cno == vp->m_start.cno) {
			v_nomove(sp);
			return (1);
		}
	} else {
		/*
		 * !!!
		 * The | command succeeded if used as a command in column 0
		 * without a count, but failed if used as a motion component
		 * in the same situation.
		 */
		if (ISMOTION(vp) && vp->m_start.cno == 0) {
			v_sol(sp);
			return (1);
		}
		vp->m_stop.cno = 0;
	}

	/*
	 * If moving right, non-motion commands move to the end of the range.
	 * Delete and yank stay at the start.  Motion commands adjust the
	 * ending point to the character before the current ending charcter.
	 *
	 * If moving left, all commands move to the end of the range.  Motion
	 * commands adjust the starting point to the character before the
	 * current starting character.
	 */
	if (vp->m_start.cno < vp->m_stop.cno)
		if (ISMOTION(vp)) {
			--vp->m_stop.cno;
			vp->m_final = vp->m_start;
		} else
			vp->m_final = vp->m_stop;
	else {
		if (ISMOTION(vp))
			--vp->m_start.cno;
		vp->m_final = vp->m_stop;
	}
	return (0);
}
/*
 * v_left -- [count]^H, [count]h
 *	Move left by columns.
 *
 * PUBLIC: int v_left __P((SCR *, VICMD *));
 */
int
v_left(SCR *sp, VICMD *vp)
{
	db_recno_t cnt;

	/*
	 * !!!
	 * The ^H and h commands always failed in the first column.
	 */
	if (vp->m_start.cno == 0) {
		v_sol(sp);
		return (1);
	}

	/* Find the end of the range. */
	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
	if (vp->m_start.cno > cnt)
		vp->m_stop.cno = vp->m_start.cno - cnt;
	else
		vp->m_stop.cno = 0;

	/*
	 * All commands move to the end of the range.  Motion commands
	 * adjust the starting point to the character before the current
	 * one.
	 */
	if (ISMOTION(vp))
		--vp->m_start.cno;
	vp->m_final = vp->m_stop;
	return (0);
}
Ejemplo n.º 6
0
/*
 * v_right -- [count]' ', [count]l
 *	Move right by columns.
 *
 * PUBLIC: int v_right __P((SCR *, VICMD *));
 */
int
v_right(SCR *sp, VICMD *vp)
{
	size_t len;
	int isempty;

	if (db_eget(sp, vp->m_start.lno, NULL, &len, &isempty)) {
		if (isempty)
			goto eol;
		return (1);
	}

	/* It's always illegal to move right on empty lines. */
	if (len == 0) {
eol:		v_eol(sp, NULL);
		return (1);
	}

	/*
	 * Non-motion commands move to the end of the range.  Delete and
	 * yank stay at the start.  Ignore others.  Adjust the end of the
	 * range for motion commands.
	 *
	 * !!!
	 * Historically, "[cdsy]l" worked at the end of a line.  Also,
	 * EOL is a count sink.
	 */
	vp->m_stop.cno = vp->m_start.cno +
	    (F_ISSET(vp, VC_C1SET) ? vp->count : 1);
	if (vp->m_start.cno == len - 1 && !ISMOTION(vp)) {
		v_eol(sp, NULL);
		return (1);
	}
	if (vp->m_stop.cno >= len) {
		vp->m_stop.cno = len - 1;
		vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
	} else if (ISMOTION(vp)) {
		--vp->m_stop.cno;
		vp->m_final = vp->m_start;
	} else
		vp->m_final = vp->m_stop;
	return (0);
}
Ejemplo n.º 7
0
/*
 * v_search --
 *	The search commands.
 */
static int
v_search(SCR *sp, VICMD *vp, CHAR_T *ptrn, size_t plen, u_int flags, dir_t dir)
{
	/* Display messages. */
	LF_SET(SEARCH_MSG);

	/* If it's a motion search, offset past end-of-line is okay. */
	if (ISMOTION(vp))
		LF_SET(SEARCH_EOL);

	/*
	 * XXX
	 * Warn if the search wraps.  See the comment above, in v_exaddr().
	 */
	if (!KEYS_WAITING(sp))
		LF_SET(SEARCH_WMSG);
		
	switch (dir) {
	case BACKWARD:
		if (b_search(sp,
		    &vp->m_start, &vp->m_stop, ptrn, plen, NULL, flags))
			return (1);
		break;
	case FORWARD:
		if (f_search(sp,
		    &vp->m_start, &vp->m_stop, ptrn, plen, NULL, flags))
			return (1);
		break;
	case NOTSET:
		msgq(sp, M_ERR, "189|No previous search pattern");
		return (1);
	default:
		abort();
	}

	/* Correct motion commands, otherwise, simply move to the location. */
	if (ISMOTION(vp)) {
		if (v_correct(sp, vp, 0))
			return(1);
	} else
		vp->m_final = vp->m_stop;
	return (0);
}
Ejemplo n.º 8
0
/*
 * v_chF -- [count]Fc
 *	Search backward in the line for the next occurrence of the
 *	specified character.
 *
 * PUBLIC: int v_chF __P((SCR *, VICMD *));
 */
int
v_chF(SCR *sp, VICMD *vp)
{
	size_t len;
	u_long cnt;
	int isempty;
	ARG_CHAR_T key;
	CHAR_T *endp, *p;

	/*
	 * !!!
	 * If it's a dot command, it doesn't reset the key for which
	 * we're searching, e.g. in "df1|f2|.|;", the ';' searches
	 * for a '2'.
	 */
	key = vp->character;
	if (!F_ISSET(vp, VC_ISDOT))
		VIP(sp)->lastckey = key;
	VIP(sp)->csearchdir = FSEARCH;

	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
		if (isempty)
			goto empty;
		return (1);
	}

	if (len == 0) {
empty:		notfound(sp, key);
		return (1);
	}

	endp = p - 1;
	p += vp->m_start.cno;
	for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
		while (--p > endp && *p != key);
		if (p == endp) {
			notfound(sp, key);
			return (1);
		}
	}

	vp->m_stop.cno = (p - endp) - 1;

	/*
	 * All commands move to the end of the range.  Motion commands
	 * adjust the starting point to the character before the current
	 * one.
	 */
	vp->m_final = vp->m_stop;
	if (ISMOTION(vp))
		--vp->m_start.cno;
	return (0);
}
Ejemplo n.º 9
0
/*
 * v_down -- [count]^J, [count]^N, [count]j, [count]^M, [count]+
 *	Move down by lines.
 *
 * PUBLIC: int v_down(SCR *, VICMD *);
 */
int
v_down(SCR *sp, VICMD *vp)
{
	recno_t lno;

	lno = vp->m_start.lno + (F_ISSET(vp, VC_C1SET) ? vp->count : 1);
	if (!db_exist(sp, lno)) {
		v_eof(sp, &vp->m_start);
		return (1);
	}
	vp->m_stop.lno = lno;
	vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
	return (0);
}
Ejemplo n.º 10
0
/*
 * v_chf -- [count]fc
 *	Search forward in the line for the next occurrence of the
 *	specified character.
 *
 * PUBLIC: int v_chf __P((SCR *, VICMD *));
 */
int
v_chf(SCR *sp, VICMD *vp)
{
	size_t len;
	u_long cnt;
	int isempty;
	ARG_CHAR_T key;
	CHAR_T *endp, *p, *startp;

	/*
	 * !!!
	 * If it's a dot command, it doesn't reset the key for which we're
	 * searching, e.g. in "df1|f2|.|;", the ';' searches for a '2'.
	 */
	key = vp->character;
	if (!F_ISSET(vp, VC_ISDOT))
		VIP(sp)->lastckey = key;
	VIP(sp)->csearchdir = fSEARCH;

	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
		if (isempty)
			goto empty;
		return (1);
	}

	if (len == 0) {
empty:		notfound(sp, key);
		return (1);
	}

	endp = (startp = p) + len;
	p += vp->m_start.cno;
	for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
		while (++p < endp && *p != key);
		if (p == endp) {
			notfound(sp, key);
			return (1);
		}
	}

	vp->m_stop.cno = p - startp;

	/*
	 * Non-motion commands move to the end of the range.
	 * Delete and yank stay at the start, ignore others.
	 */
	vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
	return (0);
}
/*
 * v_zero -- 0
 *	Move to the first column on this line.
 *
 * PUBLIC: int v_zero __P((SCR *, VICMD *));
 */
int
v_zero(SCR *sp, VICMD *vp)
{
	/*
	 * !!!
	 * The 0 command succeeded if used as a command in the first column
	 * but failed if used as a motion component in the same situation.
	 */
	if (ISMOTION(vp) && vp->m_start.cno == 0) {
		v_sol(sp);
		return (1);
	}

	/*
	 * All commands move to the end of the range.  Motion commands
	 * adjust the starting point to the character before the current
	 * one.
	 */
	vp->m_stop.cno = 0;
	if (ISMOTION(vp))
		--vp->m_start.cno;
	vp->m_final = vp->m_stop;
	return (0);
}
Ejemplo n.º 12
0
static void
goto_adjust(VICMD *vp)
{
	/* Guess that it's the end of the range. */
	vp->m_final = vp->m_stop;

	/*
	 * Non-motion commands move the cursor to the end of the range, and
	 * then to the NEXT nonblank of the line.  Historic vi always moved
	 * to the first nonblank in the line; since the H, M, and L commands
	 * are logical motions in this implementation, we do the next nonblank
	 * so that it looks approximately the same to the user.  To make this
	 * happen, the VM_RCM_SETNNB flag is set in the vcmd.c command table.
	 *
	 * If it's a motion, it's more complicated.  The best possible solution
	 * is probably to display the first nonblank of the line the cursor
	 * will eventually rest on.  This is tricky, particularly given that if
	 * the associated command is a delete, we don't yet know what line that
	 * will be.  So, we clear the VM_RCM_SETNNB flag, and set the first
	 * nonblank flag (VM_RCM_SETFNB).  Note, if the lines are sufficiently
	 * long, this can cause the cursor to warp out of the screen.  It's too
	 * hard to fix.
	 *
	 * XXX
	 * The G command is always first nonblank, so it's okay to reset it.
	 */
	if (ISMOTION(vp)) {
		F_CLR(vp, VM_RCM_MASK);
		F_SET(vp, VM_RCM_SETFNB);
	} else
		return;

	/*
	 * If moving backward in the file, delete and yank move to the end
	 * of the range, unless the line didn't change, in which case yank
	 * doesn't move.  If moving forward in the file, delete and yank
	 * stay at the start of the range.  Ignore others.
	 */
	if (vp->m_stop.lno < vp->m_start.lno ||
	    (vp->m_stop.lno == vp->m_start.lno &&
	    vp->m_stop.cno < vp->m_start.cno)) {
		if (ISCMD(vp->rkp, 'y') && vp->m_stop.lno == vp->m_start.lno)
			vp->m_final = vp->m_start;
	} else
		vp->m_final = vp->m_start;
}
Ejemplo n.º 13
0
/*
 * v_cht -- [count]tc
 *	Search forward in the line for the character before the next
 *	occurrence of the specified character.
 *
 * PUBLIC: int v_cht __P((SCR *, VICMD *));
 */
int
v_cht(SCR *sp, VICMD *vp)
{
	if (v_chf(sp, vp))
		return (1);

	/*
	 * v_chf places the cursor on the character, where the 't'
	 * command wants it to its left.  We know this is safe since
	 * we had to move right for v_chf() to have succeeded.
	 */
	--vp->m_stop.cno;

	/*
	 * Make any necessary correction to the motion decision made
	 * by the v_chf routine.
	 */
	if (!ISMOTION(vp))
		vp->m_final = vp->m_stop;

	VIP(sp)->csearchdir = tSEARCH;
	return (0);
}
Ejemplo n.º 14
0
/*
 * mark --
 *	Mark commands.
 */
static int
mark(SCR *sp, VICMD *vp, int getmark, enum which cmd)
{
	dir_t dir;
	MARK m;
	size_t len;

	if (getmark && mark_get(sp, vp->character, &vp->m_stop, M_BERR))
		return (1);

	/*
	 * !!!
	 * Historically, BQMARKS for character positions that no longer
	 * existed acted as FQMARKS.
	 *
	 * FQMARKS move to the first non-blank.
	 */
	switch (cmd) {
	case BQMARK:
		if (db_get(sp, vp->m_stop.lno, DBG_FATAL, NULL, &len))
			return (1);
		if (vp->m_stop.cno < len ||
		    (vp->m_stop.cno == len && len == 0))
			break;

		if (ISMOTION(vp))
			F_SET(vp, VM_LMODE);
		cmd = FQMARK;
		/* FALLTHROUGH */
	case FQMARK:
		vp->m_stop.cno = 0;
		if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
			return (1);
		break;
	default:
		abort();
	}

	/* Non-motion commands move to the end of the range. */
	if (!ISMOTION(vp)) {
		vp->m_final = vp->m_stop;
		return (0);
	}

	/*
	 * !!!
	 * If a motion component to a BQMARK, the cursor has to move.
	 */
	if (cmd == BQMARK &&
	    vp->m_stop.lno == vp->m_start.lno &&
	    vp->m_stop.cno == vp->m_start.cno) {
		v_nomove(sp);
		return (1);
	}

	/*
	 * If the motion is in the reverse direction, switch the start and
	 * stop MARK's so that it's in a forward direction.  (There's no
	 * reason for this other than to make the tests below easier.  The
	 * code in vi.c:vi() would have done the switch.)  Both forward
	 * and backward motions can happen for any kind of search command.
	 */
	if (vp->m_start.lno > vp->m_stop.lno ||
	    (vp->m_start.lno == vp->m_stop.lno &&
	    vp->m_start.cno > vp->m_stop.cno)) {
		m = vp->m_start;
		vp->m_start = vp->m_stop;
		vp->m_stop = m;
		dir = BACKWARD;
	} else
		dir = FORWARD;

	/*
	 * Yank cursor motion, when associated with marks as motion commands,
	 * historically behaved as follows:
	 *
	 * ` motion			' motion
	 *		Line change?		Line change?
	 *		Y	N		Y	N
	 *	      --------------	      ---------------
	 * FORWARD:  |	NM	NM	      | NM	NM
	 *	     |			      |
	 * BACKWARD: |	M	M	      | M	NM(1)
	 *
	 * where NM means the cursor didn't move, and M means the cursor
	 * moved to the mark.
	 *
	 * As the cursor was usually moved for yank commands associated
	 * with backward motions, this implementation regularizes it by
	 * changing the NM at position (1) to be an M.  This makes mark
	 * motions match search motions, which is probably A Good Thing.
	 *
	 * Delete cursor motion was always to the start of the text region,
	 * regardless.  Ignore other motion commands.
	 */
#ifdef HISTORICAL_PRACTICE
	if (ISCMD(vp->rkp, 'y')) {
		if ((cmd == BQMARK ||
		    (cmd == FQMARK && vp->m_start.lno != vp->m_stop.lno)) &&
		    (vp->m_start.lno > vp->m_stop.lno ||
		    (vp->m_start.lno == vp->m_stop.lno &&
		    vp->m_start.cno > vp->m_stop.cno)))
			vp->m_final = vp->m_stop;
	} else if (ISCMD(vp->rkp, 'd'))
		if (vp->m_start.lno > vp->m_stop.lno ||
		    (vp->m_start.lno == vp->m_stop.lno &&
		    vp->m_start.cno > vp->m_stop.cno))
			vp->m_final = vp->m_stop;
#else
	vp->m_final = vp->m_start;
#endif

	/*
	 * Forward marks are always line oriented, and it's set in the
	 * vcmd.c table.
	 */
	if (cmd == FQMARK)
		return (0);

	/*
	 * BQMARK'S moving backward and starting at column 0, and ones moving
	 * forward and ending at column 0 are corrected to the last column of
	 * the previous line.  Otherwise, adjust the starting/ending point to
	 * the character before the current one (this is safe because we know
	 * the search had to move to succeed).
	 *
	 * Mark motions become line mode opertions if they start at the first
	 * nonblank and end at column 0 of another line.
	 */
	if (vp->m_start.lno < vp->m_stop.lno && vp->m_stop.cno == 0) {
		if (db_get(sp, --vp->m_stop.lno, DBG_FATAL, NULL, &len))
			return (1);
		vp->m_stop.cno = len ? len - 1 : 0;
		len = 0;
		if (nonblank(sp, vp->m_start.lno, &len))
			return (1);
		if (vp->m_start.cno <= len)
			F_SET(vp, VM_LMODE);
	} else
		--vp->m_stop.cno;

	return (0);
}
Ejemplo n.º 15
0
/*
 * eword --
 *	Move forward to the end of the word.
 */
static int
eword(SCR *sp, VICMD *vp, enum which type)
{
	enum { INWORD, NOTWORD } state;
	VCS cs;
	u_long cnt;
	int nmw, omw;

	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
	cs.cs_lno = vp->m_start.lno;
	cs.cs_cno = vp->m_start.cno;
	if (cs_init(sp, &cs))
		return (1);

	/*
	 * !!!
	 * If in whitespace, or the next character is whitespace, move past
	 * it.  (This doesn't count as a word move.)  Stay at the character
	 * past the current one, it sets word "state" for the 'e' command.
	 */
	if (cs.cs_flags == 0 && !ISBLANK2(cs.cs_ch)) {
		if (cs_next(sp, &cs))
			return (1);
		if (cs.cs_flags == 0 && !ISBLANK2(cs.cs_ch))
			goto start;
	}
	if (cs_fblank(sp, &cs))
		return (1);

	/*
	 * Cyclically move to the next word -- this involves skipping
	 * over word characters and then any trailing non-word characters.
	 * Note, for the 'e' command, the definition of a word keeps
	 * switching.
	 */
start:	if (type == BIGWORD)
		while (cnt--) {
			nmw = ISMULTIWIDTH(sp, cs.cs_ch);
			for (;;) {
				omw = nmw;
				if (cs_next(sp, &cs))
					return (1);
				if (cs.cs_flags == CS_EOF)
					goto ret;
				if (cs.cs_flags != 0 || ISBLANK2(cs.cs_ch) ||
				    (nmw = ISMULTIWIDTH(sp, cs.cs_ch)) != omw)
					break;
			}
			/*
			 * When we reach the start of the word after the last
			 * word, we're done.  If we changed state, back up one
			 * to the end of the previous word.
			 */
			if (cnt == 0) {
				if (cs.cs_flags == 0 && cs_prev(sp, &cs))
					return (1);
				break;
			}

			/* Eat whitespace characters. */
			if (nmw == omw && cs_fblank(sp, &cs))
				return (1);
			if (cs.cs_flags == CS_EOF)
				goto ret;
		}
	else
		while (cnt--) {
			state = cs.cs_flags == 0 &&
			    inword(cs.cs_ch) ? INWORD : NOTWORD;
			nmw = ISMULTIWIDTH(sp, cs.cs_ch);
			for (;;) {
				omw = nmw;
				if (cs_next(sp, &cs))
					return (1);
				if (cs.cs_flags == CS_EOF)
					goto ret;
				if (cs.cs_flags != 0 || ISBLANK2(cs.cs_ch) ||
				    (nmw = ISMULTIWIDTH(sp, cs.cs_ch)) != omw)
					break;
				if (state == INWORD) {
					if (!inword(cs.cs_ch))
						break;
				} else
					if (inword(cs.cs_ch))
						break;
			}
			/* See comment above. */
			if (cnt == 0) {
				if (cs.cs_flags == 0 && cs_prev(sp, &cs))
					return (1);
				break;
			}

			/* Eat whitespace characters. */
			if (cs.cs_flags != 0 || ISBLANK2(cs.cs_ch))
				if (cs_fblank(sp, &cs))
					return (1);
			if (cs.cs_flags == CS_EOF)
				goto ret;
		}

	/*
	 * If we didn't move, we must be at EOF.
	 *
	 * !!!
	 * That's okay for motion commands, however.
	 */
ret:	if (!ISMOTION(vp) &&
	    cs.cs_lno == vp->m_start.lno && cs.cs_cno == vp->m_start.cno) {
		v_eof(sp, &vp->m_start);
		return (1);
	}

	/* Set the end of the range for motion commands. */
	vp->m_stop.lno = cs.cs_lno;
	vp->m_stop.cno = cs.cs_cno;

	/*
	 * Non-motion commands move to the end of the range.
	 * Delete and yank stay at the start, ignore others.
	 */
	vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
	return (0);
}
Ejemplo n.º 16
0
/*
 * bword --
 *	Move backward by words.
 */
static int
bword(SCR *sp, VICMD *vp, enum which type)
{
	enum { INWORD, NOTWORD } state;
	VCS cs;
	u_long cnt;
	int nmw, omw;

	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
	cs.cs_lno = vp->m_start.lno;
	cs.cs_cno = vp->m_start.cno;
	if (cs_init(sp, &cs))
		return (1);

	/*
	 * !!!
	 * If in whitespace, or the previous character is whitespace, move
	 * past it.  (This doesn't count as a word move.)  Stay at the
	 * character before the current one, it sets word "state" for the
	 * 'b' command.
	 */
	if (cs.cs_flags == 0 && !ISBLANK2(cs.cs_ch)) {
		if (cs_prev(sp, &cs))
			return (1);
		if (cs.cs_flags == 0 && !ISBLANK2(cs.cs_ch))
			goto start;
	}
	if (cs_bblank(sp, &cs))
		return (1);

	/*
	 * Cyclically move to the beginning of the previous word -- this
	 * involves skipping over word characters and then any trailing
	 * non-word characters.  Note, for the 'b' command, the definition
	 * of a word keeps switching.
	 */
start:	if (type == BIGWORD)
		while (cnt--) {
			nmw = ISMULTIWIDTH(sp, cs.cs_ch);
			for (;;) {
				omw = nmw;
				if (cs_prev(sp, &cs))
					return (1);
				if (cs.cs_flags == CS_SOF)
					goto ret;
				if (cs.cs_flags != 0 || ISBLANK2(cs.cs_ch) ||
				    (nmw = ISMULTIWIDTH(sp, cs.cs_ch)) != omw)
					break;
			}
			/*
			 * When we reach the end of the word before the last
			 * word, we're done.  If we changed state, move forward
			 * one to the end of the next word.
			 */
			if (cnt == 0) {
				if (cs.cs_flags == 0 && cs_next(sp, &cs))
					return (1);
				break;
			}

			/* Eat whitespace characters. */
			if (nmw == omw && cs_bblank(sp, &cs))
				return (1);
			if (cs.cs_flags == CS_SOF)
				goto ret;
		}
	else
		while (cnt--) {
			state = cs.cs_flags == 0 &&
			    inword(cs.cs_ch) ? INWORD : NOTWORD;
			nmw = ISMULTIWIDTH(sp, cs.cs_ch);
			for (;;) {
				omw = nmw;
				if (cs_prev(sp, &cs))
					return (1);
				if (cs.cs_flags == CS_SOF)
					goto ret;
				if (cs.cs_flags != 0 || ISBLANK2(cs.cs_ch) ||
				    (nmw = ISMULTIWIDTH(sp, cs.cs_ch)) != omw)
					break;
				if (state == INWORD) {
					if (!inword(cs.cs_ch))
						break;
				} else
					if (inword(cs.cs_ch))
						break;
			}
			/* See comment above. */
			if (cnt == 0) {
				if (cs.cs_flags == 0 && cs_next(sp, &cs))
					return (1);
				break;
			}

			/* Eat whitespace characters. */
			if (cs.cs_flags != 0 || ISBLANK2(cs.cs_ch))
				if (cs_bblank(sp, &cs))
					return (1);
			if (cs.cs_flags == CS_SOF)
				goto ret;
		}

	/* If we didn't move, we must be at SOF. */
ret:	if (cs.cs_lno == vp->m_start.lno && cs.cs_cno == vp->m_start.cno) {
		v_sof(sp, &vp->m_start);
		return (1);
	}

	/* Set the end of the range for motion commands. */
	vp->m_stop.lno = cs.cs_lno;
	vp->m_stop.cno = cs.cs_cno;

	/*
	 * All commands move to the end of the range.  Motion commands
	 * adjust the starting point to the character before the current
	 * one.
	 *
	 * !!!
	 * The historic vi didn't get this right -- the `yb' command yanked
	 * the right stuff and even updated the cursor value, but the cursor
	 * was not actually updated on the screen.
	 */
	vp->m_final = vp->m_stop;
	if (ISMOTION(vp))
		--vp->m_start.cno;
	return (0);
}
Ejemplo n.º 17
0
/*
 * v_sectionf -- [count]]]
 *	Move forward count sections/functions.
 *
 * !!!
 * Using ]] as a motion command was a bit special, historically.  It could
 * match } as well as the usual { and section values.  If it matched a { or
 * a section, it did NOT include the matched line.  If it matched a }, it
 * did include the line.  No clue why.
 *
 * PUBLIC: int v_sectionf(SCR *, VICMD *);
 */
int
v_sectionf(SCR *sp, VICMD *vp)
{
	recno_t cnt, lno;
	size_t len;
	CHAR_T *p;
	char *list, *lp;

	/* Get the macro list. */
	if ((list = O_STR(sp, O_SECTIONS)) == NULL)
		return (1);

	/*
	 * !!!
	 * If the starting cursor position is at or before any non-blank
	 * characters in the line, i.e. the movement is cutting all of the
	 * line's text, the buffer is in line mode.  It's a lot easier to
	 * check here, because we know that the end is going to be the start
	 * or end of a line.
	 */
	if (ISMOTION(vp))
		if (vp->m_start.cno == 0)
			F_SET(vp, VM_LMODE);
		else {
			vp->m_stop = vp->m_start;
			vp->m_stop.cno = 0;
			if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
				return (1);
			if (vp->m_start.cno <= vp->m_stop.cno)
				F_SET(vp, VM_LMODE);
		}

	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
	for (lno = vp->m_start.lno; !db_get(sp, ++lno, 0, &p, &len);) {
		if (len == 0)
			continue;
		if (p[0] == '{' || (ISMOTION(vp) && p[0] == '}')) {
			if (!--cnt) {
				if (p[0] == '{')
					goto adjust1;
				goto adjust2;
			}
			continue;
		}
		/*
		 * !!!
		 * Historic documentation (USD:15-11, 4.2) said that formfeed
		 * characters (^L) in the first column delimited sections.
		 * The historic code mentions formfeed characters, but never
		 * implements them.  Seems reasonable, do it.
		 */
		if (p[0] == '\014') {
			if (!--cnt)
				goto adjust1;
			continue;
		}
		if (p[0] != '.' || len < 2)
			continue;
		for (lp = list; *lp != '\0'; lp += 2 * sizeof(*lp))
			if (lp[0] == p[1] &&
			    ((lp[1] == ' ' && len == 2) || lp[1] == p[2]) &&
			    !--cnt) {
				/*
				 * !!!
				 * If not cutting this line, adjust to the end
				 * of the previous one.  Otherwise, position to
				 * column 0.
				 */
adjust1:			if (ISMOTION(vp))
					goto ret1;

adjust2:			vp->m_stop.lno = lno;
				vp->m_stop.cno = 0;
				goto ret2;
			}
	}

	/* If moving forward, reached EOF, check to see if we started there. */
	if (vp->m_start.lno == lno - 1) {
		v_eof(sp, NULL);
		return (1);
	}

ret1:	if (db_get(sp, --lno, DBG_FATAL, NULL, &len))
		return (1);
	vp->m_stop.lno = lno;
	vp->m_stop.cno = len ? len - 1 : 0;

	/*
	 * Non-motion commands go to the end of the range.  Delete and
	 * yank stay at the start of the range.  Ignore others.
	 */
ret2:	if (ISMOTION(vp)) {
		vp->m_final = vp->m_start;
		if (F_ISSET(vp, VM_LMODE))
			vp->m_final.cno = 0;
	} else
		vp->m_final = vp->m_stop;
	return (0);
}
Ejemplo n.º 18
0
/*
 * fword --
 *	Move forward by words.
 */
static int
fword(SCR *sp, VICMD *vp, enum which type)
{
	enum { INWORD, NOTWORD } state;
	VCS cs;
	u_long cnt;
	int nmw, omw;

	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
	cs.cs_lno = vp->m_start.lno;
	cs.cs_cno = vp->m_start.cno;
	if (cs_init(sp, &cs))
		return (1);

	/*
	 * If in white-space:
	 *	If the count is 1, and it's a change command, we're done.
	 *	Else, move to the first non-white-space character, which
	 *	counts as a single word move.  If it's a motion command,
	 *	don't move off the end of the line.
	 */
	if (cs.cs_flags == CS_EMP || (cs.cs_flags == 0 && ISBLANK2(cs.cs_ch))) {
		if (ISMOTION(vp) && cs.cs_flags != CS_EMP && cnt == 1) {
			if (ISCMD(vp->rkp, 'c'))
				return (0);
			if (ISCMD(vp->rkp, 'd') || ISCMD(vp->rkp, 'y')) {
				if (cs_fspace(sp, &cs))
					return (1);
				goto ret;
			}
		}
		if (cs_fblank(sp, &cs))
			return (1);
		--cnt;
	}

	/*
	 * Cyclically move to the next word -- this involves skipping
	 * over word characters and then any trailing non-word characters.
	 * Note, for the 'w' command, the definition of a word keeps
	 * switching.
	 */
	if (type == BIGWORD)
		while (cnt--) {
			nmw = ISMULTIWIDTH(sp, cs.cs_ch);
			for (;;) {
				omw = nmw;
				if (cs_next(sp, &cs))
					return (1);
				if (cs.cs_flags == CS_EOF)
					goto ret;
				if (cs.cs_flags != 0 || ISBLANK2(cs.cs_ch) ||
				    (nmw = ISMULTIWIDTH(sp, cs.cs_ch)) != omw)
					break;
			}
			/*
			 * If a motion command and we're at the end of the
			 * last word, we're done.  Delete and yank eat any
			 * trailing blanks, but we don't move off the end
			 * of the line regardless.
			 */
			if (cnt == 0 && ISMOTION(vp)) {
				if ((ISCMD(vp->rkp, 'd') ||
				    ISCMD(vp->rkp, 'y')) &&
				    cs_fspace(sp, &cs))
					return (1);
				break;
			}

			/* Eat whitespace characters. */
			if (nmw == omw && cs_fblank(sp, &cs))
				return (1);
			if (cs.cs_flags == CS_EOF)
				goto ret;
		}
	else
		while (cnt--) {
			state = cs.cs_flags == 0 &&
			    inword(cs.cs_ch) ? INWORD : NOTWORD;
			nmw = ISMULTIWIDTH(sp, cs.cs_ch);
			for (;;) {
				omw = nmw;
				if (cs_next(sp, &cs))
					return (1);
				if (cs.cs_flags == CS_EOF)
					goto ret;
				if (cs.cs_flags != 0 || ISBLANK2(cs.cs_ch) ||
				    (nmw = ISMULTIWIDTH(sp, cs.cs_ch)) != omw)
					break;
				if (state == INWORD) {
					if (!inword(cs.cs_ch))
						break;
				} else
					if (inword(cs.cs_ch))
						break;
			}
			/* See comment above. */
			if (cnt == 0 && ISMOTION(vp)) {
				if ((ISCMD(vp->rkp, 'd') ||
				    ISCMD(vp->rkp, 'y')) &&
				    cs_fspace(sp, &cs))
					return (1);
				break;
			}

			/* Eat whitespace characters. */
			if (cs.cs_flags != 0 || ISBLANK2(cs.cs_ch))
				if (cs_fblank(sp, &cs))
					return (1);
			if (cs.cs_flags == CS_EOF)
				goto ret;
		}

	/*
	 * If we didn't move, we must be at EOF.
	 *
	 * !!!
	 * That's okay for motion commands, however.
	 */
ret:	if (!ISMOTION(vp) &&
	    cs.cs_lno == vp->m_start.lno && cs.cs_cno == vp->m_start.cno) {
		v_eof(sp, &vp->m_start);
		return (1);
	}

	/* Adjust the end of the range for motion commands. */
	vp->m_stop.lno = cs.cs_lno;
	vp->m_stop.cno = cs.cs_cno;
	if (ISMOTION(vp) && cs.cs_flags == 0)
		--vp->m_stop.cno;

	/*
	 * Non-motion commands move to the end of the range.  Delete
	 * and yank stay at the start, ignore others.
	 */
	vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
	return (0);
}
/*
 * v_match -- %
 *	Search to matching character.
 *
 * PUBLIC: int v_match __P((SCR *, VICMD *));
 */
int
v_match(SCR *sp, VICMD *vp)
{
	VCS cs;
	MARK *mp;
	size_t cno, len, off;
	int cnt, isempty, matchc, startc, (*gc)__P((SCR *, VCS *));
	CHAR_T *p;
	char *cp;
	const char *match_chars;

	static MARK match = { 0, 0 };
	static int match_dir;

	/*
	 * Historically vi would match (), {} and [] however
	 * an update included <>.  This is ok for editing HTML
	 * but a pain in the butt for C source.
	 * Making it an option lets the user decide what is 'right'.
	 * Also fixed to do something sensible with "".
	 */
	match_chars = O_STR(sp, O_MATCHCHARS);

	/*
	 * !!!
	 * Historic practice; ignore the count.
	 *
	 * !!!
	 * Historical practice was to search for the initial character in the
	 * forward direction only.
	 */
	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
		if (isempty)
			goto nomatch;
		return (1);
	}
	for (off = vp->m_start.cno;; ++off) {
		if (off >= len) {
nomatch:		msgq(sp, M_BERR, "184|No match character on this line");
			return (1);
		}
		startc = p[off];
		cp = strchr(match_chars, startc);
		if (cp != NULL)
			break;
	}
	cnt = cp - match_chars;
	matchc = match_chars[cnt ^ 1];

	/* Alternate back-forward search if startc and matchc the same */
	if (startc == matchc) {
		/* are we continuing from where last match finished? */
		if (match.lno == vp->m_start.lno && match.cno ==vp->m_start.cno)
			/* yes - continue in sequence */
			match_dir++;
		else
			/* no - go forward, back, back, forward */
			match_dir = 1;
		if (match_dir & 2)
			cnt++;
	}
	gc = cnt & 1 ? cs_prev : cs_next;

	cs.cs_lno = vp->m_start.lno;
	cs.cs_cno = off;
	if (cs_init(sp, &cs))
		return (1);
	for (cnt = 1;;) {
		if (gc(sp, &cs))
			return (1);
		if (cs.cs_flags != 0) {
			if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF)
				break;
			continue;
		}
		if (cs.cs_ch == matchc && --cnt == 0)
			break;
		if (cs.cs_ch == startc)
			++cnt;
	}
	if (cnt) {
		msgq(sp, M_BERR, "185|Matching character not found");
		return (1);
	}

	vp->m_stop.lno = cs.cs_lno;
	vp->m_stop.cno = cs.cs_cno;

	/*
	 * If moving right, non-motion commands move to the end of the range.
	 * Delete and yank stay at the start.
	 *
	 * If moving left, all commands move to the end of the range.
	 *
	 * !!!
	 * Don't correct for leftward movement -- historic vi deleted the
	 * starting cursor position when deleting to a match.
	 */
	if (vp->m_start.lno < vp->m_stop.lno ||
	    (vp->m_start.lno == vp->m_stop.lno &&
	    vp->m_start.cno < vp->m_stop.cno))
		vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
	else
		vp->m_final = vp->m_stop;

	match.lno = vp->m_final.lno;
	match.cno = vp->m_final.cno;

	/*
	 * !!!
	 * If the motion is across lines, and the earliest cursor position
	 * is at or before any non-blank characters in the line, i.e. the
	 * movement is cutting all of the line's text, and the later cursor
	 * position has nothing other than whitespace characters between it
	 * and the end of its line, the buffer is in line mode.
	 */
	if (!ISMOTION(vp) || vp->m_start.lno == vp->m_stop.lno)
		return (0);
	mp = vp->m_start.lno < vp->m_stop.lno ? &vp->m_start : &vp->m_stop;
	if (mp->cno != 0) {
		cno = 0;
		if (nonblank(sp, mp->lno, &cno))
			return (1);
		if (cno < mp->cno)
			return (0);
	}
	mp = vp->m_start.lno < vp->m_stop.lno ? &vp->m_stop : &vp->m_start;
	if (db_get(sp, mp->lno, DBG_FATAL, &p, &len))
		return (1);
	for (p += mp->cno + 1, len -= mp->cno; --len; ++p)
		if (!ISBLANK((UCHAR_T)*p))
			return (0);
	F_SET(vp, VM_LMODE);
	return (0);
}
Ejemplo n.º 20
0
/*
 * v_exaddr --
 *	Do a vi search (which is really an ex address).
 */
static int
v_exaddr(SCR *sp, VICMD *vp, dir_t dir)
{
	static EXCMDLIST fake = { L("search") };
	EXCMD *cmdp;
	GS *gp;
	TEXT *tp;
	recno_t s_lno;
	size_t len, s_cno, tlen;
	int err, nb, type;
	char buf[20];
	CHAR_T *cmd, *t;
	CHAR_T *w;
	size_t wlen;

	/*
	 * !!!
	 * If using the search command as a motion, any addressing components
	 * are lost, i.e. y/ptrn/+2, when repeated, is the same as y/ptrn/.
	 */
	if (F_ISSET(vp, VC_ISDOT))
		return (v_search(sp, vp,
		    NULL, 0, SEARCH_PARSE | SEARCH_MSG | SEARCH_SET, dir));

	/* Get the search pattern. */
	if (v_tcmd(sp, vp, dir == BACKWARD ? CH_BSEARCH : CH_FSEARCH,
	    TXT_BS | TXT_CR | TXT_ESCAPE | TXT_PROMPT |
	    (O_ISSET(sp, O_SEARCHINCR) ? TXT_SEARCHINCR : 0)))
		return (1);

	tp = TAILQ_FIRST(sp->tiq);

	/* If the user backspaced over the prompt, do nothing. */
	if (tp->term == TERM_BS)
		return (1);

	/*
	 * If the user was doing an incremental search, then we've already
	 * updated the cursor and moved to the right location.  Return the
	 * correct values, we're done.
	 */
	if (tp->term == TERM_SEARCH) {
		vp->m_stop.lno = sp->lno;
		vp->m_stop.cno = sp->cno;
		if (ISMOTION(vp))
			return (v_correct(sp, vp, 0));
		vp->m_final = vp->m_stop;
		return (0);
	}

	/*
	 * If the user entered <escape> or <carriage-return>, the length is
	 * 1 and the right thing will happen, i.e. the prompt will be used
	 * as a command character.
	 *
	 * Build a fake ex command structure.
	 */
	gp = sp->gp;
	gp->excmd.cp = tp->lb;
	gp->excmd.clen = tp->len;
	F_INIT(&gp->excmd, E_VISEARCH);

	/*
	 * XXX
	 * Warn if the search wraps.  This is a pretty special case, but it's
	 * nice feature that wasn't in the original implementations of ex/vi.
	 * (It was added at some point to System V's version.)  This message
	 * is only displayed if there are no keys in the queue. The problem is
	 * the command is going to succeed, and the message is informational,
	 * not an error.  If a macro displays it repeatedly, e.g., the pattern
	 * only occurs once in the file and wrapscan is set, you lose big.  For
	 * example, if the macro does something like:
	 *
	 *	:map K /pattern/^MjK
	 *
	 * Each search will display the message, but the following "/pattern/"
	 * will immediately overwrite it, with strange results.  The System V
	 * vi displays the "wrapped" message multiple times, but because it's
	 * overwritten each time, it's not as noticeable.  As we don't discard
	 * messages, it's a real problem for us.
	 */
	if (!KEYS_WAITING(sp))
		F_SET(&gp->excmd, E_SEARCH_WMSG);
		
	/* Save the current line/column. */
	s_lno = sp->lno;
	s_cno = sp->cno;

	/*
	 * !!!
	 * Historically, vi / and ? commands were full-blown ex addresses,
	 * including ';' delimiters, trailing <blank>'s, multiple search
	 * strings (separated by semi-colons) and, finally, full-blown z
	 * commands after the / and ? search strings.  (If the search was
	 * being used as a motion, the trailing z command was ignored.
	 * Also, we do some argument checking on the z command, to be sure
	 * that it's not some other random command.) For multiple search
	 * strings, leading <blank>'s at the second and subsequent strings
	 * were eaten as well.  This has some (unintended?) side-effects:
	 * the command /ptrn/;3 is legal and results in moving to line 3.
	 * I suppose you could use it to optionally move to line 3...
	 *
	 * !!!
	 * Historically, if any part of the search command failed, the cursor
	 * remained unmodified (even if ; was used).  We have to play games
	 * because the underlying ex parser thinks we're modifying the cursor
	 * as we go, but I think we're compatible with historic practice.
	 *
	 * !!!
	 * Historically, the command "/STRING/;   " failed, apparently it
	 * confused the parser.  We're not that compatible.
	 */
	cmdp = &gp->excmd;
	if (ex_range(sp, cmdp, &err))
		return (1);
	
	/*
	 * Remember where any remaining command information is, and clean
	 * up the fake ex command.
	 */
	cmd = cmdp->cp;
	len = cmdp->clen;
	gp->excmd.clen = 0;

	if (err)
		goto err2;

	/* Copy out the new cursor position and make sure it's okay. */
	switch (cmdp->addrcnt) {
	case 1:
		vp->m_stop = cmdp->addr1;
		break;
	case 2:
		vp->m_stop = cmdp->addr2;
		break;
	}
	if (!db_exist(sp, vp->m_stop.lno)) {
		ex_badaddr(sp, &fake,
		    vp->m_stop.lno == 0 ? A_ZERO : A_EOF, NUM_OK);
		goto err2;
	}

	/*
	 * !!!
	 * Historic practice is that a trailing 'z' was ignored if it was a
	 * motion command.  Should probably be an error, but not worth the
	 * effort.
	 */
	if (ISMOTION(vp))
		return (v_correct(sp, vp, F_ISSET(cmdp, E_DELTA)));
		
	/*
	 * !!!
	 * Historically, if it wasn't a motion command, a delta in the search
	 * pattern turns it into a first nonblank movement.
	 */
	nb = F_ISSET(cmdp, E_DELTA);

	/* Check for the 'z' command. */
	if (len != 0) {
		if (*cmd != 'z')
			goto err1;

		/* No blanks, just like the z command. */
		for (t = cmd + 1, tlen = len - 1; tlen > 0; ++t, --tlen)
			if (!isdigit(*t))
				break;
		if (tlen &&
		    (*t == '-' || *t == '.' || *t == '+' || *t == '^')) {
			++t;
			--tlen;
			type = 1;
		} else
			type = 0;
		if (tlen)
			goto err1;

		/* The z command will do the nonblank for us. */
		nb = 0;

		/* Default to z+. */
		if (!type &&
		    v_event_push(sp, NULL, L("+"), 1, CH_NOMAP | CH_QUOTED))
			return (1);

		/* Push the user's command. */
		if (v_event_push(sp, NULL, cmd, len, CH_NOMAP | CH_QUOTED))
			return (1);

		/* Push line number so get correct z display. */
		tlen = snprintf(buf,
		    sizeof(buf), "%lu", (u_long)vp->m_stop.lno);
		CHAR2INT(sp, buf, tlen, w, wlen);
		if (v_event_push(sp, NULL, w, wlen, CH_NOMAP | CH_QUOTED))
			return (1);
		 
		/* Don't refresh until after 'z' happens. */
		F_SET(VIP(sp), VIP_S_REFRESH);
	}

	/* Non-motion commands move to the end of the range. */
	vp->m_final = vp->m_stop;
	if (nb) {
		F_CLR(vp, VM_RCM_MASK);
		F_SET(vp, VM_RCM_SETFNB);
	}
	return (0);

err1:	msgq(sp, M_ERR,
	    "188|Characters after search string, line offset and/or z command");
err2:	vp->m_final.lno = s_lno;
	vp->m_final.cno = s_cno;
	return (1);
}
Ejemplo n.º 21
0
/*
 * v_match -- %
 *	Search to matching character.
 *
 * PUBLIC: int v_match(SCR *, VICMD *);
 */
int
v_match(SCR *sp, VICMD *vp)
{
	VCS cs;
	MARK *mp;
	size_t cno, len, off;
	int cnt, isempty, matchc, startc, (*gc)(SCR *, VCS *);
	CHAR_T *p;
	CHAR_T *cp;
	const CHAR_T *match_chars;

	/*
	 * Historically vi would match (), {} and [] however
	 * an update included <>.  This is ok for editing HTML
	 * but a pain in the butt for C source.
	 * Making it an option lets the user decide what is 'right'.
	 */
	match_chars = VIP(sp)->mcs;

	/*
	 * !!!
	 * Historic practice; ignore the count.
	 *
	 * !!!
	 * Historical practice was to search for the initial character in the
	 * forward direction only.
	 */
	if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
		if (isempty)
			goto nomatch;
		return (1);
	}
	for (off = vp->m_start.cno;; ++off) {
		if (off >= len) {
nomatch:		msgq(sp, M_BERR, "184|No match character on this line");
			return (1);
		}
		startc = p[off];
		cp = STRCHR(match_chars, startc);
		if (cp != NULL) {
			cnt = cp - match_chars;
			matchc = match_chars[cnt ^ 1];
			gc = cnt & 1 ? cs_prev : cs_next;
			break;
		}
	}

	cs.cs_lno = vp->m_start.lno;
	cs.cs_cno = off;
	if (cs_init(sp, &cs))
		return (1);
	for (cnt = 1;;) {
		if (gc(sp, &cs))
			return (1);
		if (cs.cs_flags != 0) {
			if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF)
				break;
			continue;
		}
		if (cs.cs_ch == startc)
			++cnt;
		else if (cs.cs_ch == matchc && --cnt == 0)
			break;
	}
	if (cnt) {
		msgq(sp, M_BERR, "185|Matching character not found");
		return (1);
	}

	vp->m_stop.lno = cs.cs_lno;
	vp->m_stop.cno = cs.cs_cno;

	/*
	 * If moving right, non-motion commands move to the end of the range.
	 * Delete and yank stay at the start.
	 *
	 * If moving left, all commands move to the end of the range.
	 *
	 * !!!
	 * Don't correct for leftward movement -- historic vi deleted the
	 * starting cursor position when deleting to a match.
	 */
	if (vp->m_start.lno < vp->m_stop.lno ||
	    (vp->m_start.lno == vp->m_stop.lno &&
	    vp->m_start.cno < vp->m_stop.cno))
		vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
	else
		vp->m_final = vp->m_stop;

	/*
	 * !!!
	 * If the motion is across lines, and the earliest cursor position
	 * is at or before any non-blank characters in the line, i.e. the
	 * movement is cutting all of the line's text, and the later cursor
	 * position has nothing other than whitespace characters between it
	 * and the end of its line, the buffer is in line mode.
	 */
	if (!ISMOTION(vp) || vp->m_start.lno == vp->m_stop.lno)
		return (0);
	mp = vp->m_start.lno < vp->m_stop.lno ? &vp->m_start : &vp->m_stop;
	if (mp->cno != 0) {
		cno = 0;
		if (nonblank(sp, mp->lno, &cno))
			return (1);
		if (cno < mp->cno)
			return (0);
	}
	mp = vp->m_start.lno < vp->m_stop.lno ? &vp->m_stop : &vp->m_start;
	if (db_get(sp, mp->lno, DBG_FATAL, &p, &len))
		return (1);
	for (p += mp->cno + 1, len -= mp->cno; --len; ++p)
		if (!isblank(*p))
			return (0);
	F_SET(vp, VM_LMODE);
	return (0);
}
Ejemplo n.º 22
0
/*
 * v_sentenceb -- [count](
 *	Move backward count sentences.
 *
 * PUBLIC: int v_sentenceb __P((SCR *, VICMD *));
 */
int
v_sentenceb(SCR *sp, VICMD *vp)
{
	VCS cs;
	db_recno_t slno;
	size_t len, scno;
	u_long cnt;
	int last;

	/*
	 * !!!
	 * Historic vi permitted the user to hit SOF repeatedly.
	 */
	if (vp->m_start.lno == 1 && vp->m_start.cno == 0)
		return (0);

	cs.cs_lno = vp->m_start.lno;
	cs.cs_cno = vp->m_start.cno;
	if (cs_init(sp, &cs))
		return (1);

	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;

	/*
	 * !!!
	 * In empty lines, skip to the previous non-white-space character.
	 * If in text, skip to the prevous white-space character.  Believe
	 * it or not, in the paragraph:
	 *	ab cd.
	 *	AB CD.
	 * if the cursor is on the 'A' or 'B', ( moves to the 'a'.  If it
	 * is on the ' ', 'C' or 'D', it moves to the 'A'.  Yes, Virginia,
	 * Berkeley was once a major center of drug activity.
	 */
	if (cs.cs_flags == CS_EMP) {
		if (cs_bblank(sp, &cs))
			return (1);
		for (;;) {
			if (cs_prev(sp, &cs))
				return (1);
			if (cs.cs_flags != CS_EOL)
				break;
		}
	} else if (cs.cs_flags == 0 && !isblank(cs.cs_ch))
		for (;;) {
			if (cs_prev(sp, &cs))
				return (1);
			if (cs.cs_flags != 0 || isblank(cs.cs_ch))
				break;
		}

	for (last = 0;;) {
		if (cs_prev(sp, &cs))
			return (1);
		if (cs.cs_flags == CS_SOF)	/* SOF is a movement sink. */
			break;
		if (cs.cs_flags == CS_EOL) {
			last = 1;
			continue;
		}
		if (cs.cs_flags == CS_EMP) {
			if (--cnt == 0)
				goto ret;
			if (cs_bblank(sp, &cs))
				return (1);
			last = 0;
			continue;
		}
		switch (cs.cs_ch) {
		case '.':
		case '?':
		case '!':
			if (!last || --cnt != 0) {
				last = 0;
				continue;
			}

ret:			slno = cs.cs_lno;
			scno = cs.cs_cno;

			/*
			 * Move to the start of the sentence, skipping blanks
			 * and special characters.
			 */
			do {
				if (cs_next(sp, &cs))
					return (1);
			} while (!cs.cs_flags &&
			    (cs.cs_ch == ')' || cs.cs_ch == ']' ||
			    cs.cs_ch == '"' || cs.cs_ch == '\''));
			if ((cs.cs_flags || isblank(cs.cs_ch)) &&
			    cs_fblank(sp, &cs))
				return (1);

			/*
			 * If it was ".  xyz", with the cursor on the 'x', or
			 * "end.  ", with the cursor in the spaces, or the
			 * beginning of a sentence preceded by an empty line,
			 * we can end up where we started.  Fix it.
			 */
			if (vp->m_start.lno != cs.cs_lno ||
			    vp->m_start.cno != cs.cs_cno)
				goto okret;

			/*
			 * Well, if an empty line preceded possible blanks
			 * and the sentence, it could be a real sentence.
			 */
			for (;;) {
				if (cs_prev(sp, &cs))
					return (1);
				if (cs.cs_flags == CS_EOL)
					continue;
				if (cs.cs_flags == 0 && isblank(cs.cs_ch))
					continue;
				break;
			}
			if (cs.cs_flags == CS_EMP)
				goto okret;

			/* But it wasn't; try again. */
			++cnt;
			cs.cs_lno = slno;
			cs.cs_cno = scno;
			last = 0;
			break;
		case '\t':
			last = 1;
			break;
		default:
			last =
			    cs.cs_flags == CS_EOL || isblank(cs.cs_ch) ||
			    cs.cs_ch == ')' || cs.cs_ch == ']' ||
			    cs.cs_ch == '"' || cs.cs_ch == '\'' ? 1 : 0;
		}
	}

okret:	vp->m_stop.lno = cs.cs_lno;
	vp->m_stop.cno = cs.cs_cno;

	/*
	 * !!!
	 * If the starting and stopping cursor positions are at the first
	 * columns in the line, i.e. the movement is cutting an entire line,
	 * the buffer is in line mode, and the starting position is the last
	 * character of the previous line.
	 *
	 * All commands move to the end of the range.  Adjust the start of
	 * the range for motion commands.
	 */
	if (ISMOTION(vp))
		if (vp->m_start.cno == 0 &&
		    (cs.cs_flags != 0 || vp->m_stop.cno == 0)) {
			if (db_get(sp,
			    --vp->m_start.lno, DBG_FATAL, NULL, &len))
				return (1);
			vp->m_start.cno = len ? len - 1 : 0;
			F_SET(vp, VM_LMODE);
		} else
			--vp->m_start.cno;
	vp->m_final = vp->m_stop;
	return (0);
}
Ejemplo n.º 23
0
/*
 * v_sentencef -- [count])
 *	Move forward count sentences.
 *
 * PUBLIC: int v_sentencef __P((SCR *, VICMD *));
 */
int
v_sentencef(SCR *sp, VICMD *vp)
{
	enum { BLANK, NONE, PERIOD } state;
	VCS cs;
	size_t len;
	u_long cnt;

	cs.cs_lno = vp->m_start.lno;
	cs.cs_cno = vp->m_start.cno;
	if (cs_init(sp, &cs))
		return (1);

	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;

	/*
	 * !!!
	 * If in white-space, the next start of sentence counts as one.
	 * This may not handle "  .  " correctly, but it's real unclear
	 * what correctly means in that case.
	 */
	if (cs.cs_flags == CS_EMP || cs.cs_flags == 0 && isblank(cs.cs_ch)) {
		if (cs_fblank(sp, &cs))
			return (1);
		if (--cnt == 0) {
			if (vp->m_start.lno != cs.cs_lno ||
			    vp->m_start.cno != cs.cs_cno)
				goto okret;
			return (1);
		}
	}

	for (state = NONE;;) {
		if (cs_next(sp, &cs))
			return (1);
		if (cs.cs_flags == CS_EOF)
			break;
		if (cs.cs_flags == CS_EOL) {
			if ((state == PERIOD || state == BLANK) && --cnt == 0) {
				if (cs_next(sp, &cs))
					return (1);
				if (cs.cs_flags == 0 &&
				    isblank(cs.cs_ch) && cs_fblank(sp, &cs))
					return (1);
				goto okret;
			}
			state = NONE;
			continue;
		}
		if (cs.cs_flags == CS_EMP) {	/* An EMP is two sentences. */
			if (--cnt == 0)
				goto okret;
			if (cs_fblank(sp, &cs))
				return (1);
			if (--cnt == 0)
				goto okret;
			state = NONE;
			continue;
		}
		switch (cs.cs_ch) {
		case '.':
		case '?':
		case '!':
			state = PERIOD;
			break;
		case ')':
		case ']':
		case '"':
		case '\'':
			if (state != PERIOD)
				state = NONE;
			break;
		case '\t':
			if (state == PERIOD)
				state = BLANK;
			/* FALLTHROUGH */
		case ' ':
			if (state == PERIOD) {
				state = BLANK;
				break;
			}
			if (state == BLANK && --cnt == 0) {
				if (cs_fblank(sp, &cs))
					return (1);
				goto okret;
			}
			/* FALLTHROUGH */
		default:
			state = NONE;
			break;
		}
	}

	/* EOF is a movement sink, but it's an error not to have moved. */
	if (vp->m_start.lno == cs.cs_lno && vp->m_start.cno == cs.cs_cno) {
		v_eof(sp, NULL);
		return (1);
	}

okret:	vp->m_stop.lno = cs.cs_lno;
	vp->m_stop.cno = cs.cs_cno;

	/*
	 * !!!
	 * Historic, uh, features, yeah, that's right, call 'em features.
	 * If the starting and ending cursor positions are at the first
	 * column in their lines, i.e. the movement is cutting entire lines,
	 * the buffer is in line mode, and the ending position is the last
	 * character of the previous line.  Note check to make sure that
	 * it's not within a single line.
	 *
	 * Non-motion commands move to the end of the range.  Delete and
	 * yank stay at the start.  Ignore others.  Adjust the end of the
	 * range for motion commands.
	 */
	if (ISMOTION(vp)) {
		if (vp->m_start.cno == 0 &&
		    (cs.cs_flags != 0 || vp->m_stop.cno == 0)) {
			if (vp->m_start.lno < vp->m_stop.lno) {
				if (db_get(sp,
				    --vp->m_stop.lno, DBG_FATAL, NULL, &len))
					return (1);
				vp->m_stop.cno = len ? len - 1 : 0;
			}
			F_SET(vp, VM_LMODE);
		} else
			--vp->m_stop.cno;
		vp->m_final = vp->m_start;
	} else
		vp->m_final = vp->m_stop;
	return (0);
}