xref: /onnv-gate/usr/src/cmd/vi/port/ex_vmain.c (revision 802:73b56fb6544b)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
22*802Scf46844 /*
23*802Scf46844  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*802Scf46844  * Use is subject to license terms.
25*802Scf46844  */
26*802Scf46844 
270Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
280Sstevel@tonic-gate /*	  All Rights Reserved  	*/
290Sstevel@tonic-gate 
300Sstevel@tonic-gate 
310Sstevel@tonic-gate /* Copyright (c) 1981 Regents of the University of California */
32*802Scf46844 
33*802Scf46844 #pragma ident	"%Z%%M%	%I%	%E% SMI"
340Sstevel@tonic-gate 
350Sstevel@tonic-gate #include "ex.h"
360Sstevel@tonic-gate #include "ex_tty.h"
370Sstevel@tonic-gate #include "ex_vis.h"
380Sstevel@tonic-gate #ifndef PRESUNEUC
390Sstevel@tonic-gate #include <wctype.h>
400Sstevel@tonic-gate /* Undef putchar/getchar if they're defined. */
410Sstevel@tonic-gate #ifdef putchar
420Sstevel@tonic-gate #	undef putchar
430Sstevel@tonic-gate #endif
440Sstevel@tonic-gate #ifdef getchar
450Sstevel@tonic-gate #	undef getchar
460Sstevel@tonic-gate #endif
470Sstevel@tonic-gate #endif /* PRESUNEUC */
480Sstevel@tonic-gate 
490Sstevel@tonic-gate /*
500Sstevel@tonic-gate  * This is the main routine for visual.
510Sstevel@tonic-gate  * We here decode the count and possible named buffer specification
520Sstevel@tonic-gate  * preceding a command and interpret a few of the commands.
530Sstevel@tonic-gate  * Commands which involve a target (i.e. an operator) are decoded
540Sstevel@tonic-gate  * in the routine operate in ex_voperate.c.
550Sstevel@tonic-gate  */
560Sstevel@tonic-gate 
570Sstevel@tonic-gate #define	forbid(a)	{ if (a) goto fonfon; }
580Sstevel@tonic-gate 
590Sstevel@tonic-gate extern int windowchg;
600Sstevel@tonic-gate extern int sigok;
610Sstevel@tonic-gate void redraw(), windowinit();
620Sstevel@tonic-gate 
630Sstevel@tonic-gate #ifdef XPG4
640Sstevel@tonic-gate extern int P_cursor_offset;
650Sstevel@tonic-gate #endif
660Sstevel@tonic-gate 
67*802Scf46844 void
68*802Scf46844 vmain(void)
690Sstevel@tonic-gate {
70*802Scf46844 	int c, cnt, i;
710Sstevel@tonic-gate 	wchar_t esave[TUBECOLS];
720Sstevel@tonic-gate 	extern wchar_t atube[];
730Sstevel@tonic-gate 	unsigned char *oglobp;
740Sstevel@tonic-gate 	short d;
750Sstevel@tonic-gate 	line *addr;
760Sstevel@tonic-gate 	int ind, nlput;
770Sstevel@tonic-gate 	int shouldpo = 0;
780Sstevel@tonic-gate 	int tag_reset_wrap = 0;
790Sstevel@tonic-gate 	int onumber, olist, (*OPline)(), (*OPutchar)();
800Sstevel@tonic-gate 
810Sstevel@tonic-gate 
820Sstevel@tonic-gate 	vch_mac = VC_NOTINMAC;
830Sstevel@tonic-gate 	ixlatctl(0);
840Sstevel@tonic-gate 
850Sstevel@tonic-gate 	/*
860Sstevel@tonic-gate 	 * If we started as a vi command (on the command line)
870Sstevel@tonic-gate 	 * then go process initial commands (recover, next or tag).
880Sstevel@tonic-gate 	 */
890Sstevel@tonic-gate 	if (initev) {
900Sstevel@tonic-gate 		oglobp = globp;
910Sstevel@tonic-gate 		globp = initev;
920Sstevel@tonic-gate 		hadcnt = cnt = 0;
930Sstevel@tonic-gate 		i = tchng;
940Sstevel@tonic-gate 		addr = dot;
950Sstevel@tonic-gate 		goto doinit;
960Sstevel@tonic-gate 	}
970Sstevel@tonic-gate 
980Sstevel@tonic-gate 	vshowmode("");		/* As a precaution */
990Sstevel@tonic-gate 	/*
1000Sstevel@tonic-gate 	 * NB:
1010Sstevel@tonic-gate 	 *
1020Sstevel@tonic-gate 	 * The current line is always in the line buffer linebuf,
1030Sstevel@tonic-gate 	 * and the cursor at the position cursor.  You should do
1040Sstevel@tonic-gate 	 * a vsave() before moving off the line to make sure the disk
1050Sstevel@tonic-gate 	 * copy is updated if it has changed, and a getDOT() to get
1060Sstevel@tonic-gate 	 * the line back if you mung linebuf.  The motion
1070Sstevel@tonic-gate 	 * routines in ex_vwind.c handle most of this.
1080Sstevel@tonic-gate 	 */
1090Sstevel@tonic-gate 	for (;;) {
1100Sstevel@tonic-gate 		/*
1110Sstevel@tonic-gate 		 * Decode a visual command.
1120Sstevel@tonic-gate 		 * First sync the temp file if there has been a reasonable
1130Sstevel@tonic-gate 		 * amount of change.  Clear state for decoding of next
1140Sstevel@tonic-gate 		 * command.
1150Sstevel@tonic-gate 		 */
1160Sstevel@tonic-gate 		TSYNC();
1170Sstevel@tonic-gate 		vglobp = 0;
1180Sstevel@tonic-gate 		vreg = 0;
1190Sstevel@tonic-gate 		hold = 0;
1200Sstevel@tonic-gate 		seenprompt = 1;
1210Sstevel@tonic-gate 		wcursor = 0;
1220Sstevel@tonic-gate 		Xhadcnt = hadcnt = 0;
1230Sstevel@tonic-gate 		Xcnt = cnt = 1;
1240Sstevel@tonic-gate 		splitw = 0;
1250Sstevel@tonic-gate 		if (i = holdupd && !windowchg) {
1260Sstevel@tonic-gate 			if (state == VISUAL) {
1270Sstevel@tonic-gate 				sigok = 1;
1280Sstevel@tonic-gate 				(void)peekkey();
1290Sstevel@tonic-gate 				sigok = 0;
1300Sstevel@tonic-gate 			}
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate 			holdupd = 0;
1330Sstevel@tonic-gate /*
1340Sstevel@tonic-gate 			if (LINE(0) < ZERO) {
1350Sstevel@tonic-gate 				vclear();
1360Sstevel@tonic-gate 				vcnt = 0;
1370Sstevel@tonic-gate 				i = 3;
1380Sstevel@tonic-gate 			}
1390Sstevel@tonic-gate */
1400Sstevel@tonic-gate 			if (state != VISUAL) {
1410Sstevel@tonic-gate 				vcnt = 0;
1420Sstevel@tonic-gate 				vsave();
1430Sstevel@tonic-gate 				vrepaint(cursor);
1440Sstevel@tonic-gate 			} else if (i == 3)
1450Sstevel@tonic-gate 				vredraw(WTOP);
1460Sstevel@tonic-gate 			else
1470Sstevel@tonic-gate 				vsync(WTOP);
1480Sstevel@tonic-gate 			vfixcurs();
1490Sstevel@tonic-gate 		} else if(windowchg)
1500Sstevel@tonic-gate 			redraw();
1510Sstevel@tonic-gate 
1520Sstevel@tonic-gate 		/*
1530Sstevel@tonic-gate 		 * Gobble up counts and named buffer specifications.
1540Sstevel@tonic-gate 		 */
1550Sstevel@tonic-gate 		for (;;) {
1560Sstevel@tonic-gate looptop:
1570Sstevel@tonic-gate #ifdef MDEBUG
1580Sstevel@tonic-gate 			if (trace)
1590Sstevel@tonic-gate 				fprintf(trace, "pc=%c",peekkey());
1600Sstevel@tonic-gate #endif
1610Sstevel@tonic-gate 			sigok = 1;
1620Sstevel@tonic-gate 			c = peekkey();
1630Sstevel@tonic-gate 			sigok = 0;
1640Sstevel@tonic-gate 			if (isdigit(peekkey()) && peekkey() != '0') {
1650Sstevel@tonic-gate 				hadcnt = 1;
1660Sstevel@tonic-gate 				cnt = vgetcnt();
1670Sstevel@tonic-gate 				forbid (cnt <= 0);
1680Sstevel@tonic-gate 			}
1690Sstevel@tonic-gate 			if (peekkey() != '"')
1700Sstevel@tonic-gate 				break;
1710Sstevel@tonic-gate 			(void)getkey(), c = getkey();
1720Sstevel@tonic-gate 			/*
1730Sstevel@tonic-gate 			 * Buffer names be letters or digits.
1740Sstevel@tonic-gate 			 * But not '0' as that is the source of
1750Sstevel@tonic-gate 			 * an 'empty' named buffer spec in the routine
1760Sstevel@tonic-gate 			 * kshift (see ex_temp.c).
1770Sstevel@tonic-gate 			 */
1780Sstevel@tonic-gate 			if(!isascii(c) && MB_CUR_MAX > 1) {
1790Sstevel@tonic-gate 				/* get rest of character */
1800Sstevel@tonic-gate 				wchar_t wchar;
1810Sstevel@tonic-gate 				char multic[MULTI_BYTE_MAX];
1820Sstevel@tonic-gate 				ungetkey(c);
1830Sstevel@tonic-gate 				(void)_mbftowc(multic, &wchar, getkey, &Peekkey);
1840Sstevel@tonic-gate 			}
1850Sstevel@tonic-gate 			forbid (c == '0' || !isalpha(c) && !isascii(c) && !isdigit(c));
1860Sstevel@tonic-gate 			vreg = c;
1870Sstevel@tonic-gate 		}
1880Sstevel@tonic-gate reread:
1890Sstevel@tonic-gate 		/*
1900Sstevel@tonic-gate 		 * Come to reread from below after some macro expansions.
1910Sstevel@tonic-gate 		 * The call to map allows use of function key pads
1920Sstevel@tonic-gate 		 * by performing a terminal dependent mapping of inputs.
1930Sstevel@tonic-gate 		 */
1940Sstevel@tonic-gate #ifdef MDEBUG
1950Sstevel@tonic-gate 		if (trace)
1960Sstevel@tonic-gate 			fprintf(trace,"pcb=%c,",peekkey());
1970Sstevel@tonic-gate #endif
1980Sstevel@tonic-gate 		op = getkey();
1990Sstevel@tonic-gate 		maphopcnt = 0;
2000Sstevel@tonic-gate 		do {
2010Sstevel@tonic-gate 			/*
2020Sstevel@tonic-gate 			 * Keep mapping the char as long as it changes.
2030Sstevel@tonic-gate 			 * This allows for double mappings, e.g., q to #,
2040Sstevel@tonic-gate 			 * #1 to something else.
2050Sstevel@tonic-gate 			 */
2060Sstevel@tonic-gate 			c = op;
207*802Scf46844 			op = map(c, arrows, 0);
2080Sstevel@tonic-gate #ifdef MDEBUG
2090Sstevel@tonic-gate 			if (trace)
2100Sstevel@tonic-gate 				fprintf(trace,"pca=%c,",c);
2110Sstevel@tonic-gate #endif
2120Sstevel@tonic-gate 			/*
2130Sstevel@tonic-gate 			 * Maybe the mapped to char is a count. If so, we have
2140Sstevel@tonic-gate 			 * to go back to the "for" to interpret it. Likewise
2150Sstevel@tonic-gate 			 * for a buffer name.
2160Sstevel@tonic-gate 			 */
2170Sstevel@tonic-gate 			if ((isdigit(c) && c!='0') || c == '"') {
2180Sstevel@tonic-gate 				ungetkey(c);
2190Sstevel@tonic-gate 				goto looptop;
2200Sstevel@tonic-gate 			}
2210Sstevel@tonic-gate 			if (!value(vi_REMAP)) {
2220Sstevel@tonic-gate 				c = op;
2230Sstevel@tonic-gate 				break;
2240Sstevel@tonic-gate 			}
2250Sstevel@tonic-gate 			if (++maphopcnt > 256)
2260Sstevel@tonic-gate 				error(gettext("Infinite macro loop"));
2270Sstevel@tonic-gate 		} while (c != op);
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate 		/*
2300Sstevel@tonic-gate 		 * Begin to build an image of this command for possible
2310Sstevel@tonic-gate 		 * later repeat in the buffer workcmd.  It will be copied
2320Sstevel@tonic-gate 		 * to lastcmd by the routine setLAST
2330Sstevel@tonic-gate 		 * if/when completely specified.
2340Sstevel@tonic-gate 		 */
2350Sstevel@tonic-gate 		lastcp = workcmd;
2360Sstevel@tonic-gate 		if (!vglobp)
2370Sstevel@tonic-gate 			*lastcp++ = c;
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate 		/*
2400Sstevel@tonic-gate 		 * First level command decode.
2410Sstevel@tonic-gate 		 */
2420Sstevel@tonic-gate 		switch (c) {
2430Sstevel@tonic-gate 
2440Sstevel@tonic-gate 		/*
2450Sstevel@tonic-gate 		 * ^L		Clear screen e.g. after transmission error.
2460Sstevel@tonic-gate 		 */
2470Sstevel@tonic-gate 
2480Sstevel@tonic-gate 		/*
2490Sstevel@tonic-gate 		 * ^R		Retype screen, getting rid of @ lines.
2500Sstevel@tonic-gate 		 *		If in open, equivalent to ^L.
2510Sstevel@tonic-gate 		 *		On terminals where the right arrow key sends
2520Sstevel@tonic-gate 		 *		^L we make ^R act like ^L, since there is no
2530Sstevel@tonic-gate 		 *		way to get ^L.  These terminals (adm31, tvi)
2540Sstevel@tonic-gate 		 *		are intelligent so ^R is useless.  Soroc
2550Sstevel@tonic-gate 		 *		will probably foul this up, but nobody has
2560Sstevel@tonic-gate 		 *		one of them.
2570Sstevel@tonic-gate 		 */
2580Sstevel@tonic-gate 		case CTRL('l'):
2590Sstevel@tonic-gate 		case CTRL('r'):
2600Sstevel@tonic-gate 			if (c == CTRL('l') || (key_right && *key_right==CTRL('l'))) {
2610Sstevel@tonic-gate 				vclear();
2620Sstevel@tonic-gate 				vdirty(0, vcnt);
2630Sstevel@tonic-gate 			}
2640Sstevel@tonic-gate 			if (state != VISUAL) {
2650Sstevel@tonic-gate 				/*
2660Sstevel@tonic-gate 				 * Get a clean line, throw away the
2670Sstevel@tonic-gate 				 * memory of what is displayed now,
2680Sstevel@tonic-gate 				 * and move back onto the current line.
2690Sstevel@tonic-gate 				 */
2700Sstevel@tonic-gate 				vclean();
2710Sstevel@tonic-gate 				vcnt = 0;
2720Sstevel@tonic-gate 				vmoveto(dot, cursor, 0);
2730Sstevel@tonic-gate 				continue;
2740Sstevel@tonic-gate 			}
2750Sstevel@tonic-gate 			vredraw(WTOP);
2760Sstevel@tonic-gate 			/*
2770Sstevel@tonic-gate 			 * Weird glitch -- when we enter visual
2780Sstevel@tonic-gate 			 * in a very small window we may end up with
2790Sstevel@tonic-gate 			 * no lines on the screen because the line
2800Sstevel@tonic-gate 			 * at the top is too long.  This forces the screen
2810Sstevel@tonic-gate 			 * to be expanded to make room for it (after
2820Sstevel@tonic-gate 			 * we have printed @'s ick showing we goofed).
2830Sstevel@tonic-gate 			 */
2840Sstevel@tonic-gate 			if (vcnt == 0)
2850Sstevel@tonic-gate 				vrepaint(cursor);
2860Sstevel@tonic-gate 			vfixcurs();
2870Sstevel@tonic-gate 			continue;
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 		/*
2900Sstevel@tonic-gate 		 * $		Escape just cancels the current command
2910Sstevel@tonic-gate 		 *		with a little feedback.
2920Sstevel@tonic-gate 		 */
2930Sstevel@tonic-gate 		case ESCAPE:
294*802Scf46844 			(void) beep();
2950Sstevel@tonic-gate 			continue;
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate 		/*
2980Sstevel@tonic-gate 		 * @   		Macros. Bring in the macro and put it
2990Sstevel@tonic-gate 		 *		in vmacbuf, point vglobp there and punt.
3000Sstevel@tonic-gate 		 */
3010Sstevel@tonic-gate 		 case '@':
3020Sstevel@tonic-gate 			c = getesc();
3030Sstevel@tonic-gate 			if (c == 0)
3040Sstevel@tonic-gate 				continue;
3050Sstevel@tonic-gate 			if (c == '@')
3060Sstevel@tonic-gate 				c = lastmac;
3070Sstevel@tonic-gate 			if (isupper(c))
3080Sstevel@tonic-gate 				c = tolower(c);
3090Sstevel@tonic-gate 			forbid(!islower(c));
3100Sstevel@tonic-gate 			lastmac = c;
3110Sstevel@tonic-gate 			vsave();
3120Sstevel@tonic-gate 			CATCH
3130Sstevel@tonic-gate 				unsigned char tmpbuf[BUFSIZE];
3140Sstevel@tonic-gate 
315*802Scf46844 				regbuf(c, tmpbuf, sizeof (vmacbuf));
3160Sstevel@tonic-gate 				macpush(tmpbuf, 1);
3170Sstevel@tonic-gate 			ONERR
3180Sstevel@tonic-gate 				lastmac = 0;
3190Sstevel@tonic-gate 				splitw = 0;
3200Sstevel@tonic-gate 				getDOT();
3210Sstevel@tonic-gate 				vrepaint(cursor);
3220Sstevel@tonic-gate 				continue;
3230Sstevel@tonic-gate 			ENDCATCH
3240Sstevel@tonic-gate 			vmacp = vmacbuf;
3250Sstevel@tonic-gate 			goto reread;
3260Sstevel@tonic-gate 
3270Sstevel@tonic-gate 		/*
3280Sstevel@tonic-gate 		 * .		Repeat the last (modifying) open/visual command.
3290Sstevel@tonic-gate 		 */
3300Sstevel@tonic-gate 		case '.':
3310Sstevel@tonic-gate 			/*
3320Sstevel@tonic-gate 			 * Check that there was a last command, and
3330Sstevel@tonic-gate 			 * take its count and named buffer unless they
3340Sstevel@tonic-gate 			 * were given anew.  Special case if last command
3350Sstevel@tonic-gate 			 * referenced a numeric named buffer -- increment
3360Sstevel@tonic-gate 			 * the number and go to a named buffer again.
3370Sstevel@tonic-gate 			 * This allows a sequence like "1pu.u.u...
3380Sstevel@tonic-gate 			 * to successively look for stuff in the kill chain
3390Sstevel@tonic-gate 			 * much as one does in EMACS with C-Y and M-Y.
3400Sstevel@tonic-gate 			 */
3410Sstevel@tonic-gate 			forbid (lastcmd[0] == 0);
3420Sstevel@tonic-gate 			if (hadcnt)
3430Sstevel@tonic-gate 				lastcnt = cnt;
3440Sstevel@tonic-gate 			if (vreg)
3450Sstevel@tonic-gate 				lastreg = vreg;
3460Sstevel@tonic-gate 			else if (isdigit(lastreg) && lastreg < '9')
3470Sstevel@tonic-gate 				lastreg++;
3480Sstevel@tonic-gate 			vreg = lastreg;
3490Sstevel@tonic-gate 			cnt = lastcnt;
3500Sstevel@tonic-gate 			hadcnt = lasthad;
3510Sstevel@tonic-gate 			vglobp = lastcmd;
3520Sstevel@tonic-gate 			goto reread;
3530Sstevel@tonic-gate 
3540Sstevel@tonic-gate 		/*
3550Sstevel@tonic-gate 		 * ^U		Scroll up.  A count sticks around for
3560Sstevel@tonic-gate 		 *		future scrolls as the scroll amount.
3570Sstevel@tonic-gate 		 *		Attempt to hold the indentation from the
3580Sstevel@tonic-gate 		 *		top of the screen (in logical lines).
3590Sstevel@tonic-gate 		 *
3600Sstevel@tonic-gate 		 * BUG:		A ^U near the bottom of the screen
3610Sstevel@tonic-gate 		 *		on a dumb terminal (which can't roll back)
3620Sstevel@tonic-gate 		 *		causes the screen to be cleared and then
3630Sstevel@tonic-gate 		 *		redrawn almost as it was.  In this case
3640Sstevel@tonic-gate 		 *		one should simply move the cursor.
3650Sstevel@tonic-gate 		 */
3660Sstevel@tonic-gate 		case CTRL('u'):
3670Sstevel@tonic-gate 			if (hadcnt)
3680Sstevel@tonic-gate 				vSCROLL = cnt;
3690Sstevel@tonic-gate 			cnt = vSCROLL;
3700Sstevel@tonic-gate 			if (state == VISUAL)
3710Sstevel@tonic-gate 				ind = vcline, cnt += ind;
3720Sstevel@tonic-gate 			else
3730Sstevel@tonic-gate 				ind = 0;
3740Sstevel@tonic-gate 			vmoving = 0;
3750Sstevel@tonic-gate 			vup(cnt, ind, 1);
376*802Scf46844 			vnline((unsigned char *)NOSTR);
3770Sstevel@tonic-gate 			continue;
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate 		/*
3800Sstevel@tonic-gate 		 * ^D		Scroll down.  Like scroll up.
3810Sstevel@tonic-gate 		 */
3820Sstevel@tonic-gate 		case CTRL('d'):
3830Sstevel@tonic-gate #ifdef TRACE
3840Sstevel@tonic-gate 		if (trace)
3850Sstevel@tonic-gate 			fprintf(trace, "before vdown in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
3860Sstevel@tonic-gate #endif
3870Sstevel@tonic-gate 			if (hadcnt)
3880Sstevel@tonic-gate 				vSCROLL = cnt;
3890Sstevel@tonic-gate 			cnt = vSCROLL;
3900Sstevel@tonic-gate 			if (state == VISUAL)
3910Sstevel@tonic-gate 				ind = vcnt - vcline - 1, cnt += ind;
3920Sstevel@tonic-gate 			else
3930Sstevel@tonic-gate 				ind = 0;
3940Sstevel@tonic-gate 			vmoving = 0;
3950Sstevel@tonic-gate 			vdown(cnt, ind, 1);
3960Sstevel@tonic-gate #ifdef TRACE
3970Sstevel@tonic-gate 		if (trace)
3980Sstevel@tonic-gate 			fprintf(trace, "before vnline in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
3990Sstevel@tonic-gate #endif
400*802Scf46844 			vnline((unsigned char *)NOSTR);
4010Sstevel@tonic-gate #ifdef TRACE
4020Sstevel@tonic-gate 		if (trace)
4030Sstevel@tonic-gate 			fprintf(trace, "after vnline in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
4040Sstevel@tonic-gate #endif
4050Sstevel@tonic-gate 			continue;
4060Sstevel@tonic-gate 
4070Sstevel@tonic-gate 		/*
4080Sstevel@tonic-gate 		 * ^E		Glitch the screen down (one) line.
4090Sstevel@tonic-gate 		 *		Cursor left on same line in file.
4100Sstevel@tonic-gate 		 */
4110Sstevel@tonic-gate 		case CTRL('e'):
4120Sstevel@tonic-gate 			if (state != VISUAL)
4130Sstevel@tonic-gate 				continue;
4140Sstevel@tonic-gate 			if (!hadcnt)
4150Sstevel@tonic-gate 				cnt = 1;
4160Sstevel@tonic-gate 			/* Bottom line of file already on screen */
4170Sstevel@tonic-gate 			forbid(lineDOL()-lineDOT() <= vcnt-1-vcline);
4180Sstevel@tonic-gate 			ind = vcnt - vcline - 1 + cnt;
4190Sstevel@tonic-gate 			vdown(ind, ind, 1);
4200Sstevel@tonic-gate 			vnline(cursor);
4210Sstevel@tonic-gate 			continue;
4220Sstevel@tonic-gate 
4230Sstevel@tonic-gate 		/*
4240Sstevel@tonic-gate 		 * ^Y		Like ^E but up
4250Sstevel@tonic-gate 		 */
4260Sstevel@tonic-gate 		case CTRL('y'):
4270Sstevel@tonic-gate 			if (state != VISUAL)
4280Sstevel@tonic-gate 				continue;
4290Sstevel@tonic-gate 			if (!hadcnt)
4300Sstevel@tonic-gate 				cnt = 1;
4310Sstevel@tonic-gate 			forbid(lineDOT()-1<=vcline); /* line 1 already there */
4320Sstevel@tonic-gate 			ind = vcline + cnt;
4330Sstevel@tonic-gate 			vup(ind, ind, 1);
4340Sstevel@tonic-gate 			vnline(cursor);
4350Sstevel@tonic-gate 			continue;
4360Sstevel@tonic-gate 
4370Sstevel@tonic-gate 
4380Sstevel@tonic-gate 		/*
4390Sstevel@tonic-gate 		 * m		Mark position in mark register given
4400Sstevel@tonic-gate 		 *		by following letter.  Return is
4410Sstevel@tonic-gate 		 *		accomplished via ' or `; former
4420Sstevel@tonic-gate 		 *		to beginning of line where mark
4430Sstevel@tonic-gate 		 *		was set, latter to column where marked.
4440Sstevel@tonic-gate 		 */
4450Sstevel@tonic-gate 		case 'm':
4460Sstevel@tonic-gate 			/*
4470Sstevel@tonic-gate 			 * Getesc is generally used when a character
4480Sstevel@tonic-gate 			 * is read as a latter part of a command
4490Sstevel@tonic-gate 			 * to allow one to hit rubout/escape to cancel
4500Sstevel@tonic-gate 			 * what you have typed so far.  These characters
4510Sstevel@tonic-gate 			 * are mapped to 0 by the subroutine.
4520Sstevel@tonic-gate 			 */
4530Sstevel@tonic-gate 			c = getesc();
4540Sstevel@tonic-gate 			if (c == 0)
4550Sstevel@tonic-gate 				continue;
4560Sstevel@tonic-gate 
4570Sstevel@tonic-gate 			/*
4580Sstevel@tonic-gate 			 * Markreg checks that argument is a letter
4590Sstevel@tonic-gate 			 * and also maps ' and ` to the end of the range
4600Sstevel@tonic-gate 			 * to allow '' or `` to reference the previous
4610Sstevel@tonic-gate 			 * context mark.
4620Sstevel@tonic-gate 			 */
4630Sstevel@tonic-gate 			c = markreg(c);
4640Sstevel@tonic-gate 			forbid (c == 0);
4650Sstevel@tonic-gate 			vsave();
4660Sstevel@tonic-gate 			names[c - 'a'] = (*dot &~ 01);
4670Sstevel@tonic-gate 			ncols[c - 'a'] = cursor;
4680Sstevel@tonic-gate 			anymarks = 1;
4690Sstevel@tonic-gate 			continue;
4700Sstevel@tonic-gate 
4710Sstevel@tonic-gate 		/*
4720Sstevel@tonic-gate 		 * ^F		Window forwards, with 2 lines of continuity.
4730Sstevel@tonic-gate 		 *		Count repeats.
4740Sstevel@tonic-gate 		 */
4750Sstevel@tonic-gate 		case CTRL('f'):
4760Sstevel@tonic-gate 			vsave();
4770Sstevel@tonic-gate 			if (vcnt > 2) {
4780Sstevel@tonic-gate 				addr = dot + (vcnt - vcline) - 2 + (cnt-1)*basWLINES;
4790Sstevel@tonic-gate 				forbid(addr > dol);
4800Sstevel@tonic-gate 				dot = addr;
4810Sstevel@tonic-gate 				vcnt = vcline = 0;
4820Sstevel@tonic-gate 			}
4830Sstevel@tonic-gate 			vzop(0, 0, '+');
4840Sstevel@tonic-gate 			continue;
4850Sstevel@tonic-gate 
4860Sstevel@tonic-gate 		/*
4870Sstevel@tonic-gate 		 * ^B		Window backwards, with 2 lines of continuity.
4880Sstevel@tonic-gate 		 *		Inverse of ^F.
4890Sstevel@tonic-gate 		 */
4900Sstevel@tonic-gate 		case CTRL('b'):
4910Sstevel@tonic-gate 			vsave();
4920Sstevel@tonic-gate 			if (one + vcline != dot && vcnt > 2) {
4930Sstevel@tonic-gate 				addr = dot - vcline + 2 - (cnt-1)*basWLINES;
4940Sstevel@tonic-gate 				forbid (addr <= zero);
4950Sstevel@tonic-gate 				dot = addr;
4960Sstevel@tonic-gate 				vcnt = vcline = 0;
4970Sstevel@tonic-gate 			}
4980Sstevel@tonic-gate 			vzop(0, 0, '^');
4990Sstevel@tonic-gate 			continue;
5000Sstevel@tonic-gate 
5010Sstevel@tonic-gate 		/*
5020Sstevel@tonic-gate 		 * z		Screen adjustment, taking a following character:
5030Sstevel@tonic-gate 		 *			zcarriage_return		current line to top
5040Sstevel@tonic-gate 		 *			z<NL>		like zcarriage_return
5050Sstevel@tonic-gate 		 *			z-		current line to bottom
5060Sstevel@tonic-gate 		 *		also z+, z^ like ^F and ^B.
5070Sstevel@tonic-gate 		 *		A preceding count is line to use rather
5080Sstevel@tonic-gate 		 *		than current line.  A count between z and
5090Sstevel@tonic-gate 		 *		specifier character changes the screen size
5100Sstevel@tonic-gate 		 *		for the redraw.
5110Sstevel@tonic-gate 		 *
5120Sstevel@tonic-gate 		 */
5130Sstevel@tonic-gate 		case 'z':
5140Sstevel@tonic-gate 			if (state == VISUAL) {
5150Sstevel@tonic-gate 				i = vgetcnt();
5160Sstevel@tonic-gate 				if (i > 0)
5170Sstevel@tonic-gate 					vsetsiz(i);
5180Sstevel@tonic-gate 				c = getesc();
5190Sstevel@tonic-gate 				if (c == 0)
5200Sstevel@tonic-gate 					continue;
5210Sstevel@tonic-gate 			}
5220Sstevel@tonic-gate 			vsave();
5230Sstevel@tonic-gate 			vzop(hadcnt, cnt, c);
5240Sstevel@tonic-gate 			continue;
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate 		/*
5270Sstevel@tonic-gate 		 * Y		Yank lines, abbreviation for y_ or yy.
5280Sstevel@tonic-gate 		 *		Yanked lines can be put later if no
5290Sstevel@tonic-gate 		 *		changes intervene, or can be put in named
5300Sstevel@tonic-gate 		 *		buffers and put anytime in this session.
5310Sstevel@tonic-gate 		 */
5320Sstevel@tonic-gate 		case 'Y':
5330Sstevel@tonic-gate 			ungetkey('_');
5340Sstevel@tonic-gate 			c = 'y';
5350Sstevel@tonic-gate 			break;
5360Sstevel@tonic-gate 
5370Sstevel@tonic-gate 		/*
5380Sstevel@tonic-gate 		 * J		Join lines, 2 by default.  Count is number
5390Sstevel@tonic-gate 		 *		of lines to join (no join operator sorry.)
5400Sstevel@tonic-gate 		 */
5410Sstevel@tonic-gate 		case 'J':
5420Sstevel@tonic-gate 			forbid (dot == dol);
5430Sstevel@tonic-gate 			if (cnt == 1)
5440Sstevel@tonic-gate 				cnt = 2;
5450Sstevel@tonic-gate 			if (cnt > (i = dol - dot + 1))
5460Sstevel@tonic-gate 				cnt = i;
5470Sstevel@tonic-gate 			vsave();
5480Sstevel@tonic-gate 			vmacchng(1);
5490Sstevel@tonic-gate 			setLAST();
5500Sstevel@tonic-gate 			cursor = strend(linebuf);
5510Sstevel@tonic-gate 			vremote(cnt, join, 0);
5520Sstevel@tonic-gate 			notenam = (unsigned char *)"join";
5530Sstevel@tonic-gate 			vmoving = 0;
5540Sstevel@tonic-gate 			killU();
5550Sstevel@tonic-gate 			vreplace(vcline, cnt, 1);
5560Sstevel@tonic-gate 			if (!*cursor && cursor > linebuf)
5570Sstevel@tonic-gate 				cursor--;
5580Sstevel@tonic-gate 			if (notecnt == 2)
5590Sstevel@tonic-gate 				notecnt = 0;
5600Sstevel@tonic-gate 			vrepaint(cursor);
5610Sstevel@tonic-gate 			continue;
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate 		/*
5640Sstevel@tonic-gate 		 * S		Substitute text for whole lines, abbrev for c_.
5650Sstevel@tonic-gate 		 *		Count is number of lines to change.
5660Sstevel@tonic-gate 		 */
5670Sstevel@tonic-gate 		case 'S':
5680Sstevel@tonic-gate 			ungetkey('_');
5690Sstevel@tonic-gate 			c = 'c';
5700Sstevel@tonic-gate 			break;
5710Sstevel@tonic-gate 
5720Sstevel@tonic-gate 		/*
5730Sstevel@tonic-gate 		 * O		Create a new line above current and accept new
5740Sstevel@tonic-gate 		 *		input text, to an escape, there.
5750Sstevel@tonic-gate 		 *		A count specifies, for dumb terminals when
5760Sstevel@tonic-gate 		 *		slowopen is not set, the number of physical
5770Sstevel@tonic-gate 		 *		line space to open on the screen.
5780Sstevel@tonic-gate 		 *
5790Sstevel@tonic-gate 		 * o		Like O, but opens lines below.
5800Sstevel@tonic-gate 		 */
5810Sstevel@tonic-gate 		case 'O':
5820Sstevel@tonic-gate 		case 'o':
5830Sstevel@tonic-gate 			vmacchng(1);
5840Sstevel@tonic-gate 			voOpen(c, cnt);
5850Sstevel@tonic-gate 			continue;
5860Sstevel@tonic-gate 
5870Sstevel@tonic-gate 		/*
5880Sstevel@tonic-gate 		 * C		Change text to end of line, short for c$.
5890Sstevel@tonic-gate 		 */
5900Sstevel@tonic-gate 		case 'C':
5910Sstevel@tonic-gate 			if (*cursor) {
5920Sstevel@tonic-gate 				ungetkey('$'), c = 'c';
5930Sstevel@tonic-gate 				break;
5940Sstevel@tonic-gate 			}
5950Sstevel@tonic-gate 			goto appnd;
5960Sstevel@tonic-gate 
5970Sstevel@tonic-gate 		/*
5980Sstevel@tonic-gate 		 * ~	Switch case of letter under cursor
5990Sstevel@tonic-gate 		 */
6000Sstevel@tonic-gate 		case '~':
6010Sstevel@tonic-gate 			{
6020Sstevel@tonic-gate 				unsigned char mbuf[2049];
6030Sstevel@tonic-gate 				unsigned char *ccursor = cursor;
6040Sstevel@tonic-gate #ifdef PRESUNEUC
6050Sstevel@tonic-gate 				int tmp, length;
6060Sstevel@tonic-gate 				wchar_t wchar;
6070Sstevel@tonic-gate #else
6080Sstevel@tonic-gate 				int tmp, len, n;
6090Sstevel@tonic-gate 				wchar_t wc;
6100Sstevel@tonic-gate #endif /* PRESUNEUC */
6110Sstevel@tonic-gate 				unsigned char tmp1;
6120Sstevel@tonic-gate 				setLAST();
6130Sstevel@tonic-gate 				for (tmp = 0; tmp + 3 < 2048; ) {
6140Sstevel@tonic-gate 				/*
6150Sstevel@tonic-gate 				 * Use multiple 'r' commands to replace
6160Sstevel@tonic-gate 				 * alpha with alternate case.
6170Sstevel@tonic-gate 				 */
6180Sstevel@tonic-gate 
6190Sstevel@tonic-gate 					if(cnt-- <= 0)
6200Sstevel@tonic-gate 						break;
6210Sstevel@tonic-gate #ifdef PRESUNEUC
6220Sstevel@tonic-gate 					length = mbtowc(&wchar, (char *)ccursor, MULTI_BYTE_MAX);
6230Sstevel@tonic-gate #else
6240Sstevel@tonic-gate 					len = mbtowc(&wc, (char *)ccursor, MULTI_BYTE_MAX);
6250Sstevel@tonic-gate #endif /* PRESUNEUC */
6260Sstevel@tonic-gate #ifdef PRESUNEUC
6270Sstevel@tonic-gate 					if(length > 1) {
6280Sstevel@tonic-gate #else
6290Sstevel@tonic-gate 					n = iswalpha(wc);
6300Sstevel@tonic-gate 					if(len > 1 && !iswalpha(wc)) {
6310Sstevel@tonic-gate #endif /* PRESUNEUC */
6320Sstevel@tonic-gate 						mbuf[tmp+0] = ' ';
6330Sstevel@tonic-gate 						tmp++;
6340Sstevel@tonic-gate #ifdef PRESUNEUC
6350Sstevel@tonic-gate 						ccursor += length;
6360Sstevel@tonic-gate #else
6370Sstevel@tonic-gate 						ccursor += len;
6380Sstevel@tonic-gate #endif /* PRESUNEUC */
6390Sstevel@tonic-gate 						continue;
6400Sstevel@tonic-gate 					}
6410Sstevel@tonic-gate 					mbuf[tmp] = 'r';
6420Sstevel@tonic-gate #ifdef PRESUNEUC
6430Sstevel@tonic-gate 					mbuf[tmp+1] = *ccursor++;
6440Sstevel@tonic-gate #else
6450Sstevel@tonic-gate 					ccursor += ((len > 0) ? len : 1);
6460Sstevel@tonic-gate #endif /* PRESUNEUC */
6470Sstevel@tonic-gate 				/*
6480Sstevel@tonic-gate 				 * If pointing to an alpha character,
6490Sstevel@tonic-gate 				 * change the case.
6500Sstevel@tonic-gate 				 */
6510Sstevel@tonic-gate 
6520Sstevel@tonic-gate 					tmp1 = mbuf[tmp+1];
6530Sstevel@tonic-gate #ifdef PRESUNEUC
6540Sstevel@tonic-gate 					if (isupper((unsigned char)tmp1))
6550Sstevel@tonic-gate 						mbuf[tmp+1] = tolower((unsigned char)tmp1);
6560Sstevel@tonic-gate 					else
6570Sstevel@tonic-gate 						mbuf[tmp+1] = toupper((unsigned char)tmp1);
6580Sstevel@tonic-gate #else
6590Sstevel@tonic-gate 					if (iswupper(wc))
6600Sstevel@tonic-gate 						len = wctomb((char *)(mbuf + tmp + 1),
6610Sstevel@tonic-gate 							(wc = towlower(wc)));
6620Sstevel@tonic-gate 					else
6630Sstevel@tonic-gate 						len = wctomb((char *)(mbuf + tmp + 1),
6640Sstevel@tonic-gate 							(wc = towupper(wc)));
6650Sstevel@tonic-gate 					tmp += len - 1;
6660Sstevel@tonic-gate #endif /* PRESUNEUC */
6670Sstevel@tonic-gate 					if(*ccursor)
6680Sstevel@tonic-gate 				/*
6690Sstevel@tonic-gate 				 * If at end of line do not advance
6700Sstevel@tonic-gate 				 * to the next character, else use a
6710Sstevel@tonic-gate 				 * space to advance 1 column.
6720Sstevel@tonic-gate 				 */
6730Sstevel@tonic-gate 						mbuf[tmp+2] = ' ';
6740Sstevel@tonic-gate 					else {
6750Sstevel@tonic-gate 						mbuf[tmp+2] = '\0';
6760Sstevel@tonic-gate 						tmp +=3;
6770Sstevel@tonic-gate 						break;
6780Sstevel@tonic-gate 					}
6790Sstevel@tonic-gate 					tmp += 3;
6800Sstevel@tonic-gate 				}
6810Sstevel@tonic-gate 
6820Sstevel@tonic-gate 				mbuf[tmp] = 0;
6830Sstevel@tonic-gate 				macpush(mbuf, 1);
6840Sstevel@tonic-gate 			}
6850Sstevel@tonic-gate 			continue;
6860Sstevel@tonic-gate 
6870Sstevel@tonic-gate 
6880Sstevel@tonic-gate 		/*
6890Sstevel@tonic-gate 		 * A		Append at end of line, short for $a.
6900Sstevel@tonic-gate 		 */
6910Sstevel@tonic-gate 		case 'A':
6920Sstevel@tonic-gate 			operate('$', 1);
6930Sstevel@tonic-gate appnd:
6940Sstevel@tonic-gate 			c = 'a';
6950Sstevel@tonic-gate 			/* fall into ... */
6960Sstevel@tonic-gate 
6970Sstevel@tonic-gate 		/*
6980Sstevel@tonic-gate 		 * a		Appends text after cursor.  Text can continue
6990Sstevel@tonic-gate 		 *		through arbitrary number of lines.
7000Sstevel@tonic-gate 		 */
7010Sstevel@tonic-gate 		case 'a':
7020Sstevel@tonic-gate 			if (*cursor) {
7030Sstevel@tonic-gate 				wchar_t wchar;
7040Sstevel@tonic-gate 				int length = mbtowc(&wchar, (char *)cursor, MULTI_BYTE_MAX);
7050Sstevel@tonic-gate 				if (state == HARDOPEN) {
7060Sstevel@tonic-gate 					if(length < 0) {
7070Sstevel@tonic-gate 						putoctal = 1;
7080Sstevel@tonic-gate 						putchar(*cursor);
7090Sstevel@tonic-gate 						putoctal = 0;
7100Sstevel@tonic-gate 					} else
7110Sstevel@tonic-gate 						putchar(wchar);
7120Sstevel@tonic-gate 				}
7130Sstevel@tonic-gate 				if(length < 0)
7140Sstevel@tonic-gate 					cursor++;
7150Sstevel@tonic-gate 				else
7160Sstevel@tonic-gate 					cursor += length;
7170Sstevel@tonic-gate 			}
7180Sstevel@tonic-gate 			goto insrt;
7190Sstevel@tonic-gate 
7200Sstevel@tonic-gate 		/*
7210Sstevel@tonic-gate 		 * I		Insert at beginning of whitespace of line,
7220Sstevel@tonic-gate 		 *		short for ^i.
7230Sstevel@tonic-gate 		 */
7240Sstevel@tonic-gate 		case 'I':
7250Sstevel@tonic-gate 			operate('^', 1);
7260Sstevel@tonic-gate 			c = 'i';
7270Sstevel@tonic-gate 			/* fall into ... */
7280Sstevel@tonic-gate 
7290Sstevel@tonic-gate 		/*
7300Sstevel@tonic-gate 		 * R		Replace characters, one for one, by input
7310Sstevel@tonic-gate 		 *		(logically), like repeated r commands.
7320Sstevel@tonic-gate 		 *
7330Sstevel@tonic-gate 		 * BUG:		This is like the typeover mode of many other
7340Sstevel@tonic-gate 		 *		editors, and is only rarely useful.  Its
7350Sstevel@tonic-gate 		 *		implementation is a hack in a low level
7360Sstevel@tonic-gate 		 *		routine and it doesn't work very well, e.g.
7370Sstevel@tonic-gate 		 *		you can't move around within a R, etc.
7380Sstevel@tonic-gate 		 */
7390Sstevel@tonic-gate 		case 'R':
7400Sstevel@tonic-gate 			/* fall into... */
7410Sstevel@tonic-gate 
7420Sstevel@tonic-gate 		/*
7430Sstevel@tonic-gate 		 * i		Insert text to an escape in the buffer.
7440Sstevel@tonic-gate 		 *		Text is arbitrary.  This command reminds of
7450Sstevel@tonic-gate 		 *		the i command in bare teco.
7460Sstevel@tonic-gate 		 */
7470Sstevel@tonic-gate 		case 'i':
7480Sstevel@tonic-gate insrt:
7490Sstevel@tonic-gate 			/*
7500Sstevel@tonic-gate 			 * Common code for all the insertion commands.
7510Sstevel@tonic-gate 			 * Save for redo, position cursor, prepare for append
7520Sstevel@tonic-gate 			 * at command and in visual undo.  Note that nothing
7530Sstevel@tonic-gate 			 * is doomed, unless R when all is, and save the
7540Sstevel@tonic-gate 			 * current line in a the undo temporary buffer.
7550Sstevel@tonic-gate 			 */
7560Sstevel@tonic-gate 			vmacchng(1);
7570Sstevel@tonic-gate 			setLAST();
7580Sstevel@tonic-gate 			vcursat(cursor);
7590Sstevel@tonic-gate 			prepapp();
7600Sstevel@tonic-gate 			vnoapp();
7610Sstevel@tonic-gate 			doomed = c == 'R' ? 10000 : 0;
7620Sstevel@tonic-gate 			if(FIXUNDO)
7630Sstevel@tonic-gate 				vundkind = VCHNG;
7640Sstevel@tonic-gate 			vmoving = 0;
7650Sstevel@tonic-gate 			CP(vutmp, linebuf);
7660Sstevel@tonic-gate 
7670Sstevel@tonic-gate 			/*
7680Sstevel@tonic-gate 			 * If this is a repeated command, then suppress
7690Sstevel@tonic-gate 			 * fake insert mode on dumb terminals which looks
7700Sstevel@tonic-gate 			 * ridiculous and wastes lots of time even at 9600B.
7710Sstevel@tonic-gate 			 */
7720Sstevel@tonic-gate 			if (vglobp)
7730Sstevel@tonic-gate 				hold = HOLDQIK;
7740Sstevel@tonic-gate 			vappend(c, cnt, 0);
7750Sstevel@tonic-gate 			continue;
7760Sstevel@tonic-gate 
7770Sstevel@tonic-gate 		/*
7780Sstevel@tonic-gate 		 * 	An attention, normally a DEL, just beeps.
7790Sstevel@tonic-gate 		 *	If you are a vi command within ex, then
7800Sstevel@tonic-gate 		 *	two ATTN's will drop you back to command mode.
7810Sstevel@tonic-gate 		 */
7820Sstevel@tonic-gate 		case ATTN:
783*802Scf46844 			(void) beep();
7840Sstevel@tonic-gate 			if (initev || peekkey() != ATTN)
7850Sstevel@tonic-gate 				continue;
7860Sstevel@tonic-gate 			/* fall into... */
7870Sstevel@tonic-gate 
7880Sstevel@tonic-gate 		/*
7890Sstevel@tonic-gate 		 * ^\		A quit always gets command mode.
7900Sstevel@tonic-gate 		 */
7910Sstevel@tonic-gate 		case QUIT:
7920Sstevel@tonic-gate 			/*
7930Sstevel@tonic-gate 			 * Have to be careful if we were called
7940Sstevel@tonic-gate 			 *	g/xxx/vi
7950Sstevel@tonic-gate 			 * since a return will just start up again.
7960Sstevel@tonic-gate 			 * So we simulate an interrupt.
7970Sstevel@tonic-gate 			 */
7980Sstevel@tonic-gate 			if (inglobal)
7990Sstevel@tonic-gate 				onintr(0);
8000Sstevel@tonic-gate 			/* fall into... */
8010Sstevel@tonic-gate 
8020Sstevel@tonic-gate #ifdef notdef
8030Sstevel@tonic-gate 		/*
8040Sstevel@tonic-gate 		 * q		Quit back to command mode, unless called as
8050Sstevel@tonic-gate 		 *		vi on command line in which case dont do it
8060Sstevel@tonic-gate 		 */
8070Sstevel@tonic-gate 		case 'q':	/* quit */
8080Sstevel@tonic-gate 			if (initev) {
8090Sstevel@tonic-gate 				vsave();
8100Sstevel@tonic-gate 				CATCH
8110Sstevel@tonic-gate 					error(gettext("Q gets ex command mode, :q leaves vi"));
8120Sstevel@tonic-gate 				ENDCATCH
8130Sstevel@tonic-gate 				splitw = 0;
8140Sstevel@tonic-gate 				getDOT();
8150Sstevel@tonic-gate 				vrepaint(cursor);
8160Sstevel@tonic-gate 				continue;
8170Sstevel@tonic-gate 			}
8180Sstevel@tonic-gate #endif
8190Sstevel@tonic-gate 			/* fall into... */
8200Sstevel@tonic-gate 
8210Sstevel@tonic-gate 		/*
8220Sstevel@tonic-gate 		 * Q		Is like q, but always gets to command mode
8230Sstevel@tonic-gate 		 *		even if command line invocation was as vi.
8240Sstevel@tonic-gate 		 */
8250Sstevel@tonic-gate 		case 'Q':
8260Sstevel@tonic-gate 			vsave();
8270Sstevel@tonic-gate 			/*
8280Sstevel@tonic-gate 			 * If we are in the middle of a macro, throw away
8290Sstevel@tonic-gate 			 * the rest and fix up undo.
8300Sstevel@tonic-gate 			 * This code copied from getbr().
8310Sstevel@tonic-gate 			 */
8320Sstevel@tonic-gate 			if (vmacp) {
8330Sstevel@tonic-gate 				vmacp = 0;
8340Sstevel@tonic-gate 				if (inopen == -1)	/* don't mess up undo for esc esc */
8350Sstevel@tonic-gate 					vundkind = VMANY;
8360Sstevel@tonic-gate 				inopen = 1;	/* restore old setting now that macro done */
8370Sstevel@tonic-gate 			}
8380Sstevel@tonic-gate 			ixlatctl(1);
8390Sstevel@tonic-gate 			return;
8400Sstevel@tonic-gate 
8410Sstevel@tonic-gate 
8420Sstevel@tonic-gate 		/*
8430Sstevel@tonic-gate 		 * ZZ		Like :x
8440Sstevel@tonic-gate 		 */
8450Sstevel@tonic-gate 		 case 'Z':
8460Sstevel@tonic-gate 			forbid(getkey() != 'Z');
8470Sstevel@tonic-gate 			oglobp = globp;
8480Sstevel@tonic-gate 			globp = (unsigned char *)"x";
8490Sstevel@tonic-gate 			vclrech(0);
8500Sstevel@tonic-gate 			goto gogo;
8510Sstevel@tonic-gate 
8520Sstevel@tonic-gate 		/*
8530Sstevel@tonic-gate 		 * P		Put back text before cursor or before current
8540Sstevel@tonic-gate 		 *		line.  If text was whole lines goes back
8550Sstevel@tonic-gate 		 *		as whole lines.  If part of a single line
8560Sstevel@tonic-gate 		 *		or parts of whole lines splits up current
8570Sstevel@tonic-gate 		 *		line to form many new lines.
8580Sstevel@tonic-gate 		 *		May specify a named buffer, or the delete
8590Sstevel@tonic-gate 		 *		saving buffers 1-9.
8600Sstevel@tonic-gate 		 *
8610Sstevel@tonic-gate 		 * p		Like P but after rather than before.
8620Sstevel@tonic-gate 		 */
8630Sstevel@tonic-gate 		case 'P':
8640Sstevel@tonic-gate 		case 'p':
8650Sstevel@tonic-gate 			vmoving = 0;
8660Sstevel@tonic-gate #ifdef XPG4
8670Sstevel@tonic-gate 			P_cursor_offset = 0;
8680Sstevel@tonic-gate #endif
8690Sstevel@tonic-gate #ifdef notdef
8700Sstevel@tonic-gate 			forbid (!vreg && value(vi_UNDOMACRO) && inopen < 0);
8710Sstevel@tonic-gate #endif
8720Sstevel@tonic-gate 			/*
8730Sstevel@tonic-gate 			 * If previous delete was partial line, use an
8740Sstevel@tonic-gate 			 * append or insert to put it back so as to
8750Sstevel@tonic-gate 			 * use insert mode on intelligent terminals.
8760Sstevel@tonic-gate 			 */
8770Sstevel@tonic-gate 			if (!vreg && DEL[0]) {
8780Sstevel@tonic-gate 				setLAST();
8790Sstevel@tonic-gate 				forbid ((unsigned char)DEL[128] == 0200);
8800Sstevel@tonic-gate 				vglobp = DEL;
8810Sstevel@tonic-gate 				ungetkey(c == 'p' ? 'a' : 'i');
8820Sstevel@tonic-gate 				goto reread;
8830Sstevel@tonic-gate 			}
8840Sstevel@tonic-gate 
8850Sstevel@tonic-gate 			/*
8860Sstevel@tonic-gate 			 * If a register wasn't specified, then make
8870Sstevel@tonic-gate 			 * sure there is something to put back.
8880Sstevel@tonic-gate 			 */
8890Sstevel@tonic-gate 			forbid (!vreg && unddol == dol);
8900Sstevel@tonic-gate 			/*
8910Sstevel@tonic-gate 			 * If we just did a macro the whole buffer is in
8920Sstevel@tonic-gate 			 * the undo save area.  We don't want to put THAT.
8930Sstevel@tonic-gate 			 */
8940Sstevel@tonic-gate 			forbid (vundkind == VMANY && undkind==UNDALL);
8950Sstevel@tonic-gate 			vsave();
8960Sstevel@tonic-gate 			vmacchng(1);
8970Sstevel@tonic-gate 			setLAST();
8980Sstevel@tonic-gate 			i = 0;
8990Sstevel@tonic-gate 			if (vreg && partreg(vreg) || !vreg && pkill[0]) {
9000Sstevel@tonic-gate 				/*
9010Sstevel@tonic-gate 				 * Restoring multiple lines which were partial
9020Sstevel@tonic-gate 				 * lines; will leave cursor in middle
9030Sstevel@tonic-gate 				 * of line after shoving restored text in to
9040Sstevel@tonic-gate 				 * split the current line.
9050Sstevel@tonic-gate 				 */
9060Sstevel@tonic-gate 				i++;
9070Sstevel@tonic-gate 				if (c == 'p' && *cursor)
9080Sstevel@tonic-gate 					cursor = nextchr(cursor);
9090Sstevel@tonic-gate 			} else {
9100Sstevel@tonic-gate 				/*
9110Sstevel@tonic-gate 				 * In whole line case, have to back up dot
9120Sstevel@tonic-gate 				 * for P; also want to clear cursor so
9130Sstevel@tonic-gate 				 * cursor will eventually be positioned
9140Sstevel@tonic-gate 				 * at the beginning of the first put line.
9150Sstevel@tonic-gate 				 */
9160Sstevel@tonic-gate 				cursor = 0;
9170Sstevel@tonic-gate 				if (c == 'P') {
9180Sstevel@tonic-gate 					dot--, vcline--;
9190Sstevel@tonic-gate 					c = 'p';
9200Sstevel@tonic-gate 				}
9210Sstevel@tonic-gate 			}
9220Sstevel@tonic-gate 			killU();
9230Sstevel@tonic-gate 
9240Sstevel@tonic-gate 			/*
9250Sstevel@tonic-gate 			 * The call to putreg can potentially
9260Sstevel@tonic-gate 			 * bomb since there may be nothing in a named buffer.
9270Sstevel@tonic-gate 			 * We thus put a catch in here.  If we didn't and
9280Sstevel@tonic-gate 			 * there was an error we would end up in command mode.
9290Sstevel@tonic-gate 			 */
9300Sstevel@tonic-gate 			addr = dol;	/* old dol */
9310Sstevel@tonic-gate 			CATCH
932*802Scf46844 				vremote(1,
933*802Scf46844 				    vreg ? (int (*)())putreg : put, vreg);
9340Sstevel@tonic-gate 			ONERR
9350Sstevel@tonic-gate 				if (vreg == -1) {
9360Sstevel@tonic-gate 					splitw = 0;
9370Sstevel@tonic-gate 					if (op == 'P')
9380Sstevel@tonic-gate 						dot++, vcline++;
9390Sstevel@tonic-gate 					goto pfixup;
9400Sstevel@tonic-gate 				}
9410Sstevel@tonic-gate 			ENDCATCH
9420Sstevel@tonic-gate 			splitw = 0;
9430Sstevel@tonic-gate 			nlput = dol - addr + 1;
9440Sstevel@tonic-gate 			if (!i) {
9450Sstevel@tonic-gate 				/*
9460Sstevel@tonic-gate 				 * Increment undap1, undap2 to make up
9470Sstevel@tonic-gate 				 * for their incorrect initialization in the
9480Sstevel@tonic-gate 				 * routine vremote before calling put/putreg.
9490Sstevel@tonic-gate 				 */
9500Sstevel@tonic-gate 				if (FIXUNDO)
9510Sstevel@tonic-gate 					undap1++, undap2++;
9520Sstevel@tonic-gate 				vcline++;
9530Sstevel@tonic-gate 				nlput--;
9540Sstevel@tonic-gate 
9550Sstevel@tonic-gate 				/*
9560Sstevel@tonic-gate 				 * After a put want current line first line,
9570Sstevel@tonic-gate 				 * and dot was made the last line put in code
9580Sstevel@tonic-gate 				 * run so far.  This is why we increment vcline
9590Sstevel@tonic-gate 				 * above and decrease dot here.
9600Sstevel@tonic-gate 				 */
9610Sstevel@tonic-gate 				dot -= nlput - 1;
9620Sstevel@tonic-gate 			}
9630Sstevel@tonic-gate #ifdef TRACE
9640Sstevel@tonic-gate 			if (trace)
9650Sstevel@tonic-gate 				fprintf(trace, "vreplace(%d, %d, %d), undap1=%d, undap2=%d, dot=%d\n", vcline, i, nlput, lineno(undap1), lineno(undap2), lineno(dot));
9660Sstevel@tonic-gate #endif
9670Sstevel@tonic-gate 			vreplace(vcline, i, nlput);
9680Sstevel@tonic-gate #ifdef XPG4
9690Sstevel@tonic-gate 			if (op == 'P' && i > 0) {
9700Sstevel@tonic-gate 				dot += nlput - 1;
9710Sstevel@tonic-gate 				vcline += nlput - 1;
9720Sstevel@tonic-gate 				cursor += P_cursor_offset;
9730Sstevel@tonic-gate 			}
9740Sstevel@tonic-gate #endif
9750Sstevel@tonic-gate 			if (state != VISUAL) {
9760Sstevel@tonic-gate 				/*
9770Sstevel@tonic-gate 				 * Special case in open mode.
9780Sstevel@tonic-gate 				 * Force action on the screen when a single
9790Sstevel@tonic-gate 				 * line is put even if it is identical to
9800Sstevel@tonic-gate 				 * the current line, e.g. on YP; otherwise
9810Sstevel@tonic-gate 				 * you can't tell anything happened.
9820Sstevel@tonic-gate 				 */
9830Sstevel@tonic-gate 				vjumpto(dot, cursor, '.');
9840Sstevel@tonic-gate 				continue;
9850Sstevel@tonic-gate 			}
9860Sstevel@tonic-gate pfixup:
9870Sstevel@tonic-gate 			vrepaint(cursor);
9880Sstevel@tonic-gate 			vfixcurs();
9890Sstevel@tonic-gate 			continue;
9900Sstevel@tonic-gate 
9910Sstevel@tonic-gate 		/*
9920Sstevel@tonic-gate 		 * ^^		Return to previous file.
9930Sstevel@tonic-gate 		 *		Like a :e #, and thus can be used after a
9940Sstevel@tonic-gate 		 *		"No Write" diagnostic.
9950Sstevel@tonic-gate 		 */
9960Sstevel@tonic-gate 		case CTRL('^'):
9970Sstevel@tonic-gate 			forbid (hadcnt);
9980Sstevel@tonic-gate 			vsave();
9990Sstevel@tonic-gate 			ckaw();
10000Sstevel@tonic-gate 			oglobp = globp;
10010Sstevel@tonic-gate 			if (value(vi_AUTOWRITE) && !value(vi_READONLY))
10020Sstevel@tonic-gate 				globp = (unsigned char *)"e! #";
10030Sstevel@tonic-gate 			else
10040Sstevel@tonic-gate 				globp = (unsigned char *)"e #";
10050Sstevel@tonic-gate 			goto gogo;
10060Sstevel@tonic-gate 
10070Sstevel@tonic-gate #ifdef TAG_STACK
10080Sstevel@tonic-gate                 /*
10090Sstevel@tonic-gate                  * ^T           Pop the tag stack if enabled or else reset it
10100Sstevel@tonic-gate                  *              if not.
10110Sstevel@tonic-gate                  */
10120Sstevel@tonic-gate                 case CTRL('t'):
10130Sstevel@tonic-gate                         forbid (hadcnt);
10140Sstevel@tonic-gate                         vsave();
10150Sstevel@tonic-gate                         oglobp = globp;
10160Sstevel@tonic-gate                         globp = (unsigned char *) "pop";
10170Sstevel@tonic-gate                         goto gogo;
10180Sstevel@tonic-gate #endif
10190Sstevel@tonic-gate 		/*
10200Sstevel@tonic-gate 		 * ^]		Takes word after cursor as tag, and then does
10210Sstevel@tonic-gate 		 *		tag command.  Read ``go right to''.
10220Sstevel@tonic-gate 		 *		This is not a search, so the wrapscan setting
10230Sstevel@tonic-gate 		 *		must be ignored.  If set, then it is unset
10240Sstevel@tonic-gate 		 *		here and restored later.
10250Sstevel@tonic-gate 		 */
10260Sstevel@tonic-gate 		case CTRL(']'):
10270Sstevel@tonic-gate 			grabtag();
10280Sstevel@tonic-gate 			oglobp = globp;
10290Sstevel@tonic-gate 			if (value(vi_WRAPSCAN) == 0) {
10300Sstevel@tonic-gate 				tag_reset_wrap = 1;
10310Sstevel@tonic-gate 				value(vi_WRAPSCAN) = 1;
10320Sstevel@tonic-gate 			}
10330Sstevel@tonic-gate 			globp = (unsigned char *)"tag";
10340Sstevel@tonic-gate 			goto gogo;
10350Sstevel@tonic-gate 
10360Sstevel@tonic-gate 		/*
10370Sstevel@tonic-gate 		 * &		Like :&
10380Sstevel@tonic-gate 		 */
10390Sstevel@tonic-gate 		 case '&':
10400Sstevel@tonic-gate 			oglobp = globp;
10410Sstevel@tonic-gate 			globp = (unsigned char *)"&";
10420Sstevel@tonic-gate 			goto gogo;
10430Sstevel@tonic-gate 
10440Sstevel@tonic-gate 		/*
10450Sstevel@tonic-gate 		 * ^G		Bring up a status line at the bottom of
10460Sstevel@tonic-gate 		 *		the screen, like a :file command.
10470Sstevel@tonic-gate 		 *
10480Sstevel@tonic-gate 		 * BUG:		Was ^S but doesn't work in cbreak mode
10490Sstevel@tonic-gate 		 */
10500Sstevel@tonic-gate 		case CTRL('g'):
10510Sstevel@tonic-gate 			oglobp = globp;
10520Sstevel@tonic-gate 			globp = (unsigned char *)"file";
10530Sstevel@tonic-gate gogo:
10540Sstevel@tonic-gate 			addr = dot;
10550Sstevel@tonic-gate 			vsave();
10560Sstevel@tonic-gate 			goto doinit;
10570Sstevel@tonic-gate 
10580Sstevel@tonic-gate #ifdef SIGTSTP
10590Sstevel@tonic-gate 		/*
10600Sstevel@tonic-gate 		 * ^Z:	suspend editor session and temporarily return
10610Sstevel@tonic-gate 		 * 	to shell.  Only works with Berkeley/IIASA process
10620Sstevel@tonic-gate 		 *	control in kernel.
10630Sstevel@tonic-gate 		 */
10640Sstevel@tonic-gate 		case CTRL('z'):
10650Sstevel@tonic-gate 			forbid(dosusp == 0);
10660Sstevel@tonic-gate 			vsave();
10670Sstevel@tonic-gate 			oglobp = globp;
10680Sstevel@tonic-gate 			globp = (unsigned char *)"stop";
10690Sstevel@tonic-gate 			goto gogo;
10700Sstevel@tonic-gate #endif
10710Sstevel@tonic-gate 
10720Sstevel@tonic-gate 		/*
10730Sstevel@tonic-gate 		 * :		Read a command from the echo area and
10740Sstevel@tonic-gate 		 *		execute it in command mode.
10750Sstevel@tonic-gate 		 */
10760Sstevel@tonic-gate 		case ':':
10770Sstevel@tonic-gate 			forbid (hadcnt);
10780Sstevel@tonic-gate 			vsave();
10790Sstevel@tonic-gate 			i = tchng;
10800Sstevel@tonic-gate 			addr = dot;
10810Sstevel@tonic-gate 			if (readecho(c)) {
10820Sstevel@tonic-gate 				esave[0] = 0;
10830Sstevel@tonic-gate 				goto fixup;
10840Sstevel@tonic-gate 			}
10850Sstevel@tonic-gate 			getDOT();
10860Sstevel@tonic-gate 			/*
10870Sstevel@tonic-gate 			 * Use the visual undo buffer to store the global
10880Sstevel@tonic-gate 			 * string for command mode, since it is idle right now.
10890Sstevel@tonic-gate 			 */
10900Sstevel@tonic-gate 			oglobp = globp; strcpy(vutmp, genbuf+1); globp = vutmp;
10910Sstevel@tonic-gate doinit:
10920Sstevel@tonic-gate 			esave[0] = 0;
10930Sstevel@tonic-gate 			fixech();
10940Sstevel@tonic-gate 
10950Sstevel@tonic-gate 			/*
10960Sstevel@tonic-gate 			 * Have to finagle around not to lose last
10970Sstevel@tonic-gate 			 * character after this command (when run from ex
10980Sstevel@tonic-gate 			 * command mode).  This is clumsy.
10990Sstevel@tonic-gate 			 */
11000Sstevel@tonic-gate 			d = peekc; ungetchar(0);
11010Sstevel@tonic-gate 			if (shouldpo) {
11020Sstevel@tonic-gate 				/*
11030Sstevel@tonic-gate 				 * So after a "Hit return..." ":", we do
11040Sstevel@tonic-gate 				 * another "Hit return..." the next time
11050Sstevel@tonic-gate 				 */
11060Sstevel@tonic-gate 				pofix();
11070Sstevel@tonic-gate 				shouldpo = 0;
11080Sstevel@tonic-gate 			}
11090Sstevel@tonic-gate 			CATCH
11100Sstevel@tonic-gate 				/*
11110Sstevel@tonic-gate 				 * Save old values of options so we can
11120Sstevel@tonic-gate 				 * notice when they change; switch into
11130Sstevel@tonic-gate 				 * cooked mode so we are interruptible.
11140Sstevel@tonic-gate 				 */
11150Sstevel@tonic-gate 				onumber = value(vi_NUMBER);
11160Sstevel@tonic-gate 				olist = value(vi_LIST);
11170Sstevel@tonic-gate 				OPline = Pline;
11180Sstevel@tonic-gate 				OPutchar = Putchar;
11190Sstevel@tonic-gate #ifndef CBREAK
11200Sstevel@tonic-gate 				vcook();
11210Sstevel@tonic-gate #endif
11220Sstevel@tonic-gate 				commands(1, 1);
11230Sstevel@tonic-gate 				if (dot == zero && dol > zero)
11240Sstevel@tonic-gate 					dot = one;
11250Sstevel@tonic-gate #ifndef CBREAK
11260Sstevel@tonic-gate 				vraw();
11270Sstevel@tonic-gate #endif
11280Sstevel@tonic-gate 			ONERR
11290Sstevel@tonic-gate #ifndef CBREAK
11300Sstevel@tonic-gate 				vraw();
11310Sstevel@tonic-gate #endif
11320Sstevel@tonic-gate 				copy(esave, vtube[WECHO], TUBECOLS * sizeof(wchar_t));
11330Sstevel@tonic-gate 			ENDCATCH
11340Sstevel@tonic-gate 			fixol();
11350Sstevel@tonic-gate 			Pline = OPline;
11360Sstevel@tonic-gate 			Putchar = OPutchar;
11370Sstevel@tonic-gate 			ungetchar(d);
11380Sstevel@tonic-gate 			globp = oglobp;
11390Sstevel@tonic-gate 
11400Sstevel@tonic-gate 			/*
11410Sstevel@tonic-gate 			 * If we ended up with no lines in the buffer, make
11420Sstevel@tonic-gate 			 * a line.
11430Sstevel@tonic-gate 			 */
11440Sstevel@tonic-gate 			if (dot == zero) {
11450Sstevel@tonic-gate 				fixzero();
11460Sstevel@tonic-gate 			}
11470Sstevel@tonic-gate 			splitw = 0;
11480Sstevel@tonic-gate 
11490Sstevel@tonic-gate 			/*
11500Sstevel@tonic-gate 			 * Special case: did list/number options change?
11510Sstevel@tonic-gate 			 */
11520Sstevel@tonic-gate 			if (onumber != value(vi_NUMBER))
11530Sstevel@tonic-gate 				setnumb(value(vi_NUMBER));
11540Sstevel@tonic-gate 			if (olist != value(vi_LIST))
11550Sstevel@tonic-gate 				setlist(value(vi_LIST));
11560Sstevel@tonic-gate 
11570Sstevel@tonic-gate fixup:
11580Sstevel@tonic-gate 			/*
11590Sstevel@tonic-gate 			 * If a change occurred, other than
11600Sstevel@tonic-gate 			 * a write which clears changes, then
11610Sstevel@tonic-gate 			 * we should allow an undo even if .
11620Sstevel@tonic-gate 			 * didn't move.
11630Sstevel@tonic-gate 			 *
11640Sstevel@tonic-gate 			 * BUG: You can make this wrong by
11650Sstevel@tonic-gate 			 * tricking around with multiple commands
11660Sstevel@tonic-gate 			 * on one line of : escape, and including
11670Sstevel@tonic-gate 			 * a write command there, but it's not
11680Sstevel@tonic-gate 			 * worth worrying about.
11690Sstevel@tonic-gate 			 */
11700Sstevel@tonic-gate 			if (FIXUNDO && tchng && tchng != i)
11710Sstevel@tonic-gate 				vundkind = VMANY, cursor = 0;
11720Sstevel@tonic-gate 
11730Sstevel@tonic-gate 			/*
11740Sstevel@tonic-gate 			 * If we are about to do another :, hold off
11750Sstevel@tonic-gate 			 * updating of screen.
11760Sstevel@tonic-gate 			 */
11770Sstevel@tonic-gate 			if (vcnt < 0 && Peekkey == ':') {
11780Sstevel@tonic-gate 				getDOT();
11790Sstevel@tonic-gate 				shouldpo = 1;
11800Sstevel@tonic-gate 				continue;
11810Sstevel@tonic-gate 			}
11820Sstevel@tonic-gate 			shouldpo = 0;
11830Sstevel@tonic-gate 
11840Sstevel@tonic-gate 			/*
11850Sstevel@tonic-gate 			 * In the case where the file being edited is
11860Sstevel@tonic-gate 			 * new; e.g. if the initial state hasn't been
11870Sstevel@tonic-gate 			 * saved yet, then do so now.
11880Sstevel@tonic-gate 			 */
11890Sstevel@tonic-gate 			if (unddol == truedol) {
11900Sstevel@tonic-gate 				vundkind = VNONE;
11910Sstevel@tonic-gate 				Vlines = lineDOL();
11920Sstevel@tonic-gate 				if (!inglobal)
11930Sstevel@tonic-gate 					savevis();
11940Sstevel@tonic-gate 				addr = zero;
11950Sstevel@tonic-gate 				vcnt = 0;
11960Sstevel@tonic-gate 				if (esave[0] == 0)
11970Sstevel@tonic-gate 					copy(esave, vtube[WECHO], TUBECOLS * sizeof(wchar_t));
11980Sstevel@tonic-gate 			}
11990Sstevel@tonic-gate 
12000Sstevel@tonic-gate 			/*
12010Sstevel@tonic-gate 			 * If the current line moved reset the cursor position.
12020Sstevel@tonic-gate 			 */
12030Sstevel@tonic-gate 			if (dot != addr) {
12040Sstevel@tonic-gate 				vmoving = 0;
12050Sstevel@tonic-gate 				cursor = 0;
12060Sstevel@tonic-gate 			}
12070Sstevel@tonic-gate 
12080Sstevel@tonic-gate 			/*
12090Sstevel@tonic-gate 			 * If current line is not on screen or if we are
12100Sstevel@tonic-gate 			 * in open mode and . moved, then redraw.
12110Sstevel@tonic-gate 			 */
12120Sstevel@tonic-gate 			i = vcline + (dot - addr);
12130Sstevel@tonic-gate 			if(windowchg)
12140Sstevel@tonic-gate 				windowinit();
12150Sstevel@tonic-gate 			if (i < 0 || i >= vcnt && i >= -vcnt || state != VISUAL && dot != addr) {
12160Sstevel@tonic-gate 				if (state == CRTOPEN)
12170Sstevel@tonic-gate 					vup1();
12180Sstevel@tonic-gate 				if (vcnt > 0)
12190Sstevel@tonic-gate 					vcnt = 0;
1220*802Scf46844 				vjumpto(dot, (unsigned char *) 0, '.');
12210Sstevel@tonic-gate 			} else {
12220Sstevel@tonic-gate 				/*
12230Sstevel@tonic-gate 				 * Current line IS on screen.
12240Sstevel@tonic-gate 				 * If we did a [Hit return...] then
12250Sstevel@tonic-gate 				 * restore vcnt and clear screen if in visual
12260Sstevel@tonic-gate 				 */
12270Sstevel@tonic-gate 				vcline = i;
12280Sstevel@tonic-gate 				if (vcnt < 0) {
12290Sstevel@tonic-gate 					vcnt = -vcnt;
12300Sstevel@tonic-gate 					if (state == VISUAL)
12310Sstevel@tonic-gate 						vclear();
12320Sstevel@tonic-gate 					else if (state == CRTOPEN) {
12330Sstevel@tonic-gate 						vcnt = 0;
12340Sstevel@tonic-gate 					}
12350Sstevel@tonic-gate 				}
12360Sstevel@tonic-gate 
12370Sstevel@tonic-gate 				/*
12380Sstevel@tonic-gate 				 * Limit max value of vcnt based on $
12390Sstevel@tonic-gate 				 */
12400Sstevel@tonic-gate 				i = vcline + lineDOL() - lineDOT() + 1;
12410Sstevel@tonic-gate 				if (i < vcnt)
12420Sstevel@tonic-gate 					vcnt = i;
12430Sstevel@tonic-gate 
12440Sstevel@tonic-gate 				/*
12450Sstevel@tonic-gate 				 * Dirty and repaint.
12460Sstevel@tonic-gate 				 */
12470Sstevel@tonic-gate 				vdirty(0, lines);
12480Sstevel@tonic-gate 				vrepaint(cursor);
12490Sstevel@tonic-gate 			}
12500Sstevel@tonic-gate 
12510Sstevel@tonic-gate 			/*
12520Sstevel@tonic-gate 			 * If in visual, put back the echo area
12530Sstevel@tonic-gate 			 * if it was clobbered.
12540Sstevel@tonic-gate 			 */
12550Sstevel@tonic-gate 			if (state == VISUAL) {
12560Sstevel@tonic-gate 				int sdc = destcol, sdl = destline;
12570Sstevel@tonic-gate 
12580Sstevel@tonic-gate 				splitw++;
12590Sstevel@tonic-gate 				vigoto(WECHO, 0);
12600Sstevel@tonic-gate 				for (i = 0; i < TUBECOLS - 1; i++) {
12610Sstevel@tonic-gate 					if (esave[i] == 0)
12620Sstevel@tonic-gate 						break;
12630Sstevel@tonic-gate 					if(esave[i] != FILLER)
1264*802Scf46844 						(void) vputchar(esave[i]);
12650Sstevel@tonic-gate 				}
12660Sstevel@tonic-gate 				splitw = 0;
12670Sstevel@tonic-gate 				vgoto(sdl, sdc);
12680Sstevel@tonic-gate 			}
12690Sstevel@tonic-gate 			if (tag_reset_wrap == 1) {
12700Sstevel@tonic-gate 				tag_reset_wrap = 0;
12710Sstevel@tonic-gate 				value(vi_WRAPSCAN) = 0;
12720Sstevel@tonic-gate 			}
12730Sstevel@tonic-gate 			continue;
12740Sstevel@tonic-gate 
12750Sstevel@tonic-gate 		/*
12760Sstevel@tonic-gate 		 * u		undo the last changing command.
12770Sstevel@tonic-gate 		 */
12780Sstevel@tonic-gate 		case 'u':
12790Sstevel@tonic-gate 			vundo(1);
12800Sstevel@tonic-gate 			continue;
12810Sstevel@tonic-gate 
12820Sstevel@tonic-gate 		/*
12830Sstevel@tonic-gate 		 * U		restore current line to initial state.
12840Sstevel@tonic-gate 		 */
12850Sstevel@tonic-gate 		case 'U':
12860Sstevel@tonic-gate 			vUndo();
12870Sstevel@tonic-gate 			continue;
12880Sstevel@tonic-gate 
12890Sstevel@tonic-gate fonfon:
1290*802Scf46844 			(void) beep();
12910Sstevel@tonic-gate 			vmacp = 0;
12920Sstevel@tonic-gate 			inopen = 1;	/* might have been -1 */
12930Sstevel@tonic-gate 			continue;
12940Sstevel@tonic-gate 		}
12950Sstevel@tonic-gate 
12960Sstevel@tonic-gate 		/*
12970Sstevel@tonic-gate 		 * Rest of commands are decoded by the operate
12980Sstevel@tonic-gate 		 * routine.
12990Sstevel@tonic-gate 		 */
13000Sstevel@tonic-gate 		operate(c, cnt);
13010Sstevel@tonic-gate 	}
13020Sstevel@tonic-gate }
13030Sstevel@tonic-gate 
13040Sstevel@tonic-gate /*
13050Sstevel@tonic-gate  * Grab the word after the cursor so we can look for it as a tag.
13060Sstevel@tonic-gate  */
1307*802Scf46844 void
1308*802Scf46844 grabtag(void)
13090Sstevel@tonic-gate {
1310*802Scf46844 	unsigned char *cp, *dp;
13110Sstevel@tonic-gate 
13120Sstevel@tonic-gate 	cp = vpastwh(cursor);
13130Sstevel@tonic-gate 	if (*cp) {
13140Sstevel@tonic-gate 		dp = lasttag;
13150Sstevel@tonic-gate 		do {
13160Sstevel@tonic-gate 			if (dp < &lasttag[sizeof lasttag - 2])
13170Sstevel@tonic-gate 				*dp++ = *cp;
13180Sstevel@tonic-gate 			cp++;
13190Sstevel@tonic-gate 			/* only allow ascii alphabetics */
13200Sstevel@tonic-gate 		} while ((isascii(*cp) && isalpha(*cp)) || isdigit(*cp) || *cp == '_');
13210Sstevel@tonic-gate 		*dp++ = 0;
13220Sstevel@tonic-gate 	}
13230Sstevel@tonic-gate }
13240Sstevel@tonic-gate 
13250Sstevel@tonic-gate /*
13260Sstevel@tonic-gate  * Before appending lines, set up addr1 and
13270Sstevel@tonic-gate  * the command mode undo information.
13280Sstevel@tonic-gate  */
1329*802Scf46844 void
1330*802Scf46844 prepapp(void)
13310Sstevel@tonic-gate {
13320Sstevel@tonic-gate 
13330Sstevel@tonic-gate 	addr1 = dot;
13340Sstevel@tonic-gate 	deletenone();
13350Sstevel@tonic-gate 	addr1++;
13360Sstevel@tonic-gate 	appendnone();
13370Sstevel@tonic-gate }
13380Sstevel@tonic-gate 
13390Sstevel@tonic-gate /*
13400Sstevel@tonic-gate  * Execute function f with the address bounds addr1
13410Sstevel@tonic-gate  * and addr2 surrounding cnt lines starting at dot.
13420Sstevel@tonic-gate  */
1343*802Scf46844 void
13440Sstevel@tonic-gate vremote(cnt, f, arg)
13450Sstevel@tonic-gate 	int cnt, (*f)(), arg;
13460Sstevel@tonic-gate {
1347*802Scf46844 	int oing = inglobal;
13480Sstevel@tonic-gate 
13490Sstevel@tonic-gate 	addr1 = dot;
13500Sstevel@tonic-gate 	addr2 = dot + cnt - 1;
13510Sstevel@tonic-gate 	inglobal = 0;
13520Sstevel@tonic-gate 	if (FIXUNDO)
13530Sstevel@tonic-gate 		undap1 = undap2 = dot;
13540Sstevel@tonic-gate 	(*f)(arg);
13550Sstevel@tonic-gate 	inglobal = oing;
13560Sstevel@tonic-gate 	if (FIXUNDO)
13570Sstevel@tonic-gate 		vundkind = VMANY;
13580Sstevel@tonic-gate 	vmcurs = 0;
13590Sstevel@tonic-gate }
13600Sstevel@tonic-gate 
13610Sstevel@tonic-gate /*
13620Sstevel@tonic-gate  * Save the current contents of linebuf, if it has changed.
13630Sstevel@tonic-gate  */
1364*802Scf46844 void
1365*802Scf46844 vsave(void)
13660Sstevel@tonic-gate {
13670Sstevel@tonic-gate 	unsigned char temp[LBSIZE];
13680Sstevel@tonic-gate 
13690Sstevel@tonic-gate 	strncpy(temp, linebuf, sizeof (temp));
13700Sstevel@tonic-gate 	if (FIXUNDO && vundkind == VCHNG || vundkind == VCAPU) {
13710Sstevel@tonic-gate 		/*
13720Sstevel@tonic-gate 		 * If the undo state is saved in the temporary buffer
13730Sstevel@tonic-gate 		 * vutmp, then we sync this into the temp file so that
13740Sstevel@tonic-gate 		 * we will be able to undo even after we have moved off
13750Sstevel@tonic-gate 		 * the line.  It would be possible to associate a line
13760Sstevel@tonic-gate 		 * with vutmp but we assume that vutmp is only associated
13770Sstevel@tonic-gate 		 * with line dot (e.g. in case ':') above, so beware.
13780Sstevel@tonic-gate 		 */
13790Sstevel@tonic-gate 		prepapp();
13800Sstevel@tonic-gate 		strcLIN(vutmp);
13810Sstevel@tonic-gate 		putmark(dot);
13820Sstevel@tonic-gate 		vremote(1, yank, 0);
13830Sstevel@tonic-gate 		vundkind = VMCHNG;
13840Sstevel@tonic-gate 		notecnt = 0;
13850Sstevel@tonic-gate 		undkind = UNDCHANGE;
13860Sstevel@tonic-gate 	}
13870Sstevel@tonic-gate 	/*
13880Sstevel@tonic-gate 	 * Get the line out of the temp file and do nothing if it hasn't
13890Sstevel@tonic-gate 	 * changed.  This may seem like a loss, but the line will
13900Sstevel@tonic-gate 	 * almost always be in a read buffer so this may well avoid disk i/o.
13910Sstevel@tonic-gate 	 */
13920Sstevel@tonic-gate 	getDOT();
13930Sstevel@tonic-gate 	if (strncmp(linebuf, temp, sizeof (temp)) == 0)
13940Sstevel@tonic-gate 		return;
13950Sstevel@tonic-gate 	strcLIN(temp);
13960Sstevel@tonic-gate 	putmark(dot);
13970Sstevel@tonic-gate }
13980Sstevel@tonic-gate 
13990Sstevel@tonic-gate #undef	forbid
1400*802Scf46844 #define	forbid(a)	if (a) { (void) beep(); return; }
14010Sstevel@tonic-gate 
14020Sstevel@tonic-gate /*
14030Sstevel@tonic-gate  * Do a z operation.
14040Sstevel@tonic-gate  * Code here is rather long, and very uninteresting.
14050Sstevel@tonic-gate  */
1406*802Scf46844 void
1407*802Scf46844 vzop(bool hadcnt, int cnt, int c)
14080Sstevel@tonic-gate {
1409*802Scf46844 	line *addr;
14100Sstevel@tonic-gate 
14110Sstevel@tonic-gate 	if (state != VISUAL) {
14120Sstevel@tonic-gate 		/*
14130Sstevel@tonic-gate 		 * Z from open; always like a z=.
14140Sstevel@tonic-gate 		 * This code is a mess and should be cleaned up.
14150Sstevel@tonic-gate 		 */
14160Sstevel@tonic-gate 		vmoveitup(1, 1);
14170Sstevel@tonic-gate 		vgoto(outline, 0);
14180Sstevel@tonic-gate 		ostop(normf);
14190Sstevel@tonic-gate 		setoutt();
14200Sstevel@tonic-gate 		addr2 = dot;
14210Sstevel@tonic-gate 		vclear();
14220Sstevel@tonic-gate 		destline = WECHO;
14230Sstevel@tonic-gate 		zop2(Xhadcnt ? Xcnt : value(vi_WINDOW) - 1, '=');
14240Sstevel@tonic-gate 		if (state == CRTOPEN)
14250Sstevel@tonic-gate 			putnl();
14260Sstevel@tonic-gate 		putNFL();
14270Sstevel@tonic-gate 		termreset();
14280Sstevel@tonic-gate 		Outchar = vputchar;
14290Sstevel@tonic-gate 		(void)ostart();
14300Sstevel@tonic-gate 		vcnt = 0;
14310Sstevel@tonic-gate 		outline = destline = 0;
14320Sstevel@tonic-gate 		vjumpto(dot, cursor, 0);
14330Sstevel@tonic-gate 		return;
14340Sstevel@tonic-gate 	}
14350Sstevel@tonic-gate 	if (hadcnt) {
14360Sstevel@tonic-gate 		addr = zero + cnt;
14370Sstevel@tonic-gate 		if (addr < one)
14380Sstevel@tonic-gate 			addr = one;
14390Sstevel@tonic-gate 		if (addr > dol)
14400Sstevel@tonic-gate 			addr = dol;
14410Sstevel@tonic-gate 		markit(addr);
14420Sstevel@tonic-gate 	} else
14430Sstevel@tonic-gate 		switch (c) {
14440Sstevel@tonic-gate 
14450Sstevel@tonic-gate 		case '+':
14460Sstevel@tonic-gate 			addr = dot + vcnt - vcline;
14470Sstevel@tonic-gate 			break;
14480Sstevel@tonic-gate 
14490Sstevel@tonic-gate 		case '^':
14500Sstevel@tonic-gate 			addr = dot - vcline - 1;
14510Sstevel@tonic-gate 			forbid (addr < one);
14520Sstevel@tonic-gate 			c = '-';
14530Sstevel@tonic-gate 			break;
14540Sstevel@tonic-gate 
14550Sstevel@tonic-gate 		default:
14560Sstevel@tonic-gate 			addr = dot;
14570Sstevel@tonic-gate 			break;
14580Sstevel@tonic-gate 		}
14590Sstevel@tonic-gate 	switch (c) {
14600Sstevel@tonic-gate 
14610Sstevel@tonic-gate 	case '.':
14620Sstevel@tonic-gate 	case '-':
14630Sstevel@tonic-gate 		break;
14640Sstevel@tonic-gate 
14650Sstevel@tonic-gate 	case '^':
14660Sstevel@tonic-gate 		forbid (addr <= one);
14670Sstevel@tonic-gate 		break;
14680Sstevel@tonic-gate 
14690Sstevel@tonic-gate 	case '+':
14700Sstevel@tonic-gate 		forbid (addr >= dol);
14710Sstevel@tonic-gate 		/* fall into ... */
14720Sstevel@tonic-gate 
14730Sstevel@tonic-gate 	case CR:
14740Sstevel@tonic-gate 	case NL:
14750Sstevel@tonic-gate 		c = CR;
14760Sstevel@tonic-gate 		break;
14770Sstevel@tonic-gate 
14780Sstevel@tonic-gate 	default:
1479*802Scf46844 		(void) beep();
14800Sstevel@tonic-gate 		return;
14810Sstevel@tonic-gate 	}
14820Sstevel@tonic-gate 	vmoving = 0;
1483*802Scf46844 	vjumpto(addr, (unsigned char *)NOSTR, c);
14840Sstevel@tonic-gate }
1485