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&QUOTE) && (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