xref: /386bsd/usr/src/usr.bin/elvis/redraw.c (revision a2142627)
1 /* redraw.c */
2 
3 /* Author:
4  *	Steve Kirkendall
5  *	14407 SW Teal Blvd. #C
6  *	Beaverton, OR 97005
7  *	kirkenda@cs.pdx.edu
8  */
9 
10 
11 /* This file contains functions that draw text on the screen.  The major entry
12  * points are:
13  *	redrawrange()	- called from modify.c to give hints about what parts
14  *			  of the screen need to be redrawn.
15  *	redraw()	- redraws the screen (or part of it) and positions
16  *			  the cursor where it belongs.
17  *	idx2col()	- converts a markidx() value to a logical column number.
18  */
19 
20 #include "config.h"
21 #include "vi.h"
22 
23 /* This variable contains the line number that smartdrawtext() knows best */
24 static long smartlno;
25 
26 /* This function remembers where changes were made, so that the screen can be
27  * redraw in a more efficient manner.
28  */
29 static long	redrawafter;	/* line# of first line that must be redrawn */
30 static long	preredraw;	/* line# of last line changed, before change */
31 static long	postredraw;	/* line# of last line changed, after change */
32 static int	mustredraw;	/* boolean: anything forcing a screen update? */
redrawrange(after,pre,post)33 void redrawrange(after, pre, post)
34 	long	after;	/* lower bound of redrawafter */
35 	long	pre;	/* upper bound of preredraw */
36 	long	post;	/* upper bound of postredraw */
37 {
38 	if (after == redrawafter)
39 	{
40 		/* multiple insertions/deletions at the same place -- combine
41 		 * them
42 		 */
43 		preredraw -= (post - pre);
44 		if (postredraw < post)
45 		{
46 			preredraw += (post - postredraw);
47 			postredraw = post;
48 		}
49 		if (redrawafter > preredraw)
50 		{
51 			redrawafter = preredraw;
52 		}
53 		if (redrawafter < 1L)
54 		{
55 			redrawafter = 0L;
56 			preredraw = postredraw = INFINITY;
57 		}
58 	}
59 	else if (postredraw > 0L)
60 	{
61 		/* multiple changes in different places -- redraw everything
62 		 * after "after".
63 		 */
64 		postredraw = preredraw = INFINITY;
65 		if (after < redrawafter)
66 			redrawafter = after;
67 	}
68 	else
69 	{
70 		/* first change */
71 		redrawafter = after;
72 		preredraw = pre;
73 		postredraw = post;
74 	}
75 	mustredraw = TRUE;
76 }
77 
78 
79 #ifndef NO_CHARATTR
80 /* see if a given line uses character attribute strings */
hasattr(lno,text)81 static int hasattr(lno, text)
82 	long		lno;	/* the line# of the cursor */
83 	REG char	*text;	/* the text of the line, from fetchline */
84 {
85 	static long	plno;	/* previous line number */
86 	static long	chgs;	/* previous value of changes counter */
87 	static int	panswer;/* previous answer */
88 	char		*scan;
89 
90 	/* if charattr is off, then the answer is "no, it doesn't" */
91 	if (!*o_charattr)
92 	{
93 		chgs = 0; /* <- forces us to check if charattr is later set */
94 		return FALSE;
95 	}
96 
97 	/* if we already know the answer, return it... */
98 	if (lno == plno && chgs == changes)
99 	{
100 		return panswer;
101 	}
102 
103 	/* get the line & look for "\fX" */
104 	if (!text[0] || !text[1] || !text[2])
105 	{
106 		panswer = FALSE;
107 	}
108 	else
109 	{
110 		for (scan = text; scan[2] && !(scan[0] == '\\' && scan[1] == 'f'); scan++)
111 		{
112 		}
113 		panswer = (scan[2] != '\0');
114 	}
115 
116 	/* save the results */
117 	plno = lno;
118 	chgs = changes;
119 
120 	/* return the results */
121 	return panswer;
122 }
123 #endif
124 
125 
126 #ifndef NO_VISIBLE
127 /* This function checks to make sure that the correct lines are shown in
128  * reverse-video.  This is used to handle the "v" and "V" commands.
129  */
130 static long	vizlow, vizhigh;	/* the starting and ending lines */
131 static int	vizleft, vizright;	/* starting & ending indicies */
132 static int	vizchange;		/* boolean: must use stupid drawtext? */
setviz(curs)133 static void setviz(curs)
134 	MARK		curs;
135 {
136 	long		newlow, newhigh;
137 	long		extra = 0L;
138 
139 	/* for now, assume the worst... */
140 	vizchange = TRUE;
141 
142 	/* set newlow & newhigh according to V_from and cursor */
143 	if (!V_from)
144 	{
145 		/* no lines should have reverse-video */
146 		if (vizlow)
147 		{
148 			redrawrange(vizlow, vizhigh + 1L, vizhigh + 1L);
149 			vizlow = vizhigh = 0L;
150 		}
151 		else
152 		{
153 			vizchange = FALSE;
154 		}
155 		return;
156 	}
157 
158 	/* figure out which lines *SHOULD* have hilites */
159 	if (V_from < curs)
160 	{
161 		newlow = markline(V_from);
162 		newhigh = markline(curs);
163 		vizleft = markidx(V_from);
164 		vizright = markidx(curs) + 1;
165 	}
166 	else
167 	{
168 		newlow = markline(curs);
169 		newhigh = markline(V_from);
170 		vizleft = markidx(curs);
171 		vizright = markidx(V_from) + 1;
172 	}
173 
174 	/* adjust for line-mode hiliting */
175 	if (V_linemd)
176 	{
177 		vizleft = 0;
178 		vizright = BLKSIZE - 1;
179 	}
180 	else
181 	{
182 		extra = 1L;
183 	}
184 
185 	/* arrange for the necessary lines to be redrawn */
186 	if (vizlow == 0L)
187 	{
188 		/* just starting to redraw */
189 		redrawrange(newlow, newhigh, newhigh);
190 	}
191 	else
192 	{
193 		/* Were new lines added/removed at the front? */
194 		if (newlow != vizlow)
195 		{
196 			if (newlow < vizlow)
197 				redrawrange(newlow, vizlow + extra, vizlow + extra);
198 			else
199 				redrawrange(vizlow, newlow + extra, newlow + extra);
200 		}
201 
202 		/* Were new lines added/removed at the back? */
203 		if (newhigh != vizhigh)
204 		{
205 			if (newhigh < vizhigh)
206 				redrawrange(newhigh + 1L - extra, vizhigh + 1L, vizhigh + 1L);
207 			else
208 				redrawrange(vizhigh + 1L - extra, newhigh, newhigh);
209 		}
210 	}
211 
212 	/* remember which lines will contain hilighted text now */
213 	vizlow = newlow;
214 	vizhigh = newhigh;
215 }
216 #endif /* !NO_VISIBLE */
217 
218 
219 /* This function converts a MARK to a column number.  It doesn't automatically
220  * adjust for leftcol; that must be done by the calling function
221  */
idx2col(curs,text,inputting)222 int idx2col(curs, text, inputting)
223 	MARK		curs;	/* the line# & index# of the cursor */
224 	REG char	*text;	/* the text of the line, from fetchline */
225 	int		inputting;	/* boolean: called from input() ? */
226 {
227 	static MARK	pcursor;/* previous cursor, for possible shortcut */
228 	static MARK	pcol;	/* column number for pcol */
229 	static long	chgs;	/* previous value of changes counter */
230 	REG int		col;	/* used to count column numbers */
231 	REG int		idx;	/* used to count down the index */
232 	REG int		i;
233 
234 	/* for now, assume we have to start counting at the left edge */
235 	col = 0;
236 	idx = markidx(curs);
237 
238 	/* if the file hasn't changed & line number is the same & it has no
239 	 * embedded character attribute strings, can we do shortcuts?
240 	 */
241 	if (chgs == changes
242 	 && !((curs ^ pcursor) & ~(BLKSIZE - 1))
243 #ifndef NO_CHARATTR
244 	 && !hasattr(markline(curs), text)
245 #endif
246 	)
247 	{
248 		/* no movement? */
249 		if (curs == pcursor)
250 		{
251 			/* return the column of the char; for tabs, return its last column */
252 			if (text[idx] == '\t' && !inputting && !*o_list)
253 			{
254 				return pcol + *o_tabstop - (pcol % *o_tabstop) - 1;
255 			}
256 			else
257 			{
258 				return pcol;
259 			}
260 		}
261 
262 		/* movement to right? */
263 		if (curs > pcursor)
264 		{
265 			/* start counting from previous place */
266 			col = pcol;
267 			idx = markidx(curs) - markidx(pcursor);
268 			text += markidx(pcursor);
269 		}
270 	}
271 
272 	/* count over to the char after the idx position */
273 	while (idx > 0 && (i = *text)) /* yes, ASSIGNMENT! */
274 	{
275 		if (i == '\t' && !*o_list)
276 		{
277 			col += *o_tabstop;
278 			col -= col % *o_tabstop;
279 		}
280 		else if (i >= '\0' && i < ' ' || i == '\177')
281 		{
282 			col += 2;
283 		}
284 #ifndef NO_CHARATTR
285 		else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr)
286 		{
287 			text += 2; /* plus one more at bottom of loop */
288 			idx -= 2;
289 		}
290 #endif
291 		else
292 		{
293 			col++;
294 		}
295 		text++;
296 		idx--;
297 	}
298 
299 	/* save stuff to speed next call */
300 	pcursor = curs;
301 	pcol = col;
302 	chgs = changes;
303 
304 	/* return the column of the char; for tabs, return its last column */
305 	if (*text == '\t' && !inputting && !*o_list)
306 	{
307 		return col + *o_tabstop - (col % *o_tabstop) - 1;
308 	}
309 	else
310 	{
311 		return col;
312 	}
313 }
314 
315 
316 /* This function is similar to idx2col except that it takes care of sideways
317  * scrolling - for the given line, at least.
318  */
mark2phys(m,text,inputting)319 int mark2phys(m, text, inputting)
320 	MARK	m;		/* a mark to convert */
321 	char	*text;		/* the line that m refers to */
322 	int	inputting;	/* boolean: caled from input() ? */
323 {
324 	int	i;
325 
326 	i = idx2col(m, text, inputting);
327 	while (i < leftcol)
328 	{
329 		leftcol -= *o_sidescroll;
330 		mustredraw = TRUE;
331 		redrawrange(1L, INFINITY, INFINITY);
332 	}
333 	while (i > rightcol)
334 	{
335 		leftcol += *o_sidescroll;
336 		mustredraw = TRUE;
337 		redrawrange(1L, INFINITY, INFINITY);
338 	}
339 	physrow = markline(m) - topline;
340 	physcol = i - leftcol;
341 	if (*o_number)
342 		physcol += 8;
343 
344 	return physcol;
345 }
346 
347 /* This function draws a single line of text on the screen.  The screen's
348  * cursor is assumed to be located at the leftmost column of the appropriate
349  * row.
350  */
drawtext(text,lno,clr)351 static void drawtext(text, lno, clr)
352 	REG char	*text;	/* the text to draw */
353 	long		lno;	/* the number of the line to draw */
354 	int		clr;	/* boolean: do a clrtoeol? */
355 {
356 	REG int		col;	/* column number */
357 	REG int		i;
358 	REG int		tabstop;	/* *o_tabstop */
359 	REG int		limitcol;	/* leftcol or leftcol + COLS */
360 	int		abnormal;	/* boolean: charattr != A_NORMAL? */
361 #ifndef NO_VISIBLE
362 	int		rev;		/* boolean: standout mode, too? */
363 	int		idx = 0;
364 #endif
365 	char		numstr[9];
366 
367 	/* show the line number, if necessary */
368 	if (*o_number)
369 	{
370 		sprintf(numstr, "%6ld |", lno);
371 		qaddstr(numstr);
372 	}
373 
374 #ifndef NO_SENTENCE
375 	/* if we're hiding format lines, and this is one of them, then hide it */
376 	if (*o_hideformat && *text == '.')
377 	{
378 		clrtoeol();
379 #if OSK
380 		qaddch('\l');
381 #else
382 		qaddch('\n');
383 #endif
384 		return;
385 	}
386 #endif
387 
388 	/* move some things into registers... */
389 	limitcol = leftcol;
390 	tabstop = *o_tabstop;
391 	abnormal = FALSE;
392 
393 #ifndef CRUNCH
394 	if (clr)
395 		clrtoeol();
396 #endif
397 
398 	/* skip stuff that was scrolled off left edge */
399 	for (col = 0;
400 	     (i = *text) && col < limitcol; /* yes, ASSIGNMENT! */
401 	     text++)
402 	{
403 #ifndef NO_VISIBLE
404 		idx++;
405 #endif
406 		if (i == '\t' && !*o_list)
407 		{
408 			col = col + tabstop - (col % tabstop);
409 		}
410 		else if (i >= 0 && i < ' ' || i == '\177')
411 		{
412 			col += 2;
413 		}
414 #ifndef NO_CHARATTR
415 		else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr)
416 		{
417 			text += 2; /* plus one more as part of "for" loop */
418 
419 			/* since this attribute might carry over, we need it */
420 			switch (*text)
421 			{
422 			  case 'R':
423 			  case 'P':
424 				attrset(A_NORMAL);
425 				abnormal = FALSE;
426 				break;
427 
428 			  case 'B':
429 				attrset(A_BOLD);
430 				abnormal = TRUE;
431 				break;
432 
433 			  case 'U':
434 				attrset(A_UNDERLINE);
435 				abnormal = TRUE;
436 				break;
437 
438 			  case 'I':
439 				attrset(A_ALTCHARSET);
440 				abnormal = TRUE;
441 				break;
442 			}
443 		}
444 #endif
445 		else
446 		{
447 			col++;
448 		}
449 	}
450 
451 #ifndef NO_VISIBLE
452 	/* Should we start hiliting at the first char of this line? */
453 	if ((lno > vizlow && lno <= vizhigh
454 	    || lno == vizlow && vizleft < idx)
455 	   && !(lno == vizhigh && vizright < idx))
456 	{
457 		do_VISIBLE();
458 		rev = TRUE;
459 	}
460 #endif
461 
462 	/* adjust for control char that was partially visible */
463 	while (col > limitcol)
464 	{
465 		qaddch(' ');
466 		limitcol++;
467 	}
468 
469 	/* now for the visible characters */
470 	limitcol = leftcol + COLS;
471 	if (*o_number)
472 		limitcol -= 8;
473 	for (; (i = *text) && col < limitcol; text++)
474 	{
475 #ifndef NO_VISIBLE
476 		/* maybe turn hilite on/off in the middle of the line */
477 		if (lno == vizlow && vizleft == idx)
478 		{
479 			do_VISIBLE();
480 			rev = TRUE;
481 		}
482 		if (lno == vizhigh && vizright == idx)
483 		{
484 			do_SE();
485 			rev = FALSE;
486 		}
487 		idx++;
488 
489 		/* if hiliting, never emit physical tabs */
490 		if (rev && i == '\t' && !*o_list)
491 		{
492 			i = col + tabstop - (col % tabstop);
493 			do
494 			{
495 				qaddch(' ');
496 				col++;
497 			} while (col < i);
498 		}
499 		else
500 #endif /* !NO_VISIBLE */
501 		if (i == '\t' && !*o_list)
502 		{
503 			i = col + tabstop - (col % tabstop);
504 			if (i < limitcol)
505 			{
506 #ifdef CRUNCH
507 				if (!clr && has_PT && !((i - leftcol) & 7))
508 #else
509 				if (has_PT && !((i - leftcol) & 7))
510 #endif
511 				{
512 					do
513 					{
514 						qaddch('\t');
515 						col += 8; /* not exact! */
516 					} while (col < i);
517 					col = i; /* NOW it is exact */
518 				}
519 				else
520 				{
521 					do
522 					{
523 						qaddch(' ');
524 						col++;
525 					} while (col < i);
526 				}
527 			}
528 			else /* tab ending after screen? next line! */
529 			{
530 				col = limitcol;
531 				if (has_AM)
532 				{
533 					addch('\n');	/* GB */
534 				}
535 			}
536 		}
537 		else if (i >= 0 && i < ' ' || i == '\177')
538 		{
539 			col += 2;
540 			qaddch('^');
541 			if (col <= limitcol)
542 			{
543 				qaddch(i ^ '@');
544 			}
545 		}
546 #ifndef NO_CHARATTR
547 		else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr)
548 		{
549 			text += 2; /* plus one more as part of "for" loop */
550 			switch (*text)
551 			{
552 			  case 'R':
553 			  case 'P':
554 				attrset(A_NORMAL);
555 				abnormal = FALSE;
556 				break;
557 
558 			  case 'B':
559 				attrset(A_BOLD);
560 				abnormal = TRUE;
561 				break;
562 
563 			  case 'U':
564 				attrset(A_UNDERLINE);
565 				abnormal = TRUE;
566 				break;
567 
568 			  case 'I':
569 				attrset(A_ALTCHARSET);
570 				abnormal = TRUE;
571 				break;
572 			}
573 		}
574 #endif
575 		else
576 		{
577 			col++;
578 			qaddch(i);
579 		}
580 	}
581 
582 	/* get ready for the next line */
583 #ifndef NO_CHARATTR
584 	if (abnormal)
585 	{
586 		attrset(A_NORMAL);
587 	}
588 #endif
589 	if (*o_list && col < limitcol)
590 	{
591 		qaddch('$');
592 		col++;
593 	}
594 
595 #ifndef NO_VISIBLE
596 	/* did we hilite this whole line?  If so, STOP! */
597 	if (rev)
598 	{
599 		do_SE();
600 	}
601 #endif
602 
603 #ifdef CRUNCH
604 	if (clr && col < limitcol)
605 	{
606 		clrtoeol();
607 	}
608 #endif
609 	if (!has_AM || col < limitcol)
610 	{
611 		addch('\n');
612 	}
613 
614 	wqrefresh();
615 }
616 
617 
618 #ifndef CRUNCH
nudgecursor(same,scan,new,lno)619 static void nudgecursor(same, scan, new, lno)
620 	int	same;	/* number of chars to be skipped over */
621 	char	*scan;	/* where the same chars end */
622 	char	*new;	/* where the visible part of the line starts */
623 	long	lno;	/* line number of this line */
624 {
625 	int	col;
626 
627 	if (same > 0)
628 	{
629 		if (same < 5)
630 		{
631 			/* move the cursor by overwriting */
632 			while (same > 0)
633 			{
634 				qaddch(scan[-same]);
635 				same--;
636 			}
637 		}
638 		else
639 		{
640 			/* move the cursor by calling move() */
641 			col = (int)(scan - new);
642 			if (*o_number)
643 				col += 8;
644 			move((int)(lno - topline), col);
645 		}
646 	}
647 }
648 #endif /* not CRUNCH */
649 
650 /* This function draws a single line of text on the screen, possibly with
651  * some cursor optimization.  The cursor is repositioned before drawing
652  * begins, so its position before doesn't really matter.
653  */
smartdrawtext(text,lno,showit)654 static void smartdrawtext(text, lno, showit)
655 	REG char	*text;	/* the text to draw */
656 	long		lno;	/* line number of the text */
657 	int		showit;	/* boolean: output line? (else just remember it) */
658 {
659 #ifdef CRUNCH
660 	move((int)(lno - topline), 0);
661 	if (showit)
662 	{
663 		drawtext(text, lno, TRUE);
664 	}
665 #else /* not CRUNCH */
666 	static char	old[256];	/* how the line looked last time */
667 	char		new[256];	/* how it looks now */
668 	char		*build;		/* used to put chars into new[] */
669 	char		*scan;		/* used for moving thru new[] or old[] */
670 	char		*end;		/* last non-blank changed char */
671 	char		*shift;		/* used to insert/delete chars */
672 	int		same;		/* length of a run of unchanged chars */
673 	int		limitcol;
674 	int		col;
675 	int		i;
676 	char		numstr[9];
677 
678 # ifndef NO_CHARATTR
679 	/* if this line has attributes, do it the dumb way instead */
680 	if (hasattr(lno, text))
681 	{
682 		move((int)(lno - topline), 0);
683 		drawtext(text, lno, TRUE);
684 		return;
685 	}
686 # endif
687 # ifndef NO_SENTENCE
688 	/* if this line is a format line, & we're hiding format lines, then
689 	 * let the dumb drawtext() function handle it
690 	 */
691 	if (*o_hideformat && *text == '.')
692 	{
693 		move((int)(lno - topline), 0);
694 		drawtext(text, lno, TRUE);
695 		return;
696 	}
697 # endif
698 # ifndef NO_VISIBLE
699 	if (vizchange)
700 	{
701 		move((int)(lno - topline), 0);
702 		drawtext(text, lno, TRUE);
703 		smartlno = 0L;
704 		return;
705 	}
706 # endif
707 
708 	/* skip stuff that was scrolled off left edge */
709 	limitcol = leftcol;
710 	for (col = 0;
711 	     (i = *text) && col < limitcol; /* yes, ASSIGNMENT! */
712 	     text++)
713 	{
714 		if (i == '\t' && !*o_list)
715 		{
716 			col = col + *o_tabstop - (col % *o_tabstop);
717 		}
718 		else if (i >= 0 && i < ' ' || i == '\177')
719 		{
720 			col += 2;
721 		}
722 		else
723 		{
724 			col++;
725 		}
726 	}
727 
728 	/* adjust for control char that was partially visible */
729 	build = new;
730 	while (col > limitcol)
731 	{
732 		*build++ = ' ';
733 		limitcol++;
734 	}
735 
736 	/* now for the visible characters */
737 	limitcol = leftcol + COLS;
738 	if (*o_number)
739 		limitcol -= 8;
740 	for (; (i = *text) && col < limitcol; text++)
741 	{
742 		if (i == '\t' && !*o_list)
743 		{
744 			i = col + *o_tabstop - (col % *o_tabstop);
745 			while (col < i && col < limitcol)
746 			{
747 				*build++ = ' ';
748 				col++;
749 			}
750 		}
751 		else if (i >= 0 && i < ' ' || i == '\177')
752 		{
753 			col += 2;
754 			*build++ = '^';
755 			if (col <= limitcol)
756 			{
757 				*build++ = (i ^ '@');
758 			}
759 		}
760 		else
761 		{
762 			col++;
763 			*build++ = i;
764 		}
765 	}
766 	if (col < limitcol && *o_list)
767 	{
768 		*build++ = '$';
769 		col++;
770 	}
771 	end = build;
772 	while (col < limitcol)
773 	{
774 		*build++ = ' ';
775 		col++;
776 	}
777 
778 	/* if we're just supposed to remember this line, then remember it */
779 	if (!showit)
780 	{
781 		smartlno = lno;
782 		strncpy(old, new, COLS);
783 		return;
784 	}
785 
786 	/* locate the last non-blank character */
787 	while (end > new && end[-1] == ' ')
788 	{
789 		end--;
790 	}
791 
792 	/* can we optimize the displaying of this line? */
793 	if (lno != smartlno)
794 	{
795 		/* nope, can't optimize - different line */
796 		move((int)(lno - topline), 0);
797 
798 		/* show the line number, if necessary */
799 		if (*o_number)
800 		{
801 			sprintf(numstr, "%6ld |", lno);
802 			qaddstr(numstr);
803 		}
804 
805 		/* show the new line */
806 		for (scan = new, build = old; scan < end; )
807 		{
808 			qaddch(*scan);
809 			*build++ = *scan++;
810 		}
811 		if (end < new + COLS - (*o_number ? 8 : 0))
812 		{
813 			clrtoeol();
814 			while (build < old + COLS)
815 			{
816 				*build++ = ' ';
817 			}
818 		}
819 		smartlno = lno;
820 		return;
821 	}
822 
823 	/* skip any initial unchanged characters */
824 	for (scan = new, build = old; scan < end && *scan == *build; scan++, build++)
825 	{
826 	}
827 	i = (scan - new);
828 	if (*o_number)
829 		i += 8;
830 	move((int)(lno - topline), i);
831 
832 	/* The in-between characters must be changed */
833 	same = 0;
834 	while (scan < end)
835 	{
836 		/* is this character a match? */
837 		if (scan[0] == build[0])
838 		{
839 			same++;
840 		}
841 		else /* do we want to insert? */
842 		if (scan < end - 1 && scan[1] == build[0] && (has_IC || has_IM))
843 		{
844 			nudgecursor(same, scan, new, lno);
845 			same = 0;
846 
847 			insch(*scan);
848 			for (shift = old + COLS; --shift > build; )
849 			{
850 				shift[0] = shift[-1];
851 			}
852 			*build = *scan;
853 		}
854 		else /* do we want to delete? */
855 		if (build < old + COLS - 1 && scan[0] == build[1] && has_DC)
856 		{
857 			nudgecursor(same, scan, new, lno);
858 			same = 0;
859 
860 			delch();
861 			same++;
862 			for (shift = build; shift < old + COLS - 1; shift++)
863 			{
864 				shift[0] = shift[1];
865 			}
866 			if (*o_number)
867 				shift -= 8;
868 			*shift = ' ';
869 		}
870 		else /* we must overwrite */
871 		{
872 			nudgecursor(same, scan, new, lno);
873 			same = 0;
874 
875 			addch(*scan);
876 			*build = *scan;
877 		}
878 
879 		build++;
880 		scan++;
881 	}
882 
883 	/* maybe clear to EOL */
884 	end = old + COLS - (*o_number ? 8 : 0);
885 	while (build < end && *build == ' ')
886 	{
887 		build++;
888 	}
889 	if (build < end)
890 	{
891 		nudgecursor(same, scan, new, lno);
892 		same = 0;
893 
894 		clrtoeol();
895 		while (build < old + COLS)
896 		{
897 			*build++ = ' ';
898 		}
899 	}
900 #endif /* not CRUNCH */
901 }
902 
903 
904 /* This function is used in visual mode for drawing the screen (or just parts
905  * of the screen, if that's all thats needed).  It also takes care of
906  * scrolling.
907  */
redraw(curs,inputting)908 void redraw(curs, inputting)
909 	MARK	curs;		/* where to leave the screen's cursor */
910 	int	inputting;	/* boolean: being called from input() ? */
911 {
912 	char		*text;		/* a line of text to display */
913 	static long	chgs;		/* previous changes level */
914 	long		l;
915 	int		i;
916 #ifndef CRUNCH
917 	static long	showtop;	/* top line in window */
918 	static long	showbottom;	/* bottom line in window */
919 #endif
920 
921 	/* if curs == MARK_UNSET, then we should reset internal vars */
922 	if (curs == MARK_UNSET)
923 	{
924 		if (topline < 1 || topline > nlines)
925 		{
926 			topline = 1L;
927 		}
928 		else
929 		{
930 			move(LINES - 1, 0);
931 			clrtoeol();
932 		}
933 		leftcol = 0;
934 		mustredraw = TRUE;
935 		redrawafter = INFINITY;
936 		preredraw = 0L;
937 		postredraw = 0L;
938 		chgs = 0;
939 		smartlno = 0L;
940 #ifndef NO_VISIBLE
941 		vizlow = vizhigh = 0L;
942 		vizchange = FALSE;
943 #endif
944 #ifndef CRUNCH
945 		showtop = 0;
946 		showbottom = INFINITY;
947 #endif
948 		return;
949 	}
950 
951 #ifndef NO_VISIBLE
952 	/* adjustments to hilited area may force extra lines to be redrawn. */
953 	setviz(curs);
954 #endif
955 
956 	/* figure out which column the cursor will be in */
957 	l = markline(curs);
958 	text = fetchline(l);
959 	mark2phys(curs, text, inputting);
960 
961 #ifndef NO_COLOR
962 	fixcolor();
963 #endif
964 
965 	/* adjust topline, if necessary, to get the cursor on the screen */
966 	if (l >= topline && l <= botline)
967 	{
968 		/* it is on the screen already */
969 
970 		/* if the file was changed but !mustredraw, then redraw line */
971 		if (!mustredraw && (chgs != changes
972 #ifndef NO_VISIBLE
973 			|| V_from
974 #endif
975 #ifndef CRUNCH
976 			|| l < showtop || l > showbottom
977 #endif
978 							))
979 		{
980 			smartdrawtext(text, l, (chgs != changes));
981 		}
982 	}
983 	else if (l < topline && l > topline - LINES && (has_SR || has_AL))
984 	{
985 		/* near top - scroll down */
986 		if (!mustredraw)
987 		{
988 			move(0,0);
989 			while (l < topline)
990 			{
991 				topline--;
992 				if (has_SR)
993 				{
994 					do_SR();
995 				}
996 				else
997 				{
998 					insertln();
999 				}
1000 				text = fetchline(topline);
1001 				drawtext(text, topline, FALSE);
1002 				do_UP();
1003 			}
1004 
1005 			/* blank out the last line */
1006 			move(LINES - 1, 0);
1007 			clrtoeol();
1008 		}
1009 		else
1010 		{
1011 			topline = l;
1012 			redrawrange(0L, INFINITY, INFINITY);
1013 		}
1014 	}
1015 	else if (l > topline && l < botline + LINES)
1016 	{
1017 		/* near bottom -- scroll up */
1018 		if (!mustredraw)
1019 		{
1020 			move(LINES - 1,0);
1021 			clrtoeol();
1022 			while (l > botline)
1023 			{
1024 				topline++; /* <-- also adjusts botline */
1025 				text = fetchline(botline);
1026 				drawtext(text, botline, FALSE);
1027 			}
1028 #ifndef CRUNCH
1029 			showbottom = l;
1030 #endif
1031 		}
1032 		else
1033 		{
1034 			topline = l - (LINES - 2);
1035 			redrawrange(0L, INFINITY, INFINITY);
1036 		}
1037 	}
1038 	else
1039 	{
1040 		/* distant line - center it & force a redraw */
1041 		topline = l - (LINES / 2) - 1;
1042 		if (topline < 1)
1043 		{
1044 			topline = 1;
1045 		}
1046 		redrawrange(0L, INFINITY, INFINITY);
1047 		changes++;
1048 	}
1049 
1050 #ifndef CRUNCH
1051 	/* make sure the current line is included in the "window" */
1052 	if (l < showtop)
1053 	{
1054 		redrawrange(l, showtop, showtop);
1055 		showtop = l;
1056 	}
1057 	if (l > showbottom)
1058 	{
1059 		redrawrange(showbottom, l, l);
1060 		showbottom = l;
1061 	}
1062 #endif
1063 
1064 
1065 	/* Now... do we really have to redraw? */
1066 	if (mustredraw)
1067 	{
1068 		/* If redrawfter (and friends) aren't set, assume we should
1069 		 * redraw everything.
1070 		 */
1071 		if (redrawafter == INFINITY)
1072 		{
1073 			redrawafter = 0L;
1074 			preredraw = postredraw = INFINITY;
1075 		}
1076 
1077 #ifndef CRUNCH
1078 		/* shrink the window, if possible */
1079 		if (showtop < topline)
1080 		{
1081 			showtop = topline;
1082 		}
1083 		if (showbottom > botline)
1084 		{
1085 			showbottom = botline;
1086 		}
1087 		if (postredraw == INFINITY)
1088 		{
1089 			/* these will be set to more reasonable values later */
1090 			showtop = INFINITY;
1091 			showbottom = 0L;
1092 		}
1093 #endif
1094 
1095 		/* adjust smartlno to correspond with inserted/deleted lines */
1096 		if (smartlno >= redrawafter)
1097 		{
1098 			if (smartlno < preredraw && postredraw != preredraw) /*!!!*/
1099 			{
1100 				smartlno = 0L;
1101 			}
1102 			else
1103 			{
1104 				smartlno += (postredraw - preredraw);
1105 			}
1106 		}
1107 
1108 		/* should we insert some lines into the screen? */
1109 		if (preredraw < postredraw && preredraw <= botline)
1110 		{
1111 			/* lines were inserted into the file */
1112 
1113 			/* decide where insertion should start */
1114 			if (preredraw < topline)
1115 			{
1116 				l = topline;
1117 			}
1118 			else
1119 			{
1120 				l = preredraw;
1121 			}
1122 
1123 			/* insert the lines... maybe */
1124 			if (l + postredraw - preredraw > botline || !has_AL || *o_number)
1125 			{
1126 				/* Whoa!  a whole screen full - just redraw */
1127 				preredraw = postredraw = INFINITY;
1128 			}
1129 			else
1130 			{
1131 				/* really insert 'em */
1132 				move((int)(l - topline), 0);
1133 				for (i = postredraw - preredraw; i > 0; i--)
1134 				{
1135 					insertln();
1136 				}
1137 
1138 				/* NOTE: the contents of those lines will be
1139 				 * drawn as part of the regular redraw loop.
1140 				 */
1141 
1142 				/* clear the last line */
1143 				move(LINES - 1, 0);
1144 				clrtoeol();
1145 			}
1146 		}
1147 
1148 		/* do we want to delete some lines from the screen? */
1149 		if (preredraw > postredraw && postredraw <= botline)
1150 		{
1151 			if (preredraw > botline || !has_DL || *o_number)
1152 			{
1153 				postredraw = preredraw = INFINITY;
1154 			}
1155 			else /* we'd best delete some lines from the screen */
1156 			{
1157 				/* clear the last line, so it doesn't look
1158 				 * ugly as it gets pulled up into the screen
1159 				 */
1160 				move(LINES - 1, 0);
1161 				clrtoeol();
1162 
1163 				/* delete the lines */
1164 				move((int)(postredraw - topline), 0);
1165 			 	for (l = postredraw;
1166 				     l < preredraw && l <= botline;
1167 				     l++)
1168 				{
1169 					deleteln();
1170 				}
1171 
1172 				/* draw the lines that are now newly visible
1173 				 * at the bottom of the screen
1174 				 */
1175 				i = LINES - 1 + (postredraw - preredraw);
1176 				move(i, 0);
1177 				for (l = topline + i; l <= botline; l++)
1178 				{
1179 					/* clear this line */
1180 					clrtoeol();
1181 
1182 					/* draw the line, or ~ for non-lines */
1183 					if (l <= nlines)
1184 					{
1185 						text = fetchline(l);
1186 						drawtext(text, l, FALSE);
1187 					}
1188 					else
1189 					{
1190 						addstr("~\n");
1191 					}
1192 				}
1193 			}
1194 		}
1195 
1196 		/* redraw the current line */
1197 		l = markline(curs);
1198 		pfetch(l);
1199 		smartdrawtext(ptext, l, TRUE);
1200 
1201 #ifndef CRUNCH
1202 		/* decide which lines must be in the "window" around the cursor */
1203 		l = markline(curs);
1204 		if ((*o_window & 0xff) + 1 == LINES)
1205 		{
1206 			showtop = 1;
1207 			showbottom = INFINITY;
1208 		}
1209 		else if (l < showtop || l > showbottom)
1210 		{
1211 			l -= (*o_window & 0xff) / 2;
1212 			if (l < topline)
1213 			{
1214 				l = topline;
1215 			}
1216 			if (l < showtop)
1217 			{
1218 				showtop = l;
1219 			}
1220 			l += (*o_window & 0xff) - 1;
1221 			if (l > botline)
1222 			{
1223 				showtop = showtop - l + botline;
1224 				l = botline;
1225 			}
1226 			if (l > showbottom)
1227 			{
1228 				showbottom = l;
1229 			}
1230 		}
1231 #endif
1232 
1233 		/* decide where we should start redrawing from */
1234 		if (redrawafter < topline)
1235 		{
1236 			l = topline;
1237 		}
1238 		else
1239 		{
1240 			l = redrawafter;
1241 		}
1242 		if (l <= botline && l < postredraw && (l != smartlno || botline != smartlno))
1243 		{
1244 			/* draw the other lines */
1245 			move((int)(l - topline), 0);
1246 			for (; l <= botline && l < postredraw; l++)
1247 			{
1248 				/* we already drew the current line, so skip it now */
1249 				if (l == smartlno)
1250 				{
1251 #if OSK
1252 					qaddch('\l');
1253 #else
1254 					qaddch('\n');
1255 #endif
1256 					continue;
1257 				}
1258 
1259 				/* draw the line, or ~ for non-lines */
1260 				if (l > nlines)
1261 				{
1262 					qaddch('~');
1263 					clrtoeol();
1264 					addch('\n');
1265 				}
1266 #ifndef CRUNCH
1267 				else if (l < showtop || l > showbottom)
1268 				{
1269 					qaddch('@');
1270 					clrtoeol();
1271 					addch('\n');
1272 				}
1273 #endif
1274 				else
1275 				{
1276 					text = fetchline(l);
1277 					drawtext(text, l, TRUE);
1278 				}
1279 			}
1280 		}
1281 
1282 		mustredraw = FALSE;
1283 	}
1284 
1285 	/* force total (non-partial) redraw next time if not set */
1286 	redrawafter = INFINITY;
1287 	preredraw = 0L;
1288 	postredraw = 0L;
1289 
1290 	/* move the cursor to where it belongs */
1291 	move((int)(markline(curs) - topline), physcol);
1292 	wqrefresh();
1293 
1294 	chgs = changes;
1295 }
1296