OpenSolaris_b135/cmd/vi/port/ex_voper.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/


/*
 * Copyright (c) 1981 Regents of the University of California
 */

#include "ex.h"
#include "ex_tty.h"
#include "ex_vis.h"
#include <regexpr.h>
#ifndef PRESUNEUC
#include <wctype.h>
/* Undef putchar/getchar if they're defined. */
#ifdef putchar
#undef putchar
#endif
#ifdef getchar
#undef getchar
#endif
#endif /* PRESUNEUC */

#ifdef PRESUNEUC
#define	blank()		isspace(wcursor[0])
#endif /* PRESUNEUC */
#define	forbid(a)	if (a) goto errlab;

unsigned char	vscandir[2] =	{ '/', 0 };

static int get_addr();

/*
 * Decode an operator/operand type command.
 * Eventually we switch to an operator subroutine in ex_vops.c.
 * The work here is setting up a function variable to point
 * to the routine we want, and manipulation of the variables
 * wcursor and wdot, which mark the other end of the affected
 * area.  If wdot is zero, then the current line is the other end,
 * and if wcursor is zero, then the first non-blank location of the
 * other line is implied.
 */
void
operate(int c, int cnt)
{
	wchar_t i;
	int (*moveop)(), (*deleteop)();
	int (*opf)();
	bool subop = 0;
	unsigned char *oglobp, *ocurs;
	line *addr;
	line *odot;
	int oc;
	static unsigned char lastFKND;
	static wchar_t lastFCHR;
	short d;
/* #ifdef PTR_ADDRESSES */
	int mouse_x;
	int mouse_y;
	int oline;
/* #endif PTR_ADDRESSES */

	moveop = vmove, deleteop = (int (*)())vdelete;
	wcursor = cursor;
	wdot = NOLINE;
	notecnt = 0;
	dir = 1;
	switch (c) {

	/*
	 * d		delete operator.
	 */
	case 'd':
		moveop = (int (*)())vdelete;
		deleteop = beep;
		break;

	/*
	 * s		substitute characters, like c\040, i.e. change space.
	 */
	case 's':
		ungetkey(' ');
		subop++;
		/* fall into ... */

	/*
	 * c		Change operator.
	 */
	case 'c':
		if (c == 'c' && workcmd[0] == 'C' || workcmd[0] == 'S')
			subop++;
		moveop = (int (*)())vchange;
		deleteop = beep;
		break;

	/*
	 * !		Filter through a UNIX command.
	 */
	case '!':
		moveop = vfilter;
		deleteop = beep;
		break;

	/*
	 * y		Yank operator.  Place specified text so that it
	 *		can be put back with p/P.  Also yanks to named buffers.
	 */
	case 'y':
		moveop = vyankit;
		deleteop = beep;
		break;

	/*
	 * =		Reformat operator (for LISP).
	 */
	case '=':
		forbid(!value(vi_LISP));
		/* fall into ... */

	/*
	 * >		Right shift operator.
	 * <		Left shift operator.
	 */
	case '<':
	case '>':
		moveop = vshftop;
		deleteop = beep;
		break;

	/*
	 * r		Replace character under cursor with single following
	 *		character.
	 */
	case 'r':
		vmacchng(1);
		vrep(cnt);
		return;

	default:
		goto nocount;
	}
	vmacchng(1);
	/*
	 * Had an operator, so accept another count.
	 * Multiply counts together.
	 */
	if (isdigit(peekkey()) && peekkey() != '0') {
		cnt *= vgetcnt();
		Xcnt = cnt;
		forbid(cnt <= 0);
	}

	/*
	 * Get next character, mapping it and saving as
	 * part of command for repeat.
	 */
	c = map(getesc(), arrows, 0);
	if (c == 0)
		return;
	if (!subop)
		*lastcp++ = c;
nocount:
	opf = moveop;
	switch (c) {

/* #ifdef PTR_ADDRESSES */
	/*
	 * ^X^_		Netty Mouse positioning hack
	 * ^X^]
	 */
	case CTRL('X'):
/*
 *	Read in mouse stuff
 */
		c = getkey();			/* ^_ or ^] */
		if ((c != CTRL('_')) && (c != (CTRL(']'))))
			break;
		getkey();			/* mouse button */
		mouse_x = get_addr() + 1;
		mouse_y = get_addr() + 1;
		if (mouse_y < WTOP)
			break;
		if (Pline == numbline)
			mouse_x -= 8;
		if (mouse_x < 0)
			mouse_x = 0;
		if (mouse_x > WCOLS)
			break;
/*
 *	Find the line on the screen
 */
		for (i = 0; i <= WECHO; i++) {
			if (vlinfo[i].vliny >= mouse_y)
				break;
		}
		if (i > WECHO)
			break;
/*
 *	Look for lines longer than one line - note  odd case at zero
 */
		if (i) {
			if (vlinfo[i - 1].vdepth > 1) {
				mouse_x += WCOLS * (mouse_y -
				    (vlinfo[i].vliny -
				    (vlinfo[i - 1].vdepth - 1)));
			}
		}
		else
		{
			mouse_x += WCOLS * (mouse_y - 1);
		}
/*
 *	Set the line
 */
		vsave();
		ocurs = cursor;
		odot = dot;
		oline = vcline;
		operate('H', i);
/*
 *	Set the column
 */
		getDOT();
		if (Pline == numbline)
			mouse_x += 8;
		vmovcol = mouse_x;
		vmoving = 1;
		wcursor = vfindcol(mouse_x);
/*
 *	Reset everything so that stuff like delete and change work
 */
		wdot = (odot - oline) + i - 1;
		cursor = ocurs;
		vcline = oline;
		dot = odot;
		getDOT();
		break;
/* #endif PTR_ADDRESSES */

	/*
	 * b		Back up a word.
	 * B		Back up a word, liberal definition.
	 */
	case 'b':
	case 'B':
		dir = -1;
		/* fall into ... */

	/*
	 * w		Forward a word.
	 * W		Forward a word, liberal definition.
	 */
	case 'W':
	case 'w':
		wdkind = c & ' ';
		forbid(lfind(2, cnt, opf, (line *)0) < 0);
		vmoving = 0;
		break;

	/*
	 * E		to end of following blank/nonblank word
	 */
	case 'E':
		wdkind = 0;
		goto ein;

	/*
	 * e		To end of following word.
	 */
	case 'e':
		wdkind = 1;
ein:
		forbid(lfind(3, cnt - 1, opf, (line *)0) < 0);
		vmoving = 0;
		break;

	/*
	 * (		Back an s-expression.
	 */
	case '(':
		dir = -1;
		/* fall into... */

	/*
	 * )		Forward an s-expression.
	 */
	case ')':
		forbid(lfind(0, cnt, opf, (line *) 0) < 0);
		markDOT();
		break;

	/*
	 * {		Back an s-expression, but don't stop on atoms.
	 *		In text mode, a paragraph.  For C, a balanced set
	 *		of {}'s.
	 */
	case '{':
		dir = -1;
		/* fall into... */

	/*
	 * }		Forward an s-expression, but don't stop on atoms.
	 *		In text mode, back paragraph.  For C, back a balanced
	 *		set of {}'s.
	 */
	case '}':
		forbid(lfind(1, cnt, opf, (line *) 0) < 0);
		markDOT();
		break;

	/*
	 * %		To matching () or {}.  If not at ( or { scan for
	 *		first such after cursor on this line.
	 */
	case '%':
		vsave();
		ocurs = cursor;
		odot = wdot = dot;
		oglobp = globp;
		CATCH
			i = lmatchp((line *) 0);
		ONERR
			globp = oglobp;
			dot = wdot = odot;
			cursor = ocurs;
			splitw = 0;
			vclean();
			vjumpto(dot, ocurs, 0);
			return;
		ENDCATCH
#ifdef TRACE
		if (trace)
			fprintf(trace, "after lmatchp in %, dot=%d, wdot=%d, "
			    "dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
#endif
		getDOT();
		forbid(!i);
		if (opf != vmove)
			if (dir > 0)
				wcursor++;
			else
				cursor++;
		else
			markDOT();
		vmoving = 0;
		break;

	/*
	 * [		Back to beginning of defun, i.e. an ( in column 1.
	 *		For text, back to a section macro.
	 *		For C, back to a { in column 1 (~~ beg of function.)
	 */
	case '[':
		dir = -1;
		/* fall into ... */

	/*
	 * ]		Forward to next defun, i.e. a ( in column 1.
	 *		For text, forward section.
	 *		For C, forward to a } in column 1 (if delete or such)
	 *		or if a move to a { in column 1.
	 */
	case ']':
		if (!vglobp)
			forbid(getkey() != c);
#ifndef XPG4
		forbid(Xhadcnt);
#endif
		vsave();
#ifdef XPG4
		if (cnt > 1) {
			while (cnt-- > 1) {
				i = lbrack(c, opf);
				getDOT();
				forbid(!i);
				markDOT();
				if (ospeed > B300)
					hold |= HOLDWIG;
				(*opf)(c);
			}
		}
#endif /* XPG4 */
		i = lbrack(c, opf);
		getDOT();
		forbid(!i);
		markDOT();
		if (ospeed > B300)
			hold |= HOLDWIG;
		break;

	/*
	 * ,		Invert last find with f F t or T, like inverse
	 *		of ;.
	 */
	case ',':
		forbid(lastFKND == 0);
		c = isupper(lastFKND) ? tolower(lastFKND) : toupper(lastFKND);
		i = lastFCHR;
		if (vglobp == 0)
			vglobp = (unsigned char *)"";
		subop++;
		goto nocount;

	/*
	 * 0		To beginning of real line.
	 */
	case '0':
		wcursor = linebuf;
		vmoving = 0;
		break;

	/*
	 * ;		Repeat last find with f F t or T.
	 */
	case ';':
		forbid(lastFKND == 0);
		c = lastFKND;
		i = lastFCHR;
		subop++;
		goto nocount;

	/*
	 * F		Find single character before cursor in current line.
	 * T		Like F, but stops before character.
	 */
	case 'F':	/* inverted find */
	case 'T':
		dir = -1;
		/* fall into ... */

	/*
	 * f		Find single character following cursor in current line.
	 * t		Like f, but stope before character.
	 */
	case 'f':	/* find */
	case 't':
		if (!subop) {
			int length;
			wchar_t wchar;
			length = _mbftowc(lastcp, &wchar, getesc, &Peekkey);
			if (length <= 0 || wchar == 0) {
				(void) beep();
				return;
			}
			i = wchar;
			lastcp += length;
		}
		if (vglobp == 0)
			lastFKND = c, lastFCHR = i;
		for (; cnt > 0; cnt--)
			forbid(find(i) == 0);
		vmoving = 0;
		switch (c) {

		case 'T':
			wcursor = nextchr(wcursor);
			break;

		case 't':
			wcursor = lastchr(linebuf, wcursor);
		case 'f':
fixup:
			if (moveop != vmove)
				wcursor = nextchr(wcursor);
			break;
		}
		break;

	/*
	 * |		Find specified print column in current line.
	 */
	case '|':
		if (Pline == numbline)
			cnt += 8;
		vmovcol = cnt;
		vmoving = 1;
		wcursor = vfindcol(cnt);
		break;

	/*
	 * ^		To beginning of non-white space on line.
	 */
	case '^':
		wcursor = vskipwh(linebuf);
		vmoving = 0;
		break;

	/*
	 * $		To end of line.
	 */
	case '$':
		if (opf == vmove) {
			vmoving = 1;
			vmovcol = 20000;
		} else
			vmoving = 0;
		if (cnt > 1) {
			if (opf == vmove) {
				wcursor = 0;
				cnt--;
			} else
				wcursor = linebuf;
			/* This is wrong at EOF */
			wdot = dot + cnt;
			break;
		}
		if (linebuf[0]) {
			wcursor = strend(linebuf);
			wcursor = lastchr(linebuf, wcursor);
			goto fixup;
		}
		wcursor = linebuf;
		break;

	/*
	 * h		Back a character.
	 * ^H		Back a character.
	 */
	case 'h':
	case CTRL('h'):
		dir = -1;
		/* fall into ... */

	/*
	 * space	Forward a character.
	 */
	case 'l':
	case ' ':
		forbid(margin() || opf == vmove && edge());
		while (cnt > 0 && !margin()) {
			if (dir == 1)
				wcursor = nextchr(wcursor);
			else
				wcursor = lastchr(linebuf, wcursor);
			cnt--;
		}
		if (margin() && opf == vmove || wcursor < linebuf) {
			if (dir == 1)
				wcursor = lastchr(linebuf, wcursor);
			else
				wcursor = linebuf;
		}
		vmoving = 0;
		break;

	/*
	 * D		Delete to end of line, short for d$.
	 */
	case 'D':
		cnt = INF;
		goto deleteit;

	/*
	 * X		Delete character before cursor.
	 */
	case 'X':
		dir = -1;
		/* fall into ... */
deleteit:
	/*
	 * x		Delete character at cursor, leaving cursor where it is.
	 */
	case 'x':
		if (margin())
			goto errlab;
		vmacchng(1);
		while (cnt > 0 && !margin()) {
			if (dir == 1)
				wcursor = nextchr(wcursor);
			else
				wcursor = lastchr(linebuf, wcursor);
			cnt--;
		}
		opf = deleteop;
		vmoving = 0;
		break;

	default:
		/*
		 * Stuttered operators are equivalent to the operator on
		 * a line, thus turn dd into d_.
		 */
		if (opf == vmove || c != workcmd[0]) {
errlab:
			(void) beep();
			vmacp = 0;
			return;
		}
		/* fall into ... */

	/*
	 * _		Target for a line or group of lines.
	 *		Stuttering is more convenient; this is mostly
	 *		for aesthetics.
	 */
	case '_':
		wdot = dot + cnt - 1;
		vmoving = 0;
		wcursor = 0;
		break;

	/*
	 * H		To first, home line on screen.
	 *		Count is for count'th line rather than first.
	 */
	case 'H':
		wdot = (dot - vcline) + cnt - 1;
		if (opf == vmove)
			markit(wdot);
		vmoving = 0;
		wcursor = 0;
		break;

	/*
	 * -		Backwards lines, to first non-white character.
	 */
	case '-':
		wdot = dot - cnt;
		vmoving = 0;
		wcursor = 0;
		break;

	/*
	 * ^P		To previous line same column.  Ridiculous on the
	 *		console of the VAX since it puts console in LSI mode.
	 */
	case 'k':
	case CTRL('p'):
		wdot = dot - cnt;
		if (vmoving == 0)
			vmoving = 1, vmovcol = column(cursor);
		wcursor = 0;
		break;

	/*
	 * L		To last line on screen, or count'th line from the
	 *		bottom.
	 */
	case 'L':
		wdot = dot + vcnt - vcline - cnt;
		if (opf == vmove)
			markit(wdot);
		vmoving = 0;
		wcursor = 0;
		break;

	/*
	 * M		To the middle of the screen.
	 */
	case 'M':
		wdot = dot + ((vcnt + 1) / 2) - vcline - 1;
		if (opf == vmove)
			markit(wdot);
		vmoving = 0;
		wcursor = 0;
		break;

	/*
	 * +		Forward line, to first non-white.
	 *
	 * CR		Convenient synonym for +.
	 */
	case '+':
	case CR:
		wdot = dot + cnt;
		vmoving = 0;
		wcursor = 0;
		break;

	/*
	 * ^N		To next line, same column if possible.
	 *
	 * LF		Linefeed is a convenient synonym for ^N.
	 */
	case CTRL('n'):
	case 'j':
	case NL:
		wdot = dot + cnt;
		if (vmoving == 0)
			vmoving = 1, vmovcol = column(cursor);
		wcursor = 0;
		break;

	/*
	 * n		Search to next match of current pattern.
	 */
	case 'n':
		vglobp = vscandir;
		c = *vglobp++;
		goto nocount;

	/*
	 * N		Like n but in reverse direction.
	 */
	case 'N':
		vglobp = vscandir[0] == '/' ? (unsigned char *)"?" :
		    (unsigned char *)"/";
		c = *vglobp++;
		goto nocount;

	/*
	 * '		Return to line specified by following mark,
	 *		first white position on line.
	 *
	 * `		Return to marked line at remembered column.
	 */
	case '\'':
	case '`':
		d = c;
		c = getesc();
		if (c == 0)
			return;
		c = markreg(c);
		forbid(c == 0);
		wdot = getmark(c);
		forbid(wdot == NOLINE);
		forbid(Xhadcnt);
		vmoving = 0;
		wcursor = d == '`' ? ncols[c - 'a'] : 0;
		if (opf == vmove && (wdot != dot ||
		    (d == '`' && wcursor != cursor)))
			markDOT();
		if (wcursor) {
			vsave();
			getline(*wdot);
			if (wcursor > strend(linebuf))
				wcursor = 0;
			else {
				cnt = wcursor - linebuf;
				/*CSTYLED*/
				for (wcursor = linebuf; wcursor - linebuf < cnt; ) 
					wcursor = nextchr(wcursor);
				if (wcursor - linebuf > cnt)
					wcursor = lastchr(linebuf, wcursor);
			}
			getDOT();
		}
		if (ospeed > B300)
			hold |= HOLDWIG;
		break;

	/*
	 * G		Goto count'th line, or last line if no count
	 *		given.
	 */
	case 'G':
		if (!Xhadcnt)
			cnt = lineDOL();
		wdot = zero + cnt;
		forbid(wdot < one || wdot > dol);
		if (opf == vmove)
			markit(wdot);
		vmoving = 0;
		wcursor = 0;
		break;

	/*
	 * /		Scan forward for following re.
	 * ?		Scan backward for following re.
	 */
	case '/':
	case '?':
		forbid(Xhadcnt);
		vsave();
		oc = c;
		ocurs = cursor;
		odot = dot;
		wcursor = 0;
		if (readecho(c))
			return;
		if (!vglobp)
			vscandir[0] = genbuf[0];
		oglobp = globp; CP(vutmp, genbuf); globp = vutmp;
		d = peekc;
fromsemi:
		ungetchar(0);
		fixech();
		CATCH
#ifndef CBREAK
			/*
			 * Lose typeahead (ick).
			 */
			vcook();
#endif
			addr = address(cursor);
#ifndef CBREAK
			vraw();
#endif
		ONERR
#ifndef CBREAK
			vraw();
#endif
slerr:
			globp = oglobp;
			dot = odot;
			cursor = ocurs;
			ungetchar(d);
			splitw = 0;
			vclean();
			vjumpto(dot, ocurs, 0);
			return;
		ENDCATCH
		if (globp == 0)
			globp = (unsigned char *)"";
		else if (peekc)
			--globp;
		if (*globp == ';') {
			/* /foo/;/bar/ */
			globp++;
			dot = addr;
			cursor = (unsigned char *)loc1;
			goto fromsemi;
		}
		dot = odot;
		ungetchar(d);
		c = 0;
		if (*globp == 'z')
			globp++, c = '\n';
		if (any(*globp, "^+-."))
			c = *globp++;
		i = 0;
		while (isdigit(*globp))
			i = i * 10 + *globp++ - '0';
		if (any(*globp, "^+-."))
			c = *globp++;
		if (*globp) {
			/* random junk after the pattern */
			(void) beep();
			goto slerr;
		}
		globp = oglobp;
		splitw = 0;
		vmoving = 0;
		wcursor = (unsigned char *)loc1;
		if (i != 0)
			vsetsiz(i);
		if (opf == vmove) {
			if (state == ONEOPEN || state == HARDOPEN)
				outline = destline = WBOT;
			if (addr != dot || (unsigned char *)loc1 != cursor)
				markDOT();
			if (loc1 > (char *)linebuf && *loc1 == 0)
				loc1 = (char *)lastchr(linebuf, loc1);
			if (c)
				vjumpto(addr, (unsigned char *)loc1, c);
			else {
				vmoving = 0;
				if (loc1) {
					vmoving++;
					vmovcol = column(loc1);
				}
				getDOT();
				if (state == CRTOPEN && addr != dot)
					vup1();
				vupdown(addr - dot, NOSTR);
			}
			if (oc == '/') {	/* forward search */
				if (dot < odot ||
				    (dot == odot && cursor <= ocurs))
					warnf(value(vi_TERSE) ?
			gettext("Search wrapped BOTTOM") :
			gettext("Search wrapped around BOTTOM of buffer"));
			} else {		/* backward search */
				if (dot > odot ||
				    (dot == odot && cursor >= ocurs))
					warnf(value(vi_TERSE) ?
			gettext("Search wrapped TOP") :
			gettext("Search wrapped around TOP of buffer"));
			}
			return;
		}
		lastcp[-1] = 'n';
		getDOT();
		wdot = addr;
		break;
	}
	/*
	 * Apply.
	 */
	if (vreg && wdot == 0)
		wdot = dot;
	(*opf)(c);
	wdot = NOLINE;
}

static void
lfixol()
{
	unsigned char *savevglobp;
	int savesplit;

	if (Outchar == vputchar)
		return;

	/* Show messages */
	putnl();
	if (inopen > 0 && clr_eol)
		vclreol();
	if (enter_standout_mode && exit_bold)
		putpad((unsigned char *)enter_standout_mode);
	lprintf(gettext("[Hit return to continue] "), 0);
	if (enter_standout_mode && exit_bold)
		putpad((unsigned char *)exit_bold);

	/* Get key input for confirmation */
	savevglobp = vglobp;
	vglobp = 0; /* force typed input */
	getkey();
	vglobp = savevglobp;

	/* reset output function */
	Outchar = vputchar;

	/* Clean up screen */
	savesplit = splitw;
	splitw = 0;
	vclear();
	vdirty(0, WLINES);
	vredraw(WTOP);
	splitw = savesplit;
}

void
warnf(char *str, char *cp)
{
	int saveline, savecol, savesplit;

	saveline = outline;
	savecol = outcol;
	savesplit = splitw;
	splitw = 1;
	vgoto(WECHO, 0);
	if (!enter_standout_mode || !exit_bold)
		dingdong();
	if (clr_eol)
		vclreol();
	if (enter_standout_mode && exit_bold)
		putpad((unsigned char *)enter_standout_mode);
	lprintf(str, cp);
	if (enter_standout_mode && exit_bold)
		putpad((unsigned char *)exit_bold);
	lfixol();
	vgoto(saveline, savecol);
	splitw = savesplit;
}

/* #ifdef PTR_ADDRESSES */
/*
 *	read in a row or column address
 *
 */
static int
get_addr()
{
	short  c;
	short  next;

	c = getkey();
	next = 0;
	switch (c) {
	case CTRL('A'):
		next = 96;
		c = getkey();
		break;

	case CTRL('B'):
		next = 192;
		c = getkey();
		break;
	}
	if (c < ' ')
		return (-1);
	return (next + c - ' ');
}
/* #endif PTR_ADDRESSES */

/*
 * Find single character c, in direction dir from cursor.
 */
int
find(wchar_t c)
{

	wchar_t wchar;
	int length;
	for (;;) {
		if (edge())
			return (0);
		if (dir == 1)
			wcursor = nextchr(wcursor);
		else
			wcursor = lastchr(linebuf, wcursor);
		if ((length = mbtowc(&wchar, (char *)wcursor,
		    MULTI_BYTE_MAX)) > 0 && wchar == c)
			return (1);
	}
}

/*
 * Do a word motion with operator op, and cnt more words
 * to go after this.
 */
int
word(int (*op)(), int cnt)
{
	int which;
	unsigned char *iwc;
	line *iwdot = wdot;
	wchar_t wchar;
	int length;

	if (dir == 1) {
		iwc = wcursor;
		which = wordch(wcursor);
		while (wordof(which, wcursor)) {
			length = mbtowc(&wchar, (char *)wcursor,
			    MULTI_BYTE_MAX);
			if (length <= 0)
				length = 1;
			if (cnt == 1 && op != vmove && wcursor[length] == 0) {
				wcursor += length;
				break;
			}
			if (!lnext())
				return (0);
			if (wcursor == linebuf)
				break;
		}
		/* Unless last segment of a change skip blanks */
		if (op != (int (*)())vchange || cnt > 1)
			while (!margin() && blank()) {
				if (!lnext())
					return (0);
			}
		else
			if (wcursor == iwc && iwdot == wdot && *iwc)
				wcursor = nextchr(wcursor);
		if (op == vmove && margin()) {
			wcursor = lastchr(linebuf, wcursor);
#ifdef XPG4
			if (wcursor < linebuf) {
				wcursor = linebuf;
			}
#endif /* XPG4 */
		}
	} else {
		if (!lnext())
			return (0);
		while (blank())
			if (!lnext())
				return (0);
		if (!margin()) {
			which = wordch(wcursor);
			while (!margin() && wordof(which, wcursor))
				wcursor = lastchr(linebuf, wcursor);
		}
#ifdef PRESUNEUC
		if (wcursor < linebuf || !wordof(which, wcursor))
			wcursor = nextchr(wcursor);
#else
		if (wcursor < linebuf)
			wcursor++;
		else if (!wordof(which, wcursor))
			wcursor = nextchr(wcursor);
#endif /* PRESUNEUC */
	}
	return (1);
}

/*
 * To end of word, with operator op and cnt more motions
 * remaining after this.
 */
int
eend(int (*op)())
{
	int which;

	if (!lnext())
		return (0);
	while (blank())
		if (!lnext())
			return (0);
	which = wordch(wcursor);
	while (wordof(which, wcursor)) {
		if (wcursor[1] == 0) {
			wcursor = nextchr(wcursor);
			break;
		}
		if (!lnext())
			return (0);
	}
	if (op == vyankit)
		wcursor = lastchr(linebuf, wcursor) + 1;
	else if (op != (int (*)())vchange && op != (int (*)())vdelete &&
	    wcursor > linebuf)
		wcursor = lastchr(linebuf, wcursor);
	return (1);
}

/*
 * Wordof tells whether the character at *wc is in a word of
 * kind which (blank/nonblank words are 0, conservative words 1).
 */
int
wordof(unsigned char which, unsigned char *wc)
{
#ifdef PRESUNEUC

	if (isspace(*wc))
#else
	wchar_t z;

	(void) mbtowc(&z, (char *)wc, MB_LEN_MAX);
	if (iswspace(z))
#endif /* PRESUNEUC */
		return (0);
	return (!wdkind || wordch(wc) == which);
}

/*
 * Wordch tells whether character at *wc is a word character
 * i.e. an alfa, digit, or underscore.
 */
#ifdef PRESUNEUC
#define	SS2 0216
#define	SS3 0217
#endif /* PRESUNEUC */

int
wordch(unsigned char *wc)
{
	int length;
	wchar_t c;

	length = mbtowc(&c, (char *)wc, MULTI_BYTE_MAX);
	if (length <= 0)
		return (0);
	if (length > 1)
#ifndef PRESUNEUC
		if (wdwc)
			return (*wdwc)(c);
		else
#endif /* PRESUNEUC */
		return (length);
#ifndef PRESUNEUC
	return (isalpha(*wc) || isdigit(*wc) || *wc == '_');
#else
	return (isalpha(c) || isdigit(c) || c == '_');
#endif /* PRESUNEUC */
}

/*
 * Edge tells when we hit the last character in the current line.
 */
int
edge(void)
{

	if (linebuf[0] == 0)
		return (1);
	if (dir == 1)
		return (*(nextchr(wcursor)) == 0);
	else
		return (wcursor == linebuf);
}

/*
 * Margin tells us when we have fallen off the end of the line.
 */
int
margin(void)
{

	return (wcursor < linebuf || wcursor[0] == 0);
}
#ifndef PRESUNEUC

/*
 * Blank tells if the cursor is currently on a TAB, RETURN,
 * NEWLINE, FORMFEED, bertical tab, or SPACE character from EUC
 * primary and supplementary codesets.
 */
int
blank(void)
{
	wchar_t z;

	(void) mbtowc(&z, (char *)wcursor, MB_CUR_MAX);
	return (iswspace((int)z));
}
#endif /* PRESUNEUC */