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/*
23 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
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
54unsigned char	vscandir[2] =	{ '/', 0 };
55
56static 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 */
68void
69operate(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		/* FALLTHROUGH */
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		/* FALLTHROUGH */
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;
187nocount:
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		/* FALLTHROUGH */
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;
297ein:
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		/* FALLTHROUGH */
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		/* FALLTHROUGH */
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		/* FALLTHROUGH */
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		/* FALLTHROUGH */
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			/* FALLTHROUGH */
487		case 'f':
488fixup:
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		/* FALLTHROUGH */
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		/* FALLTHROUGH */
585deleteit:
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]) {
610errlab:
611			(void) beep();
612			vmacp = 0;
613			return;
614		}
615		/* FALLTHROUGH */
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			getaline(*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;
801fromsemi:
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
819slerr:
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			(void) 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, (unsigned char *)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
912static void
913lfixol()
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((unsigned char *)enter_standout_mode);
927	lprintf(gettext("[Hit return to continue] "), 0);
928	if (enter_standout_mode && exit_bold)
929		putpad((unsigned char *)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
949void
950warnf(char *str, char *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((unsigned char *)enter_standout_mode);
965	lprintf(str, cp);
966	if (enter_standout_mode && exit_bold)
967		putpad((unsigned char *)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 */
978static int
979get_addr()
980{
981	short  c;
982	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 */
1006int
1007find(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 */
1029int
1030word(int (*op)(), int cnt)
1031{
1032	int which;
1033	unsigned char *iwc;
1034	line *iwdot = wdot;
1035	wchar_t wchar;
1036	int length;
1037
1038	if (dir == 1) {
1039		iwc = wcursor;
1040		which = wordch(wcursor);
1041		while (wordof(which, wcursor)) {
1042			length = mbtowc(&wchar, (char *)wcursor,
1043			    MULTI_BYTE_MAX);
1044			if (length <= 0)
1045				length = 1;
1046			if (cnt == 1 && op != vmove && wcursor[length] == 0) {
1047				wcursor += length;
1048				break;
1049			}
1050			if (!lnext())
1051				return (0);
1052			if (wcursor == linebuf)
1053				break;
1054		}
1055		/* Unless last segment of a change skip blanks */
1056		if (op != (int (*)())vchange || cnt > 1)
1057			while (!margin() && blank()) {
1058				if (!lnext())
1059					return (0);
1060			}
1061		else
1062			if (wcursor == iwc && iwdot == wdot && *iwc)
1063				wcursor = nextchr(wcursor);
1064		if (op == vmove && margin()) {
1065			wcursor = lastchr(linebuf, wcursor);
1066#ifdef XPG4
1067			if (wcursor < linebuf) {
1068				wcursor = linebuf;
1069			}
1070#endif /* XPG4 */
1071		}
1072	} else {
1073		if (!lnext())
1074			return (0);
1075		while (blank())
1076			if (!lnext())
1077				return (0);
1078		if (!margin()) {
1079			which = wordch(wcursor);
1080			while (!margin() && wordof(which, wcursor))
1081				wcursor = lastchr(linebuf, wcursor);
1082		}
1083#ifdef PRESUNEUC
1084		if (wcursor < linebuf || !wordof(which, wcursor))
1085			wcursor = nextchr(wcursor);
1086#else
1087		if (wcursor < linebuf)
1088			wcursor++;
1089		else if (!wordof(which, wcursor))
1090			wcursor = nextchr(wcursor);
1091#endif /* PRESUNEUC */
1092	}
1093	return (1);
1094}
1095
1096/*
1097 * To end of word, with operator op and cnt more motions
1098 * remaining after this.
1099 */
1100int
1101eend(int (*op)())
1102{
1103	int which;
1104
1105	if (!lnext())
1106		return (0);
1107	while (blank())
1108		if (!lnext())
1109			return (0);
1110	which = wordch(wcursor);
1111	while (wordof(which, wcursor)) {
1112		if (wcursor[1] == 0) {
1113			wcursor = nextchr(wcursor);
1114			break;
1115		}
1116		if (!lnext())
1117			return (0);
1118	}
1119	if (op == vyankit)
1120		wcursor = lastchr(linebuf, wcursor) + 1;
1121	else if (op != (int (*)())vchange && op != (int (*)())vdelete &&
1122	    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 */
1131int
1132wordof(unsigned char which, unsigned char *wc)
1133{
1134#ifdef PRESUNEUC
1135
1136	if (isspace(*wc))
1137#else
1138	wchar_t z;
1139
1140	(void) mbtowc(&z, (char *)wc, MB_LEN_MAX);
1141	if (iswspace(z))
1142#endif /* PRESUNEUC */
1143		return (0);
1144	return (!wdkind || wordch(wc) == which);
1145}
1146
1147/*
1148 * Wordch tells whether character at *wc is a word character
1149 * i.e. an alfa, digit, or underscore.
1150 */
1151#ifdef PRESUNEUC
1152#define	SS2 0216
1153#define	SS3 0217
1154#endif /* PRESUNEUC */
1155
1156int
1157wordch(unsigned char *wc)
1158{
1159	int length;
1160	wchar_t c;
1161
1162	length = mbtowc(&c, (char *)wc, MULTI_BYTE_MAX);
1163	if (length <= 0)
1164		return (0);
1165	if (length > 1)
1166#ifndef PRESUNEUC
1167		if (wdwc)
1168			return (*wdwc)(c);
1169		else
1170#endif /* PRESUNEUC */
1171		return (length);
1172#ifndef PRESUNEUC
1173	return (isalpha(*wc) || isdigit(*wc) || *wc == '_');
1174#else
1175	return (isalpha(c) || isdigit(c) || c == '_');
1176#endif /* PRESUNEUC */
1177}
1178
1179/*
1180 * Edge tells when we hit the last character in the current line.
1181 */
1182int
1183edge(void)
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 */
1197int
1198margin(void)
1199{
1200
1201	return (wcursor < linebuf || wcursor[0] == 0);
1202}
1203#ifndef PRESUNEUC
1204
1205/*
1206 * Blank tells if the cursor is currently on a TAB, RETURN,
1207 * NEWLINE, FORMFEED, bertical tab, or SPACE character from EUC
1208 * primary and supplementary codesets.
1209 */
1210int
1211blank(void)
1212{
1213	wchar_t z;
1214
1215	(void) mbtowc(&z, (char *)wcursor, MB_CUR_MAX);
1216	return (iswspace((int)z));
1217}
1218#endif /* PRESUNEUC */
1219