xref: /titanic_44/usr/src/cmd/vi/port/ex_voper.c (revision b7f45089ccbe01bab3d7c7377b49d80d2ae18a69)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright (c) 1981 Regents of the University of California
28  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
29  * Use is subject to license terms.
30  */
31 
32 #pragma ident	"%Z%%M%	%I%	%E% SMI"
33 #include "ex.h"
34 #include "ex_tty.h"
35 #include "ex_vis.h"
36 #include <regexpr.h>
37 #ifndef PRESUNEUC
38 #include <wctype.h>
39 /* Undef putchar/getchar if they're defined. */
40 #ifdef putchar
41 #undef putchar
42 #endif
43 #ifdef getchar
44 #undef getchar
45 #endif
46 #endif /* PRESUNEUC */
47 
48 #ifdef PRESUNEUC
49 #define	blank()		isspace(wcursor[0])
50 #endif /* PRESUNEUC */
51 #define	forbid(a)	if (a) goto errlab;
52 
53 unsigned char	vscandir[2] =	{ '/', 0 };
54 
55 /*
56  * Decode an operator/operand type command.
57  * Eventually we switch to an operator subroutine in ex_vops.c.
58  * The work here is setting up a function variable to point
59  * to the routine we want, and manipulation of the variables
60  * wcursor and wdot, which mark the other end of the affected
61  * area.  If wdot is zero, then the current line is the other end,
62  * and if wcursor is zero, then the first non-blank location of the
63  * other line is implied.
64  */
65 operate(c, cnt)
66 	register int c, cnt;
67 {
68 	register wchar_t i;
69 	int (*moveop)(), (*deleteop)();
70 	register int (*opf)();
71 	bool subop = 0;
72 	unsigned char *oglobp, *ocurs;
73 	register line *addr;
74 	line *odot;
75 	int oc;
76 	static unsigned char lastFKND;
77 	static wchar_t lastFCHR;
78 	short d;
79 /* #ifdef PTR_ADDRESSES */
80 	int mouse_x;
81 	int mouse_y;
82 	int oline;
83 	static int get_addr();
84 /* #endif PTR_ADDRESSES */
85 
86 	moveop = vmove, deleteop = vdelete;
87 	wcursor = cursor;
88 	wdot = NOLINE;
89 	notecnt = 0;
90 	dir = 1;
91 	switch (c) {
92 
93 	/*
94 	 * d		delete operator.
95 	 */
96 	case 'd':
97 		moveop = vdelete;
98 		deleteop = beep;
99 		break;
100 
101 	/*
102 	 * s		substitute characters, like c\040, i.e. change space.
103 	 */
104 	case 's':
105 		ungetkey(' ');
106 		subop++;
107 		/* fall into ... */
108 
109 	/*
110 	 * c		Change operator.
111 	 */
112 	case 'c':
113 		if (c == 'c' && workcmd[0] == 'C' || workcmd[0] == 'S')
114 			subop++;
115 		moveop = vchange;
116 		deleteop = beep;
117 		break;
118 
119 	/*
120 	 * !		Filter through a UNIX command.
121 	 */
122 	case '!':
123 		moveop = vfilter;
124 		deleteop = beep;
125 		break;
126 
127 	/*
128 	 * y		Yank operator.  Place specified text so that it
129 	 *		can be put back with p/P.  Also yanks to named buffers.
130 	 */
131 	case 'y':
132 		moveop = vyankit;
133 		deleteop = beep;
134 		break;
135 
136 	/*
137 	 * =		Reformat operator (for LISP).
138 	 */
139 	case '=':
140 		forbid(!value(vi_LISP));
141 		/* fall into ... */
142 
143 	/*
144 	 * >		Right shift operator.
145 	 * <		Left shift operator.
146 	 */
147 	case '<':
148 	case '>':
149 		moveop = vshftop;
150 		deleteop = beep;
151 		break;
152 
153 	/*
154 	 * r		Replace character under cursor with single following
155 	 *		character.
156 	 */
157 	case 'r':
158 		vmacchng(1);
159 		vrep(cnt);
160 		return;
161 
162 	default:
163 		goto nocount;
164 	}
165 	vmacchng(1);
166 	/*
167 	 * Had an operator, so accept another count.
168 	 * Multiply counts together.
169 	 */
170 	if (isdigit(peekkey()) && peekkey() != '0') {
171 		cnt *= vgetcnt();
172 		Xcnt = cnt;
173 		forbid(cnt <= 0);
174 	}
175 
176 	/*
177 	 * Get next character, mapping it and saving as
178 	 * part of command for repeat.
179 	 */
180 	c = map(getesc(), arrows, 0);
181 	if (c == 0)
182 		return;
183 	if (!subop)
184 		*lastcp++ = c;
185 nocount:
186 	opf = moveop;
187 	switch (c) {
188 
189 /* #ifdef PTR_ADDRESSES */
190 	/*
191 	 * ^X^_		Netty Mouse positioning hack
192 	 * ^X^]
193 	 */
194 	case CTRL('X'):
195 /*
196  *	Read in mouse stuff
197  */
198 		c = getkey();			/* ^_ or ^] */
199 		if ((c != CTRL('_')) && (c != (CTRL(']'))))
200 			break;
201 		getkey();			/* mouse button */
202 		mouse_x = get_addr() + 1;
203 		mouse_y = get_addr() + 1;
204 		if (mouse_y < WTOP)
205 			break;
206 		if (Pline == numbline)
207 			mouse_x -= 8;
208 		if (mouse_x < 0)
209 			mouse_x = 0;
210 		if (mouse_x > WCOLS)
211 			break;
212 /*
213  *	Find the line on the screen
214  */
215 		for (i = 0; i <= WECHO; i++)
216 		{
217 			if (vlinfo[i].vliny >= mouse_y)
218 				break;
219 		}
220 		if (i > WECHO)
221 			break;
222 /*
223  *	Look for lines longer than one line - note  odd case at zero
224  */
225 		if (i)
226 		{
227 			if (vlinfo[i - 1].vdepth > 1)
228 			{
229 				mouse_x += WCOLS * (mouse_y -
230 					(vlinfo[i].vliny -
231 					(vlinfo[i - 1].vdepth - 1)));
232 			}
233 		}
234 		else
235 		{
236 			mouse_x += WCOLS * (mouse_y - 1);
237 		}
238 /*
239  *	Set the line
240  */
241 		vsave();
242 		ocurs = cursor;
243 		odot = dot;
244 		oline = vcline;
245 		operate('H', i);
246 /*
247  *	Set the column
248  */
249 		getDOT();
250 		if (Pline == numbline)
251 			mouse_x += 8;
252 		vmovcol = mouse_x;
253 		vmoving = 1;
254 		wcursor = vfindcol(mouse_x);
255 /*
256  *	Reset everything so that stuff like delete and change work
257  */
258 		wdot = (odot - oline) + i - 1;
259 		cursor = ocurs;
260 		vcline = oline;
261 		dot = odot;
262 		getDOT();
263 		break;
264 /* #endif PTR_ADDRESSES */
265 
266 	/*
267 	 * b		Back up a word.
268 	 * B		Back up a word, liberal definition.
269 	 */
270 	case 'b':
271 	case 'B':
272 		dir = -1;
273 		/* fall into ... */
274 
275 	/*
276 	 * w		Forward a word.
277 	 * W		Forward a word, liberal definition.
278 	 */
279 	case 'W':
280 	case 'w':
281 		wdkind = c & ' ';
282 		forbid(lfind(2, cnt, opf, (line *)0) < 0);
283 		vmoving = 0;
284 		break;
285 
286 	/*
287 	 * E		to end of following blank/nonblank word
288 	 */
289 	case 'E':
290 		wdkind = 0;
291 		goto ein;
292 
293 	/*
294 	 * e		To end of following word.
295 	 */
296 	case 'e':
297 		wdkind = 1;
298 ein:
299 		forbid(lfind(3, cnt - 1, opf, (line *)0) < 0);
300 		vmoving = 0;
301 		break;
302 
303 	/*
304 	 * (		Back an s-expression.
305 	 */
306 	case '(':
307 		dir = -1;
308 		/* fall into... */
309 
310 	/*
311 	 * )		Forward an s-expression.
312 	 */
313 	case ')':
314 		forbid(lfind(0, cnt, opf, (line *) 0) < 0);
315 		markDOT();
316 		break;
317 
318 	/*
319 	 * {		Back an s-expression, but don't stop on atoms.
320 	 *		In text mode, a paragraph.  For C, a balanced set
321 	 *		of {}'s.
322 	 */
323 	case '{':
324 		dir = -1;
325 		/* fall into... */
326 
327 	/*
328 	 * }		Forward an s-expression, but don't stop on atoms.
329 	 *		In text mode, back paragraph.  For C, back a balanced
330 	 *		set of {}'s.
331 	 */
332 	case '}':
333 		forbid(lfind(1, cnt, opf, (line *) 0) < 0);
334 		markDOT();
335 		break;
336 
337 	/*
338 	 * %		To matching () or {}.  If not at ( or { scan for
339 	 *		first such after cursor on this line.
340 	 */
341 	case '%':
342 		vsave();
343 		ocurs = cursor;
344 		odot = wdot = dot;
345 		oglobp = globp;
346 		CATCH
347 			i = lmatchp((line *) 0);
348 		ONERR
349 			globp = oglobp;
350 			dot = wdot = odot;
351 			cursor = ocurs;
352 			splitw = 0;
353 			vclean();
354 			vjumpto(dot, ocurs, 0);
355 			return;
356 		ENDCATCH
357 #ifdef TRACE
358 		if (trace)
359 			fprintf(trace, "after lmatchp in %, dot=%d, wdot=%d, "
360 			    "dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
361 #endif
362 		getDOT();
363 		forbid(!i);
364 		if (opf != vmove)
365 			if (dir > 0)
366 				wcursor++;
367 			else
368 				cursor++;
369 		else
370 			markDOT();
371 		vmoving = 0;
372 		break;
373 
374 	/*
375 	 * [		Back to beginning of defun, i.e. an ( in column 1.
376 	 *		For text, back to a section macro.
377 	 *		For C, back to a { in column 1 (~~ beg of function.)
378 	 */
379 	case '[':
380 		dir = -1;
381 		/* fall into ... */
382 
383 	/*
384 	 * ]		Forward to next defun, i.e. a ( in column 1.
385 	 *		For text, forward section.
386 	 *		For C, forward to a } in column 1 (if delete or such)
387 	 *		or if a move to a { in column 1.
388 	 */
389 	case ']':
390 		if (!vglobp)
391 			forbid(getkey() != c);
392 #ifndef XPG4
393 		forbid(Xhadcnt);
394 #endif
395 		vsave();
396 #ifdef XPG4
397 		if (cnt > 1) {
398 			while (cnt-- > 1) {
399 				i = lbrack(c, opf);
400 				getDOT();
401 				forbid(!i);
402 				markDOT();
403 				if (ospeed > B300)
404 					hold |= HOLDWIG;
405 				(*opf)(c);
406 			}
407 		}
408 #endif /* XPG4 */
409 		i = lbrack(c, opf);
410 		getDOT();
411 		forbid(!i);
412 		markDOT();
413 		if (ospeed > B300)
414 			hold |= HOLDWIG;
415 		break;
416 
417 	/*
418 	 * ,		Invert last find with f F t or T, like inverse
419 	 *		of ;.
420 	 */
421 	case ',':
422 		forbid(lastFKND == 0);
423 		c = isupper(lastFKND) ? tolower(lastFKND) : toupper(lastFKND);
424 		i = lastFCHR;
425 		if (vglobp == 0)
426 			vglobp = (unsigned char *)"";
427 		subop++;
428 		goto nocount;
429 
430 	/*
431 	 * 0		To beginning of real line.
432 	 */
433 	case '0':
434 		wcursor = linebuf;
435 		vmoving = 0;
436 		break;
437 
438 	/*
439 	 * ;		Repeat last find with f F t or T.
440 	 */
441 	case ';':
442 		forbid(lastFKND == 0);
443 		c = lastFKND;
444 		i = lastFCHR;
445 		subop++;
446 		goto nocount;
447 
448 	/*
449 	 * F		Find single character before cursor in current line.
450 	 * T		Like F, but stops before character.
451 	 */
452 	case 'F':	/* inverted find */
453 	case 'T':
454 		dir = -1;
455 		/* fall into ... */
456 
457 	/*
458 	 * f		Find single character following cursor in current line.
459 	 * t		Like f, but stope before character.
460 	 */
461 	case 'f':	/* find */
462 	case 't':
463 		if (!subop) {
464 			int length;
465 			wchar_t wchar;
466 			length = _mbftowc(lastcp, &wchar, getesc, &Peekkey);
467 			if (length <= 0 || wchar == 0) {
468 				beep();
469 				return;
470 			}
471 			i = wchar;
472 			lastcp += length;
473 		}
474 		if (vglobp == 0)
475 			lastFKND = c, lastFCHR = i;
476 		for (; cnt > 0; cnt--)
477 			forbid(find(i) == 0);
478 		vmoving = 0;
479 		switch (c) {
480 
481 		case 'T':
482 			wcursor = nextchr(wcursor);
483 			break;
484 
485 		case 't':
486 			wcursor = lastchr(linebuf, wcursor);
487 		case 'f':
488 fixup:
489 			if (moveop != vmove)
490 				wcursor = nextchr(wcursor);
491 			break;
492 		}
493 		break;
494 
495 	/*
496 	 * |		Find specified print column in current line.
497 	 */
498 	case '|':
499 		if (Pline == numbline)
500 			cnt += 8;
501 		vmovcol = cnt;
502 		vmoving = 1;
503 		wcursor = vfindcol(cnt);
504 		break;
505 
506 	/*
507 	 * ^		To beginning of non-white space on line.
508 	 */
509 	case '^':
510 		wcursor = vskipwh(linebuf);
511 		vmoving = 0;
512 		break;
513 
514 	/*
515 	 * $		To end of line.
516 	 */
517 	case '$':
518 		if (opf == vmove) {
519 			vmoving = 1;
520 			vmovcol = 20000;
521 		} else
522 			vmoving = 0;
523 		if (cnt > 1) {
524 			if (opf == vmove) {
525 				wcursor = 0;
526 				cnt--;
527 			} else
528 				wcursor = linebuf;
529 			/* This is wrong at EOF */
530 			wdot = dot + cnt;
531 			break;
532 		}
533 		if (linebuf[0]) {
534 			wcursor = strend(linebuf);
535 			wcursor = lastchr(linebuf, wcursor);
536 			goto fixup;
537 		}
538 		wcursor = linebuf;
539 		break;
540 
541 	/*
542 	 * h		Back a character.
543 	 * ^H		Back a character.
544 	 */
545 	case 'h':
546 	case CTRL('h'):
547 		dir = -1;
548 		/* fall into ... */
549 
550 	/*
551 	 * space	Forward a character.
552 	 */
553 	case 'l':
554 	case ' ':
555 		forbid(margin() || opf == vmove && edge());
556 		while (cnt > 0 && !margin()) {
557 			if (dir == 1)
558 				wcursor = nextchr(wcursor);
559 			else
560 				wcursor = lastchr(linebuf, wcursor);
561 			cnt--;
562 		}
563 		if (margin() && opf == vmove || wcursor < linebuf) {
564 			if (dir == 1)
565 				wcursor = lastchr(linebuf, wcursor);
566 			else
567 				wcursor = linebuf;
568 		}
569 		vmoving = 0;
570 		break;
571 
572 	/*
573 	 * D		Delete to end of line, short for d$.
574 	 */
575 	case 'D':
576 		cnt = INF;
577 		goto deleteit;
578 
579 	/*
580 	 * X		Delete character before cursor.
581 	 */
582 	case 'X':
583 		dir = -1;
584 		/* fall into ... */
585 deleteit:
586 	/*
587 	 * x		Delete character at cursor, leaving cursor where it is.
588 	 */
589 	case 'x':
590 		if (margin())
591 			goto errlab;
592 		vmacchng(1);
593 		while (cnt > 0 && !margin()) {
594 			if (dir == 1)
595 				wcursor = nextchr(wcursor);
596 			else
597 				wcursor = lastchr(linebuf, wcursor);
598 			cnt--;
599 		}
600 		opf = deleteop;
601 		vmoving = 0;
602 		break;
603 
604 	default:
605 		/*
606 		 * Stuttered operators are equivalent to the operator on
607 		 * a line, thus turn dd into d_.
608 		 */
609 		if (opf == vmove || c != workcmd[0]) {
610 errlab:
611 			beep();
612 			vmacp = 0;
613 			return;
614 		}
615 		/* fall into ... */
616 
617 	/*
618 	 * _		Target for a line or group of lines.
619 	 *		Stuttering is more convenient; this is mostly
620 	 *		for aesthetics.
621 	 */
622 	case '_':
623 		wdot = dot + cnt - 1;
624 		vmoving = 0;
625 		wcursor = 0;
626 		break;
627 
628 	/*
629 	 * H		To first, home line on screen.
630 	 *		Count is for count'th line rather than first.
631 	 */
632 	case 'H':
633 		wdot = (dot - vcline) + cnt - 1;
634 		if (opf == vmove)
635 			markit(wdot);
636 		vmoving = 0;
637 		wcursor = 0;
638 		break;
639 
640 	/*
641 	 * -		Backwards lines, to first non-white character.
642 	 */
643 	case '-':
644 		wdot = dot - cnt;
645 		vmoving = 0;
646 		wcursor = 0;
647 		break;
648 
649 	/*
650 	 * ^P		To previous line same column.  Ridiculous on the
651 	 *		console of the VAX since it puts console in LSI mode.
652 	 */
653 	case 'k':
654 	case CTRL('p'):
655 		wdot = dot - cnt;
656 		if (vmoving == 0)
657 			vmoving = 1, vmovcol = column(cursor);
658 		wcursor = 0;
659 		break;
660 
661 	/*
662 	 * L		To last line on screen, or count'th line from the
663 	 *		bottom.
664 	 */
665 	case 'L':
666 		wdot = dot + vcnt - vcline - cnt;
667 		if (opf == vmove)
668 			markit(wdot);
669 		vmoving = 0;
670 		wcursor = 0;
671 		break;
672 
673 	/*
674 	 * M		To the middle of the screen.
675 	 */
676 	case 'M':
677 		wdot = dot + ((vcnt + 1) / 2) - vcline - 1;
678 		if (opf == vmove)
679 			markit(wdot);
680 		vmoving = 0;
681 		wcursor = 0;
682 		break;
683 
684 	/*
685 	 * +		Forward line, to first non-white.
686 	 *
687 	 * CR		Convenient synonym for +.
688 	 */
689 	case '+':
690 	case CR:
691 		wdot = dot + cnt;
692 		vmoving = 0;
693 		wcursor = 0;
694 		break;
695 
696 	/*
697 	 * ^N		To next line, same column if possible.
698 	 *
699 	 * LF		Linefeed is a convenient synonym for ^N.
700 	 */
701 	case CTRL('n'):
702 	case 'j':
703 	case NL:
704 		wdot = dot + cnt;
705 		if (vmoving == 0)
706 			vmoving = 1, vmovcol = column(cursor);
707 		wcursor = 0;
708 		break;
709 
710 	/*
711 	 * n		Search to next match of current pattern.
712 	 */
713 	case 'n':
714 		vglobp = vscandir;
715 		c = *vglobp++;
716 		goto nocount;
717 
718 	/*
719 	 * N		Like n but in reverse direction.
720 	 */
721 	case 'N':
722 		vglobp = vscandir[0] == '/' ? (unsigned char *)"?" :
723 		    (unsigned char *)"/";
724 		c = *vglobp++;
725 		goto nocount;
726 
727 	/*
728 	 * '		Return to line specified by following mark,
729 	 *		first white position on line.
730 	 *
731 	 * `		Return to marked line at remembered column.
732 	 */
733 	case '\'':
734 	case '`':
735 		d = c;
736 		c = getesc();
737 		if (c == 0)
738 			return;
739 		c = markreg(c);
740 		forbid(c == 0);
741 		wdot = getmark(c);
742 		forbid(wdot == NOLINE);
743 		forbid(Xhadcnt);
744 		vmoving = 0;
745 		wcursor = d == '`' ? ncols[c - 'a'] : 0;
746 		if (opf == vmove && (wdot != dot ||
747 			(d == '`' && wcursor != cursor)))
748 			markDOT();
749 		if (wcursor) {
750 			vsave();
751 			getline(*wdot);
752 			if (wcursor > strend(linebuf))
753 				wcursor = 0;
754 			else {
755 				cnt = wcursor - linebuf;
756 				/*CSTYLED*/
757 				for (wcursor = linebuf; wcursor - linebuf < cnt; )
758 					wcursor = nextchr(wcursor);
759 				if (wcursor - linebuf > cnt)
760 					wcursor = lastchr(linebuf, wcursor);
761 			}
762 			getDOT();
763 		}
764 		if (ospeed > B300)
765 			hold |= HOLDWIG;
766 		break;
767 
768 	/*
769 	 * G		Goto count'th line, or last line if no count
770 	 *		given.
771 	 */
772 	case 'G':
773 		if (!Xhadcnt)
774 			cnt = lineDOL();
775 		wdot = zero + cnt;
776 		forbid(wdot < one || wdot > dol);
777 		if (opf == vmove)
778 			markit(wdot);
779 		vmoving = 0;
780 		wcursor = 0;
781 		break;
782 
783 	/*
784 	 * /		Scan forward for following re.
785 	 * ?		Scan backward for following re.
786 	 */
787 	case '/':
788 	case '?':
789 		forbid(Xhadcnt);
790 		vsave();
791 		oc = c;
792 		ocurs = cursor;
793 		odot = dot;
794 		wcursor = 0;
795 		if (readecho(c))
796 			return;
797 		if (!vglobp)
798 			vscandir[0] = genbuf[0];
799 		oglobp = globp; CP(vutmp, genbuf); globp = vutmp;
800 		d = peekc;
801 fromsemi:
802 		ungetchar(0);
803 		fixech();
804 		CATCH
805 #ifndef CBREAK
806 			/*
807 			 * Lose typeahead (ick).
808 			 */
809 			vcook();
810 #endif
811 			addr = address(cursor);
812 #ifndef CBREAK
813 			vraw();
814 #endif
815 		ONERR
816 #ifndef CBREAK
817 			vraw();
818 #endif
819 slerr:
820 			globp = oglobp;
821 			dot = odot;
822 			cursor = ocurs;
823 			ungetchar(d);
824 			splitw = 0;
825 			vclean();
826 			vjumpto(dot, ocurs, 0);
827 			return;
828 		ENDCATCH
829 		if (globp == 0)
830 			globp = (unsigned char *)"";
831 		else if (peekc)
832 			--globp;
833 		if (*globp == ';') {
834 			/* /foo/;/bar/ */
835 			globp++;
836 			dot = addr;
837 			cursor = (unsigned char *)loc1;
838 			goto fromsemi;
839 		}
840 		dot = odot;
841 		ungetchar(d);
842 		c = 0;
843 		if (*globp == 'z')
844 			globp++, c = '\n';
845 		if (any(*globp, "^+-."))
846 			c = *globp++;
847 		i = 0;
848 		while (isdigit(*globp))
849 			i = i * 10 + *globp++ - '0';
850 		if (any(*globp, "^+-."))
851 			c = *globp++;
852 		if (*globp) {
853 			/* random junk after the pattern */
854 			beep();
855 			goto slerr;
856 		}
857 		globp = oglobp;
858 		splitw = 0;
859 		vmoving = 0;
860 		wcursor = (unsigned char *)loc1;
861 		if (i != 0)
862 			vsetsiz(i);
863 		if (opf == vmove) {
864 			if (state == ONEOPEN || state == HARDOPEN)
865 				outline = destline = WBOT;
866 			if (addr != dot || (unsigned char *)loc1 != cursor)
867 				markDOT();
868 			if (loc1 > (char *)linebuf && *loc1 == 0)
869 				loc1 = (char *)lastchr(linebuf, loc1);
870 			if (c)
871 				vjumpto(addr, loc1, c);
872 			else {
873 				vmoving = 0;
874 				if (loc1) {
875 					vmoving++;
876 					vmovcol = column(loc1);
877 				}
878 				getDOT();
879 				if (state == CRTOPEN && addr != dot)
880 					vup1();
881 				vupdown(addr - dot, NOSTR);
882 			}
883 			if (oc == '/') {	/* forward search */
884 				if (dot < odot ||
885 				    (dot == odot && cursor <= ocurs))
886 					warnf(value(vi_TERSE) ?
887 			gettext("Search wrapped BOTTOM") :
888 			gettext("Search wrapped around BOTTOM of buffer"));
889 			} else {		/* backward search */
890 				if (dot > odot ||
891 				    (dot == odot && cursor >= ocurs))
892 					warnf(value(vi_TERSE) ?
893 			gettext("Search wrapped TOP") :
894 			gettext("Search wrapped around TOP of buffer"));
895 			}
896 			return;
897 		}
898 		lastcp[-1] = 'n';
899 		getDOT();
900 		wdot = addr;
901 		break;
902 	}
903 	/*
904 	 * Apply.
905 	 */
906 	if (vreg && wdot == 0)
907 		wdot = dot;
908 	(*opf)(c);
909 	wdot = NOLINE;
910 }
911 
912 static void
913 lfixol()
914 {
915 	unsigned char *savevglobp;
916 	int savesplit;
917 
918 	if (Outchar == vputchar)
919 		return;
920 
921 	/* Show messages */
922 	putnl();
923 	if (inopen > 0 && clr_eol)
924 		vclreol();
925 	if (enter_standout_mode && exit_bold)
926 		putpad(enter_standout_mode);
927 	lprintf(gettext("[Hit return to continue] "), 0);
928 	if (enter_standout_mode && exit_bold)
929 		putpad(exit_bold);
930 
931 	/* Get key input for confirmation */
932 	savevglobp = vglobp;
933 	vglobp = 0; /* force typed input */
934 	getkey();
935 	vglobp = savevglobp;
936 
937 	/* reset output function */
938 	Outchar = vputchar;
939 
940 	/* Clean up screen */
941 	savesplit = splitw;
942 	splitw = 0;
943 	vclear();
944 	vdirty(0, WLINES);
945 	vredraw(WTOP);
946 	splitw = savesplit;
947 }
948 
949 warnf(str, cp)
950 	char *str, *cp;
951 {
952 	int saveline, savecol, savesplit;
953 
954 	saveline = outline;
955 	savecol = outcol;
956 	savesplit = splitw;
957 	splitw = 1;
958 	vgoto(WECHO, 0);
959 	if (!enter_standout_mode || !exit_bold)
960 		dingdong();
961 	if (clr_eol)
962 		vclreol();
963 	if (enter_standout_mode && exit_bold)
964 		putpad(enter_standout_mode);
965 	lprintf(str, cp);
966 	if (enter_standout_mode && exit_bold)
967 		putpad(exit_bold);
968 	lfixol();
969 	vgoto(saveline, savecol);
970 	splitw = savesplit;
971 }
972 
973 /* #ifdef PTR_ADDRESSES */
974 /*
975  *	read in a row or column address
976  *
977  */
978 static int
979 get_addr()
980 {
981 	register short  c;
982 	register short  next;
983 
984 	c = getkey();
985 	next = 0;
986 	switch (c) {
987 	case CTRL('A'):
988 		next = 96;
989 		c = getkey();
990 		break;
991 
992 	case CTRL('B'):
993 		next = 192;
994 		c = getkey();
995 		break;
996 	}
997 	if (c < ' ')
998 		return (-1);
999 	return (next + c - ' ');
1000 }
1001 /* #endif PTR_ADDRESSES */
1002 
1003 /*
1004  * Find single character c, in direction dir from cursor.
1005  */
1006 find(c)
1007 	wchar_t c;
1008 {
1009 
1010 	wchar_t wchar;
1011 	int length;
1012 	for (;;) {
1013 		if (edge())
1014 			return (0);
1015 		if (dir == 1)
1016 			wcursor = nextchr(wcursor);
1017 		else
1018 			wcursor = lastchr(linebuf, wcursor);
1019 		if ((length = mbtowc(&wchar, (char *)wcursor,
1020 		    MULTI_BYTE_MAX)) > 0 && wchar == c)
1021 			return (1);
1022 	}
1023 }
1024 
1025 /*
1026  * Do a word motion with operator op, and cnt more words
1027  * to go after this.
1028  */
1029 word(op, cnt)
1030 	register int (*op)();
1031 	int cnt;
1032 {
1033 	register int which;
1034 	register unsigned char *iwc;
1035 	register line *iwdot = wdot;
1036 	wchar_t wchar;
1037 	int length;
1038 
1039 	if (dir == 1) {
1040 		iwc = wcursor;
1041 		which = wordch(wcursor);
1042 		while (wordof(which, wcursor)) {
1043 			length = mbtowc(&wchar, (char *)wcursor,
1044 			    MULTI_BYTE_MAX);
1045 			if (length <= 0)
1046 				length = 1;
1047 			if (cnt == 1 && op != vmove && wcursor[length] == 0) {
1048 				wcursor += length;
1049 				break;
1050 			}
1051 			if (!lnext())
1052 				return (0);
1053 			if (wcursor == linebuf)
1054 				break;
1055 		}
1056 		/* Unless last segment of a change skip blanks */
1057 		if (op != vchange || cnt > 1)
1058 			while (!margin() && blank()) {
1059 				if (!lnext())
1060 					return (0);
1061 			}
1062 		else
1063 			if (wcursor == iwc && iwdot == wdot && *iwc)
1064 				wcursor = nextchr(wcursor);
1065 		if (op == vmove && margin()) {
1066 			wcursor = lastchr(linebuf, wcursor);
1067 #ifdef XPG4
1068 			if (wcursor < linebuf) {
1069 				wcursor = linebuf;
1070 			}
1071 #endif /* XPG4 */
1072 		}
1073 	} else {
1074 		if (!lnext())
1075 			return (0);
1076 		while (blank())
1077 			if (!lnext())
1078 				return (0);
1079 		if (!margin()) {
1080 			which = wordch(wcursor);
1081 			while (!margin() && wordof(which, wcursor))
1082 				wcursor = lastchr(linebuf, wcursor);
1083 		}
1084 #ifdef PRESUNEUC
1085 		if (wcursor < linebuf || !wordof(which, wcursor))
1086 			wcursor = nextchr(wcursor);
1087 #else
1088 		if (wcursor < linebuf)
1089 			wcursor++;
1090 		else if (!wordof(which, wcursor))
1091 			wcursor = nextchr(wcursor);
1092 #endif /* PRESUNEUC */
1093 	}
1094 	return (1);
1095 }
1096 
1097 /*
1098  * To end of word, with operator op and cnt more motions
1099  * remaining after this.
1100  */
1101 eend(op)
1102 	register int (*op)();
1103 {
1104 	register int which;
1105 
1106 	if (!lnext())
1107 		return (0);
1108 	while (blank())
1109 		if (!lnext())
1110 			return (0);
1111 	which = wordch(wcursor);
1112 	while (wordof(which, wcursor)) {
1113 		if (wcursor[1] == 0) {
1114 			wcursor = nextchr(wcursor);
1115 			break;
1116 		}
1117 		if (!lnext())
1118 			return (0);
1119 	}
1120 	if (op == vyankit)
1121 		wcursor = lastchr(linebuf, wcursor) + 1;
1122 	else if (op != vchange && op != vdelete && wcursor > linebuf)
1123 		wcursor = lastchr(linebuf, wcursor);
1124 	return (1);
1125 }
1126 
1127 /*
1128  * Wordof tells whether the character at *wc is in a word of
1129  * kind which (blank/nonblank words are 0, conservative words 1).
1130  */
1131 wordof(which, wc)
1132 	unsigned char which;
1133 	register unsigned char *wc;
1134 {
1135 #ifdef PRESUNEUC
1136 
1137 	if (isspace(*wc))
1138 #else
1139 	wchar_t z;
1140 
1141 	(void) mbtowc(&z, (char *)wc, MB_LEN_MAX);
1142 	if (iswspace(z))
1143 #endif /* PRESUNEUC */
1144 		return (0);
1145 	return (!wdkind || wordch(wc) == which);
1146 }
1147 
1148 /*
1149  * Wordch tells whether character at *wc is a word character
1150  * i.e. an alfa, digit, or underscore.
1151  */
1152 #ifdef PRESUNEUC
1153 #define	SS2 0216
1154 #define	SS3 0217
1155 #endif /* PRESUNEUC */
1156 
1157 wordch(wc)
1158 	unsigned char *wc;
1159 {
1160 	register int length;
1161 	wchar_t c;
1162 
1163 	length = mbtowc(&c, (char *)wc, MULTI_BYTE_MAX);
1164 	if (length <= 0)
1165 		return (0);
1166 	if (length > 1)
1167 #ifndef PRESUNEUC
1168 		if (wdwc)
1169 			return (*wdwc)(c);
1170 		else
1171 #endif /* PRESUNEUC */
1172 		return (length);
1173 #ifndef PRESUNEUC
1174 	return (isalpha(*wc) || isdigit(*wc) || *wc == '_');
1175 #else
1176 	return (isalpha(c) || isdigit(c) || c == '_');
1177 #endif /* PRESUNEUC */
1178 }
1179 
1180 /*
1181  * Edge tells when we hit the last character in the current line.
1182  */
1183 edge()
1184 {
1185 
1186 	if (linebuf[0] == 0)
1187 		return (1);
1188 	if (dir == 1)
1189 		return (*(nextchr(wcursor)) == 0);
1190 	else
1191 		return (wcursor == linebuf);
1192 }
1193 
1194 /*
1195  * Margin tells us when we have fallen off the end of the line.
1196  */
1197 margin()
1198 {
1199 
1200 	return (wcursor < linebuf || wcursor[0] == 0);
1201 }
1202 #ifndef PRESUNEUC
1203 
1204 /*
1205  * Blank tells if the cursor is currently on a TAB, RETURN,
1206  * NEWLINE, FORMFEED, bertical tab, or SPACE character from EUC
1207  * primary and supplementary codesets.
1208  */
1209 blank()
1210 {
1211 	wchar_t z;
1212 
1213 	(void) mbtowc(&z, (char *)wcursor, MB_CUR_MAX);
1214 	return (iswspace((int)z));
1215 }
1216 #endif /* PRESUNEUC */
1217