1 /****************************************************************************
2  * Copyright (c) 1998-2010,2011 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: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
31  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
32  *     and: Thomas E. Dickey                        1996-on                 *
33  *     and: Juergen Pfeifer                         2009                    *
34  ****************************************************************************/
35 
36 /*
37  * Terminal setup routines common to termcap and terminfo:
38  *
39  *		use_env(bool)
40  *		setupterm(char *, int, int *)
41  */
42 
43 #include <curses.priv.h>
44 #include <tic.h>		/* for MAX_NAME_SIZE */
45 
46 #if SVR4_TERMIO && !defined(_POSIX_SOURCE)
47 #define _POSIX_SOURCE
48 #endif
49 
50 #if HAVE_LOCALE_H
51 #include <locale.h>
52 #endif
53 
54 MODULE_ID("$Id: lib_setup.c,v 1.135 2011/02/06 01:04:21 tom 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's 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 termp->type.
105 
106 /*
107  * Wrap global variables in this module.
108  */
109 #if USE_REENTRANT
110 
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 = termp->type.term_names;
122 	}
123     }
124 #else
125     if (cur_term != 0) {
126 	result = cur_term->type.term_names;
127     }
128 #endif
129     return result;
130 }
131 
132 NCURSES_EXPORT(int *)
133 _nc_ptr_Lines(SCREEN *sp)
134 {
135     return ptrLines(sp);
136 }
137 
138 NCURSES_EXPORT(int)
139 NCURSES_PUBLIC_VAR(LINES) (void)
140 {
141     return *_nc_ptr_Lines(CURRENT_SCREEN);
142 }
143 
144 NCURSES_EXPORT(int *)
145 _nc_ptr_Cols(SCREEN *sp)
146 {
147     return ptrCols(sp);
148 }
149 
150 NCURSES_EXPORT(int)
151 NCURSES_PUBLIC_VAR(COLS) (void)
152 {
153     return *_nc_ptr_Cols(CURRENT_SCREEN);
154 }
155 
156 NCURSES_EXPORT(int *)
157 _nc_ptr_Tabsize(SCREEN *sp)
158 {
159     return ptrTabsize(sp);
160 }
161 
162 NCURSES_EXPORT(int)
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)
176 NCURSES_SP_NAME(set_tabsize) (NCURSES_SP_DCLx int value)
177 {
178     int code = OK;
179 #if USE_REENTRANT
180     if (SP_PARM) {
181 	SP_PARM->_TABSIZE = value;
182     } else {
183 	code = ERR;
184     }
185 #else
186     (void) SP_PARM;
187     TABSIZE = value;
188 #endif
189     return code;
190 }
191 
192 #if NCURSES_SP_FUNCS
193 NCURSES_EXPORT(int)
194 set_tabsize(int value)
195 {
196     return NCURSES_SP_NAME(set_tabsize) (CURRENT_SCREEN, value);
197 }
198 #endif
199 #endif /* NCURSES_EXT_FUNCS */
200 
201 #if USE_SIGWINCH
202 /*
203  * If we have a pending SIGWINCH, set the flag in each screen.
204  */
205 NCURSES_EXPORT(int)
206 _nc_handle_sigwinch(SCREEN *sp)
207 {
208     SCREEN *scan;
209 
210     if (_nc_globals.have_sigwinch) {
211 	_nc_globals.have_sigwinch = 0;
212 
213 	for (each_screen(scan)) {
214 	    scan->_sig_winch = TRUE;
215 	}
216     }
217 
218     return (sp ? sp->_sig_winch : 0);
219 }
220 
221 #endif
222 
223 NCURSES_EXPORT(void)
224 NCURSES_SP_NAME(use_env) (NCURSES_SP_DCLx bool f)
225 {
226     T((T_CALLED("use_env(%p,%d)"), (void *) SP_PARM, (int) f));
227 #if NCURSES_SP_FUNCS
228     if (IsPreScreen(SP_PARM)) {
229 	SP_PARM->_use_env = f;
230     }
231 #else
232     _nc_prescreen.use_env = f;
233 #endif
234     returnVoid;
235 }
236 
237 #if NCURSES_SP_FUNCS
238 NCURSES_EXPORT(void)
239 use_env(bool f)
240 {
241     T((T_CALLED("use_env(%d)"), (int) f));
242     _nc_prescreen.use_env = f;
243     returnVoid;
244 }
245 #endif
246 
247 NCURSES_EXPORT(void)
248 _nc_get_screensize(SCREEN *sp,
249 #ifdef USE_TERM_DRIVER
250 		   TERMINAL * termp,
251 #endif
252 		   int *linep, int *colp)
253 /* Obtain lines/columns values from the environment and/or terminfo entry */
254 {
255 #ifdef USE_TERM_DRIVER
256     TERMINAL_CONTROL_BLOCK *TCB;
257     int my_tabsize;
258 
259     assert(termp != 0 && linep != 0 && colp != 0);
260     TCB = (TERMINAL_CONTROL_BLOCK *) termp;
261 
262     my_tabsize = TCB->info.tabsize;
263     TCB->drv->size(TCB, linep, colp);
264 
265 #if USE_REENTRANT
266     if (sp != 0) {
267 	sp->_TABSIZE = my_tabsize;
268     }
269 #else
270     (void) sp;
271     TABSIZE = my_tabsize;
272 #endif
273     T(("TABSIZE = %d", my_tabsize));
274 #else /* !USE_TERM_DRIVER */
275     TERMINAL *termp = cur_term;
276     int my_tabsize;
277 
278     /* figure out the size of the screen */
279     T(("screen size: terminfo lines = %d columns = %d", lines, columns));
280 
281     *linep = (int) lines;
282     *colp = (int) columns;
283 
284     if (_nc_prescreen.use_env) {
285 	int value;
286 
287 #ifdef __EMX__
288 	{
289 	    int screendata[2];
290 	    _scrsize(screendata);
291 	    *colp = screendata[0];
292 	    *linep = screendata[1];
293 	    T(("EMX screen size: environment LINES = %d COLUMNS = %d",
294 	       *linep, *colp));
295 	}
296 #endif
297 #if HAVE_SIZECHANGE
298 	/* try asking the OS */
299 	if (isatty(cur_term->Filedes)) {
300 	    STRUCT_WINSIZE size;
301 
302 	    errno = 0;
303 	    do {
304 		if (ioctl(cur_term->Filedes, IOCTL_WINSIZE, &size) >= 0) {
305 		    *linep = ((sp != 0 && sp->_filtered)
306 			      ? 1
307 			      : WINSIZE_ROWS(size));
308 		    *colp = WINSIZE_COLS(size);
309 		    T(("SYS screen size: environment LINES = %d COLUMNS = %d",
310 		       *linep, *colp));
311 		    break;
312 		}
313 	    } while
314 		(errno == EINTR);
315 	}
316 #endif /* HAVE_SIZECHANGE */
317 
318 	/*
319 	 * Finally, look for environment variables.
320 	 *
321 	 * Solaris lets users override either dimension with an environment
322 	 * variable.
323 	 */
324 	if ((value = _nc_getenv_num("LINES")) > 0) {
325 	    *linep = value;
326 	    T(("screen size: environment LINES = %d", *linep));
327 	}
328 	if ((value = _nc_getenv_num("COLUMNS")) > 0) {
329 	    *colp = value;
330 	    T(("screen size: environment COLUMNS = %d", *colp));
331 	}
332 
333 	/* if we can't get dynamic info about the size, use static */
334 	if (*linep <= 0) {
335 	    *linep = (int) lines;
336 	}
337 	if (*colp <= 0) {
338 	    *colp = (int) columns;
339 	}
340 
341 	/* the ultimate fallback, assume fixed 24x80 size */
342 	if (*linep <= 0) {
343 	    *linep = 24;
344 	}
345 	if (*colp <= 0) {
346 	    *colp = 80;
347 	}
348 
349 	/*
350 	 * Put the derived values back in the screen-size caps, so
351 	 * tigetnum() and tgetnum() will do the right thing.
352 	 */
353 	lines = (short) (*linep);
354 	columns = (short) (*colp);
355     }
356 
357     T(("screen size is %dx%d", *linep, *colp));
358 
359     if (VALID_NUMERIC(init_tabs))
360 	my_tabsize = (int) init_tabs;
361     else
362 	my_tabsize = 8;
363 
364 #if USE_REENTRANT
365     if (sp != 0)
366 	sp->_TABSIZE = my_tabsize;
367 #else
368     TABSIZE = my_tabsize;
369 #endif
370     T(("TABSIZE = %d", TABSIZE));
371 #endif /* USE_TERM_DRIVER */
372 }
373 
374 #if USE_SIZECHANGE
375 NCURSES_EXPORT(void)
376 _nc_update_screensize(SCREEN *sp)
377 {
378     int new_lines;
379     int new_cols;
380 
381 #ifdef USE_TERM_DRIVER
382     int old_lines;
383     int old_cols;
384 
385     assert(sp != 0);
386 
387     CallDriver_2(sp, getsize, &old_lines, &old_cols);
388 
389 #else
390     TERMINAL *termp = cur_term;
391     int old_lines = lines;
392     int old_cols = columns;
393 #endif
394 
395     TINFO_GET_SIZE(sp, sp->_term, &new_lines, &new_cols);
396 
397     /*
398      * See is_term_resized() and resizeterm().
399      * We're doing it this way because those functions belong to the upper
400      * ncurses library, while this resides in the lower terminfo library.
401      */
402     if (sp != 0
403 	&& sp->_resize != 0) {
404 	if ((new_lines != old_lines) || (new_cols != old_cols))
405 	    sp->_resize(NCURSES_SP_ARGx new_lines, new_cols);
406 	sp->_sig_winch = FALSE;
407     }
408 }
409 #endif
410 
411 /****************************************************************************
412  *
413  * Terminal setup
414  *
415  ****************************************************************************/
416 
417 #define ret_error(code, fmt, arg)	if (errret) {\
418 					    *errret = code;\
419 					    returnCode(ERR);\
420 					} else {\
421 					    fprintf(stderr, fmt, arg);\
422 					    exit(EXIT_FAILURE);\
423 					}
424 
425 #define ret_error0(code, msg)		if (errret) {\
426 					    *errret = code;\
427 					    returnCode(ERR);\
428 					} else {\
429 					    fprintf(stderr, msg);\
430 					    exit(EXIT_FAILURE);\
431 					}
432 
433 #if USE_DATABASE || USE_TERMCAP
434 /*
435  * Return 1 if entry found, 0 if not found, -1 if database not accessible,
436  * just like tgetent().
437  */
438 int
439 _nc_setup_tinfo(const char *const tn, TERMTYPE *const tp)
440 {
441     char filename[PATH_MAX];
442     int status = _nc_read_entry(tn, filename, tp);
443 
444     /*
445      * If we have an entry, force all of the cancelled strings to null
446      * pointers so we don't have to test them in the rest of the library.
447      * (The terminfo compiler bypasses this logic, since it must know if
448      * a string is cancelled, for merging entries).
449      */
450     if (status == TGETENT_YES) {
451 	unsigned n;
452 	for_each_boolean(n, tp) {
453 	    if (!VALID_BOOLEAN(tp->Booleans[n]))
454 		tp->Booleans[n] = FALSE;
455 	}
456 	for_each_string(n, tp) {
457 	    if (tp->Strings[n] == CANCELLED_STRING)
458 		tp->Strings[n] = ABSENT_STRING;
459 	}
460     }
461     return (status);
462 }
463 #endif
464 
465 /*
466 **	Take the real command character out of the CC environment variable
467 **	and substitute it in for the prototype given in 'command_character'.
468 */
469 void
470 _nc_tinfo_cmdch(TERMINAL * termp, char proto)
471 {
472     unsigned i;
473     char CC;
474     char *tmp;
475 
476     /*
477      * Only use the character if the string is a single character,
478      * since it is fairly common for developers to set the C compiler
479      * name as an environment variable - using the same symbol.
480      */
481     if ((tmp = getenv("CC")) != 0 && strlen(tmp) == 1) {
482 	CC = *tmp;
483 	for_each_string(i, &(termp->type)) {
484 	    for (tmp = termp->type.Strings[i]; *tmp; tmp++) {
485 		if (*tmp == proto)
486 		    *tmp = CC;
487 	    }
488 	}
489     }
490 }
491 
492 /*
493  * Find the locale which is in effect.
494  */
495 NCURSES_EXPORT(char *)
496 _nc_get_locale(void)
497 {
498     char *env;
499 #if HAVE_LOCALE_H
500     /*
501      * This is preferable to using getenv() since it ensures that we are using
502      * the locale which was actually initialized by the application.
503      */
504     env = setlocale(LC_CTYPE, 0);
505 #else
506     if (((env = getenv("LC_ALL")) != 0 && *env != '\0')
507 	|| ((env = getenv("LC_CTYPE")) != 0 && *env != '\0')
508 	|| ((env = getenv("LANG")) != 0 && *env != '\0')) {
509 	;
510     }
511 #endif
512     T(("_nc_get_locale %s", _nc_visbuf(env)));
513     return env;
514 }
515 
516 /*
517  * Check if we are running in a UTF-8 locale.
518  */
519 NCURSES_EXPORT(int)
520 _nc_unicode_locale(void)
521 {
522     int result = 0;
523 #if HAVE_LANGINFO_CODESET
524     char *env = nl_langinfo(CODESET);
525     result = !strcmp(env, "UTF-8");
526     T(("_nc_unicode_locale(%s) ->%d", env, result));
527 #else
528     char *env = _nc_get_locale();
529     if (env != 0) {
530 	if (strstr(env, ".UTF-8") != 0) {
531 	    result = 1;
532 	    T(("_nc_unicode_locale(%s) ->%d", env, result));
533 	}
534     }
535 #endif
536     return result;
537 }
538 
539 #define CONTROL_N(s) ((s) != 0 && strstr(s, "\016") != 0)
540 #define CONTROL_O(s) ((s) != 0 && strstr(s, "\017") != 0)
541 
542 /*
543  * Check for known broken cases where a UTF-8 locale breaks the alternate
544  * character set.
545  */
546 NCURSES_EXPORT(int)
547 _nc_locale_breaks_acs(TERMINAL * termp)
548 {
549     const char *env_name = "NCURSES_NO_UTF8_ACS";
550     char *env;
551     int value;
552     int result = 0;
553 
554     if ((env = getenv(env_name)) != 0) {
555 	result = _nc_getenv_num(env_name);
556     } else if ((value = tigetnum("U8")) >= 0) {
557 	result = value;		/* use extension feature */
558     } else if ((env = getenv("TERM")) != 0) {
559 	if (strstr(env, "linux")) {
560 	    result = 1;		/* always broken */
561 	} else if (strstr(env, "screen") != 0
562 		   && ((env = getenv("TERMCAP")) != 0
563 		       && strstr(env, "screen") != 0)
564 		   && strstr(env, "hhII00") != 0) {
565 	    if (CONTROL_N(enter_alt_charset_mode) ||
566 		CONTROL_O(enter_alt_charset_mode) ||
567 		CONTROL_N(set_attributes) ||
568 		CONTROL_O(set_attributes)) {
569 		result = 1;
570 	    }
571 	}
572     }
573     return result;
574 }
575 
576 NCURSES_EXPORT(int)
577 TINFO_SETUP_TERM(TERMINAL ** tp,
578 		 NCURSES_CONST char *tname,
579 		 int Filedes,
580 		 int *errret,
581 		 bool reuse)
582 {
583 #ifdef USE_TERM_DRIVER
584     TERMINAL_CONTROL_BLOCK *TCB = 0;
585 #else
586     int status;
587 #endif
588     TERMINAL *termp;
589     SCREEN *sp = 0;
590     int code = ERR;
591 
592     START_TRACE();
593 
594 #ifdef USE_TERM_DRIVER
595     T((T_CALLED("_nc_setupterm_ex(%p,%s,%d,%p)"),
596        (void *) tp, _nc_visbuf(tname), Filedes, (void *) errret));
597 
598     if (tp == 0) {
599 	ret_error0(TGETENT_ERR,
600 		   "Invalid parameter, internal error.\n");
601     } else
602 	termp = *tp;
603 #else
604     termp = cur_term;
605     T((T_CALLED("setupterm(%s,%d,%p)"), _nc_visbuf(tname), Filedes, (void *) errret));
606 #endif
607 
608     if (tname == 0) {
609 	tname = getenv("TERM");
610 	if (tname == 0 || *tname == '\0') {
611 	    ret_error0(TGETENT_ERR, "TERM environment variable not set.\n");
612 	}
613     }
614 
615     if (strlen(tname) > MAX_NAME_SIZE) {
616 	ret_error(TGETENT_ERR,
617 		  "TERM environment must be <= %d characters.\n",
618 		  MAX_NAME_SIZE);
619     }
620 
621     T(("your terminal name is %s", tname));
622 
623     /*
624      * Allow output redirection.  This is what SVr3 does.  If stdout is
625      * directed to a file, screen updates go to standard error.
626      */
627     if (Filedes == STDOUT_FILENO && !isatty(Filedes))
628 	Filedes = STDERR_FILENO;
629 
630     /*
631      * Check if we have already initialized to use this terminal.  If so, we
632      * do not need to re-read the terminfo entry, or obtain TTY settings.
633      *
634      * This is an improvement on SVr4 curses.  If an application mixes curses
635      * and termcap calls, it may call both initscr and tgetent.  This is not
636      * really a good thing to do, but can happen if someone tries using ncurses
637      * with the readline library.  The problem we are fixing is that when
638      * tgetent calls setupterm, the resulting Ottyb struct in cur_term is
639      * zeroed.  A subsequent call to endwin uses the zeroed terminal settings
640      * rather than the ones saved in initscr.  So we check if cur_term appears
641      * to contain terminal settings for the same output file as our current
642      * call - and copy those terminal settings.  (SVr4 curses does not do this,
643      * however applications that are working around the problem will still work
644      * properly with this feature).
645      */
646     if (reuse
647 	&& (termp != 0)
648 	&& termp->Filedes == Filedes
649 	&& termp->_termname != 0
650 	&& !strcmp(termp->_termname, tname)
651 	&& _nc_name_match(termp->type.term_names, tname, "|")) {
652 	T(("reusing existing terminal information and mode-settings"));
653 	code = OK;
654     } else {
655 #ifdef USE_TERM_DRIVER
656 	termp = (TERMINAL *) typeCalloc(TERMINAL_CONTROL_BLOCK, 1);
657 #else
658 	termp = typeCalloc(TERMINAL, 1);
659 #endif
660 	if (termp == 0) {
661 	    ret_error0(TGETENT_ERR,
662 		       "Not enough memory to create terminal structure.\n");
663 	}
664 #ifdef USE_TERM_DRIVER
665 	INIT_TERM_DRIVER();
666 	TCB = (TERMINAL_CONTROL_BLOCK *) termp;
667 	code = _nc_globals.term_driver(TCB, tname, errret);
668 	if (code == OK) {
669 	    termp->Filedes = (short) Filedes;
670 	    termp->_termname = strdup(tname);
671 	} else {
672 	    ret_error0(TGETENT_ERR,
673 		       "Could not find any driver to handle this terminal.\n");
674 	}
675 #else
676 #if USE_DATABASE || USE_TERMCAP
677 	status = _nc_setup_tinfo(tname, &termp->type);
678 #else
679 	status = TGETENT_NO;
680 #endif
681 
682 	/* try fallback list if entry on disk */
683 	if (status != TGETENT_YES) {
684 	    const TERMTYPE *fallback = _nc_fallback(tname);
685 
686 	    if (fallback) {
687 		termp->type = *fallback;
688 		status = TGETENT_YES;
689 	    }
690 	}
691 
692 	if (status != TGETENT_YES) {
693 	    del_curterm(termp);
694 	    if (status == TGETENT_ERR) {
695 		ret_error0(status, "terminals database is inaccessible\n");
696 	    } else if (status == TGETENT_NO) {
697 		ret_error(status, "'%s': unknown terminal type.\n", tname);
698 	    }
699 	}
700 #if !USE_REENTRANT
701 	strncpy(ttytype, termp->type.term_names, NAMESIZE - 1);
702 	ttytype[NAMESIZE - 1] = '\0';
703 #endif
704 
705 	termp->Filedes = (short) Filedes;
706 	termp->_termname = strdup(tname);
707 
708 	set_curterm(termp);
709 
710 	if (command_character)
711 	    _nc_tinfo_cmdch(termp, *command_character);
712 
713 	/*
714 	 * If an application calls setupterm() rather than initscr() or
715 	 * newterm(), we will not have the def_prog_mode() call in
716 	 * _nc_setupscreen().  Do it now anyway, so we can initialize the
717 	 * baudrate.
718 	 */
719 	if (isatty(Filedes)) {
720 	    def_prog_mode();
721 	    baudrate();
722 	}
723 	code = OK;
724 #endif
725     }
726 
727 #ifdef USE_TERM_DRIVER
728     *tp = termp;
729     NCURSES_SP_NAME(set_curterm) (sp, termp);
730     TCB->drv->init(TCB);
731 #else
732     sp = SP;
733 #endif
734 
735     /*
736      * We should always check the screensize, just in case.
737      */
738     TINFO_GET_SIZE(sp, termp, ptrLines(sp), ptrCols(sp));
739 
740     if (errret)
741 	*errret = TGETENT_YES;
742 
743 #ifndef USE_TERM_DRIVER
744     if (generic_type) {
745 	ret_error(TGETENT_NO, "'%s': I need something more specific.\n", tname);
746     }
747     if (hard_copy) {
748 	ret_error(TGETENT_YES, "'%s': I can't handle hardcopy terminals.\n", tname);
749     }
750 #endif
751     returnCode(code);
752 }
753 
754 #if NCURSES_SP_FUNCS
755 /*
756  * In case of handling multiple screens, we need to have a screen before
757  * initialization in setupscreen takes place.  This is to extend the substitute
758  * for some of the stuff in _nc_prescreen, especially for slk and ripoff
759  * handling which should be done per screen.
760  */
761 NCURSES_EXPORT(SCREEN *)
762 new_prescr(void)
763 {
764     static SCREEN *sp;
765 
766     START_TRACE();
767     T((T_CALLED("new_prescr()")));
768 
769     if (sp == 0) {
770 	sp = _nc_alloc_screen_sp();
771 	if (sp != 0) {
772 	    sp->rsp = sp->rippedoff;
773 	    sp->_filtered = _nc_prescreen.filter_mode;
774 	    sp->_use_env = _nc_prescreen.use_env;
775 #if NCURSES_NO_PADDING
776 	    sp->_no_padding = _nc_prescreen._no_padding;
777 #endif
778 	    sp->slk_format = 0;
779 	    sp->_slk = 0;
780 	    sp->_prescreen = TRUE;
781 	    SP_PRE_INIT(sp);
782 #if USE_REENTRANT
783 	    sp->_TABSIZE = _nc_prescreen._TABSIZE;
784 	    sp->_ESCDELAY = _nc_prescreen._ESCDELAY;
785 #endif
786 	}
787     }
788     returnSP(sp);
789 }
790 #endif
791 
792 #ifdef USE_TERM_DRIVER
793 /*
794  * This entrypoint is called from tgetent() to allow a special case of reusing
795  * the same TERMINAL data (see comment).
796  */
797 NCURSES_EXPORT(int)
798 _nc_setupterm(NCURSES_CONST char *tname,
799 	      int Filedes,
800 	      int *errret,
801 	      bool reuse)
802 {
803     int res;
804     TERMINAL *termp;
805     res = TINFO_SETUP_TERM(&termp, tname, Filedes, errret, reuse);
806     if (ERR != res)
807 	NCURSES_SP_NAME(set_curterm) (CURRENT_SCREEN_PRE, termp);
808     return res;
809 }
810 #endif
811 
812 /*
813  *	setupterm(termname, Filedes, errret)
814  *
815  *	Find and read the appropriate object file for the terminal
816  *	Make cur_term point to the structure.
817  */
818 NCURSES_EXPORT(int)
819 setupterm(NCURSES_CONST char *tname, int Filedes, int *errret)
820 {
821     return _nc_setupterm(tname, Filedes, errret, FALSE);
822 }
823