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