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