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 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 *) 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 (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) 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) 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) 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) 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) 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) 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) 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) 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 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 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 *) 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) 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) 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) 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 *) 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) 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 *) 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) 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) 1022 setupterm(const char *tname, int Filedes, int *errret) 1023 { 1024 START_TRACE(); 1025 return _nc_setupterm(tname, Filedes, errret, FALSE); 1026 } 1027