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