Tue Nov 21 07:43:47 2017 UTC ()
A boundary between single- and multi-width chars is regarded as a word boundary.
Suggested on tech-userland@ without any objections.


(rin)
diff -r1.3 -r1.4 src/external/bsd/nvi/dist/common/key.h
diff -r1.3 -r1.4 src/external/bsd/nvi/dist/vi/v_word.c

cvs diff -r1.3 -r1.4 src/external/bsd/nvi/dist/common/key.h (expand / switch to unified diff)

--- src/external/bsd/nvi/dist/common/key.h 2016/01/07 14:07:01 1.3
+++ src/external/bsd/nvi/dist/common/key.h 2017/11/21 07:43:47 1.4
@@ -1,57 +1,59 @@ @@ -1,57 +1,59 @@
1/* $NetBSD: key.h,v 1.3 2016/01/07 14:07:01 christos Exp $ */ 1/* $NetBSD: key.h,v 1.4 2017/11/21 07:43:47 rin Exp $ */
2/*- 2/*-
3 * Copyright (c) 1991, 1993, 1994 3 * Copyright (c) 1991, 1993, 1994
4 * The Regents of the University of California. All rights reserved. 4 * The Regents of the University of California. All rights reserved.
5 * Copyright (c) 1991, 1993, 1994, 1995, 1996 5 * Copyright (c) 1991, 1993, 1994, 1995, 1996
6 * Keith Bostic. All rights reserved. 6 * Keith Bostic. All rights reserved.
7 * 7 *
8 * See the LICENSE file for redistribution information. 8 * See the LICENSE file for redistribution information.
9 * 9 *
10 * Id: key.h,v 10.50 2001/06/28 17:53:58 skimo Exp (Berkeley) Date: 2001/06/28 17:53:58  10 * Id: key.h,v 10.50 2001/06/28 17:53:58 skimo Exp (Berkeley) Date: 2001/06/28 17:53:58
11 */ 11 */
12 12
13#include "multibyte.h" 13#include "multibyte.h"
14 14
15#ifdef USE_WIDECHAR 15#ifdef USE_WIDECHAR
16#define FILE2INT5(sp,buf,n,nlen,w,wlen) \ 16#define FILE2INT5(sp,buf,n,nlen,w,wlen) \
17 sp->conv.file2int(sp, n, nlen, &buf, &wlen, &w) 17 sp->conv.file2int(sp, n, nlen, &buf, &wlen, &w)
18#define INT2FILE(sp,w,wlen,n,nlen) \ 18#define INT2FILE(sp,w,wlen,n,nlen) \
19 sp->conv.int2file(sp, w, wlen, &sp->wp->cw, &nlen, &n) 19 sp->conv.int2file(sp, w, wlen, &sp->wp->cw, &nlen, &n)
20#define CHAR2INT5(sp,buf,n,nlen,w,wlen) \ 20#define CHAR2INT5(sp,buf,n,nlen,w,wlen) \
21 sp->conv.sys2int(sp, n, nlen, &buf, &wlen, &w) 21 sp->conv.sys2int(sp, n, nlen, &buf, &wlen, &w)
22#define INT2CHAR(sp,w,wlen,n,nlen) \ 22#define INT2CHAR(sp,w,wlen,n,nlen) \
23 sp->conv.int2sys(sp, w, wlen, &sp->wp->cw, &nlen, &n) 23 sp->conv.int2sys(sp, w, wlen, &sp->wp->cw, &nlen, &n)
24#define INT2SYS(sp,w,wlen,n,nlen) \ 24#define INT2SYS(sp,w,wlen,n,nlen) \
25 sp->conv.int2sys(sp, w, wlen, &sp->wp->cw, &nlen, &n) 25 sp->conv.int2sys(sp, w, wlen, &sp->wp->cw, &nlen, &n)
26#define INPUT2INT5(sp,cw,n,nlen,w,wlen) \ 26#define INPUT2INT5(sp,cw,n,nlen,w,wlen) \
27 sp->conv.input2int(sp, n, nlen, &(cw), &wlen, &w) 27 sp->conv.input2int(sp, n, nlen, &(cw), &wlen, &w)
28#define INTISWIDE(c) (wctob(c) == EOF) /* XXX wrong name */ 28#define INTISWIDE(c) (wctob(c) == EOF) /* XXX wrong name */
29#define CHAR_WIDTH(sp, ch) wcwidth(ch) 29#define CHAR_WIDTH(sp, ch) wcwidth(ch)
 30#define ISMULTIWIDTH(sp, ch) (INTISWIDE(ch) && CHAR_WIDTH(sp, ch) > 1)
