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