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