xref: /freebsd/contrib/nvi/vi/v_word.c (revision b8ba871b)
1b8ba871bSPeter Wemm /*-
2b8ba871bSPeter Wemm  * Copyright (c) 1992, 1993, 1994
3b8ba871bSPeter Wemm  *	The Regents of the University of California.  All rights reserved.
4b8ba871bSPeter Wemm  * Copyright (c) 1992, 1993, 1994, 1995, 1996
5b8ba871bSPeter Wemm  *	Keith Bostic.  All rights reserved.
6b8ba871bSPeter Wemm  *
7b8ba871bSPeter Wemm  * See the LICENSE file for redistribution information.
8b8ba871bSPeter Wemm  */
9b8ba871bSPeter Wemm 
10b8ba871bSPeter Wemm #include "config.h"
11b8ba871bSPeter Wemm 
12b8ba871bSPeter Wemm #include <sys/types.h>
13b8ba871bSPeter Wemm #include <sys/queue.h>
14b8ba871bSPeter Wemm #include <sys/time.h>
15b8ba871bSPeter Wemm 
16b8ba871bSPeter Wemm #include <bitstring.h>
17b8ba871bSPeter Wemm #include <ctype.h>
18b8ba871bSPeter Wemm #include <limits.h>
19b8ba871bSPeter Wemm #include <stdio.h>
20b8ba871bSPeter Wemm 
21b8ba871bSPeter Wemm #include "../common/common.h"
22b8ba871bSPeter Wemm #include "vi.h"
23b8ba871bSPeter Wemm 
24b8ba871bSPeter Wemm /*
25b8ba871bSPeter Wemm  * There are two types of "words".  Bigwords are easy -- groups of anything
26b8ba871bSPeter Wemm  * delimited by whitespace.  Normal words are trickier.  They are either a
27b8ba871bSPeter Wemm  * group of characters, numbers and underscores, or a group of anything but,
28b8ba871bSPeter Wemm  * delimited by whitespace.  When for a word, if you're in whitespace, it's
29b8ba871bSPeter Wemm  * easy, just remove the whitespace and go to the beginning or end of the
30b8ba871bSPeter Wemm  * word.  Otherwise, figure out if the next character is in a different group.
31b8ba871bSPeter Wemm  * If it is, go to the beginning or end of that group, otherwise, go to the
32b8ba871bSPeter Wemm  * beginning or end of the current group.  The historic version of vi didn't
33b8ba871bSPeter Wemm  * get this right, so, for example, there were cases where "4e" was not the
34b8ba871bSPeter Wemm  * same as "eeee" -- in particular, single character words, and commands that
35b8ba871bSPeter Wemm  * began in whitespace were almost always handled incorrectly.  To get it right
36b8ba871bSPeter Wemm  * you have to resolve the cursor after each search so that the look-ahead to
37b8ba871bSPeter Wemm  * figure out what type of "word" the cursor is in will be correct.
38b8ba871bSPeter Wemm  *
39b8ba871bSPeter Wemm  * Empty lines, and lines that consist of only white-space characters count
40b8ba871bSPeter Wemm  * as a single word, and the beginning and end of the file counts as an
41b8ba871bSPeter Wemm  * infinite number of words.
42b8ba871bSPeter Wemm  *
43b8ba871bSPeter Wemm  * Movements associated with commands are different than movement commands.
44b8ba871bSPeter Wemm  * For example, in "abc  def", with the cursor on the 'a', "cw" is from
45b8ba871bSPeter Wemm  * 'a' to 'c', while "w" is from 'a' to 'd'.  In general, trailing white
46b8ba871bSPeter Wemm  * space is discarded from the change movement.  Another example is that,
47b8ba871bSPeter Wemm  * in the same string, a "cw" on any white space character replaces that
48b8ba871bSPeter Wemm  * single character, and nothing else.  Ain't nothin' in here that's easy.
49b8ba871bSPeter Wemm  *
50b8ba871bSPeter Wemm  * One historic note -- in the original vi, the 'w', 'W' and 'B' commands
51b8ba871bSPeter Wemm  * would treat groups of empty lines as individual words, i.e. the command
52b8ba871bSPeter Wemm  * would move the cursor to each new empty line.  The 'e' and 'E' commands
53b8ba871bSPeter Wemm  * would treat groups of empty lines as a single word, i.e. the first use
54b8ba871bSPeter Wemm  * would move past the group of lines.  The 'b' command would just beep at
55b8ba871bSPeter Wemm  * you, or, if you did it from the start of the line as part of a motion
56b8ba871bSPeter Wemm  * command, go absolutely nuts.  If the lines contained only white-space
57b8ba871bSPeter Wemm  * characters, the 'w' and 'W' commands would just beep at you, and the 'B',
58b8ba871bSPeter Wemm  * 'b', 'E' and 'e' commands would treat the group as a single word, and
59b8ba871bSPeter Wemm  * the 'B' and 'b' commands will treat the lines as individual words.  This
60b8ba871bSPeter Wemm  * implementation treats all of these cases as a single white-space word.
61b8ba871bSPeter Wemm  */
62b8ba871bSPeter Wemm 
63b8ba871bSPeter Wemm enum which {BIGWORD, LITTLEWORD};
64b8ba871bSPeter Wemm 
65b8ba871bSPeter Wemm static int bword(SCR *, VICMD *, enum which);
66b8ba871bSPeter Wemm static int eword(SCR *, VICMD *, enum which);
67b8ba871bSPeter Wemm static int fword(SCR *, VICMD *, enum which);
68b8ba871bSPeter Wemm 
69b8ba871bSPeter Wemm /*
70b8ba871bSPeter Wemm  * v_wordW -- [count]W
71b8ba871bSPeter Wemm  *	Move forward a bigword at a time.
72b8ba871bSPeter Wemm  *
73b8ba871bSPeter Wemm  * PUBLIC: int v_wordW(SCR *, VICMD *);
74b8ba871bSPeter Wemm  */
75b8ba871bSPeter Wemm int
v_wordW(SCR * sp,VICMD * vp)76b8ba871bSPeter Wemm v_wordW(SCR *sp, VICMD *vp)
77b8ba871bSPeter Wemm {
78b8ba871bSPeter Wemm 	return (fword(sp, vp, BIGWORD));
79b8ba871bSPeter Wemm }
80b8ba871bSPeter Wemm 
81b8ba871bSPeter Wemm /*
82b8ba871bSPeter Wemm  * v_wordw -- [count]w
83b8ba871bSPeter Wemm  *	Move forward a word at a time.
84b8ba871bSPeter Wemm  *
85b8ba871bSPeter Wemm  * PUBLIC: int v_wordw(SCR *, VICMD *);
86b8ba871bSPeter Wemm  */
87b8ba871bSPeter Wemm int
v_wordw(SCR * sp,VICMD * vp)88b8ba871bSPeter Wemm v_wordw(SCR *sp, VICMD *vp)
89b8ba871bSPeter Wemm {
90b8ba871bSPeter Wemm 	return (fword(sp, vp, LITTLEWORD));
91b8ba871bSPeter Wemm }
92b8ba871bSPeter Wemm 
93b8ba871bSPeter Wemm /*
94b8ba871bSPeter Wemm  * fword --
95b8ba871bSPeter Wemm  *	Move forward by words.
96b8ba871bSPeter Wemm  */
97b8ba871bSPeter Wemm static int
fword(SCR * sp,VICMD * vp,enum which type)98b8ba871bSPeter Wemm fword(SCR *sp, VICMD *vp, enum which type)
99b8ba871bSPeter Wemm {
100b8ba871bSPeter Wemm 	enum { INWORD, NOTWORD } state;
101b8ba871bSPeter Wemm 	VCS cs;
102b8ba871bSPeter Wemm 	u_long cnt;
103b8ba871bSPeter Wemm 
104b8ba871bSPeter Wemm 	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
105b8ba871bSPeter Wemm 	cs.cs_lno = vp->m_start.lno;
106b8ba871bSPeter Wemm 	cs.cs_cno = vp->m_start.cno;
107b8ba871bSPeter Wemm 	if (cs_init(sp, &cs))
108b8ba871bSPeter Wemm 		return (1);
109b8ba871bSPeter Wemm 
110b8ba871bSPeter Wemm 	/*
111b8ba871bSPeter Wemm 	 * If in white-space:
112b8ba871bSPeter Wemm 	 *	If the count is 1, and it's a change command, we're done.
113b8ba871bSPeter Wemm 	 *	Else, move to the first non-white-space character, which
114b8ba871bSPeter Wemm 	 *	counts as a single word move.  If it's a motion command,
115b8ba871bSPeter Wemm 	 *	don't move off the end of the line.
116b8ba871bSPeter Wemm 	 */
117b8ba871bSPeter Wemm 	if (cs.cs_flags == CS_EMP || (cs.cs_flags == 0 && ISBLANK(cs.cs_ch))) {
118b8ba871bSPeter Wemm 		if (ISMOTION(vp) && cs.cs_flags != CS_EMP && cnt == 1) {
119b8ba871bSPeter Wemm 			if (ISCMD(vp->rkp, 'c'))
120b8ba871bSPeter Wemm 				return (0);
121b8ba871bSPeter Wemm 			if (ISCMD(vp->rkp, 'd') || ISCMD(vp->rkp, 'y')) {
122b8ba871bSPeter Wemm 				if (cs_fspace(sp, &cs))
123b8ba871bSPeter Wemm 					return (1);
124b8ba871bSPeter Wemm 				goto ret;
125b8ba871bSPeter Wemm 			}
126b8ba871bSPeter Wemm 		}
127b8ba871bSPeter Wemm 		if (cs_fblank(sp, &cs))
128b8ba871bSPeter Wemm 			return (1);
129b8ba871bSPeter Wemm 		--cnt;
130b8ba871bSPeter Wemm 	}
131b8ba871bSPeter Wemm 
132b8ba871bSPeter Wemm 	/*
133b8ba871bSPeter Wemm 	 * Cyclically move to the next word -- this involves skipping
134b8ba871bSPeter Wemm 	 * over word characters and then any trailing non-word characters.
135b8ba871bSPeter Wemm 	 * Note, for the 'w' command, the definition of a word keeps
136b8ba871bSPeter Wemm 	 * switching.
137b8ba871bSPeter Wemm 	 */
138b8ba871bSPeter Wemm 	if (type == BIGWORD)
139b8ba871bSPeter Wemm 		while (cnt--) {
140b8ba871bSPeter Wemm 			for (;;) {
141b8ba871bSPeter Wemm 				if (cs_next(sp, &cs))
142b8ba871bSPeter Wemm 					return (1);
143b8ba871bSPeter Wemm 				if (cs.cs_flags == CS_EOF)
144b8ba871bSPeter Wemm 					goto ret;
145b8ba871bSPeter Wemm 				if (cs.cs_flags != 0 || ISBLANK(cs.cs_ch))
146b8ba871bSPeter Wemm 					break;
147b8ba871bSPeter Wemm 			}
148b8ba871bSPeter Wemm 			/*
149b8ba871bSPeter Wemm 			 * If a motion command and we're at the end of the
150b8ba871bSPeter Wemm 			 * last word, we're done.  Delete and yank eat any
151b8ba871bSPeter Wemm 			 * trailing blanks, but we don't move off the end
152b8ba871bSPeter Wemm 			 * of the line regardless.
153b8ba871bSPeter Wemm 			 */
154b8ba871bSPeter Wemm 			if (cnt == 0 && ISMOTION(vp)) {
155b8ba871bSPeter Wemm 				if ((ISCMD(vp->rkp, 'd') ||
156b8ba871bSPeter Wemm 				    ISCMD(vp->rkp, 'y')) &&
157b8ba871bSPeter Wemm 				    cs_fspace(sp, &cs))
158b8ba871bSPeter Wemm 					return (1);
159b8ba871bSPeter Wemm 				break;
160b8ba871bSPeter Wemm 			}
161b8ba871bSPeter Wemm 
162b8ba871bSPeter Wemm 			/* Eat whitespace characters. */
163b8ba871bSPeter Wemm 			if (cs_fblank(sp, &cs))
164b8ba871bSPeter Wemm 				return (1);
165b8ba871bSPeter Wemm 			if (cs.cs_flags == CS_EOF)
166b8ba871bSPeter Wemm 				goto ret;
167b8ba871bSPeter Wemm 		}
168b8ba871bSPeter Wemm 	else
169b8ba871bSPeter Wemm 		while (cnt--) {
170b8ba871bSPeter Wemm 			state = cs.cs_flags == 0 &&
171b8ba871bSPeter Wemm 			    inword(cs.cs_ch) ? INWORD : NOTWORD;
172b8ba871bSPeter Wemm 			for (;;) {
173b8ba871bSPeter Wemm 				if (cs_next(sp, &cs))
174b8ba871bSPeter Wemm 					return (1);
175b8ba871bSPeter Wemm 				if (cs.cs_flags == CS_EOF)
176b8ba871bSPeter Wemm 					goto ret;
177b8ba871bSPeter Wemm 				if (cs.cs_flags != 0 || ISBLANK(cs.cs_ch))
178b8ba871bSPeter Wemm 					break;
179b8ba871bSPeter Wemm 				if (state == INWORD) {
180b8ba871bSPeter Wemm 					if (!inword(cs.cs_ch))
181b8ba871bSPeter Wemm 						break;
182b8ba871bSPeter Wemm 				} else
183b8ba871bSPeter Wemm 					if (inword(cs.cs_ch))
184b8ba871bSPeter Wemm 						break;
185b8ba871bSPeter Wemm 			}
186b8ba871bSPeter Wemm 			/* See comment above. */
187b8ba871bSPeter Wemm 			if (cnt == 0 && ISMOTION(vp)) {
188b8ba871bSPeter Wemm 				if ((ISCMD(vp->rkp, 'd') ||
189b8ba871bSPeter Wemm 				    ISCMD(vp->rkp, 'y')) &&
190b8ba871bSPeter Wemm 				    cs_fspace(sp, &cs))
191b8ba871bSPeter Wemm 					return (1);
192b8ba871bSPeter Wemm 				break;
193b8ba871bSPeter Wemm 			}
194b8ba871bSPeter Wemm 
195b8ba871bSPeter Wemm 			/* Eat whitespace characters. */
196b8ba871bSPeter Wemm 			if (cs.cs_flags != 0 || ISBLANK(cs.cs_ch))
197b8ba871bSPeter Wemm 				if (cs_fblank(sp, &cs))
198b8ba871bSPeter Wemm 					return (1);
199b8ba871bSPeter Wemm 			if (cs.cs_flags == CS_EOF)
200b8ba871bSPeter Wemm 				goto ret;
201b8ba871bSPeter Wemm 		}
202b8ba871bSPeter Wemm 
203b8ba871bSPeter Wemm 	/*
204b8ba871bSPeter Wemm 	 * If we didn't move, we must be at EOF.
205b8ba871bSPeter Wemm 	 *
206b8ba871bSPeter Wemm 	 * !!!
207b8ba871bSPeter Wemm 	 * That's okay for motion commands, however.
208b8ba871bSPeter Wemm 	 */
209b8ba871bSPeter Wemm ret:	if (!ISMOTION(vp) &&
210b8ba871bSPeter Wemm 	    cs.cs_lno == vp->m_start.lno && cs.cs_cno == vp->m_start.cno) {
211b8ba871bSPeter Wemm 		v_eof(sp, &vp->m_start);
212b8ba871bSPeter Wemm 		return (1);
213b8ba871bSPeter Wemm 	}
214b8ba871bSPeter Wemm 
215b8ba871bSPeter Wemm 	/* Adjust the end of the range for motion commands. */
216b8ba871bSPeter Wemm 	vp->m_stop.lno = cs.cs_lno;
217b8ba871bSPeter Wemm 	vp->m_stop.cno = cs.cs_cno;
218b8ba871bSPeter Wemm 	if (ISMOTION(vp) && cs.cs_flags == 0)
219b8ba871bSPeter Wemm 		--vp->m_stop.cno;
220b8ba871bSPeter Wemm 
221b8ba871bSPeter Wemm 	/*
222b8ba871bSPeter Wemm 	 * Non-motion commands move to the end of the range.  Delete
223b8ba871bSPeter Wemm 	 * and yank stay at the start, ignore others.
224b8ba871bSPeter Wemm 	 */
225b8ba871bSPeter Wemm 	vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
226b8ba871bSPeter Wemm 	return (0);
227b8ba871bSPeter Wemm }
228b8ba871bSPeter Wemm 
229b8ba871bSPeter Wemm /*
230b8ba871bSPeter Wemm  * v_wordE -- [count]E
231b8ba871bSPeter Wemm  *	Move forward to the end of the bigword.
232b8ba871bSPeter Wemm  *
233b8ba871bSPeter Wemm  * PUBLIC: int v_wordE(SCR *, VICMD *);
234b8ba871bSPeter Wemm  */
235b8ba871bSPeter Wemm int
v_wordE(SCR * sp,VICMD * vp)236b8ba871bSPeter Wemm v_wordE(SCR *sp, VICMD *vp)
237b8ba871bSPeter Wemm {
238b8ba871bSPeter Wemm 	return (eword(sp, vp, BIGWORD));
239b8ba871bSPeter Wemm }
240b8ba871bSPeter Wemm 
241b8ba871bSPeter Wemm /*
242b8ba871bSPeter Wemm  * v_worde -- [count]e
243b8ba871bSPeter Wemm  *	Move forward to the end of the word.
244b8ba871bSPeter Wemm  *
245b8ba871bSPeter Wemm  * PUBLIC: int v_worde(SCR *, VICMD *);
246b8ba871bSPeter Wemm  */
247b8ba871bSPeter Wemm int
v_worde(SCR * sp,VICMD * vp)248b8ba871bSPeter Wemm v_worde(SCR *sp, VICMD *vp)
249b8ba871bSPeter Wemm {
250b8ba871bSPeter Wemm 	return (eword(sp, vp, LITTLEWORD));
251b8ba871bSPeter Wemm }
252b8ba871bSPeter Wemm 
253b8ba871bSPeter Wemm /*
254b8ba871bSPeter Wemm  * eword --
255b8ba871bSPeter Wemm  *	Move forward to the end of the word.
256b8ba871bSPeter Wemm  */
257b8ba871bSPeter Wemm static int
eword(SCR * sp,VICMD * vp,enum which type)258b8ba871bSPeter Wemm eword(SCR *sp, VICMD *vp, enum which type)
259b8ba871bSPeter Wemm {
260b8ba871bSPeter Wemm 	enum { INWORD, NOTWORD } state;
261b8ba871bSPeter Wemm 	VCS cs;
262b8ba871bSPeter Wemm 	u_long cnt;
263b8ba871bSPeter Wemm 
264b8ba871bSPeter Wemm 	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
265b8ba871bSPeter Wemm 	cs.cs_lno = vp->m_start.lno;
266b8ba871bSPeter Wemm 	cs.cs_cno = vp->m_start.cno;
267b8ba871bSPeter Wemm 	if (cs_init(sp, &cs))
268b8ba871bSPeter Wemm 		return (1);
269b8ba871bSPeter Wemm 
270b8ba871bSPeter Wemm 	/*
271b8ba871bSPeter Wemm 	 * !!!
272b8ba871bSPeter Wemm 	 * If in whitespace, or the next character is whitespace, move past
273b8ba871bSPeter Wemm 	 * it.  (This doesn't count as a word move.)  Stay at the character
274b8ba871bSPeter Wemm 	 * past the current one, it sets word "state" for the 'e' command.
275b8ba871bSPeter Wemm 	 */
276b8ba871bSPeter Wemm 	if (cs.cs_flags == 0 && !ISBLANK(cs.cs_ch)) {
277b8ba871bSPeter Wemm 		if (cs_next(sp, &cs))
278b8ba871bSPeter Wemm 			return (1);
279b8ba871bSPeter Wemm 		if (cs.cs_flags == 0 && !ISBLANK(cs.cs_ch))
280b8ba871bSPeter Wemm 			goto start;
281b8ba871bSPeter Wemm 	}
282b8ba871bSPeter Wemm 	if (cs_fblank(sp, &cs))
283b8ba871bSPeter Wemm 		return (1);
284b8ba871bSPeter Wemm 
285b8ba871bSPeter Wemm 	/*
286b8ba871bSPeter Wemm 	 * Cyclically move to the next word -- this involves skipping
287b8ba871bSPeter Wemm 	 * over word characters and then any trailing non-word characters.
288b8ba871bSPeter Wemm 	 * Note, for the 'e' command, the definition of a word keeps
289b8ba871bSPeter Wemm 	 * switching.
290b8ba871bSPeter Wemm 	 */
291b8ba871bSPeter Wemm start:	if (type == BIGWORD)
292b8ba871bSPeter Wemm 		while (cnt--) {
293b8ba871bSPeter Wemm 			for (;;) {
294b8ba871bSPeter Wemm 				if (cs_next(sp, &cs))
295b8ba871bSPeter Wemm 					return (1);
296b8ba871bSPeter Wemm 				if (cs.cs_flags == CS_EOF)
297b8ba871bSPeter Wemm 					goto ret;
298b8ba871bSPeter Wemm 				if (cs.cs_flags != 0 || ISBLANK(cs.cs_ch))
299b8ba871bSPeter Wemm 					break;
300b8ba871bSPeter Wemm 			}
301b8ba871bSPeter Wemm 			/*
302b8ba871bSPeter Wemm 			 * When we reach the start of the word after the last
303b8ba871bSPeter Wemm 			 * word, we're done.  If we changed state, back up one
304b8ba871bSPeter Wemm 			 * to the end of the previous word.
305b8ba871bSPeter Wemm 			 */
306b8ba871bSPeter Wemm 			if (cnt == 0) {
307b8ba871bSPeter Wemm 				if (cs.cs_flags == 0 && cs_prev(sp, &cs))
308b8ba871bSPeter Wemm 					return (1);
309b8ba871bSPeter Wemm 				break;
310b8ba871bSPeter Wemm 			}
311b8ba871bSPeter Wemm 
312b8ba871bSPeter Wemm 			/* Eat whitespace characters. */
313b8ba871bSPeter Wemm 			if (cs_fblank(sp, &cs))
314b8ba871bSPeter Wemm 				return (1);
315b8ba871bSPeter Wemm 			if (cs.cs_flags == CS_EOF)
316b8ba871bSPeter Wemm 				goto ret;
317b8ba871bSPeter Wemm 		}
318b8ba871bSPeter Wemm 	else
319b8ba871bSPeter Wemm 		while (cnt--) {
320b8ba871bSPeter Wemm 			state = cs.cs_flags == 0 &&
321b8ba871bSPeter Wemm 			    inword(cs.cs_ch) ? INWORD : NOTWORD;
322b8ba871bSPeter Wemm 			for (;;) {
323b8ba871bSPeter Wemm 				if (cs_next(sp, &cs))
324b8ba871bSPeter Wemm 					return (1);
325b8ba871bSPeter Wemm 				if (cs.cs_flags == CS_EOF)
326b8ba871bSPeter Wemm 					goto ret;
327b8ba871bSPeter Wemm 				if (cs.cs_flags != 0 || ISBLANK(cs.cs_ch))
328b8ba871bSPeter Wemm 					break;
329b8ba871bSPeter Wemm 				if (state == INWORD) {
330b8ba871bSPeter Wemm 					if (!inword(cs.cs_ch))
331b8ba871bSPeter Wemm 						break;
332b8ba871bSPeter Wemm 				} else
333b8ba871bSPeter Wemm 					if (inword(cs.cs_ch))
334b8ba871bSPeter Wemm 						break;
335b8ba871bSPeter Wemm 			}
336b8ba871bSPeter Wemm 			/* See comment above. */
337b8ba871bSPeter Wemm 			if (cnt == 0) {
338b8ba871bSPeter Wemm 				if (cs.cs_flags == 0 && cs_prev(sp, &cs))
339b8ba871bSPeter Wemm 					return (1);
340b8ba871bSPeter Wemm 				break;
341b8ba871bSPeter Wemm 			}
342b8ba871bSPeter Wemm 
343b8ba871bSPeter Wemm 			/* Eat whitespace characters. */
344b8ba871bSPeter Wemm 			if (cs.cs_flags != 0 || ISBLANK(cs.cs_ch))
345b8ba871bSPeter Wemm 				if (cs_fblank(sp, &cs))
346b8ba871bSPeter Wemm 					return (1);
347b8ba871bSPeter Wemm 			if (cs.cs_flags == CS_EOF)
348b8ba871bSPeter Wemm 				goto ret;
349b8ba871bSPeter Wemm 		}
350b8ba871bSPeter Wemm 
351b8ba871bSPeter Wemm 	/*
352b8ba871bSPeter Wemm 	 * If we didn't move, we must be at EOF.
353b8ba871bSPeter Wemm 	 *
354b8ba871bSPeter Wemm 	 * !!!
355b8ba871bSPeter Wemm 	 * That's okay for motion commands, however.
356b8ba871bSPeter Wemm 	 */
357b8ba871bSPeter Wemm ret:	if (!ISMOTION(vp) &&
358b8ba871bSPeter Wemm 	    cs.cs_lno == vp->m_start.lno && cs.cs_cno == vp->m_start.cno) {
359b8ba871bSPeter Wemm 		v_eof(sp, &vp->m_start);
360b8ba871bSPeter Wemm 		return (1);
361b8ba871bSPeter Wemm 	}
362b8ba871bSPeter Wemm 
363b8ba871bSPeter Wemm 	/* Set the end of the range for motion commands. */
364b8ba871bSPeter Wemm 	vp->m_stop.lno = cs.cs_lno;
365b8ba871bSPeter Wemm 	vp->m_stop.cno = cs.cs_cno;
366b8ba871bSPeter Wemm 
367b8ba871bSPeter Wemm 	/*
368b8ba871bSPeter Wemm 	 * Non-motion commands move to the end of the range.
369b8ba871bSPeter Wemm 	 * Delete and yank stay at the start, ignore others.
370b8ba871bSPeter Wemm 	 */
371b8ba871bSPeter Wemm 	vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
372b8ba871bSPeter Wemm 	return (0);
373b8ba871bSPeter Wemm }
374b8ba871bSPeter Wemm 
375b8ba871bSPeter Wemm /*
376b8ba871bSPeter Wemm  * v_WordB -- [count]B
377b8ba871bSPeter Wemm  *	Move backward a bigword at a time.
378b8ba871bSPeter Wemm  *
379b8ba871bSPeter Wemm  * PUBLIC: int v_wordB(SCR *, VICMD *);
380b8ba871bSPeter Wemm  */
381b8ba871bSPeter Wemm int
v_wordB(SCR * sp,VICMD * vp)382b8ba871bSPeter Wemm v_wordB(SCR *sp, VICMD *vp)
383b8ba871bSPeter Wemm {
384b8ba871bSPeter Wemm 	return (bword(sp, vp, BIGWORD));
385b8ba871bSPeter Wemm }
386b8ba871bSPeter Wemm 
387b8ba871bSPeter Wemm /*
388b8ba871bSPeter Wemm  * v_wordb -- [count]b
389b8ba871bSPeter Wemm  *	Move backward a word at a time.
390b8ba871bSPeter Wemm  *
391b8ba871bSPeter Wemm  * PUBLIC: int v_wordb(SCR *, VICMD *);
392b8ba871bSPeter Wemm  */
393b8ba871bSPeter Wemm int
v_wordb(SCR * sp,VICMD * vp)394b8ba871bSPeter Wemm v_wordb(SCR *sp, VICMD *vp)
395b8ba871bSPeter Wemm {
396b8ba871bSPeter Wemm 	return (bword(sp, vp, LITTLEWORD));
397b8ba871bSPeter Wemm }
398b8ba871bSPeter Wemm 
399b8ba871bSPeter Wemm /*
400b8ba871bSPeter Wemm  * bword --
401b8ba871bSPeter Wemm  *	Move backward by words.
402b8ba871bSPeter Wemm  */
403b8ba871bSPeter Wemm static int
bword(SCR * sp,VICMD * vp,enum which type)404b8ba871bSPeter Wemm bword(SCR *sp, VICMD *vp, enum which type)
405b8ba871bSPeter Wemm {
406b8ba871bSPeter Wemm 	enum { INWORD, NOTWORD } state;
407b8ba871bSPeter Wemm 	VCS cs;
408b8ba871bSPeter Wemm 	u_long cnt;
409b8ba871bSPeter Wemm 
410b8ba871bSPeter Wemm 	cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
411b8ba871bSPeter Wemm 	cs.cs_lno = vp->m_start.lno;
412b8ba871bSPeter Wemm 	cs.cs_cno = vp->m_start.cno;
413b8ba871bSPeter Wemm 	if (cs_init(sp, &cs))
414b8ba871bSPeter Wemm 		return (1);
415b8ba871bSPeter Wemm 
416b8ba871bSPeter Wemm 	/*
417b8ba871bSPeter Wemm 	 * !!!
418b8ba871bSPeter Wemm 	 * If in whitespace, or the previous character is whitespace, move
419b8ba871bSPeter Wemm 	 * past it.  (This doesn't count as a word move.)  Stay at the
420b8ba871bSPeter Wemm 	 * character before the current one, it sets word "state" for the
421b8ba871bSPeter Wemm 	 * 'b' command.
422b8ba871bSPeter Wemm 	 */
423b8ba871bSPeter Wemm 	if (cs.cs_flags == 0 && !ISBLANK(cs.cs_ch)) {
424b8ba871bSPeter Wemm 		if (cs_prev(sp, &cs))
425b8ba871bSPeter Wemm 			return (1);
426b8ba871bSPeter Wemm 		if (cs.cs_flags == 0 && !ISBLANK(cs.cs_ch))
427b8ba871bSPeter Wemm 			goto start;
428b8ba871bSPeter Wemm 	}
429b8ba871bSPeter Wemm 	if (cs_bblank(sp, &cs))
430b8ba871bSPeter Wemm 		return (1);
431b8ba871bSPeter Wemm 
432b8ba871bSPeter Wemm 	/*
433b8ba871bSPeter Wemm 	 * Cyclically move to the beginning of the previous word -- this
434b8ba871bSPeter Wemm 	 * involves skipping over word characters and then any trailing
435b8ba871bSPeter Wemm 	 * non-word characters.  Note, for the 'b' command, the definition
436b8ba871bSPeter Wemm 	 * of a word keeps switching.
437b8ba871bSPeter Wemm 	 */
438b8ba871bSPeter Wemm start:	if (type == BIGWORD)
439b8ba871bSPeter Wemm 		while (cnt--) {
440b8ba871bSPeter Wemm 			for (;;) {
441b8ba871bSPeter Wemm 				if (cs_prev(sp, &cs))
442b8ba871bSPeter Wemm 					return (1);
443b8ba871bSPeter Wemm 				if (cs.cs_flags == CS_SOF)
444b8ba871bSPeter Wemm 					goto ret;
445b8ba871bSPeter Wemm 				if (cs.cs_flags != 0 || ISBLANK(cs.cs_ch))
446b8ba871bSPeter Wemm 					break;
447b8ba871bSPeter Wemm 			}
448b8ba871bSPeter Wemm 			/*
449b8ba871bSPeter Wemm 			 * When we reach the end of the word before the last
450b8ba871bSPeter Wemm 			 * word, we're done.  If we changed state, move forward
451b8ba871bSPeter Wemm 			 * one to the end of the next word.
452b8ba871bSPeter Wemm 			 */
453b8ba871bSPeter Wemm 			if (cnt == 0) {
454b8ba871bSPeter Wemm 				if (cs.cs_flags == 0 && cs_next(sp, &cs))
455b8ba871bSPeter Wemm 					return (1);
456b8ba871bSPeter Wemm 				break;
457b8ba871bSPeter Wemm 			}
458b8ba871bSPeter Wemm 
459b8ba871bSPeter Wemm 			/* Eat whitespace characters. */
460b8ba871bSPeter Wemm 			if (cs_bblank(sp, &cs))
461b8ba871bSPeter Wemm 				return (1);
462b8ba871bSPeter Wemm 			if (cs.cs_flags == CS_SOF)
463b8ba871bSPeter Wemm 				goto ret;
464b8ba871bSPeter Wemm 		}
465b8ba871bSPeter Wemm 	else
466b8ba871bSPeter Wemm 		while (cnt--) {
467b8ba871bSPeter Wemm 			state = cs.cs_flags == 0 &&
468b8ba871bSPeter Wemm 			    inword(cs.cs_ch) ? INWORD : NOTWORD;
469b8ba871bSPeter Wemm 			for (;;) {
470b8ba871bSPeter Wemm 				if (cs_prev(sp, &cs))
471b8ba871bSPeter Wemm 					return (1);
472b8ba871bSPeter Wemm 				if (cs.cs_flags == CS_SOF)
473b8ba871bSPeter Wemm 					goto ret;
474b8ba871bSPeter Wemm 				if (cs.cs_flags != 0 || ISBLANK(cs.cs_ch))
475b8ba871bSPeter Wemm 					break;
476b8ba871bSPeter Wemm 				if (state == INWORD) {
477b8ba871bSPeter Wemm 					if (!inword(cs.cs_ch))
478b8ba871bSPeter Wemm 						break;
479b8ba871bSPeter Wemm 				} else
480b8ba871bSPeter Wemm 					if (inword(cs.cs_ch))
481b8ba871bSPeter Wemm 						break;
482b8ba871bSPeter Wemm 			}
483b8ba871bSPeter Wemm 			/* See comment above. */
484b8ba871bSPeter Wemm 			if (cnt == 0) {
485b8ba871bSPeter Wemm 				if (cs.cs_flags == 0 && cs_next(sp, &cs))
486b8ba871bSPeter Wemm 					return (1);
487b8ba871bSPeter Wemm 				break;
488b8ba871bSPeter Wemm 			}
489b8ba871bSPeter Wemm 
490b8ba871bSPeter Wemm 			/* Eat whitespace characters. */
491b8ba871bSPeter Wemm 			if (cs.cs_flags != 0 || ISBLANK(cs.cs_ch))
492b8ba871bSPeter Wemm 				if (cs_bblank(sp, &cs))
493b8ba871bSPeter Wemm 					return (1);
494b8ba871bSPeter Wemm 			if (cs.cs_flags == CS_SOF)
495b8ba871bSPeter Wemm 				goto ret;
496b8ba871bSPeter Wemm 		}
497b8ba871bSPeter Wemm 
498b8ba871bSPeter Wemm 	/* If we didn't move, we must be at SOF. */
499b8ba871bSPeter Wemm ret:	if (cs.cs_lno == vp->m_start.lno && cs.cs_cno == vp->m_start.cno) {
500b8ba871bSPeter Wemm 		v_sof(sp, &vp->m_start);
501b8ba871bSPeter Wemm 		return (1);
502b8ba871bSPeter Wemm 	}
503b8ba871bSPeter Wemm 
504b8ba871bSPeter Wemm 	/* Set the end of the range for motion commands. */
505b8ba871bSPeter Wemm 	vp->m_stop.lno = cs.cs_lno;
506b8ba871bSPeter Wemm 	vp->m_stop.cno = cs.cs_cno;
507b8ba871bSPeter Wemm 
508b8ba871bSPeter Wemm 	/*
509b8ba871bSPeter Wemm 	 * All commands move to the end of the range.  Motion commands
510b8ba871bSPeter Wemm 	 * adjust the starting point to the character before the current
511b8ba871bSPeter Wemm 	 * one.
512b8ba871bSPeter Wemm 	 *
513b8ba871bSPeter Wemm 	 * !!!
514b8ba871bSPeter Wemm 	 * The historic vi didn't get this right -- the `yb' command yanked
515b8ba871bSPeter Wemm 	 * the right stuff and even updated the cursor value, but the cursor
516b8ba871bSPeter Wemm 	 * was not actually updated on the screen.
517b8ba871bSPeter Wemm 	 */
518b8ba871bSPeter Wemm 	vp->m_final = vp->m_stop;
519b8ba871bSPeter Wemm 	if (ISMOTION(vp))
520b8ba871bSPeter Wemm 		--vp->m_start.cno;
521b8ba871bSPeter Wemm 	return (0);
522b8ba871bSPeter Wemm }
523b8ba871bSPeter Wemm