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