xref: /illumos-gate/usr/src/cmd/vi/port/ex_vadj.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
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_tune.h"
37 #include "ex_tty.h"
38 #include "ex_vis.h"
39 
40 /*
41  * Routines to deal with management of logical versus physical
42  * display, opening and redisplaying lines on the screen, and
43  * use of intelligent terminal operations.  Routines to deal with
44  * screen cleanup after a change.
45  */
46 
47 /*
48  * Display a new line at physical line p, returning
49  * the depth of the newly displayed line.  We may decide
50  * to expand the window on an intelligent terminal if it is
51  * less than a full screen by deleting a line above the top of the
52  * window before doing an insert line to keep all the good text
53  * on the screen in which case the line may actually end up
54  * somewhere other than line p.
55  */
56 void
57 vopen(line *tp, int p)
58 {
59 	int cnt;
60 	struct vlinfo *vp, *vpc;
61 
62 #ifdef ADEBUG
63 	if (trace != NULL)
64 		tfixnl(), fprintf(trace, "vopen(%d, %d)\n", lineno(tp), p);
65 #endif
66 	if (state != VISUAL) {
67 		if (vcnt)
68 			if (hold & HOLDROL)
69 				vup1();
70 			else
71 				vclean();
72 
73 		/*
74 		 * Forget all that we once knew.
75 		 */
76 		vcnt = vcline = 0;
77 		p = WBOT; LASTLINE = WBOT + 1;
78 		state = bastate;
79 		WTOP = basWTOP;
80 		WLINES = basWLINES;
81 	}
82 	vpc = &vlinfo[vcline];
83 	for (vp = &vlinfo[vcnt]; vp >= vpc; vp--)
84 		vlcopy(vp[1], vp[0]);
85 	vcnt++;
86 	if (Pline == numbline)
87 		/*
88 		 * Dirtying all the lines is rather inefficient
89 		 * internally, but number mode is used rarely
90 		 * and so it's not worth optimizing.
91 		 */
92 		vdirty(vcline+1, WECHO);
93 	getline(*tp);
94 
95 	/*
96 	 * If we are opening at the top of the window, can try a window
97 	 * expansion at the top.
98 	 */
99 	if (state == VISUAL && vcline == 0 && vcnt > 1 && p > ZERO) {
100 		cnt = p + vdepth() - LINE(1);
101 		if (cnt > 0) {
102 			p -= cnt;
103 			if (p < ZERO)
104 				p = ZERO;
105 			WTOP = p;
106 			WLINES = WBOT - WTOP + 1;
107 		}
108 	}
109 	vpc->vliny = p, vpc->vdepth = 0, vpc->vflags = 0;
110 	cnt = vreopen(p, lineno(tp), vcline);
111 	if (vcline + 1 == vcnt)
112 		LINE(vcnt) = LINE(vcline) + cnt;
113 }
114 
115 /*
116  * Redisplay logical line l at physical line p with line number lineno.
117  */
118 int
119 vreopen(int p, int lineno, int l)
120 {
121 	int d;
122 	struct vlinfo *vp = &vlinfo[l];
123 
124 	d = vp->vdepth;
125 	if (d == 0 || (vp->vflags & VDIRT))
126 		vp->vdepth = d = vdepth();
127 	vp->vliny = p, vp->vflags &= ~VDIRT;
128 
129 	/*
130 	 * Try to win by making the screen larger rather than inserting
131 	 * a line and driving text off the bottom.
132 	 */
133 	p = vglitchup(l, 0);
134 
135 	/*
136 	 * BUG:	Should consider using clr_eol here to clear to end of line.
137 	 *	As it stands we always strike over the current text.
138 	 *	Since often the current text is the same as what
139 	 *	we are overstriking with, it tends not to show.
140 	 *	On the other hand if it is different and we end up
141 	 *	spacing out a lot of text, we could have won with
142 	 *	a clr_eol.  This is probably worthwhile at low speed
143 	 *	only however, since clearly computation will be
144 	 *	necessary to determine which way to go.
145 	 */
146 	vigoto(p, 0);
147 	pline(lineno);
148 
149 	if (state == VISUAL && l == vcline && vp->vliny < 0) {
150 		vp->vliny = 0;
151 		vscrap();
152 		return (d);
153 	}
154 
155 	/*
156 	 * When we are typing part of a line for hardcopy open, don't
157 	 * want to type the '$' marking an end of line if in list mode.
158 	 */
159 	if (hold & HOLDDOL)
160 		return (d);
161 	if (Putchar == listchar)
162 		putchar('$');
163 
164 	/*
165 	 * Optimization of cursor motion may prevent screen rollup if the
166 	 * line has blanks/tabs at the end unless we force the cursor to appear
167 	 * on the last line segment.
168 	 */
169 	if (vp->vliny + d - 1 > WBOT)
170 		vcsync();
171 
172 	/*
173 	 * Switch into hardcopy open mode if we are in one line (adm3)
174 	 * open mode and this line is now too long.  If in hardcopy
175 	 * open mode, then call sethard to move onto the next line
176 	 * with appropriate positioning.
177 	 */
178 	if (state == ONEOPEN) {
179 		WCOLS = OCOLUMNS;
180 		if (vdepth() > 1) {
181 			WCOLS = TUBECOLS;
182 			sethard();
183 		} else
184 			WCOLS = TUBECOLS;
185 	} else if (state == HARDOPEN)
186 		sethard();
187 
188 	/*
189 	 * Unless we filled (completely) the last line we typed on,
190 	 * we have to clear to the end of the line
191 	 * in case stuff is left from before.
192 	 */
193 	if (vp->vliny + d > destline) {
194 		if (insert_null_glitch && destcol == WCOLS)
195 			vigoto(vp->vliny + d - 1, 0);
196 		vclreol();
197 	}
198 	return (d);
199 }
200 
201 /*
202  * Real work for winning growing of window at top
203  * when inserting in the middle of a partially full
204  * screen on an intelligent terminal.  We have as argument
205  * the logical line number to be inserted after, and the offset
206  * from that line where the insert will go.
207  * We look at the picture of depths and positions, and if we can
208  * delete some (blank) lines from the top of the screen so that
209  * later inserts will not push stuff off the bottom.
210  */
211 int
212 vglitchup(int l, int o)
213 {
214 	struct vlinfo *vp = &vlinfo[l];
215 	int need;
216 	int p = vp->vliny;
217 	short oldhold, oldheldech;
218 	bool glitched = 0;
219 
220  	if (l < vcnt - 1) {
221 		need = p + vp->vdepth - (vp+1)->vliny;
222 		if (need > 0) {
223 			if (state == VISUAL && WTOP - ZERO >= need && insert_line && delete_line) {
224 				glitched++;
225 				WTOP -= need;
226 				WLINES = WBOT - WTOP + 1;
227 				p -= need;
228 				if (p + o == WTOP) {
229 					vp->vliny = WTOP;
230 					return (WTOP + o);
231 				}
232 				vdellin(WTOP, need, -1);
233 				oldheldech = heldech;
234 				oldhold = hold;
235 				hold |= HOLDECH;
236 			}
237 			vinslin((vp+1)->vliny, need, l);
238 			if (glitched) {
239 				hold = oldhold;
240 				heldech = oldheldech;
241 			}
242 		}
243 	} else
244 		vp[1].vliny = vp[0].vliny + vp->vdepth;
245 	return (p + o);
246 }
247 
248 /*
249  * Insert cnt blank lines before line p,
250  * logically and (if supported) physically.
251  */
252 void
253 vinslin(int p, int cnt, int l)
254 {
255 	int i;
256 	bool could = 1;
257 
258 #ifdef ADEBUG
259 	if (trace)
260 		tfixnl(), fprintf(trace, "vinslin(%d, %d, %d)\n", p, cnt, l);
261 #endif
262 	if (p + cnt > WBOT && clr_eos) {
263 		/*
264 		 * Really quick -- clear to end of screen.
265 		 */
266 		cnt = WECHO + 1 - p;
267 		vgoto(p, 0), vputp(clr_eos, cnt);
268 		vclrech(1);
269 		vadjAL(p, cnt);
270 	} else if (scroll_reverse && p == WTOP && costSR < costAL) {
271 		/*
272 		 * Use reverse scroll mode of the terminal, at
273 		 * the top of the window.  Reverse linefeed works
274 		 * too, since we only use it from line WTOP.
275 		 */
276 		for (i = cnt; i > 0; i--) {
277 			vgoto(p, 0), vputp(scroll_reverse, 0);
278 			if (i > 1 && (hold & HOLDAT) == 0)
279 				putchar('@');
280 			/*
281 			 * If we are at the top of the screen, and the
282 			 * terminal retains display above, then we
283 			 * should try to clear to end of line.
284 			 * Have to use clr_eol since we don't remember what is
285 			 * actually on the line.
286 			 */
287 			if (clr_eol && (memory_above || p != 0))
288 				vputp(clr_eol, 1);
289 		}
290 		vadjAL(p, cnt);
291 	} else if (insert_line) {
292 		/*
293 		 * Use insert line.
294 		 */
295 		vgoto(p, 0);
296 		if (parm_insert_line && (cnt>1 || *insert_line==0)) {
297 			/* insert cnt lines.  Should do @'s too. */
298 			vputp(tparm(parm_insert_line, cnt, p), WECHO+1-p);
299 		}
300 		else if (change_scroll_region && *insert_line==0) {
301 			/* vt100 change scrolling region to fake insert_line */
302 			vputp(save_cursor, 1);
303 			vputp(tparm(change_scroll_region, p, lines-1), 1);
304 			vputp(restore_cursor, 1);	/* change_scroll_region homes stupid cursor */
305 			for (i=cnt; i>0; i--)
306 				vputp(scroll_reverse, 1);	/* should do @'s */
307 			vputp(tparm(change_scroll_region, 0, lines-1), 1);
308 			vputp(restore_cursor, 1);	/* Once again put it back */
309 		}
310 		else {
311 			vputp(insert_line, WECHO + 1 - p);
312 			for (i = cnt - 1; i > 0; i--) {
313 				vgoto(outline+1, 0);
314 				vputp(insert_line, WECHO + 1 - outline);
315 				if ((hold & HOLDAT) == 0)
316 					putchar('@');
317 			}
318 		}
319 		vadjAL(p, cnt);
320 	} else
321 		could = 0;
322 	vopenup(cnt, could, l);
323 }
324 
325 /*
326  * Logically open up after line l, cnt of them.
327  * We need to know if it was done ``physically'' since in this
328  * case we accept what the hardware gives us.  If we have to do
329  * it ourselves (brute force) we will squish out @ lines in the process
330  * if this will save us work.
331  */
332 void
333 vopenup(int cnt, bool could, int l)
334 {
335 	struct vlinfo *vc = &vlinfo[l + 1];
336 	struct vlinfo *ve = &vlinfo[vcnt];
337 
338 #ifdef ADEBUG
339 	if (trace)
340 		tfixnl(), fprintf(trace, "vopenup(%d, %d, %d)\n", cnt, could, l);
341 #endif
342 	if (could)
343 		/*
344 		 * This will push @ lines down the screen,
345 		 * just as the hardware did.  Since the default
346 		 * for intelligent terminals is to never have @
347 		 * lines on the screen, this should never happen,
348 		 * and the code makes no special effort to be nice in this
349 		 * case, e.g. squishing out the @ lines by delete lines
350 		 * before doing append lines.
351 		 */
352 		for (; vc <= ve; vc++)
353 			vc->vliny += cnt;
354 	else {
355 		/*
356 		 * Will have to clean up brute force eventually,
357 		 * so push the line data around as little as possible.
358 		 */
359 		vc->vliny += cnt, vc->vflags |= VDIRT;
360 		while (vc < ve) {
361 			int i = vc->vliny + vc->vdepth;
362 
363 			vc++;
364 			if (i <= vc->vliny)
365 				break;
366 			vc->vliny = i, vc->vflags |= VDIRT;
367 		}
368 	}
369 	vscrap();
370 }
371 
372 /*
373  * Adjust data structure internally to account for insertion of
374  * blank lines on the screen.
375  */
376 void
377 vadjAL(int p, int cnt)
378 {
379 	wchar_t *tlines[TUBELINES];
380 	int from, to;
381 
382 #ifdef ADEBUG
383 	if (trace)
384 		tfixnl(), fprintf(trace, "vadjal(%d, %d)\n", p, cnt);
385 #endif
386 	copy(tlines, vtube, sizeof vtube);	/*SASSIGN*/
387 	for (from = p, to = p + cnt; to <= WECHO; from++, to++)
388 		vtube[to] = tlines[from];
389 	for (to = p; from <= WECHO; from++, to++) {
390 		vtube[to] = tlines[from];
391 		vclrbyte(vtube[to], WCOLS);
392 	}
393 	/*
394 	 * Have to clear the echo area since its contents aren't
395 	 * necessarily consistent with the rest of the display.
396 	 */
397 	vclrech(0);
398 }
399 
400 /*
401  * Roll the screen up logically and physically
402  * so that line dl is the bottom line on the screen.
403  */
404 void
405 vrollup(int dl)
406 {
407 	int cnt;
408 	int dc = destcol;
409 
410 #ifdef ADEBUG
411 	if (trace)
412 		tfixnl(), fprintf(trace, "vrollup(%d)\n", dl);
413 #endif
414 	cnt = dl - (splitw ? WECHO : WBOT);
415 	if (splitw && (state == VISUAL || state == CRTOPEN))
416 		holdupd = 1;
417 	vmoveitup(cnt, 1);
418 	vscroll(cnt);
419 	destline = dl - cnt, destcol = dc;
420 }
421 
422 void
423 vup1(void)
424 {
425 
426 	vrollup(WBOT + 1);
427 }
428 
429 /*
430  * Scroll the screen up cnt lines physically.
431  * If doclr is true, do a clear eol if the terminal
432  * has standout (to prevent it from scrolling up)
433  */
434 void
435 vmoveitup(int cnt, bool doclr)
436 {
437 
438 	if (cnt == 0)
439 		return;
440 #ifdef ADEBUG
441 	if (trace)
442 		tfixnl(), fprintf(trace, "vmoveitup(%d)\n", cnt);
443 #endif
444 	if (doclr)
445 		vclrech(0);
446 	if (scroll_forward) {
447 		destline = WECHO;
448 		destcol = (NONL ? 0 : outcol % WCOLS);
449 		fgoto();
450 		while (cnt > 0)
451 			vputp(scroll_forward, 0), cnt--;
452 	}
453 	else {
454 		destline = WECHO + cnt;
455 		destcol = (NONL ? 0 : outcol % WCOLS);
456 		fgoto();
457 		if (state == ONEOPEN || state == HARDOPEN) {
458 			outline = destline = 0;
459 			vclrbyte(vtube[0], WCOLS);
460 		}
461 	}
462 	/* Get rid of line we just rolled up */
463 	if (doclr && memory_below && clr_eol)
464 		vclrech(0);
465 }
466 
467 /*
468  * Scroll the screen up cnt lines logically.
469  */
470 void
471 vscroll(int cnt)
472 {
473 	int from, to;
474 	wchar_t *tlines[TUBELINES];
475 
476 #ifdef ADEBUG
477 	if (trace)
478 		fprintf(trace, "vscroll(%d)\n", cnt);
479 #endif
480 	if (cnt < 0 || cnt > TUBELINES)
481 		error(gettext("Internal error: vscroll"));
482 	if (cnt == 0)
483 		return;
484 	copy(tlines, vtube, sizeof vtube);
485 	for (to = ZERO, from = ZERO + cnt; to <= WECHO - cnt; to++, from++)
486 		vtube[to] = tlines[from];
487 	for (from = ZERO; to <= WECHO; to++, from++) {
488 		vtube[to] = tlines[from];
489 		vclrbyte(vtube[to], WCOLS);
490 	}
491 	for (from = 0; from <= vcnt; from++)
492 		LINE(from) -= cnt;
493 }
494 
495 /*
496  * Discard logical lines due to physical wandering off the screen.
497  */
498 void
499 vscrap(void)
500 {
501 	int i, j;
502 
503 #ifdef ADEBUG
504 	if (trace)
505 		tfixnl(), fprintf(trace, "vscrap\n"), tvliny();
506 #endif
507 	if (splitw)
508 		return;
509 	if (vcnt && WBOT != WECHO && LINE(0) < WTOP && LINE(0) >= ZERO) {
510 		WTOP = LINE(0);
511 		WLINES = WBOT - WTOP + 1;
512 	}
513 	for (j = 0; j < vcnt; j++)
514 		if (LINE(j) >= WTOP) {
515 			if (j == 0)
516 				break;
517 			/*
518 			 * Discard the first j physical lines off the top.
519 			 */
520 			vcnt -= j, vcline -= j;
521 			for (i = 0; i <= vcnt; i++)
522 				vlcopy(vlinfo[i], vlinfo[i + j]);
523 			break;
524 		}
525 	/*
526 	 * Discard lines off the bottom.
527 	 */
528 	if (vcnt) {
529 		for (j = 0; j <= vcnt; j++)
530 			if (LINE(j) > WBOT || LINE(j) + DEPTH(j) - 1 > WBOT) {
531 				vcnt = j;
532 				break;
533 			}
534 		if (vcnt == 0)
535 			LASTLINE = 0;
536 		else
537 			LASTLINE = LINE(vcnt-1) + DEPTH(vcnt-1);
538 	}
539 #ifdef ADEBUG
540 	if (trace)
541 		tvliny();
542 #endif
543 	/*
544 	 * May have no lines!
545 	 */
546 }
547 
548 /*
549  * Repaint the screen, with cursor at curs, aftern an arbitrary change.
550  * Handle notification on large changes.
551  */
552 void
553 vrepaint(unsigned char *curs)
554 {
555 
556 	wdot = NOLINE;
557 	/*
558 	 * In open want to notify first.
559 	 */
560 	noteit(0);
561 	vscrap();
562 
563 	/*
564 	 * Deal with a totally useless display.
565 	 */
566 	if (vcnt == 0 || vcline < 0 || vcline > vcnt || holdupd && state != VISUAL) {
567 		line *odol = dol;
568 
569 		vcnt = 0;
570 		if (holdupd)
571 			if (state == VISUAL)
572 				(void)peekkey();
573 			else
574 				vup1();
575 		holdupd = 0;
576 		if (odol == zero)
577 			fixzero();
578 		vcontext(dot, '.');
579 		noteit(1);
580 		if (noteit(1) == 0 && odol == zero) {
581 			CATCH
582 				error(gettext("No lines in buffer"));
583 			ENDCATCH
584 			linebuf[0] = 0;
585 			splitw = 0;
586 		}
587 		vnline(curs);
588 		return;
589 	}
590 
591 	/*
592 	 * Have some useful displayed text; refresh it.
593 	 */
594 	getDOT();
595 
596 	/*
597 	 * This is for boundary conditions in open mode.
598 	 */
599 	if (FLAGS(0) & VDIRT)
600 		vsync(WTOP);
601 
602 	/*
603 	 * If the current line is after the last displayed line
604 	 * or the bottom of the screen, then special effort is needed
605 	 * to get it on the screen.  We first try a redraw at the
606 	 * last line on the screen, hoping it will fill in where @
607 	 * lines are now.  If this doesn't work, then roll it onto
608 	 * the screen.
609 	 */
610 	if (vcline >= vcnt || LINE(vcline) > WBOT) {
611 		short oldhold = hold;
612 		hold |= HOLDAT, vredraw(LASTLINE), hold = oldhold;
613 		if (vcline >= vcnt) {
614 			int i = vcline - vcnt + 1;
615 
616 			dot -= i;
617 			vcline -= i;
618 			vroll(i);
619 		} else
620 			vsyncCL();
621 	} else
622 		vsync(vcline > 0 ? LINE(vcline - 1) : WTOP);
623 
624 	/*
625 	 * Notification on large change for visual
626 	 * has to be done last or we may lose
627 	 * the echo area with redisplay.
628 	 */
629 	noteit(1);
630 
631 	/*
632 	 * Finally.  Move the cursor onto the current line.
633 	 */
634 	vnline(curs);
635 }
636 
637 /*
638  * Fully cleanup the screen, leaving no @ lines except at end when
639  * line after last won't completely fit.  The routine vsync is
640  * more conservative and much less work on dumb terminals.
641  */
642 void
643 vredraw(int p)
644 {
645 	int l;
646 	line *tp;
647 	unsigned char temp[LBSIZE];
648 	bool anydl = 0;
649 	short oldhold = hold;
650 
651 #ifdef ADEBUG
652 	if (trace)
653 		tfixnl(), fprintf(trace, "vredraw(%d)\n", p), tvliny();
654 #endif
655 	if (holdupd) {
656 		holdupd = 3;
657 		return;
658 	}
659 	if (state == HARDOPEN || splitw)
660 		return;
661 	if (p < 0 /* || p > WECHO */)
662 		error(gettext("Internal error: vredraw"));
663 
664 	/*
665 	 * Trim the ragged edges (lines which are off the screen but
666 	 * not yet logically discarded), save the current line, and
667 	 * search for first logical line affected by the redraw.
668 	 */
669 	vscrap();
670 	CP(temp, linebuf);
671 	l = 0;
672 	tp = dot - vcline;
673 	if (vcnt == 0)
674 		LINE(0) = WTOP;
675 	while (l < vcnt && LINE(l) < p)
676 		l++, tp++;
677 
678 	/*
679 	 * We hold off echo area clearing during the redraw in deference
680 	 * to a final clear of the echo area at the end if appropriate.
681 	 */
682 	heldech = 0;
683 	hold |= HOLDECH;
684 	for (; l < vcnt && Peekkey != ATTN; l++) {
685 		if (l == vcline)
686 			strcLIN(temp);
687 		else
688 			getline(*tp);
689 
690 		/*
691 		 * Delete junk between displayed lines.
692 		 */
693 		if (LINE(l) != LINE(l + 1) && LINE(l) != p) {
694 			if (anydl == 0 && memory_below && clr_eos) {
695 				hold = oldhold;
696 				vclrech(0);
697 				anydl = 1;
698 				hold |= HOLDECH;
699 				heldech = 0;
700 			}
701 			vdellin(p, LINE(l) - p, l);
702 		}
703 
704 		/*
705 		 * If line image is not know to be up to date, then
706 		 * redisplay it;  else just skip onward.
707 		 */
708 		LINE(l) = p;
709 		if (FLAGS(l) & VDIRT) {
710 			DEPTH(l) = vdepth();
711 			if (l != vcline && p + DEPTH(l) - 1 > WBOT) {
712 				vscrap();
713 				break;
714 			}
715 			FLAGS(l) &= ~VDIRT;
716 			(void) vreopen(p, lineno(tp), l);
717 			p = LINE(l) + DEPTH(l);
718 		} else
719 			p += DEPTH(l);
720 		tp++;
721 	}
722 
723 	/*
724 	 * That takes care of lines which were already partially displayed.
725 	 * Now try to fill the rest of the screen with text.
726 	 */
727 	if (state == VISUAL && p <= WBOT) {
728 		int ovcline = vcline;
729 
730 		vcline = l;
731 		for (; tp <= dol && Peekkey != ATTN; tp++) {
732 			getline(*tp);
733 			if (p + vdepth() - 1 > WBOT)
734 				break;
735 			vopen(tp, p);
736 			p += DEPTH(vcline);
737 			vcline++;
738 		}
739 		vcline = ovcline;
740 	}
741 
742 	/*
743 	 * That's all the text we can get on.
744 	 * Now rest of lines (if any) get either a ~ if they
745 	 * are past end of file, or an @ if the next line won't fit.
746 	 */
747 	for (; p <= WBOT && Peekkey != ATTN; p++)
748 		vclrlin(p, tp);
749 	strcLIN(temp);
750 	hold = oldhold;
751 	if (heldech)
752 		vclrech(0);
753 #ifdef ADEBUG
754 	if (trace)
755 		tvliny();
756 #endif
757 }
758 
759 /*
760  * Do the real work in deleting cnt lines starting at line p from
761  * the display.  First affected line is line l.
762  */
763 void
764 vdellin(int p, int cnt, int l)
765 {
766 	int i;
767 
768 	if (cnt == 0)
769 		return;
770 	if (delete_line == NOSTR || cnt < 0) {
771 		/*
772 		 * Can't do it; just remember that line l is munged.
773 		 */
774 		FLAGS(l) |= VDIRT;
775 		return;
776 	}
777 #ifdef ADEBUG
778 	if (trace)
779 		tfixnl(), fprintf(trace, "vdellin(%d, %d, %d)\n", p, cnt, l);
780 #endif
781 	/*
782 	 * Send the deletes to the screen and then adjust logical
783 	 * and physical internal data structures.
784 	 */
785 	vgoto(p, 0);
786 	if (parm_delete_line && (cnt>1 || *delete_line==0)) {
787 		vputp(tparm(parm_delete_line, cnt, p), WECHO-p);
788 	}
789 	else if (change_scroll_region && *delete_line==0) {
790 		/* vt100: fake delete_line by changing scrolling region */
791 		vputp(save_cursor, 1);	/* Save since change_scroll_region homes stupid cursor */
792 		vputp(tparm(change_scroll_region, p, lines-1), 1);
793 		vputp(tparm(cursor_address, lines-1, 0), 1);/* Go to lower left corner */
794 		for (i=0; i<cnt; i++)		/* .. and scroll cnt times */
795 			(void) putch('\n');	/* should check NL too */
796 		vputp(tparm(change_scroll_region, 0, lines-1), 1);/* restore scrolling region */
797 		vputp(restore_cursor, 1);			/* put cursor back */
798 	}
799 	else {
800 		for (i = 0; i < cnt; i++)
801 			vputp(delete_line, WECHO - p);
802 	}
803 	vadjDL(p, cnt);
804 	vcloseup(l, cnt);
805 }
806 /*
807  * Adjust internal physical screen image to account for deleted lines.
808  */
809 void
810 vadjDL(int p, int cnt)
811 {
812 	wchar_t *tlines[TUBELINES];
813 	int from, to;
814 
815 #ifdef ADEBUG
816 	if (trace)
817 		tfixnl(), fprintf(trace, "vadjDL(%d, %d)\n", p, cnt);
818 #endif
819 	/*
820 	 * Would like to use structured assignment but early
821 	 * v7 compiler (released with phototypesetter for v6)
822 	 * can't hack it.
823 	 */
824 	copy(tlines, vtube, sizeof vtube);	/*SASSIGN*/
825 	for (from = p + cnt, to = p; from <= WECHO; from++, to++)
826 		vtube[to] = tlines[from];
827 	for (from = p; to <= WECHO; from++, to++) {
828 		vtube[to] = tlines[from];
829 		vclrbyte(vtube[to], WCOLS);
830 	}
831 }
832 /*
833  * Sync the screen, like redraw but more lazy and willing to leave
834  * @ lines on the screen.  VsyncCL syncs starting at the current line.
835  * In any case, if the redraw option is set then all syncs map to redraws
836  * as if vsync didn't exist.
837  */
838 void
839 vsyncCL(void)
840 {
841 
842 	vsync(LINE(vcline));
843 }
844 
845 void
846 vsync(int p)
847 {
848 
849 	if (value(vi_REDRAW))
850 		vredraw(p);
851 	else
852 		vsync1(p);
853 }
854 
855 /*
856  * The guts of a sync.  Similar to redraw but
857  * just less ambitious.
858  */
859 void
860 vsync1(int p)
861 {
862 	int l;
863 	unsigned char temp[LBSIZE];
864 	struct vlinfo *vp = &vlinfo[0];
865 	short oldhold = hold;
866 
867 #ifdef ADEBUG
868 	if (trace)
869 		tfixnl(), fprintf(trace, "vsync1(%d)\n", p), tvliny();
870 #endif
871 	if (holdupd) {
872 		if (holdupd < 3)
873 			holdupd = 2;
874 		return;
875 	}
876 	if (state == HARDOPEN || splitw)
877 		return;
878 	vscrap();
879 	CP(temp, linebuf);
880 	if (vcnt == 0)
881 		LINE(0) = WTOP;
882 	l = 0;
883 	while (l < vcnt && vp->vliny < p)
884 		l++, vp++;
885 	heldech = 0;
886 	hold |= HOLDECH;
887 	while (p <= WBOT && Peekkey != ATTN) {
888 		/*
889 		 * Want to put a line here if not in visual and first line
890 		 * or if there are lies left and this line starts before
891 		 * the current line, or if this line is piled under the
892 		 * next line (vreplace does this and we undo it).
893 		 */
894 		if (l == 0 && state != VISUAL ||
895 		    (l < vcnt && (vp->vliny <= p || vp[0].vliny == vp[1].vliny))) {
896 			if (l == 0 || vp->vliny < p || (vp->vflags & VDIRT)) {
897 				if (l == vcline)
898 					strcLIN(temp);
899 				else
900 					getline(dot[l - vcline]);
901 				/*
902 				 * Be careful that a long line doesn't cause the
903 				 * screen to shoot up.
904 				 */
905 				if (l != vcline && (vp->vflags & VDIRT)) {
906 					vp->vdepth = vdepth();
907 					vp->vflags &= ~VDIRT;
908 					if (p + vp->vdepth - 1 > WBOT)
909 						break;
910 				}
911 				(void) vreopen(p, lineDOT() + (l - vcline), l);
912 			}
913 			p = vp->vliny + vp->vdepth;
914 			vp++;
915 			l++;
916 		} else
917 			/*
918 			 * A physical line between logical lines,
919 			 * so we settle for an @ at the beginning.
920 			 */
921 			vclrlin(p, dot + (l - vcline)), p++;
922 	}
923 	strcLIN(temp);
924 	hold = oldhold;
925 	if (heldech)
926 		vclrech(0);
927 }
928 
929 /*
930  * Subtract (logically) cnt physical lines from the
931  * displayed position of lines starting with line l.
932  */
933 void
934 vcloseup(int l, int cnt)
935 {
936 	int i;
937 
938 #ifdef ADEBUG
939 	if (trace)
940 		tfixnl(), fprintf(trace, "vcloseup(%d, %d)\n", l, cnt);
941 #endif
942 	for (i = l + 1; i <= vcnt; i++)
943 		LINE(i) -= cnt;
944 }
945 
946 /*
947  * Workhorse for rearranging line descriptors on changes.
948  * The idea here is that, starting with line l, cnt lines
949  * have been replaced with newcnt lines.  All of these may
950  * be ridiculous, i.e. l may be -1000, cnt 50 and newcnt 0,
951  * since we may be called from an undo after the screen has
952  * moved a lot.  Thus we have to be careful.
953  *
954  * Many boundary conditions here.
955  */
956 void
957 vreplace(int l, int cnt, int newcnt)
958 {
959 	int from, to, i;
960 	bool savenote = 0;
961 
962 #ifdef ADEBUG
963 	if (trace) {
964 		tfixnl(), fprintf(trace, "vreplace(%d, %d, %d)\n", l, cnt, newcnt);
965 		tvliny();
966 	}
967 #endif
968 	if (l >= vcnt)
969 		return;
970 	if (l < 0) {
971 		if (l + cnt < 0) {
972 			/*
973 			 * Nothing on the screen is relevant.
974 			 * Settle for redrawing from scratch (later).
975 			 */
976 			vcnt = 0;
977 			return;
978 		}
979 		/*
980 		 * Normalize l to top of screen; the add is
981 		 * really a subtract from cnt since l is negative.
982 		 */
983 		cnt += l;
984 		l = 0;
985 
986 		/*
987 		 * Unseen lines were affected so notify (later).
988 		 */
989 		savenote++;
990 	}
991 
992 	/*
993 	 * These shouldn't happen
994 	 * but would cause great havoc.
995 	 */
996 	if (cnt < 0)
997 		cnt = 0;
998 	if (newcnt < 0)
999 		newcnt = 0;
1000 
1001 	/*
1002 	 * Surely worthy of note if more than report
1003 	 * lines were changed.
1004 	 */
1005 	if (cnt > value(vi_REPORT) || newcnt > value(vi_REPORT))
1006 		savenote++;
1007 
1008 	/*
1009 	 * Same number of lines affected as on screen, and we
1010 	 * can insert and delete lines.  Thus we just type
1011 	 * over them, since otherwise we will push them
1012 	 * slowly off the screen, a clear lose.
1013 	 */
1014 	if (cnt == newcnt || vcnt - l == newcnt && insert_line && delete_line) {
1015 		if (cnt > 1 && l + cnt > vcnt)
1016 			savenote++;
1017 		vdirty(l, newcnt);
1018 	} else {
1019 		/*
1020 		 * Lines are going away, squish them out.
1021 		 */
1022 		if (cnt > 0) {
1023 			/*
1024 			 * If non-displayed lines went away,
1025 			 * always notify.
1026 			 */
1027 			if (cnt > 1 && l + cnt > vcnt)
1028 				savenote++;
1029 			if (l + cnt >= vcnt)
1030 				cnt = vcnt - l;
1031 			else
1032 				for (from = l + cnt, to = l; from <= vcnt; to++, from++)
1033 					vlcopy(vlinfo[to], vlinfo[from]);
1034 			vcnt -= cnt;
1035 		}
1036 		/*
1037 		 * Open up space for new lines appearing.
1038 		 * All new lines are piled in the same place,
1039 		 * and will be unpiled by vredraw/vsync, which
1040 		 * inserts lines in front as it unpiles.
1041 		 */
1042 		if (newcnt > 0) {
1043 			/*
1044 			 * Newlines are appearing which may not show,
1045 			 * so notify (this is only approximately correct
1046 			 * when long lines are present).
1047 			 */
1048 			if (newcnt > 1 && l + newcnt > vcnt + 1)
1049 				savenote++;
1050 
1051 			/*
1052 			 * If there will be more lines than fit, then
1053 			 * just throw way the rest of the stuff on the screen.
1054 			 */
1055 			if (l + newcnt > WBOT && insert_line && delete_line) {
1056 				vcnt = l;
1057 				goto skip;
1058 			}
1059 			from = vcnt, to = vcnt + newcnt;
1060 			i = TUBELINES - to;
1061 			if (i < 0)
1062 				from += i, to += i;
1063 			vcnt = to;
1064 			for (; from >= l; from--, to--)
1065 				vlcopy(vlinfo[to], vlinfo[from]);
1066 			for (from = to + 1, to = l; to < l + newcnt && to <= WBOT + 1; to++) {
1067 				LINE(to) = LINE(from);
1068 				DEPTH(to) = 0;
1069 				FLAGS(to) = VDIRT;
1070 			}
1071 		}
1072 	}
1073 skip:
1074 	if (Pline == numbline && cnt != newcnt)
1075 		/*
1076 		 * When lines positions are shifted, the numbers
1077 		 * will be wrong.
1078 		 */
1079 		vdirty(l, WECHO);
1080 	if (!savenote)
1081 		notecnt = 0;
1082 #ifdef ADEBUG
1083 	if (trace)
1084 		tvliny();
1085 #endif
1086 }
1087 
1088 /*
1089  * Start harcopy open.
1090  * Print an image of the line to the left of the cursor
1091  * under the full print of the line and position the cursor.
1092  * If we are in a scroll ^D within hardcopy open then all this
1093  * is suppressed.
1094  */
1095 void
1096 sethard(void)
1097 {
1098 
1099 	if (state == VISUAL)
1100 		return;
1101 	rubble = 0;
1102 	state = HARDOPEN;
1103 	if (hold & HOLDROL)
1104 		return;
1105 	vup1();
1106 	LINE(0) = WBOT;
1107 	if (Pline == numbline)
1108 		vgoto(WBOT, 0), viprintf("%6d  ", lineDOT());
1109 }
1110 
1111 /*
1112  * Mark the lines starting at base for i lines
1113  * as dirty so that they will be checked for correct
1114  * display at next sync/redraw.
1115  */
1116 void
1117 vdirty(int base, int i)
1118 {
1119 	int l;
1120 
1121 	for (l = base; l < vcnt; l++) {
1122 		if (--i < 0)
1123 			return;
1124 		FLAGS(l) |= VDIRT;
1125 	}
1126 }
1127