xref: /titanic_51/usr/src/cmd/vi/port/ex_voper.c (revision e7cc6ebf609061441749597604aafeb756a5a9d3)
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 		{
221 			if (vlinfo[i].vliny >= mouse_y)
222 				break;
223 		}
224 		if (i > WECHO)
225 			break;
226 /*
227  *	Look for lines longer than one line - note  odd case at zero
228  */
229 		if (i)
230 		{
231 			if (vlinfo[i - 1].vdepth > 1)
232 			{
233 				mouse_x += WCOLS * (mouse_y -
234 					(vlinfo[i].vliny -
235 					(vlinfo[i - 1].vdepth - 1)));
236 			}
237 		}
238 		else
239 		{
240 			mouse_x += WCOLS * (mouse_y - 1);
241 		}
242 /*
243  *	Set the line
244  */
245 		vsave();
246 		ocurs = cursor;
247 		odot = dot;
248 		oline = vcline;
249 		operate('H', i);
250 /*
251  *	Set the column
252  */
253 		getDOT();
254 		if (Pline == numbline)
255 			mouse_x += 8;
256 		vmovcol = mouse_x;
257 		vmoving = 1;
258 		wcursor = vfindcol(mouse_x);
259 /*
260  *	Reset everything so that stuff like delete and change work
261  */
262 		wdot = (odot - oline) + i - 1;
263 		cursor = ocurs;
264 		vcline = oline;
265 		dot = odot;
266 		getDOT();
267 		break;
268 /* #endif PTR_ADDRESSES */
269 
270 	/*
271 	 * b		Back up a word.
272 	 * B		Back up a word, liberal definition.
273 	 */
274 	case 'b':
275 	case 'B':
276 		dir = -1;
277 		/* fall into ... */
278 
279 	/*
280 	 * w		Forward a word.
281 	 * W		Forward a word, liberal definition.
282 	 */
283 	case 'W':
284 	case 'w':
285 		wdkind = c & ' ';
286 		forbid(lfind(2, cnt, opf, (line *)0) < 0);
287 		vmoving = 0;
288 		break;
289 
290 	/*
291 	 * E		to end of following blank/nonblank word
292 	 */
293 	case 'E':
294 		wdkind = 0;
295 		goto ein;
296 
297 	/*
298 	 * e		To end of following word.
299 	 */
300 	case 'e':
301 		wdkind = 1;
302 ein:
303 		forbid(lfind(3, cnt - 1, opf, (line *)0) < 0);
304 		vmoving = 0;
305 		break;
306 
307 	/*
308 	 * (		Back an s-expression.
309 	 */
310 	case '(':
311 		dir = -1;
312 		/* fall into... */
313 
314 	/*
315 	 * )		Forward an s-expression.
316 	 */
317 	case ')':
318 		forbid(lfind(0, cnt, opf, (line *) 0) < 0);
319 		markDOT();
320 		break;
321 
322 	/*
323 	 * {		Back an s-expression, but don't stop on atoms.
324 	 *		In text mode, a paragraph.  For C, a balanced set
325 	 *		of {}'s.
326 	 */
327 	case '{':
328 		dir = -1;
329 		/* fall into... */
330 
331 	/*
332 	 * }		Forward an s-expression, but don't stop on atoms.
333 	 *		In text mode, back paragraph.  For C, back a balanced
334 	 *		set of {}'s.
335 	 */
336 	case '}':
337 		forbid(lfind(1, cnt, opf, (line *) 0) < 0);
338 		markDOT();
339 		break;
340 
341 	/*
342 	 * %		To matching () or {}.  If not at ( or { scan for
343 	 *		first such after cursor on this line.
344 	 */
345 	case '%':
346 		vsave();
347 		ocurs = cursor;
348 		odot = wdot = dot;
349 		oglobp = globp;
350 		CATCH
351 			i = lmatchp((line *) 0);
352 		ONERR
353 			globp = oglobp;
354 			dot = wdot = odot;
355 			cursor = ocurs;
356 			splitw = 0;
357 			vclean();
358 			vjumpto(dot, ocurs, 0);
359 			return;
360 		ENDCATCH
361 #ifdef TRACE
362 		if (trace)
363 			fprintf(trace, "after lmatchp in %, dot=%d, wdot=%d, "
364 			    "dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
365 #endif
366 		getDOT();
367 		forbid(!i);
368 		if (opf != vmove)
369 			if (dir > 0)
370 				wcursor++;
371 			else
372 				cursor++;
373 		else
374 			markDOT();
375 		vmoving = 0;
376 		break;
377 
378 	/*
379 	 * [		Back to beginning of defun, i.e. an ( in column 1.
380 	 *		For text, back to a section macro.
381 	 *		For C, back to a { in column 1 (~~ beg of function.)
382 	 */
383 	case '[':
384 		dir = -1;
385 		/* fall into ... */
386 
387 	/*
388 	 * ]		Forward to next defun, i.e. a ( in column 1.
389 	 *		For text, forward section.
390 	 *		For C, forward to a } in column 1 (if delete or such)
391 	 *		or if a move to a { in column 1.
392 	 */
393 	case ']':
394 		if (!vglobp)
395 			forbid(getkey() != c);
396 #ifndef XPG4
397 		forbid(Xhadcnt);
398 #endif
399 		vsave();
400 #ifdef XPG4
401 		if (cnt > 1) {
402 			while (cnt-- > 1) {
403 				i = lbrack(c, opf);
404 				getDOT();
405 				forbid(!i);
406 				markDOT();
407 				if (ospeed > B300)
408 					hold |= HOLDWIG;
409 				(*opf)(c);
410 			}
411 		}
412 #endif /* XPG4 */
413 		i = lbrack(c, opf);
414 		getDOT();
415 		forbid(!i);
416 		markDOT();
417 		if (ospeed > B300)
418 			hold |= HOLDWIG;
419 		break;
420 
421 	/*
422 	 * ,		Invert last find with f F t or T, like inverse
423 	 *		of ;.
424 	 */
425 	case ',':
426 		forbid(lastFKND == 0);
427 		c = isupper(lastFKND) ? tolower(lastFKND) : toupper(lastFKND);
428 		i = lastFCHR;
429 		if (vglobp == 0)
430 			vglobp = (unsigned char *)"";
431 		subop++;
432 		goto nocount;
433 
434 	/*
435 	 * 0		To beginning of real line.
436 	 */
437 	case '0':
438 		wcursor = linebuf;
439 		vmoving = 0;
440 		break;
441 
442 	/*
443 	 * ;		Repeat last find with f F t or T.
444 	 */
445 	case ';':
446 		forbid(lastFKND == 0);
447 		c = lastFKND;
448 		i = lastFCHR;
449 		subop++;
450 		goto nocount;
451 
452 	/*
453 	 * F		Find single character before cursor in current line.
454 	 * T		Like F, but stops before character.
455 	 */
456 	case 'F':	/* inverted find */
457 	case 'T':
458 		dir = -1;
459 		/* fall into ... */
460 
461 	/*
462 	 * f		Find single character following cursor in current line.
463 	 * t		Like f, but stope before character.
464 	 */
465 	case 'f':	/* find */
466 	case 't':
467 		if (!subop) {
468 			int length;
469 			wchar_t wchar;
470 			length = _mbftowc(lastcp, &wchar, getesc, &Peekkey);
471 			if (length <= 0 || wchar == 0) {
472 				(void) beep();
473 				return;
474 			}
475 			i = wchar;
476 			lastcp += length;
477 		}
478 		if (vglobp == 0)
479 			lastFKND = c, lastFCHR = i;
480 		for (; cnt > 0; cnt--)
481 			forbid(find(i) == 0);
482 		vmoving = 0;
483 		switch (c) {
484 
485 		case 'T':
486 			wcursor = nextchr(wcursor);
487 			break;
488 
489 		case 't':
490 			wcursor = lastchr(linebuf, wcursor);
491 		case 'f':
492 fixup:
493 			if (moveop != vmove)
494 				wcursor = nextchr(wcursor);
495 			break;
496 		}
497 		break;
498 
499 	/*
500 	 * |		Find specified print column in current line.
501 	 */
502 	case '|':
503 		if (Pline == numbline)
504 			cnt += 8;
505 		vmovcol = cnt;
506 		vmoving = 1;
507 		wcursor = vfindcol(cnt);
508 		break;
509 
510 	/*
511 	 * ^		To beginning of non-white space on line.
512 	 */
513 	case '^':
514 		wcursor = vskipwh(linebuf);
515 		vmoving = 0;
516 		break;
517 
518 	/*
519 	 * $		To end of line.
520 	 */
521 	case '$':
522 		if (opf == vmove) {
523 			vmoving = 1;
524 			vmovcol = 20000;
525 		} else
526 			vmoving = 0;
527 		if (cnt > 1) {
528 			if (opf == vmove) {
529 				wcursor = 0;
530 				cnt--;
531 			} else
532 				wcursor = linebuf;
533 			/* This is wrong at EOF */
534 			wdot = dot + cnt;
535 			break;
536 		}
537 		if (linebuf[0]) {
538 			wcursor = strend(linebuf);
539 			wcursor = lastchr(linebuf, wcursor);
540 			goto fixup;
541 		}
542 		wcursor = linebuf;
543 		break;
544 
545 	/*
546 	 * h		Back a character.
547 	 * ^H		Back a character.
548 	 */
549 	case 'h':
550 	case CTRL('h'):
551 		dir = -1;
552 		/* fall into ... */
553 
554 	/*
555 	 * space	Forward a character.
556 	 */
557 	case 'l':
558 	case ' ':
559 		forbid(margin() || opf == vmove && edge());
560 		while (cnt > 0 && !margin()) {
561 			if (dir == 1)
562 				wcursor = nextchr(wcursor);
563 			else
564 				wcursor = lastchr(linebuf, wcursor);
565 			cnt--;
566 		}
567 		if (margin() && opf == vmove || wcursor < linebuf) {
568 			if (dir == 1)
569 				wcursor = lastchr(linebuf, wcursor);
570 			else
571 				wcursor = linebuf;
572 		}
573 		vmoving = 0;
574 		break;
575 
576 	/*
577 	 * D		Delete to end of line, short for d$.
578 	 */
579 	case 'D':
580 		cnt = INF;
581 		goto deleteit;
582 
583 	/*
584 	 * X		Delete character before cursor.
585 	 */
586 	case 'X':
587 		dir = -1;
588 		/* fall into ... */
589 deleteit:
590 	/*
591 	 * x		Delete character at cursor, leaving cursor where it is.
592 	 */
593 	case 'x':
594 		if (margin())
595 			goto errlab;
596 		vmacchng(1);
597 		while (cnt > 0 && !margin()) {
598 			if (dir == 1)
599 				wcursor = nextchr(wcursor);
600 			else
601 				wcursor = lastchr(linebuf, wcursor);
602 			cnt--;
603 		}
604 		opf = deleteop;
605 		vmoving = 0;
606 		break;
607 
608 	default:
609 		/*
610 		 * Stuttered operators are equivalent to the operator on
611 		 * a line, thus turn dd into d_.
612 		 */
613 		if (opf == vmove || c != workcmd[0]) {
614 errlab:
615 			(void) beep();
616 			vmacp = 0;
617 			return;
618 		}
619 		/* fall into ... */
620 
621 	/*
622 	 * _		Target for a line or group of lines.
623 	 *		Stuttering is more convenient; this is mostly
624 	 *		for aesthetics.
625 	 */
626 	case '_':
627 		wdot = dot + cnt - 1;
628 		vmoving = 0;
629 		wcursor = 0;
630 		break;
631 
632 	/*
633 	 * H		To first, home line on screen.
634 	 *		Count is for count'th line rather than first.
635 	 */
636 	case 'H':
637 		wdot = (dot - vcline) + cnt - 1;
638 		if (opf == vmove)
639 			markit(wdot);
640 		vmoving = 0;
641 		wcursor = 0;
642 		break;
643 
644 	/*
645 	 * -		Backwards lines, to first non-white character.
646 	 */
647 	case '-':
648 		wdot = dot - cnt;
649 		vmoving = 0;
650 		wcursor = 0;
651 		break;
652 
653 	/*
654 	 * ^P		To previous line same column.  Ridiculous on the
655 	 *		console of the VAX since it puts console in LSI mode.
656 	 */
657 	case 'k':
658 	case CTRL('p'):
659 		wdot = dot - cnt;
660 		if (vmoving == 0)
661 			vmoving = 1, vmovcol = column(cursor);
662 		wcursor = 0;
663 		break;
664 
665 	/*
666 	 * L		To last line on screen, or count'th line from the
667 	 *		bottom.
668 	 */
669 	case 'L':
670 		wdot = dot + vcnt - vcline - cnt;
671 		if (opf == vmove)
672 			markit(wdot);
673 		vmoving = 0;
674 		wcursor = 0;
675 		break;
676 
677 	/*
678 	 * M		To the middle of the screen.
679 	 */
680 	case 'M':
681 		wdot = dot + ((vcnt + 1) / 2) - vcline - 1;
682 		if (opf == vmove)
683 			markit(wdot);
684 		vmoving = 0;
685 		wcursor = 0;
686 		break;
687 
688 	/*
689 	 * +		Forward line, to first non-white.
690 	 *
691 	 * CR		Convenient synonym for +.
692 	 */
693 	case '+':
694 	case CR:
695 		wdot = dot + cnt;
696 		vmoving = 0;
697 		wcursor = 0;
698 		break;
699 
700 	/*
701 	 * ^N		To next line, same column if possible.
702 	 *
703 	 * LF		Linefeed is a convenient synonym for ^N.
704 	 */
705 	case CTRL('n'):
706 	case 'j':
707 	case NL:
708 		wdot = dot + cnt;
709 		if (vmoving == 0)
710 			vmoving = 1, vmovcol = column(cursor);
711 		wcursor = 0;
712 		break;
713 
714 	/*
715 	 * n		Search to next match of current pattern.
716 	 */
717 	case 'n':
718 		vglobp = vscandir;
719 		c = *vglobp++;
720 		goto nocount;
721 
722 	/*
723 	 * N		Like n but in reverse direction.
724 	 */
725 	case 'N':
726 		vglobp = vscandir[0] == '/' ? (unsigned char *)"?" :
727 		    (unsigned char *)"/";
728 		c = *vglobp++;
729 		goto nocount;
730 
731 	/*
732 	 * '		Return to line specified by following mark,
733 	 *		first white position on line.
734 	 *
735 	 * `		Return to marked line at remembered column.
736 	 */
737 	case '\'':
738 	case '`':
739 		d = c;
740 		c = getesc();
741 		if (c == 0)
742 			return;
743 		c = markreg(c);
744 		forbid(c == 0);
745 		wdot = getmark(c);
746 		forbid(wdot == NOLINE);
747 		forbid(Xhadcnt);
748 		vmoving = 0;
749 		wcursor = d == '`' ? ncols[c - 'a'] : 0;
750 		if (opf == vmove && (wdot != dot ||
751 			(d == '`' && wcursor != cursor)))
752 			markDOT();
753 		if (wcursor) {
754 			vsave();
755 			getline(*wdot);
756 			if (wcursor > strend(linebuf))
757 				wcursor = 0;
758 			else {
759 				cnt = wcursor - linebuf;
760 				/*CSTYLED*/
761 				for (wcursor = linebuf; wcursor - linebuf < cnt; )
762 					wcursor = nextchr(wcursor);
763 				if (wcursor - linebuf > cnt)
764 					wcursor = lastchr(linebuf, wcursor);
765 			}
766 			getDOT();
767 		}
768 		if (ospeed > B300)
769 			hold |= HOLDWIG;
770 		break;
771 
772 	/*
773 	 * G		Goto count'th line, or last line if no count
774 	 *		given.
775 	 */
776 	case 'G':
777 		if (!Xhadcnt)
778 			cnt = lineDOL();
779 		wdot = zero + cnt;
780 		forbid(wdot < one || wdot > dol);
781 		if (opf == vmove)
782 			markit(wdot);
783 		vmoving = 0;
784 		wcursor = 0;
785 		break;
786 
787 	/*
788 	 * /		Scan forward for following re.
789 	 * ?		Scan backward for following re.
790 	 */
791 	case '/':
792 	case '?':
793 		forbid(Xhadcnt);
794 		vsave();
795 		oc = c;
796 		ocurs = cursor;
797 		odot = dot;
798 		wcursor = 0;
799 		if (readecho(c))
800 			return;
801 		if (!vglobp)
802 			vscandir[0] = genbuf[0];
803 		oglobp = globp; CP(vutmp, genbuf); globp = vutmp;
804 		d = peekc;
805 fromsemi:
806 		ungetchar(0);
807 		fixech();
808 		CATCH
809 #ifndef CBREAK
810 			/*
811 			 * Lose typeahead (ick).
812 			 */
813 			vcook();
814 #endif
815 			addr = address(cursor);
816 #ifndef CBREAK
817 			vraw();
818 #endif
819 		ONERR
820 #ifndef CBREAK
821 			vraw();
822 #endif
823 slerr:
824 			globp = oglobp;
825 			dot = odot;
826 			cursor = ocurs;
827 			ungetchar(d);
828 			splitw = 0;
829 			vclean();
830 			vjumpto(dot, ocurs, 0);
831 			return;
832 		ENDCATCH
833 		if (globp == 0)
834 			globp = (unsigned char *)"";
835 		else if (peekc)
836 			--globp;
837 		if (*globp == ';') {
838 			/* /foo/;/bar/ */
839 			globp++;
840 			dot = addr;
841 			cursor = (unsigned char *)loc1;
842 			goto fromsemi;
843 		}
844 		dot = odot;
845 		ungetchar(d);
846 		c = 0;
847 		if (*globp == 'z')
848 			globp++, c = '\n';
849 		if (any(*globp, "^+-."))
850 			c = *globp++;
851 		i = 0;
852 		while (isdigit(*globp))
853 			i = i * 10 + *globp++ - '0';
854 		if (any(*globp, "^+-."))
855 			c = *globp++;
856 		if (*globp) {
857 			/* random junk after the pattern */
858 			(void) beep();
859 			goto slerr;
860 		}
861 		globp = oglobp;
862 		splitw = 0;
863 		vmoving = 0;
864 		wcursor = (unsigned char *)loc1;
865 		if (i != 0)
866 			vsetsiz(i);
867 		if (opf == vmove) {
868 			if (state == ONEOPEN || state == HARDOPEN)
869 				outline = destline = WBOT;
870 			if (addr != dot || (unsigned char *)loc1 != cursor)
871 				markDOT();
872 			if (loc1 > (char *)linebuf && *loc1 == 0)
873 				loc1 = (char *)lastchr(linebuf, loc1);
874 			if (c)
875 				vjumpto(addr, (unsigned char *)loc1, c);
876 			else {
877 				vmoving = 0;
878 				if (loc1) {
879 					vmoving++;
880 					vmovcol = column(loc1);
881 				}
882 				getDOT();
883 				if (state == CRTOPEN && addr != dot)
884 					vup1();
885 				vupdown(addr - dot, NOSTR);
886 			}
887 			if (oc == '/') {	/* forward search */
888 				if (dot < odot ||
889 				    (dot == odot && cursor <= ocurs))
890 					warnf(value(vi_TERSE) ?
891 			gettext("Search wrapped BOTTOM") :
892 			gettext("Search wrapped around BOTTOM of buffer"));
893 			} else {		/* backward search */
894 				if (dot > odot ||
895 				    (dot == odot && cursor >= ocurs))
896 					warnf(value(vi_TERSE) ?
897 			gettext("Search wrapped TOP") :
898 			gettext("Search wrapped around TOP of buffer"));
899 			}
900 			return;
901 		}
902 		lastcp[-1] = 'n';
903 		getDOT();
904 		wdot = addr;
905 		break;
906 	}
907 	/*
908 	 * Apply.
909 	 */
910 	if (vreg && wdot == 0)
911 		wdot = dot;
912 	(*opf)(c);
913 	wdot = NOLINE;
914 }
915 
916 static void
917 lfixol()
918 {
919 	unsigned char *savevglobp;
920 	int savesplit;
921 
922 	if (Outchar == vputchar)
923 		return;
924 
925 	/* Show messages */
926 	putnl();
927 	if (inopen > 0 && clr_eol)
928 		vclreol();
929 	if (enter_standout_mode && exit_bold)
930 		putpad((unsigned char *)enter_standout_mode);
931 	lprintf(gettext("[Hit return to continue] "), 0);
932 	if (enter_standout_mode && exit_bold)
933 		putpad((unsigned char *)exit_bold);
934 
935 	/* Get key input for confirmation */
936 	savevglobp = vglobp;
937 	vglobp = 0; /* force typed input */
938 	getkey();
939 	vglobp = savevglobp;
940 
941 	/* reset output function */
942 	Outchar = vputchar;
943 
944 	/* Clean up screen */
945 	savesplit = splitw;
946 	splitw = 0;
947 	vclear();
948 	vdirty(0, WLINES);
949 	vredraw(WTOP);
950 	splitw = savesplit;
951 }
952 
953 void
954 warnf(char *str, char *cp)
955 {
956 	int saveline, savecol, savesplit;
957 
958 	saveline = outline;
959 	savecol = outcol;
960 	savesplit = splitw;
961 	splitw = 1;
962 	vgoto(WECHO, 0);
963 	if (!enter_standout_mode || !exit_bold)
964 		dingdong();
965 	if (clr_eol)
966 		vclreol();
967 	if (enter_standout_mode && exit_bold)
968 		putpad((unsigned char *)enter_standout_mode);
969 	lprintf(str, cp);
970 	if (enter_standout_mode && exit_bold)
971 		putpad((unsigned char *)exit_bold);
972 	lfixol();
973 	vgoto(saveline, savecol);
974 	splitw = savesplit;
975 }
976 
977 /* #ifdef PTR_ADDRESSES */
978 /*
979  *	read in a row or column address
980  *
981  */
982 static int
983 get_addr()
984 {
985 	short  c;
986 	short  next;
987 
988 	c = getkey();
989 	next = 0;
990 	switch (c) {
991 	case CTRL('A'):
992 		next = 96;
993 		c = getkey();
994 		break;
995 
996 	case CTRL('B'):
997 		next = 192;
998 		c = getkey();
999 		break;
1000 	}
1001 	if (c < ' ')
1002 		return (-1);
1003 	return (next + c - ' ');
1004 }
1005 /* #endif PTR_ADDRESSES */
1006 
1007 /*
1008  * Find single character c, in direction dir from cursor.
1009  */
1010 int
1011 find(wchar_t c)
1012 {
1013 
1014 	wchar_t wchar;
1015 	int length;
1016 	for (;;) {
1017 		if (edge())
1018 			return (0);
1019 		if (dir == 1)
1020 			wcursor = nextchr(wcursor);
1021 		else
1022 			wcursor = lastchr(linebuf, wcursor);
1023 		if ((length = mbtowc(&wchar, (char *)wcursor,
1024 		    MULTI_BYTE_MAX)) > 0 && wchar == c)
1025 			return (1);
1026 	}
1027 }
1028 
1029 /*
1030  * Do a word motion with operator op, and cnt more words
1031  * to go after this.
1032  */
1033 int
1034 word(int (*op)(), int cnt)
1035 {
1036 	int which;
1037 	unsigned char *iwc;
1038 	line *iwdot = wdot;
1039 	wchar_t wchar;
1040 	int length;
1041 
1042 	if (dir == 1) {
1043 		iwc = wcursor;
1044 		which = wordch(wcursor);
1045 		while (wordof(which, wcursor)) {
1046 			length = mbtowc(&wchar, (char *)wcursor,
1047 			    MULTI_BYTE_MAX);
1048 			if (length <= 0)
1049 				length = 1;
1050 			if (cnt == 1 && op != vmove && wcursor[length] == 0) {
1051 				wcursor += length;
1052 				break;
1053 			}
1054 			if (!lnext())
1055 				return (0);
1056 			if (wcursor == linebuf)
1057 				break;
1058 		}
1059 		/* Unless last segment of a change skip blanks */
1060 		if (op != (int (*)())vchange || cnt > 1)
1061 			while (!margin() && blank()) {
1062 				if (!lnext())
1063 					return (0);
1064 			}
1065 		else
1066 			if (wcursor == iwc && iwdot == wdot && *iwc)
1067 				wcursor = nextchr(wcursor);
1068 		if (op == vmove && margin()) {
1069 			wcursor = lastchr(linebuf, wcursor);
1070 #ifdef XPG4
1071 			if (wcursor < linebuf) {
1072 				wcursor = linebuf;
1073 			}
1074 #endif /* XPG4 */
1075 		}
1076 	} else {
1077 		if (!lnext())
1078 			return (0);
1079 		while (blank())
1080 			if (!lnext())
1081 				return (0);
1082 		if (!margin()) {
1083 			which = wordch(wcursor);
1084 			while (!margin() && wordof(which, wcursor))
1085 				wcursor = lastchr(linebuf, wcursor);
1086 		}
1087 #ifdef PRESUNEUC
1088 		if (wcursor < linebuf || !wordof(which, wcursor))
1089 			wcursor = nextchr(wcursor);
1090 #else
1091 		if (wcursor < linebuf)
1092 			wcursor++;
1093 		else if (!wordof(which, wcursor))
1094 			wcursor = nextchr(wcursor);
1095 #endif /* PRESUNEUC */
1096 	}
1097 	return (1);
1098 }
1099 
1100 /*
1101  * To end of word, with operator op and cnt more motions
1102  * remaining after this.
1103  */
1104 int
1105 eend(int (*op)())
1106 {
1107 	int which;
1108 
1109 	if (!lnext())
1110 		return (0);
1111 	while (blank())
1112 		if (!lnext())
1113 			return (0);
1114 	which = wordch(wcursor);
1115 	while (wordof(which, wcursor)) {
1116 		if (wcursor[1] == 0) {
1117 			wcursor = nextchr(wcursor);
1118 			break;
1119 		}
1120 		if (!lnext())
1121 			return (0);
1122 	}
1123 	if (op == vyankit)
1124 		wcursor = lastchr(linebuf, wcursor) + 1;
1125 	else if (op != (int (*)())vchange && op != (int (*)())vdelete &&
1126 	    wcursor > linebuf)
1127 		wcursor = lastchr(linebuf, wcursor);
1128 	return (1);
1129 }
1130 
1131 /*
1132  * Wordof tells whether the character at *wc is in a word of
1133  * kind which (blank/nonblank words are 0, conservative words 1).
1134  */
1135 int
1136 wordof(unsigned char which, unsigned char *wc)
1137 {
1138 #ifdef PRESUNEUC
1139 
1140 	if (isspace(*wc))
1141 #else
1142 	wchar_t z;
1143 
1144 	(void) mbtowc(&z, (char *)wc, MB_LEN_MAX);
1145 	if (iswspace(z))
1146 #endif /* PRESUNEUC */
1147 		return (0);
1148 	return (!wdkind || wordch(wc) == which);
1149 }
1150 
1151 /*
1152  * Wordch tells whether character at *wc is a word character
1153  * i.e. an alfa, digit, or underscore.
1154  */
1155 #ifdef PRESUNEUC
1156 #define	SS2 0216
1157 #define	SS3 0217
1158 #endif /* PRESUNEUC */
1159 
1160 int
1161 wordch(unsigned char *wc)
1162 {
1163 	int length;
1164 	wchar_t c;
1165 
1166 	length = mbtowc(&c, (char *)wc, MULTI_BYTE_MAX);
1167 	if (length <= 0)
1168 		return (0);
1169 	if (length > 1)
1170 #ifndef PRESUNEUC
1171 		if (wdwc)
1172 			return (*wdwc)(c);
1173 		else
1174 #endif /* PRESUNEUC */
1175 		return (length);
1176 #ifndef PRESUNEUC
1177 	return (isalpha(*wc) || isdigit(*wc) || *wc == '_');
1178 #else
1179 	return (isalpha(c) || isdigit(c) || c == '_');
1180 #endif /* PRESUNEUC */
1181 }
1182 
1183 /*
1184  * Edge tells when we hit the last character in the current line.
1185  */
1186 int
1187 edge(void)
1188 {
1189 
1190 	if (linebuf[0] == 0)
1191 		return (1);
1192 	if (dir == 1)
1193 		return (*(nextchr(wcursor)) == 0);
1194 	else
1195 		return (wcursor == linebuf);
1196 }
1197 
1198 /*
1199  * Margin tells us when we have fallen off the end of the line.
1200  */
1201 int
1202 margin(void)
1203 {
1204 
1205 	return (wcursor < linebuf || wcursor[0] == 0);
1206 }
1207 #ifndef PRESUNEUC
1208 
1209 /*
1210  * Blank tells if the cursor is currently on a TAB, RETURN,
1211  * NEWLINE, FORMFEED, bertical tab, or SPACE character from EUC
1212  * primary and supplementary codesets.
1213  */
1214 int
1215 blank(void)
1216 {
1217 	wchar_t z;
1218 
1219 	(void) mbtowc(&z, (char *)wcursor, MB_CUR_MAX);
1220 	return (iswspace((int)z));
1221 }
1222 #endif /* PRESUNEUC */
1223