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