| @@ -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 |
16 | static 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 "; | | 16 | static 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 | |
73 | enum which {BIGWORD, LITTLEWORD}; | | 76 | enum which {BIGWORD, LITTLEWORD}; |
74 | | | 77 | |
75 | static int bword __P((SCR *, VICMD *, enum which)); | | 78 | static int bword __P((SCR *, VICMD *, enum which)); |
76 | static int eword __P((SCR *, VICMD *, enum which)); | | 79 | static int eword __P((SCR *, VICMD *, enum which)); |
77 | static int fword __P((SCR *, VICMD *, enum which)); | | 80 | static 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 | */ |
107 | static int | | 110 | static int |
108 | fword(SCR *sp, VICMD *vp, enum which type) | | 111 | fword(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 | */ |
267 | static int | | 277 | static int |
268 | eword(SCR *sp, VICMD *vp, enum which type) | | 278 | eword(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 | */ |
301 | start: if (type == BIGWORD) | | 312 | start: 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 | */ |
413 | static int | | 430 | static int |
414 | bword(SCR *sp, VICMD *vp, enum which type) | | 431 | bword(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 | */ |
448 | start: if (type == BIGWORD) | | 466 | start: 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; |