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