xref: /illumos-gate/usr/src/cmd/vi/port/ex_vops.c (revision cc6c5292fa8a241fe50604cf6a918edfbf7cd7d2)
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 
39 /*
40  * This file defines the operation sequences which interface the
41  * logical changes to the file buffer with the internal and external
42  * display representations.
43  */
44 
45 /*
46  * Undo.
47  *
48  * Undo is accomplished in two ways.  We often for small changes in the
49  * current line know how (in terms of a change operator) how the change
50  * occurred.  Thus on an intelligent terminal we can undo the operation
51  * by another such operation, using insert and delete character
52  * stuff.  The pointers vU[AD][12] index the buffer vutmp when this
53  * is possible and provide the necessary information.
54  *
55  * The other case is that the change involved multiple lines or that
56  * we have moved away from the line or forgotten how the change was
57  * accomplished.  In this case we do a redisplay and hope that the
58  * low level optimization routines (which don't look for winning
59  * via insert/delete character) will not lose too badly.
60  */
61 unsigned char	*vUA1, *vUA2;
62 unsigned char	*vUD1, *vUD2;
63 
64 vUndo()
65 {
66 
67 	/*
68 	 * Avoid UU which clobbers ability to do u.
69 	 */
70 	if (vundkind == VNONE || vundkind == VCAPU || vUNDdot != dot) {
71 		beep();
72 		return;
73 	}
74 	CP(vutmp, linebuf);
75 	vUD1 = linebuf; vUD2 = strend(linebuf);
76 	putmk1(dot, vUNDsav);
77 	getDOT();
78 	vUA1 = linebuf; vUA2 = strend(linebuf);
79 	vundkind = VCAPU;
80 	if (state == ONEOPEN || state == HARDOPEN) {
81 		vjumpto(dot, vUNDcurs, 0);
82 		return;
83 	}
84 	vdirty(vcline, 1);
85 	if(MB_CUR_MAX > 1)
86 		rewrite = _ON;
87 	vsyncCL();
88 	if(MB_CUR_MAX > 1)
89 		rewrite = _OFF;
90 	cursor = linebuf;
91 	vfixcurs();
92 }
93 
94 vundo(show)
95 bool show;	/* if true update the screen */
96 {
97 	register int cnt;
98 	register line *addr;
99 	register unsigned char *cp;
100 	unsigned char temp[LBSIZE];
101 	bool savenote;
102 	int (*OO)();
103 	short oldhold = hold;
104 	unsigned multic[MULTI_BYTE_MAX];
105 	int length;
106 	wchar_t wchar;
107 
108 	switch (vundkind) {
109 
110 	case VMANYINS:
111 		wcursor = 0;
112 		addr1 = undap1;
113 		addr2 = undap2 - 1;
114 		vsave();
115 		YANKreg('1');
116 		notecnt = 0;
117 		/* fall into ... */
118 
119 	case VMANY:
120 	case VMCHNG:
121 		vsave();
122 		addr = dot - vcline;
123 		notecnt = 1;
124 		if (undkind == UNDPUT && undap1 == undap2) {
125 			beep();
126 			break;
127 		}
128 		/*
129 		 * Undo() call below basically replaces undap1 to undap2-1
130 		 * with dol through unddol-1.  Hack screen image to
131 		 * reflect this replacement.
132 		 */
133 		if (show)
134 			if (undkind == UNDMOVE)
135 				vdirty(0, lines);
136 			else
137 				vreplace(undap1 - addr, undap2 - undap1,
138 				    undkind == UNDPUT ? 0 : unddol - dol);
139 		savenote = notecnt;
140 		undo(1);
141 		if (show && (vundkind != VMCHNG || addr != dot))
142 			killU();
143 		vundkind = VMANY;
144 		cnt = dot - addr;
145 		if (cnt < 0 || cnt > vcnt || state != VISUAL) {
146 			if (show)
147 				vjumpto(dot, NOSTR, '.');
148 			break;
149 		}
150 		if (!savenote)
151 			notecnt = 0;
152 		if (show) {
153 			vcline = cnt;
154 			if(MB_CUR_MAX > 1)
155 				rewrite = _ON;
156 			vrepaint(vmcurs);
157 			if(MB_CUR_MAX > 1)
158 				rewrite = _OFF;
159 		}
160 		vmcurs = 0;
161 		break;
162 
163 	case VCHNG:
164 	case VCAPU:
165 		vundkind = VCHNG;
166 		strcpy(temp, vutmp);
167 		strcpy(vutmp, linebuf);
168 		doomed = lcolumn(vUA2) - lcolumn(vUA1);
169 		strcLIN(temp);
170 		cp = vUA1; vUA1 = vUD1; vUD1 = cp;
171 		cp = vUA2; vUA2 = vUD2; vUD2 = cp;
172 		if (!show)
173 			break;
174 		cursor = vUD1;
175 		if (state == HARDOPEN) {
176 			doomed = 0;
177 			vsave();
178 			vopen(dot, WBOT);
179 			vnline(cursor);
180 			break;
181 		}
182 		/*
183 		 * Pseudo insert command.
184 		 */
185 		vcursat(cursor);
186 		OO = Outchar; Outchar = vinschar; hold |= HOLDQIK;
187 		vprepins();
188 		temp[vUA2 - linebuf] = 0;
189 		for (cp = &temp[vUA1 - linebuf]; *cp;) {
190 			length = mbtowc(&wchar, (char *)cp, MULTI_BYTE_MAX);
191 			if(length < 0) {
192 				putoctal = 1;
193 				putchar(*cp++);
194 				putoctal = 0;
195 			} else {
196 				putchar(wchar);
197 				cp += length;
198 			}
199 		}
200 		Outchar = OO; hold = oldhold;
201 		endim();
202 		physdc(cindent(), cindent() + doomed);
203 		doomed = 0;
204 		vdirty(vcline, 1);
205 		if(MB_CUR_MAX > 1)
206 			rewrite = _ON;
207 		vsyncCL();
208 		if(MB_CUR_MAX > 1)
209 			rewrite = _OFF;
210 		if (cursor > linebuf && cursor >= strend(linebuf))
211 			cursor = lastchr(linebuf, cursor);
212 		vfixcurs();
213 		break;
214 
215 	case VNONE:
216 		beep();
217 		break;
218 	}
219 }
220 
221 /*
222  * Routine to handle a change inside a macro.
223  * Fromvis is true if we were called from a visual command (as
224  * opposed to an ex command).  This has nothing to do with being
225  * in open/visual mode as :s/foo/bar is not fromvis.
226  */
227 vmacchng(fromvis)
228 bool fromvis;
229 {
230 	line *savedot, *savedol;
231 	unsigned char *savecursor;
232 	unsigned char savelb[LBSIZE];
233 	int nlines, more;
234 	register line *a1, *a2;
235 	unsigned char ch;	/* DEBUG */
236 	int copyw(), copywR();
237 
238 	if (!inopen)
239 		return;
240 	if (!vmacp)
241 		vch_mac = VC_NOTINMAC;
242 #ifdef UNDOTRACE
243 	if (trace)
244 		fprintf(trace, "vmacchng, vch_mac=%d, linebuf='%s', *dot=%o\n", vch_mac, linebuf, *dot);
245 #endif
246 	if (vmacp && fromvis)
247 		vsave();
248 #ifdef UNDOTRACE
249 	if (trace)
250 		fprintf(trace, "after vsave, linebuf='%s', *dot=%o\n", linebuf, *dot);
251 #endif
252 	switch(vch_mac) {
253 	case VC_NOCHANGE:
254 		vch_mac = VC_ONECHANGE;
255 		break;
256 	case VC_ONECHANGE:
257 		/* Save current state somewhere */
258 #ifdef UNDOTRACE
259 		vudump("before vmacchng hairy case");
260 #endif
261 		savedot = dot; savedol = dol; savecursor = cursor;
262 		CP(savelb, linebuf);
263 		nlines = dol - zero;
264 		while ((line *) endcore - truedol < nlines)
265 			if (morelines() < 0)
266 				return;	/* or could be fatal error */
267 		copyw(truedol+1, zero+1, nlines);
268 		truedol += nlines;
269 
270 #ifdef UNDOTRACE
271 		visdump("before vundo");
272 #endif
273 		/* Restore state as it was at beginning of macro */
274 		vundo(0);
275 #ifdef UNDOTRACE
276 		visdump("after vundo");
277 		vudump("after vundo");
278 #endif
279 
280 		/* Do the saveall we should have done then */
281 		saveall();
282 #ifdef UNDOTRACE
283 		vudump("after saveall");
284 #endif
285 
286 		/* Restore current state from where saved */
287 		more = savedol - dol; /* amount we shift everything by */
288 		if (more)
289 			(*(more>0 ? copywR : copyw))(savedol+1, dol+1, truedol-dol);
290 		unddol += more; truedol += more; undap2 += more;
291 
292 		truedol -= nlines;
293 		copyw(zero+1, truedol+1, nlines);
294 		dot = savedot; dol = savedol ; cursor = savecursor;
295 		CP(linebuf, savelb);
296 		vch_mac = VC_MANYCHANGE;
297 
298 		/* Arrange that no further undo saving happens within macro */
299 		otchng = tchng;	/* Copied this line blindly - bug? */
300 		inopen = -1;	/* no need to save since it had to be 1 or -1 before */
301 		vundkind = VMANY;
302 #ifdef UNDOTRACE
303 		vudump("after vmacchng");
304 #endif
305 		break;
306 	case VC_NOTINMAC:
307 	case VC_MANYCHANGE:
308 		/* Nothing to do for various reasons. */
309 		break;
310 	}
311 }
312 
313 /*
314  * Initialize undo information before an append.
315  */
316 vnoapp()
317 {
318 
319 	vUD1 = vUD2 = cursor;
320 }
321 
322 /*
323  * All the rest of the motion sequences have one or more
324  * cases to deal with.  In the case wdot == 0, operation
325  * is totally within current line, from cursor to wcursor.
326  * If wdot is given, but wcursor is 0, then operation affects
327  * the inclusive line range.  The hardest case is when both wdot
328  * and wcursor are given, then operation affects from line dot at
329  * cursor to line wdot at wcursor.
330  */
331 
332 /*
333  * Move is simple, except for moving onto new lines in hardcopy open mode.
334  */
335 vmove()
336 {
337 	register int cnt;
338 
339 	if (wdot) {
340 		if (wdot < one || wdot > dol) {
341 			beep();
342 			return;
343 		}
344 		cnt = wdot - dot;
345 		wdot = NOLINE;
346 		if (cnt)
347 			killU();
348 		vupdown(cnt, wcursor);
349 		return;
350 	}
351 
352 	/*
353 	 * When we move onto a new line, save information for U undo.
354 	 */
355 	if (vUNDdot != dot) {
356 		vUNDsav = *dot;
357 		vUNDcurs = wcursor;
358 		vUNDdot = dot;
359 	}
360 
361 	/*
362 	 * In hardcopy open, type characters to left of cursor
363 	 * on new line, or back cursor up if its to left of where we are.
364 	 * In any case if the current line is ``rubbled'' i.e. has trashy
365 	 * looking overstrikes on it or \'s from deletes, we reprint
366 	 * so it is more comprehensible (and also because we can't work
367 	 * if we let it get more out of sync since column() won't work right.
368 	 */
369 	if (state == HARDOPEN) {
370 		register unsigned char *cp;
371 		if (rubble) {
372 			register int c;
373 			int oldhold = hold;
374 
375 			sethard();
376 			cp = wcursor;
377 			c = *cp;
378 			*cp = 0;
379 			hold |= HOLDDOL;
380 			vreopen(WTOP, lineDOT(), vcline);
381 			hold = oldhold;
382 			*cp = c;
383 		} else if (wcursor > cursor) {
384 			register int length;
385 			char multic[MULTI_BYTE_MAX];
386 			wchar_t wchar;
387 			vfixcurs();
388 			for (cp = cursor; *cp && cp < wcursor;) {
389 				length = mbtowc(&wchar, (char *)cp, MULTI_BYTE_MAX);
390 				if(length == 0)
391 					putchar(' ');
392 				else if(length < 0) {
393 					putoctal = 1;
394 					putchar(*cp++);
395 					putoctal = 0;
396 				} else {
397 					cp += length;
398 					putchar(wchar);
399 				}
400 			}
401 		}
402 	}
403 	vsetcurs(wcursor);
404 }
405 
406 /*
407  * Delete operator.
408  *
409  * Hard case of deleting a range where both wcursor and wdot
410  * are specified is treated as a special case of change and handled
411  * by vchange (although vchange may pass it back if it degenerates
412  * to a full line range delete.)
413  */
414 vdelete(c)
415 	unsigned char c;
416 {
417 	register unsigned char *cp;
418 	register int i;
419 
420 	if (wdot) {
421 		if (wcursor) {
422 			vchange('d');
423 			return;
424 		}
425 		if ((i = xdw()) < 0)
426 			return;
427 		if (state != VISUAL) {
428 			vgoto(LINE(0), 0);
429 			vputchar('@');
430 		}
431 		wdot = dot;
432 		vremote(i, delete, 0);
433 		notenam = (unsigned char *)"delete";
434 		DEL[0] = 0;
435 		killU();
436 		vreplace(vcline, i, 0);
437 		if (wdot > dol)
438 			vcline--;
439 		vrepaint(NOSTR);
440 		return;
441 	}
442 	if (wcursor < linebuf)
443 		wcursor = linebuf;
444 	if (cursor == wcursor) {
445 		beep();
446 		return;
447 	}
448 	i = vdcMID();
449 	cp = cursor;
450 	setDEL();
451 	CP(cp, wcursor);
452 	if (cp > linebuf && (cp[0] == 0 || c == '#'))
453 		cp = lastchr(linebuf, cp);
454 	if (state == HARDOPEN) {
455 		bleep(i, cp);
456 		cursor = cp;
457 		return;
458 	}
459 	physdc(lcolumn(cursor), i);
460 	DEPTH(vcline) = 0;
461 	if(MB_CUR_MAX > 1)
462 		rewrite = _ON;
463 	vreopen(LINE(vcline), lineDOT(), vcline);
464 	if(MB_CUR_MAX > 1)
465 		rewrite = _OFF;
466 	vsyncCL();
467 	vsetcurs(cp);
468 }
469 
470 /*
471  * Change operator.
472  *
473  * In a single line we mark the end of the changed area with '$'.
474  * On multiple whole lines, we clear the lines first.
475  * Across lines with both wcursor and wdot given, we delete
476  * and sync then append (but one operation for undo).
477  */
478 vchange(c)
479 	unsigned char c;
480 {
481 	register unsigned char *cp;
482 	register int i, ind, cnt;
483 	line *addr;
484 
485 	if (wdot) {
486 		/*
487 		 * Change/delete of lines or across line boundaries.
488 		 */
489 		if ((cnt = xdw()) < 0)
490 			return;
491 		getDOT();
492 		if (wcursor && cnt == 1) {
493 			/*
494 			 * Not really.
495 			 */
496 			wdot = 0;
497 			if (c == 'd') {
498 				vdelete(c);
499 				return;
500 			}
501 			goto smallchange;
502 		}
503 		if (cursor && wcursor) {
504 			/*
505 			 * Across line boundaries, but not
506 			 * necessarily whole lines.
507 			 * Construct what will be left.
508 			 */
509 			*cursor = 0;
510 			strcpy(genbuf, linebuf);
511 			getline(*wdot);
512 			if (strlen(genbuf) + strlen(wcursor) > LBSIZE - 2) {
513 				getDOT();
514 				beep();
515 				return;
516 			}
517 			strcat(genbuf, wcursor);
518 			if (c == 'd' && *vpastwh(genbuf) == 0) {
519 				/*
520 				 * Although this is a delete
521 				 * spanning line boundaries, what
522 				 * would be left is all white space,
523 				 * so take it all away.
524 				 */
525 				wcursor = 0;
526 				getDOT();
527 				op = 0;
528 				notpart(lastreg);
529 				notpart('1');
530 				vdelete(c);
531 				return;
532 			}
533 			ind = -1;
534 		} else if (c == 'd' && wcursor == 0) {
535 			vdelete(c);
536 			return;
537 		} else
538 			/*
539 			 * We are just substituting text for whole lines,
540 			 * so determine the first autoindent.
541 			 */
542 			if (value(vi_LISP) && value(vi_AUTOINDENT))
543 				ind = lindent(dot);
544 			else
545 				ind = whitecnt(linebuf);
546 		i = vcline >= 0 ? LINE(vcline) : WTOP;
547 
548 		/*
549 		 * Delete the lines from the buffer,
550 		 * and remember how the partial stuff came about in
551 		 * case we are told to put.
552 		 */
553 		addr = dot;
554 		vremote(cnt, delete, 0);
555 		setpk();
556 		notenam = (unsigned char *)"delete";
557 		if (c != 'd')
558 			notenam = (unsigned char *)"change";
559 		/*
560 		 * If DEL[0] were nonzero, put would put it back
561 		 * rather than the deleted lines.
562 		 */
563 		DEL[0] = 0;
564 		if (cnt > 1)
565 			killU();
566 
567 		/*
568 		 * Now hack the screen image coordination.
569 		 */
570 		vreplace(vcline, cnt, 0);
571 		wdot = NOLINE;
572 		noteit(0);
573 		vcline--;
574 		if (addr <= dol)
575 			dot--;
576 
577 		/*
578 		 * If this is a across line delete/change,
579 		 * cursor stays where it is; just splice together the pieces
580 		 * of the new line.  Otherwise generate a autoindent
581 		 * after a S command.
582 		 */
583 		if (ind >= 0) {
584 			*genindent(ind) = 0;
585 			vdoappend(genbuf);
586 		} else {
587 			vmcurs = cursor;
588 			strcLIN(genbuf);
589 			vdoappend(linebuf);
590 		}
591 
592 		/*
593 		 * Indicate a change on hardcopies by
594 		 * erasing the current line.
595 		 */
596 		if (c != 'd' && state != VISUAL && state != HARDOPEN) {
597 			int oldhold = hold;
598 
599 			hold |= HOLDAT, vclrlin(i, dot), hold = oldhold;
600 		}
601 
602 		/*
603 		 * Open the line (logically) on the screen, and
604 		 * update the screen tail.  Unless we are really a delete
605 		 * go off and gather up inserted characters.
606 		 */
607 		vcline++;
608 		if (vcline < 0)
609 			vcline = 0;
610 		vopen(dot, i);
611 		vsyncCL();
612 		noteit(1);
613 		if (c != 'd') {
614 			if (ind >= 0) {
615 				cursor = linebuf;
616 				linebuf[0] = 0;
617 				vfixcurs();
618 			} else {
619 				ind = 0;
620 				vcursat(cursor);
621 			}
622 			vappend('x', 1, ind);
623 			return;
624 		}
625 		if (*cursor == 0 && cursor > linebuf)
626 			cursor = lastchr(linebuf, cursor);
627 		vrepaint(cursor);
628 		return;
629 	}
630 
631 smallchange:
632 	/*
633 	 * The rest of this is just low level hacking on changes
634 	 * of small numbers of characters.
635 	 */
636 	if (wcursor < linebuf)
637 		wcursor = linebuf;
638 	if (cursor == wcursor) {
639 		beep();
640 		return;
641 	}
642 	i = vdcMID();
643 	cp = cursor;
644 	if (state != HARDOPEN)
645 		vfixcurs();
646 
647 	/*
648 	 * Put out the \\'s indicating changed text in hardcopy,
649 	 * or mark the end of the change with $ if not hardcopy.
650 	 */
651 	if (state == HARDOPEN)
652 		bleep(i, cp);
653 	else {
654 		vcursbef(wcursor);
655 		putchar('$');
656 		i = cindent();
657 	}
658 
659 	/*
660 	 * Remember the deleted text for possible put,
661 	 * and then prepare and execute the input portion of the change.
662 	 */
663 	cursor = cp;
664 	setDEL();
665 	CP(cursor, wcursor);
666 	if (state != HARDOPEN) {
667 		/* place cursor at beginning of changing text */
668 		vgotoCL(lcolumn(cp));
669 		doomed = i - cindent();
670 	} else {
671 /*
672 		sethard();
673 		wcursor = cursor;
674 		cursor = linebuf;
675 		vgoto(outline, value(vi_NUMBER) << 3);
676 		vmove();
677 */
678 		doomed = 0;
679 	}
680 	prepapp();
681 	vappend('c', 1, 0);
682 }
683 
684 /*
685  * Open new lines.
686  *
687  * Tricky thing here is slowopen.  This causes display updating
688  * to be held off so that 300 baud dumb terminals don't lose badly.
689  * This also suppressed counts, which otherwise say how many blank
690  * space to open up.  Counts are also suppressed on intelligent terminals.
691  * Actually counts are obsoleted, since if your terminal is slow
692  * you are better off with slowopen.
693  */
694 voOpen(c, cnt)
695 	int c;	/* mjm: char --> int */
696 	register int cnt;
697 {
698 	register int ind = 0, i;
699 	short oldhold = hold;
700 
701 	if (value(vi_SLOWOPEN) || value(vi_REDRAW) && insert_line && delete_line)
702 		cnt = 1;
703 	vsave();
704 	setLAST();
705 	if (value(vi_AUTOINDENT))
706 		ind = whitecnt(linebuf);
707 	if (c == 'O') {
708 		vcline--;
709 		dot--;
710 		if (dot > zero)
711 			getDOT();
712 	}
713 	if (value(vi_AUTOINDENT)) {
714 		if (value(vi_LISP))
715 			ind = lindent(dot + 1);
716 	}
717 	killU();
718 	prepapp();
719 	if (FIXUNDO)
720 		vundkind = VMANY;
721 	if (state != VISUAL)
722 		c = WBOT + 1;
723 	else {
724 		c = vcline < 0 ? WTOP - cnt : LINE(vcline) + DEPTH(vcline);
725 		if (c < ZERO)
726 			c = ZERO;
727 		i = LINE(vcline + 1) - c;
728 		if (i < cnt && c <= WBOT && (!insert_line || !delete_line))
729 			vinslin(c, cnt - i, vcline);
730 	}
731 	*genindent(ind) = 0;
732 	vdoappend(genbuf);
733 	vcline++;
734 	oldhold = hold;
735 	hold |= HOLDROL;
736 	vopen(dot, c);
737 	hold = oldhold;
738 	if (value(vi_SLOWOPEN))
739 		/*
740 		 * Oh, so lazy!
741 		 */
742 		vscrap();
743 	else
744 		vsync1(LINE(vcline));
745 	cursor = linebuf;
746 	linebuf[0] = 0;
747 	vappend('o', 1, ind);
748 }
749 
750 /*
751  * > < and = shift operators.
752  *
753  * Note that =, which aligns lisp, is just a ragged sort of shift,
754  * since it never distributes text between lines.
755  */
756 unsigned char	vshnam[2] = { 'x', 0 };
757 
758 vshftop()
759 {
760 	register line *addr;
761 	register int cnt;
762 
763 	if ((cnt = xdw()) < 0)
764 		return;
765 	addr = dot;
766 	vremote(cnt, vshift, 0);
767 	vshnam[0] = op;
768 	notenam = vshnam;
769 	dot = addr;
770 	vreplace(vcline, cnt, cnt);
771 	if (state == HARDOPEN)
772 		vcnt = 0;
773 	vrepaint(NOSTR);
774 }
775 
776 /*
777  * !.
778  *
779  * Filter portions of the buffer through unix commands.
780  */
781 vfilter()
782 {
783 	register line *addr;
784 	register int cnt;
785 	unsigned char *oglobp;
786 	short d;
787 
788 	if ((cnt = xdw()) < 0)
789 		return;
790 	if (vglobp)
791 		vglobp = (unsigned char *)uxb;
792 	if (readecho('!'))
793 		return;
794 	oglobp = globp; globp = genbuf + 1;
795 	d = peekc; ungetchar(0);
796 	CATCH
797 		fixech();
798 		unix0(0, 0);
799 	ONERR
800 		splitw = 0;
801 		ungetchar(d);
802 		vrepaint(cursor);
803 		globp = oglobp;
804 		return;
805 	ENDCATCH
806 	ungetchar(d); globp = oglobp;
807 	addr = dot;
808 	CATCH
809 		vgoto(WECHO, 0); flusho();
810 		vremote(cnt, vi_filter, 2);
811 	ONERR
812 		vdirty(0, lines);
813 	ENDCATCH
814 	if (dot == zero && dol > zero)
815 		dot = one;
816 	splitw = 0;
817 	notenam = (unsigned char *)"";
818 	/*
819 	 * BUG: we shouldn't be depending on what undap2 and undap1 are,
820 	 * since we may be inside a macro.  What's really wanted is the
821 	 * number of lines we read from the filter.  However, the mistake
822 	 * will be an overestimate so it only results in extra work,
823 	 * it shouldn't cause any real mess-ups.
824 	 */
825 	vreplace(vcline, cnt, undap2 - undap1);
826 	dot = addr;
827 	if (dot > dol) {
828 		dot--;
829 		vcline--;
830 	}
831 	vrepaint(NOSTR);
832 }
833 
834 /*
835  * Xdw exchanges dot and wdot if appropriate and also checks
836  * that wdot is reasonable.  Its name comes from
837  *	xchange dotand wdot
838  */
839 xdw()
840 {
841 	register unsigned char *cp;
842 	register int cnt;
843 /*
844 	register int notp = 0;
845  */
846 
847 	if (wdot == NOLINE || wdot < one || wdot > dol) {
848 		beep();
849 		return (-1);
850 	}
851 	vsave();
852 	setLAST();
853 	if (dot > wdot || (dot == wdot && wcursor != 0 && cursor > wcursor)) {
854 		register line *addr;
855 
856 		vcline -= dot - wdot;
857 		addr = dot; dot = wdot; wdot = addr;
858 		cp = cursor; cursor = wcursor; wcursor = cp;
859 	}
860 	/*
861 	 * If a region is specified but wcursor is at the beginning
862 	 * of the last line, then we move it to be the end of the
863 	 * previous line (actually off the end).
864 	 */
865 	if (cursor && wcursor == linebuf && wdot > dot) {
866 		wdot--;
867 		getDOT();
868 		if (vpastwh(linebuf) >= cursor)
869 			wcursor = 0;
870 		else {
871 			getline(*wdot);
872 			wcursor = strend(linebuf);
873 			getDOT();
874 		}
875 		/*
876 		 * Should prepare in caller for possible dot == wdot.
877 		 */
878 	}
879 	cnt = wdot - dot + 1;
880 	if (vreg) {
881 		vremote(cnt, YANKreg, vreg);
882 /*
883 		if (notp)
884 			notpart(vreg);
885  */
886 	}
887 
888 	/*
889 	 * Kill buffer code.  If delete operator is c or d, then save
890 	 * the region in numbered buffers.
891 	 *
892 	 * BUG:			This may be somewhat inefficient due
893 	 *			to the way named buffer are implemented,
894 	 *			necessitating some optimization.
895 	 */
896 	vreg = 0;
897 	if (any(op, "cd")) {
898 		vremote(cnt, YANKreg, '1');
899 /*
900 		if (notp)
901 			notpart('1');
902  */
903 	}
904 	return (cnt);
905 }
906 
907 /*
908  * Routine for vremote to call to implement shifts.
909  */
910 vshift()
911 {
912 
913 	shift(op, 1);
914 }
915 
916 /*
917  * Replace a single character with the next input character.
918  * A funny kind of insert.
919  */
920 vrep(cnt)
921 	register int cnt;
922 {
923 	register int i, c;
924 	register unsigned char *endcurs;
925 	endcurs = cursor;
926 	for(i = 1; i <= cnt; i++) {
927 		if(!*endcurs) {
928 			beep();
929 			return;
930 		}
931 		endcurs = nextchr(endcurs);
932 	}
933 	i = lcolumn(endcurs);
934 	vcursat(cursor);
935 	doomed = i - cindent();
936 	/*
937 	 * TRANSLATION_NOTE
938 	 *	"r" is a terse mode message that corresponds to
939 	 *	"REPLACE 1 CHAR".
940 	 *	Translated message of "r" must be 1 character (not byte).
941 	 *	Or, just leave it.
942 	 */
943 	if(value(vi_TERSE))
944 		vshowmode(gettext("r"));
945 	else
946 		vshowmode(gettext("REPLACE 1 CHAR"));
947 	if (!vglobp) {
948 		c = getesc();
949 		if (c == 0) {
950 			vshowmode("");
951 			vfixcurs();
952 			return;
953 		}
954 		ungetkey(c);
955 	}
956 	CP(vutmp, linebuf);
957 	if (FIXUNDO)
958 		vundkind = VCHNG;
959 	wcursor = endcurs;
960 	vUD1 = cursor; vUD2 = wcursor;
961 	CP(cursor, wcursor);
962 	prepapp();
963 	vappend('r', cnt, 0);
964 	*lastcp++ = INS[0];
965 	setLAST();
966 }
967 
968 /*
969  * Yank.
970  *
971  * Yanking to string registers occurs for free (essentially)
972  * in the routine xdw().
973  */
974 vyankit()
975 {
976 	register int cnt;
977 
978 	if (wdot) {
979 		if ((cnt = xdw()) < 0)
980 			return;
981 		vremote(cnt, yank, 0);
982 		setpk();
983 		notenam = (unsigned char *)"yank";
984 		if (FIXUNDO)
985 			vundkind = VNONE;
986 		DEL[0] = 0;
987 		wdot = NOLINE;
988 		if (notecnt <= vcnt - vcline && notecnt < value(vi_REPORT))
989 			notecnt = 0;
990 		vrepaint(cursor);
991 		return;
992 	}
993 	takeout(DEL);
994 }
995 
996 /*
997  * Set pkill variables so a put can
998  * know how to put back partial text.
999  * This is necessary because undo needs the complete
1000  * line images to be saved, while a put wants to trim
1001  * the first and last lines.  The compromise
1002  * is for put to be more clever.
1003  */
1004 setpk()
1005 {
1006 
1007 	if (wcursor) {
1008 		pkill[0] = cursor;
1009 		pkill[1] = wcursor;
1010 	}
1011 }
1012