ncurses 6.2 - patch 20200404
[ncurses.git] / ncurses / tinfo / tinfo_driver.c
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 };