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