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