xref: /titanic_44/usr/src/cmd/vi/port/ex_vops.c (revision f56c1286e5113aa46bd6e723da14d30c123153f2)
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 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  * Tricky thing here is slowopen.  This causes display updating
696  * to be held off so that 300 baud dumb terminals don't lose badly.
697  * This also suppressed counts, which otherwise say how many blank
698  * space to open up.  Counts are also suppressed on intelligent terminals.
699  * Actually counts are obsoleted, since if your terminal is slow
700  * you are better off with slowopen.
701  */
702 void
703 voOpen(int c, int cnt)
704 {
705 	int ind = 0, i;
706 	short oldhold = hold;
707 
708 	if (value(vi_SLOWOPEN) || value(vi_REDRAW) && insert_line && delete_line)
709 		cnt = 1;
710 	vsave();
711 	setLAST();
712 	if (value(vi_AUTOINDENT))
713 		ind = whitecnt(linebuf);
714 	if (c == 'O') {
715 		vcline--;
716 		dot--;
717 		if (dot > zero)
718 			getDOT();
719 	}
720 	if (value(vi_AUTOINDENT)) {
721 		if (value(vi_LISP))
722 			ind = lindent(dot + 1);
723 	}
724 	killU();
725 	prepapp();
726 	if (FIXUNDO)
727 		vundkind = VMANY;
728 	if (state != VISUAL)
729 		c = WBOT + 1;
730 	else {
731 		c = vcline < 0 ? WTOP - cnt : LINE(vcline) + DEPTH(vcline);
732 		if (c < ZERO)
733 			c = ZERO;
734 		i = LINE(vcline + 1) - c;
735 		if (i < cnt && c <= WBOT && (!insert_line || !delete_line))
736 			vinslin(c, cnt - i, vcline);
737 	}
738 	*genindent(ind) = 0;
739 	vdoappend(genbuf);
740 	vcline++;
741 	oldhold = hold;
742 	hold |= HOLDROL;
743 	vopen(dot, c);
744 	hold = oldhold;
745 	if (value(vi_SLOWOPEN))
746 		/*
747 		 * Oh, so lazy!
748 		 */
749 		vscrap();
750 	else
751 		vsync1(LINE(vcline));
752 	cursor = linebuf;
753 	linebuf[0] = 0;
754 	vappend('o', 1, ind);
755 }
756 
757 /*
758  * > < and = shift operators.
759  *
760  * Note that =, which aligns lisp, is just a ragged sort of shift,
761  * since it never distributes text between lines.
762  */
763 unsigned char	vshnam[2] = { 'x', 0 };
764 
765 int
766 vshftop(void)
767 {
768 	line *addr;
769 	int cnt;
770 
771 	if ((cnt = xdw()) < 0)
772 		return (0);
773 	addr = dot;
774 	vremote(cnt, vshift, 0);
775 	vshnam[0] = op;
776 	notenam = vshnam;
777 	dot = addr;
778 	vreplace(vcline, cnt, cnt);
779 	if (state == HARDOPEN)
780 		vcnt = 0;
781 	vrepaint(NOSTR);
782 	return (0);
783 }
784 
785 /*
786  * !.
787  *
788  * Filter portions of the buffer through unix commands.
789  */
790 int
791 vfilter(void)
792 {
793 	line *addr;
794 	int cnt;
795 	unsigned char *oglobp;
796 	short d;
797 
798 	if ((cnt = xdw()) < 0)
799 		return (0);
800 	if (vglobp)
801 		vglobp = (unsigned char *)uxb;
802 	if (readecho('!'))
803 		return (0);
804 	oglobp = globp; globp = genbuf + 1;
805 	d = peekc; ungetchar(0);
806 	CATCH
807 		fixech();
808 		unix0(0, 0);
809 	ONERR
810 		splitw = 0;
811 		ungetchar(d);
812 		vrepaint(cursor);
813 		globp = oglobp;
814 		return (0);
815 	ENDCATCH
816 	ungetchar(d); globp = oglobp;
817 	addr = dot;
818 	CATCH
819 		vgoto(WECHO, 0); flusho();
820 		vremote(cnt, vi_filter, 2);
821 	ONERR
822 		vdirty(0, lines);
823 	ENDCATCH
824 	if (dot == zero && dol > zero)
825 		dot = one;
826 	splitw = 0;
827 	notenam = (unsigned char *)"";
828 	/*
829 	 * BUG: we shouldn't be depending on what undap2 and undap1 are,
830 	 * since we may be inside a macro.  What's really wanted is the
831 	 * number of lines we read from the filter.  However, the mistake
832 	 * will be an overestimate so it only results in extra work,
833 	 * it shouldn't cause any real mess-ups.
834 	 */
835 	vreplace(vcline, cnt, undap2 - undap1);
836 	dot = addr;
837 	if (dot > dol) {
838 		dot--;
839 		vcline--;
840 	}
841 	vrepaint(NOSTR);
842 	return (0);
843 }
844 
845 /*
846  * Xdw exchanges dot and wdot if appropriate and also checks
847  * that wdot is reasonable.  Its name comes from
848  *	xchange dotand wdot
849  */
850 int
851 xdw(void)
852 {
853 	unsigned char *cp;
854 	int cnt;
855 /*
856 	register int notp = 0;
857  */
858 
859 	if (wdot == NOLINE || wdot < one || wdot > dol) {
860 		(void) beep();
861 		return (-1);
862 	}
863 	vsave();
864 	setLAST();
865 	if (dot > wdot || (dot == wdot && wcursor != 0 && cursor > wcursor)) {
866 		line *addr;
867 
868 		vcline -= dot - wdot;
869 		addr = dot; dot = wdot; wdot = addr;
870 		cp = cursor; cursor = wcursor; wcursor = cp;
871 	}
872 	/*
873 	 * If a region is specified but wcursor is at the beginning
874 	 * of the last line, then we move it to be the end of the
875 	 * previous line (actually off the end).
876 	 */
877 	if (cursor && wcursor == linebuf && wdot > dot) {
878 		wdot--;
879 		getDOT();
880 		if (vpastwh(linebuf) >= cursor)
881 			wcursor = 0;
882 		else {
883 			getline(*wdot);
884 			wcursor = strend(linebuf);
885 			getDOT();
886 		}
887 		/*
888 		 * Should prepare in caller for possible dot == wdot.
889 		 */
890 	}
891 	cnt = wdot - dot + 1;
892 	if (vreg) {
893 		vremote(cnt, YANKreg, vreg);
894 /*
895 		if (notp)
896 			notpart(vreg);
897  */
898 	}
899 
900 	/*
901 	 * Kill buffer code.  If delete operator is c or d, then save
902 	 * the region in numbered buffers.
903 	 *
904 	 * BUG:			This may be somewhat inefficient due
905 	 *			to the way named buffer are implemented,
906 	 *			necessitating some optimization.
907 	 */
908 	vreg = 0;
909 	if (any(op, "cd")) {
910 		vremote(cnt, YANKreg, '1');
911 /*
912 		if (notp)
913 			notpart('1');
914  */
915 	}
916 	return (cnt);
917 }
918 
919 /*
920  * Routine for vremote to call to implement shifts.
921  */
922 int
923 vshift(void)
924 {
925 
926 	shift(op, 1);
927 	return (0);
928 }
929 
930 /*
931  * Replace a single character with the next input character.
932  * A funny kind of insert.
933  */
934 void
935 vrep(int cnt)
936 {
937 	int i, c;
938 	unsigned char *endcurs;
939 	endcurs = cursor;
940 	for(i = 1; i <= cnt; i++) {
941 		if(!*endcurs) {
942 			(void) beep();
943 			return;
944 		}
945 		endcurs = nextchr(endcurs);
946 	}
947 	i = lcolumn(endcurs);
948 	vcursat(cursor);
949 	doomed = i - cindent();
950 	/*
951 	 * TRANSLATION_NOTE
952 	 *	"r" is a terse mode message that corresponds to
953 	 *	"REPLACE 1 CHAR".
954 	 *	Translated message of "r" must be 1 character (not byte).
955 	 *	Or, just leave it.
956 	 */
957 	if(value(vi_TERSE))
958 		vshowmode(gettext("r"));
959 	else
960 		vshowmode(gettext("REPLACE 1 CHAR"));
961 	if (!vglobp) {
962 		c = getesc();
963 		if (c == 0) {
964 			vshowmode("");
965 			vfixcurs();
966 			return;
967 		}
968 		ungetkey(c);
969 	}
970 	CP(vutmp, linebuf);
971 	if (FIXUNDO)
972 		vundkind = VCHNG;
973 	wcursor = endcurs;
974 	vUD1 = cursor; vUD2 = wcursor;
975 	CP(cursor, wcursor);
976 	prepapp();
977 	vappend('r', cnt, 0);
978 	*lastcp++ = INS[0];
979 	setLAST();
980 }
981 
982 /*
983  * Yank.
984  *
985  * Yanking to string registers occurs for free (essentially)
986  * in the routine xdw().
987  */
988 int
989 vyankit(void)
990 {
991 	int cnt;
992 
993 	if (wdot) {
994 		if ((cnt = xdw()) < 0)
995 			return (0);
996 		vremote(cnt, yank, 0);
997 		setpk();
998 		notenam = (unsigned char *)"yank";
999 		if (FIXUNDO)
1000 			vundkind = VNONE;
1001 		DEL[0] = 0;
1002 		wdot = NOLINE;
1003 		if (notecnt <= vcnt - vcline && notecnt < value(vi_REPORT))
1004 			notecnt = 0;
1005 		vrepaint(cursor);
1006 		return (0);
1007 	}
1008 	takeout(DEL);
1009 	return (0);
1010 
1011 }
1012 
1013 /*
1014  * Set pkill variables so a put can
1015  * know how to put back partial text.
1016  * This is necessary because undo needs the complete
1017  * line images to be saved, while a put wants to trim
1018  * the first and last lines.  The compromise
1019  * is for put to be more clever.
1020  */
1021 void
1022 setpk(void)
1023 {
1024 
1025 	if (wcursor) {
1026 		pkill[0] = cursor;
1027 		pkill[1] = wcursor;
1028 	}
1029 }
1030