xref: /titanic_44/usr/src/cmd/vi/port/ex_vops2.c (revision b7f45089ccbe01bab3d7c7377b49d80d2ae18a69)
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 /*
23  * Copyright 1997 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 
31 /* Copyright (c) 1981 Regents of the University of California */
32 
33 #pragma ident	"%Z%%M%	%I%	%E% SMI"
34 
35 #include "ex.h"
36 #include "ex_tty.h"
37 #include "ex_vis.h"
38 #ifndef PRESUNEUC
39 #include <wctype.h>
40 /* Undef putchar/getchar if they're defined. */
41 #ifdef putchar
42 #	undef putchar
43 #endif
44 #ifdef getchar
45 #	undef getchar
46 #endif
47 #endif /* PRESUNEUC */
48 
49 /*
50  * Low level routines for operations sequences,
51  * and mostly, insert mode (and a subroutine
52  * to read an input line, including in the echo area.)
53  */
54 extern unsigned char	*vUA1, *vUA2;		/* extern; also in ex_vops.c */
55 extern unsigned char	*vUD1, *vUD2;		/* extern; also in ex_vops.c */
56 
57 /*
58  * Obleeperate characters in hardcopy
59  * open with \'s.
60  */
61 bleep(i, cp)
62 	register int i;
63 	unsigned char *cp;
64 {
65 
66 	i -= lcolumn(nextchr(cp));
67 	do
68 		putchar('\\' | QUOTE);
69 	while (--i >= 0);
70 	rubble = 1;
71 }
72 
73 /*
74  * Common code for middle part of delete
75  * and change operating on parts of lines.
76  */
77 vdcMID()
78 {
79 	register unsigned char *cp;
80 
81 	squish();
82 	setLAST();
83 	if (FIXUNDO)
84 		vundkind = VCHNG, CP(vutmp, linebuf);
85 	if (wcursor < cursor)
86 		cp = wcursor, wcursor = cursor, cursor = cp;
87 	vUD1 = vUA1 = vUA2 = cursor; vUD2 = wcursor;
88 	return (lcolumn(wcursor));
89 }
90 
91 /*
92  * Take text from linebuf and stick it
93  * in the VBSIZE buffer BUF.  Used to save
94  * deleted text of part of line.
95  */
96 takeout(BUF)
97 	unsigned char *BUF;
98 {
99 	register unsigned char *cp;
100 
101 	if (wcursor < linebuf)
102 		wcursor = linebuf;
103 	if (cursor == wcursor) {
104 		beep();
105 		return;
106 	}
107 	if (wcursor < cursor) {
108 		cp = wcursor;
109 		wcursor = cursor;
110 		cursor = cp;
111 	}
112 	setBUF(BUF);
113 	if ((unsigned char)BUF[128] == 0200)
114 		beep();
115 }
116 
117 /*
118  * Are we at the end of the printed representation of the
119  * line?  Used internally in hardcopy open.
120  */
121 ateopr()
122 {
123 	register wchar_t i, c;
124 	register wchar_t *cp = vtube[destline] + destcol;
125 
126 	for (i = WCOLS - destcol; i > 0; i--) {
127 		c = *cp++;
128 		if (c == 0) {
129 			/*
130 			 * Optimization to consider returning early, saving
131 			 * CPU time.  We have to make a special check that
132 			 * we aren't missing a mode indicator.
133 			 */
134 			if (destline == WECHO && destcol < WCOLS-11 && vtube[WECHO][WCOLS-20])
135 				return 0;
136 			return (1);
137 		}
138 		if (c != ' ' && (c & QUOTE) == 0)
139 			return (0);
140 	}
141 	return (1);
142 }
143 
144 /*
145  * Append.
146  *
147  * This routine handles the top level append, doing work
148  * as each new line comes in, and arranging repeatability.
149  * It also handles append with repeat counts, and calculation
150  * of autoindents for new lines.
151  */
152 bool	vaifirst;
153 bool	gobbled;
154 unsigned char	*ogcursor;
155 
156 static int 	INSCDCNT; /* number of ^D's (backtabs) in insertion buffer */
157 
158 static int 	inscdcnt; /*
159 			   * count of ^D's (backtabs) not seen yet when doing
160 		 	   * repeat of insertion
161 			   */
162 
163 vappend(ch, cnt, indent)
164 	int ch;		/* char --> int */
165 	int cnt, indent;
166 {
167 	register int i;
168 	register unsigned char *gcursor;
169 	bool escape;
170 	int repcnt, savedoomed;
171 	short oldhold = hold;
172 
173 	/*
174 	 * Before a move in hardopen when the line is dirty
175 	 * or we are in the middle of the printed representation,
176 	 * we retype the line to the left of the cursor so the
177 	 * insert looks clean.
178 	 */
179 
180 	if (ch != 'o' && state == HARDOPEN && (rubble || !ateopr())) {
181 		rubble = 1;
182 		gcursor = cursor;
183 		i = *gcursor;
184 		*gcursor = ' ';
185 		wcursor = gcursor;
186 		vmove();
187 		*gcursor = i;
188 	}
189 	vaifirst = indent == 0;
190 
191 	/*
192 	 * Handle replace character by (eventually)
193 	 * limiting the number of input characters allowed
194 	 * in the vgetline routine.
195 	 */
196 	if (ch == 'r')
197 		repcnt = 2;
198 	else
199 		repcnt = 0;
200 
201 	/*
202 	 * If an autoindent is specified, then
203 	 * generate a mixture of blanks to tabs to implement
204 	 * it and place the cursor after the indent.
205 	 * Text read by the vgetline routine will be placed in genbuf,
206 	 * so the indent is generated there.
207 	 */
208 	if (value(vi_AUTOINDENT) && indent != 0) {
209 		unsigned char x;
210 		gcursor = genindent(indent);
211 		*gcursor = 0;
212 		vgotoCL(nqcolumn(lastchr(linebuf, cursor), genbuf));
213 	} else {
214 		gcursor = genbuf;
215 		*gcursor = 0;
216 		if (ch == 'o')
217 			vfixcurs();
218 	}
219 
220 	/*
221 	 * Prepare for undo.  Pointers delimit inserted portion of line.
222 	 */
223 	vUA1 = vUA2 = cursor;
224 
225 	/*
226 	 * If we are not in a repeated command and a ^@ comes in
227 	 * then this means the previous inserted text.
228 	 * If there is none or it was too long to be saved,
229 	 * then beep() and also arrange to undo any damage done
230 	 * so far (e.g. if we are a change.)
231 	 */
232 	switch (ch) {
233 	case 'r':
234 		break;
235 	case 'a':
236 		/*
237 		 * TRANSLATION_NOTE
238 		 *	"A" is a terse mode message corresponding to
239 		 *	"APPEND MODE".
240 		 *	Translated message of "A" must be 1 character (not byte).
241 		 *	Or, just leave it.
242 		 */
243 		if (value(vi_TERSE)) {
244 			vshowmode(gettext("A"));
245 		} else {
246 			vshowmode(gettext("APPEND MODE"));
247 		}
248 		break;
249 	case 's':
250 		/*
251 		 * TRANSLATION_NOTE
252 		 *	"S" is a terse mode message corresponding to
253 		 *	"SUBSTITUTE MODE".
254 		 *	Translated message of "S" must be 1 character (not byte).
255 		 *	Or, just leave it.
256 		 */
257 		if (value(vi_TERSE)) {
258 			vshowmode(gettext("S"));
259 		} else {
260 			vshowmode(gettext("SUBSTITUTE MODE"));
261 		}
262 		break;
263 	case 'c':
264 		/*
265 		 * TRANSLATION_NOTE
266 		 *	"C" is a terse mode message corresponding to
267 		 *	"CHANGE MODE".
268 		 *	Translated message of "C" must be 1 character (not byte).
269 		 *	Or, just leave it.
270 		 */
271 		if (value(vi_TERSE)) {
272 			vshowmode(gettext("C"));
273 		} else {
274 			vshowmode(gettext("CHANGE MODE"));
275 		}
276 		break;
277 	case 'R':
278 		/*
279 		 * TRANSLATION_NOTE
280 		 *	"R" is a terse mode message corresponding to
281 		 *	"REPLACE MODE".
282 		 *	Translated message of "R" must be 1 character (not byte).
283 		 *	Or, just leave it.
284 		 */
285 		if (value(vi_TERSE)) {
286 			vshowmode(gettext("R"));
287 		} else {
288 			vshowmode(gettext("REPLACE MODE"));
289 		}
290 		break;
291 	case 'o':
292 		/*
293 		 * TRANSLATION_NOTE
294 		 *	"O" is a terse mode message corresponding to
295 		 *	"OPEN MODE".
296 		 *	Translated message of "O" must be 1 character (not byte).
297 		 *	Or, just leave it.
298 		 */
299 		if (value(vi_TERSE)) {
300 			vshowmode(gettext("O"));
301 		} else {
302 			vshowmode(gettext("OPEN MODE"));
303 		}
304 		break;
305 	case 'i':
306 		/*
307 		 * TRANSLATION_NOTE
308 		 *	"I" is a terse mode message corresponding to
309 		 *	"INSERT MODE" and the following "INPUT MODE".
310 		 *	Translated message of "I" must be 1 character (not byte).
311 		 *	Or, just leave it.
312 		 */
313 		if (value(vi_TERSE)) {
314 			vshowmode(gettext("I"));
315 		} else {
316 			vshowmode(gettext("INSERT MODE"));
317 		}
318 		break;
319 	default:
320 		/*
321 		 * TRANSLATION_NOTE
322 		 *	"I" is a terse mode message corresponding to
323 		 *	"INPUT MODE" and the previous "INSERT MODE".
324 		 *	Translated message of "I" must be 1 character (not byte).
325 		 *	Or, just leave it.
326 		 */
327 		if (value(vi_TERSE)) {
328 			vshowmode(gettext("I"));
329 		} else {
330 			vshowmode(gettext("INPUT MODE"));
331 		}
332 	}
333 	ixlatctl(1);
334 	if ((vglobp && *vglobp == 0) || peekbr()) {
335 		if (INS[128] == 0200) {
336 			beep();
337 			if (!splitw)
338 				ungetkey('u');
339 			doomed = 0;
340 			hold = oldhold;
341 			return;
342 		}
343 		/*
344 		 * Unread input from INS.
345 		 * An escape will be generated at end of string.
346 		 * Hold off n^^2 type update on dumb terminals.
347 		 */
348 		vglobp = INS;
349 		inscdcnt = INSCDCNT;
350 		hold |= HOLDQIK;
351 	} else if (vglobp == 0) {
352 		/*
353 		 * Not a repeated command, get
354 		 * a new inserted text for repeat.
355 		 */
356 		INS[0] = 0;
357 		INS[128] = 0;
358 		INSCDCNT = 0;
359 	}
360 
361 	/*
362 	 * For wrapmargin to hack away second space after a '.'
363 	 * when the first space caused a line break we keep
364 	 * track that this happened in gobblebl, which says
365 	 * to gobble up a blank silently.
366 	 */
367 	gobblebl = 0;
368 
369 	/*
370 	 * Text gathering loop.
371 	 * New text goes into genbuf starting at gcursor.
372 	 * cursor preserves place in linebuf where text will eventually go.
373 	 */
374 	if (*cursor == 0 || state == CRTOPEN)
375 		hold |= HOLDROL;
376 	for (;;) {
377 		if (ch == 'r' && repcnt == 0)
378 			escape = 0;
379 		else {
380 			ixlatctl(1);
381 			gcursor = vgetline(repcnt, gcursor, &escape, ch);
382 			ixlatctl(0);
383 
384 			/*
385 			 * After an append, stick information
386 			 * about the ^D's and ^^D's and 0^D's in
387 			 * the repeated text buffer so repeated
388 			 * inserts of stuff indented with ^D as backtab's
389 			 * can work.
390 			 */
391 			if (HADUP)
392 				addtext("^");
393 			else if (HADZERO)
394 				addtext("0");
395 			if(!vglobp)
396 				INSCDCNT = CDCNT;
397 			while (CDCNT > 0) {
398 				addtext("\004");
399 				CDCNT--;
400 			}
401 			if (gobbled)
402 				addtext(" ");
403 			addtext(ogcursor);
404 		}
405 		repcnt = 0;
406 
407 		/*
408 		 * Smash the generated and preexisting indents together
409 		 * and generate one cleanly made out of tabs and spaces
410 		 * if we are using autoindent.
411 		 */
412 		if (!vaifirst && value(vi_AUTOINDENT)) {
413 			i = fixindent(indent);
414 			if (!HADUP)
415 				indent = i;
416 			gcursor = strend(genbuf);
417 		}
418 
419 		/*
420 		 * Limit the repetition count based on maximum
421 		 * possible line length; do output implied
422 		 * by further count (> 1) and cons up the new line
423 		 * in linebuf.
424 		 */
425 		cnt = vmaxrep(ch, cnt);
426 		CP(gcursor + 1, cursor);
427 		do {
428 			CP(cursor, genbuf);
429 			if (cnt > 1) {
430 				int oldhold = hold;
431 
432 				Outchar = vinschar;
433 				hold |= HOLDQIK;
434 				printf("%s", genbuf);
435 				hold = oldhold;
436 				Outchar = vputchar;
437 			}
438 			cursor += gcursor - genbuf;
439 		} while (--cnt > 0);
440 		endim();
441 		vUA2 = cursor;
442 		if (escape != '\n')
443 			CP(cursor, gcursor + 1);
444 
445 		/*
446 		 * If doomed characters remain, clobber them,
447 		 * and reopen the line to get the display exact.
448 		 */
449 		if (state != HARDOPEN) {
450 			DEPTH(vcline) = 0;
451 			savedoomed = doomed;
452 			if (doomed > 0) {
453 				register int cind = cindent();
454 
455 				physdc(cind, cind + doomed);
456 				doomed = 0;
457 			}
458 			if(MB_CUR_MAX > 1)
459 				rewrite = _ON;
460 			i = vreopen(LINE(vcline), lineDOT(), vcline);
461 			if(MB_CUR_MAX > 1)
462 				rewrite = _OFF;
463 #ifdef TRACE
464 			if (trace)
465 				fprintf(trace, "restoring doomed from %d to %d\n", doomed, savedoomed);
466 #endif
467 			if (ch == 'R')
468 				doomed = savedoomed;
469 		}
470 
471 		/*
472 		 * All done unless we are continuing on to another line.
473 		 */
474 		if (escape != '\n') {
475 			vshowmode("");
476 			break;
477 		}
478 
479 		/*
480 		 * Set up for the new line.
481 		 * First save the current line, then construct a new
482 		 * first image for the continuation line consisting
483 		 * of any new autoindent plus the pushed ahead text.
484 		 */
485 		killU();
486 		addtext(gobblebl ? " " : "\n");
487 		vsave();
488 		cnt = 1;
489 		if (value(vi_AUTOINDENT)) {
490 			if (value(vi_LISP))
491 				indent = lindent(dot + 1);
492 			else
493 			     if (!HADUP && vaifirst)
494 				indent = whitecnt(linebuf);
495 			vaifirst = 0;
496 			strcLIN(vpastwh(gcursor + 1));
497 			gcursor = genindent(indent);
498 			*gcursor = 0;
499 			if (gcursor + strlen(linebuf) > &genbuf[LBSIZE - 2])
500 				gcursor = genbuf;
501 			CP(gcursor, linebuf);
502 		} else {
503 			CP(genbuf, gcursor + 1);
504 			gcursor = genbuf;
505 		}
506 
507 		/*
508 		 * If we started out as a single line operation and are now
509 		 * turning into a multi-line change, then we had better yank
510 		 * out dot before it changes so that undo will work
511 		 * correctly later.
512 		 */
513 		if (FIXUNDO && vundkind == VCHNG) {
514 			vremote(1, yank, 0);
515 			undap1--;
516 		}
517 
518 		/*
519 		 * Now do the append of the new line in the buffer,
520 		 * and update the display.  If slowopen
521 		 * we don't do very much.
522 		 */
523 		vdoappend(genbuf);
524 		vundkind = VMANYINS;
525 		vcline++;
526 		if (state != VISUAL)
527 			vshow(dot, NOLINE);
528 		else {
529 			i += LINE(vcline - 1);
530 			vopen(dot, i);
531 			if (value(vi_SLOWOPEN))
532 				vscrap();
533 			else
534 				vsync1(LINE(vcline));
535 		}
536 		switch (ch) {
537 		case 'r':
538 			break;
539 		case 'a':
540 			if (value(vi_TERSE)) {
541 				vshowmode(gettext("A"));
542 			} else {
543 				vshowmode(gettext("APPEND MODE"));
544 			}
545 			break;
546 		case 's':
547 			if (value(vi_TERSE)) {
548 				vshowmode(gettext("S"));
549 			} else {
550 				vshowmode(gettext("SUBSTITUTE MODE"));
551 			}
552 			break;
553 		case 'c':
554 			if (value(vi_TERSE)) {
555 				vshowmode(gettext("C"));
556 			} else {
557 				vshowmode(gettext("CHANGE MODE"));
558 			}
559 			break;
560 		case 'R':
561 			if (value(vi_TERSE)) {
562 				vshowmode(gettext("R"));
563 			} else {
564 				vshowmode(gettext("REPLACE MODE"));
565 			}
566 			break;
567 		case 'i':
568 			if (value(vi_TERSE)) {
569 				vshowmode(gettext("I"));
570 			} else {
571 				vshowmode(gettext("INSERT MODE"));
572 			}
573 			break;
574 		case 'o':
575 			if (value(vi_TERSE)) {
576 				vshowmode(gettext("O"));
577 			} else {
578 				vshowmode(gettext("OPEN MODE"));
579 			}
580 			break;
581 		default:
582 			if (value(vi_TERSE)) {
583 				vshowmode(gettext("I"));
584 			} else {
585 				vshowmode(gettext("INPUT MODE"));
586 			}
587 		}
588 		strcLIN(gcursor);
589 		*gcursor = 0;
590 		cursor = linebuf;
591 		vgotoCL(nqcolumn(cursor - 1, genbuf));
592 	}
593 
594 	/*
595 	 * All done with insertion, position the cursor
596 	 * and sync the screen.
597 	 */
598 	hold = oldhold;
599 	if (cursor > linebuf)
600 		cursor = lastchr(linebuf, cursor);
601 	if (state != HARDOPEN)
602 		vsyncCL();
603 	else if (cursor > linebuf)
604 		back1();
605 	doomed = 0;
606 	wcursor = cursor;
607 	vmove();
608 }
609 
610 /*
611  * Subroutine for vgetline to back up a single character position,
612  * backwards around end of lines (vgoto can't hack columns which are
613  * less than 0 in general).
614  */
615 back1()
616 {
617 
618 	vgoto(destline - 1, WCOLS + destcol - 1);
619 }
620 
621 /*
622  * Get a line into genbuf after gcursor.
623  * Cnt limits the number of input characters
624  * accepted and is used for handling the replace
625  * single character command.  Aescaped is the location
626  * where we stick a termination indicator (whether we
627  * ended with an ESCAPE or a newline/return.
628  *
629  * We do erase-kill type processing here and also
630  * are careful about the way we do this so that it is
631  * repeatable.  (I.e. so that your kill doesn't happen,
632  * when you repeat an insert if it was escaped with \ the
633  * first time you did it.  commch is the command character
634  * involved, including the prompt for readline.
635  */
636 unsigned char *
637 vgetline(cnt, gcursor, aescaped, commch)
638 	int cnt;
639 	register unsigned char *gcursor;
640 	bool *aescaped;
641 	unsigned char commch;
642 {
643 	register int c, ch;
644 	register unsigned char *cp, *pcp;
645 	int x, y, iwhite, backsl=0;
646 	unsigned char *iglobp;
647 	int (*OO)() = Outchar;
648 	int length, width;
649 	unsigned char multic[MULTI_BYTE_MAX+1];
650 	wchar_t wchar = 0;
651 	unsigned char	*p;
652 	int	len;
653 
654 
655 	/*
656 	 * Clear the output state and counters
657 	 * for autoindent backwards motion (counts of ^D, etc.)
658 	 * Remember how much white space at beginning of line so
659 	 * as not to allow backspace over autoindent.
660 	 */
661 
662 	*aescaped = 0;
663 	ogcursor = gcursor;
664 	flusho();
665 	CDCNT = 0;
666 	HADUP = 0;
667 	HADZERO = 0;
668 	gobbled = 0;
669 	iwhite = whitecnt(genbuf);
670 	iglobp = vglobp;
671 
672 	/*
673 	 * Clear abbreviation recursive-use count
674 	 */
675 	abbrepcnt = 0;
676 	/*
677 	 * Carefully avoid using vinschar in the echo area.
678 	 */
679 	if (splitw)
680 		Outchar = vputchar;
681 	else {
682 		Outchar = vinschar;
683 		vprepins();
684 	}
685 	for (;;) {
686 		length = 0;
687 		backsl = 0;
688 		if (gobblebl)
689 			gobblebl--;
690 		if (cnt != 0) {
691 			cnt--;
692 			if (cnt == 0)
693 				goto vadone;
694 		}
695 		c = getkey();
696 		if (c != ATTN)
697 			c &= 0377;
698 		ch = c;
699 		maphopcnt = 0;
700 		if (vglobp == 0 && Peekkey == 0 && commch != 'r')
701 			while ((ch = map(c, immacs, commch)) != c) {
702 				c = ch;
703 				if (!value(vi_REMAP))
704 					break;
705 				if (++maphopcnt > 256)
706 					error(gettext("Infinite macro loop"));
707 			}
708 		if (!iglobp) {
709 
710 			/*
711 			 * Erase-kill type processing.
712 			 * Only happens if we were not reading
713 			 * from untyped input when we started.
714 			 * Map users erase to ^H, kill to -1 for switch.
715 			 */
716 			if (c == tty.c_cc[VERASE])
717 				c = CTRL('h');
718 			else if (c == tty.c_cc[VKILL])
719 				c = -1;
720 			switch (c) {
721 
722 			/*
723 			 * ^?		Interrupt drops you back to visual
724 			 *		command mode with an unread interrupt
725 			 *		still in the input buffer.
726 			 *
727 			 * ^\		Quit does the same as interrupt.
728 			 *		If you are a ex command rather than
729 			 *		a vi command this will drop you
730 			 *		back to command mode for sure.
731 			 */
732 			case ATTN:
733 			case QUIT:
734 				ungetkey(c);
735 				goto vadone;
736 
737 			/*
738 			 * ^H		Backs up a character in the input.
739 			 *
740 			 * BUG:		Can't back around line boundaries.
741 			 *		This is hard because stuff has
742 			 *		already been saved for repeat.
743 			 */
744 			case CTRL('h'):
745 bakchar:
746 				cp = lastchr(ogcursor, gcursor);
747 				if (cp < ogcursor) {
748 					if (splitw) {
749 						/*
750 						 * Backspacing over readecho
751 						 * prompt. Pretend delete but
752 						 * don't beep.
753 						 */
754 						ungetkey(c);
755 						goto vadone;
756 					}
757 					beep();
758 					continue;
759 				}
760 				goto vbackup;
761 
762 			/*
763 			 * ^W		Back up a white/non-white word.
764 			 */
765 			case CTRL('w'):
766 				wdkind = 1;
767 				for (cp = gcursor; cp > ogcursor && isspace(cp[-1]); cp--)
768 					continue;
769 				pcp = lastchr(ogcursor, cp);
770 				for (c = wordch(pcp);
771 				    cp > ogcursor && wordof(c, pcp); cp = pcp, pcp = lastchr(ogcursor, cp))
772 					continue;
773 				goto vbackup;
774 
775 			/*
776 			 * users kill	Kill input on this line, back to
777 			 *		the autoindent.
778 			 */
779 			case -1:
780 				cp = ogcursor;
781 vbackup:
782 				if (cp == gcursor) {
783 					beep();
784 					continue;
785 				}
786 				endim();
787 				*cp = 0;
788 				c = cindent();
789 				vgotoCL(nqcolumn(lastchr(linebuf, cursor), genbuf));
790 
791 				if (doomed >= 0)
792 					doomed += c - cindent();
793 				gcursor = cp;
794 				continue;
795 
796 			/*
797 			 * \		Followed by erase or kill
798 			 *		maps to just the erase or kill.
799 			 */
800 			case '\\':
801 				x = destcol, y = destline;
802 				putchar('\\');
803 				vcsync();
804 				c = getkey();
805 				if (c == tty.c_cc[VERASE]
806 				    || c == tty.c_cc[VKILL])
807 				{
808 					vgoto(y, x);
809 					if (doomed >= 0)
810 						doomed++;
811 					multic[0] = wchar = c;
812 					length = 1;
813 					goto def;
814 				}
815 				ungetkey(c), c = '\\';
816 				backsl = 1;
817 				break;
818 
819 			/*
820 			 * ^Q		Super quote following character
821 			 *		Only ^@ is verboten (trapped at
822 			 *		a lower level) and \n forces a line
823 			 *		split so doesn't really go in.
824 			 *
825 			 * ^V		Synonym for ^Q
826 			 */
827 			case CTRL('q'):
828 			case CTRL('v'):
829 				x = destcol, y = destline;
830 				putchar('^');
831 				vgoto(y, x);
832 				c = getkey();
833 #ifdef USG
834 				if (c == ATTN)
835 					c = tty.c_cc[VINTR];
836 #endif
837 				if (c != NL) {
838 					if (doomed >= 0)
839 						doomed++;
840 					multic[0] = wchar = c;
841 					length = 1;
842 					goto def;
843 				}
844 				break;
845 			}
846 		}
847 
848 		/*
849 		 * If we get a blank not in the echo area
850 		 * consider splitting the window in the wrapmargin.
851 		 */
852 		if(!backsl) {
853 			ungetkey(c);
854 			if((length = _mbftowc((char *)multic, &wchar, getkey, &Peekkey)) <= 0) {
855 				beep();
856 				continue;
857 			}
858 		} else {
859 			length = 1;
860 			multic[0] = '\\';
861 		}
862 
863 		if (c != NL && !splitw) {
864 			if (c == ' ' && gobblebl) {
865 				gobbled = 1;
866 				continue;
867 			}
868 			if ((width = wcwidth(wchar)) <= 0)
869 				width = (wchar <= 0177 ? 1 : 4);
870 			if (value(vi_WRAPMARGIN) &&
871 				(outcol + width - 1 >= OCOLUMNS - value(vi_WRAPMARGIN) ||
872 				 backsl && outcol==0) &&
873 				commch != 'r') {
874 				/*
875 				 * At end of word and hit wrapmargin.
876 				 * Move the word to next line and keep going.
877 				 */
878 				unsigned char *wp;
879 				int bytelength;
880 #ifndef PRESUNEUC
881 				unsigned char *tgcursor;
882 				wchar_t wc1, wc2;
883 				tgcursor = gcursor;
884 #endif /* PRESUNEUC */
885 				wdkind = 1;
886 				strncpy(gcursor, multic, length);
887 				gcursor += length;
888 				if (backsl) {
889 #ifdef PRESUNEUC
890 					if((length = mbftowc((char *)multic, &wchar, getkey, &Peekkey)) <= 0) {
891 #else
892 					if((length = _mbftowc((char *)multic, &wchar, getkey, &Peekkey)) <= 0) {
893 #endif /* PRESUNEUC */
894 						beep();
895 						continue;
896 					}
897 					strncpy(gcursor, multic, length);
898 					gcursor += length;
899 				}
900 				*gcursor = 0;
901 				/*
902 				 * Find end of previous word if we are past it.
903 				 */
904 				for (cp=gcursor; cp>ogcursor && isspace(cp[-1]); cp--)
905 					;
906 #ifdef PRESUNEUC
907 				/* find screen width of previous word */
908 				width = 0;
909 				for(wp = cp; *wp; )
910 #else
911 				/* count screen width of pending characters */
912 				width = 0;
913 				for(wp = tgcursor; wp < cp;)
914 #endif /* PRESUNEUC */
915 					if((bytelength = mbtowc(&wchar, (char *)wp, MULTI_BYTE_MAX)) < 0) {
916 						width+=4;
917 						wp++;
918 					} else {
919 						int curwidth = wcwidth(wchar);
920 						if(curwidth <= 0)
921 							width += (*wp < 0200 ? 2 : 4);
922 						else
923 							width += curwidth;
924 						wp += bytelength;
925 					}
926 
927 #ifdef PRESUNEUC
928 				if (outcol+(backsl?OCOLUMNS:0) - width >= OCOLUMNS - value(vi_WRAPMARGIN)) {
929 #else
930 				if (outcol+(backsl?OCOLUMNS:0) + width -1 >= OCOLUMNS - value(vi_WRAPMARGIN)) {
931 #endif /* PRESUNEUC */
932 					/*
933 					 * Find beginning of previous word.
934 					 */
935 #ifdef PRESUNEUC
936 					for (; cp>ogcursor && !isspace(cp[-1]); cp--)
937 						;
938 #else
939 					wc1 = wc2 = 0;
940 					while (cp>ogcursor) {
941 						if (isspace(cp[-1])) {
942 							break;
943 						}
944 						if (!multibyte) {
945 							cp--;
946 							continue;
947 						}
948 						wp = (unsigned char *)(cp -
949 							MB_CUR_MAX);
950 						if (wp < ogcursor)
951 							wp = ogcursor;
952 						while (cp > wp) {
953 /* 7tabs */if (wc2) {
954 /* 7tabs */	if ((bytelength = mbtowc(&wc1, (char *)wp, cp-wp)) != cp-wp) {
955 /* 7tabs */		wp++;
956 /* 7tabs */		wc1 = 0;
957 /* 7tabs */		continue;
958 /* 7tabs */	}
959 /* 7tabs */} else {
960 /* 7tabs */	if ((bytelength = mbtowc(&wc2, (char *)wp, cp-wp)) != cp-wp) {
961 /* 7tabs */		wp++;
962 /* 7tabs */		wc2 = 0;
963 /* 7tabs */		continue;
964 /* 7tabs */	}
965 /* 7tabs */}
966 /* 7tabs */if (wc1) {
967 /* 7tabs */	if (wdbdg && (!iswascii(wc1) || !iswascii(wc2))) {
968 /* 7tabs */		if ((*wdbdg)(wc1, wc2, 2) < 5) {
969 /* 7tabs */			goto ws;
970 /* 7tabs */		}
971 /* 7tabs */	}
972 /* 7tabs */	wc2 = wc1;
973 /* 7tabs */	wc1 = 0;
974 /* 7tabs */	cp -= bytelength - 1;
975 /* 7tabs */	break;
976 /* 7tabs */} else {
977 /* 7tabs */	cp -= bytelength - 1;
978 /* 7tabs */	break;
979 /* 7tabs */}
980 						}
981 						cp--;
982 					}
983 ws:
984 #endif /* PRESUNEUC */
985 					if (cp <= ogcursor) {
986 						/*
987 						 * There is a single word that
988 						 * is too long to fit.  Just
989 						 * let it pass, but beep for
990 						 * each new letter to warn
991 						 * the luser.
992 						 */
993 						gcursor -= length;
994 						c = *gcursor;
995 						*gcursor = 0;
996 						beep();
997 						goto dontbreak;
998 					}
999 					/*
1000 					 * Save it for next line.
1001 					 */
1002 					macpush(cp, 0);
1003 #ifdef PRESUNEUC
1004 					cp--;
1005 #endif /* PRESUNEUC */
1006 				}
1007 				macpush("\n", 0);
1008 				/*
1009 				 * Erase white space before the word.
1010 				 */
1011 				while (cp > ogcursor && isspace(cp[-1]))
1012 					cp--;	/* skip blank */
1013 				gobblebl = 3;
1014 				goto vbackup;
1015 			}
1016 		dontbreak:;
1017 		}
1018 
1019 		/*
1020 		 * Word abbreviation mode.
1021 		 */
1022 		if (anyabbrs && gcursor > ogcursor && !wordch(multic) && wordch(lastchr(ogcursor, gcursor))) {
1023 				int wdtype, abno;
1024 
1025 				multic[length] = 0;
1026 				wdkind = 1;
1027 				cp = lastchr(ogcursor, gcursor);
1028 				pcp = lastchr(ogcursor, cp);
1029 				for (wdtype = wordch(pcp);
1030 				    cp > ogcursor && wordof(wdtype, pcp); cp = pcp, pcp = lastchr(ogcursor, pcp))
1031 					;
1032 				*gcursor = 0;
1033 				for (abno=0; abbrevs[abno].mapto; abno++) {
1034 					if (eq(cp, abbrevs[abno].cap)) {
1035 						if(abbrepcnt == 0) {
1036 							if(reccnt(abbrevs[abno].cap, abbrevs[abno].mapto))
1037 								abbrepcnt = 1;
1038 							macpush(multic, 0);
1039 							macpush(abbrevs[abno].mapto);
1040 							goto vbackup;
1041 						} else
1042 							abbrepcnt = 0;
1043 					}
1044 				}
1045 		}
1046 
1047 		switch (c) {
1048 
1049 		/*
1050 		 * ^M		Except in repeat maps to \n.
1051 		 */
1052 		case CR:
1053 			if (vglobp) {
1054 				multic[0] = wchar = c;
1055 				length = 1;
1056 				goto def;
1057 			}
1058 			c = '\n';
1059 			/* presto chango ... */
1060 
1061 		/*
1062 		 * \n		Start new line.
1063 		 */
1064 		case NL:
1065 			*aescaped = c;
1066 			goto vadone;
1067 
1068 		/*
1069 		 * escape	End insert unless repeat and more to repeat.
1070 		 */
1071 		case ESCAPE:
1072 			if (lastvgk) {
1073 				multic[0] = wchar = c;
1074 				length = 1;
1075 				goto def;
1076 			}
1077 			goto vadone;
1078 
1079 		/*
1080 		 * ^D		Backtab.
1081 		 * ^T		Software forward tab.
1082 		 *
1083 		 *		Unless in repeat where this means these
1084 		 *		were superquoted in.
1085 		 */
1086 		case CTRL('t'):
1087 			if (vglobp) {
1088 				multic[0] = wchar = c;
1089 				length = 1;
1090 				goto def;
1091 			}
1092 			/* fall into ... */
1093 
1094 			*gcursor = 0;
1095 			cp = vpastwh(genbuf);
1096 			c = whitecnt(genbuf);
1097 			if (ch == CTRL('t')) {
1098 				/*
1099 				 * ^t just generates new indent replacing
1100 				 * current white space rounded up to soft
1101 				 * tab stop increment.
1102 				 */
1103 				if (cp != gcursor)
1104 					/*
1105 					 * BUG:		Don't hack ^T except
1106 					 *		right after initial
1107 					 *		white space.
1108 					 */
1109 					continue;
1110 				cp = genindent(iwhite = backtab(c + value(vi_SHIFTWIDTH) + 1));
1111 				ogcursor = cp;
1112 				goto vbackup;
1113 			}
1114 			/*
1115 			 * ^D works only if we are at the (end of) the
1116 			 * generated autoindent.  We count the ^D for repeat
1117 			 * purposes.
1118 			 */
1119 		case CTRL('d'):
1120 			/* check if ^d was superquoted in */
1121 			if(vglobp && inscdcnt <= 0) {
1122 				multic[0] = wchar = c;
1123 				length = 1;
1124 				goto def;
1125 			}
1126 			if(vglobp)
1127 				inscdcnt--;
1128 			*gcursor = 0;
1129 			cp = vpastwh(genbuf);
1130 			c = whitecnt(genbuf);
1131 			if (c == iwhite && c != 0)
1132 				if (cp == gcursor) {
1133 					iwhite = backtab(c);
1134 					CDCNT++;
1135 					ogcursor = cp = genindent(iwhite);
1136 					goto vbackup;
1137 				} else if (&cp[1] == gcursor &&
1138 				    (*cp == '^' || *cp == '0')) {
1139 					/*
1140 					 * ^^D moves to margin, then back
1141 					 * to current indent on next line.
1142 					 *
1143 					 * 0^D moves to margin and then
1144 					 * stays there.
1145 					 */
1146 					HADZERO = *cp == '0';
1147 					ogcursor = cp = genbuf;
1148 					HADUP = 1 - HADZERO;
1149 					CDCNT = 1;
1150 					endim();
1151 					back1();
1152 					vputchar(' ');
1153 					goto vbackup;
1154 				}
1155 
1156 			if (vglobp && vglobp - iglobp >= 2) {
1157 				if ((p = vglobp - MB_CUR_MAX) < iglobp)
1158 					p = iglobp;
1159 				for ( ; p < &vglobp[-2]; p += len) {
1160 					if ((len = mblen((char *)p, MB_CUR_MAX)) <= 0)
1161 						len = 1;
1162 				}
1163 				if ((p == &vglobp[-2]) &&
1164 			            (*p == '^' || *p == '0') &&
1165 			            gcursor == ogcursor + 1)
1166 					goto bakchar;
1167 			}
1168 			continue;
1169 
1170 		default:
1171 			/*
1172 			 * Possibly discard control inputs.
1173 			 */
1174 			if (!vglobp && junk(c)) {
1175 				beep();
1176 				continue;
1177 			}
1178 def:
1179 			if (!backsl) {
1180 				putchar(wchar);
1181 				flush();
1182 			}
1183 			if (gcursor + length - 1 > &genbuf[LBSIZE - 2])
1184 				error(gettext("Line too long"));
1185 			(void)strncpy(gcursor, multic, length);
1186 			gcursor += length;
1187 			vcsync();
1188 			if (value(vi_SHOWMATCH) && !iglobp)
1189 				if (c == ')' || c == '}')
1190 					lsmatch(gcursor);
1191 			continue;
1192 		}
1193 	}
1194 vadone:
1195 	*gcursor = 0;
1196 	if (Outchar != termchar)
1197 		Outchar = OO;
1198 	endim();
1199 	return (gcursor);
1200 }
1201 
1202 int	vgetsplit();
1203 unsigned char	*vsplitpt;
1204 
1205 /*
1206  * Append the line in buffer at lp
1207  * to the buffer after dot.
1208  */
1209 vdoappend(lp)
1210 	unsigned char *lp;
1211 {
1212 	register int oing = inglobal;
1213 
1214 	vsplitpt = lp;
1215 	inglobal = 1;
1216 	(void)append(vgetsplit, dot);
1217 	inglobal = oing;
1218 }
1219 
1220 /*
1221  * Subroutine for vdoappend to pass to append.
1222  */
1223 vgetsplit()
1224 {
1225 
1226 	if (vsplitpt == 0)
1227 		return (EOF);
1228 	strcLIN(vsplitpt);
1229 	vsplitpt = 0;
1230 	return (0);
1231 }
1232 
1233 /*
1234  * Vmaxrep determines the maximum repetition factor
1235  * allowed that will yield total line length less than
1236  * LBSIZE characters and also does hacks for the R command.
1237  */
1238 vmaxrep(ch, cnt)
1239 	unsigned char ch;
1240 	register int cnt;
1241 {
1242 	register int len;
1243 	unsigned char *cp;
1244 	int repcnt, oldcnt, replen;
1245 	if (cnt > LBSIZE - 2)
1246 		cnt = LBSIZE - 2;
1247 	if (ch == 'R') {
1248 		len = strlen(cursor);
1249 		oldcnt = 0;
1250 		for(cp = cursor; *cp; ) {
1251 			oldcnt++;
1252 			cp = nextchr(cp);
1253 		}
1254 		repcnt = 0;
1255 		for(cp = genbuf; *cp; ) {
1256 			repcnt++;
1257 			cp = nextchr(cp);
1258 		}
1259 		/*
1260 		 * if number of characters in replacement string
1261 		 * (repcnt) is less than number of characters following
1262 		 * cursor (oldcnt), find end of repcnt
1263 		 * characters after cursor
1264 		 */
1265 		if(repcnt < oldcnt) {
1266 			for(cp = cursor; repcnt > 0; repcnt--)
1267 				cp = nextchr(cp);
1268 			len = cp - cursor;
1269 		}
1270 		CP(cursor, cursor + len);
1271 		vUD2 += len;
1272 	}
1273 	len = strlen(linebuf);
1274 	replen = strlen(genbuf);
1275 	if (len + cnt * replen <= LBSIZE - 2)
1276 		return (cnt);
1277 	cnt = (LBSIZE - 2 - len) / replen;
1278 	if (cnt == 0) {
1279 		vsave();
1280 		error(gettext("Line too long"));
1281 	}
1282 	return (cnt);
1283 }
1284 
1285 /*
1286  * Determine how many occurrences of word 'CAP' are in 'MAPTO'.  To be
1287  * considered an occurrence there must be both a nonword-prefix, a
1288  * complete match of 'CAP' within 'MAPTO', and a nonword-suffix.
1289  * Note that the beginning and end of 'MAPTO' are considered to be
1290  * valid nonword delimiters.
1291  */
1292 reccnt(cap, mapto)
1293 unsigned char *cap;
1294 unsigned char *mapto;
1295 {
1296 	register int i, cnt, final;
1297 
1298 	cnt = 0;
1299 	final = strlen(mapto) - strlen(cap);
1300 
1301 	for (i=0; i <= final; i++)
1302 	  if ((strncmp(cap, mapto+i, strlen(cap)) == 0)       /* match */
1303 	  && (i == 0     || !wordch(&mapto[i-1]))	      /* prefix ok */
1304 	  && (i == final || !wordch(&mapto[i+strlen(cap)])))  /* suffix ok */
1305 		cnt++;
1306 	return (cnt);
1307 }
1308 
1309