1 /****************************************************************************
2  * Copyright 2018-2019,2020 Thomas E. Dickey                                *
3  * Copyright 2008-2016,2017 Free Software Foundation, Inc.                  *
4  *                                                                          *
5  * Permission is hereby granted, free of charge, to any person obtaining a  *
6  * copy of this software and associated documentation files (the            *
7  * "Software"), to deal in the Software without restriction, including      *
8  * without limitation the rights to use, copy, modify, merge, publish,      *
9  * distribute, distribute with modifications, sublicense, and/or sell       *
10  * copies of the Software, and to permit persons to whom the Software is    *
11  * furnished to do so, subject to the following conditions:                 *
12  *                                                                          *
13  * The above copyright notice and this permission notice shall be included  *
14  * in all copies or substantial portions of the Software.                   *
15  *                                                                          *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
19  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
20  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
21  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
22  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
23  *                                                                          *
24  * Except as contained in this notice, the name(s) of the above copyright   *
25  * holders shall not be used in advertising or otherwise to promote the     *
26  * sale, use or other dealings in this Software without prior written       *
27  * authorization.                                                           *
28  ****************************************************************************/
29 
30 /****************************************************************************
31  *  Author: Juergen Pfeifer                                                 *
32  *     and: Thomas E. Dickey                                                *
33  ****************************************************************************/
34 
35 #include <curses.priv.h>
36 #define CUR TerminalType((TERMINAL*)TCB).
37 #include <tic.h>
38 #include <termcap.h>		/* ospeed */
39 
40 #if HAVE_NANOSLEEP
41 #include <time.h>
42 #if HAVE_SYS_TIME_H
43 #include <sys/time.h>		/* needed for MacOS X DP3 */
44 #endif
45 #endif
46 
47 #if HAVE_SIZECHANGE
48 # if !defined(sun) || !TERMIOS
49 #  if HAVE_SYS_IOCTL_H
50 #   include <sys/ioctl.h>
51 #  endif
52 # endif
53 #endif
54 
55 MODULE_ID("$Id: tinfo_driver.c,v 1.67 2020/02/02 23:34:34 tom Exp $")
56 
57 /*
58  * SCO defines TIOCGSIZE and the corresponding struct.  Other systems (SunOS,
59  * Solaris, IRIX) define TIOCGWINSZ and struct winsize.
60  */
61 #ifdef TIOCGSIZE
62 # define IOCTL_WINSIZE TIOCGSIZE
63 # define STRUCT_WINSIZE struct ttysize
64 # define WINSIZE_ROWS(n) (int)n.ts_lines
65 # define WINSIZE_COLS(n) (int)n.ts_cols
66 #else
67 # ifdef TIOCGWINSZ
68 #  define IOCTL_WINSIZE TIOCGWINSZ
69 #  define STRUCT_WINSIZE struct winsize
70 #  define WINSIZE_ROWS(n) (int)n.ws_row
71 #  define WINSIZE_COLS(n) (int)n.ws_col
72 # endif
73 #endif
74 
75 /*
76  * These should be screen structure members.  They need to be globals for
77  * historical reasons.  So we assign them in start_color() and also in
78  * set_term()'s screen-switching logic.
79  */
80 #if USE_REENTRANT
81 NCURSES_EXPORT(int)
82 NCURSES_PUBLIC_VAR(COLOR_PAIRS) (void)
83 {
84     return CURRENT_SCREEN ? CURRENT_SCREEN->_pair_count : -1;
85 }
86 NCURSES_EXPORT(int)
87 NCURSES_PUBLIC_VAR(COLORS) (void)
88 {
89     return CURRENT_SCREEN ? CURRENT_SCREEN->_color_count : -1;
90 }
91 #else
92 NCURSES_EXPORT_VAR(int) COLOR_PAIRS = 0;
93 NCURSES_EXPORT_VAR(int) COLORS = 0;
94 #endif
95 
96 #define TCBMAGIC NCDRV_MAGIC(NCDRV_TINFO)
97 #define AssertTCB() assert(TCB!=0 && TCB->magic==TCBMAGIC)
98 #define SetSP() assert(TCB->csp!=0); sp = TCB->csp; (void) sp
99 
100 /*
101  * This routine needs to do all the work to make curscr look
102  * like newscr.
103  */
104 static int
105 drv_doupdate(TERMINAL_CONTROL_BLOCK * TCB)
106 {
107     AssertTCB();
108     return TINFO_DOUPDATE(TCB->csp);
109 }
110 
111 static const char *
112 drv_Name(TERMINAL_CONTROL_BLOCK * TCB)
113 {
114     (void) TCB;
115     return "tinfo";
116 }
117 
118 static void
119 get_baudrate(TERMINAL *termp)
120 {
121     int my_ospeed;
122     int result;
123     if (GET_TTY(termp->Filedes, &termp->Nttyb) == OK) {
124 #ifdef TERMIOS
125 	termp->Nttyb.c_oflag &= (unsigned) (~OFLAGS_TABS);
126 #else
127 	termp->Nttyb.sg_flags &= (unsigned) (~XTABS);
128 #endif
129     }
130 #ifdef USE_OLD_TTY
131     result = (int) cfgetospeed(&(termp->Nttyb));
132     my_ospeed = (NCURSES_OSPEED) _nc_ospeed(result);
133 #else /* !USE_OLD_TTY */
134 #ifdef TERMIOS
135     my_ospeed = (NCURSES_OSPEED) cfgetospeed(&(termp->Nttyb));
136 #else
137     my_ospeed = (NCURSES_OSPEED) termp->Nttyb.sg_ospeed;
138 #endif
139     result = _nc_baudrate(my_ospeed);
140 #endif
141     termp->_baudrate = result;
142     ospeed = (NCURSES_OSPEED) my_ospeed;
143 }
144 
145 #undef SETUP_FAIL
146 #define SETUP_FAIL FALSE
147 
148 #define NO_COPY {}
149 
150 static bool
151 drv_CanHandle(TERMINAL_CONTROL_BLOCK * TCB, const char *tname, int *errret)
152 {
153     bool result = FALSE;
154     int status;
155     TERMINAL *termp;
156     SCREEN *sp;
157 
158     START_TRACE();
159     T((T_CALLED("tinfo::drv_CanHandle(%p)"), (void *) TCB));
160 
161     assert(TCB != 0 && tname != 0);
162     termp = (TERMINAL *) TCB;
163     sp = TCB->csp;
164     TCB->magic = TCBMAGIC;
165 
166 #if (NCURSES_USE_DATABASE || NCURSES_USE_TERMCAP)
167     status = _nc_setup_tinfo(tname, &TerminalType(termp));
168     T(("_nc_setup_tinfo returns %d", status));
169 #else
170     T(("no database available"));
171     status = TGETENT_NO;
172 #endif
173 
174     /* try fallback list if entry on disk */
175     if (status != TGETENT_YES) {
176 	const TERMTYPE2 *fallback = _nc_fallback2(tname);
177 
178 	if (fallback) {
179 	    T(("found fallback entry"));
180 	    TerminalType(termp) = *fallback;
181 	    status = TGETENT_YES;
182 	}
183     }
184 
185     if (status != TGETENT_YES) {
186 	NCURSES_SP_NAME(del_curterm) (NCURSES_SP_ARGx termp);
187 	if (status == TGETENT_ERR) {
188 	    ret_error0(status, "terminals database is inaccessible\n");
189 	} else if (status == TGETENT_NO) {
190 	    ret_error1(status, "unknown terminal type.\n",
191 		       tname, NO_COPY);
192 	} else {
193 	    ret_error0(status, "unexpected return-code\n");
194 	}
195     }
196     result = TRUE;
197 #if NCURSES_EXT_NUMBERS
198     _nc_export_termtype2(&termp->type, &TerminalType(termp));
199 #endif
200 #if !USE_REENTRANT
201     save_ttytype(termp);
202 #endif
203 
204     if (command_character)
205 	_nc_tinfo_cmdch(termp, *command_character);
206 
207     /*
208      * If an application calls setupterm() rather than initscr() or
209      * newterm(), we will not have the def_prog_mode() call in
210      * _nc_setupscreen().  Do it now anyway, so we can initialize the
211      * baudrate.
212      */
213     if (sp == 0 && NC_ISATTY(termp->Filedes)) {
214 	get_baudrate(termp);
215     }
216 #if NCURSES_EXT_NUMBERS
217 #define cleanup_termtype() \
218     _nc_free_termtype2(&TerminalType(termp)); \
219     _nc_free_termtype(&termp->type)
220 #else
221 #define cleanup_termtype() \
222     _nc_free_termtype2(&TerminalType(termp))
223 #endif
224 
225     if (generic_type) {
226 	/*
227 	 * BSD 4.3's termcap contains mis-typed "gn" for wy99.  Do a sanity
228 	 * check before giving up.
229 	 */
230 	if ((VALID_STRING(cursor_address)
231 	     || (VALID_STRING(cursor_down) && VALID_STRING(cursor_home)))
232 	    && VALID_STRING(clear_screen)) {
233 	    cleanup_termtype();
234 	    ret_error1(TGETENT_YES, "terminal is not really generic.\n",
235 		       tname, NO_COPY);
236 	} else {
237 	    cleanup_termtype();
238 	    ret_error1(TGETENT_NO, "I need something more specific.\n",
239 		       tname, NO_COPY);
240 	}
241     }
242     if (hard_copy) {
243 	cleanup_termtype();
244 	ret_error1(TGETENT_YES, "I can't handle hardcopy terminals.\n",
245 		   tname, NO_COPY);
246     }
247 
248     returnBool(result);
249 }
250 
251 static int
252 drv_dobeepflash(TERMINAL_CONTROL_BLOCK * TCB, int beepFlag)
253 {
254     SCREEN *sp;
255     int res = ERR;
256 
257     AssertTCB();
258     SetSP();
259 
260     /* FIXME: should make sure that we are not in altchar mode */
261     if (beepFlag) {
262 	if (bell) {
263 	    res = NCURSES_PUTP2("bell", bell);
264 	    NCURSES_SP_NAME(_nc_flush) (sp);
265 	} else if (flash_screen) {
266 	    res = NCURSES_PUTP2("flash_screen", flash_screen);
267 	    NCURSES_SP_NAME(_nc_flush) (sp);
268 	}
269     } else {
270 	if (flash_screen) {
271 	    res = NCURSES_PUTP2("flash_screen", flash_screen);
272 	    NCURSES_SP_NAME(_nc_flush) (sp);
273 	} else if (bell) {
274 	    res = NCURSES_PUTP2("bell", bell);
275 	    NCURSES_SP_NAME(_nc_flush) (sp);
276 	}
277     }
278     return res;
279 }
280 
281 /*
282  * SVr4 curses is known to interchange color codes (1,4) and (3,6), possibly
283  * to maintain compatibility with a pre-ANSI scheme.  The same scheme is
284  * also used in the FreeBSD syscons.
285  */
286 static int
287 toggled_colors(int c)
288 {
289     if (c < 16) {
290 	static const int table[] =
291 	{0, 4, 2, 6, 1, 5, 3, 7,
292 	 8, 12, 10, 14, 9, 13, 11, 15};
293 	c = table[c];
294     }
295     return c;
296 }
297 
298 static int
299 drv_print(TERMINAL_CONTROL_BLOCK * TCB, char *data, int len)
300 {
301     SCREEN *sp;
302 
303     AssertTCB();
304     SetSP();
305 #if NCURSES_EXT_FUNCS
306     return NCURSES_SP_NAME(mcprint) (TCB->csp, data, len);
307 #else
308     return ERR;
309 #endif
310 }
311 
312 static int
313 drv_defaultcolors(TERMINAL_CONTROL_BLOCK * TCB, int fg, int bg)
314 {
315     SCREEN *sp;
316     int code = ERR;
317 
318     AssertTCB();
319     SetSP();
320 
321     if (sp != 0 && orig_pair && orig_colors && (initialize_pair != 0)) {
322 #if NCURSES_EXT_FUNCS
323 	sp->_default_color = isDefaultColor(fg) || isDefaultColor(bg);
324 	sp->_has_sgr_39_49 = (NCURSES_SP_NAME(tigetflag) (NCURSES_SP_ARGx
325 							  "AX")
326 			      == TRUE);
327 	sp->_default_fg = isDefaultColor(fg) ? COLOR_DEFAULT : fg;
328 	sp->_default_bg = isDefaultColor(bg) ? COLOR_DEFAULT : bg;
329 	if (sp->_color_pairs != 0) {
330 	    bool save = sp->_default_color;
331 	    sp->_default_color = TRUE;
332 	    NCURSES_SP_NAME(init_pair) (NCURSES_SP_ARGx
333 					0,
334 					(short)fg,
335 					(short)bg);
336 	    sp->_default_color = save;
337 	}
338 #endif
339 	code = OK;
340     }
341     return (code);
342 }
343 
344 static void
345 drv_setcolor(TERMINAL_CONTROL_BLOCK * TCB,
346 	     int fore,
347 	     int color,
348 	     NCURSES_SP_OUTC outc)
349 {
350     SCREEN *sp;
351 
352     AssertTCB();
353     SetSP();
354 
355     if (fore) {
356 	if (set_a_foreground) {
357 	    TPUTS_TRACE("set_a_foreground");
358 	    NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
359 				    TPARM_1(set_a_foreground, color), 1, outc);
360 	} else {
361 	    TPUTS_TRACE("set_foreground");
362 	    NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
363 				    TPARM_1(set_foreground,
364 					    toggled_colors(color)), 1, outc);
365 	}
366     } else {
367 	if (set_a_background) {
368 	    TPUTS_TRACE("set_a_background");
369 	    NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
370 				    TPARM_1(set_a_background, color), 1, outc);
371 	} else {
372 	    TPUTS_TRACE("set_background");
373 	    NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
374 				    TPARM_1(set_background,
375 					    toggled_colors(color)), 1, outc);
376 	}
377     }
378 }
379 
380 static bool
381 drv_rescol(TERMINAL_CONTROL_BLOCK * TCB)
382 {
383     bool result = FALSE;
384     SCREEN *sp;
385 
386     AssertTCB();
387     SetSP();
388 
389     if (orig_pair != 0) {
390 	NCURSES_PUTP2("orig_pair", orig_pair);
391 	result = TRUE;
392     }
393     return result;
394 }
395 
396 static bool
397 drv_rescolors(TERMINAL_CONTROL_BLOCK * TCB)
398 {
399     int result = FALSE;
400     SCREEN *sp;
401 
402     AssertTCB();
403     SetSP();
404 
405     if (orig_colors != 0) {
406 	NCURSES_PUTP2("orig_colors", orig_colors);
407 	result = TRUE;
408     }
409     return result;
410 }
411 
412 static int
413 drv_size(TERMINAL_CONTROL_BLOCK * TCB, int *linep, int *colp)
414 {
415     SCREEN *sp;
416     bool useEnv = TRUE;
417     bool useTioctl = TRUE;
418 
419     AssertTCB();
420     sp = TCB->csp;		/* can be null here */
421 
422     if (sp) {
423 	useEnv = sp->_use_env;
424 	useTioctl = sp->use_tioctl;
425     } else {
426 	useEnv = _nc_prescreen.use_env;
427 	useTioctl = _nc_prescreen.use_tioctl;
428     }
429 
430     /* figure out the size of the screen */
431     T(("screen size: terminfo lines = %d columns = %d", lines, columns));
432 
433     *linep = (int) lines;
434     *colp = (int) columns;
435 
436     if (useEnv || useTioctl) {
437 	int value;
438 
439 #ifdef __EMX__
440 	{
441 	    int screendata[2];
442 	    _scrsize(screendata);
443 	    *colp = screendata[0];
444 	    *linep = ((sp != 0 && sp->_filtered)
445 		      ? 1
446 		      : screendata[1]);
447 	    T(("EMX screen size: environment LINES = %d COLUMNS = %d",
448 	       *linep, *colp));
449 	}
450 #endif
451 #if HAVE_SIZECHANGE
452 	/* try asking the OS */
453 	{
454 	    TERMINAL *termp = (TERMINAL *) TCB;
455 	    if (NC_ISATTY(termp->Filedes)) {
456 		STRUCT_WINSIZE size;
457 
458 		errno = 0;
459 		do {
460 		    if (ioctl(termp->Filedes, IOCTL_WINSIZE, &size) >= 0) {
461 			*linep = ((sp != 0 && sp->_filtered)
462 				  ? 1
463 				  : WINSIZE_ROWS(size));
464 			*colp = WINSIZE_COLS(size);
465 			T(("SYS screen size: environment LINES = %d COLUMNS = %d",
466 			   *linep, *colp));
467 			break;
468 		    }
469 		} while
470 		    (errno == EINTR);
471 	    }
472 	}
473 #endif /* HAVE_SIZECHANGE */
474 
475 	if (useEnv) {
476 	    if (useTioctl) {
477 		/*
478 		 * If environment variables are used, update them.
479 		 */
480 		if ((sp == 0 || !sp->_filtered) && _nc_getenv_num("LINES") > 0) {
481 		    _nc_setenv_num("LINES", *linep);
482 		}
483 		if (_nc_getenv_num("COLUMNS") > 0) {
484 		    _nc_setenv_num("COLUMNS", *colp);
485 		}
486 	    }
487 
488 	    /*
489 	     * Finally, look for environment variables.
490 	     *
491 	     * Solaris lets users override either dimension with an environment
492 	     * variable.
493 	     */
494 	    if ((value = _nc_getenv_num("LINES")) > 0) {
495 		*linep = value;
496 		T(("screen size: environment LINES = %d", *linep));
497 	    }
498 	    if ((value = _nc_getenv_num("COLUMNS")) > 0) {
499 		*colp = value;
500 		T(("screen size: environment COLUMNS = %d", *colp));
501 	    }
502 	}
503 
504 	/* if we can't get dynamic info about the size, use static */
505 	if (*linep <= 0) {
506 	    *linep = (int) lines;
507 	}
508 	if (*colp <= 0) {
509 	    *colp = (int) columns;
510 	}
511 
512 	/* the ultimate fallback, assume fixed 24x80 size */
513 	if (*linep <= 0) {
514 	    *linep = 24;
515 	}
516 	if (*colp <= 0) {
517 	    *colp = 80;
518 	}
519 
520 	/*
521 	 * Put the derived values back in the screen-size caps, so
522 	 * tigetnum() and tgetnum() will do the right thing.
523 	 */
524 	lines = (short) (*linep);
525 	columns = (short) (*colp);
526     }
527 
528     T(("screen size is %dx%d", *linep, *colp));
529     return OK;
530 }
531 
532 static int
533 drv_getsize(TERMINAL_CONTROL_BLOCK * TCB, int *l, int *c)
534 {
535     AssertTCB();
536     assert(l != 0 && c != 0);
537     *l = lines;
538     *c = columns;
539     return OK;
540 }
541 
542 static int
543 drv_setsize(TERMINAL_CONTROL_BLOCK * TCB, int l, int c)
544 {
545     AssertTCB();
546     lines = (short) l;
547     columns = (short) c;
548     return OK;
549 }
550 
551 static int
552 drv_sgmode(TERMINAL_CONTROL_BLOCK * TCB, int setFlag, TTY * buf)
553 {
554     SCREEN *sp = TCB->csp;
555     TERMINAL *_term = (TERMINAL *) TCB;
556     int result = OK;
557 
558     AssertTCB();
559     if (setFlag) {
560 	for (;;) {
561 	    if (SET_TTY(_term->Filedes, buf) != 0) {
562 		if (errno == EINTR)
563 		    continue;
564 		if (errno == ENOTTY) {
565 		    if (sp)
566 			sp->_notty = TRUE;
567 		}
568 		result = ERR;
569 	    }
570 	    break;
571 	}
572     } else {
573 	for (;;) {
574 	    if (GET_TTY(_term->Filedes, buf) != 0) {
575 		if (errno == EINTR)
576 		    continue;
577 		result = ERR;
578 	    }
579 	    break;
580 	}
581     }
582     return result;
583 }
584 
585 static int
586 drv_mode(TERMINAL_CONTROL_BLOCK * TCB, int progFlag, int defFlag)
587 {
588     SCREEN *sp;
589     TERMINAL *_term = (TERMINAL *) TCB;
590     int code = ERR;
591 
592     AssertTCB();
593     sp = TCB->csp;
594 
595     if (progFlag)		/* prog mode */
596     {
597 	if (defFlag) {
598 	    /* def_prog_mode */
599 	    /*
600 	     * Turn off the XTABS bit in the tty structure if it was on.
601 	     */
602 	    if ((drv_sgmode(TCB, FALSE, &(_term->Nttyb)) == OK)) {
603 #ifdef TERMIOS
604 		_term->Nttyb.c_oflag &= (unsigned) ~OFLAGS_TABS;
605 #else
606 		_term->Nttyb.sg_flags &= (unsigned) ~XTABS;
607 #endif
608 		code = OK;
609 	    }
610 	} else {
611 	    /* reset_prog_mode */
612 	    if (drv_sgmode(TCB, TRUE, &(_term->Nttyb)) == OK) {
613 		if (sp) {
614 		    if (sp->_keypad_on)
615 			_nc_keypad(sp, TRUE);
616 		}
617 		code = OK;
618 	    }
619 	}
620     } else {			/* shell mode */
621 	if (defFlag) {
622 	    /* def_shell_mode */
623 	    /*
624 	     * If XTABS was on, remove the tab and backtab capabilities.
625 	     */
626 	    if (drv_sgmode(TCB, FALSE, &(_term->Ottyb)) == OK) {
627 #ifdef TERMIOS
628 		if (_term->Ottyb.c_oflag & OFLAGS_TABS)
629 		    tab = back_tab = NULL;
630 #else
631 		if (_term->Ottyb.sg_flags & XTABS)
632 		    tab = back_tab = NULL;
633 #endif
634 		code = OK;
635 	    }
636 	} else {
637 	    /* reset_shell_mode */
638 	    if (sp) {
639 		_nc_keypad(sp, FALSE);
640 		NCURSES_SP_NAME(_nc_flush) (sp);
641 	    }
642 	    code = drv_sgmode(TCB, TRUE, &(_term->Ottyb));
643 	}
644     }
645     return (code);
646 }
647 
648 static void
649 drv_wrap(SCREEN *sp)
650 {
651     if (sp) {
652 	sp->_mouse_wrap(sp);
653 	NCURSES_SP_NAME(_nc_screen_wrap) (sp);
654 	NCURSES_SP_NAME(_nc_mvcur_wrap) (sp);	/* wrap up cursor addressing */
655     }
656 }
657 
658 static void
659 drv_release(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED)
660 {
661 }
662 
663 #  define SGR0_TEST(mode) (mode != 0) && (exit_attribute_mode == 0 || strcmp(mode, exit_attribute_mode))
664 
665 static void
666 drv_screen_init(SCREEN *sp)
667 {
668     TERMINAL_CONTROL_BLOCK *TCB = TCBOf(sp);
669 
670     AssertTCB();
671 
672     /*
673      * Check for mismatched graphic-rendition capabilities.  Most SVr4
674      * terminfo trees contain entries that have rmul or rmso equated to
675      * sgr0 (Solaris curses copes with those entries).  We do this only
676      * for curses, since many termcap applications assume that
677      * smso/rmso and smul/rmul are paired, and will not function
678      * properly if we remove rmso or rmul.  Curses applications
679      * shouldn't be looking at this detail.
680      */
681     sp->_use_rmso = SGR0_TEST(exit_standout_mode);
682     sp->_use_rmul = SGR0_TEST(exit_underline_mode);
683 
684     /*
685      * Check whether we can optimize scrolling under dumb terminals in
686      * case we do not have any of these capabilities, scrolling
687      * optimization will be useless.
688      */
689     sp->_scrolling = ((scroll_forward && scroll_reverse) ||
690 		      ((parm_rindex ||
691 			parm_insert_line ||
692 			insert_line) &&
693 		       (parm_index ||
694 			parm_delete_line ||
695 			delete_line)));
696 
697     NCURSES_SP_NAME(baudrate) (sp);
698 
699     NCURSES_SP_NAME(_nc_mvcur_init) (sp);
700     /* initialize terminal to a sane state */
701     NCURSES_SP_NAME(_nc_screen_init) (sp);
702 }
703 
704 static void
705 drv_init(TERMINAL_CONTROL_BLOCK * TCB)
706 {
707     TERMINAL *trm;
708 
709     AssertTCB();
710 
711     trm = (TERMINAL *) TCB;
712 
713     TCB->info.initcolor = VALID_STRING(initialize_color);
714     TCB->info.canchange = can_change;
715     TCB->info.hascolor = ((VALID_NUMERIC(max_colors) && VALID_NUMERIC(max_pairs)
716 			   && (((set_foreground != NULL)
717 				&& (set_background != NULL))
718 			       || ((set_a_foreground != NULL)
719 				   && (set_a_background != NULL))
720 			       || set_color_pair)) ? TRUE : FALSE);
721 
722     TCB->info.caninit = !(exit_ca_mode && non_rev_rmcup);
723 
724     TCB->info.maxpairs = VALID_NUMERIC(max_pairs) ? max_pairs : 0;
725     TCB->info.maxcolors = VALID_NUMERIC(max_colors) ? max_colors : 0;
726     TCB->info.numlabels = VALID_NUMERIC(num_labels) ? num_labels : 0;
727     TCB->info.labelwidth = VALID_NUMERIC(label_width) ? label_width : 0;
728     TCB->info.labelheight = VALID_NUMERIC(label_height) ? label_height : 0;
729     TCB->info.nocolorvideo = VALID_NUMERIC(no_color_video) ? no_color_video
730 	: 0;
731     TCB->info.tabsize = VALID_NUMERIC(init_tabs) ? (int) init_tabs : 8;
732 
733     TCB->info.defaultPalette = hue_lightness_saturation ? _nc_hls_palette : _nc_cga_palette;
734 
735     /*
736      * If an application calls setupterm() rather than initscr() or
737      * newterm(), we will not have the def_prog_mode() call in
738      * _nc_setupscreen().  Do it now anyway, so we can initialize the
739      * baudrate.
740      */
741     if (NC_ISATTY(trm->Filedes)) {
742 	TCB->drv->td_mode(TCB, TRUE, TRUE);
743     }
744 }
745 
746 #define MAX_PALETTE	8
747 #define InPalette(n)	((n) >= 0 && (n) < MAX_PALETTE)
748 
749 static void
750 drv_initpair(TERMINAL_CONTROL_BLOCK * TCB, int pair, int f, int b)
751 {
752     SCREEN *sp;
753 
754     AssertTCB();
755     SetSP();
756 
757     if ((initialize_pair != NULL) && InPalette(f) && InPalette(b)) {
758 	const color_t *tp = InfoOf(sp).defaultPalette;
759 
760 	TR(TRACE_ATTRS,
761 	   ("initializing pair: pair = %d, fg=(%d,%d,%d), bg=(%d,%d,%d)",
762 	    pair,
763 	    tp[f].red, tp[f].green, tp[f].blue,
764 	    tp[b].red, tp[b].green, tp[b].blue));
765 
766 	NCURSES_PUTP2("initialize_pair",
767 		      TPARM_7(initialize_pair,
768 			      pair,
769 			      tp[f].red, tp[f].green, tp[f].blue,
770 			      tp[b].red, tp[b].green, tp[b].blue));
771     }
772 }
773 
774 static int
775 default_fg(SCREEN *sp)
776 {
777 #if NCURSES_EXT_FUNCS
778     return (sp != 0) ? sp->_default_fg : COLOR_WHITE;
779 #else
780     return COLOR_WHITE;
781 #endif
782 }
783 
784 static int
785 default_bg(SCREEN *sp)
786 {
787 #if NCURSES_EXT_FUNCS
788     return sp != 0 ? sp->_default_bg : COLOR_BLACK;
789 #else
790     return COLOR_BLACK;
791 #endif
792 }
793 
794 static void
795 drv_initcolor(TERMINAL_CONTROL_BLOCK * TCB,
796 	      int color, int r, int g, int b)
797 {
798     SCREEN *sp = TCB->csp;
799 
800     AssertTCB();
801     if (initialize_color != NULL) {
802 	NCURSES_PUTP2("initialize_color",
803 		      TPARM_4(initialize_color, color, r, g, b));
804     }
805 }
806 
807 static void
808 drv_do_color(TERMINAL_CONTROL_BLOCK * TCB,
809 	     int old_pair,
810 	     int pair,
811 	     int reverse,
812 	     NCURSES_SP_OUTC outc)
813 {
814     SCREEN *sp = TCB->csp;
815     int fg = COLOR_DEFAULT;
816     int bg = COLOR_DEFAULT;
817     int old_fg, old_bg;
818 
819     AssertTCB();
820     if (sp == 0)
821 	return;
822 
823     if (pair < 0 || pair >= COLOR_PAIRS) {
824 	return;
825     } else if (pair != 0) {
826 	if (set_color_pair) {
827 	    TPUTS_TRACE("set_color_pair");
828 	    NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx
829 				    TPARM_1(set_color_pair, pair), 1, outc);
830 	    return;
831 	} else if (sp != 0) {
832 	    _nc_pair_content(SP_PARM, pair, &fg, &bg);
833 	}
834     }
835 
836     if (old_pair >= 0
837 	&& sp != 0
838 	&& _nc_pair_content(SP_PARM, old_pair, &old_fg, &old_bg) != ERR) {
839 	if ((isDefaultColor(fg) && !isDefaultColor(old_fg))
840 	    || (isDefaultColor(bg) && !isDefaultColor(old_bg))) {
841 #if NCURSES_EXT_FUNCS
842 	    /*
843 	     * A minor optimization - but extension.  If "AX" is specified in
844 	     * the terminal description, treat it as screen's indicator of ECMA
845 	     * SGR 39 and SGR 49, and assume the two sequences are independent.
846 	     */
847 	    if (sp->_has_sgr_39_49
848 		&& isDefaultColor(old_bg)
849 		&& !isDefaultColor(old_fg)) {
850 		NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx "\033[39m", 1, outc);
851 	    } else if (sp->_has_sgr_39_49
852 		       && isDefaultColor(old_fg)
853 		       && !isDefaultColor(old_bg)) {
854 		NCURSES_SP_NAME(tputs) (NCURSES_SP_ARGx "\033[49m", 1, outc);
855 	    } else
856 #endif
857 		drv_rescol(TCB);
858 	}
859     } else {
860 	drv_rescol(TCB);
861 	if (old_pair < 0)
862 	    return;
863     }
864 
865 #if NCURSES_EXT_FUNCS
866     if (isDefaultColor(fg))
867 	fg = default_fg(sp);
868     if (isDefaultColor(bg))
869 	bg = default_bg(sp);
870 #endif
871 
872     if (reverse) {
873 	int xx = fg;
874 	fg = bg;
875 	bg = xx;
876     }
877 
878     TR(TRACE_ATTRS, ("setting colors: pair = %d, fg = %d, bg = %d", pair,
879 		     fg, bg));
880 
881     if (!isDefaultColor(fg)) {
882 	drv_setcolor(TCB, TRUE, fg, outc);
883     }
884     if (!isDefaultColor(bg)) {
885 	drv_setcolor(TCB, FALSE, bg, outc);
886     }
887 }
888 
889 #define xterm_kmous "\033[M"
890 static void
891 init_xterm_mouse(SCREEN *sp)
892 {
893     sp->_mouse_type = M_XTERM;
894     sp->_mouse_xtermcap = NCURSES_SP_NAME(tigetstr) (NCURSES_SP_ARGx "XM");
895     if (!VALID_STRING(sp->_mouse_xtermcap))
896 	sp->_mouse_xtermcap = "\033[?1000%?%p1%{1}%=%th%el%;";
897 }
898 
899 static void
900 drv_initmouse(TERMINAL_CONTROL_BLOCK * TCB)
901 {
902     SCREEN *sp;
903 
904     AssertTCB();
905     SetSP();
906 
907     /* we know how to recognize mouse events under "xterm" */
908     if (sp != 0) {
909 	if (NonEmpty(key_mouse)) {
910 	    init_xterm_mouse(sp);
911 	} else if (strstr(SP_TERMTYPE term_names, "xterm") != 0) {
912 	    if (_nc_add_to_try(&(sp->_keytry), xterm_kmous, KEY_MOUSE) == OK)
913 		init_xterm_mouse(sp);
914 	}
915     }
916 }
917 
918 static int
919 drv_testmouse(TERMINAL_CONTROL_BLOCK * TCB,
920 	      int delay
921 	      EVENTLIST_2nd(_nc_eventlist * evl))
922 {
923     int rc = 0;
924     SCREEN *sp;
925 
926     AssertTCB();
927     SetSP();
928 
929 #if USE_SYSMOUSE
930     if ((sp->_mouse_type == M_SYSMOUSE)
931 	&& (sp->_sysmouse_head < sp->_sysmouse_tail)) {
932 	rc = TW_MOUSE;
933     } else
934 #endif
935     {
936 	rc = TCBOf(sp)->drv->td_twait(TCBOf(sp),
937 				      TWAIT_MASK,
938 				      delay,
939 				      (int *) 0
940 				      EVENTLIST_2nd(evl));
941 #if USE_SYSMOUSE
942 	if ((sp->_mouse_type == M_SYSMOUSE)
943 	    && (sp->_sysmouse_head < sp->_sysmouse_tail)
944 	    && (rc == 0)
945 	    && (errno == EINTR)) {
946 	    rc |= TW_MOUSE;
947 	}
948 #endif
949     }
950     return rc;
951 }
952 
953 static int
954 drv_mvcur(TERMINAL_CONTROL_BLOCK * TCB, int yold, int xold, int ynew, int xnew)
955 {
956     SCREEN *sp = TCB->csp;
957     AssertTCB();
958     return NCURSES_SP_NAME(_nc_mvcur) (sp, yold, xold, ynew, xnew);
959 }
960 
961 static void
962 drv_hwlabel(TERMINAL_CONTROL_BLOCK * TCB, int labnum, char *text)
963 {
964     SCREEN *sp = TCB->csp;
965 
966     AssertTCB();
967     if (labnum > 0 && labnum <= num_labels) {
968 	NCURSES_PUTP2("plab_norm",
969 		      TPARM_2(plab_norm, labnum, text));
970     }
971 }
972 
973 static void
974 drv_hwlabelOnOff(TERMINAL_CONTROL_BLOCK * TCB, int OnFlag)
975 {
976     SCREEN *sp = TCB->csp;
977 
978     AssertTCB();
979     if (OnFlag) {
980 	NCURSES_PUTP2("label_on", label_on);
981     } else {
982 	NCURSES_PUTP2("label_off", label_off);
983     }
984 }
985 
986 static chtype
987 drv_conattr(TERMINAL_CONTROL_BLOCK * TCB)
988 {
989     SCREEN *sp = TCB->csp;
990     chtype attrs = A_NORMAL;
991 
992     AssertTCB();
993     if (enter_alt_charset_mode)
994 	attrs |= A_ALTCHARSET;
995 
996     if (enter_blink_mode)
997 	attrs |= A_BLINK;
998 
999     if (enter_bold_mode)
1000 	attrs |= A_BOLD;
1001 
1002     if (enter_dim_mode)
1003 	attrs |= A_DIM;
1004 
1005     if (enter_reverse_mode)
1006 	attrs |= A_REVERSE;
1007 
1008     if (enter_standout_mode)
1009 	attrs |= A_STANDOUT;
1010 
1011     if (enter_protected_mode)
1012 	attrs |= A_PROTECT;
1013 
1014     if (enter_secure_mode)
1015 	attrs |= A_INVIS;
1016 
1017     if (enter_underline_mode)
1018 	attrs |= A_UNDERLINE;
1019 
1020     if (sp && sp->_coloron)
1021 	attrs |= A_COLOR;
1022 
1023 #if USE_ITALIC
1024     if (enter_italics_mode)
1025 	attrs |= A_ITALIC;
1026 #endif
1027 
1028     return (attrs);
1029 }
1030 
1031 static void
1032 drv_setfilter(TERMINAL_CONTROL_BLOCK * TCB)
1033 {
1034     AssertTCB();
1035 
1036     /* *INDENT-EQLS* */
1037     clear_screen     = ABSENT_STRING;
1038     cursor_address   = ABSENT_STRING;
1039     cursor_down      = ABSENT_STRING;
1040     cursor_up        = ABSENT_STRING;
1041     parm_down_cursor = ABSENT_STRING;
1042     parm_up_cursor   = ABSENT_STRING;
1043     row_address      = ABSENT_STRING;
1044     cursor_home      = carriage_return;
1045 
1046     if (back_color_erase)
1047 	clr_eos = ABSENT_STRING;
1048 }
1049 
1050 static void
1051 drv_initacs(TERMINAL_CONTROL_BLOCK * TCB, chtype *real_map, chtype *fake_map)
1052 {
1053     SCREEN *sp = TCB->csp;
1054 
1055     AssertTCB();
1056     assert(sp != 0);
1057     if (ena_acs != NULL) {
1058 	NCURSES_PUTP2("ena_acs", ena_acs);
1059     }
1060 #if NCURSES_EXT_FUNCS
1061     /*
1062      * Linux console "supports" the "PC ROM" character set by the coincidence
1063      * that smpch/rmpch and smacs/rmacs have the same values.  ncurses has
1064      * no codepage support (see SCO Merge for an example).  Outside of the
1065      * values defined in acsc, there are no definitions for the "PC ROM"
1066      * character set (assumed by some applications to be codepage 437), but we
1067      * allow those applications to use those codepoints.
1068      *
1069      * test/blue.c uses this feature.
1070      */
1071 #define PCH_KLUDGE(a,b) (a != 0 && b != 0 && !strcmp(a,b))
1072     if (PCH_KLUDGE(enter_pc_charset_mode, enter_alt_charset_mode) &&
1073 	PCH_KLUDGE(exit_pc_charset_mode, exit_alt_charset_mode)) {
1074 	size_t i;
1075 	for (i = 1; i < ACS_LEN; ++i) {
1076 	    if (real_map[i] == 0) {
1077 		real_map[i] = (chtype) i;
1078 		if (real_map != fake_map) {
1079 		    if (sp != 0)
1080 			sp->_screen_acs_map[i] = TRUE;
1081 		}
1082 	    }
1083 	}
1084     }
1085 #endif
1086 
1087     if (acs_chars != NULL) {
1088 	size_t i = 0;
1089 	size_t length = strlen(acs_chars);
1090 
1091 	while (i + 1 < length) {
1092 	    if (acs_chars[i] != 0 && UChar(acs_chars[i]) < ACS_LEN) {
1093 		real_map[UChar(acs_chars[i])] = UChar(acs_chars[i + 1]) | A_ALTCHARSET;
1094 		T(("#%d real_map[%s] = %s",
1095 		   (int) i,
1096 		   _tracechar(UChar(acs_chars[i])),
1097 		   _tracechtype(real_map[UChar(acs_chars[i])])));
1098 		if (sp != 0) {
1099 		    sp->_screen_acs_map[UChar(acs_chars[i])] = TRUE;
1100 		}
1101 	    }
1102 	    i += 2;
1103 	}
1104     }
1105 #ifdef TRACE
1106     /* Show the equivalent mapping, noting if it does not match the
1107      * given attribute, whether by re-ordering or duplication.
1108      */
1109     if (USE_TRACEF(TRACE_CALLS)) {
1110 	size_t n, m;
1111 	char show[ACS_LEN * 2 + 1];
1112 	for (n = 1, m = 0; n < ACS_LEN; n++) {
1113 	    if (real_map[n] != 0) {
1114 		show[m++] = (char) n;
1115 		show[m++] = (char) ChCharOf(real_map[n]);
1116 	    }
1117 	}
1118 	show[m] = 0;
1119 	if (acs_chars == NULL || strcmp(acs_chars, show))
1120 	    _tracef("%s acs_chars %s",
1121 		    (acs_chars == NULL) ? "NULL" : "READ",
1122 		    _nc_visbuf(acs_chars));
1123 	_tracef("%s acs_chars %s",
1124 		(acs_chars == NULL)
1125 		? "NULL"
1126 		: (strcmp(acs_chars, show)
1127 		   ? "DIFF"
1128 		   : "SAME"),
1129 		_nc_visbuf(show));
1130 	_nc_unlock_global(tracef);
1131     }
1132 #endif /* TRACE */
1133 }
1134 
1135 #define ENSURE_TINFO(sp) (TCBOf(sp)->drv->isTerminfo)
1136 
1137 NCURSES_EXPORT(void)
1138 _nc_cookie_init(SCREEN *sp)
1139 {
1140     bool support_cookies = USE_XMC_SUPPORT;
1141     TERMINAL_CONTROL_BLOCK *TCB = (TERMINAL_CONTROL_BLOCK *) (sp->_term);
1142 
1143     if (sp == 0 || !ENSURE_TINFO(sp))
1144 	return;
1145 
1146 #if USE_XMC_SUPPORT
1147     /*
1148      * If we have no magic-cookie support compiled-in, or if it is suppressed
1149      * in the environment, reset the support-flag.
1150      */
1151     if (magic_cookie_glitch >= 0) {
1152 	if (getenv("NCURSES_NO_MAGIC_COOKIE") != 0) {
1153 	    support_cookies = FALSE;
1154 	}
1155     }
1156 #endif
1157 
1158     if (!support_cookies && magic_cookie_glitch >= 0) {
1159 	T(("will disable attributes to work w/o magic cookies"));
1160     }
1161 
1162     if (magic_cookie_glitch > 0) {	/* tvi, wyse */
1163 
1164 	sp->_xmc_triggers = sp->_ok_attributes & XMC_CONFLICT;
1165 #if 0
1166 	/*
1167 	 * We "should" treat colors as an attribute.  The wyse350 (and its
1168 	 * clones) appear to be the only ones that have both colors and magic
1169 	 * cookies.
1170 	 */
1171 	if (has_colors()) {
1172 	    sp->_xmc_triggers |= A_COLOR;
1173 	}
1174 #endif
1175 	sp->_xmc_suppress = sp->_xmc_triggers & (chtype) ~(A_BOLD);
1176 
1177 	T(("magic cookie attributes %s", _traceattr(sp->_xmc_suppress)));
1178 	/*
1179 	 * Supporting line-drawing may be possible.  But make the regular
1180 	 * video attributes work first.
1181 	 */
1182 	acs_chars = ABSENT_STRING;
1183 	ena_acs = ABSENT_STRING;
1184 	enter_alt_charset_mode = ABSENT_STRING;
1185 	exit_alt_charset_mode = ABSENT_STRING;
1186 #if USE_XMC_SUPPORT
1187 	/*
1188 	 * To keep the cookie support simple, suppress all of the optimization
1189 	 * hooks except for clear_screen and the cursor addressing.
1190 	 */
1191 	if (support_cookies) {
1192 	    clr_eol = ABSENT_STRING;
1193 	    clr_eos = ABSENT_STRING;
1194 	    set_attributes = ABSENT_STRING;
1195 	}
1196 #endif
1197     } else if (magic_cookie_glitch == 0) {	/* hpterm */
1198     }
1199 
1200     /*
1201      * If magic cookies are not supported, cancel the strings that set
1202      * video attributes.
1203      */
1204     if (!support_cookies && magic_cookie_glitch >= 0) {
1205 	magic_cookie_glitch = ABSENT_NUMERIC;
1206 	set_attributes = ABSENT_STRING;
1207 	enter_blink_mode = ABSENT_STRING;
1208 	enter_bold_mode = ABSENT_STRING;
1209 	enter_dim_mode = ABSENT_STRING;
1210 	enter_reverse_mode = ABSENT_STRING;
1211 	enter_standout_mode = ABSENT_STRING;
1212 	enter_underline_mode = ABSENT_STRING;
1213     }
1214 
1215     /* initialize normal acs before wide, since we use mapping in the latter */
1216 #if !USE_WIDEC_SUPPORT
1217     if (_nc_unicode_locale() && _nc_locale_breaks_acs(sp->_term)) {
1218 	acs_chars = NULL;
1219 	ena_acs = NULL;
1220 	enter_alt_charset_mode = NULL;
1221 	exit_alt_charset_mode = NULL;
1222 	set_attributes = NULL;
1223     }
1224 #endif
1225 }
1226 
1227 static int
1228 drv_twait(TERMINAL_CONTROL_BLOCK * TCB,
1229 	  int mode,
1230 	  int milliseconds,
1231 	  int *timeleft
1232 	  EVENTLIST_2nd(_nc_eventlist * evl))
1233 {
1234     SCREEN *sp;
1235 
1236     AssertTCB();
1237     SetSP();
1238 
1239     return _nc_timed_wait(sp, mode, milliseconds, timeleft EVENTLIST_2nd(evl));
1240 }
1241 
1242 static int
1243 drv_read(TERMINAL_CONTROL_BLOCK * TCB, int *buf)
1244 {
1245     SCREEN *sp;
1246     unsigned char c2 = 0;
1247     int n;
1248 
1249     AssertTCB();
1250     assert(buf);
1251     SetSP();
1252 
1253 # if USE_PTHREADS_EINTR
1254     if ((pthread_self) && (pthread_kill) && (pthread_equal))
1255 	_nc_globals.read_thread = pthread_self();
1256 # endif
1257     n = (int) read(sp->_ifd, &c2, (size_t) 1);
1258 #if USE_PTHREADS_EINTR
1259     _nc_globals.read_thread = 0;
1260 #endif
1261     *buf = (int) c2;
1262     return n;
1263 }
1264 
1265 static int
1266 drv_nap(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int ms)
1267 {
1268 #if HAVE_NANOSLEEP
1269     {
1270 	struct timespec request, remaining;
1271 	request.tv_sec = ms / 1000;
1272 	request.tv_nsec = (ms % 1000) * 1000000;
1273 	while (nanosleep(&request, &remaining) == -1
1274 	       && errno == EINTR) {
1275 	    request = remaining;
1276 	}
1277     }
1278 #else
1279     _nc_timed_wait(0, 0, ms, (int *) 0 EVENTLIST_2nd(0));
1280 #endif
1281     return OK;
1282 }
1283 
1284 static int
1285 __nc_putp(SCREEN *sp, const char *name GCC_UNUSED, const char *value)
1286 {
1287     int rc = ERR;
1288 
1289     if (value) {
1290 	rc = NCURSES_PUTP2(name, value);
1291     }
1292     return rc;
1293 }
1294 
1295 static int
1296 __nc_putp_flush(SCREEN *sp, const char *name, const char *value)
1297 {
1298     int rc = __nc_putp(sp, name, value);
1299     if (rc != ERR) {
1300 	NCURSES_SP_NAME(_nc_flush) (sp);
1301     }
1302     return rc;
1303 }
1304 
1305 static int
1306 drv_kpad(TERMINAL_CONTROL_BLOCK * TCB, int flag)
1307 {
1308     int ret = ERR;
1309     SCREEN *sp;
1310 
1311     AssertTCB();
1312 
1313     sp = TCB->csp;
1314 
1315     if (sp) {
1316 	if (flag) {
1317 	    (void) __nc_putp_flush(sp, "keypad_xmit", keypad_xmit);
1318 	} else if (!flag && keypad_local) {
1319 	    (void) __nc_putp_flush(sp, "keypad_local", keypad_local);
1320 	}
1321 	if (flag && !sp->_tried) {
1322 	    _nc_init_keytry(sp);
1323 	    sp->_tried = TRUE;
1324 	}
1325 	ret = OK;
1326     }
1327 
1328     return ret;
1329 }
1330 
1331 static int
1332 drv_keyok(TERMINAL_CONTROL_BLOCK * TCB, int c, int flag)
1333 {
1334     SCREEN *sp;
1335     int code = ERR;
1336     int count = 0;
1337     char *s;
1338 
1339     AssertTCB();
1340     SetSP();
1341 
1342     if (c >= 0) {
1343 	unsigned ch = (unsigned) c;
1344 	if (flag) {
1345 	    while ((s = _nc_expand_try(sp->_key_ok,
1346 				       ch, &count, (size_t) 0)) != 0) {
1347 		if (_nc_remove_key(&(sp->_key_ok), ch)) {
1348 		    code = _nc_add_to_try(&(sp->_keytry), s, ch);
1349 		    free(s);
1350 		    count = 0;
1351 		    if (code != OK)
1352 			break;
1353 		} else {
1354 		    free(s);
1355 		}
1356 	    }
1357 	} else {
1358 	    while ((s = _nc_expand_try(sp->_keytry,
1359 				       ch, &count, (size_t) 0)) != 0) {
1360 		if (_nc_remove_key(&(sp->_keytry), ch)) {
1361 		    code = _nc_add_to_try(&(sp->_key_ok), s, ch);
1362 		    free(s);
1363 		    count = 0;
1364 		    if (code != OK)
1365 			break;
1366 		} else {
1367 		    free(s);
1368 		}
1369 	    }
1370 	}
1371     }
1372     return (code);
1373 }
1374 
1375 static int
1376 drv_cursorSet(TERMINAL_CONTROL_BLOCK * TCB, int vis)
1377 {
1378     SCREEN *sp;
1379     int code = ERR;
1380 
1381     AssertTCB();
1382     SetSP();
1383 
1384     T((T_CALLED("tinfo:drv_cursorSet(%p,%d)"), (void *) SP_PARM, vis));
1385 
1386     if (SP_PARM != 0 && IsTermInfo(SP_PARM)) {
1387 	switch (vis) {
1388 	case 2:
1389 	    code = NCURSES_PUTP2_FLUSH("cursor_visible", cursor_visible);
1390 	    break;
1391 	case 1:
1392 	    code = NCURSES_PUTP2_FLUSH("cursor_normal", cursor_normal);
1393 	    break;
1394 	case 0:
1395 	    code = NCURSES_PUTP2_FLUSH("cursor_invisible", cursor_invisible);
1396 	    break;
1397 	}
1398     } else {
1399 	code = ERR;
1400     }
1401     returnCode(code);
1402 }
1403 
1404 static bool
1405 drv_kyExist(TERMINAL_CONTROL_BLOCK * TCB, int key)
1406 {
1407     bool res = FALSE;
1408 
1409     AssertTCB();
1410     if (TCB->csp)
1411 	res = TINFO_HAS_KEY(TCB->csp, key) == 0 ? FALSE : TRUE;
1412 
1413     return res;
1414 }
1415 
1416 NCURSES_EXPORT_VAR (TERM_DRIVER) _nc_TINFO_DRIVER = {
1417     TRUE,
1418 	drv_Name,		/* Name */
1419 	drv_CanHandle,		/* CanHandle */
1420 	drv_init,		/* init */
1421 	drv_release,		/* release */
1422 	drv_size,		/* size */
1423 	drv_sgmode,		/* sgmode */
1424 	drv_conattr,		/* conattr */
1425 	drv_mvcur,		/* hwcur */
1426 	drv_mode,		/* mode */
1427 	drv_rescol,		/* rescol */
1428 	drv_rescolors,		/* rescolors */
1429 	drv_setcolor,		/* color */
1430 	drv_dobeepflash,	/* doBeepOrFlash */
1431 	drv_initpair,		/* initpair */
1432 	drv_initcolor,		/* initcolor */
1433 	drv_do_color,		/* docolor */
1434 	drv_initmouse,		/* initmouse */
1435 	drv_testmouse,		/* testmouse */
1436 	drv_setfilter,		/* setfilter */
1437 	drv_hwlabel,		/* hwlabel */
1438 	drv_hwlabelOnOff,	/* hwlabelOnOff */
1439 	drv_doupdate,		/* update */
1440 	drv_defaultcolors,	/* defaultcolors */
1441 	drv_print,		/* print */
1442 	drv_getsize,		/* getsize */
1443 	drv_setsize,		/* setsize */
1444 	drv_initacs,		/* initacs */
1445 	drv_screen_init,	/* scinit */
1446 	drv_wrap,		/* scexit */
1447 	drv_twait,		/* twait  */
1448 	drv_read,		/* read */
1449 	drv_nap,		/* nap */
1450 	drv_kpad,		/* kpad */
1451 	drv_keyok,		/* kyOk */
1452 	drv_kyExist,		/* kyExist */
1453 	drv_cursorSet		/* cursorSet */
1454 };
1455