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		/* 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;
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		/* 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;
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		/* 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':
487fixup:
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 ... */
584deleteit:
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]) {
609errlab:
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			getaline(*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;
800fromsemi:
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
818slerr:
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
911static void
912lfixol()
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
948void
949warnf(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 */
977static int
978get_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 */
1005int
1006find(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 */
1028int
1029word(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 */
1099int
1100eend(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 */
1130int
1131wordof(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
1155int
1156wordch(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 */
1181int
1182edge(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 */
1196int
1197margin(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 */
1209int
1210blank(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