30#else 31#else
31#define FILE2INT5(sp,buf,n,nlen,w,wlen) \ 32#define FILE2INT5(sp,buf,n,nlen,w,wlen) \
32 (w = n, wlen = nlen, 0) 33 (w = n, wlen = nlen, 0)
33#define INT2FILE(sp,w,wlen,n,nlen) \ 34#define INT2FILE(sp,w,wlen,n,nlen) \
34 (n = w, nlen = wlen, 0) 35 (n = w, nlen = wlen, 0)
35#define CHAR2INT5(sp,buf,n,nlen,w,wlen) \ 36#define CHAR2INT5(sp,buf,n,nlen,w,wlen) \
36 (w = n, wlen = nlen, 0) 37 (w = n, wlen = nlen, 0)
37#define INT2CHAR(sp,w,wlen,n,nlen) \ 38#define INT2CHAR(sp,w,wlen,n,nlen) \
38 (n = w, nlen = wlen, 0) 39 (n = w, nlen = wlen, 0)
39#define INT2SYS(sp,w,wlen,n,nlen) \ 40#define INT2SYS(sp,w,wlen,n,nlen) \
40 (n = w, nlen = wlen, 0) 41 (n = w, nlen = wlen, 0)
41#define INPUT2INT5(sp,buf,n,nlen,w,wlen) \ 42#define INPUT2INT5(sp,buf,n,nlen,w,wlen) \
42 (w = n, wlen = nlen, 0) 43 (w = n, wlen = nlen, 0)
43#define INTISWIDE(c) 0 44#define INTISWIDE(c) 0
44#define CHAR_WIDTH(sp, ch) 1 45#define CHAR_WIDTH(sp, ch) 1
 46#define ISMULTIWIDTH(sp, ch) 0
45#endif 47#endif
46#define FILE2INT(sp,n,nlen,w,wlen) \ 48#define FILE2INT(sp,n,nlen,w,wlen) \
47 FILE2INT5(sp,sp->wp->cw,n,nlen,w,wlen) 49 FILE2INT5(sp,sp->wp->cw,n,nlen,w,wlen)
48#define CHAR2INT(sp,n,nlen,w,wlen) \ 50#define CHAR2INT(sp,n,nlen,w,wlen) \
49 CHAR2INT5(sp,sp->wp->cw,n,nlen,w,wlen) 51 CHAR2INT5(sp,sp->wp->cw,n,nlen,w,wlen)
50 52
51#define MEMCPYW(to, from, n) \ 53#define MEMCPYW(to, from, n) \
52 memcpy(to, from, (n) * sizeof(CHAR_T)) 54 memcpy(to, from, (n) * sizeof(CHAR_T))
53#define MEMMOVEW(to, from, n) \ 55#define MEMMOVEW(to, from, n) \
54 memmove(to, from, (n) * sizeof(CHAR_T)) 56 memmove(to, from, (n) * sizeof(CHAR_T))
55 57
56/* The maximum number of columns any character can take up on a screen. */ 58/* The maximum number of columns any character can take up on a screen. */
57#define MAX_CHARACTER_COLUMNS 4 59#define MAX_CHARACTER_COLUMNS 4

cvs diff -r1.3 -r1.4 src/external/bsd/nvi/dist/vi/v_word.c (expand / switch to unified diff)

