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