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_vops2.c	1.34 (gritter) 1/12/05";
77 #endif
78 #endif
79 
80 /* from ex_vops2.c	6.8 (Berkeley) 6/7/85 */
81 
82 #include "ex.h"
83 #include "ex_tty.h"
84 #include "ex_vis.h"
85 
86 /*
87  * Low level routines for operations sequences,
88  * and mostly, insert mode (and a subroutine
89  * to read an input line, including in the echo area.)
90  */
91 extern char	*vUA1, *vUA2;		/* mjm: extern; also in ex_vops.c */
92 extern char	*vUD1, *vUD2;		/* mjm: extern; also in ex_vops.c */
93 
94 /*
95  * Obleeperate characters in hardcopy
96  * open with \'s.
97  */
98 void
bleep(register int i,char * cp)99 bleep(register int i, char *cp)
100 {
101 
102 	i -= column(cp);
103 	do
104 		putchar('\\' | QUOTE);
105 	while (--i >= 0);
106 	rubble = 1;
107 }
108 
109 /*
110  * Common code for middle part of delete
111  * and change operating on parts of lines.
112  */
113 int
vdcMID(void)114 vdcMID(void)
115 {
116 	register char *cp;
117 
118 	squish();
119 	setLAST();
120 	if (FIXUNDO)
121 		vundkind = VCHNG, CP(vutmp, linebuf);
122 	if (wcursor < cursor)
123 		cp = wcursor, wcursor = cursor, cursor = cp;
124 	vUD1 = vUA1 = vUA2 = cursor; vUD2 = wcursor;
125 	return (column(wcursor + skipleft(linebuf, wcursor)));
126 }
127 
128 /*
129  * Take text from linebuf and stick it
130  * in the VBSIZE buffer BUF.  Used to save
131  * deleted text of part of line.
132  */
133 void
takeout(cell * BUF)134 takeout(cell *BUF)
135 {
136 	register char *cp;
137 
138 	if (wcursor < linebuf)
139 		wcursor = linebuf;
140 	if (cursor == wcursor) {
141 		beep();
142 		return;
143 	}
144 	if (wcursor < cursor) {
145 		cp = wcursor;
146 		wcursor = cursor;
147 		cursor = cp;
148 	}
149 	setBUF(BUF);
150 	if ((BUF[0] & (QUOTE|TRIM)) == OVERBUF)
151 		beep();
152 }
153 
154 /*
155  * Are we at the end of the printed representation of the
156  * line?  Used internally in hardcopy open.
157  */
158 int
ateopr(void)159 ateopr(void)
160 {
161 	register int i, c;
162 	register cell *cp = vtube[destline] + destcol;
163 
164 	for (i = WCOLS - destcol; i > 0; i--) {
165 		c = *cp++;
166 		if (c == 0)
167 			return (1);
168 		if (c != ' ' && (c & QUOTE) == 0)
169 			return (0);
170 	}
171 	return (1);
172 }
173 
174 void
showmode(int mode)175 showmode(int mode)
176 {
177 	int sdc = destcol, sdl = destline;
178 	char *ocurs, *str;
179 
180 	if (value(SHOWMODE) == 0 || TCOLUMNS <= 20 || state == ONEOPEN
181 			|| state == HARDOPEN || vmacp != NULL)
182 		return;
183 	ocurs = cursor;
184 	fixech();
185 	vgoto(WECHO, TCOLUMNS - 20);
186 	switch (mode) {
187 	case 0:		str = catgets(catd, 1, 227,
188 					"               ");
189 			break;
190 	case 'A':	/*FALLTHROUGH*/
191 	case 'a':	str = catgets(catd, 1, 228,
192 					"AAPPEND MODE");
193 			break;
194 	case 'C':	/*FALLTHROUGH*/
195 	case 'c':	str = catgets(catd, 1, 229,
196 					"CCHANGE MODE");
197 			break;
198 	case 'O':	/*FALLTHROUGH*/
199 	case 'o':	str = catgets(catd, 1, 230,
200 					"OOPEN MODE");
201 			break;
202 	case 'R':	str = catgets(catd, 1, 231,
203 					"RREPLACE MODE");
204 			break;
205 	case 'r':	str = catgets(catd, 1, 232,
206 					"rREPLACE 1 CHAR");
207 			break;
208 	default:	str = catgets(catd, 1, 233,
209 					"IINSERT MODE");
210 	}
211 	if (value(TERSE))
212 		putchar(str[0]);
213 	else
214 		printf(&str[1]);
215 	vgoto(sdl, sdc);
216 	cursor = ocurs;
217 	splitw = 0;
218 }
219 
220 /*
221  * Append.
222  *
223  * This routine handles the top level append, doing work
224  * as each new line comes in, and arranging repeatability.
225  * It also handles append with repeat counts, and calculation
226  * of autoindents for new lines.
227  */
228 bool	vaifirst;
229 bool	gobbled;
230 char	*ogcursor;
231 
232 /*
233  * The addtext() and addto() routines combined, accepting a single
234  * cell character.
235  */
236 void
addc(cell c)237 addc(cell c)
238 {
239 	register cell *cp = INS;
240 
241 	if (vglobp)
242 		return;
243 	if ((cp[0] & (QUOTE|TRIM)) != OVERBUF) {
244 		if (cellen(cp) + 2 >= VBSIZE) {
245 			cp[0] = OVERBUF;
246 			lastcmd[0] = 0;
247 		} else {
248 			while (*cp)
249 				cp++;
250 			*cp++ = c;
251 			*cp++ = 0;
252 		}
253 	}
254 }
255 
256 void
vappend(int ch,int cnt,int indent)257 vappend(int ch, int cnt, int indent)
258 /*	int ch;		/\* mjm: char --> int */
259 {
260 	register int i = 0;
261 	register char *gcursor;
262 	bool escape;
263 	int repcnt, savedoomed;
264 	short oldhold = hold;
265 #ifdef	SIGWINCH
266 	sigset_t set, oset;
267 #endif
268 
269 	/*
270 	 * Before a move in hardopen when the line is dirty
271 	 * or we are in the middle of the printed representation,
272 	 * we retype the line to the left of the cursor so the
273 	 * insert looks clean.
274 	 */
275 	if (ch != 'o' && state == HARDOPEN && (rubble || !ateopr())) {
276 		rubble = 1;
277 		gcursor = cursor;
278 		i = *gcursor;
279 		*gcursor = ' ';
280 		wcursor = gcursor;
281 		vmove(0);
282 		*gcursor = i;
283 	}
284 	vaifirst = indent == 0;
285 
286 	showmode(ch);
287 
288 	/*
289 	 * Handle replace character by (eventually)
290 	 * limiting the number of input characters allowed
291 	 * in the vgetline routine.
292 	 */
293 	if (ch == 'r')
294 		repcnt = 2;
295 	else
296 		repcnt = 0;
297 
298 	/*
299 	 * If an autoindent is specified, then
300 	 * generate a mixture of blanks to tabs to implement
301 	 * it and place the cursor after the indent.
302 	 * Text read by the vgetline routine will be placed in genbuf,
303 	 * so the indent is generated there.
304 	 */
305 	if (value(AUTOINDENT) && indent != 0) {
306 		gcursor = genindent(indent);
307 		*gcursor = 0;
308 		vgotoCL(qcolumn(cursor + skipright(cursor, linebuf), genbuf));
309 	} else {
310 		gcursor = genbuf;
311 		*gcursor = 0;
312 		if (ch == 'o')
313 			vfixcurs();
314 	}
315 
316 	/*
317 	 * Prepare for undo.  Pointers delimit inserted portion of line.
318 	 */
319 	vUA1 = vUA2 = cursor;
320 
321 	/*
322 	 * If we are not in a repeated command and a ^@ comes in
323 	 * then this means the previous inserted text.
324 	 * If there is none or it was too long to be saved,
325 	 * then beep() and also arrange to undo any damage done
326 	 * so far (e.g. if we are a change.)
327 	 */
328 	if ((vglobp && *vglobp == 0) || peekbr()) {
329 		if ((INS[0] & (QUOTE|TRIM)) == OVERBUF) {
330 			beep();
331 			if (!splitw)
332 				ungetkey('u');
333 			doomed = 0;
334 			hold = oldhold;
335 			showmode(0);
336 			return;
337 		}
338 		/*
339 		 * Unread input from INS.
340 		 * An escape will be generated at end of string.
341 		 * Hold off n^^2 type update on dumb terminals.
342 		 */
343 		vglobp = INS;
344 		hold |= HOLDQIK;
345 	} else if (vglobp == 0)
346 		/*
347 		 * Not a repeated command, get
348 		 * a new inserted text for repeat.
349 		 */
350 		INS[0] = 0;
351 
352 	/*
353 	 * For wrapmargin to hack away second space after a '.'
354 	 * when the first space caused a line break we keep
355 	 * track that this happened in gobblebl, which says
356 	 * to gobble up a blank silently.
357 	 */
358 	gobblebl = 0;
359 
360 #ifdef	SIGWINCH
361 	sigemptyset(&set);
362 	sigaddset(&set, SIGWINCH);
363 	sigprocmask(SIG_BLOCK, &set, &oset);
364 #endif
365 	/*
366 	 * Text gathering loop.
367 	 * New text goes into genbuf starting at gcursor.
368 	 * cursor preserves place in linebuf where text will eventually go.
369 	 */
370 	if (*cursor == 0 || state == CRTOPEN)
371 		hold |= HOLDROL;
372 	for (;;) {
373 		if (ch == 'r' && repcnt == 0)
374 			escape = 0;
375 		else {
376 			gcursor = vgetline(repcnt, gcursor, &escape, ch);
377 
378 			/*
379 			 * After an append, stick information
380 			 * about the ^D's and ^^D's and 0^D's in
381 			 * the repeated text buffer so repeated
382 			 * inserts of stuff indented with ^D as backtab's
383 			 * can work.
384 			 */
385 			if (HADUP)
386 				addc('^');
387 			else if (HADZERO)
388 				addc('0');
389 			while (CDCNT > 0)
390 #ifndef	BIT8
391 				addc('\204'), CDCNT--;
392 #else
393 				addc(OVERBUF), CDCNT--;
394 #endif
395 
396 			if (gobbled)
397 				addc(' ');
398 			addtext(ogcursor);
399 		}
400 		repcnt = 0;
401 
402 		/*
403 		 * Smash the generated and preexisting indents together
404 		 * and generate one cleanly made out of tabs and spaces
405 		 * if we are using autoindent.
406 		 */
407 		if (!vaifirst && value(AUTOINDENT)) {
408 			i = fixindent(indent);
409 			if (!HADUP)
410 				indent = i;
411 			gcursor = strend(genbuf);
412 		}
413 
414 		/*
415 		 * Limit the repetition count based on maximum
416 		 * possible line length; do output implied
417 		 * by further count (> 1) and cons up the new line
418 		 * in linebuf.
419 		 */
420 		cnt = vmaxrep(ch, cnt);
421 		CP(gcursor + skipright(ogcursor, gcursor), cursor);
422 		do {
423 			CP(cursor, genbuf);
424 			if (cnt > 1) {
425 				int oldhold = hold;
426 
427 				Outchar = vinschar;
428 				hold |= HOLDQIK;
429 				printf("%s", genbuf);
430 				hold = oldhold;
431 				Outchar = vputchar;
432 			}
433 			cursor += gcursor - genbuf;
434 		} while (--cnt > 0);
435 		endim();
436 		vUA2 = cursor;
437 		if (escape != '\n')
438 			CP(cursor, gcursor + skipright(ogcursor, gcursor));
439 
440 		/*
441 		 * If doomed characters remain, clobber them,
442 		 * and reopen the line to get the display exact.
443 		 */
444 		if (state != HARDOPEN) {
445 			DEPTH(vcline) = 0;
446 			savedoomed = doomed;
447 			if (doomed > 0) {
448 				register int cind = cindent();
449 
450 				physdc(cind, cind + doomed);
451 				doomed = 0;
452 			}
453 			i = vreopen(LINE(vcline), lineDOT(), vcline);
454 #ifdef TRACE
455 			if (trace)
456 				fprintf(trace, "restoring doomed from %d to %d\n", doomed, savedoomed);
457 #endif
458 			if (ch == 'R')
459 				doomed = savedoomed;
460 		}
461 
462 		/*
463 		 * All done unless we are continuing on to another line.
464 		 */
465 		if (escape != '\n')
466 			break;
467 
468 		/*
469 		 * Set up for the new line.
470 		 * First save the current line, then construct a new
471 		 * first image for the continuation line consisting
472 		 * of any new autoindent plus the pushed ahead text.
473 		 */
474 		showmode(0);
475 		killU();
476 		addc(gobblebl ? ' ' : '\n');
477 		vsave();
478 		cnt = 1;
479 		if (value(AUTOINDENT)) {
480 #ifdef LISPCODE
481 			if (value(LISP))
482 				indent = lindent(dot + 1);
483 			else
484 #endif
485 			     if (!HADUP && vaifirst)
486 				indent = whitecnt(linebuf);
487 			vaifirst = 0;
488 			strcLIN(vpastwh(gcursor + 1));
489 			gcursor = genindent(indent);
490 			*gcursor = 0;
491 			if (gcursor + strlen(linebuf) > &genbuf[LBSIZE - 2])
492 				gcursor = genbuf;
493 			CP(gcursor, linebuf);
494 		} else {
495 			CP(genbuf, gcursor + skipright(ogcursor, gcursor));
496 			gcursor = genbuf;
497 		}
498 
499 		/*
500 		 * If we started out as a single line operation and are now
501 		 * turning into a multi-line change, then we had better yank
502 		 * out dot before it changes so that undo will work
503 		 * correctly later.
504 		 */
505 		if (FIXUNDO && vundkind == VCHNG) {
506 			vremote(1, yank, 0);
507 			undap1--;
508 		}
509 
510 		/*
511 		 * Now do the append of the new line in the buffer,
512 		 * and update the display.  If slowopen
513 		 * we don't do very much.
514 		 */
515 		vdoappend(genbuf);
516 		vundkind = VMANYINS;
517 		vcline++;
518 		if (state != VISUAL)
519 			vshow(dot, NOLINE);
520 		else {
521 			i += LINE(vcline - 1);
522 			vopen(dot, i);
523 			if (value(SLOWOPEN))
524 				vscrap();
525 			else
526 				vsync1(LINE(vcline));
527 		}
528 		strcLIN(gcursor);
529 		*gcursor = 0;
530 		cursor = linebuf;
531 		vgotoCL(qcolumn(cursor + skipleft(ogcursor, cursor), genbuf));
532 		showmode(ch);
533 	}
534 
535 	/*
536 	 * All done with insertion, position the cursor
537 	 * and sync the screen.
538 	 */
539 	showmode(0);
540 	hold = oldhold;
541 	if (cursor > linebuf)
542 		cursor += skipleft(linebuf, cursor);
543 	if (state != HARDOPEN)
544 		vsyncCL();
545 	else if (cursor > linebuf)
546 		back1();
547 	doomed = 0;
548 	wcursor = cursor;
549 	vmove(0);
550 #ifdef	SIGWINCH
551 	sigprocmask(SIG_SETMASK, &oset, NULL);
552 #endif
553 }
554 
555 /*
556  * Subroutine for vgetline to back up a single character position,
557  * backwards around end of lines (vgoto can't hack columns which are
558  * less than 0 in general).
559  */
560 void
back1(void)561 back1(void)
562 {
563 
564 	vgoto(destline - 1, WCOLS + destcol - 1);
565 }
566 
567 #define	gappend(c) { \
568 		int	_c = c; \
569 		xgappend(_c, &gcursor); \
570 	}
571 
572 static void
xgappend(int c,char ** gp)573 xgappend(int c, char **gp)
574 {
575 	if (*gp >= &genbuf[MAXBSIZE-mb_cur_max-1]) {
576 		beep();
577 		return;
578 	}
579 #ifdef	MB
580 	if (mb_cur_max > 1 && !(c & INVBIT)) {
581 		char	mb[MB_LEN_MAX];
582 		int	i, n;
583 		n = wctomb(mb, c);
584 		for (i = 0; i < n; i++)
585 			*(*gp)++ = mb[i];
586 	} else
587 #endif	/* MB */
588 		*(*gp)++ = c & 0377;
589 }
590 
591 /*
592  * Get a line into genbuf after gcursor.
593  * Cnt limits the number of input characters
594  * accepted and is used for handling the replace
595  * single character command.  Aescaped is the location
596  * where we stick a termination indicator (whether we
597  * ended with an ESCAPE or a newline/return.
598  *
599  * We do erase-kill type processing here and also
600  * are careful about the way we do this so that it is
601  * repeatable.  (I.e. so that your kill doesn't happen,
602  * when you repeat an insert if it was escaped with \ the
603  * first time you did it.  commch is the command character
604  * involved, including the prompt for readline.
605  */
606 char *
vgetline(int cnt,char * gcursor,bool * aescaped,int commch)607 vgetline(int cnt, char *gcursor, bool *aescaped, int commch)
608 {
609 	register int c, ch;
610 	register char *cp;
611 	int x, y, iwhite, backsl=0;
612 	cell *iglobp;
613 	char cstr[2];
614 	int (*OO)() = Outchar;
615 
616 	/*
617 	 * Clear the output state and counters
618 	 * for autoindent backwards motion (counts of ^D, etc.)
619 	 * Remember how much white space at beginning of line so
620 	 * as not to allow backspace over autoindent.
621 	 */
622 	*aescaped = 0;
623 	ogcursor = gcursor;
624 	flusho();
625 	CDCNT = 0;
626 	HADUP = 0;
627 	HADZERO = 0;
628 	gobbled = 0;
629 	iwhite = whitecnt(genbuf);
630 	iglobp = vglobp;
631 
632 	/*
633 	 * Carefully avoid using vinschar in the echo area.
634 	 */
635 	if (splitw)
636 		Outchar = vputchar;
637 	else {
638 		Outchar = vinschar;
639 		vprepins();
640 	}
641 	for (;;) {
642 		backsl = 0;
643 		if (gobblebl)
644 			gobblebl--;
645 		if (cnt != 0) {
646 			cnt--;
647 			if (cnt == 0)
648 				goto vadone;
649 		}
650 		c = getkey();
651 		if (c != ATTN)
652 			c &= (QUOTE|TRIM);
653 		ch = c;
654 		maphopcnt = 0;
655 		if (vglobp == 0 && Peekkey == 0 && commch != 'r')
656 			while ((ch = map(c, immacs)) != c) {
657 				c = ch;
658 				if (!value(REMAP))
659 					break;
660 				if (++maphopcnt > 256)
661 					error(catgets(catd, 1, 234,
662 						"Infinite macro loop"));
663 			}
664 		if (!iglobp) {
665 
666 			/*
667 			 * Erase-kill type processing.
668 			 * Only happens if we were not reading
669 			 * from untyped input when we started.
670 			 * Map users erase to ^H, kill to -1 for switch.
671 			 */
672 			if (c == tty.c_cc[VERASE])
673 				c = CTRL('h');
674 			else if (c == tty.c_cc[VKILL])
675 				c = -1;
676 			if (c == ATTN)
677 				goto case_ATTN;
678 			switch (c) {
679 
680 			/*
681 			 * ^?		Interrupt drops you back to visual
682 			 *		command mode with an unread interrupt
683 			 *		still in the input buffer.
684 			 *
685 			 * ^\		Quit does the same as interrupt.
686 			 *		If you are a ex command rather than
687 			 *		a vi command this will drop you
688 			 *		back to command mode for sure.
689 			 */
690 			case QUIT:
691 case_ATTN:
692 				ungetkey(c);
693 				goto vadone;
694 
695 			/*
696 			 * ^H		Backs up a character in the input.
697 			 *
698 			 * BUG:		Can't back around line boundaries.
699 			 *		This is hard because stuff has
700 			 *		already been saved for repeat.
701 			 */
702 			case CTRL('h'):
703 bakchar:
704 				cp = gcursor + skipleft(ogcursor, gcursor);
705 				if (cp < ogcursor) {
706 					if (splitw) {
707 						/*
708 						 * Backspacing over readecho
709 						 * prompt. Pretend delete but
710 						 * don't beep.
711 						 */
712 						ungetkey(c);
713 						goto vadone;
714 					}
715 					beep();
716 					continue;
717 				}
718 				goto vbackup;
719 
720 			/*
721 			 * ^W		Back up a white/non-white word.
722 			 */
723 			case CTRL('w'):
724 				wdkind = 1;
725 				for (cp = gcursor; cp > ogcursor
726 						&& isspace(cp[-1]&0377); cp--)
727 					continue;
728 				for (c = wordch(cp - 1);
729 				    cp > ogcursor && wordof(c, cp - 1); cp--)
730 					continue;
731 				goto vbackup;
732 
733 			/*
734 			 * users kill	Kill input on this line, back to
735 			 *		the autoindent.
736 			 */
737 			case -1:
738 				cp = ogcursor;
739 vbackup:
740 				if (cp == gcursor) {
741 					beep();
742 					continue;
743 				}
744 				endim();
745 				*cp = 0;
746 				c = cindent();
747 				vgotoCL(qcolumn(cursor +
748 					skipleft(linebuf, cursor), genbuf));
749 				if (doomed >= 0)
750 					doomed += c - cindent();
751 				gcursor = cp;
752 				continue;
753 
754 			/*
755 			 * \		Followed by erase or kill
756 			 *		maps to just the erase or kill.
757 			 */
758 			case '\\':
759 				x = destcol, y = destline;
760 				putchar('\\');
761 				vcsync();
762 				c = getkey();
763 				if (c == tty.c_cc[VERASE]
764 				    || c == tty.c_cc[VKILL])
765 				{
766 					vgoto(y, x);
767 					if (doomed >= 0)
768 						doomed++;
769 					goto def;
770 				}
771 				ungetkey(c), c = '\\';
772 				backsl = 1;
773 				break;
774 
775 			/*
776 			 * ^Q		Super quote following character
777 			 *		Only ^@ is verboten (trapped at
778 			 *		a lower level) and \n forces a line
779 			 *		split so doesn't really go in.
780 			 *
781 			 * ^V		Synonym for ^Q
782 			 */
783 			case CTRL('q'):
784 			case CTRL('v'):
785 				x = destcol, y = destline;
786 				putchar('^');
787 				vgoto(y, x);
788 				c = getkey();
789 				if (c != NL) {
790 					if (doomed >= 0)
791 						doomed++;
792 					goto def;
793 				}
794 				break;
795 			}
796 		}
797 
798 		/*
799 		 * If we get a blank not in the echo area
800 		 * consider splitting the window in the wrapmargin.
801 		 */
802 		if (c != NL && !splitw) {
803 			if (c == ' ' && gobblebl) {
804 				gobbled = 1;
805 				continue;
806 			}
807 			if (value(WRAPMARGIN) &&
808 				(outcol >= OCOLUMNS - value(WRAPMARGIN) ||
809 				 backsl && outcol==0) &&
810 				commch != 'r') {
811 				/*
812 				 * At end of word and hit wrapmargin.
813 				 * Move the word to next line and keep going.
814 				 */
815 				wdkind = 1;
816 				gappend(c);
817 				if (backsl)
818 					gappend(getkey());
819 				*gcursor = 0;
820 				/*
821 				 * Find end of previous word if we are past it.
822 				 */
823 				for (cp=gcursor; cp>ogcursor
824 						&& isspace(cp[-1]&0377); cp--)
825 					;
826 				if (outcol+(backsl?OCOLUMNS:0) - (gcursor-cp) >= OCOLUMNS - value(WRAPMARGIN)) {
827 					/*
828 					 * Find beginning of previous word.
829 					 */
830 					for (; cp>ogcursor && !isspace(cp[-1]&0377); cp--)
831 						;
832 					if (cp <= ogcursor) {
833 						/*
834 						 * There is a single word that
835 						 * is too long to fit.  Just
836 						 * let it pass, but beep for
837 						 * each new letter to warn
838 						 * the luser.
839 						 */
840 						c = *--gcursor;
841 						*gcursor = 0;
842 						beep();
843 						goto dontbreak;
844 					}
845 					/*
846 					 * Save it for next line.
847 					 */
848 					macpush(cp, 0);
849 					cp--;
850 				}
851 				macpush("\n", 0);
852 				/*
853 				 * Erase white space before the word.
854 				 */
855 				while (cp > ogcursor && isspace(cp[-1]&0377))
856 					cp--;	/* skip blank */
857 				gobblebl = 3;
858 				goto vbackup;
859 			}
860 		dontbreak:;
861 		}
862 
863 		/*
864 		 * Word abbreviation mode.
865 		 */
866 		cstr[0] = c;
867 		if (anyabbrs && gcursor > ogcursor && !wordch(cstr) && wordch(gcursor-1)) {
868 				int wdtype, abno;
869 
870 				cstr[1] = 0;
871 				wdkind = 1;
872 				cp = gcursor + skipleft(ogcursor, gcursor);
873 				for (wdtype = wordch(cp - 1);
874 				    cp > ogcursor && wordof(wdtype, cp - 1); cp--)
875 					;
876 				*gcursor = 0;
877 				for (abno=0; abbrevs[abno].mapto; abno++) {
878 					if (!abbrevs[abno].hadthis &&
879 						eq(cp, abbrevs[abno].cap)) {
880 						abbrevs[abno].hadthis++;
881 						macpush(cstr, 0);
882 						macpush(abbrevs[abno].mapto, 0);
883 						goto vbackup;
884 					}
885 				}
886 		}
887 
888 #ifdef	BIT8
889 		if (c == OVERBUF)
890 			goto btrp;
891 #endif
892 		switch (c) {
893 
894 		/*
895 		 * ^M		Except in repeat maps to \n.
896 		 */
897 		case CR:
898 			if (vglobp)
899 				goto def;
900 			c = '\n';
901 			/* presto chango ... */
902 
903 		/*
904 		 * \n		Start new line.
905 		 */
906 		case NL:
907 			*aescaped = c;
908 			goto vadone;
909 
910 		/*
911 		 * escape	End insert unless repeat and more to repeat.
912 		 */
913 		case ESCAPE:
914 			if (lastvgk)
915 				goto def;
916 			goto vadone;
917 
918 		/*
919 		 * ^D		Backtab.
920 		 * ^T		Software forward tab.
921 		 *
922 		 *		Unless in repeat where this means these
923 		 *		were superquoted in.
924 		 */
925 		case CTRL('d'):
926 		case CTRL('t'):
927 			if (vglobp)
928 				goto def;
929 			/* fall into ... */
930 
931 		/*
932 		 * ^D|QUOTE	Is a backtab (in a repeated command).
933 		 */
934 #ifndef	BIT8
935 		case CTRL('d') | QUOTE:
936 #else
937 btrp:
938 #endif
939 			*gcursor = 0;
940 			cp = vpastwh(genbuf);
941 			c = whitecnt(genbuf);
942 			if (ch == CTRL('t')) {
943 				/*
944 				 * ^t just generates new indent replacing
945 				 * current white space rounded up to soft
946 				 * tab stop increment.
947 				 */
948 				if (cp != gcursor)
949 					/*
950 					 * BUG:		Don't hack ^T except
951 					 *		right after initial
952 					 *		white space.
953 					 */
954 					continue;
955 				cp = genindent(iwhite = backtab(c + value(SHIFTWIDTH) + 1));
956 				ogcursor = cp;
957 				goto vbackup;
958 			}
959 			/*
960 			 * ^D works only if we are at the (end of) the
961 			 * generated autoindent.  We count the ^D for repeat
962 			 * purposes.
963 			 */
964 			if (c == iwhite && c != 0)
965 				if (cp == gcursor) {
966 					iwhite = backtab(c);
967 					CDCNT++;
968 					ogcursor = cp = genindent(iwhite);
969 					goto vbackup;
970 				} else if (&cp[1] == gcursor &&
971 				    (*cp == '^' || *cp == '0')) {
972 					/*
973 					 * ^^D moves to margin, then back
974 					 * to current indent on next line.
975 					 *
976 					 * 0^D moves to margin and then
977 					 * stays there.
978 					 */
979 					HADZERO = *cp == '0';
980 					ogcursor = cp = genbuf;
981 					HADUP = 1 - HADZERO;
982 					CDCNT = 1;
983 					endim();
984 					back1();
985 					vputchar(' ');
986 					goto vbackup;
987 				}
988 			if (vglobp && vglobp - iglobp >= 2 &&
989 			    (vglobp[-2] == '^' || vglobp[-2] == '0')
990 			    && gcursor == ogcursor + 1)
991 				goto bakchar;
992 			continue;
993 
994 		default:
995 			/*
996 			 * Possibly discard control inputs.
997 			 */
998 			if (!vglobp && junk(c)) {
999 				beep();
1000 				continue;
1001 			}
1002 def:
1003 			if (!backsl) {
1004 				/* int cnt; */
1005 				putchar(c);
1006 				flush();
1007 			}
1008 			if (gcursor > &genbuf[LBSIZE - 2])
1009 				error(catgets(catd, 1, 235, "Line too long"));
1010 			gappend(c & TRIM);
1011 			vcsync();
1012 			if (value(SHOWMATCH) && !iglobp)
1013 				if (c == ')' || c == '}')
1014 					lsmatch(gcursor);
1015 			continue;
1016 		}
1017 	}
1018 vadone:
1019 	*gcursor = 0;
1020 	if (Outchar != termchar)
1021 		Outchar = OO;
1022 	endim();
1023 	return (gcursor);
1024 }
1025 
1026 char	*vsplitpt;
1027 
1028 /*
1029  * Append the line in buffer at lp
1030  * to the buffer after dot.
1031  */
1032 void
vdoappend(char * lp)1033 vdoappend(char *lp)
1034 {
1035 	register int oing = inglobal;
1036 
1037 	vsplitpt = lp;
1038 	inglobal = 1;
1039 	ignore(append(vgetsplit, dot));
1040 	inglobal = oing;
1041 }
1042 
1043 /*
1044  * Subroutine for vdoappend to pass to append.
1045  */
1046 int
vgetsplit(void)1047 vgetsplit(void)
1048 {
1049 
1050 	if (vsplitpt == 0)
1051 		return (EOF);
1052 	strcLIN(vsplitpt);
1053 	vsplitpt = 0;
1054 	return (0);
1055 }
1056 
1057 /*
1058  * Vmaxrep determines the maximum repetitition factor
1059  * allowed that will yield total line length less than
1060  * LBSIZE characters and also does hacks for the R command.
1061  */
1062 int
vmaxrep(int ch,register int cnt)1063 vmaxrep(int ch, register int cnt)
1064 {
1065 	register int len, replen;
1066 
1067 	if (cnt > LBSIZE - 2)
1068 		cnt = LBSIZE - 2;
1069 	replen = strlen(genbuf);
1070 	if (ch == 'R') {
1071 		len = strlen(cursor);
1072 		if (replen < len)
1073 			len = replen;
1074 #ifdef	MB
1075 		if (mb_cur_max > 1) {
1076 			char	*cp, *gp;
1077 			int	c, g;
1078 			for (gp = genbuf, g = 0; *gp; g++)
1079 				gp += wskipright(genbuf, gp);
1080 			for (cp = cursor, c = 0; c < g; c++)
1081 				cp += wskipright(cursor, cp);
1082 			CP(cursor, cp);
1083 		} else
1084 #endif	/* MB */
1085 			CP(cursor, cursor + len);
1086 		vUD2 += len;
1087 	}
1088 	len = strlen(linebuf);
1089 	if (len + cnt * replen <= LBSIZE - 2)
1090 		return (cnt);
1091 	cnt = (LBSIZE - 2 - len) / replen;
1092 	if (cnt == 0) {
1093 		vsave();
1094 		error(catgets(catd, 1, 236, "Line too long"));
1095 	}
1096 	return (cnt);
1097 }
1098