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