1 /*
2 * This code contains changes by
3 * Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
4 *
5 * Conditions 1, 2, and 4 and the no-warranty notice below apply
6 * to these changes.
7 *
8 *
9 * Copyright (c) 1980, 1993
10 * The Regents of the University of California. All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by the University of
23 * California, Berkeley and its contributors.
24 * 4. Neither the name of the University nor the names of its contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
39 *
40 *
41 * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * Redistributions of source code and documentation must retain the
47 * above copyright notice, this list of conditions and the following
48 * disclaimer.
49 * Redistributions in binary form must reproduce the above copyright
50 * notice, this list of conditions and the following disclaimer in the
51 * documentation and/or other materials provided with the distribution.
52 * All advertising materials mentioning features or use of this software
53 * must display the following acknowledgement:
54 * This product includes software developed or owned by Caldera
55 * International, Inc.
56 * Neither the name of Caldera International, Inc. nor the names of
57 * other contributors may be used to endorse or promote products
58 * derived from this software without specific prior written permission.
59 *
60 * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
61 * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
62 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
63 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
64 * ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE
65 * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
66 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
67 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
68 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
69 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
70 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
71 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
72 */
73
74 #ifndef lint
75 #ifdef DOSCCS
76 static char sccsid[] = "@(#)ex_voper.c 1.27 (gritter) 2/15/05";
77 #endif
78 #endif
79
80 /* from ex_voper.c 7.4 (Berkeley) 6/7/85 */
81
82 #include "ex.h"
83 #include "ex_re.h"
84 #include "ex_tty.h"
85 #include "ex_vis.h"
86
87 #ifdef MB
88 static int
cblank(char * cp)89 cblank(char *cp)
90 {
91 if (mb_cur_max > 1 && *cp & 0200) {
92 int c;
93 return mbtowi(&c, cp, mb_cur_max) > 0 && iswspace(c);
94 } else
95 return isspace(*cp&0377);
96 }
97 #define blank() cblank(wcursor)
98 #else /* !MB */
99 #define cblank(cp) isspace(*cp&0377)
100 #define blank() xisspace(wcursor[0]&TRIM)
101 #endif /* !MB */
102 #define forbid(a) if (a) goto errlab;
103
104 cell vscandir[2] = { '/', 0 };
105
106 /*
107 * Decode an operator/operand type command.
108 * Eventually we switch to an operator subroutine in ex_vops.c.
109 * The work here is setting up a function variable to point
110 * to the routine we want, and manipulation of the variables
111 * wcursor and wdot, which mark the other end of the affected
112 * area. If wdot is zero, then the current line is the other end,
113 * and if wcursor is zero, then the first non-blank location of the
114 * other line is implied.
115 */
116 void
operate(register int c,register int cnt)117 operate(register int c, register int cnt)
118 {
119 register int i = 0;
120 void (*moveop)(int), (*deleteop)(int);
121 void (*opf)(int);
122 bool subop = 0;
123 char *oglobp, *ocurs;
124 register line *addr;
125 line *odot;
126 static int lastFKND, lastFCHR;
127 short d;
128 cell nullcell[1], qmarkcell[2], slashcell[2];
129
130 CLOBBGRD(opf);
131 CLOBBGRD(d);
132 qmarkcell[0] = '?';
133 slashcell[0] = '/';
134 nullcell[0] = qmarkcell[1] = slashcell[1] = 0;
135 moveop = vmove, deleteop = vdelete;
136 wcursor = cursor;
137 wdot = NOLINE;
138 notecnt = 0;
139 dir = 1;
140 switch (c) {
141
142 /*
143 * d delete operator.
144 */
145 case 'd':
146 moveop = vdelete;
147 deleteop = (void (*)(int))beep;
148 break;
149
150 /*
151 * s substitute characters, like c\040, i.e. change space.
152 */
153 case 's':
154 ungetkey(' ');
155 subop++;
156 /* fall into ... */
157
158 /*
159 * c Change operator.
160 */
161 case 'c':
162 if (c == 'c' && workcmd[0] == 'C' || workcmd[0] == 'S')
163 subop++;
164 moveop = vchange;
165 deleteop = (void (*)(int))beep;
166 break;
167
168 /*
169 * ! Filter through a UNIX command.
170 */
171 case '!':
172 moveop = vfilter;
173 deleteop = (void (*)(int))beep;
174 break;
175
176 /*
177 * y Yank operator. Place specified text so that it
178 * can be put back with p/P. Also yanks to named buffers.
179 */
180 case 'y':
181 moveop = vyankit;
182 deleteop = (void (*)(int))beep;
183 break;
184
185 /*
186 * = Reformat operator (for LISP).
187 */
188 #ifdef LISPCODE
189 case '=':
190 forbid(!value(LISP));
191 /* fall into ... */
192 #endif
193
194 /*
195 * > Right shift operator.
196 * < Left shift operator.
197 */
198 case '<':
199 case '>':
200 moveop = vshftop;
201 deleteop = (void (*)(int))beep;
202 break;
203
204 /*
205 * r Replace character under cursor with single following
206 * character.
207 */
208 case 'r':
209 vmacchng(1);
210 vrep(cnt);
211 return;
212
213 default:
214 goto nocount;
215 }
216 vmacchng(1);
217 /*
218 * Had an operator, so accept another count.
219 * Multiply counts together.
220 */
221 if (xisdigit(peekkey()) && peekkey() != '0') {
222 cnt *= vgetcnt();
223 Xcnt = cnt;
224 forbid (cnt <= 0);
225 }
226
227 /*
228 * Get next character, mapping it and saving as
229 * part of command for repeat.
230 */
231 c = map(getesc(),arrows);
232 if (c == 0)
233 return;
234 if (!subop)
235 *lastcp++ = c;
236 nocount:
237 opf = moveop;
238 switch (c) {
239
240 /*
241 * b Back up a word.
242 * B Back up a word, liberal definition.
243 */
244 case 'b':
245 case 'B':
246 dir = -1;
247 /* fall into ... */
248
249 /*
250 * w Forward a word.
251 * W Forward a word, liberal definition.
252 */
253 case 'W':
254 case 'w':
255 wdkind = c & ' ';
256 forbid(llfind(2, cnt, opf, 0) < 0);
257 vmoving = 0;
258 break;
259
260 /*
261 * E to end of following blank/nonblank word
262 */
263 case 'E':
264 wdkind = 0;
265 goto ein;
266
267 /*
268 * e To end of following word.
269 */
270 case 'e':
271 wdkind = 1;
272 ein:
273 forbid(llfind(3, cnt - 1, opf, 0) < 0);
274 vmoving = 0;
275 break;
276
277 /*
278 * ( Back an s-expression.
279 */
280 case '(':
281 dir = -1;
282 /* fall into... */
283
284 /*
285 * ) Forward an s-expression.
286 */
287 case ')':
288 forbid(llfind(0, cnt, opf, (line *) 0) < 0);
289 markDOT();
290 break;
291
292 /*
293 * { Back an s-expression, but don't stop on atoms.
294 * In text mode, a paragraph. For C, a balanced set
295 * of {}'s.
296 */
297 case '{':
298 dir = -1;
299 /* fall into... */
300
301 /*
302 * } Forward an s-expression, but don't stop on atoms.
303 * In text mode, back paragraph. For C, back a balanced
304 * set of {}'s.
305 */
306 case '}':
307 forbid(llfind(1, cnt, opf, (line *) 0) < 0);
308 markDOT();
309 break;
310
311 /*
312 * % To matching () or {}. If not at ( or { scan for
313 * first such after cursor on this line.
314 */
315 case '%':
316 vsave();
317 i = lmatchp((line *) 0);
318 #ifdef TRACE
319 if (trace)
320 fprintf(trace, "after lmatchp in %, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
321 #endif
322 getDOT();
323 forbid(!i);
324 if (opf != vmove)
325 if (dir > 0)
326 wcursor += skipright(linebuf, wcursor);
327 else
328 cursor += skipright(linebuf, cursor);
329 else
330 markDOT();
331 vmoving = 0;
332 break;
333
334 /*
335 * [ Back to beginning of defun, i.e. an ( in column 1.
336 * For text, back to a section macro.
337 * For C, back to a { in column 1 (~~ beg of function.)
338 */
339 case '[':
340 dir = -1;
341 /* fall into ... */
342
343 /*
344 * ] Forward to next defun, i.e. a ( in column 1.
345 * For text, forward section.
346 * For C, forward to a } in column 1 (if delete or such)
347 * or if a move to a { in column 1.
348 */
349 case ']':
350 if (!vglobp)
351 forbid(getkey() != c);
352 forbid (Xhadcnt);
353 vsave();
354 i = lbrack(c, opf);
355 getDOT();
356 forbid(!i);
357 markDOT();
358 if (ospeed > B300)
359 hold |= HOLDWIG;
360 break;
361
362 /*
363 * , Invert last find with f F t or T, like inverse
364 * of ;.
365 */
366 case ',':
367 forbid (lastFKND == 0);
368 c = xisupper(lastFKND&TRIM)
369 ? xtolower(lastFKND&TRIM) : xtoupper(lastFKND&TRIM);
370 i = lastFCHR;
371 if (vglobp == 0)
372 vglobp = nullcell;
373 subop++;
374 goto nocount;
375
376 /*
377 * 0 To beginning of real line.
378 */
379 case '0':
380 wcursor = linebuf;
381 vmoving = 0;
382 break;
383
384 /*
385 * ; Repeat last find with f F t or T.
386 */
387 case ';':
388 forbid (lastFKND == 0);
389 c = lastFKND;
390 i = lastFCHR;
391 subop++;
392 goto nocount;
393
394 /*
395 * F Find single character before cursor in current line.
396 * T Like F, but stops before character.
397 */
398 case 'F': /* inverted find */
399 case 'T':
400 dir = -1;
401 /* fall into ... */
402
403 /*
404 * f Find single character following cursor in current line.
405 * t Like f, but stope before character.
406 */
407 case 'f': /* find */
408 case 't':
409 if (!subop) {
410 i = getesc();
411 if (i == 0)
412 return;
413 *lastcp++ = i;
414 }
415 if (vglobp == 0)
416 lastFKND = c, lastFCHR = i;
417 for (; cnt > 0; cnt--)
418 forbid (find(i) == 0);
419 vmoving = 0;
420 switch (c) {
421
422 case 'T':
423 wcursor += skipright(linebuf, wcursor);
424 break;
425
426 case 't':
427 wcursor += skipleft(linebuf, wcursor);
428 case 'f':
429 fixup:
430 if (moveop != vmove)
431 wcursor += skipright(linebuf, wcursor);
432 break;
433 }
434 break;
435
436 /*
437 * | Find specified print column in current line.
438 */
439 case '|':
440 if (Pline == numbline)
441 cnt += 8;
442 vmovcol = cnt;
443 vmoving = 1;
444 wcursor = vfindcol(cnt);
445 break;
446
447 /*
448 * ^ To beginning of non-white space on line.
449 */
450 case '^':
451 wcursor = vskipwh(linebuf);
452 vmoving = 0;
453 break;
454
455 /*
456 * $ To end of line.
457 */
458 case '$':
459 if (opf == vmove) {
460 vmoving = 1;
461 vmovcol = 20000;
462 } else
463 vmoving = 0;
464 if (cnt > 1) {
465 if (opf == vmove) {
466 wcursor = 0;
467 cnt--;
468 } else
469 wcursor = linebuf;
470 /* This is wrong at EOF */
471 wdot = dot + cnt;
472 break;
473 }
474 if (linebuf[0]) {
475 wcursor = strend(linebuf) - 1;
476 goto fixup;
477 }
478 wcursor = linebuf;
479 break;
480
481 /*
482 * h Back a character.
483 * ^H Back a character.
484 */
485 case 'h':
486 case CTRL('h'):
487 dir = -1;
488 /* fall into ... */
489
490 /*
491 * space Forward a character.
492 */
493 case 'l':
494 case ' ':
495 forbid (margin() || opf == vmove && edge());
496 while (cnt > 0 && !margin()) {
497 wcursor += dir>0 ? skipright(linebuf, wcursor) :
498 skipleft(linebuf, wcursor);
499 cnt--;
500 }
501 if (margin() && opf == vmove || wcursor < linebuf)
502 wcursor -= dir;
503 vmoving = 0;
504 break;
505
506 /*
507 * D Delete to end of line, short for d$.
508 */
509 case 'D':
510 cnt = INF;
511 goto deleteit;
512
513 /*
514 * X Delete character before cursor.
515 */
516 case 'X':
517 dir = -1;
518 /* fall into ... */
519 deleteit:
520 /*
521 * x Delete character at cursor, leaving cursor where it is.
522 */
523 case 'x':
524 if (margin())
525 goto errlab;
526 vmacchng(1);
527 while (cnt > 0 && !margin()) {
528 wcursor += dir > 0 ? skipright(linebuf, wcursor) :
529 skipleft(linebuf, wcursor);
530 cnt--;
531 }
532 opf = deleteop;
533 vmoving = 0;
534 break;
535
536 default:
537 /*
538 * Stuttered operators are equivalent to the operator on
539 * a line, thus turn dd into d_.
540 */
541 if (opf == vmove || c != workcmd[0]) {
542 errlab:
543 beep();
544 vmacp = 0;
545 return;
546 }
547 /* fall into ... */
548
549 /*
550 * _ Target for a line or group of lines.
551 * Stuttering is more convenient; this is mostly
552 * for aesthetics.
553 */
554 case '_':
555 wdot = dot + cnt - 1;
556 vmoving = 0;
557 wcursor = 0;
558 break;
559
560 /*
561 * H To first, home line on screen.
562 * Count is for count'th line rather than first.
563 */
564 case 'H':
565 wdot = (dot - vcline) + cnt - 1;
566 if (opf == vmove)
567 markit(wdot);
568 vmoving = 0;
569 wcursor = 0;
570 break;
571
572 /*
573 * - Backwards lines, to first non-white character.
574 */
575 case '-':
576 wdot = dot - cnt;
577 vmoving = 0;
578 wcursor = 0;
579 break;
580
581 /*
582 * ^P To previous line same column. Ridiculous on the
583 * console of the VAX since it puts console in LSI mode.
584 */
585 case 'k':
586 case CTRL('p'):
587 wdot = dot - cnt;
588 if (vmoving == 0)
589 vmoving = 1, vmovcol = lcolumn(cursor);
590 wcursor = 0;
591 break;
592
593 /*
594 * L To last line on screen, or count'th line from the
595 * bottom.
596 */
597 case 'L':
598 wdot = dot + vcnt - vcline - cnt;
599 if (opf == vmove)
600 markit(wdot);
601 vmoving = 0;
602 wcursor = 0;
603 break;
604
605 /*
606 * M To the middle of the screen.
607 */
608 case 'M':
609 wdot = dot + ((vcnt + 1) / 2) - vcline - 1;
610 if (opf == vmove)
611 markit(wdot);
612 vmoving = 0;
613 wcursor = 0;
614 break;
615
616 /*
617 * + Forward line, to first non-white.
618 *
619 * CR Convenient synonym for +.
620 */
621 case '+':
622 case CR:
623 wdot = dot + cnt;
624 vmoving = 0;
625 wcursor = 0;
626 break;
627
628 /*
629 * ^N To next line, same column if possible.
630 *
631 * LF Linefeed is a convenient synonym for ^N.
632 */
633 case CTRL('n'):
634 case 'j':
635 case NL:
636 wdot = dot + cnt;
637 if (vmoving == 0)
638 vmoving = 1, vmovcol = lcolumn(cursor);
639 wcursor = 0;
640 break;
641
642 /*
643 * n Search to next match of current pattern.
644 */
645 case 'n':
646 vglobp = vscandir;
647 c = *vglobp++;
648 goto nocount;
649
650 /*
651 * N Like n but in reverse direction.
652 */
653 case 'N':
654 vglobp = vscandir[0] == '/' ? qmarkcell : slashcell;
655 c = *vglobp++;
656 goto nocount;
657
658 /*
659 * ' Return to line specified by following mark,
660 * first white position on line.
661 *
662 * ` Return to marked line at remembered column.
663 */
664 case '\'':
665 case '`':
666 d = c;
667 c = getesc();
668 if (c == 0)
669 return;
670 c = markreg(c);
671 forbid (c == 0);
672 wdot = getmark(c);
673 forbid (wdot == NOLINE);
674 forbid (Xhadcnt);
675 vmoving = 0;
676 wcursor = d == '`' ? ncols[c - 'a'] : 0;
677 if (opf == vmove && (wdot != dot || (d == '`' && wcursor != cursor)))
678 markDOT();
679 if (wcursor) {
680 vsave();
681 getline(*wdot);
682 if (wcursor > strend(linebuf))
683 wcursor = 0;
684 getDOT();
685 }
686 if (ospeed > B300)
687 hold |= HOLDWIG;
688 break;
689
690 /*
691 * G Goto count'th line, or last line if no count
692 * given.
693 */
694 case 'G':
695 if (!Xhadcnt)
696 cnt = lineDOL();
697 wdot = zero + cnt;
698 forbid (wdot < one || wdot > dol);
699 if (opf == vmove)
700 markit(wdot);
701 vmoving = 0;
702 wcursor = 0;
703 break;
704
705 /*
706 * / Scan forward for following re.
707 * ? Scan backward for following re.
708 */
709 case '/':
710 case '?':
711 forbid (Xhadcnt);
712 vsave();
713 ocurs = cursor;
714 odot = dot;
715 wcursor = 0;
716 if (readecho(c))
717 return;
718 if (!vglobp)
719 vscandir[0] = genbuf[0];
720 oglobp = globp;
721 CP(vutmp, genbuf);
722 globp = vutmp;
723 d = peekc;
724 fromsemi:
725 ungetchar(0);
726 fixech();
727 CATCH
728 addr = address(cursor);
729 ONERR
730 slerr:
731 globp = oglobp;
732 dot = odot;
733 cursor = ocurs;
734 ungetchar(d);
735 splitw = 0;
736 vclean();
737 vjumpto(dot, ocurs, 0);
738 return;
739 ENDCATCH
740 if (globp == 0)
741 globp = "";
742 else if (peekc)
743 --globp;
744 if (*globp == ';') {
745 /* /foo/;/bar/ */
746 globp++;
747 dot = addr;
748 cursor = loc1;
749 goto fromsemi;
750 }
751 dot = odot;
752 ungetchar(d);
753 c = 0;
754 if (*globp == 'z')
755 globp++, c = '\n';
756 if (any(*globp, "^+-."))
757 c = *globp++;
758 i = 0;
759 while (xisdigit(*globp&TRIM))
760 i = i * 10 + *globp++ - '0';
761 if (any(*globp, "^+-."))
762 c = *globp++;
763 if (*globp) {
764 /* random junk after the pattern */
765 beep();
766 goto slerr;
767 }
768 globp = oglobp;
769 splitw = 0;
770 vmoving = 0;
771 wcursor = loc1;
772 if (i != 0)
773 vsetsiz(i);
774 if (opf == vmove) {
775 if (state == ONEOPEN || state == HARDOPEN)
776 outline = destline = WBOT;
777 if (addr != dot || loc1 != cursor)
778 markDOT();
779 if (loc1 > linebuf && *loc1 == 0)
780 loc1--;
781 if (c)
782 vjumpto(addr, loc1, c);
783 else {
784 vmoving = 0;
785 if (loc1) {
786 vmoving++;
787 vmovcol = column(loc1);
788 }
789 getDOT();
790 if (state == CRTOPEN && addr != dot)
791 vup1();
792 vupdown(addr - dot, NOSTR);
793 }
794 return;
795 }
796 lastcp[-1] = 'n';
797 getDOT();
798 wdot = addr;
799 break;
800 }
801 /*
802 * Apply.
803 */
804 if (vreg && wdot == 0)
805 wdot = dot;
806 (*opf)(c);
807 wdot = NOLINE;
808 }
809
810 /*
811 * Find single character c, in direction dir from cursor.
812 */
813 int
find(int c)814 find(int c)
815 {
816
817 for(;;) {
818 if (edge())
819 return (0);
820 wcursor += dir>0 ? skipright(linebuf, wcursor) :
821 skipleft(linebuf, wcursor-1);
822 if (samechar(wcursor, c))
823 return (1);
824 }
825 }
826
827 /*
828 * Do a word motion with operator op, and cnt more words
829 * to go after this.
830 */
831 int
word(register void (* op)(int),int cnt)832 word(register void (*op)(int), int cnt)
833 {
834 register int which = 0, i;
835 register char *iwc;
836 register line *iwdot = wdot;
837
838 if (dir == 1) {
839 iwc = wcursor;
840 which = wordch(wcursor);
841 while (wordof(which, wcursor)) {
842 if (cnt == 1 && op != vmove &&
843 wcursor[i = skipright(linebuf, wcursor)]
844 == 0) {
845 wcursor += i;
846 break;
847 }
848 if (!lnext())
849 return (0);
850 if (wcursor == linebuf)
851 break;
852 }
853 /* Unless last segment of a change skip blanks */
854 if (op != vchange || cnt > 1)
855 while (!margin() && blank())
856 wcursor += skipright(linebuf, wcursor);
857 else
858 if (wcursor == iwc && iwdot == wdot && *iwc)
859 wcursor += skipright(linebuf, wcursor);
860 if (op == vmove && margin()) {
861 if (wcursor == linebuf)
862 wcursor--;
863 else if (!lnext())
864 return (0);
865 }
866 } else {
867 if (!lnext())
868 return (0);
869 while (blank())
870 if (!lnext())
871 return (0);
872 if (!margin()) {
873 which = wordch(wcursor);
874 while (!margin() && wordof(which, wcursor))
875 wcursor--;
876 }
877 if (wcursor < linebuf || !wordof(which, wcursor))
878 wcursor += skipright(linebuf, wcursor);
879 }
880 return (1);
881 }
882
883 /*
884 * To end of word, with operator op and cnt more motions
885 * remaining after this.
886 */
887 void
eend(register void (* op)(int))888 eend(register void (*op)(int))
889 {
890 register int which;
891
892 if (!lnext())
893 return;
894 while (blank())
895 if (!lnext())
896 return;
897 which = wordch(wcursor);
898 while (wordof(which, wcursor)) {
899 if (wcursor[1] == 0) {
900 wcursor++;
901 break;
902 }
903 if (!lnext())
904 return;
905 }
906 if (op != vchange && op != vdelete && wcursor > linebuf)
907 wcursor--;
908 }
909
910 /*
911 * Wordof tells whether the character at *wc is in a word of
912 * kind which (blank/nonblank words are 0, conservative words 1).
913 */
914 int
wordof(int which,register char * wc)915 wordof(int which, register char *wc)
916 {
917
918 if (cblank(wc))
919 return (0);
920 return (!wdkind || wordch(wc) == which);
921 }
922
923 /*
924 * Wordch tells whether character at *wc is a word character
925 * i.e. an alfa, digit, or underscore.
926 */
927 int
wordch(char * wc)928 wordch(char *wc)
929 {
930 int c;
931
932 #ifdef MB
933 if (mb_cur_max > 0 && *wc & 0200) {
934 mbtowi(&c, wc, mb_cur_max);
935 if (c & INVBIT)
936 return 1;
937 } else
938 #endif
939 c = wc[0]&0377;
940 return (xisalnum(c) || c == '_'
941 #ifdef BIT8
942 #ifdef ISO8859_1
943 /*
944 * We consider all ISO 8859-1 characters except for
945 * no-break-space as word characters.
946 */
947 || c&0200 && (!(c"E) && (c&TRIM) != 0240)
948 #endif
949 #endif
950 );
951 }
952
953 /*
954 * Edge tells when we hit the last character in the current line.
955 */
956 int
edge(void)957 edge(void)
958 {
959
960 if (linebuf[0] == 0)
961 return (1);
962 if (dir == 1)
963 return (wcursor[skipright(linebuf, wcursor)] == 0);
964 else
965 return (wcursor == linebuf);
966 }
967
968 /*
969 * Margin tells us when we have fallen off the end of the line.
970 */
971 int
margin(void)972 margin(void)
973 {
974
975 return (wcursor < linebuf || wcursor[0] == 0);
976 }
977