xref: /illumos-gate/usr/src/lib/libxcurses/src/libc/xcurses/doupdate.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 (c) 1995, by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * doupdate.c
31  *
32  * XCurses Library
33  *
34  * Copyright 1990, 1995 by Mortice Kern Systems Inc.  All rights reserved.
35  *
36  */
37 
38 #ifdef M_RCSID
39 #ifndef lint
40 static char const rcsID[] = "$Header: /rd/src/libc/xcurses/rcs/doupdate.c 1.9 1995/07/26 17:45:06 ant Exp $";
41 #endif
42 #endif
43 
44 #include <private.h>
45 #include <string.h>
46 #include <setjmp.h>
47 #include <signal.h>
48 
49 #undef SIGTSTP
50 
51 /*
52  * Disable typeahead trapping because it slow down updated dramatically
53  * on MPE/iX.
54  */
55 #ifdef MPE_STUB
56 #undef M_CURSES_TYPEAHEAD
57 #endif
58 
59 /*
60  * This value is the ideal length for the cursor addressing sequence
61  * being four bytes long, ie. "<escape><cursor addressing code><row><col>".
62  * eg. VT52 - "\EYrc" or ADM3A - "\E=rc"
63  */
64 #define JUMP_SIZE	4
65 
66 /*
67  * This value is the ideal length for the clear-to-eol sequence
68  * being two bytes long, ie "<escape><clear eol code>".
69  */
70 #define CEOL_SIZE	2
71 
72 #define GOTO(r,c)	(__m_mvcur(curscr->_cury, curscr->_curx,r,c,__m_outc),\
73 			curscr->_cury = r, curscr->_curx = c)
74 
75 typedef struct cost_op {
76 	short cost;
77 	short op;
78 } lcost;
79 
80 typedef void (*t_action)(int, int);
81 
82 static jmp_buf breakout;
83 
84 #define LC(i,j) 	(lc[(i) * (LINES + 1) + (j)])
85 
86 static lcost *lc = (lcost *) 0;
87 static unsigned long *nhash = (unsigned long *) 0;
88 static t_action *del = (t_action *) 0;
89 static t_action *ins_rep = (t_action *) 0;
90 
91 static WINDOW *newscr;
92 
93 STATIC void erase_bottom(int);
94 STATIC void clear_bottom(int);
95 STATIC void complex(void);
96 STATIC int cost(int, int);
97 STATIC void lines_delete(int, int);
98 STATIC void lines_insert(int, int);
99 STATIC void lines_replace(int, int);
100 STATIC void script(int, int);
101 STATIC int scroll_dn(int);
102 STATIC int scroll_up(int);
103 STATIC void simple(void);
104 STATIC void text_replace(int);
105 STATIC void block_over(int, int, int);
106 
107 /*f
108  * Wrapper that streams Curses output.
109  *
110  * All escape sequences going to the screen come through here.
111  * All ordinary characters go to the screen via the putc in doupdate.c
112  */
113 int
114 __m_outc(ch)
115 int ch;
116 {
117         return putc(ch, __m_screen->_of);
118 }
119 
120 /*
121  * Allocate or grow doupdate() structures.
122  */
123 int
124 __m_doupdate_init()
125 {
126 	void *new;
127 	static short nlines = 0;
128 
129 	if (lines <= 0)
130 		return -1;
131 
132 	if (lines <= nlines)
133 		return 0;
134 
135 	new = m_malloc((lines+1) * (lines+1) * sizeof *lc);
136 	if (new == (void *) 0)
137 		return -1;
138 	if (lc != (lcost *) 0)
139 		free(lc);
140 	lc = (lcost *) new;
141 
142 	new = m_malloc((lines + lines) * sizeof *del);
143 	if (new == (void *) 0)
144 		return -1;
145 	if (del != (t_action *) 0)
146 		free(del);
147 	del = (t_action *) new;
148 	ins_rep = del + lines;
149 
150 	new = m_malloc(lines * sizeof *nhash);
151 	if (new == (void *) 0)
152 		return -1;
153 	if (nhash != (unsigned long *) 0)
154 		free(nhash);
155 	nhash = (unsigned long *) new;
156 
157 	nlines = lines;
158 
159 	return 0;
160 }
161 
162 STATIC void
163 erase_bottom(y)
164 int y;
165 {
166 	int i;
167 
168 	for (i = y; i < LINES; ++i) {
169 		(void) __m_cc_erase(curscr, i, 0, i, curscr->_maxx-1);
170 		__m_cc_hash(curscr, __m_screen->_hash, i);
171 	}
172 }
173 
174 /*f
175  *  Clear from the start of the current row to bottom of screen.
176  */
177 STATIC void
178 clear_bottom(y)
179 int y;
180 {
181 	erase_bottom(y);
182 
183 	/* Restore default color pair before doing area clears. */
184 	if (back_color_erase)
185 		(void) vid_puts(WA_NORMAL, 0, (void *) 0, __m_outc);
186 
187 	if (y == 0 && clear_screen != (char *) 0) {
188 		(void) tputs(clear_screen, 1, __m_outc);
189 	} else {
190 		(void) __m_mvcur(-1, -1, y, 0, __m_outc);
191 		if (clr_eos != (char *) 0) {
192 			(void) tputs(clr_eos, 1, __m_outc);
193 		} else if (clr_eol != (char *) 0) {
194 			for (;;) {
195 				(void) tputs(clr_eol, 1, __m_outc);
196 				if (LINES <= y)
197 					break;
198 				(void) __m_mvcur(y, 0, y+1, 0, __m_outc);
199 				++y;
200 			}
201 		}
202 	}
203 
204 	curscr->_cury = y;
205 	curscr->_curx = 0;
206 }
207 
208 /*f
209  * Replace a line of text.
210  *
211  * The principal scheme is to overwrite the region of a line between
212  * the first and last differing characters.  A clear-eol is used to
213  * optimise an update region that consist largely of blanks.  This can
214  * happen fairly often in the case of scrolled lines or full redraws.
215  *
216  * Miller's line redraw algorithm, used in the 'S' editor [Mil87],
217  * should be re-investigated to see if it is simple and fast enough for
218  * our needs, and if it can be adapted to handle the ceol_standout_glitch
219  * (HP 2392A terminals) and multibyte character sequences.
220  *
221  * Very early versions of this code applied a Gosling algorithm column
222  * wise in addition to the row-wise used in complex().  It was removed
223  * in favour of both computation and transmission speed.  The assumption
224  * being that overwrites of a line region occured far more frequently
225  * than the need to insert/delete several isolated characters.
226  *
227  * References:
228  * [Mil87]	W. Miller, A Software Tools Sampler, Prentice-Hall, 1987
229  */
230 STATIC void
231 text_replace(row)
232 int row;
233 {
234 	short npair;
235 	attr_t cookie, nattr;
236 	cchar_t *optr, *nptr;
237 	int col, last, tail, jump, count;
238 
239 #ifdef M_CURSES_TYPEAHEAD
240 	/* Before replacing a line of text, check for type-ahead. */
241 	if (__m_screen->_flags & S_ISATTY) {
242 		unsigned char cc;
243 
244 		if (read(__m_screen->_kfd, &cc, sizeof cc) == sizeof cc) {
245 			(void) ungetch(cc);
246 			longjmp(breakout, 1);
247 		}
248 	}
249 #endif /* M_CURSES_TYPEAHEAD */
250 
251 	col = newscr->_first[row];
252 	if (col < 0)
253 		col = 0;
254 
255 	last = newscr->_last[row];
256 	if (COLS < last)
257 		last = COLS;
258 
259 	if (clr_eol != (char *) 0) {
260 		/* Find start of blank tail region. */
261 		nptr = &newscr->_line[row][COLS];
262 		for (tail = COLS; 0 < tail; --tail) {
263 			if (!__m_cc_compare(--nptr, &newscr->_bg, 1))
264 				break;
265 		}
266 
267 		/* Only consider clear-to-end-of-line optimization if the
268 		 * blank tail falls within the end of the dirty region by
269 		 * more than ideal length of clear-to-end-of-line sequence.
270 		 * Else disable the check by forcing tail to be at the
271 		 * end-of-line.
272 		 */
273 		if (last < tail + CEOL_SIZE)
274 			tail = COLS;
275 	}
276 
277 	optr = &curscr->_line[row][col];
278 	nptr = &newscr->_line[row][col];
279 
280 	for (jump = -1; col < last; ) {
281 		/* Skip common regions. */
282 		for (count = 0; __m_cc_compare(optr, nptr, 1); ++count) {
283 			/* Advance before possible goto. */
284 			++optr;
285 			++nptr;
286 
287 			if (last <= ++col)
288 				goto done;
289 		}
290 
291                 /* Move the cursor by redrawing characters or using
292                  * cursor motion commands.  The first time that we
293                  * address this row, jump equals -1, so that the cursor
294                  * will be forced to the correct screen line.  Once
295                  * there, we should be able to track the cursor motion
296                  * along the line and jump only when the cost of redrawing
297 		 * to column N is more expensive than a jump to column N.
298                  */
299                 if (jump < count) {
300 			/* First time addressing this row or cost of
301 			 * jumping cheaper than redrawing.
302 			 */
303                         jump = JUMP_SIZE;
304                         GOTO(row, col);
305 			count = 0;
306 
307 			/* If attributes at start of field are different
308 			 * force an attribute cookie to be dropped.
309 			 */
310 			if (ceol_standout_glitch
311 			&& (optr->_at != nptr->_at || optr->_co != nptr->_co))
312 				ATTR_STATE |= WA_COOKIE;
313                 } else {
314                         /* Redraw to move short distance. */
315 			optr -= count;
316 			nptr -= count;
317 			col -= count;
318 		}
319 
320 		/* Write difference region. */
321 		while (col < last
322 		&& (!__m_cc_compare(optr, nptr, 1) || 0 < count--)) {
323 write_loop:
324 			/* Check for clear-to-end-of-line optimization. */
325 			if (clr_eol != (char *) 0 && tail <= col) {
326 				/* For HP terminals, only clear-to-end-of-line
327 				 * once the attributes have been turned off.
328 				 * Other terminals, we can proceed normally.
329 				 */
330 				if (!ceol_standout_glitch
331 				|| ATTR_STATE == WA_NORMAL) {
332 					curscr->_curx = col;
333 					goto done;
334 				}
335 			}
336 
337 			++col;
338 
339 			/* Make sure we don't scroll the screen by writing
340 			 * to the bottom right corner.
341 			 */
342 			if (COLS <= col && LINES-1 <= row
343 			&& auto_right_margin && !eat_newline_glitch) {
344 				/*** TODO
345 				 *** Insert character/auto_right_margin
346 				 *** hacks for writting into the last
347 				 *** column of the last line so as not
348 				 *** to scroll.
349 				 ***/
350 				curscr->_curx = col;
351 				goto done;
352 			}
353 
354 			/* Remember any existing attribute cookie. */
355 			cookie = optr->_at & WA_COOKIE;
356 
357 			nattr = nptr->_at;
358 			npair = nptr->_co;
359 
360 			/* Change attribute state.  On HP terminals we also
361 			 * have to check for attribute cookies that may need
362 			 * to be changed.
363 			 */
364 			if (ATTR_STATE != nattr
365 			|| optr->_at != nattr || optr->_co != npair) {
366 				(void) vid_puts(
367 					nattr, npair, (void *) 0, __m_outc
368 				);
369 
370 				/* Remember new or existing cookie. */
371 				cookie = WA_COOKIE;
372 			}
373 
374 			/* Don't display internal characters. */
375 			if (nptr->_f)
376 				(void) __m_cc_write(nptr);
377 
378 			/* Update copy of screen image. */
379 			*optr++ = *nptr++;
380 			optr->_at |= cookie;
381 		}
382 
383 		curscr->_curx = col;
384 
385 		/* Check the attributes at the end of the field with
386 		 * those of start of the next common region.  If they
387 		 * differ, force another iteration of the write-loop
388 		 * that will change the attribute state.
389 		 */
390 		if (ceol_standout_glitch && col < COLS
391 		&& ATTR_STATE != (optr->_at & ~WA_COOKIE))
392 			goto write_loop;
393 	}
394 done:
395 	/* Before leaving this line, check if we have to turn off
396 	 * attributes and record a cookie.
397 	 */
398 	if (!move_standout_mode && ATTR_STATE != WA_NORMAL) {
399 		/* ceol_standout_glitch, which affects HP terminals,
400 		 * drops hidden cookies on the screen where ever the
401 		 * cursor is, so disabling attributes before a cursor
402 		 * motion operation could disturb existing highlights.
403 		 */
404 		if (ceol_standout_glitch)
405 			/* Attributes on an HP terminal do not cross lines. */
406 			ATTR_STATE = A_NORMAL;
407 		else
408 			(void) vid_puts(WA_NORMAL, 0, (void *) 0, __m_outc);
409 	}
410 
411 	/* Re-check for clear to end-of-line optimization. */
412 	if (clr_eol != (char *) 0 && tail <= col && col < last) {
413 		/* Is the tail of the current screen image non-blank? */
414 		for (tail = col; tail < COLS; ++tail, ++optr)
415 			if (!__m_cc_compare(optr, &newscr->_bg, 1))
416 				break;
417 
418 		/* If tail didn't reach the right margin of
419 		 * the current screen image, then we will
420 		 * make it look like the new image with a
421 		 * clear to end-of-line.
422 		 */
423 		if (tail < COLS) {
424 			/* Restore default color pair before area clear. */
425 			if (back_color_erase)
426 				(void) vid_puts(
427 					WA_NORMAL, 0, (void *) 0, __m_outc
428 				);
429 
430 			(void) tputs(clr_eol, 1, __m_outc);
431 			__m_cc_erase(curscr, row, tail, row, COLS-1);
432 		}
433 	}
434 
435 	/* Line wrapping checks. */
436 	if (COLS <= curscr->_curx) {
437 		--curscr->_curx;
438 		if (auto_right_margin && row < LINES-1) {
439 			if (eat_newline_glitch) {
440 				__m_outc('\r');
441 				__m_outc('\n');
442 			}
443 			++curscr->_cury;
444 			curscr->_curx = 0;
445 		}
446 	}
447 }
448 
449 /*f
450  * Replace a block of lines.
451  * Only ever used for complex().
452  */
453 STATIC void
454 lines_replace(from, to_1)
455 int from, to_1;
456 {
457 	for (; from < to_1; ++from)
458 		text_replace(from);
459 }
460 
461 /*f
462  * Delete a block of lines.
463  * Only ever used for complex().
464  */
465 STATIC void
466 lines_delete(from, to_1)
467 int from, to_1;
468 {
469 	int count = to_1 - from;
470 
471 	if (LINES <= to_1) {
472 		clear_bottom(from);
473 	} else {
474 		GOTO(from, 0);
475 		(void) winsdelln(curscr, -count);
476 
477 		if (parm_delete_line != (char *) 0) {
478 			/* Assume that the sequence to delete more than one
479 			 * line is faster than repeated single delete_lines.
480 			 */
481 			(void) tputs(
482 				tparm(
483 					parm_delete_line, (long) count,
484 					0, 0, 0, 0, 0, 0, 0, 0
485 				), count, __m_outc
486 			);
487 		} else if (delete_line != (char *) 0) {
488 			while (from++ < to_1)
489 				(void) tputs(delete_line, 1, __m_outc);
490 		} else  {
491 			/* Error -- what to do. */
492 			return;
493 		}
494 	}
495 }
496 
497 /*f
498  * Insert a block of lines.
499  * Only ever used for complex().
500  *
501  * We must assume that insert_line and parm_insert_line reset the
502  * cursor column to zero.  Therefore it is text_replace() responsiblity
503  * to move the cursor to the correct column to begin the update.
504  */
505 STATIC void
506 lines_insert(from, to_1)
507 int from, to_1;
508 {
509 	int row, count = to_1 - from;
510 
511 	/* Position the cursor and insert a block of lines into the screen
512 	 * image now, insert lines into the physical screen, then draw the
513 	 * new screen lines.
514 	 */
515 	GOTO(from, 0);
516 	(void) winsdelln(curscr, count);
517 
518 	if (parm_insert_line != (char *) 0) {
519 		/* Assume that the sequence to insert more than one line is
520 		 * faster than repeated single insert_lines.
521 		 */
522 		(void) tputs(
523 			tparm(
524 				parm_insert_line, (long) count,
525 				0, 0, 0, 0, 0, 0, 0, 0
526 			), count, __m_outc
527 		);
528 	} else if (insert_line != (char *) 0) {
529 		/* For the single line insert we use to iterate moving
530 		 * the cursor, inserting, and then drawing a line.  That
531 		 * would appear to be slow but visually appealing.  However,
532 		 * people on slow terminals want speed and those on fast
533 		 * terminal won't see it.
534 		 */
535 		for (row = from; row < to_1; ++row)
536 			(void) tputs(insert_line, 1, __m_outc);
537 	} else {
538 		/* Error -- what to do. */
539 		return;
540 	}
541 
542 	for (row = from; row < to_1; ++row)
543 		text_replace(row);
544 }
545 
546 STATIC int
547 scroll_up(n)
548 int n;
549 {
550 	int count = n;
551 	int start, finish, to, row;
552 
553 	if (scroll_forward != (char *) 0) {
554 		GOTO(LINES-1, 0);
555 		while (0 < n--)
556 			(void) tputs(scroll_forward, 1, __m_outc);
557 	} else if (parm_delete_line != (char *) 0 && 1 < n) {
558 		GOTO(0, 0);
559 		(void) tputs(
560 			tparm(
561 				parm_delete_line, (long) n,
562 				0, 0, 0, 0, 0, 0, 0, 0
563 			), n, __m_outc
564 		);
565 	} else if (delete_line != (char *) 0) {
566 		GOTO(0, 0);
567 		while (0 < n--)
568 			(void) tputs(delete_line, 1, __m_outc);
569 	} else {
570 		return 0;
571 	}
572 
573 	/* Scroll recorded image. */
574 	start = 0;
575 	finish = count-1;
576 	to = lines;
577 
578 	(void) __m_cc_erase(curscr, start, 0, finish, curscr->_maxx-1);
579 	(void) __m_ptr_move(
580 		(void **) curscr->_line, curscr->_maxy, start, finish, to
581 	);
582 
583 	simple();
584 
585 	return 1;
586 }
587 
588 STATIC int
589 scroll_dn(n)
590 int n;
591 {
592 	int count = n;
593 	int start, finish, to, row;
594 
595 	if (LINES < n)
596 		return 0;
597 
598 	if (scroll_reverse != (char *) 0) {
599 		GOTO(0, 0);
600 		while (0 < n--)
601 			(void) tputs(scroll_reverse, 1, __m_outc);
602 	} else if (parm_insert_line != (char *) 0 && 1 < n) {
603 		GOTO(0, 0);
604 		(void) tputs(
605 			tparm(
606 				parm_insert_line, (long) n,
607 				0, 0, 0, 0, 0, 0, 0, 0
608 			), n, __m_outc
609 		);
610 	} else if (insert_line != (char *) 0) {
611 		GOTO(0, 0);
612 		while (0 < n--)
613 			(void) tputs(insert_line, 1, __m_outc);
614 	} else {
615 		return 0;
616 	}
617 
618 	/* Scroll recorded image. */
619 	start = lines - count;
620 	finish = lines - 1;
621 	to = 0;
622 
623 	(void) __m_cc_erase(curscr, start, 0, finish, curscr->_maxx-1);
624 	(void) __m_ptr_move(
625 		(void **) curscr->_line, curscr->_maxy, start, finish, to
626 	);
627 
628 	simple();
629 
630 	return 1;
631 }
632 
633 #ifdef NEVER
634 STATIC int
635 is_same_line(old, new, count)
636 cchar_t *old, *new;
637 int count;
638 {
639 	while (0 < count--)
640 		if (!__m_cc_compare(old, new, 1))
641 			return 0;
642 
643 	return 1;
644 }
645 #endif /* NEVER */
646 
647 /*f
648  * Dynamic programming algorithm for the string edit problem.
649  *
650  * This is a modified Gosling cost algorithm that takes into account
651  * null/move operations.
652  *
653  * Costs for move, delete, replace, and insert are 0, 1, 2, and 3
654  * repectively.
655  */
656 STATIC int
657 cost(fr, lr)
658 int fr, lr;
659 {
660 	register lcost *lcp;
661 	register int or, nr, cc;
662 	register unsigned long *ohash = __m_screen->_hash;
663 	cchar_t **oline = curscr->_line;
664 	cchar_t **nline = newscr->_line;
665 	int linesz = COLS * sizeof **oline;
666 
667 	/* Prepare initial row and column of cost matrix.
668 	 *
669 	 *	0 3 6 9 ...
670 	 *	1
671 	 *	2
672 	 *	3
673 	 *	:
674 	 */
675 	LC(fr,fr).cost = 0;
676 	for (cc = 1, ++lr, nr = fr+1; nr <= lr; ++nr, ++cc) {
677 		/* Top row is 3, 6, 9, ... */
678 		LC(fr,nr).cost = cc * 3;
679 		LC(fr,nr).op = 'i';
680 
681 		/* Left column is 1, 2, 3, ... */
682 		LC(nr,fr).cost = cc;
683 		LC(nr,fr).op = 'd';
684 	}
685 
686 	for (--lr, or = fr; or <= lr; ++or) {
687 		for (nr = fr; nr <= lr; ++nr) {
688 			lcp = &LC(or+1,nr+1);
689 
690 			/* Assume move op. */
691 			lcp->cost = LC(or,nr).cost;
692 			lcp->op = 'm';
693 
694 			if (ohash[or] != nhash[nr]
695 #ifdef NEVER
696 /* Should no longer require this code.  Using the POSIX 32-bit CRC to
697  * generate a hash value should be sufficient now, since text_replace()
698  * will compare the contents of a line and output only the dirty regions.
699  */
700 			|| !is_same_line(oline[or], nline[nr], linesz)
701 #endif
702 			) {
703 				/* Lines are different, assume replace op. */
704 				lcp->cost += 2;
705 				lcp->op = 'r';
706 			}
707 
708 			/* Compare insert op. */
709 			if ((cc = LC(or+1,nr).cost + 3) < lcp->cost) {
710 				lcp->cost = cc;
711 				lcp->op = 'i';
712 			}
713 
714 			/* Compare delete op. */
715 			if ((cc = LC(or,nr+1).cost + 1) < lcp->cost) {
716 				lcp->cost = cc;
717 				lcp->op = 'd';
718 			}
719 		}
720 	}
721 
722 	return LC(lr+1,lr+1).cost;
723 }
724 
725 /*f
726  * Build edit script.
727  *
728  * Normally this would be a recursve routine doing the deletes, inserts,
729  * and replaces on individual lines. Instead we build the script so that
730  * we can later do the operations on a block basis.  For terminals with
731  * parm_delete or parm_insert strings this will be better in terms of the
732  * number of characters sent to delete and insert a block of lines.
733  *
734  * Also we can optimize the script so that tail inserts become replaces.
735  * This saves unnecessary inserts operations when the tail can just be
736  * overwritten.
737  */
738 STATIC void
739 script(fr, lr)
740 int fr, lr;
741 {
742 	int i, j;
743 	cchar_t *cp;
744 
745 	i = j = lr + 1;
746 
747 	memset(del, 0, sizeof *del * LINES);
748 	memset(ins_rep, 0, sizeof *ins_rep * LINES);
749 
750 	do {
751 		/* We don't have to bounds check i or j becuase row fr and
752 		 * column fr of lc have been preset in order to guarantee the
753 		 * correct motion.
754 		 */
755 		switch (LC(i,j).op) {
756 		case 'i':
757 			--j;
758 			ins_rep[j] = lines_insert;
759 			break;
760 		case 'd':
761 			--i;
762 			del[i] = lines_delete;
763 			break;
764 		case 'm':
765 			--i;
766 			--j;
767 			break;
768 		case 'r':
769 			--i;
770 			--j;
771 			ins_rep[j] = lines_replace;
772 			break;
773 		}
774 	} while (fr < i || fr < j);
775 
776 	/* Optimize Tail Inserts */
777 	for (i = LINES-1; 0 <= i && ins_rep[i] == lines_insert; --i) {
778 		/* Make each character in the screen line image invalid. */
779 		for (cp = curscr->_line[i], j = 0; j < COLS; ++j, ++cp)
780 			cp->_n = -1;
781 		ins_rep[i] = lines_replace;
782 	}
783 }
784 
785 /*f
786  * Complex update algorithm using insert/delete line operations.
787  *
788  * References:
789  * [MyM86]	E.W. Myers & W. Miller, Row Replacement Algorithms for
790  *		Screen Editors, TR 86-19, Dept. Computer Science, U. of Arizona
791  * [MyM87]	E.W. Myers & W. Miller, A Simple Row Replacement Method,
792  *		TR 86-28, Dept. Computer Science, U. of Arizona
793  * [Mil87]	W. Miller, A Software Tools Sampler, Prentice-Hall, 1987
794  * [Gos81]	James Gosling, A redisplay algorithm, Proceedings of the
795  *		ACM Symposium on Text Manipulation, SIGPLAN Notices,
796  *		16(6) June 1981, pg 123-129
797  *
798  * All the above were reviewed and experimented with.  Due to the nature of
799  * Curses' having to handling overlapping WINDOWs, the only suitable
800  * algorithum is [Gos81].  The others are better suited to editor type
801  * applications that have one window being the entire terminal screen.
802  *
803  */
804 STATIC void
805 complex()
806 {
807 	int fr = -1;
808 	int i, j, lr;
809 	t_action func;
810 
811 	/* Find block of lines to change */
812 	for (i = 0; i < LINES; ++i) {
813 		if (newscr->_first[i] < newscr->_last[i]) {
814 			/* Compute new hash. */
815 			__m_cc_hash(newscr, nhash, i);
816 			if (fr == -1)
817 				fr = i;
818 			lr = i;
819 		} else {
820 			/* Line not dirty so hash same as before. */
821 			nhash[i] = __m_screen->_hash[i];
822 		}
823 	}
824 
825 	if (fr != -1) {
826 		/* Gosling */
827 		cost(fr, lr);
828 		script(fr, lr);
829 
830 		/* Do deletes first in reverse order. */
831 		for (j = lr; fr <= j; --j) {
832 			if (del[j] != (t_action) 0) {
833 				for (i = j-1; fr <= i; --i)
834 					if (del[i] == (t_action) 0)
835 						break;
836 
837 				lines_delete(i+1, j+1);
838 				j = i;
839 			}
840 		}
841 
842 		/* Do insert/replace in forward order. */
843 		for (i = fr; i <= lr; ++i) {
844 			if ((func = ins_rep[i]) != (t_action) 0) {
845 				/* Find size of block */
846 				for (j = i; j <= lr && ins_rep[j] == func; ++j)
847 					;
848 				(*func)(i, j);
849 				i = j-1;
850 			}
851 		}
852 record:
853 		/* _line[], which contains pointers to screen lines,
854 		 * may be shuffled.
855 		 */
856 		for (i = fr; i <= lr; ++i) {
857 			/* Save new hash for next update. */
858 			__m_screen->_hash[i] = nhash[i];
859 
860 			/* Mark line as untouched. */
861 			newscr->_first[i] = newscr->_maxx;
862 			newscr->_last[i] = -1;
863 		}
864 	}
865 }
866 
867 /*f
868  * Simple screen update algorithm
869  *
870  * We perform a simple incremental update of the terminal screen.
871  * Only the segment of a line that was touched is replaced on the
872  * line.
873  */
874 STATIC void
875 simple()
876 {
877 	int row;
878 
879 	for (row = 0; row < LINES; ++row) {
880 		if (newscr->_first[row] < newscr->_last[row]) {
881 			text_replace(row);
882 
883 			/* Mark line as untouched. */
884 			newscr->_first[row] = newscr->_maxx;
885 			newscr->_last[row] = -1;
886 
887 			if (__m_screen->_flags & S_INS_DEL_LINE)
888 				__m_cc_hash(newscr, nhash, row);
889 		}
890 	}
891 
892 	newscr->_flags &= ~W_REDRAW_WINDOW;
893 }
894 
895 /*f
896  * Send all changes made to _newscr to the physical terminal.
897  *
898  * If idlok() is set TRUE then doupdate will try and use hardware insert
899  * and delete line sequences in an effort to optimize output.  idlok()
900  * should really only be used in applications that want a proper scrolling
901  * effect.
902  *
903  * Added scroll heuristic to handle special case where a full size window
904  * with full size scroll region, will scroll the window and replace dirty
905  * lines instead of performing usual cost/script operations.
906  */
907 int
908 doupdate()
909 {
910 #ifdef SIGTSTP
911 	int (*oldsig)(int) = signal(SIGTSTP, SIG_IGN);
912 #endif
913 
914 #ifdef M_CURSES_TYPEAHEAD
915 	unsigned char cc;
916 	int min, time, icanon;
917 
918 	if (__m_screen->_flags & S_ISATTY) {
919 		/* Set up non-blocking input for typeahead trapping. */
920 		min = cur_term->_prog.c_cc[VMIN];
921 		time = cur_term->_prog.c_cc[VTIME];
922 		icanon = cur_term->_prog.c_lflag & ICANON;
923 
924 		cur_term->_prog.c_cc[VMIN] = 0;
925 		cur_term->_prog.c_cc[VTIME] = 0;
926 		cur_term->_prog.c_lflag &= ~ICANON;
927 
928 		(void) tcsetattr(__m_screen->_kfd, TCSANOW, &cur_term->_prog);
929 	}
930 #endif /* M_CURSES_TYPEAHEAD */
931 
932 #ifdef M_CURSES_TRACE
933 	__m_trace(
934 		"doupdate(void) using %s algorithm.",
935 		(__m_screen->_flags & S_INS_DEL_LINE) ? "complex" : "simple"
936 	);
937 #endif
938 
939 	newscr = __m_screen->_newscr;
940 
941 	if (__m_screen->_flags & S_ENDWIN) {
942 		/* Return from temporary escape done with endwin(). */
943 		__m_screen->_flags &= ~S_ENDWIN;
944 
945 		(void) reset_prog_mode();
946 		if (enter_ca_mode != (char *) 0)
947 			(void) tputs(enter_ca_mode, 1, __m_outc);
948 		if (keypad_xmit != (char *) 0)
949 			(void) tputs(keypad_xmit, 1, __m_outc);
950 		if (ena_acs != (char *) 0)
951 			(void) tputs(ena_acs, 1, __m_outc);
952 
953 		/* Force redraw of screen. */
954 		newscr->_flags |= W_CLEAR_WINDOW;
955 	}
956 
957 #ifdef M_CURSES_TYPEAHEAD
958 	if (setjmp(breakout) == 0) {
959 		if ((__m_screen->_flags & S_ISATTY)
960 		&& read(__m_screen->_kfd, &cc, sizeof cc) == sizeof cc) {
961 			(void) ungetch(cc);
962 			longjmp(breakout, 1);
963 		}
964 #endif /* M_CURSES_TYPEAHEAD */
965 
966 		/* When redrawwing a window, we not only assume that line
967 		 * noise may have lost characters, but line noise may have
968 		 * generated bogus characters on the screen outside the
969 		 * the window in question, in which case redraw the entire
970 		 * screen to be sure.
971 		 */
972 		if (newscr->_flags & (W_CLEAR_WINDOW | W_REDRAW_WINDOW)) {
973 			clear_bottom(0);
974 			newscr->_flags &= ~W_CLEAR_WINDOW;
975 			(void) wtouchln(newscr, 0, newscr->_maxy, 1);
976 		}
977 
978 		if (newscr->_flags & W_REDRAW_WINDOW)
979 			simple();
980 #if 0		/* This first expression, of undefined section, is useless
981 		 * since newscr->_scroll is unsigned and never LT zero.
982 		 */
983 		else if (newscr->_scroll < 0 && scroll_dn(-newscr->_scroll))
984 #else
985 		else if (scroll_dn(-newscr->_scroll))
986 #endif
987 			;
988 		else if (0 < newscr->_scroll && scroll_up(newscr->_scroll))
989 			;
990 		else if (__m_screen->_flags & S_INS_DEL_LINE)
991 			complex();
992 		else
993 			simple();
994 
995 		if (!(newscr->_flags & W_LEAVE_CURSOR))
996 			GOTO(newscr->_cury, newscr->_curx);
997 
998 		if (!(curscr->_flags & W_FLUSH))
999 			(void) fflush(__m_screen->_of);
1000 #ifdef M_CURSES_TYPEAHEAD
1001 	}
1002 
1003 	if (__m_screen->_flags & S_ISATTY) {
1004 		/* Restore previous input mode. */
1005 		cur_term->_prog.c_cc[VMIN] = min;
1006 		cur_term->_prog.c_cc[VTIME] = time;
1007 		cur_term->_prog.c_lflag |= icanon;
1008 
1009 		(void) tcsetattr(__m_screen->_kfd,TCSANOW,&cur_term->_prog);
1010 	}
1011 #endif /* M_CURSES_TYPEAHEAD */
1012 
1013 	newscr->_scroll = curscr->_scroll = 0;
1014 #ifdef SIGTSTP
1015 	signal(SIGTSTP, oldsig);
1016 #endif
1017 
1018 	return __m_return_code("doupdate", OK);
1019 }
1020 
1021 /*
1022  * If true, the implementation may use hardware insert and delete,
1023  * character features of the terminal.  The window parameter
1024  * is ignored.
1025  */
1026 void
1027 idcok(WINDOW *w, bool bf)
1028 {
1029 #ifdef M_CURSES_TRACE
1030 	__m_trace("idcok(%p, %d)", w, bf);
1031 #endif
1032 
1033 	__m_screen->_flags &= ~S_INS_DEL_CHAR;
1034 	if (bf)
1035 		__m_screen->_flags |= S_INS_DEL_CHAR;
1036 
1037 	__m_return_void("idcok");
1038 }
1039 
1040 /*
1041  * If true, the implementation may use hardware insert, delete,
1042  * and scroll line features of the terminal.  The window parameter
1043  * is ignored.
1044  */
1045 int
1046 idlok(WINDOW *w, bool bf)
1047 {
1048 #ifdef M_CURSES_TRACE
1049 	__m_trace("idlok(%p, %d)", w, bf);
1050 #endif
1051 
1052 	__m_screen->_flags &= ~S_INS_DEL_LINE;
1053 	if (bf && has_il())
1054 		__m_screen->_flags |= S_INS_DEL_LINE;
1055 
1056 	return __m_return_code("idlok", OK);
1057 }
1058 
1059 /*
1060  * Use the POSIX 32-bit CRC function to compute a hash value
1061  * for the window line.
1062  */
1063 void
1064 __m_cc_hash(w, array, y)
1065 WINDOW *w;
1066 unsigned long *array;
1067 int y;
1068 {
1069 	array[y] = 0;
1070 	m_crcposix(
1071 		&array[y], (unsigned char *) w->_line[y],
1072 		(size_t) (w->_maxx * sizeof **w->_line)
1073 	);
1074 }
1075 
1076 
1077