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