xref: /onnv-gate/usr/src/cmd/vi/port/ex_vmain.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23*0Sstevel@tonic-gate /*	  All Rights Reserved  	*/
24*0Sstevel@tonic-gate 
25*0Sstevel@tonic-gate 
26*0Sstevel@tonic-gate /* Copyright (c) 1981 Regents of the University of California */
27*0Sstevel@tonic-gate /*
28*0Sstevel@tonic-gate  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
29*0Sstevel@tonic-gate  * Use is subject to license terms.
30*0Sstevel@tonic-gate  */
31*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4.0 1.22	*/
32*0Sstevel@tonic-gate 
33*0Sstevel@tonic-gate #include "ex.h"
34*0Sstevel@tonic-gate #include "ex_tty.h"
35*0Sstevel@tonic-gate #include "ex_vis.h"
36*0Sstevel@tonic-gate #ifndef PRESUNEUC
37*0Sstevel@tonic-gate #include <wctype.h>
38*0Sstevel@tonic-gate /* Undef putchar/getchar if they're defined. */
39*0Sstevel@tonic-gate #ifdef putchar
40*0Sstevel@tonic-gate #	undef putchar
41*0Sstevel@tonic-gate #endif
42*0Sstevel@tonic-gate #ifdef getchar
43*0Sstevel@tonic-gate #	undef getchar
44*0Sstevel@tonic-gate #endif
45*0Sstevel@tonic-gate #endif /* PRESUNEUC */
46*0Sstevel@tonic-gate 
47*0Sstevel@tonic-gate /*
48*0Sstevel@tonic-gate  * This is the main routine for visual.
49*0Sstevel@tonic-gate  * We here decode the count and possible named buffer specification
50*0Sstevel@tonic-gate  * preceding a command and interpret a few of the commands.
51*0Sstevel@tonic-gate  * Commands which involve a target (i.e. an operator) are decoded
52*0Sstevel@tonic-gate  * in the routine operate in ex_voperate.c.
53*0Sstevel@tonic-gate  */
54*0Sstevel@tonic-gate 
55*0Sstevel@tonic-gate #define	forbid(a)	{ if (a) goto fonfon; }
56*0Sstevel@tonic-gate 
57*0Sstevel@tonic-gate extern int windowchg;
58*0Sstevel@tonic-gate extern int sigok;
59*0Sstevel@tonic-gate void redraw(), windowinit();
60*0Sstevel@tonic-gate 
61*0Sstevel@tonic-gate #ifdef XPG4
62*0Sstevel@tonic-gate extern int P_cursor_offset;
63*0Sstevel@tonic-gate #endif
64*0Sstevel@tonic-gate 
65*0Sstevel@tonic-gate vmain()
66*0Sstevel@tonic-gate {
67*0Sstevel@tonic-gate 	register int c, cnt, i;
68*0Sstevel@tonic-gate 	wchar_t esave[TUBECOLS];
69*0Sstevel@tonic-gate 	extern wchar_t atube[];
70*0Sstevel@tonic-gate 	unsigned char *oglobp;
71*0Sstevel@tonic-gate 	short d;
72*0Sstevel@tonic-gate 	line *addr;
73*0Sstevel@tonic-gate 	int ind, nlput;
74*0Sstevel@tonic-gate 	int shouldpo = 0;
75*0Sstevel@tonic-gate 	int tag_reset_wrap = 0;
76*0Sstevel@tonic-gate 	int onumber, olist, (*OPline)(), (*OPutchar)();
77*0Sstevel@tonic-gate 
78*0Sstevel@tonic-gate 
79*0Sstevel@tonic-gate 	vch_mac = VC_NOTINMAC;
80*0Sstevel@tonic-gate 	ixlatctl(0);
81*0Sstevel@tonic-gate 
82*0Sstevel@tonic-gate 	/*
83*0Sstevel@tonic-gate 	 * If we started as a vi command (on the command line)
84*0Sstevel@tonic-gate 	 * then go process initial commands (recover, next or tag).
85*0Sstevel@tonic-gate 	 */
86*0Sstevel@tonic-gate 	if (initev) {
87*0Sstevel@tonic-gate 		oglobp = globp;
88*0Sstevel@tonic-gate 		globp = initev;
89*0Sstevel@tonic-gate 		hadcnt = cnt = 0;
90*0Sstevel@tonic-gate 		i = tchng;
91*0Sstevel@tonic-gate 		addr = dot;
92*0Sstevel@tonic-gate 		goto doinit;
93*0Sstevel@tonic-gate 	}
94*0Sstevel@tonic-gate 
95*0Sstevel@tonic-gate 	vshowmode("");		/* As a precaution */
96*0Sstevel@tonic-gate 	/*
97*0Sstevel@tonic-gate 	 * NB:
98*0Sstevel@tonic-gate 	 *
99*0Sstevel@tonic-gate 	 * The current line is always in the line buffer linebuf,
100*0Sstevel@tonic-gate 	 * and the cursor at the position cursor.  You should do
101*0Sstevel@tonic-gate 	 * a vsave() before moving off the line to make sure the disk
102*0Sstevel@tonic-gate 	 * copy is updated if it has changed, and a getDOT() to get
103*0Sstevel@tonic-gate 	 * the line back if you mung linebuf.  The motion
104*0Sstevel@tonic-gate 	 * routines in ex_vwind.c handle most of this.
105*0Sstevel@tonic-gate 	 */
106*0Sstevel@tonic-gate 	for (;;) {
107*0Sstevel@tonic-gate 		/*
108*0Sstevel@tonic-gate 		 * Decode a visual command.
109*0Sstevel@tonic-gate 		 * First sync the temp file if there has been a reasonable
110*0Sstevel@tonic-gate 		 * amount of change.  Clear state for decoding of next
111*0Sstevel@tonic-gate 		 * command.
112*0Sstevel@tonic-gate 		 */
113*0Sstevel@tonic-gate 		TSYNC();
114*0Sstevel@tonic-gate 		vglobp = 0;
115*0Sstevel@tonic-gate 		vreg = 0;
116*0Sstevel@tonic-gate 		hold = 0;
117*0Sstevel@tonic-gate 		seenprompt = 1;
118*0Sstevel@tonic-gate 		wcursor = 0;
119*0Sstevel@tonic-gate 		Xhadcnt = hadcnt = 0;
120*0Sstevel@tonic-gate 		Xcnt = cnt = 1;
121*0Sstevel@tonic-gate 		splitw = 0;
122*0Sstevel@tonic-gate 		if (i = holdupd && !windowchg) {
123*0Sstevel@tonic-gate 			if (state == VISUAL) {
124*0Sstevel@tonic-gate 				sigok = 1;
125*0Sstevel@tonic-gate 				(void)peekkey();
126*0Sstevel@tonic-gate 				sigok = 0;
127*0Sstevel@tonic-gate 			}
128*0Sstevel@tonic-gate 
129*0Sstevel@tonic-gate 			holdupd = 0;
130*0Sstevel@tonic-gate /*
131*0Sstevel@tonic-gate 			if (LINE(0) < ZERO) {
132*0Sstevel@tonic-gate 				vclear();
133*0Sstevel@tonic-gate 				vcnt = 0;
134*0Sstevel@tonic-gate 				i = 3;
135*0Sstevel@tonic-gate 			}
136*0Sstevel@tonic-gate */
137*0Sstevel@tonic-gate 			if (state != VISUAL) {
138*0Sstevel@tonic-gate 				vcnt = 0;
139*0Sstevel@tonic-gate 				vsave();
140*0Sstevel@tonic-gate 				vrepaint(cursor);
141*0Sstevel@tonic-gate 			} else if (i == 3)
142*0Sstevel@tonic-gate 				vredraw(WTOP);
143*0Sstevel@tonic-gate 			else
144*0Sstevel@tonic-gate 				vsync(WTOP);
145*0Sstevel@tonic-gate 			vfixcurs();
146*0Sstevel@tonic-gate 		} else if(windowchg)
147*0Sstevel@tonic-gate 			redraw();
148*0Sstevel@tonic-gate 
149*0Sstevel@tonic-gate 		/*
150*0Sstevel@tonic-gate 		 * Gobble up counts and named buffer specifications.
151*0Sstevel@tonic-gate 		 */
152*0Sstevel@tonic-gate 		for (;;) {
153*0Sstevel@tonic-gate looptop:
154*0Sstevel@tonic-gate #ifdef MDEBUG
155*0Sstevel@tonic-gate 			if (trace)
156*0Sstevel@tonic-gate 				fprintf(trace, "pc=%c",peekkey());
157*0Sstevel@tonic-gate #endif
158*0Sstevel@tonic-gate 			sigok = 1;
159*0Sstevel@tonic-gate 			c = peekkey();
160*0Sstevel@tonic-gate 			sigok = 0;
161*0Sstevel@tonic-gate 			if (isdigit(peekkey()) && peekkey() != '0') {
162*0Sstevel@tonic-gate 				hadcnt = 1;
163*0Sstevel@tonic-gate 				cnt = vgetcnt();
164*0Sstevel@tonic-gate 				forbid (cnt <= 0);
165*0Sstevel@tonic-gate 			}
166*0Sstevel@tonic-gate 			if (peekkey() != '"')
167*0Sstevel@tonic-gate 				break;
168*0Sstevel@tonic-gate 			(void)getkey(), c = getkey();
169*0Sstevel@tonic-gate 			/*
170*0Sstevel@tonic-gate 			 * Buffer names be letters or digits.
171*0Sstevel@tonic-gate 			 * But not '0' as that is the source of
172*0Sstevel@tonic-gate 			 * an 'empty' named buffer spec in the routine
173*0Sstevel@tonic-gate 			 * kshift (see ex_temp.c).
174*0Sstevel@tonic-gate 			 */
175*0Sstevel@tonic-gate 			if(!isascii(c) && MB_CUR_MAX > 1) {
176*0Sstevel@tonic-gate 				/* get rest of character */
177*0Sstevel@tonic-gate 				wchar_t wchar;
178*0Sstevel@tonic-gate 				char multic[MULTI_BYTE_MAX];
179*0Sstevel@tonic-gate 				ungetkey(c);
180*0Sstevel@tonic-gate 				(void)_mbftowc(multic, &wchar, getkey, &Peekkey);
181*0Sstevel@tonic-gate 			}
182*0Sstevel@tonic-gate 			forbid (c == '0' || !isalpha(c) && !isascii(c) && !isdigit(c));
183*0Sstevel@tonic-gate 			vreg = c;
184*0Sstevel@tonic-gate 		}
185*0Sstevel@tonic-gate reread:
186*0Sstevel@tonic-gate 		/*
187*0Sstevel@tonic-gate 		 * Come to reread from below after some macro expansions.
188*0Sstevel@tonic-gate 		 * The call to map allows use of function key pads
189*0Sstevel@tonic-gate 		 * by performing a terminal dependent mapping of inputs.
190*0Sstevel@tonic-gate 		 */
191*0Sstevel@tonic-gate #ifdef MDEBUG
192*0Sstevel@tonic-gate 		if (trace)
193*0Sstevel@tonic-gate 			fprintf(trace,"pcb=%c,",peekkey());
194*0Sstevel@tonic-gate #endif
195*0Sstevel@tonic-gate 		op = getkey();
196*0Sstevel@tonic-gate 		maphopcnt = 0;
197*0Sstevel@tonic-gate 		do {
198*0Sstevel@tonic-gate 			/*
199*0Sstevel@tonic-gate 			 * Keep mapping the char as long as it changes.
200*0Sstevel@tonic-gate 			 * This allows for double mappings, e.g., q to #,
201*0Sstevel@tonic-gate 			 * #1 to something else.
202*0Sstevel@tonic-gate 			 */
203*0Sstevel@tonic-gate 			c = op;
204*0Sstevel@tonic-gate 			op = map(c,arrows,0);
205*0Sstevel@tonic-gate #ifdef MDEBUG
206*0Sstevel@tonic-gate 			if (trace)
207*0Sstevel@tonic-gate 				fprintf(trace,"pca=%c,",c);
208*0Sstevel@tonic-gate #endif
209*0Sstevel@tonic-gate 			/*
210*0Sstevel@tonic-gate 			 * Maybe the mapped to char is a count. If so, we have
211*0Sstevel@tonic-gate 			 * to go back to the "for" to interpret it. Likewise
212*0Sstevel@tonic-gate 			 * for a buffer name.
213*0Sstevel@tonic-gate 			 */
214*0Sstevel@tonic-gate 			if ((isdigit(c) && c!='0') || c == '"') {
215*0Sstevel@tonic-gate 				ungetkey(c);
216*0Sstevel@tonic-gate 				goto looptop;
217*0Sstevel@tonic-gate 			}
218*0Sstevel@tonic-gate 			if (!value(vi_REMAP)) {
219*0Sstevel@tonic-gate 				c = op;
220*0Sstevel@tonic-gate 				break;
221*0Sstevel@tonic-gate 			}
222*0Sstevel@tonic-gate 			if (++maphopcnt > 256)
223*0Sstevel@tonic-gate 				error(gettext("Infinite macro loop"));
224*0Sstevel@tonic-gate 		} while (c != op);
225*0Sstevel@tonic-gate 
226*0Sstevel@tonic-gate 		/*
227*0Sstevel@tonic-gate 		 * Begin to build an image of this command for possible
228*0Sstevel@tonic-gate 		 * later repeat in the buffer workcmd.  It will be copied
229*0Sstevel@tonic-gate 		 * to lastcmd by the routine setLAST
230*0Sstevel@tonic-gate 		 * if/when completely specified.
231*0Sstevel@tonic-gate 		 */
232*0Sstevel@tonic-gate 		lastcp = workcmd;
233*0Sstevel@tonic-gate 		if (!vglobp)
234*0Sstevel@tonic-gate 			*lastcp++ = c;
235*0Sstevel@tonic-gate 
236*0Sstevel@tonic-gate 		/*
237*0Sstevel@tonic-gate 		 * First level command decode.
238*0Sstevel@tonic-gate 		 */
239*0Sstevel@tonic-gate 		switch (c) {
240*0Sstevel@tonic-gate 
241*0Sstevel@tonic-gate 		/*
242*0Sstevel@tonic-gate 		 * ^L		Clear screen e.g. after transmission error.
243*0Sstevel@tonic-gate 		 */
244*0Sstevel@tonic-gate 
245*0Sstevel@tonic-gate 		/*
246*0Sstevel@tonic-gate 		 * ^R		Retype screen, getting rid of @ lines.
247*0Sstevel@tonic-gate 		 *		If in open, equivalent to ^L.
248*0Sstevel@tonic-gate 		 *		On terminals where the right arrow key sends
249*0Sstevel@tonic-gate 		 *		^L we make ^R act like ^L, since there is no
250*0Sstevel@tonic-gate 		 *		way to get ^L.  These terminals (adm31, tvi)
251*0Sstevel@tonic-gate 		 *		are intelligent so ^R is useless.  Soroc
252*0Sstevel@tonic-gate 		 *		will probably foul this up, but nobody has
253*0Sstevel@tonic-gate 		 *		one of them.
254*0Sstevel@tonic-gate 		 */
255*0Sstevel@tonic-gate 		case CTRL('l'):
256*0Sstevel@tonic-gate 		case CTRL('r'):
257*0Sstevel@tonic-gate 			if (c == CTRL('l') || (key_right && *key_right==CTRL('l'))) {
258*0Sstevel@tonic-gate 				vclear();
259*0Sstevel@tonic-gate 				vdirty(0, vcnt);
260*0Sstevel@tonic-gate 			}
261*0Sstevel@tonic-gate 			if (state != VISUAL) {
262*0Sstevel@tonic-gate 				/*
263*0Sstevel@tonic-gate 				 * Get a clean line, throw away the
264*0Sstevel@tonic-gate 				 * memory of what is displayed now,
265*0Sstevel@tonic-gate 				 * and move back onto the current line.
266*0Sstevel@tonic-gate 				 */
267*0Sstevel@tonic-gate 				vclean();
268*0Sstevel@tonic-gate 				vcnt = 0;
269*0Sstevel@tonic-gate 				vmoveto(dot, cursor, 0);
270*0Sstevel@tonic-gate 				continue;
271*0Sstevel@tonic-gate 			}
272*0Sstevel@tonic-gate 			vredraw(WTOP);
273*0Sstevel@tonic-gate 			/*
274*0Sstevel@tonic-gate 			 * Weird glitch -- when we enter visual
275*0Sstevel@tonic-gate 			 * in a very small window we may end up with
276*0Sstevel@tonic-gate 			 * no lines on the screen because the line
277*0Sstevel@tonic-gate 			 * at the top is too long.  This forces the screen
278*0Sstevel@tonic-gate 			 * to be expanded to make room for it (after
279*0Sstevel@tonic-gate 			 * we have printed @'s ick showing we goofed).
280*0Sstevel@tonic-gate 			 */
281*0Sstevel@tonic-gate 			if (vcnt == 0)
282*0Sstevel@tonic-gate 				vrepaint(cursor);
283*0Sstevel@tonic-gate 			vfixcurs();
284*0Sstevel@tonic-gate 			continue;
285*0Sstevel@tonic-gate 
286*0Sstevel@tonic-gate 		/*
287*0Sstevel@tonic-gate 		 * $		Escape just cancels the current command
288*0Sstevel@tonic-gate 		 *		with a little feedback.
289*0Sstevel@tonic-gate 		 */
290*0Sstevel@tonic-gate 		case ESCAPE:
291*0Sstevel@tonic-gate 			beep();
292*0Sstevel@tonic-gate 			continue;
293*0Sstevel@tonic-gate 
294*0Sstevel@tonic-gate 		/*
295*0Sstevel@tonic-gate 		 * @   		Macros. Bring in the macro and put it
296*0Sstevel@tonic-gate 		 *		in vmacbuf, point vglobp there and punt.
297*0Sstevel@tonic-gate 		 */
298*0Sstevel@tonic-gate 		 case '@':
299*0Sstevel@tonic-gate 			c = getesc();
300*0Sstevel@tonic-gate 			if (c == 0)
301*0Sstevel@tonic-gate 				continue;
302*0Sstevel@tonic-gate 			if (c == '@')
303*0Sstevel@tonic-gate 				c = lastmac;
304*0Sstevel@tonic-gate 			if (isupper(c))
305*0Sstevel@tonic-gate 				c = tolower(c);
306*0Sstevel@tonic-gate 			forbid(!islower(c));
307*0Sstevel@tonic-gate 			lastmac = c;
308*0Sstevel@tonic-gate 			vsave();
309*0Sstevel@tonic-gate 			CATCH
310*0Sstevel@tonic-gate 				unsigned char tmpbuf[BUFSIZE];
311*0Sstevel@tonic-gate 
312*0Sstevel@tonic-gate 				regbuf(c,tmpbuf,sizeof(vmacbuf));
313*0Sstevel@tonic-gate 				macpush(tmpbuf, 1);
314*0Sstevel@tonic-gate 			ONERR
315*0Sstevel@tonic-gate 				lastmac = 0;
316*0Sstevel@tonic-gate 				splitw = 0;
317*0Sstevel@tonic-gate 				getDOT();
318*0Sstevel@tonic-gate 				vrepaint(cursor);
319*0Sstevel@tonic-gate 				continue;
320*0Sstevel@tonic-gate 			ENDCATCH
321*0Sstevel@tonic-gate 			vmacp = vmacbuf;
322*0Sstevel@tonic-gate 			goto reread;
323*0Sstevel@tonic-gate 
324*0Sstevel@tonic-gate 		/*
325*0Sstevel@tonic-gate 		 * .		Repeat the last (modifying) open/visual command.
326*0Sstevel@tonic-gate 		 */
327*0Sstevel@tonic-gate 		case '.':
328*0Sstevel@tonic-gate 			/*
329*0Sstevel@tonic-gate 			 * Check that there was a last command, and
330*0Sstevel@tonic-gate 			 * take its count and named buffer unless they
331*0Sstevel@tonic-gate 			 * were given anew.  Special case if last command
332*0Sstevel@tonic-gate 			 * referenced a numeric named buffer -- increment
333*0Sstevel@tonic-gate 			 * the number and go to a named buffer again.
334*0Sstevel@tonic-gate 			 * This allows a sequence like "1pu.u.u...
335*0Sstevel@tonic-gate 			 * to successively look for stuff in the kill chain
336*0Sstevel@tonic-gate 			 * much as one does in EMACS with C-Y and M-Y.
337*0Sstevel@tonic-gate 			 */
338*0Sstevel@tonic-gate 			forbid (lastcmd[0] == 0);
339*0Sstevel@tonic-gate 			if (hadcnt)
340*0Sstevel@tonic-gate 				lastcnt = cnt;
341*0Sstevel@tonic-gate 			if (vreg)
342*0Sstevel@tonic-gate 				lastreg = vreg;
343*0Sstevel@tonic-gate 			else if (isdigit(lastreg) && lastreg < '9')
344*0Sstevel@tonic-gate 				lastreg++;
345*0Sstevel@tonic-gate 			vreg = lastreg;
346*0Sstevel@tonic-gate 			cnt = lastcnt;
347*0Sstevel@tonic-gate 			hadcnt = lasthad;
348*0Sstevel@tonic-gate 			vglobp = lastcmd;
349*0Sstevel@tonic-gate 			goto reread;
350*0Sstevel@tonic-gate 
351*0Sstevel@tonic-gate 		/*
352*0Sstevel@tonic-gate 		 * ^U		Scroll up.  A count sticks around for
353*0Sstevel@tonic-gate 		 *		future scrolls as the scroll amount.
354*0Sstevel@tonic-gate 		 *		Attempt to hold the indentation from the
355*0Sstevel@tonic-gate 		 *		top of the screen (in logical lines).
356*0Sstevel@tonic-gate 		 *
357*0Sstevel@tonic-gate 		 * BUG:		A ^U near the bottom of the screen
358*0Sstevel@tonic-gate 		 *		on a dumb terminal (which can't roll back)
359*0Sstevel@tonic-gate 		 *		causes the screen to be cleared and then
360*0Sstevel@tonic-gate 		 *		redrawn almost as it was.  In this case
361*0Sstevel@tonic-gate 		 *		one should simply move the cursor.
362*0Sstevel@tonic-gate 		 */
363*0Sstevel@tonic-gate 		case CTRL('u'):
364*0Sstevel@tonic-gate 			if (hadcnt)
365*0Sstevel@tonic-gate 				vSCROLL = cnt;
366*0Sstevel@tonic-gate 			cnt = vSCROLL;
367*0Sstevel@tonic-gate 			if (state == VISUAL)
368*0Sstevel@tonic-gate 				ind = vcline, cnt += ind;
369*0Sstevel@tonic-gate 			else
370*0Sstevel@tonic-gate 				ind = 0;
371*0Sstevel@tonic-gate 			vmoving = 0;
372*0Sstevel@tonic-gate 			vup(cnt, ind, 1);
373*0Sstevel@tonic-gate 			vnline(NOSTR);
374*0Sstevel@tonic-gate 			continue;
375*0Sstevel@tonic-gate 
376*0Sstevel@tonic-gate 		/*
377*0Sstevel@tonic-gate 		 * ^D		Scroll down.  Like scroll up.
378*0Sstevel@tonic-gate 		 */
379*0Sstevel@tonic-gate 		case CTRL('d'):
380*0Sstevel@tonic-gate #ifdef TRACE
381*0Sstevel@tonic-gate 		if (trace)
382*0Sstevel@tonic-gate 			fprintf(trace, "before vdown in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
383*0Sstevel@tonic-gate #endif
384*0Sstevel@tonic-gate 			if (hadcnt)
385*0Sstevel@tonic-gate 				vSCROLL = cnt;
386*0Sstevel@tonic-gate 			cnt = vSCROLL;
387*0Sstevel@tonic-gate 			if (state == VISUAL)
388*0Sstevel@tonic-gate 				ind = vcnt - vcline - 1, cnt += ind;
389*0Sstevel@tonic-gate 			else
390*0Sstevel@tonic-gate 				ind = 0;
391*0Sstevel@tonic-gate 			vmoving = 0;
392*0Sstevel@tonic-gate 			vdown(cnt, ind, 1);
393*0Sstevel@tonic-gate #ifdef TRACE
394*0Sstevel@tonic-gate 		if (trace)
395*0Sstevel@tonic-gate 			fprintf(trace, "before vnline in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
396*0Sstevel@tonic-gate #endif
397*0Sstevel@tonic-gate 			vnline(NOSTR);
398*0Sstevel@tonic-gate #ifdef TRACE
399*0Sstevel@tonic-gate 		if (trace)
400*0Sstevel@tonic-gate 			fprintf(trace, "after vnline in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
401*0Sstevel@tonic-gate #endif
402*0Sstevel@tonic-gate 			continue;
403*0Sstevel@tonic-gate 
404*0Sstevel@tonic-gate 		/*
405*0Sstevel@tonic-gate 		 * ^E		Glitch the screen down (one) line.
406*0Sstevel@tonic-gate 		 *		Cursor left on same line in file.
407*0Sstevel@tonic-gate 		 */
408*0Sstevel@tonic-gate 		case CTRL('e'):
409*0Sstevel@tonic-gate 			if (state != VISUAL)
410*0Sstevel@tonic-gate 				continue;
411*0Sstevel@tonic-gate 			if (!hadcnt)
412*0Sstevel@tonic-gate 				cnt = 1;
413*0Sstevel@tonic-gate 			/* Bottom line of file already on screen */
414*0Sstevel@tonic-gate 			forbid(lineDOL()-lineDOT() <= vcnt-1-vcline);
415*0Sstevel@tonic-gate 			ind = vcnt - vcline - 1 + cnt;
416*0Sstevel@tonic-gate 			vdown(ind, ind, 1);
417*0Sstevel@tonic-gate 			vnline(cursor);
418*0Sstevel@tonic-gate 			continue;
419*0Sstevel@tonic-gate 
420*0Sstevel@tonic-gate 		/*
421*0Sstevel@tonic-gate 		 * ^Y		Like ^E but up
422*0Sstevel@tonic-gate 		 */
423*0Sstevel@tonic-gate 		case CTRL('y'):
424*0Sstevel@tonic-gate 			if (state != VISUAL)
425*0Sstevel@tonic-gate 				continue;
426*0Sstevel@tonic-gate 			if (!hadcnt)
427*0Sstevel@tonic-gate 				cnt = 1;
428*0Sstevel@tonic-gate 			forbid(lineDOT()-1<=vcline); /* line 1 already there */
429*0Sstevel@tonic-gate 			ind = vcline + cnt;
430*0Sstevel@tonic-gate 			vup(ind, ind, 1);
431*0Sstevel@tonic-gate 			vnline(cursor);
432*0Sstevel@tonic-gate 			continue;
433*0Sstevel@tonic-gate 
434*0Sstevel@tonic-gate 
435*0Sstevel@tonic-gate 		/*
436*0Sstevel@tonic-gate 		 * m		Mark position in mark register given
437*0Sstevel@tonic-gate 		 *		by following letter.  Return is
438*0Sstevel@tonic-gate 		 *		accomplished via ' or `; former
439*0Sstevel@tonic-gate 		 *		to beginning of line where mark
440*0Sstevel@tonic-gate 		 *		was set, latter to column where marked.
441*0Sstevel@tonic-gate 		 */
442*0Sstevel@tonic-gate 		case 'm':
443*0Sstevel@tonic-gate 			/*
444*0Sstevel@tonic-gate 			 * Getesc is generally used when a character
445*0Sstevel@tonic-gate 			 * is read as a latter part of a command
446*0Sstevel@tonic-gate 			 * to allow one to hit rubout/escape to cancel
447*0Sstevel@tonic-gate 			 * what you have typed so far.  These characters
448*0Sstevel@tonic-gate 			 * are mapped to 0 by the subroutine.
449*0Sstevel@tonic-gate 			 */
450*0Sstevel@tonic-gate 			c = getesc();
451*0Sstevel@tonic-gate 			if (c == 0)
452*0Sstevel@tonic-gate 				continue;
453*0Sstevel@tonic-gate 
454*0Sstevel@tonic-gate 			/*
455*0Sstevel@tonic-gate 			 * Markreg checks that argument is a letter
456*0Sstevel@tonic-gate 			 * and also maps ' and ` to the end of the range
457*0Sstevel@tonic-gate 			 * to allow '' or `` to reference the previous
458*0Sstevel@tonic-gate 			 * context mark.
459*0Sstevel@tonic-gate 			 */
460*0Sstevel@tonic-gate 			c = markreg(c);
461*0Sstevel@tonic-gate 			forbid (c == 0);
462*0Sstevel@tonic-gate 			vsave();
463*0Sstevel@tonic-gate 			names[c - 'a'] = (*dot &~ 01);
464*0Sstevel@tonic-gate 			ncols[c - 'a'] = cursor;
465*0Sstevel@tonic-gate 			anymarks = 1;
466*0Sstevel@tonic-gate 			continue;
467*0Sstevel@tonic-gate 
468*0Sstevel@tonic-gate 		/*
469*0Sstevel@tonic-gate 		 * ^F		Window forwards, with 2 lines of continuity.
470*0Sstevel@tonic-gate 		 *		Count repeats.
471*0Sstevel@tonic-gate 		 */
472*0Sstevel@tonic-gate 		case CTRL('f'):
473*0Sstevel@tonic-gate 			vsave();
474*0Sstevel@tonic-gate 			if (vcnt > 2) {
475*0Sstevel@tonic-gate 				addr = dot + (vcnt - vcline) - 2 + (cnt-1)*basWLINES;
476*0Sstevel@tonic-gate 				forbid(addr > dol);
477*0Sstevel@tonic-gate 				dot = addr;
478*0Sstevel@tonic-gate 				vcnt = vcline = 0;
479*0Sstevel@tonic-gate 			}
480*0Sstevel@tonic-gate 			vzop(0, 0, '+');
481*0Sstevel@tonic-gate 			continue;
482*0Sstevel@tonic-gate 
483*0Sstevel@tonic-gate 		/*
484*0Sstevel@tonic-gate 		 * ^B		Window backwards, with 2 lines of continuity.
485*0Sstevel@tonic-gate 		 *		Inverse of ^F.
486*0Sstevel@tonic-gate 		 */
487*0Sstevel@tonic-gate 		case CTRL('b'):
488*0Sstevel@tonic-gate 			vsave();
489*0Sstevel@tonic-gate 			if (one + vcline != dot && vcnt > 2) {
490*0Sstevel@tonic-gate 				addr = dot - vcline + 2 - (cnt-1)*basWLINES;
491*0Sstevel@tonic-gate 				forbid (addr <= zero);
492*0Sstevel@tonic-gate 				dot = addr;
493*0Sstevel@tonic-gate 				vcnt = vcline = 0;
494*0Sstevel@tonic-gate 			}
495*0Sstevel@tonic-gate 			vzop(0, 0, '^');
496*0Sstevel@tonic-gate 			continue;
497*0Sstevel@tonic-gate 
498*0Sstevel@tonic-gate 		/*
499*0Sstevel@tonic-gate 		 * z		Screen adjustment, taking a following character:
500*0Sstevel@tonic-gate 		 *			zcarriage_return		current line to top
501*0Sstevel@tonic-gate 		 *			z<NL>		like zcarriage_return
502*0Sstevel@tonic-gate 		 *			z-		current line to bottom
503*0Sstevel@tonic-gate 		 *		also z+, z^ like ^F and ^B.
504*0Sstevel@tonic-gate 		 *		A preceding count is line to use rather
505*0Sstevel@tonic-gate 		 *		than current line.  A count between z and
506*0Sstevel@tonic-gate 		 *		specifier character changes the screen size
507*0Sstevel@tonic-gate 		 *		for the redraw.
508*0Sstevel@tonic-gate 		 *
509*0Sstevel@tonic-gate 		 */
510*0Sstevel@tonic-gate 		case 'z':
511*0Sstevel@tonic-gate 			if (state == VISUAL) {
512*0Sstevel@tonic-gate 				i = vgetcnt();
513*0Sstevel@tonic-gate 				if (i > 0)
514*0Sstevel@tonic-gate 					vsetsiz(i);
515*0Sstevel@tonic-gate 				c = getesc();
516*0Sstevel@tonic-gate 				if (c == 0)
517*0Sstevel@tonic-gate 					continue;
518*0Sstevel@tonic-gate 			}
519*0Sstevel@tonic-gate 			vsave();
520*0Sstevel@tonic-gate 			vzop(hadcnt, cnt, c);
521*0Sstevel@tonic-gate 			continue;
522*0Sstevel@tonic-gate 
523*0Sstevel@tonic-gate 		/*
524*0Sstevel@tonic-gate 		 * Y		Yank lines, abbreviation for y_ or yy.
525*0Sstevel@tonic-gate 		 *		Yanked lines can be put later if no
526*0Sstevel@tonic-gate 		 *		changes intervene, or can be put in named
527*0Sstevel@tonic-gate 		 *		buffers and put anytime in this session.
528*0Sstevel@tonic-gate 		 */
529*0Sstevel@tonic-gate 		case 'Y':
530*0Sstevel@tonic-gate 			ungetkey('_');
531*0Sstevel@tonic-gate 			c = 'y';
532*0Sstevel@tonic-gate 			break;
533*0Sstevel@tonic-gate 
534*0Sstevel@tonic-gate 		/*
535*0Sstevel@tonic-gate 		 * J		Join lines, 2 by default.  Count is number
536*0Sstevel@tonic-gate 		 *		of lines to join (no join operator sorry.)
537*0Sstevel@tonic-gate 		 */
538*0Sstevel@tonic-gate 		case 'J':
539*0Sstevel@tonic-gate 			forbid (dot == dol);
540*0Sstevel@tonic-gate 			if (cnt == 1)
541*0Sstevel@tonic-gate 				cnt = 2;
542*0Sstevel@tonic-gate 			if (cnt > (i = dol - dot + 1))
543*0Sstevel@tonic-gate 				cnt = i;
544*0Sstevel@tonic-gate 			vsave();
545*0Sstevel@tonic-gate 			vmacchng(1);
546*0Sstevel@tonic-gate 			setLAST();
547*0Sstevel@tonic-gate 			cursor = strend(linebuf);
548*0Sstevel@tonic-gate 			vremote(cnt, join, 0);
549*0Sstevel@tonic-gate 			notenam = (unsigned char *)"join";
550*0Sstevel@tonic-gate 			vmoving = 0;
551*0Sstevel@tonic-gate 			killU();
552*0Sstevel@tonic-gate 			vreplace(vcline, cnt, 1);
553*0Sstevel@tonic-gate 			if (!*cursor && cursor > linebuf)
554*0Sstevel@tonic-gate 				cursor--;
555*0Sstevel@tonic-gate 			if (notecnt == 2)
556*0Sstevel@tonic-gate 				notecnt = 0;
557*0Sstevel@tonic-gate 			vrepaint(cursor);
558*0Sstevel@tonic-gate 			continue;
559*0Sstevel@tonic-gate 
560*0Sstevel@tonic-gate 		/*
561*0Sstevel@tonic-gate 		 * S		Substitute text for whole lines, abbrev for c_.
562*0Sstevel@tonic-gate 		 *		Count is number of lines to change.
563*0Sstevel@tonic-gate 		 */
564*0Sstevel@tonic-gate 		case 'S':
565*0Sstevel@tonic-gate 			ungetkey('_');
566*0Sstevel@tonic-gate 			c = 'c';
567*0Sstevel@tonic-gate 			break;
568*0Sstevel@tonic-gate 
569*0Sstevel@tonic-gate 		/*
570*0Sstevel@tonic-gate 		 * O		Create a new line above current and accept new
571*0Sstevel@tonic-gate 		 *		input text, to an escape, there.
572*0Sstevel@tonic-gate 		 *		A count specifies, for dumb terminals when
573*0Sstevel@tonic-gate 		 *		slowopen is not set, the number of physical
574*0Sstevel@tonic-gate 		 *		line space to open on the screen.
575*0Sstevel@tonic-gate 		 *
576*0Sstevel@tonic-gate 		 * o		Like O, but opens lines below.
577*0Sstevel@tonic-gate 		 */
578*0Sstevel@tonic-gate 		case 'O':
579*0Sstevel@tonic-gate 		case 'o':
580*0Sstevel@tonic-gate 			vmacchng(1);
581*0Sstevel@tonic-gate 			voOpen(c, cnt);
582*0Sstevel@tonic-gate 			continue;
583*0Sstevel@tonic-gate 
584*0Sstevel@tonic-gate 		/*
585*0Sstevel@tonic-gate 		 * C		Change text to end of line, short for c$.
586*0Sstevel@tonic-gate 		 */
587*0Sstevel@tonic-gate 		case 'C':
588*0Sstevel@tonic-gate 			if (*cursor) {
589*0Sstevel@tonic-gate 				ungetkey('$'), c = 'c';
590*0Sstevel@tonic-gate 				break;
591*0Sstevel@tonic-gate 			}
592*0Sstevel@tonic-gate 			goto appnd;
593*0Sstevel@tonic-gate 
594*0Sstevel@tonic-gate 		/*
595*0Sstevel@tonic-gate 		 * ~	Switch case of letter under cursor
596*0Sstevel@tonic-gate 		 */
597*0Sstevel@tonic-gate 		case '~':
598*0Sstevel@tonic-gate 			{
599*0Sstevel@tonic-gate 				unsigned char mbuf[2049];
600*0Sstevel@tonic-gate 				unsigned char *ccursor = cursor;
601*0Sstevel@tonic-gate #ifdef PRESUNEUC
602*0Sstevel@tonic-gate 				int tmp, length;
603*0Sstevel@tonic-gate 				wchar_t wchar;
604*0Sstevel@tonic-gate #else
605*0Sstevel@tonic-gate 				int tmp, len, n;
606*0Sstevel@tonic-gate 				wchar_t wc;
607*0Sstevel@tonic-gate #endif /* PRESUNEUC */
608*0Sstevel@tonic-gate 				unsigned char tmp1;
609*0Sstevel@tonic-gate 				setLAST();
610*0Sstevel@tonic-gate 				for (tmp = 0; tmp + 3 < 2048; ) {
611*0Sstevel@tonic-gate 				/*
612*0Sstevel@tonic-gate 				 * Use multiple 'r' commands to replace
613*0Sstevel@tonic-gate 				 * alpha with alternate case.
614*0Sstevel@tonic-gate 				 */
615*0Sstevel@tonic-gate 
616*0Sstevel@tonic-gate 					if(cnt-- <= 0)
617*0Sstevel@tonic-gate 						break;
618*0Sstevel@tonic-gate #ifdef PRESUNEUC
619*0Sstevel@tonic-gate 					length = mbtowc(&wchar, (char *)ccursor, MULTI_BYTE_MAX);
620*0Sstevel@tonic-gate #else
621*0Sstevel@tonic-gate 					len = mbtowc(&wc, (char *)ccursor, MULTI_BYTE_MAX);
622*0Sstevel@tonic-gate #endif /* PRESUNEUC */
623*0Sstevel@tonic-gate #ifdef PRESUNEUC
624*0Sstevel@tonic-gate 					if(length > 1) {
625*0Sstevel@tonic-gate #else
626*0Sstevel@tonic-gate 					n = iswalpha(wc);
627*0Sstevel@tonic-gate 					if(len > 1 && !iswalpha(wc)) {
628*0Sstevel@tonic-gate #endif /* PRESUNEUC */
629*0Sstevel@tonic-gate 						mbuf[tmp+0] = ' ';
630*0Sstevel@tonic-gate 						tmp++;
631*0Sstevel@tonic-gate #ifdef PRESUNEUC
632*0Sstevel@tonic-gate 						ccursor += length;
633*0Sstevel@tonic-gate #else
634*0Sstevel@tonic-gate 						ccursor += len;
635*0Sstevel@tonic-gate #endif /* PRESUNEUC */
636*0Sstevel@tonic-gate 						continue;
637*0Sstevel@tonic-gate 					}
638*0Sstevel@tonic-gate 					mbuf[tmp] = 'r';
639*0Sstevel@tonic-gate #ifdef PRESUNEUC
640*0Sstevel@tonic-gate 					mbuf[tmp+1] = *ccursor++;
641*0Sstevel@tonic-gate #else
642*0Sstevel@tonic-gate 					ccursor += ((len > 0) ? len : 1);
643*0Sstevel@tonic-gate #endif /* PRESUNEUC */
644*0Sstevel@tonic-gate 				/*
645*0Sstevel@tonic-gate 				 * If pointing to an alpha character,
646*0Sstevel@tonic-gate 				 * change the case.
647*0Sstevel@tonic-gate 				 */
648*0Sstevel@tonic-gate 
649*0Sstevel@tonic-gate 					tmp1 = mbuf[tmp+1];
650*0Sstevel@tonic-gate #ifdef PRESUNEUC
651*0Sstevel@tonic-gate 					if (isupper((unsigned char)tmp1))
652*0Sstevel@tonic-gate 						mbuf[tmp+1] = tolower((unsigned char)tmp1);
653*0Sstevel@tonic-gate 					else
654*0Sstevel@tonic-gate 						mbuf[tmp+1] = toupper((unsigned char)tmp1);
655*0Sstevel@tonic-gate #else
656*0Sstevel@tonic-gate 					if (iswupper(wc))
657*0Sstevel@tonic-gate 						len = wctomb((char *)(mbuf + tmp + 1),
658*0Sstevel@tonic-gate 							(wc = towlower(wc)));
659*0Sstevel@tonic-gate 					else
660*0Sstevel@tonic-gate 						len = wctomb((char *)(mbuf + tmp + 1),
661*0Sstevel@tonic-gate 							(wc = towupper(wc)));
662*0Sstevel@tonic-gate 					tmp += len - 1;
663*0Sstevel@tonic-gate #endif /* PRESUNEUC */
664*0Sstevel@tonic-gate 					if(*ccursor)
665*0Sstevel@tonic-gate 				/*
666*0Sstevel@tonic-gate 				 * If at end of line do not advance
667*0Sstevel@tonic-gate 				 * to the next character, else use a
668*0Sstevel@tonic-gate 				 * space to advance 1 column.
669*0Sstevel@tonic-gate 				 */
670*0Sstevel@tonic-gate 						mbuf[tmp+2] = ' ';
671*0Sstevel@tonic-gate 					else {
672*0Sstevel@tonic-gate 						mbuf[tmp+2] = '\0';
673*0Sstevel@tonic-gate 						tmp +=3;
674*0Sstevel@tonic-gate 						break;
675*0Sstevel@tonic-gate 					}
676*0Sstevel@tonic-gate 					tmp += 3;
677*0Sstevel@tonic-gate 				}
678*0Sstevel@tonic-gate 
679*0Sstevel@tonic-gate 				mbuf[tmp] = 0;
680*0Sstevel@tonic-gate 				macpush(mbuf, 1);
681*0Sstevel@tonic-gate 			}
682*0Sstevel@tonic-gate 			continue;
683*0Sstevel@tonic-gate 
684*0Sstevel@tonic-gate 
685*0Sstevel@tonic-gate 		/*
686*0Sstevel@tonic-gate 		 * A		Append at end of line, short for $a.
687*0Sstevel@tonic-gate 		 */
688*0Sstevel@tonic-gate 		case 'A':
689*0Sstevel@tonic-gate 			operate('$', 1);
690*0Sstevel@tonic-gate appnd:
691*0Sstevel@tonic-gate 			c = 'a';
692*0Sstevel@tonic-gate 			/* fall into ... */
693*0Sstevel@tonic-gate 
694*0Sstevel@tonic-gate 		/*
695*0Sstevel@tonic-gate 		 * a		Appends text after cursor.  Text can continue
696*0Sstevel@tonic-gate 		 *		through arbitrary number of lines.
697*0Sstevel@tonic-gate 		 */
698*0Sstevel@tonic-gate 		case 'a':
699*0Sstevel@tonic-gate 			if (*cursor) {
700*0Sstevel@tonic-gate 				wchar_t wchar;
701*0Sstevel@tonic-gate 				int length = mbtowc(&wchar, (char *)cursor, MULTI_BYTE_MAX);
702*0Sstevel@tonic-gate 				if (state == HARDOPEN) {
703*0Sstevel@tonic-gate 					if(length < 0) {
704*0Sstevel@tonic-gate 						putoctal = 1;
705*0Sstevel@tonic-gate 						putchar(*cursor);
706*0Sstevel@tonic-gate 						putoctal = 0;
707*0Sstevel@tonic-gate 					} else
708*0Sstevel@tonic-gate 						putchar(wchar);
709*0Sstevel@tonic-gate 				}
710*0Sstevel@tonic-gate 				if(length < 0)
711*0Sstevel@tonic-gate 					cursor++;
712*0Sstevel@tonic-gate 				else
713*0Sstevel@tonic-gate 					cursor += length;
714*0Sstevel@tonic-gate 			}
715*0Sstevel@tonic-gate 			goto insrt;
716*0Sstevel@tonic-gate 
717*0Sstevel@tonic-gate 		/*
718*0Sstevel@tonic-gate 		 * I		Insert at beginning of whitespace of line,
719*0Sstevel@tonic-gate 		 *		short for ^i.
720*0Sstevel@tonic-gate 		 */
721*0Sstevel@tonic-gate 		case 'I':
722*0Sstevel@tonic-gate 			operate('^', 1);
723*0Sstevel@tonic-gate 			c = 'i';
724*0Sstevel@tonic-gate 			/* fall into ... */
725*0Sstevel@tonic-gate 
726*0Sstevel@tonic-gate 		/*
727*0Sstevel@tonic-gate 		 * R		Replace characters, one for one, by input
728*0Sstevel@tonic-gate 		 *		(logically), like repeated r commands.
729*0Sstevel@tonic-gate 		 *
730*0Sstevel@tonic-gate 		 * BUG:		This is like the typeover mode of many other
731*0Sstevel@tonic-gate 		 *		editors, and is only rarely useful.  Its
732*0Sstevel@tonic-gate 		 *		implementation is a hack in a low level
733*0Sstevel@tonic-gate 		 *		routine and it doesn't work very well, e.g.
734*0Sstevel@tonic-gate 		 *		you can't move around within a R, etc.
735*0Sstevel@tonic-gate 		 */
736*0Sstevel@tonic-gate 		case 'R':
737*0Sstevel@tonic-gate 			/* fall into... */
738*0Sstevel@tonic-gate 
739*0Sstevel@tonic-gate 		/*
740*0Sstevel@tonic-gate 		 * i		Insert text to an escape in the buffer.
741*0Sstevel@tonic-gate 		 *		Text is arbitrary.  This command reminds of
742*0Sstevel@tonic-gate 		 *		the i command in bare teco.
743*0Sstevel@tonic-gate 		 */
744*0Sstevel@tonic-gate 		case 'i':
745*0Sstevel@tonic-gate insrt:
746*0Sstevel@tonic-gate 			/*
747*0Sstevel@tonic-gate 			 * Common code for all the insertion commands.
748*0Sstevel@tonic-gate 			 * Save for redo, position cursor, prepare for append
749*0Sstevel@tonic-gate 			 * at command and in visual undo.  Note that nothing
750*0Sstevel@tonic-gate 			 * is doomed, unless R when all is, and save the
751*0Sstevel@tonic-gate 			 * current line in a the undo temporary buffer.
752*0Sstevel@tonic-gate 			 */
753*0Sstevel@tonic-gate 			vmacchng(1);
754*0Sstevel@tonic-gate 			setLAST();
755*0Sstevel@tonic-gate 			vcursat(cursor);
756*0Sstevel@tonic-gate 			prepapp();
757*0Sstevel@tonic-gate 			vnoapp();
758*0Sstevel@tonic-gate 			doomed = c == 'R' ? 10000 : 0;
759*0Sstevel@tonic-gate 			if(FIXUNDO)
760*0Sstevel@tonic-gate 				vundkind = VCHNG;
761*0Sstevel@tonic-gate 			vmoving = 0;
762*0Sstevel@tonic-gate 			CP(vutmp, linebuf);
763*0Sstevel@tonic-gate 
764*0Sstevel@tonic-gate 			/*
765*0Sstevel@tonic-gate 			 * If this is a repeated command, then suppress
766*0Sstevel@tonic-gate 			 * fake insert mode on dumb terminals which looks
767*0Sstevel@tonic-gate 			 * ridiculous and wastes lots of time even at 9600B.
768*0Sstevel@tonic-gate 			 */
769*0Sstevel@tonic-gate 			if (vglobp)
770*0Sstevel@tonic-gate 				hold = HOLDQIK;
771*0Sstevel@tonic-gate 			vappend(c, cnt, 0);
772*0Sstevel@tonic-gate 			continue;
773*0Sstevel@tonic-gate 
774*0Sstevel@tonic-gate 		/*
775*0Sstevel@tonic-gate 		 * 	An attention, normally a DEL, just beeps.
776*0Sstevel@tonic-gate 		 *	If you are a vi command within ex, then
777*0Sstevel@tonic-gate 		 *	two ATTN's will drop you back to command mode.
778*0Sstevel@tonic-gate 		 */
779*0Sstevel@tonic-gate 		case ATTN:
780*0Sstevel@tonic-gate 			beep();
781*0Sstevel@tonic-gate 			if (initev || peekkey() != ATTN)
782*0Sstevel@tonic-gate 				continue;
783*0Sstevel@tonic-gate 			/* fall into... */
784*0Sstevel@tonic-gate 
785*0Sstevel@tonic-gate 		/*
786*0Sstevel@tonic-gate 		 * ^\		A quit always gets command mode.
787*0Sstevel@tonic-gate 		 */
788*0Sstevel@tonic-gate 		case QUIT:
789*0Sstevel@tonic-gate 			/*
790*0Sstevel@tonic-gate 			 * Have to be careful if we were called
791*0Sstevel@tonic-gate 			 *	g/xxx/vi
792*0Sstevel@tonic-gate 			 * since a return will just start up again.
793*0Sstevel@tonic-gate 			 * So we simulate an interrupt.
794*0Sstevel@tonic-gate 			 */
795*0Sstevel@tonic-gate 			if (inglobal)
796*0Sstevel@tonic-gate 				onintr(0);
797*0Sstevel@tonic-gate 			/* fall into... */
798*0Sstevel@tonic-gate 
799*0Sstevel@tonic-gate #ifdef notdef
800*0Sstevel@tonic-gate 		/*
801*0Sstevel@tonic-gate 		 * q		Quit back to command mode, unless called as
802*0Sstevel@tonic-gate 		 *		vi on command line in which case dont do it
803*0Sstevel@tonic-gate 		 */
804*0Sstevel@tonic-gate 		case 'q':	/* quit */
805*0Sstevel@tonic-gate 			if (initev) {
806*0Sstevel@tonic-gate 				vsave();
807*0Sstevel@tonic-gate 				CATCH
808*0Sstevel@tonic-gate 					error(gettext("Q gets ex command mode, :q leaves vi"));
809*0Sstevel@tonic-gate 				ENDCATCH
810*0Sstevel@tonic-gate 				splitw = 0;
811*0Sstevel@tonic-gate 				getDOT();
812*0Sstevel@tonic-gate 				vrepaint(cursor);
813*0Sstevel@tonic-gate 				continue;
814*0Sstevel@tonic-gate 			}
815*0Sstevel@tonic-gate #endif
816*0Sstevel@tonic-gate 			/* fall into... */
817*0Sstevel@tonic-gate 
818*0Sstevel@tonic-gate 		/*
819*0Sstevel@tonic-gate 		 * Q		Is like q, but always gets to command mode
820*0Sstevel@tonic-gate 		 *		even if command line invocation was as vi.
821*0Sstevel@tonic-gate 		 */
822*0Sstevel@tonic-gate 		case 'Q':
823*0Sstevel@tonic-gate 			vsave();
824*0Sstevel@tonic-gate 			/*
825*0Sstevel@tonic-gate 			 * If we are in the middle of a macro, throw away
826*0Sstevel@tonic-gate 			 * the rest and fix up undo.
827*0Sstevel@tonic-gate 			 * This code copied from getbr().
828*0Sstevel@tonic-gate 			 */
829*0Sstevel@tonic-gate 			if (vmacp) {
830*0Sstevel@tonic-gate 				vmacp = 0;
831*0Sstevel@tonic-gate 				if (inopen == -1)	/* don't mess up undo for esc esc */
832*0Sstevel@tonic-gate 					vundkind = VMANY;
833*0Sstevel@tonic-gate 				inopen = 1;	/* restore old setting now that macro done */
834*0Sstevel@tonic-gate 			}
835*0Sstevel@tonic-gate 			ixlatctl(1);
836*0Sstevel@tonic-gate 			return;
837*0Sstevel@tonic-gate 
838*0Sstevel@tonic-gate 
839*0Sstevel@tonic-gate 		/*
840*0Sstevel@tonic-gate 		 * ZZ		Like :x
841*0Sstevel@tonic-gate 		 */
842*0Sstevel@tonic-gate 		 case 'Z':
843*0Sstevel@tonic-gate 			forbid(getkey() != 'Z');
844*0Sstevel@tonic-gate 			oglobp = globp;
845*0Sstevel@tonic-gate 			globp = (unsigned char *)"x";
846*0Sstevel@tonic-gate 			vclrech(0);
847*0Sstevel@tonic-gate 			goto gogo;
848*0Sstevel@tonic-gate 
849*0Sstevel@tonic-gate 		/*
850*0Sstevel@tonic-gate 		 * P		Put back text before cursor or before current
851*0Sstevel@tonic-gate 		 *		line.  If text was whole lines goes back
852*0Sstevel@tonic-gate 		 *		as whole lines.  If part of a single line
853*0Sstevel@tonic-gate 		 *		or parts of whole lines splits up current
854*0Sstevel@tonic-gate 		 *		line to form many new lines.
855*0Sstevel@tonic-gate 		 *		May specify a named buffer, or the delete
856*0Sstevel@tonic-gate 		 *		saving buffers 1-9.
857*0Sstevel@tonic-gate 		 *
858*0Sstevel@tonic-gate 		 * p		Like P but after rather than before.
859*0Sstevel@tonic-gate 		 */
860*0Sstevel@tonic-gate 		case 'P':
861*0Sstevel@tonic-gate 		case 'p':
862*0Sstevel@tonic-gate 			vmoving = 0;
863*0Sstevel@tonic-gate #ifdef XPG4
864*0Sstevel@tonic-gate 			P_cursor_offset = 0;
865*0Sstevel@tonic-gate #endif
866*0Sstevel@tonic-gate #ifdef notdef
867*0Sstevel@tonic-gate 			forbid (!vreg && value(vi_UNDOMACRO) && inopen < 0);
868*0Sstevel@tonic-gate #endif
869*0Sstevel@tonic-gate 			/*
870*0Sstevel@tonic-gate 			 * If previous delete was partial line, use an
871*0Sstevel@tonic-gate 			 * append or insert to put it back so as to
872*0Sstevel@tonic-gate 			 * use insert mode on intelligent terminals.
873*0Sstevel@tonic-gate 			 */
874*0Sstevel@tonic-gate 			if (!vreg && DEL[0]) {
875*0Sstevel@tonic-gate 				setLAST();
876*0Sstevel@tonic-gate 				forbid ((unsigned char)DEL[128] == 0200);
877*0Sstevel@tonic-gate 				vglobp = DEL;
878*0Sstevel@tonic-gate 				ungetkey(c == 'p' ? 'a' : 'i');
879*0Sstevel@tonic-gate 				goto reread;
880*0Sstevel@tonic-gate 			}
881*0Sstevel@tonic-gate 
882*0Sstevel@tonic-gate 			/*
883*0Sstevel@tonic-gate 			 * If a register wasn't specified, then make
884*0Sstevel@tonic-gate 			 * sure there is something to put back.
885*0Sstevel@tonic-gate 			 */
886*0Sstevel@tonic-gate 			forbid (!vreg && unddol == dol);
887*0Sstevel@tonic-gate 			/*
888*0Sstevel@tonic-gate 			 * If we just did a macro the whole buffer is in
889*0Sstevel@tonic-gate 			 * the undo save area.  We don't want to put THAT.
890*0Sstevel@tonic-gate 			 */
891*0Sstevel@tonic-gate 			forbid (vundkind == VMANY && undkind==UNDALL);
892*0Sstevel@tonic-gate 			vsave();
893*0Sstevel@tonic-gate 			vmacchng(1);
894*0Sstevel@tonic-gate 			setLAST();
895*0Sstevel@tonic-gate 			i = 0;
896*0Sstevel@tonic-gate 			if (vreg && partreg(vreg) || !vreg && pkill[0]) {
897*0Sstevel@tonic-gate 				/*
898*0Sstevel@tonic-gate 				 * Restoring multiple lines which were partial
899*0Sstevel@tonic-gate 				 * lines; will leave cursor in middle
900*0Sstevel@tonic-gate 				 * of line after shoving restored text in to
901*0Sstevel@tonic-gate 				 * split the current line.
902*0Sstevel@tonic-gate 				 */
903*0Sstevel@tonic-gate 				i++;
904*0Sstevel@tonic-gate 				if (c == 'p' && *cursor)
905*0Sstevel@tonic-gate 					cursor = nextchr(cursor);
906*0Sstevel@tonic-gate 			} else {
907*0Sstevel@tonic-gate 				/*
908*0Sstevel@tonic-gate 				 * In whole line case, have to back up dot
909*0Sstevel@tonic-gate 				 * for P; also want to clear cursor so
910*0Sstevel@tonic-gate 				 * cursor will eventually be positioned
911*0Sstevel@tonic-gate 				 * at the beginning of the first put line.
912*0Sstevel@tonic-gate 				 */
913*0Sstevel@tonic-gate 				cursor = 0;
914*0Sstevel@tonic-gate 				if (c == 'P') {
915*0Sstevel@tonic-gate 					dot--, vcline--;
916*0Sstevel@tonic-gate 					c = 'p';
917*0Sstevel@tonic-gate 				}
918*0Sstevel@tonic-gate 			}
919*0Sstevel@tonic-gate 			killU();
920*0Sstevel@tonic-gate 
921*0Sstevel@tonic-gate 			/*
922*0Sstevel@tonic-gate 			 * The call to putreg can potentially
923*0Sstevel@tonic-gate 			 * bomb since there may be nothing in a named buffer.
924*0Sstevel@tonic-gate 			 * We thus put a catch in here.  If we didn't and
925*0Sstevel@tonic-gate 			 * there was an error we would end up in command mode.
926*0Sstevel@tonic-gate 			 */
927*0Sstevel@tonic-gate 			addr = dol;	/* old dol */
928*0Sstevel@tonic-gate 			CATCH
929*0Sstevel@tonic-gate 				vremote(1, vreg ? putreg : put, vreg);
930*0Sstevel@tonic-gate 			ONERR
931*0Sstevel@tonic-gate 				if (vreg == -1) {
932*0Sstevel@tonic-gate 					splitw = 0;
933*0Sstevel@tonic-gate 					if (op == 'P')
934*0Sstevel@tonic-gate 						dot++, vcline++;
935*0Sstevel@tonic-gate 					goto pfixup;
936*0Sstevel@tonic-gate 				}
937*0Sstevel@tonic-gate 			ENDCATCH
938*0Sstevel@tonic-gate 			splitw = 0;
939*0Sstevel@tonic-gate 			nlput = dol - addr + 1;
940*0Sstevel@tonic-gate 			if (!i) {
941*0Sstevel@tonic-gate 				/*
942*0Sstevel@tonic-gate 				 * Increment undap1, undap2 to make up
943*0Sstevel@tonic-gate 				 * for their incorrect initialization in the
944*0Sstevel@tonic-gate 				 * routine vremote before calling put/putreg.
945*0Sstevel@tonic-gate 				 */
946*0Sstevel@tonic-gate 				if (FIXUNDO)
947*0Sstevel@tonic-gate 					undap1++, undap2++;
948*0Sstevel@tonic-gate 				vcline++;
949*0Sstevel@tonic-gate 				nlput--;
950*0Sstevel@tonic-gate 
951*0Sstevel@tonic-gate 				/*
952*0Sstevel@tonic-gate 				 * After a put want current line first line,
953*0Sstevel@tonic-gate 				 * and dot was made the last line put in code
954*0Sstevel@tonic-gate 				 * run so far.  This is why we increment vcline
955*0Sstevel@tonic-gate 				 * above and decrease dot here.
956*0Sstevel@tonic-gate 				 */
957*0Sstevel@tonic-gate 				dot -= nlput - 1;
958*0Sstevel@tonic-gate 			}
959*0Sstevel@tonic-gate #ifdef TRACE
960*0Sstevel@tonic-gate 			if (trace)
961*0Sstevel@tonic-gate 				fprintf(trace, "vreplace(%d, %d, %d), undap1=%d, undap2=%d, dot=%d\n", vcline, i, nlput, lineno(undap1), lineno(undap2), lineno(dot));
962*0Sstevel@tonic-gate #endif
963*0Sstevel@tonic-gate 			vreplace(vcline, i, nlput);
964*0Sstevel@tonic-gate #ifdef XPG4
965*0Sstevel@tonic-gate 			if (op == 'P' && i > 0) {
966*0Sstevel@tonic-gate 				dot += nlput - 1;
967*0Sstevel@tonic-gate 				vcline += nlput - 1;
968*0Sstevel@tonic-gate 				cursor += P_cursor_offset;
969*0Sstevel@tonic-gate 			}
970*0Sstevel@tonic-gate #endif
971*0Sstevel@tonic-gate 			if (state != VISUAL) {
972*0Sstevel@tonic-gate 				/*
973*0Sstevel@tonic-gate 				 * Special case in open mode.
974*0Sstevel@tonic-gate 				 * Force action on the screen when a single
975*0Sstevel@tonic-gate 				 * line is put even if it is identical to
976*0Sstevel@tonic-gate 				 * the current line, e.g. on YP; otherwise
977*0Sstevel@tonic-gate 				 * you can't tell anything happened.
978*0Sstevel@tonic-gate 				 */
979*0Sstevel@tonic-gate 				vjumpto(dot, cursor, '.');
980*0Sstevel@tonic-gate 				continue;
981*0Sstevel@tonic-gate 			}
982*0Sstevel@tonic-gate pfixup:
983*0Sstevel@tonic-gate 			vrepaint(cursor);
984*0Sstevel@tonic-gate 			vfixcurs();
985*0Sstevel@tonic-gate 			continue;
986*0Sstevel@tonic-gate 
987*0Sstevel@tonic-gate 		/*
988*0Sstevel@tonic-gate 		 * ^^		Return to previous file.
989*0Sstevel@tonic-gate 		 *		Like a :e #, and thus can be used after a
990*0Sstevel@tonic-gate 		 *		"No Write" diagnostic.
991*0Sstevel@tonic-gate 		 */
992*0Sstevel@tonic-gate 		case CTRL('^'):
993*0Sstevel@tonic-gate 			forbid (hadcnt);
994*0Sstevel@tonic-gate 			vsave();
995*0Sstevel@tonic-gate 			ckaw();
996*0Sstevel@tonic-gate 			oglobp = globp;
997*0Sstevel@tonic-gate 			if (value(vi_AUTOWRITE) && !value(vi_READONLY))
998*0Sstevel@tonic-gate 				globp = (unsigned char *)"e! #";
999*0Sstevel@tonic-gate 			else
1000*0Sstevel@tonic-gate 				globp = (unsigned char *)"e #";
1001*0Sstevel@tonic-gate 			goto gogo;
1002*0Sstevel@tonic-gate 
1003*0Sstevel@tonic-gate #ifdef TAG_STACK
1004*0Sstevel@tonic-gate                 /*
1005*0Sstevel@tonic-gate                  * ^T           Pop the tag stack if enabled or else reset it
1006*0Sstevel@tonic-gate                  *              if not.
1007*0Sstevel@tonic-gate                  */
1008*0Sstevel@tonic-gate                 case CTRL('t'):
1009*0Sstevel@tonic-gate                         forbid (hadcnt);
1010*0Sstevel@tonic-gate                         vsave();
1011*0Sstevel@tonic-gate                         oglobp = globp;
1012*0Sstevel@tonic-gate                         globp = (unsigned char *) "pop";
1013*0Sstevel@tonic-gate                         goto gogo;
1014*0Sstevel@tonic-gate #endif
1015*0Sstevel@tonic-gate 		/*
1016*0Sstevel@tonic-gate 		 * ^]		Takes word after cursor as tag, and then does
1017*0Sstevel@tonic-gate 		 *		tag command.  Read ``go right to''.
1018*0Sstevel@tonic-gate 		 *		This is not a search, so the wrapscan setting
1019*0Sstevel@tonic-gate 		 *		must be ignored.  If set, then it is unset
1020*0Sstevel@tonic-gate 		 *		here and restored later.
1021*0Sstevel@tonic-gate 		 */
1022*0Sstevel@tonic-gate 		case CTRL(']'):
1023*0Sstevel@tonic-gate 			grabtag();
1024*0Sstevel@tonic-gate 			oglobp = globp;
1025*0Sstevel@tonic-gate 			if (value(vi_WRAPSCAN) == 0) {
1026*0Sstevel@tonic-gate 				tag_reset_wrap = 1;
1027*0Sstevel@tonic-gate 				value(vi_WRAPSCAN) = 1;
1028*0Sstevel@tonic-gate 			}
1029*0Sstevel@tonic-gate 			globp = (unsigned char *)"tag";
1030*0Sstevel@tonic-gate 			goto gogo;
1031*0Sstevel@tonic-gate 
1032*0Sstevel@tonic-gate 		/*
1033*0Sstevel@tonic-gate 		 * &		Like :&
1034*0Sstevel@tonic-gate 		 */
1035*0Sstevel@tonic-gate 		 case '&':
1036*0Sstevel@tonic-gate 			oglobp = globp;
1037*0Sstevel@tonic-gate 			globp = (unsigned char *)"&";
1038*0Sstevel@tonic-gate 			goto gogo;
1039*0Sstevel@tonic-gate 
1040*0Sstevel@tonic-gate 		/*
1041*0Sstevel@tonic-gate 		 * ^G		Bring up a status line at the bottom of
1042*0Sstevel@tonic-gate 		 *		the screen, like a :file command.
1043*0Sstevel@tonic-gate 		 *
1044*0Sstevel@tonic-gate 		 * BUG:		Was ^S but doesn't work in cbreak mode
1045*0Sstevel@tonic-gate 		 */
1046*0Sstevel@tonic-gate 		case CTRL('g'):
1047*0Sstevel@tonic-gate 			oglobp = globp;
1048*0Sstevel@tonic-gate 			globp = (unsigned char *)"file";
1049*0Sstevel@tonic-gate gogo:
1050*0Sstevel@tonic-gate 			addr = dot;
1051*0Sstevel@tonic-gate 			vsave();
1052*0Sstevel@tonic-gate 			goto doinit;
1053*0Sstevel@tonic-gate 
1054*0Sstevel@tonic-gate #ifdef SIGTSTP
1055*0Sstevel@tonic-gate 		/*
1056*0Sstevel@tonic-gate 		 * ^Z:	suspend editor session and temporarily return
1057*0Sstevel@tonic-gate 		 * 	to shell.  Only works with Berkeley/IIASA process
1058*0Sstevel@tonic-gate 		 *	control in kernel.
1059*0Sstevel@tonic-gate 		 */
1060*0Sstevel@tonic-gate 		case CTRL('z'):
1061*0Sstevel@tonic-gate 			forbid(dosusp == 0);
1062*0Sstevel@tonic-gate 			vsave();
1063*0Sstevel@tonic-gate 			oglobp = globp;
1064*0Sstevel@tonic-gate 			globp = (unsigned char *)"stop";
1065*0Sstevel@tonic-gate 			goto gogo;
1066*0Sstevel@tonic-gate #endif
1067*0Sstevel@tonic-gate 
1068*0Sstevel@tonic-gate 		/*
1069*0Sstevel@tonic-gate 		 * :		Read a command from the echo area and
1070*0Sstevel@tonic-gate 		 *		execute it in command mode.
1071*0Sstevel@tonic-gate 		 */
1072*0Sstevel@tonic-gate 		case ':':
1073*0Sstevel@tonic-gate 			forbid (hadcnt);
1074*0Sstevel@tonic-gate 			vsave();
1075*0Sstevel@tonic-gate 			i = tchng;
1076*0Sstevel@tonic-gate 			addr = dot;
1077*0Sstevel@tonic-gate 			if (readecho(c)) {
1078*0Sstevel@tonic-gate 				esave[0] = 0;
1079*0Sstevel@tonic-gate 				goto fixup;
1080*0Sstevel@tonic-gate 			}
1081*0Sstevel@tonic-gate 			getDOT();
1082*0Sstevel@tonic-gate 			/*
1083*0Sstevel@tonic-gate 			 * Use the visual undo buffer to store the global
1084*0Sstevel@tonic-gate 			 * string for command mode, since it is idle right now.
1085*0Sstevel@tonic-gate 			 */
1086*0Sstevel@tonic-gate 			oglobp = globp; strcpy(vutmp, genbuf+1); globp = vutmp;
1087*0Sstevel@tonic-gate doinit:
1088*0Sstevel@tonic-gate 			esave[0] = 0;
1089*0Sstevel@tonic-gate 			fixech();
1090*0Sstevel@tonic-gate 
1091*0Sstevel@tonic-gate 			/*
1092*0Sstevel@tonic-gate 			 * Have to finagle around not to lose last
1093*0Sstevel@tonic-gate 			 * character after this command (when run from ex
1094*0Sstevel@tonic-gate 			 * command mode).  This is clumsy.
1095*0Sstevel@tonic-gate 			 */
1096*0Sstevel@tonic-gate 			d = peekc; ungetchar(0);
1097*0Sstevel@tonic-gate 			if (shouldpo) {
1098*0Sstevel@tonic-gate 				/*
1099*0Sstevel@tonic-gate 				 * So after a "Hit return..." ":", we do
1100*0Sstevel@tonic-gate 				 * another "Hit return..." the next time
1101*0Sstevel@tonic-gate 				 */
1102*0Sstevel@tonic-gate 				pofix();
1103*0Sstevel@tonic-gate 				shouldpo = 0;
1104*0Sstevel@tonic-gate 			}
1105*0Sstevel@tonic-gate 			CATCH
1106*0Sstevel@tonic-gate 				/*
1107*0Sstevel@tonic-gate 				 * Save old values of options so we can
1108*0Sstevel@tonic-gate 				 * notice when they change; switch into
1109*0Sstevel@tonic-gate 				 * cooked mode so we are interruptible.
1110*0Sstevel@tonic-gate 				 */
1111*0Sstevel@tonic-gate 				onumber = value(vi_NUMBER);
1112*0Sstevel@tonic-gate 				olist = value(vi_LIST);
1113*0Sstevel@tonic-gate 				OPline = Pline;
1114*0Sstevel@tonic-gate 				OPutchar = Putchar;
1115*0Sstevel@tonic-gate #ifndef CBREAK
1116*0Sstevel@tonic-gate 				vcook();
1117*0Sstevel@tonic-gate #endif
1118*0Sstevel@tonic-gate 				commands(1, 1);
1119*0Sstevel@tonic-gate 				if (dot == zero && dol > zero)
1120*0Sstevel@tonic-gate 					dot = one;
1121*0Sstevel@tonic-gate #ifndef CBREAK
1122*0Sstevel@tonic-gate 				vraw();
1123*0Sstevel@tonic-gate #endif
1124*0Sstevel@tonic-gate 			ONERR
1125*0Sstevel@tonic-gate #ifndef CBREAK
1126*0Sstevel@tonic-gate 				vraw();
1127*0Sstevel@tonic-gate #endif
1128*0Sstevel@tonic-gate 				copy(esave, vtube[WECHO], TUBECOLS * sizeof(wchar_t));
1129*0Sstevel@tonic-gate 			ENDCATCH
1130*0Sstevel@tonic-gate 			fixol();
1131*0Sstevel@tonic-gate 			Pline = OPline;
1132*0Sstevel@tonic-gate 			Putchar = OPutchar;
1133*0Sstevel@tonic-gate 			ungetchar(d);
1134*0Sstevel@tonic-gate 			globp = oglobp;
1135*0Sstevel@tonic-gate 
1136*0Sstevel@tonic-gate 			/*
1137*0Sstevel@tonic-gate 			 * If we ended up with no lines in the buffer, make
1138*0Sstevel@tonic-gate 			 * a line.
1139*0Sstevel@tonic-gate 			 */
1140*0Sstevel@tonic-gate 			if (dot == zero) {
1141*0Sstevel@tonic-gate 				fixzero();
1142*0Sstevel@tonic-gate 			}
1143*0Sstevel@tonic-gate 			splitw = 0;
1144*0Sstevel@tonic-gate 
1145*0Sstevel@tonic-gate 			/*
1146*0Sstevel@tonic-gate 			 * Special case: did list/number options change?
1147*0Sstevel@tonic-gate 			 */
1148*0Sstevel@tonic-gate 			if (onumber != value(vi_NUMBER))
1149*0Sstevel@tonic-gate 				setnumb(value(vi_NUMBER));
1150*0Sstevel@tonic-gate 			if (olist != value(vi_LIST))
1151*0Sstevel@tonic-gate 				setlist(value(vi_LIST));
1152*0Sstevel@tonic-gate 
1153*0Sstevel@tonic-gate fixup:
1154*0Sstevel@tonic-gate 			/*
1155*0Sstevel@tonic-gate 			 * If a change occurred, other than
1156*0Sstevel@tonic-gate 			 * a write which clears changes, then
1157*0Sstevel@tonic-gate 			 * we should allow an undo even if .
1158*0Sstevel@tonic-gate 			 * didn't move.
1159*0Sstevel@tonic-gate 			 *
1160*0Sstevel@tonic-gate 			 * BUG: You can make this wrong by
1161*0Sstevel@tonic-gate 			 * tricking around with multiple commands
1162*0Sstevel@tonic-gate 			 * on one line of : escape, and including
1163*0Sstevel@tonic-gate 			 * a write command there, but it's not
1164*0Sstevel@tonic-gate 			 * worth worrying about.
1165*0Sstevel@tonic-gate 			 */
1166*0Sstevel@tonic-gate 			if (FIXUNDO && tchng && tchng != i)
1167*0Sstevel@tonic-gate 				vundkind = VMANY, cursor = 0;
1168*0Sstevel@tonic-gate 
1169*0Sstevel@tonic-gate 			/*
1170*0Sstevel@tonic-gate 			 * If we are about to do another :, hold off
1171*0Sstevel@tonic-gate 			 * updating of screen.
1172*0Sstevel@tonic-gate 			 */
1173*0Sstevel@tonic-gate 			if (vcnt < 0 && Peekkey == ':') {
1174*0Sstevel@tonic-gate 				getDOT();
1175*0Sstevel@tonic-gate 				shouldpo = 1;
1176*0Sstevel@tonic-gate 				continue;
1177*0Sstevel@tonic-gate 			}
1178*0Sstevel@tonic-gate 			shouldpo = 0;
1179*0Sstevel@tonic-gate 
1180*0Sstevel@tonic-gate 			/*
1181*0Sstevel@tonic-gate 			 * In the case where the file being edited is
1182*0Sstevel@tonic-gate 			 * new; e.g. if the initial state hasn't been
1183*0Sstevel@tonic-gate 			 * saved yet, then do so now.
1184*0Sstevel@tonic-gate 			 */
1185*0Sstevel@tonic-gate 			if (unddol == truedol) {
1186*0Sstevel@tonic-gate 				vundkind = VNONE;
1187*0Sstevel@tonic-gate 				Vlines = lineDOL();
1188*0Sstevel@tonic-gate 				if (!inglobal)
1189*0Sstevel@tonic-gate 					savevis();
1190*0Sstevel@tonic-gate 				addr = zero;
1191*0Sstevel@tonic-gate 				vcnt = 0;
1192*0Sstevel@tonic-gate 				if (esave[0] == 0)
1193*0Sstevel@tonic-gate 					copy(esave, vtube[WECHO], TUBECOLS * sizeof(wchar_t));
1194*0Sstevel@tonic-gate 			}
1195*0Sstevel@tonic-gate 
1196*0Sstevel@tonic-gate 			/*
1197*0Sstevel@tonic-gate 			 * If the current line moved reset the cursor position.
1198*0Sstevel@tonic-gate 			 */
1199*0Sstevel@tonic-gate 			if (dot != addr) {
1200*0Sstevel@tonic-gate 				vmoving = 0;
1201*0Sstevel@tonic-gate 				cursor = 0;
1202*0Sstevel@tonic-gate 			}
1203*0Sstevel@tonic-gate 
1204*0Sstevel@tonic-gate 			/*
1205*0Sstevel@tonic-gate 			 * If current line is not on screen or if we are
1206*0Sstevel@tonic-gate 			 * in open mode and . moved, then redraw.
1207*0Sstevel@tonic-gate 			 */
1208*0Sstevel@tonic-gate 			i = vcline + (dot - addr);
1209*0Sstevel@tonic-gate 			if(windowchg)
1210*0Sstevel@tonic-gate 				windowinit();
1211*0Sstevel@tonic-gate 			if (i < 0 || i >= vcnt && i >= -vcnt || state != VISUAL && dot != addr) {
1212*0Sstevel@tonic-gate 				if (state == CRTOPEN)
1213*0Sstevel@tonic-gate 					vup1();
1214*0Sstevel@tonic-gate 				if (vcnt > 0)
1215*0Sstevel@tonic-gate 					vcnt = 0;
1216*0Sstevel@tonic-gate 				vjumpto(dot, (char *) 0, '.');
1217*0Sstevel@tonic-gate 			} else {
1218*0Sstevel@tonic-gate 				/*
1219*0Sstevel@tonic-gate 				 * Current line IS on screen.
1220*0Sstevel@tonic-gate 				 * If we did a [Hit return...] then
1221*0Sstevel@tonic-gate 				 * restore vcnt and clear screen if in visual
1222*0Sstevel@tonic-gate 				 */
1223*0Sstevel@tonic-gate 				vcline = i;
1224*0Sstevel@tonic-gate 				if (vcnt < 0) {
1225*0Sstevel@tonic-gate 					vcnt = -vcnt;
1226*0Sstevel@tonic-gate 					if (state == VISUAL)
1227*0Sstevel@tonic-gate 						vclear();
1228*0Sstevel@tonic-gate 					else if (state == CRTOPEN) {
1229*0Sstevel@tonic-gate 						vcnt = 0;
1230*0Sstevel@tonic-gate 					}
1231*0Sstevel@tonic-gate 				}
1232*0Sstevel@tonic-gate 
1233*0Sstevel@tonic-gate 				/*
1234*0Sstevel@tonic-gate 				 * Limit max value of vcnt based on $
1235*0Sstevel@tonic-gate 				 */
1236*0Sstevel@tonic-gate 				i = vcline + lineDOL() - lineDOT() + 1;
1237*0Sstevel@tonic-gate 				if (i < vcnt)
1238*0Sstevel@tonic-gate 					vcnt = i;
1239*0Sstevel@tonic-gate 
1240*0Sstevel@tonic-gate 				/*
1241*0Sstevel@tonic-gate 				 * Dirty and repaint.
1242*0Sstevel@tonic-gate 				 */
1243*0Sstevel@tonic-gate 				vdirty(0, lines);
1244*0Sstevel@tonic-gate 				vrepaint(cursor);
1245*0Sstevel@tonic-gate 			}
1246*0Sstevel@tonic-gate 
1247*0Sstevel@tonic-gate 			/*
1248*0Sstevel@tonic-gate 			 * If in visual, put back the echo area
1249*0Sstevel@tonic-gate 			 * if it was clobbered.
1250*0Sstevel@tonic-gate 			 */
1251*0Sstevel@tonic-gate 			if (state == VISUAL) {
1252*0Sstevel@tonic-gate 				int sdc = destcol, sdl = destline;
1253*0Sstevel@tonic-gate 
1254*0Sstevel@tonic-gate 				splitw++;
1255*0Sstevel@tonic-gate 				vigoto(WECHO, 0);
1256*0Sstevel@tonic-gate 				for (i = 0; i < TUBECOLS - 1; i++) {
1257*0Sstevel@tonic-gate 					if (esave[i] == 0)
1258*0Sstevel@tonic-gate 						break;
1259*0Sstevel@tonic-gate 					if(esave[i] != FILLER)
1260*0Sstevel@tonic-gate 						vputchar(esave[i]);
1261*0Sstevel@tonic-gate 				}
1262*0Sstevel@tonic-gate 				splitw = 0;
1263*0Sstevel@tonic-gate 				vgoto(sdl, sdc);
1264*0Sstevel@tonic-gate 			}
1265*0Sstevel@tonic-gate 			if (tag_reset_wrap == 1) {
1266*0Sstevel@tonic-gate 				tag_reset_wrap = 0;
1267*0Sstevel@tonic-gate 				value(vi_WRAPSCAN) = 0;
1268*0Sstevel@tonic-gate 			}
1269*0Sstevel@tonic-gate 			continue;
1270*0Sstevel@tonic-gate 
1271*0Sstevel@tonic-gate 		/*
1272*0Sstevel@tonic-gate 		 * u		undo the last changing command.
1273*0Sstevel@tonic-gate 		 */
1274*0Sstevel@tonic-gate 		case 'u':
1275*0Sstevel@tonic-gate 			vundo(1);
1276*0Sstevel@tonic-gate 			continue;
1277*0Sstevel@tonic-gate 
1278*0Sstevel@tonic-gate 		/*
1279*0Sstevel@tonic-gate 		 * U		restore current line to initial state.
1280*0Sstevel@tonic-gate 		 */
1281*0Sstevel@tonic-gate 		case 'U':
1282*0Sstevel@tonic-gate 			vUndo();
1283*0Sstevel@tonic-gate 			continue;
1284*0Sstevel@tonic-gate 
1285*0Sstevel@tonic-gate fonfon:
1286*0Sstevel@tonic-gate 			beep();
1287*0Sstevel@tonic-gate 			vmacp = 0;
1288*0Sstevel@tonic-gate 			inopen = 1;	/* might have been -1 */
1289*0Sstevel@tonic-gate 			continue;
1290*0Sstevel@tonic-gate 		}
1291*0Sstevel@tonic-gate 
1292*0Sstevel@tonic-gate 		/*
1293*0Sstevel@tonic-gate 		 * Rest of commands are decoded by the operate
1294*0Sstevel@tonic-gate 		 * routine.
1295*0Sstevel@tonic-gate 		 */
1296*0Sstevel@tonic-gate 		operate(c, cnt);
1297*0Sstevel@tonic-gate 	}
1298*0Sstevel@tonic-gate }
1299*0Sstevel@tonic-gate 
1300*0Sstevel@tonic-gate /*
1301*0Sstevel@tonic-gate  * Grab the word after the cursor so we can look for it as a tag.
1302*0Sstevel@tonic-gate  */
1303*0Sstevel@tonic-gate grabtag()
1304*0Sstevel@tonic-gate {
1305*0Sstevel@tonic-gate 	register unsigned char *cp, *dp;
1306*0Sstevel@tonic-gate 
1307*0Sstevel@tonic-gate 	cp = vpastwh(cursor);
1308*0Sstevel@tonic-gate 	if (*cp) {
1309*0Sstevel@tonic-gate 		dp = lasttag;
1310*0Sstevel@tonic-gate 		do {
1311*0Sstevel@tonic-gate 			if (dp < &lasttag[sizeof lasttag - 2])
1312*0Sstevel@tonic-gate 				*dp++ = *cp;
1313*0Sstevel@tonic-gate 			cp++;
1314*0Sstevel@tonic-gate 			/* only allow ascii alphabetics */
1315*0Sstevel@tonic-gate 		} while ((isascii(*cp) && isalpha(*cp)) || isdigit(*cp) || *cp == '_');
1316*0Sstevel@tonic-gate 		*dp++ = 0;
1317*0Sstevel@tonic-gate 	}
1318*0Sstevel@tonic-gate }
1319*0Sstevel@tonic-gate 
1320*0Sstevel@tonic-gate /*
1321*0Sstevel@tonic-gate  * Before appending lines, set up addr1 and
1322*0Sstevel@tonic-gate  * the command mode undo information.
1323*0Sstevel@tonic-gate  */
1324*0Sstevel@tonic-gate prepapp()
1325*0Sstevel@tonic-gate {
1326*0Sstevel@tonic-gate 
1327*0Sstevel@tonic-gate 	addr1 = dot;
1328*0Sstevel@tonic-gate 	deletenone();
1329*0Sstevel@tonic-gate 	addr1++;
1330*0Sstevel@tonic-gate 	appendnone();
1331*0Sstevel@tonic-gate }
1332*0Sstevel@tonic-gate 
1333*0Sstevel@tonic-gate /*
1334*0Sstevel@tonic-gate  * Execute function f with the address bounds addr1
1335*0Sstevel@tonic-gate  * and addr2 surrounding cnt lines starting at dot.
1336*0Sstevel@tonic-gate  */
1337*0Sstevel@tonic-gate vremote(cnt, f, arg)
1338*0Sstevel@tonic-gate 	int cnt, (*f)(), arg;
1339*0Sstevel@tonic-gate {
1340*0Sstevel@tonic-gate 	register int oing = inglobal;
1341*0Sstevel@tonic-gate 
1342*0Sstevel@tonic-gate 	addr1 = dot;
1343*0Sstevel@tonic-gate 	addr2 = dot + cnt - 1;
1344*0Sstevel@tonic-gate 	inglobal = 0;
1345*0Sstevel@tonic-gate 	if (FIXUNDO)
1346*0Sstevel@tonic-gate 		undap1 = undap2 = dot;
1347*0Sstevel@tonic-gate 	(*f)(arg);
1348*0Sstevel@tonic-gate 	inglobal = oing;
1349*0Sstevel@tonic-gate 	if (FIXUNDO)
1350*0Sstevel@tonic-gate 		vundkind = VMANY;
1351*0Sstevel@tonic-gate 	vmcurs = 0;
1352*0Sstevel@tonic-gate }
1353*0Sstevel@tonic-gate 
1354*0Sstevel@tonic-gate /*
1355*0Sstevel@tonic-gate  * Save the current contents of linebuf, if it has changed.
1356*0Sstevel@tonic-gate  */
1357*0Sstevel@tonic-gate vsave()
1358*0Sstevel@tonic-gate {
1359*0Sstevel@tonic-gate 	unsigned char temp[LBSIZE];
1360*0Sstevel@tonic-gate 
1361*0Sstevel@tonic-gate 	strncpy(temp, linebuf, sizeof (temp));
1362*0Sstevel@tonic-gate 	if (FIXUNDO && vundkind == VCHNG || vundkind == VCAPU) {
1363*0Sstevel@tonic-gate 		/*
1364*0Sstevel@tonic-gate 		 * If the undo state is saved in the temporary buffer
1365*0Sstevel@tonic-gate 		 * vutmp, then we sync this into the temp file so that
1366*0Sstevel@tonic-gate 		 * we will be able to undo even after we have moved off
1367*0Sstevel@tonic-gate 		 * the line.  It would be possible to associate a line
1368*0Sstevel@tonic-gate 		 * with vutmp but we assume that vutmp is only associated
1369*0Sstevel@tonic-gate 		 * with line dot (e.g. in case ':') above, so beware.
1370*0Sstevel@tonic-gate 		 */
1371*0Sstevel@tonic-gate 		prepapp();
1372*0Sstevel@tonic-gate 		strcLIN(vutmp);
1373*0Sstevel@tonic-gate 		putmark(dot);
1374*0Sstevel@tonic-gate 		vremote(1, yank, 0);
1375*0Sstevel@tonic-gate 		vundkind = VMCHNG;
1376*0Sstevel@tonic-gate 		notecnt = 0;
1377*0Sstevel@tonic-gate 		undkind = UNDCHANGE;
1378*0Sstevel@tonic-gate 	}
1379*0Sstevel@tonic-gate 	/*
1380*0Sstevel@tonic-gate 	 * Get the line out of the temp file and do nothing if it hasn't
1381*0Sstevel@tonic-gate 	 * changed.  This may seem like a loss, but the line will
1382*0Sstevel@tonic-gate 	 * almost always be in a read buffer so this may well avoid disk i/o.
1383*0Sstevel@tonic-gate 	 */
1384*0Sstevel@tonic-gate 	getDOT();
1385*0Sstevel@tonic-gate 	if (strncmp(linebuf, temp, sizeof (temp)) == 0)
1386*0Sstevel@tonic-gate 		return;
1387*0Sstevel@tonic-gate 	strcLIN(temp);
1388*0Sstevel@tonic-gate 	putmark(dot);
1389*0Sstevel@tonic-gate }
1390*0Sstevel@tonic-gate 
1391*0Sstevel@tonic-gate #undef	forbid
1392*0Sstevel@tonic-gate #define	forbid(a)	if (a) { beep(); return; }
1393*0Sstevel@tonic-gate 
1394*0Sstevel@tonic-gate /*
1395*0Sstevel@tonic-gate  * Do a z operation.
1396*0Sstevel@tonic-gate  * Code here is rather long, and very uninteresting.
1397*0Sstevel@tonic-gate  */
1398*0Sstevel@tonic-gate vzop(hadcnt, cnt, c)
1399*0Sstevel@tonic-gate 	bool hadcnt;
1400*0Sstevel@tonic-gate 	int cnt;
1401*0Sstevel@tonic-gate 	register int c;
1402*0Sstevel@tonic-gate {
1403*0Sstevel@tonic-gate 	register line *addr;
1404*0Sstevel@tonic-gate 
1405*0Sstevel@tonic-gate 	if (state != VISUAL) {
1406*0Sstevel@tonic-gate 		/*
1407*0Sstevel@tonic-gate 		 * Z from open; always like a z=.
1408*0Sstevel@tonic-gate 		 * This code is a mess and should be cleaned up.
1409*0Sstevel@tonic-gate 		 */
1410*0Sstevel@tonic-gate 		vmoveitup(1, 1);
1411*0Sstevel@tonic-gate 		vgoto(outline, 0);
1412*0Sstevel@tonic-gate 		ostop(normf);
1413*0Sstevel@tonic-gate 		setoutt();
1414*0Sstevel@tonic-gate 		addr2 = dot;
1415*0Sstevel@tonic-gate 		vclear();
1416*0Sstevel@tonic-gate 		destline = WECHO;
1417*0Sstevel@tonic-gate 		zop2(Xhadcnt ? Xcnt : value(vi_WINDOW) - 1, '=');
1418*0Sstevel@tonic-gate 		if (state == CRTOPEN)
1419*0Sstevel@tonic-gate 			putnl();
1420*0Sstevel@tonic-gate 		putNFL();
1421*0Sstevel@tonic-gate 		termreset();
1422*0Sstevel@tonic-gate 		Outchar = vputchar;
1423*0Sstevel@tonic-gate 		(void)ostart();
1424*0Sstevel@tonic-gate 		vcnt = 0;
1425*0Sstevel@tonic-gate 		outline = destline = 0;
1426*0Sstevel@tonic-gate 		vjumpto(dot, cursor, 0);
1427*0Sstevel@tonic-gate 		return;
1428*0Sstevel@tonic-gate 	}
1429*0Sstevel@tonic-gate 	if (hadcnt) {
1430*0Sstevel@tonic-gate 		addr = zero + cnt;
1431*0Sstevel@tonic-gate 		if (addr < one)
1432*0Sstevel@tonic-gate 			addr = one;
1433*0Sstevel@tonic-gate 		if (addr > dol)
1434*0Sstevel@tonic-gate 			addr = dol;
1435*0Sstevel@tonic-gate 		markit(addr);
1436*0Sstevel@tonic-gate 	} else
1437*0Sstevel@tonic-gate 		switch (c) {
1438*0Sstevel@tonic-gate 
1439*0Sstevel@tonic-gate 		case '+':
1440*0Sstevel@tonic-gate 			addr = dot + vcnt - vcline;
1441*0Sstevel@tonic-gate 			break;
1442*0Sstevel@tonic-gate 
1443*0Sstevel@tonic-gate 		case '^':
1444*0Sstevel@tonic-gate 			addr = dot - vcline - 1;
1445*0Sstevel@tonic-gate 			forbid (addr < one);
1446*0Sstevel@tonic-gate 			c = '-';
1447*0Sstevel@tonic-gate 			break;
1448*0Sstevel@tonic-gate 
1449*0Sstevel@tonic-gate 		default:
1450*0Sstevel@tonic-gate 			addr = dot;
1451*0Sstevel@tonic-gate 			break;
1452*0Sstevel@tonic-gate 		}
1453*0Sstevel@tonic-gate 	switch (c) {
1454*0Sstevel@tonic-gate 
1455*0Sstevel@tonic-gate 	case '.':
1456*0Sstevel@tonic-gate 	case '-':
1457*0Sstevel@tonic-gate 		break;
1458*0Sstevel@tonic-gate 
1459*0Sstevel@tonic-gate 	case '^':
1460*0Sstevel@tonic-gate 		forbid (addr <= one);
1461*0Sstevel@tonic-gate 		break;
1462*0Sstevel@tonic-gate 
1463*0Sstevel@tonic-gate 	case '+':
1464*0Sstevel@tonic-gate 		forbid (addr >= dol);
1465*0Sstevel@tonic-gate 		/* fall into ... */
1466*0Sstevel@tonic-gate 
1467*0Sstevel@tonic-gate 	case CR:
1468*0Sstevel@tonic-gate 	case NL:
1469*0Sstevel@tonic-gate 		c = CR;
1470*0Sstevel@tonic-gate 		break;
1471*0Sstevel@tonic-gate 
1472*0Sstevel@tonic-gate 	default:
1473*0Sstevel@tonic-gate 		beep();
1474*0Sstevel@tonic-gate 		return;
1475*0Sstevel@tonic-gate 	}
1476*0Sstevel@tonic-gate 	vmoving = 0;
1477*0Sstevel@tonic-gate 	vjumpto(addr, NOSTR, c);
1478*0Sstevel@tonic-gate }
1479