1 /* $OpenBSD: lib_setup.c,v 1.14 2023/10/17 09:52:09 nicm Exp $ */
2
3 /****************************************************************************
4 * Copyright 2018-2022,2023 Thomas E. Dickey *
5 * Copyright 1998-2016,2017 Free Software Foundation, Inc. *
6 * *
7 * Permission is hereby granted, free of charge, to any person obtaining a *
8 * copy of this software and associated documentation files (the *
9 * "Software"), to deal in the Software without restriction, including *
10 * without limitation the rights to use, copy, modify, merge, publish, *
11 * distribute, distribute with modifications, sublicense, and/or sell *
12 * copies of the Software, and to permit persons to whom the Software is *
13 * furnished to do so, subject to the following conditions: *
14 * *
15 * The above copyright notice and this permission notice shall be included *
16 * in all copies or substantial portions of the Software. *
17 * *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
21 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
24 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
25 * *
26 * Except as contained in this notice, the name(s) of the above copyright *
27 * holders shall not be used in advertising or otherwise to promote the *
28 * sale, use or other dealings in this Software without prior written *
29 * authorization. *
30 ****************************************************************************/
31
32 /****************************************************************************
33 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 *
34 * and: Eric S. Raymond <esr@snark.thyrsus.com> *
35 * and: Thomas E. Dickey 1996-on *
36 * and: Juergen Pfeifer 2009 *
37 ****************************************************************************/
38
39 /*
40 * Terminal setup routines common to termcap and terminfo:
41 *
42 * use_env(bool)
43 * use_tioctl(bool)
44 * setupterm(char *, int, int *)
45 */
46
47 #include <curses.priv.h>
48 #include <tic.h> /* for MAX_NAME_SIZE */
49
50 #if HAVE_LOCALE_H
51 #include <locale.h>
52 #endif
53
54 MODULE_ID("$Id: lib_setup.c,v 1.14 2023/10/17 09:52:09 nicm Exp $")
55
56 /****************************************************************************
57 *
58 * Terminal size computation
59 *
60 ****************************************************************************/
61
62 #if HAVE_SIZECHANGE
63 # if !defined(sun) || !TERMIOS
64 # if HAVE_SYS_IOCTL_H
65 # include <sys/ioctl.h>
66 # endif
67 # endif
68 #endif
69
70 #if NEED_PTEM_H
71 /* On SCO, they neglected to define struct winsize in termios.h -- it is only
72 * in termio.h and ptem.h (the former conflicts with other definitions).
73 */
74 # include <sys/stream.h>
75 # include <sys/ptem.h>
76 #endif
77
78 #if HAVE_LANGINFO_CODESET
79 #include <langinfo.h>
80 #endif
81
82 /*
83 * SCO defines TIOCGSIZE and the corresponding struct. Other systems (SunOS,
84 * Solaris, IRIX) define TIOCGWINSZ and struct winsize.
85 */
86 #ifdef TIOCGSIZE
87 # define IOCTL_WINSIZE TIOCGSIZE
88 # define STRUCT_WINSIZE struct ttysize
89 # define WINSIZE_ROWS(n) (int)n.ts_lines
90 # define WINSIZE_COLS(n) (int)n.ts_cols
91 #else
92 # ifdef TIOCGWINSZ
93 # define IOCTL_WINSIZE TIOCGWINSZ
94 # define STRUCT_WINSIZE struct winsize
95 # define WINSIZE_ROWS(n) (int)n.ws_row
96 # define WINSIZE_COLS(n) (int)n.ws_col
97 # endif
98 #endif
99
100 /*
101 * Reduce explicit use of "cur_term" global variable.
102 */
103 #undef CUR
104 #define CUR TerminalType(termp).
105
106 /*
107 * Wrap global variables in this module.
108 */
109 #if USE_REENTRANT
110
NCURSES_EXPORT(char *)111 NCURSES_EXPORT(char *)
112 NCURSES_PUBLIC_VAR(ttytype) (void)
113 {
114 static char empty[] = "";
115 char *result = empty;
116
117 #if NCURSES_SP_FUNCS
118 if (CURRENT_SCREEN) {
119 TERMINAL *termp = TerminalOf(CURRENT_SCREEN);
120 if (termp != 0) {
121 result = TerminalType(termp).term_names;
122 }
123 }
124 #else
125 if (cur_term != 0) {
126 result = TerminalType(cur_term).term_names;
127 }
128 #endif
129 return result;
130 }
131
132 NCURSES_EXPORT(int *)
_nc_ptr_Lines(SCREEN * sp)133 _nc_ptr_Lines(SCREEN *sp)
134 {
135 return ptrLines(sp);
136 }
137
138 NCURSES_EXPORT(int)
NCURSES_PUBLIC_VAR(LINES)139 NCURSES_PUBLIC_VAR(LINES) (void)
140 {
141 return *_nc_ptr_Lines(CURRENT_SCREEN);
142 }
143
144 NCURSES_EXPORT(int *)
_nc_ptr_Cols(SCREEN * sp)145 _nc_ptr_Cols(SCREEN *sp)
146 {
147 return ptrCols(sp);
148 }
149
150 NCURSES_EXPORT(int)
NCURSES_PUBLIC_VAR(COLS)151 NCURSES_PUBLIC_VAR(COLS) (void)
152 {
153 return *_nc_ptr_Cols(CURRENT_SCREEN);
154 }
155
156 NCURSES_EXPORT(int *)
_nc_ptr_Tabsize(SCREEN * sp)157 _nc_ptr_Tabsize(SCREEN *sp)
158 {
159 return ptrTabsize(sp);
160 }
161
162 NCURSES_EXPORT(int)
NCURSES_PUBLIC_VAR(TABSIZE)163 NCURSES_PUBLIC_VAR(TABSIZE) (void)
164 {
165 return *_nc_ptr_Tabsize(CURRENT_SCREEN);
166 }
167 #else
168 NCURSES_EXPORT_VAR(char) ttytype[NAMESIZE] = "";
169 NCURSES_EXPORT_VAR(int) LINES = 0;
170 NCURSES_EXPORT_VAR(int) COLS = 0;
171 NCURSES_EXPORT_VAR(int) TABSIZE = 8;
172 #endif
173
174 #if NCURSES_EXT_FUNCS
175 NCURSES_EXPORT(int)
NCURSES_SP_NAME(set_tabsize)176 NCURSES_SP_NAME(set_tabsize) (NCURSES_SP_DCLx int value)
177 {
178 int code = OK;
179 if (value <= 0) {
180 code = ERR;
181 } else {
182 #if USE_REENTRANT
183 if (SP_PARM) {
184 SP_PARM->_TABSIZE = value;
185 } else {
186 code = ERR;
187 }
188 #else
189 (void) SP_PARM;
190 TABSIZE = value;
191 #endif
192 }
193 return code;
194 }
195
196 #if NCURSES_SP_FUNCS
197 NCURSES_EXPORT(int)
set_tabsize(int value)198 set_tabsize(int value)
199 {
200 return NCURSES_SP_NAME(set_tabsize) (CURRENT_SCREEN, value);
201 }
202 #endif
203 #endif /* NCURSES_EXT_FUNCS */
204
205 #if USE_SIGWINCH
206 /*
207 * If we have a pending SIGWINCH, set the flag in each screen.
208 */
209 NCURSES_EXPORT(int)
_nc_handle_sigwinch(SCREEN * sp)210 _nc_handle_sigwinch(SCREEN *sp)
211 {
212 SCREEN *scan;
213
214 if (_nc_globals.have_sigwinch) {
215 _nc_globals.have_sigwinch = 0;
216
217 for (each_screen(scan)) {
218 scan->_sig_winch = TRUE;
219 }
220 }
221
222 return (sp ? sp->_sig_winch : 0);
223 }
224
225 #endif
226
227 NCURSES_EXPORT(void)
NCURSES_SP_NAME(use_env)228 NCURSES_SP_NAME(use_env) (NCURSES_SP_DCLx bool f)
229 {
230 START_TRACE();
231 T((T_CALLED("use_env(%p,%d)"), (void *) SP_PARM, (int) f));
232 #if NCURSES_SP_FUNCS
233 if (IsPreScreen(SP_PARM)) {
234 SP_PARM->_use_env = f;
235 }
236 #else
237 _nc_prescreen.use_env = f;
238 #endif
239 returnVoid;
240 }
241
242 NCURSES_EXPORT(void)
NCURSES_SP_NAME(use_tioctl)243 NCURSES_SP_NAME(use_tioctl) (NCURSES_SP_DCLx bool f)
244 {
245 START_TRACE();
246 T((T_CALLED("use_tioctl(%p,%d)"), (void *) SP_PARM, (int) f));
247 #if NCURSES_SP_FUNCS
248 if (IsPreScreen(SP_PARM)) {
249 SP_PARM->use_tioctl = f;
250 }
251 #else
252 _nc_prescreen.use_tioctl = f;
253 #endif
254 returnVoid;
255 }
256
257 #if NCURSES_SP_FUNCS
258 NCURSES_EXPORT(void)
use_env(bool f)259 use_env(bool f)
260 {
261 START_TRACE();
262 T((T_CALLED("use_env(%d)"), (int) f));
263 _nc_prescreen.use_env = f;
264 returnVoid;
265 }
266
267 NCURSES_EXPORT(void)
use_tioctl(bool f)268 use_tioctl(bool f)
269 {
270 START_TRACE();
271 T((T_CALLED("use_tioctl(%d)"), (int) f));
272 _nc_prescreen.use_tioctl = f;
273 returnVoid;
274 }
275 #endif
276
277 NCURSES_EXPORT(void)
_nc_get_screensize(SCREEN * sp,TERMINAL * termp,int * linep,int * colp)278 _nc_get_screensize(SCREEN *sp,
279 #ifdef USE_TERM_DRIVER
280 TERMINAL *termp,
281 #endif
282 int *linep, int *colp)
283 /* Obtain lines/columns values from the environment and/or terminfo entry */
284 {
285 #ifdef USE_TERM_DRIVER
286 TERMINAL_CONTROL_BLOCK *TCB;
287 int my_tabsize;
288
289 assert(termp != 0 && linep != 0 && colp != 0);
290 TCB = (TERMINAL_CONTROL_BLOCK *) termp;
291
292 my_tabsize = TCB->info.tabsize;
293 TCB->drv->td_size(TCB, linep, colp);
294
295 #if USE_REENTRANT
296 if (sp != 0) {
297 sp->_TABSIZE = my_tabsize;
298 }
299 #else
300 (void) sp;
301 TABSIZE = my_tabsize;
302 #endif
303 T(("TABSIZE = %d", my_tabsize));
304 #else /* !USE_TERM_DRIVER */
305 TERMINAL *termp = cur_term;
306 int my_tabsize;
307 bool useEnv = _nc_prescreen.use_env;
308 bool useTioctl = _nc_prescreen.use_tioctl;
309
310 #ifdef EXP_WIN32_DRIVER
311 /* If we are here, then Windows console is used in terminfo mode.
312 We need to figure out the size using the console API
313 */
314 _nc_console_size(linep, colp);
315 T(("screen size: winconsole lines = %d columns = %d", *linep, *colp));
316 #else
317 /* figure out the size of the screen */
318 T(("screen size: terminfo lines = %d columns = %d", lines, columns));
319
320 *linep = (int) lines;
321 *colp = (int) columns;
322 #endif
323
324 #if NCURSES_SP_FUNCS
325 if (sp) {
326 useEnv = sp->_use_env;
327 useTioctl = sp->use_tioctl;
328 }
329 #endif
330
331 if (useEnv || useTioctl) {
332 #ifdef __EMX__
333 {
334 int screendata[2];
335 _scrsize(screendata);
336 *colp = screendata[0];
337 *linep = ((sp != 0 && sp->_filtered)
338 ? 1
339 : screendata[1]);
340 T(("EMX screen size: environment LINES = %d COLUMNS = %d",
341 *linep, *colp));
342 }
343 #endif
344 #if HAVE_SIZECHANGE
345 /* try asking the OS */
346 if (NC_ISATTY(cur_term->Filedes)) {
347 STRUCT_WINSIZE size;
348
349 errno = 0;
350 do {
351 if (ioctl(cur_term->Filedes, IOCTL_WINSIZE, &size) >= 0) {
352 *linep = ((sp != 0 && sp->_filtered)
353 ? 1
354 : WINSIZE_ROWS(size));
355 *colp = WINSIZE_COLS(size);
356 T(("SYS screen size: environment LINES = %d COLUMNS = %d",
357 *linep, *colp));
358 break;
359 }
360 } while
361 (errno == EINTR);
362 }
363 #endif /* HAVE_SIZECHANGE */
364
365 if (useEnv) {
366 int value;
367
368 if (useTioctl) {
369 /*
370 * If environment variables are used, update them.
371 */
372 if ((sp == 0 || !sp->_filtered) && _nc_getenv_num("LINES") > 0) {
373 _nc_setenv_num("LINES", *linep);
374 }
375 if (_nc_getenv_num("COLUMNS") > 0) {
376 _nc_setenv_num("COLUMNS", *colp);
377 }
378 }
379
380 /*
381 * Finally, look for environment variables.
382 *
383 * Solaris lets users override either dimension with an environment
384 * variable.
385 */
386 if ((value = _nc_getenv_num("LINES")) > 0) {
387 *linep = value;
388 T(("screen size: environment LINES = %d", *linep));
389 }
390 if ((value = _nc_getenv_num("COLUMNS")) > 0) {
391 *colp = value;
392 T(("screen size: environment COLUMNS = %d", *colp));
393 }
394 }
395
396 /* if we can't get dynamic info about the size, use static */
397 if (*linep <= 0) {
398 *linep = (int) lines;
399 }
400 if (*colp <= 0) {
401 *colp = (int) columns;
402 }
403
404 /* the ultimate fallback, assume fixed 24x80 size */
405 if (*linep <= 0) {
406 *linep = 24;
407 }
408 if (*colp <= 0) {
409 *colp = 80;
410 }
411
412 /*
413 * Put the derived values back in the screen-size caps, so
414 * tigetnum() and tgetnum() will do the right thing.
415 */
416 lines = (NCURSES_INT2) (*linep);
417 columns = (NCURSES_INT2) (*colp);
418 #if NCURSES_EXT_NUMBERS
419 #define OldNumber(termp,name) \
420 (termp)->type.Numbers[(&name - (termp)->type2.Numbers)]
421 OldNumber(termp, lines) = (short) (*linep);
422 OldNumber(termp, columns) = (short) (*colp);
423 #endif
424 }
425
426 T(("screen size is %dx%d", *linep, *colp));
427
428 if (VALID_NUMERIC(init_tabs))
429 my_tabsize = (int) init_tabs;
430 else
431 my_tabsize = 8;
432
433 #if USE_REENTRANT
434 if (sp != 0)
435 sp->_TABSIZE = my_tabsize;
436 #else
437 TABSIZE = my_tabsize;
438 #endif
439 T(("TABSIZE = %d", TABSIZE));
440 #endif /* USE_TERM_DRIVER */
441 }
442
443 #if USE_SIZECHANGE
444 NCURSES_EXPORT(void)
_nc_update_screensize(SCREEN * sp)445 _nc_update_screensize(SCREEN *sp)
446 {
447 int new_lines;
448 int new_cols;
449
450 #ifdef USE_TERM_DRIVER
451 int old_lines;
452 int old_cols;
453
454 assert(sp != 0);
455
456 CallDriver_2(sp, td_getsize, &old_lines, &old_cols);
457
458 #else
459 TERMINAL *termp = cur_term;
460 int old_lines = lines;
461 int old_cols = columns;
462 #endif
463
464 if (sp != 0) {
465 TINFO_GET_SIZE(sp, sp->_term, &new_lines, &new_cols);
466 /*
467 * See is_term_resized() and resizeterm().
468 * We're doing it this way because those functions belong to the upper
469 * ncurses library, while this resides in the lower terminfo library.
470 */
471 if (sp->_resize != 0) {
472 if ((new_lines != old_lines) || (new_cols != old_cols)) {
473 sp->_resize(NCURSES_SP_ARGx new_lines, new_cols);
474 } else if (sp->_sig_winch && (sp->_ungetch != 0)) {
475 sp->_ungetch(SP_PARM, KEY_RESIZE); /* so application can know this */
476 }
477 sp->_sig_winch = FALSE;
478 }
479 }
480 }
481 #endif /* USE_SIZECHANGE */
482
483 /****************************************************************************
484 *
485 * Terminal setup
486 *
487 ****************************************************************************/
488
489 #if NCURSES_USE_DATABASE || NCURSES_USE_TERMCAP
490 /*
491 * Return 1 if entry found, 0 if not found, -1 if database not accessible,
492 * just like tgetent().
493 */
494 int
_nc_setup_tinfo(const char * const tn,TERMTYPE2 * const tp)495 _nc_setup_tinfo(const char *const tn, TERMTYPE2 *const tp)
496 {
497 char filename[PATH_MAX];
498 int status = _nc_read_entry2(tn, filename, tp);
499
500 /*
501 * If we have an entry, force all of the cancelled strings to null
502 * pointers so we don't have to test them in the rest of the library.
503 * (The terminfo compiler bypasses this logic, since it must know if
504 * a string is cancelled, for merging entries).
505 */
506 if (status == TGETENT_YES) {
507 unsigned n;
508 for_each_boolean(n, tp) {
509 if (!VALID_BOOLEAN(tp->Booleans[n]))
510 tp->Booleans[n] = FALSE;
511 }
512 for_each_string(n, tp) {
513 if (tp->Strings[n] == CANCELLED_STRING)
514 tp->Strings[n] = ABSENT_STRING;
515 }
516 }
517 return (status);
518 }
519 #endif
520
521 /*
522 ** Take the real command character out of the CC environment variable
523 ** and substitute it in for the prototype given in 'command_character'.
524 */
525 void
_nc_tinfo_cmdch(TERMINAL * termp,int proto)526 _nc_tinfo_cmdch(TERMINAL *termp, int proto)
527 {
528 char *tmp;
529
530 /*
531 * Only use the character if the string is a single character,
532 * since it is fairly common for developers to set the C compiler
533 * name as an environment variable - using the same symbol.
534 */
535 if ((tmp = getenv("CC")) != 0 && strlen(tmp) == 1) {
536 unsigned i;
537 char CC = *tmp;
538
539 for_each_string(i, &(termp->type)) {
540 for (tmp = termp->type.Strings[i]; tmp && *tmp; tmp++) {
541 if (UChar(*tmp) == proto)
542 *tmp = CC;
543 }
544 }
545 }
546 }
547
548 /*
549 * Find the locale which is in effect.
550 */
551 NCURSES_EXPORT(char *)
_nc_get_locale(void)552 _nc_get_locale(void)
553 {
554 char *env;
555 #if HAVE_LOCALE_H
556 /*
557 * This is preferable to using getenv() since it ensures that we are using
558 * the locale which was actually initialized by the application.
559 */
560 env = setlocale(LC_CTYPE, 0);
561 #else
562 if (((env = getenv("LANG")) != 0 && *env != '\0')
563 || ((env = getenv("LC_CTYPE")) != 0 && *env != '\0')
564 || ((env = getenv("LC_ALL")) != 0 && *env != '\0')) {
565 ;
566 }
567 #endif
568 T(("_nc_get_locale %s", _nc_visbuf(env)));
569 return env;
570 }
571
572 /*
573 * Check if we are running in a UTF-8 locale.
574 */
575 NCURSES_EXPORT(int)
_nc_unicode_locale(void)576 _nc_unicode_locale(void)
577 {
578 int result = 0;
579 #if defined(_NC_WINDOWS) && USE_WIDEC_SUPPORT
580 result = 1;
581 #elif HAVE_LANGINFO_CODESET
582 char *env = nl_langinfo(CODESET);
583 result = !strcmp(env, "UTF-8");
584 T(("_nc_unicode_locale(%s) ->%d", env, result));
585 #else
586 char *env = _nc_get_locale();
587 if (env != 0) {
588 if (strstr(env, ".UTF-8") != 0) {
589 result = 1;
590 T(("_nc_unicode_locale(%s) ->%d", env, result));
591 }
592 }
593 #endif
594 return result;
595 }
596
597 #define CONTROL_N(s) ((s) != 0 && strstr(s, "\016") != 0)
598 #define CONTROL_O(s) ((s) != 0 && strstr(s, "\017") != 0)
599
600 /*
601 * Check for known broken cases where a UTF-8 locale breaks the alternate
602 * character set.
603 */
604 NCURSES_EXPORT(int)
_nc_locale_breaks_acs(TERMINAL * termp)605 _nc_locale_breaks_acs(TERMINAL *termp)
606 {
607 const char *env_name = "NCURSES_NO_UTF8_ACS";
608 const char *env;
609 int value;
610 int result = 0;
611
612 T((T_CALLED("_nc_locale_breaks_acs:%d"), result));
613 if (getenv(env_name) != 0) {
614 result = _nc_getenv_num(env_name);
615 } else if ((value = tigetnum("U8")) >= 0) {
616 result = value; /* use extension feature */
617 } else if ((env = getenv("TERM")) != 0) {
618 if (strstr(env, "linux")) {
619 result = 1; /* always broken */
620 } else if (strstr(env, "screen") != 0
621 && ((env = getenv("TERMCAP")) != 0
622 && strstr(env, "screen") != 0)
623 && strstr(env, "hhII00") != 0) {
624 if (CONTROL_N(enter_alt_charset_mode) ||
625 CONTROL_O(enter_alt_charset_mode) ||
626 CONTROL_N(set_attributes) ||
627 CONTROL_O(set_attributes)) {
628 result = 1;
629 }
630 }
631 }
632 returnCode(result);
633 }
634
635 NCURSES_EXPORT(int)
TINFO_SETUP_TERM(TERMINAL ** tp,const char * tname,int Filedes,int * errret,int reuse)636 TINFO_SETUP_TERM(TERMINAL **tp,
637 const char *tname,
638 int Filedes,
639 int *errret,
640 int reuse)
641 {
642 #ifdef USE_TERM_DRIVER
643 TERMINAL_CONTROL_BLOCK *TCB = 0;
644 #endif
645 TERMINAL *termp;
646 SCREEN *sp = 0;
647 char *myname;
648 int code = ERR;
649
650 START_TRACE();
651
652 #ifdef USE_TERM_DRIVER
653 T((T_CALLED("_nc_setupterm_ex(%p,%s,%d,%p)"),
654 (void *) tp, _nc_visbuf(tname), Filedes, (void *) errret));
655
656 if (tp == 0) {
657 ret_error0(TGETENT_ERR,
658 "Invalid parameter, internal error.\n");
659 } else
660 termp = *tp;
661 #else
662 termp = cur_term;
663 T((T_CALLED("setupterm(%s,%d,%p)"), _nc_visbuf(tname), Filedes, (void *) errret));
664 #endif
665
666 if (tname == 0) {
667 tname = getenv("TERM");
668 #if defined(EXP_WIN32_DRIVER)
669 if (!VALID_TERM_ENV(tname, NO_TERMINAL)) {
670 T(("Failure with TERM=%s", NonNull(tname)));
671 ret_error0(TGETENT_ERR, "TERM environment variable not set.\n");
672 }
673 #elif defined(USE_TERM_DRIVER)
674 if (!NonEmpty(tname))
675 tname = "unknown";
676 #else
677 if (!NonEmpty(tname)) {
678 T(("Failure with TERM=%s", NonNull(tname)));
679 ret_error0(TGETENT_ERR, "TERM environment variable not set.\n");
680 }
681 #endif
682 }
683 myname = strdup(tname);
684 if (myname == NULL || strlen(myname) > MAX_NAME_SIZE) {
685 ret_error(TGETENT_ERR,
686 "TERM environment must be 1..%d characters.\n",
687 MAX_NAME_SIZE,
688 free(myname));
689 }
690
691 T(("your terminal name is %s", myname));
692
693 /*
694 * Allow output redirection. This is what SVr3 does. If stdout is
695 * directed to a file, screen updates go to standard error.
696 */
697 if (Filedes == STDOUT_FILENO && !NC_ISATTY(Filedes))
698 Filedes = STDERR_FILENO;
699 #if defined(EXP_WIN32_DRIVER)
700 if (Filedes != STDERR_FILENO && NC_ISATTY(Filedes))
701 _setmode(Filedes, _O_BINARY);
702 #endif
703
704 /*
705 * Check if we have already initialized to use this terminal. If so, we
706 * do not need to re-read the terminfo entry, or obtain TTY settings.
707 *
708 * This is an improvement on SVr4 curses. If an application mixes curses
709 * and termcap calls, it may call both initscr and tgetent. This is not
710 * really a good thing to do, but can happen if someone tries using ncurses
711 * with the readline library. The problem we are fixing is that when
712 * tgetent calls setupterm, the resulting Ottyb struct in cur_term is
713 * zeroed. A subsequent call to endwin uses the zeroed terminal settings
714 * rather than the ones saved in initscr. So we check if cur_term appears
715 * to contain terminal settings for the same output file as our current
716 * call - and copy those terminal settings. (SVr4 curses does not do this,
717 * however applications that are working around the problem will still work
718 * properly with this feature).
719 */
720 if (reuse
721 && (termp != 0)
722 && termp->Filedes == Filedes
723 && termp->_termname != 0
724 && !strcmp(termp->_termname, myname)
725 && _nc_name_match(TerminalType(termp).term_names, myname, "|")) {
726 T(("reusing existing terminal information and mode-settings"));
727 code = OK;
728 #ifdef USE_TERM_DRIVER
729 TCB = (TERMINAL_CONTROL_BLOCK *) termp;
730 #endif
731 } else {
732 #ifdef USE_TERM_DRIVER
733 TERMINAL_CONTROL_BLOCK *my_tcb;
734 termp = 0;
735 if ((my_tcb = typeCalloc(TERMINAL_CONTROL_BLOCK, 1)) != 0)
736 termp = &(my_tcb->term);
737 #else
738 int status;
739
740 termp = typeCalloc(TERMINAL, 1);
741 #endif
742 if (termp == 0) {
743 ret_error1(TGETENT_ERR,
744 "Not enough memory to create terminal structure.\n",
745 myname, free(myname));
746 }
747 ++_nc_globals.terminal_count;
748 #if HAVE_SYSCONF
749 {
750 long limit;
751 #ifdef LINE_MAX
752 limit = LINE_MAX;
753 #else
754 limit = _nc_globals.getstr_limit;
755 #endif
756 #ifdef _SC_LINE_MAX
757 if (limit < sysconf(_SC_LINE_MAX))
758 limit = sysconf(_SC_LINE_MAX);
759 #endif
760 if (_nc_globals.getstr_limit < (int) limit)
761 _nc_globals.getstr_limit = (int) limit;
762 }
763 #endif /* HAVE_SYSCONF */
764 T(("using %d for getstr limit", _nc_globals.getstr_limit));
765
766 #ifdef USE_TERM_DRIVER
767 INIT_TERM_DRIVER();
768 /*
769 * _nc_get_driver() will call td_CanHandle() for each driver, and win_driver
770 * needs file descriptor to do the test, so set it before calling.
771 */
772 termp->Filedes = (short) Filedes;
773 TCB = (TERMINAL_CONTROL_BLOCK *) termp;
774 code = _nc_globals.term_driver(TCB, myname, errret);
775 if (code == OK) {
776 termp->_termname = strdup(myname);
777 } else {
778 ret_error1(errret ? *errret : TGETENT_ERR,
779 "Could not find any driver to handle terminal.\n",
780 myname, free(myname));
781 }
782 #else
783 #if NCURSES_USE_DATABASE || NCURSES_USE_TERMCAP
784 status = _nc_setup_tinfo(myname, &TerminalType(termp));
785 T(("_nc_setup_tinfo returns %d", status));
786 #else
787 T(("no database available"));
788 status = TGETENT_NO;
789 #endif
790
791 /* try fallback list if entry on disk */
792 if (status != TGETENT_YES) {
793 const TERMTYPE2 *fallback = _nc_fallback2(myname);
794
795 if (fallback) {
796 T(("found fallback entry"));
797 _nc_copy_termtype2(&(TerminalType(termp)), fallback);
798 status = TGETENT_YES;
799 }
800 }
801
802 if (status != TGETENT_YES) {
803 del_curterm(termp);
804 if (status == TGETENT_ERR) {
805 free(myname);
806 ret_error0(status, "terminals database is inaccessible\n");
807 } else if (status == TGETENT_NO) {
808 ret_error1(status, "unknown terminal type.\n",
809 myname, free(myname));
810 } else {
811 free(myname);
812 ret_error0(status, "unexpected return-code\n");
813 }
814 }
815 #if NCURSES_EXT_NUMBERS
816 _nc_export_termtype2(&termp->type, &TerminalType(termp));
817 #endif
818 #if !USE_REENTRANT
819 save_ttytype(termp);
820 #endif
821
822 termp->Filedes = (short) Filedes;
823 termp->_termname = strdup(myname);
824
825 set_curterm(termp);
826
827 if (command_character)
828 _nc_tinfo_cmdch(termp, UChar(*command_character));
829
830 /*
831 * If an application calls setupterm() rather than initscr() or
832 * newterm(), we will not have the def_prog_mode() call in
833 * _nc_setupscreen(). Do it now anyway, so we can initialize the
834 * baudrate. Also get the shell-mode so that erasechar() works.
835 */
836 if (NC_ISATTY(Filedes)) {
837 NCURSES_SP_NAME(def_shell_mode) (NCURSES_SP_ARG);
838 NCURSES_SP_NAME(def_prog_mode) (NCURSES_SP_ARG);
839 NCURSES_SP_NAME(baudrate) (NCURSES_SP_ARG);
840 }
841 code = OK;
842 #endif
843 }
844
845 #ifdef USE_TERM_DRIVER
846 *tp = termp;
847 NCURSES_SP_NAME(set_curterm) (sp, termp);
848 TCB->drv->td_init(TCB);
849 #else
850 sp = SP;
851 #endif
852
853 /*
854 * We should always check the screensize, just in case.
855 */
856 TINFO_GET_SIZE(sp, termp, ptrLines(sp), ptrCols(sp));
857
858 if (errret)
859 *errret = TGETENT_YES;
860
861 #ifndef USE_TERM_DRIVER
862 if (generic_type) {
863 /*
864 * BSD 4.3's termcap contains mis-typed "gn" for wy99. Do a sanity
865 * check before giving up.
866 */
867 if ((VALID_STRING(cursor_address)
868 || (VALID_STRING(cursor_down) && VALID_STRING(cursor_home)))
869 && VALID_STRING(clear_screen)) {
870 ret_error1(TGETENT_YES, "terminal is not really generic.\n",
871 myname, free(myname));
872 } else {
873 del_curterm(termp);
874 ret_error1(TGETENT_NO, "I need something more specific.\n",
875 myname, free(myname));
876 }
877 } else if (hard_copy) {
878 ret_error1(TGETENT_YES, "I can't handle hardcopy terminals.\n",
879 myname, free(myname));
880 }
881 #endif
882 free(myname);
883 returnCode(code);
884 }
885
886 #ifdef USE_PTHREADS
887 /*
888 * Returns a non-null pointer unless a new screen should be allocated because
889 * no match was found in the pre-screen cache.
890 */
891 NCURSES_EXPORT(SCREEN *)
_nc_find_prescr(void)892 _nc_find_prescr(void)
893 {
894 SCREEN *result = 0;
895 PRESCREEN_LIST *p;
896 pthread_t id = GetThreadID();
897 for (p = _nc_prescreen.allocated; p != 0; p = p->next) {
898 if (p->id == id) {
899 result = p->sp;
900 break;
901 }
902 }
903 return result;
904 }
905
906 /*
907 * Tells ncurses to forget that this thread was associated with the pre-screen
908 * cache. It does not modify the pre-screen cache itself, since that is used
909 * for creating new screens.
910 */
911 NCURSES_EXPORT(void)
_nc_forget_prescr(void)912 _nc_forget_prescr(void)
913 {
914 PRESCREEN_LIST *p, *q;
915 pthread_t id = GetThreadID();
916 _nc_lock_global(screen);
917 for (p = _nc_prescreen.allocated, q = 0; p != 0; q = p, p = p->next) {
918 if (p->id == id) {
919 if (q) {
920 q->next = p->next;
921 } else {
922 _nc_prescreen.allocated = p->next;
923 }
924 free(p);
925 break;
926 }
927 }
928 _nc_unlock_global(screen);
929 }
930 #endif /* USE_PTHREADS */
931
932 #if NCURSES_SP_FUNCS
933 /*
934 * In case of handling multiple screens, we need to have a screen before
935 * initialization in _nc_setupscreen takes place. This is to extend the
936 * substitute for some of the stuff in _nc_prescreen, especially for slk and
937 * ripoff handling which should be done per screen.
938 */
939 NCURSES_EXPORT(SCREEN *)
new_prescr(void)940 new_prescr(void)
941 {
942 SCREEN *sp;
943
944 START_TRACE();
945 T((T_CALLED("new_prescr()")));
946
947 _nc_lock_global(screen);
948 if ((sp = _nc_find_prescr()) == 0) {
949 sp = _nc_alloc_screen_sp();
950 T(("_nc_alloc_screen_sp %p", (void *) sp));
951 if (sp != 0) {
952 #ifdef USE_PTHREADS
953 PRESCREEN_LIST *p = typeCalloc(PRESCREEN_LIST, 1);
954 if (p != 0) {
955 p->id = GetThreadID();
956 p->sp = sp;
957 p->next = _nc_prescreen.allocated;
958 _nc_prescreen.allocated = p;
959 }
960 #else
961 _nc_prescreen.allocated = sp;
962 #endif
963 sp->rsp = sp->rippedoff;
964 sp->_filtered = _nc_prescreen.filter_mode;
965 sp->_use_env = _nc_prescreen.use_env;
966 #if NCURSES_NO_PADDING
967 sp->_no_padding = _nc_prescreen._no_padding;
968 #endif
969 sp->slk_format = 0;
970 sp->_slk = 0;
971 sp->_prescreen = TRUE;
972 SP_PRE_INIT(sp);
973 #if USE_REENTRANT
974 sp->_TABSIZE = _nc_prescreen._TABSIZE;
975 sp->_ESCDELAY = _nc_prescreen._ESCDELAY;
976 #endif
977 }
978 } else {
979 T(("_nc_alloc_screen_sp %p (reuse)", (void *) sp));
980 }
981 _nc_unlock_global(screen);
982 returnSP(sp);
983 }
984 #endif
985
986 #ifdef USE_TERM_DRIVER
987 /*
988 * This entrypoint is called from tgetent() to allow a special case of reusing
989 * the same TERMINAL data (see comment).
990 */
991 NCURSES_EXPORT(int)
_nc_setupterm(const char * tname,int Filedes,int * errret,int reuse)992 _nc_setupterm(const char *tname,
993 int Filedes,
994 int *errret,
995 int reuse)
996 {
997 int rc = ERR;
998 TERMINAL *termp = 0;
999
1000 _nc_init_pthreads();
1001 _nc_lock_global(prescreen);
1002 START_TRACE();
1003 if (TINFO_SETUP_TERM(&termp, tname, Filedes, errret, reuse) == OK) {
1004 _nc_forget_prescr();
1005 if (NCURSES_SP_NAME(set_curterm) (CURRENT_SCREEN_PRE, termp) != 0) {
1006 rc = OK;
1007 }
1008 }
1009 _nc_unlock_global(prescreen);
1010
1011 return rc;
1012 }
1013 #endif
1014
1015 /*
1016 * setupterm(termname, Filedes, errret)
1017 *
1018 * Find and read the appropriate object file for the terminal
1019 * Make cur_term point to the structure.
1020 */
1021 NCURSES_EXPORT(int)
setupterm(const char * tname,int Filedes,int * errret)1022 setupterm(const char *tname, int Filedes, int *errret)
1023 {
1024 START_TRACE();
1025 return _nc_setupterm(tname, Filedes, errret, FALSE);
1026 }
1027