--- src/external/bsd/nvi/dist/vi/v_word.c 2014/01/26 21:43:45 1.3
+++ src/external/bsd/nvi/dist/vi/v_word.c 2017/11/21 07:43:47 1.4
@@ -1,32 +1,32 @@ @@ -1,32 +1,32 @@
1/* $NetBSD: v_word.c,v 1.3 2014/01/26 21:43:45 christos Exp $ */ 1/* $NetBSD: v_word.c,v 1.4 2017/11/21 07:43:47 rin Exp $ */
2/*- 2/*-
3 * Copyright (c) 1992, 1993, 1994 3 * Copyright (c) 1992, 1993, 1994
4 * The Regents of the University of California. All rights reserved. 4 * The Regents of the University of California. All rights reserved.
5 * Copyright (c) 1992, 1993, 1994, 1995, 1996 5 * Copyright (c) 1992, 1993, 1994, 1995, 1996
6 * Keith Bostic. All rights reserved. 6 * Keith Bostic. All rights reserved.
7 * 7 *
8 * See the LICENSE file for redistribution information. 8 * See the LICENSE file for redistribution information.
9 */ 9 */
10 10
11#include "config.h" 11#include "config.h"
12 12
13#include <sys/cdefs.h> 13#include <sys/cdefs.h>
14#if 0 14#if 0
15#ifndef lint 15#ifndef lint
16static const char sccsid[] = "Id: v_word.c,v 10.6 2001/06/25 15:19:36 skimo Exp (Berkeley) Date: 2001/06/25 15:19:36 "; 16static const char sccsid[] = "Id: v_word.c,v 10.6 2001/06/25 15:19:36 skimo Exp (Berkeley) Date: 2001/06/25 15:19:36 ";
17#endif /* not lint */ 17#endif /* not lint */
18#else 18#else
19__RCSID("$NetBSD: v_word.c,v 1.3 2014/01/26 21:43:45 christos Exp $"); 19__RCSID("$NetBSD: v_word.c,v 1.4 2017/11/21 07:43:47 rin Exp $");
20#endif 20#endif
21 21
22#include <sys/types.h> 22#include <sys/types.h>
23#include <sys/queue.h> 23#include <sys/queue.h>
24#include <sys/time.h> 24#include <sys/time.h>
25 25
26#include <bitstring.h> 26#include <bitstring.h>
27#include <ctype.h> 27#include <ctype.h>
28#include <limits.h> 28#include <limits.h>
29#include <stdio.h> 29#include <stdio.h>
30 30
31#include "../common/common.h" 31#include "../common/common.h"
32#include "vi.h" 32#include "vi.h"
@@ -58,26 +58,29 @@ __RCSID("$NetBSD: v_word.c,v 1.3 2014/01 @@ -58,26 +58,29 @@ __RCSID("$NetBSD: v_word.c,v 1.3 2014/01
58 * single character, and nothing else. Ain't nothin' in here that's easy. 58 * single character, and nothing else. Ain't nothin' in here that's easy.
59 * 59 *
60 * One historic note -- in the original vi, the 'w', 'W' and 'B' commands 60 * One historic note -- in the original vi, the 'w', 'W' and 'B' commands
61 * would treat groups of empty lines as individual words, i.e. the command 61 * would treat groups of empty lines as individual words, i.e. the command
62 * would move the cursor to each new empty line. The 'e' and 'E' commands 62 * would move the cursor to each new empty line. The 'e' and 'E' commands
63 * would treat groups of empty lines as a single word, i.e. the first use 63 * would treat groups of empty lines as a single word, i.e. the first use
64 * would move past the group of lines. The 'b' command would just beep at 64 * would move past the group of lines. The 'b' command would just beep at
65 * you, or, if you did it from the start of the line as part of a motion 65 * you, or, if you did it from the start of the line as part of a motion
66 * command, go absolutely nuts. If the lines contained only white-space 66 * command, go absolutely nuts. If the lines contained only white-space
67 * characters, the 'w' and 'W' commands would just beep at you, and the 'B', 67 * characters, the 'w' and 'W' commands would just beep at you, and the 'B',
68 * 'b', 'E' and 'e' commands would treat the group as a single word, and 68 * 'b', 'E' and 'e' commands would treat the group as a single word, and
69 * the 'B' and 'b' commands will treat the lines as individual words. This 69 * the 'B' and 'b' commands will treat the lines as individual words. This
70 * implementation treats all of these cases as a single white-space word. 70 * implementation treats all of these cases as a single white-space word.
 71 *
 72 * We regard a boundary between single- and multi-width characters as
 73 * a word boundary.
71 */ 74 */
72 75
73enum which {BIGWORD, LITTLEWORD}; 76enum which {BIGWORD, LITTLEWORD};
74 77
75static int bword __P((SCR *, VICMD *, enum which)); 78static int bword __P((SCR *, VICMD *, enum which));
76static int eword __P((SCR *, VICMD *, enum which)); 79static int eword __P((SCR *, VICMD *, enum which));
77static int fword __P((SCR *, VICMD *, enum which)); 80static int fword __P((SCR *, VICMD *, enum which));
78 81
79/* 82/*
80 * v_wordW -- [count]W 83 * v_wordW -- [count]W
81 * Move forward a bigword at a time. 84 * Move forward a bigword at a time.
82 * 85 *
83 * PUBLIC: int v_wordW __P((SCR *, VICMD *)); 86 * PUBLIC: int v_wordW __P((SCR *, VICMD *));
@@ -100,26 +103,27 @@ v_wordw(SCR *sp, VICMD *vp) @@ -100,26 +103,27 @@ v_wordw(SCR *sp, VICMD *vp)
100 return (fword(sp, vp, LITTLEWORD)); 103 return (fword(sp, vp, LITTLEWORD));
101} 104}
102 105
103/* 106/*
104 * fword -- 107 * fword --
105 * Move forward by words. 108 * Move forward by words.
106 */ 109 */
107static int 110static int
108fword(SCR *sp, VICMD *vp, enum which type) 111fword(SCR *sp, VICMD *vp, enum which type)
109{ 112{
110 enum { INWORD, NOTWORD } state; 113 enum { INWORD, NOTWORD } state;
111 VCS cs; 114 VCS cs;
112 u_long cnt; 115 u_long cnt;
 116 int nmw, omw;
113 117
114 cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; 118 cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
115 cs.cs_lno = vp->m_start.lno; 119 cs.cs_lno = vp->m_start.lno;
116 cs.cs_cno = vp->m_start.cno; 120 cs.cs_cno = vp->m_start.cno;
117 if (cs_init(sp, &cs)) 121 if (cs_init(sp, &cs))
118 return (1); 122 return (1);
119 123
120 /* 124 /*
121 * If in white-space: 125 * If in white-space:
122 * If the count is 1, and it's a change command, we're done. 126 * If the count is 1, and it's a change command, we're done.
123 * Else, move to the first non-white-space character, which 127 * Else, move to the first non-white-space character, which
124 * counts as a single word move. If it's a motion command, 128 * counts as a single word move. If it's a motion command,
125 * don't move off the end of the line. 129 * don't move off the end of the line.
@@ -137,64 +141,70 @@ fword(SCR *sp, VICMD *vp, enum which typ @@ -137,64 +141,70 @@ fword(SCR *sp, VICMD *vp, enum which typ
137 if (cs_fblank(sp, &cs)) 141 if (cs_fblank(sp, &cs))
138 return (1); 142 return (1);
139 --cnt; 143 --cnt;
140 } 144 }
141 145
142 /* 146 /*
143 * Cyclically move to the next word -- this involves skipping 147 * Cyclically move to the next word -- this involves skipping
144 * over word characters and then any trailing non-word characters. 148 * over word characters and then any trailing non-word characters.
145 * Note, for the 'w' command, the definition of a word keeps 149 * Note, for the 'w' command, the definition of a word keeps
146 * switching. 150 * switching.
147 */ 151 */
148 if (type == BIGWORD) 152 if (type == BIGWORD)
149 while (cnt--) { 153 while (cnt--) {
 154 nmw = ISMULTIWIDTH(sp, cs.cs_ch);
150 for (;;) { 155 for (;;) {
 156 omw = nmw;
151 if (cs_next(sp, &cs)) 157 if (cs_next(sp, &cs))
152 return (1); 158 return (1);
153 if (cs.cs_flags == CS_EOF) 159 if (cs.cs_flags == CS_EOF)
154 goto ret; 160 goto ret;
155 if (cs.cs_flags != 0 || ISBLANK2(cs.cs_ch)) 161 if (cs.cs_flags != 0 || ISBLANK2(cs.cs_ch) ||
 162 (nmw = ISMULTIWIDTH(sp, cs.cs_ch)) != omw)
156 break; 163 break;
157 } 164 }
158 /* 165 /*
159 * If a motion command and we're at the end of the 166 * If a motion command and we're at the end of the
160 * last word, we're done. Delete and yank eat any 167 * last word, we're done. Delete and yank eat any
161 * trailing blanks, but we don't move off the end 168 * trailing blanks, but we don't move off the end
162 * of the line regardless. 169 * of the line regardless.
163 */ 170 */
164 if (cnt == 0 && ISMOTION(vp)) { 171 if (cnt == 0 && ISMOTION(vp)) {
165 if ((ISCMD(vp->rkp, 'd') || 172 if ((ISCMD(vp->rkp, 'd') ||
166 ISCMD(vp->rkp, 'y')) && 173 ISCMD(vp->rkp, 'y')) &&
167 cs_fspace(sp, &cs)) 174 cs_fspace(sp, &cs))
168 return (1); 175 return (1);
169 break; 176 break;
170 } 177 }
171 178
172 /* Eat whitespace characters. */ 179 /* Eat whitespace characters. */
173 if (cs_fblank(sp, &cs)) 180 if (nmw == omw && cs_fblank(sp, &cs))
174 return (1); 181 return (1);
175 if (cs.cs_flags == CS_EOF) 182 if (cs.cs_flags == CS_EOF)
176 goto ret; 183 goto ret;
177 } 184 }
178 else 185 else
179 while (cnt--) { 186 while (cnt--) {
180 state = cs.cs_flags == 0 && 187 state = cs.cs_flags == 0 &&
181 inword(cs.cs_ch) ? INWORD : NOTWORD; 188 inword(cs.cs_ch) ? INWORD : NOTWORD;
 189 nmw = ISMULTIWIDTH(sp, cs.cs_ch);
182 for (;;) { 190 for (;;) {
 191 omw = nmw;
183 if (cs_next(sp, &cs)) 192 if (cs_next(sp, &cs))
184 return (1); 193 return (1);
185 if (cs.cs_flags == CS_EOF) 194 if (cs.cs_flags == CS_EOF)
186 goto ret; 195 goto ret;
187 if (cs.cs_flags != 0 || ISBLANK2(cs.cs_ch)) 196 if (cs.cs_flags != 0 || ISBLANK2(cs.cs_ch) ||
 197 (nmw = ISMULTIWIDTH(sp, cs.cs_ch)) != omw)
188 break; 198 break;
189 if (state == INWORD) { 199 if (state == INWORD) {
190 if (!inword(cs.cs_ch)) 200 if (!inword(cs.cs_ch))
191 break; 201 break;
192 } else 202 } else
193 if (inword(cs.cs_ch)) 203 if (inword(cs.cs_ch))
194 break; 204 break;
195 } 205 }
196 /* See comment above. */ 206 /* See comment above. */
197 if (cnt == 0 && ISMOTION(vp)) { 207 if (cnt == 0 && ISMOTION(vp)) {
198 if ((ISCMD(vp->rkp, 'd') || 208 if ((ISCMD(vp->rkp, 'd') ||
199 ISCMD(vp->rkp, 'y')) && 209 ISCMD(vp->rkp, 'y')) &&
200 cs_fspace(sp, &cs)) 210 cs_fspace(sp, &cs))
@@ -260,26 +270,27 @@ v_worde(SCR *sp, VICMD *vp) @@ -260,26 +270,27 @@ v_worde(SCR *sp, VICMD *vp)
260 return (eword(sp, vp, LITTLEWORD)); 270 return (eword(sp, vp, LITTLEWORD));
261} 271}
262 272
263/* 273/*
264 * eword -- 274 * eword --
265 * Move forward to the end of the word. 275 * Move forward to the end of the word.
266 */ 276 */
267static int 277static int
268eword(SCR *sp, VICMD *vp, enum which type) 278eword(SCR *sp, VICMD *vp, enum which type)
269{ 279{
270 enum { INWORD, NOTWORD } state; 280 enum { INWORD, NOTWORD } state;
271 VCS cs; 281 VCS cs;
272 u_long cnt; 282 u_long cnt;
 283 int nmw, omw;
273 284
274 cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; 285 cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
275 cs.cs_lno = vp->m_start.lno; 286 cs.cs_lno = vp->m_start.lno;
276 cs.cs_cno = vp->m_start.cno; 287 cs.cs_cno = vp->m_start.cno;
277 if (cs_init(sp, &cs)) 288 if (cs_init(sp, &cs))
278 return (1); 289 return (1);
279 290
280 /* 291 /*
281 * !!! 292 * !!!
282 * If in whitespace, or the next character is whitespace, move past 293 * If in whitespace, or the next character is whitespace, move past
283 * it. (This doesn't count as a word move.) Stay at the character 294 * it. (This doesn't count as a word move.) Stay at the character
284 * past the current one, it sets word "state" for the 'e' command. 295 * past the current one, it sets word "state" for the 'e' command.
285 */ 296 */
@@ -290,61 +301,67 @@ eword(SCR *sp, VICMD *vp, enum which typ @@ -290,61 +301,67 @@ eword(SCR *sp, VICMD *vp, enum which typ
290 goto start; 301 goto start;
291 } 302 }
292 if (cs_fblank(sp, &cs)) 303 if (cs_fblank(sp, &cs))
293 return (1); 304 return (1);
294 305
295 /* 306 /*
296 * Cyclically move to the next word -- this involves skipping 307 * Cyclically move to the next word -- this involves skipping
297 * over word characters and then any trailing non-word characters. 308 * over word characters and then any trailing non-word characters.
298 * Note, for the 'e' command, the definition of a word keeps 309 * Note, for the 'e' command, the definition of a word keeps
299 * switching. 310 * switching.
300 */ 311 */
301start: if (type == BIGWORD) 312start: if (type == BIGWORD)
302 while (cnt--) { 313 while (cnt--) {
 314 nmw = ISMULTIWIDTH(sp, cs.cs_ch);
303 for (;;) { 315 for (;;) {
 316 omw = nmw;
304 if (cs_next(sp, &cs)) 317 if (cs_next(sp, &cs))
305 return (1); 318 return (1);
306 if (cs.cs_flags == CS_EOF) 319 if (cs.cs_flags == CS_EOF)
307 goto ret; 320 goto ret;
308 if (cs.cs_flags != 0 || ISBLANK2(cs.cs_ch)) 321 if (cs.cs_flags != 0 || ISBLANK2(cs.cs_ch) ||
 322 (nmw = ISMULTIWIDTH(sp, cs.cs_ch)) != omw)
309 break; 323 break;
310 } 324 }
311 /* 325 /*
312 * When we reach the start of the word after the last 326 * When we reach the start of the word after the last
313 * word, we're done. If we changed state, back up one 327 * word, we're done. If we changed state, back up one
314 * to the end of the previous word. 328 * to the end of the previous word.
315 */ 329 */
316 if (cnt == 0) { 330 if (cnt == 0) {
317 if (cs.cs_flags == 0 && cs_prev(sp, &cs)) 331 if (cs.cs_flags == 0 && cs_prev(sp, &cs))
318 return (1); 332 return (1);
319 break; 333 break;
320 } 334 }
321 335
322 /* Eat whitespace characters. */ 336 /* Eat whitespace characters. */
323 if (cs_fblank(sp, &cs)) 337 if (nmw == omw && cs_fblank(sp, &cs))
324 return (1); 338 return (1);
325 if (cs.cs_flags == CS_EOF) 339 if (cs.cs_flags == CS_EOF)
326 goto ret; 340 goto ret;
327 } 341 }
328 else 342 else
329 while (cnt--) { 343 while (cnt--) {
330 state = cs.cs_flags == 0 && 344 state = cs.cs_flags == 0 &&
331 inword(cs.cs_ch) ? INWORD : NOTWORD; 345 inword(cs.cs_ch) ? INWORD : NOTWORD;
 346 nmw = ISMULTIWIDTH(sp, cs.cs_ch);
332 for (;;) { 347 for (;;) {
 348 omw = nmw;
333 if (cs_next(sp, &cs)) 349 if (cs_next(sp, &cs))
334 return (1); 350 return (1);
335 if (cs.cs_flags == CS_EOF) 351 if (cs.cs_flags == CS_EOF)
336 goto ret; 352 goto ret;
337 if (cs.cs_flags != 0 || ISBLANK2(cs.cs_ch)) 353 if (cs.cs_flags != 0 || ISBLANK2(cs.cs_ch) ||
 354 (nmw = ISMULTIWIDTH(sp, cs.cs_ch)) != omw)
338 break; 355 break;
339 if (state == INWORD) { 356 if (state == INWORD) {
340 if (!inword(cs.cs_ch)) 357 if (!inword(cs.cs_ch))
341 break; 358 break;
342 } else 359 } else
343 if (inword(cs.cs_ch)) 360 if (inword(cs.cs_ch))
344 break; 361 break;
345 } 362 }
346 /* See comment above. */ 363 /* See comment above. */
347 if (cnt == 0) { 364 if (cnt == 0) {
348 if (cs.cs_flags == 0 && cs_prev(sp, &cs)) 365 if (cs.cs_flags == 0 && cs_prev(sp, &cs))
349 return (1); 366 return (1);
350 break; 367 break;
@@ -406,26 +423,27 @@ v_wordb(SCR *sp, VICMD *vp) @@ -406,26 +423,27 @@ v_wordb(SCR *sp, VICMD *vp)
406 return (bword(sp, vp, LITTLEWORD)); 423 return (bword(sp, vp, LITTLEWORD));
407} 424}
408 425
409/* 426/*
410 * bword -- 427 * bword --
411 * Move backward by words. 428 * Move backward by words.
412 */ 429 */
413static int 430static int
414bword(SCR *sp, VICMD *vp, enum which type) 431bword(SCR *sp, VICMD *vp, enum which type)
415{ 432{
416 enum { INWORD, NOTWORD } state; 433 enum { INWORD, NOTWORD } state;
417 VCS cs; 434 VCS cs;
418 u_long cnt; 435 u_long cnt;
 436 int nmw, omw;
419 437
420 cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; 438 cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
421 cs.cs_lno = vp->m_start.lno; 439 cs.cs_lno = vp->m_start.lno;
422 cs.cs_cno = vp->m_start.cno; 440 cs.cs_cno = vp->m_start.cno;
423 if (cs_init(sp, &cs)) 441 if (cs_init(sp, &cs))
424 return (1); 442 return (1);
425 443
426 /* 444 /*
427 * !!! 445 * !!!
428 * If in whitespace, or the previous character is whitespace, move 446 * If in whitespace, or the previous character is whitespace, move
429 * past it. (This doesn't count as a word move.) Stay at the 447 * past it. (This doesn't count as a word move.) Stay at the
430 * character before the current one, it sets word "state" for the 448 * character before the current one, it sets word "state" for the
431 * 'b' command. 449 * 'b' command.
@@ -437,61 +455,67 @@ bword(SCR *sp, VICMD *vp, enum which typ @@ -437,61 +455,67 @@ bword(SCR *sp, VICMD *vp, enum which typ
437 goto start; 455 goto start;
438 } 456 }
439 if (cs_bblank(sp, &cs)) 457 if (cs_bblank(sp, &cs))
440 return (1); 458 return (1);
441 459
442 /* 460 /*
443 * Cyclically move to the beginning of the previous word -- this 461 * Cyclically move to the beginning of the previous word -- this
444 * involves skipping over word characters and then any trailing 462 * involves skipping over word characters and then any trailing
445 * non-word characters. Note, for the 'b' command, the definition 463 * non-word characters. Note, for the 'b' command, the definition
446 * of a word keeps switching. 464 * of a word keeps switching.
447 */ 465 */
448start: if (type == BIGWORD) 466start: if (type == BIGWORD)
449 while (cnt--) { 467 while (cnt--) {
 468 nmw = ISMULTIWIDTH(sp, cs.cs_ch);
450 for (;;) { 469 for (;;) {
 470 omw = nmw;
451 if (cs_prev(sp, &cs)) 471 if (cs_prev(sp, &cs))
452 return (1); 472 return (1);
453 if (cs.cs_flags == CS_SOF) 473 if (cs.cs_flags == CS_SOF)
454 goto ret; 474 goto ret;
455 if (cs.cs_flags != 0 || ISBLANK2(cs.cs_ch)) 475 if (cs.cs_flags != 0 || ISBLANK2(cs.cs_ch) ||
 476 (nmw = ISMULTIWIDTH(sp, cs.cs_ch)) != omw)
456 break; 477 break;
457 } 478 }
458 /* 479 /*
459 * When we reach the end of the word before the last 480 * When we reach the end of the word before the last
460 * word, we're done. If we changed state, move forward 481 * word, we're done. If we changed state, move forward
461 * one to the end of the next word. 482 * one to the end of the next word.
462 */ 483 */
463 if (cnt == 0) { 484 if (cnt == 0) {
464 if (cs.cs_flags == 0 && cs_next(sp, &cs)) 485 if (cs.cs_flags == 0 && cs_next(sp, &cs))
465 return (1); 486 return (1);
466 break; 487 break;
467 } 488 }
468 489
469 /* Eat whitespace characters. */ 490 /* Eat whitespace characters. */
470 if (cs_bblank(sp, &cs)) 491 if (nmw == omw && cs_bblank(sp, &cs))
471 return (1); 492 return (1);
472 if (cs.cs_flags == CS_SOF) 493 if (cs.cs_flags == CS_SOF)
473 goto ret; 494 goto ret;
474 } 495 }
475 else 496 else
476 while (cnt--) { 497 while (cnt--) {
477 state = cs.cs_flags == 0 && 498 state = cs.cs_flags == 0 &&
478 inword(cs.cs_ch) ? INWORD : NOTWORD; 499 inword(cs.cs_ch) ? INWORD : NOTWORD;
 500 nmw = ISMULTIWIDTH(sp, cs.cs_ch);
479 for (;;) { 501 for (;;) {
 502 omw = nmw;
480 if (cs_prev(sp, &cs)) 503 if (cs_prev(sp, &cs))
481 return (1); 504 return (1);
482 if (cs.cs_flags == CS_SOF) 505 if (cs.cs_flags == CS_SOF)
483 goto ret; 506 goto ret;
484 if (cs.cs_flags != 0 || ISBLANK2(cs.cs_ch)) 507 if (cs.cs_flags != 0 || ISBLANK2(cs.cs_ch) ||
 508 (nmw = ISMULTIWIDTH(sp, cs.cs_ch)) != omw)
485 break; 509 break;
486 if (state == INWORD) { 510 if (state == INWORD) {
487 if (!inword(cs.cs_ch)) 511 if (!inword(cs.cs_ch))
488 break; 512 break;
489 } else 513 } else
490 if (inword(cs.cs_ch)) 514 if (inword(cs.cs_ch))
491 break; 515 break;
492 } 516 }
493 /* See comment above. */ 517 /* See comment above. */
494 if (cnt == 0) { 518 if (cnt == 0) {
495 if (cs.cs_flags == 0 && cs_next(sp, &cs)) 519 if (cs.cs_flags == 0 && cs_next(sp, &cs))
496 return (1); 520 return (1);
497 break; 521 break;