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