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 54MODULE_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 111NCURSES_EXPORT(char *) 112NCURSES_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 132NCURSES_EXPORT(int *) 133_nc_ptr_Lines(SCREEN *sp) 134{ 135 return ptrLines(sp); 136} 137 138NCURSES_EXPORT(int) 139NCURSES_PUBLIC_VAR(LINES) (void) 140{ 141 return *_nc_ptr_Lines(CURRENT_SCREEN); 142} 143 144NCURSES_EXPORT(int *) 145_nc_ptr_Cols(SCREEN *sp) 146{ 147 return ptrCols(sp); 148} 149 150NCURSES_EXPORT(int) 151NCURSES_PUBLIC_VAR(COLS) (void) 152{ 153 return *_nc_ptr_Cols(CURRENT_SCREEN); 154} 155 156NCURSES_EXPORT(int *) 157_nc_ptr_Tabsize(SCREEN *sp) 158{ 159 return ptrTabsize(sp); 160} 161 162NCURSES_EXPORT(int) 163NCURSES_PUBLIC_VAR(TABSIZE) (void) 164{ 165 return *_nc_ptr_Tabsize(CURRENT_SCREEN); 166} 167#else 168NCURSES_EXPORT_VAR(char) ttytype[NAMESIZE] = ""; 169NCURSES_EXPORT_VAR(int) LINES = 0; 170NCURSES_EXPORT_VAR(int) COLS = 0; 171NCURSES_EXPORT_VAR(int) TABSIZE = 8; 172#endif 173 174#if NCURSES_EXT_FUNCS 175NCURSES_EXPORT(int) 176NCURSES_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 197NCURSES_EXPORT(int) 198set_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 */ 209NCURSES_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 227NCURSES_EXPORT(void) 228NCURSES_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 242NCURSES_EXPORT(void) 243NCURSES_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 258NCURSES_EXPORT(void) 259use_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 267NCURSES_EXPORT(void) 268use_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 277NCURSES_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 444NCURSES_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 */ 494int 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*/ 525void 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 */ 551NCURSES_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 */ 575NCURSES_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 */ 604NCURSES_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 635NCURSES_EXPORT(int) 636TINFO_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 */ 891NCURSES_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 */ 911NCURSES_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 */ 939NCURSES_EXPORT(SCREEN *) 940new_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 */ 991NCURSES_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 */ 1021NCURSES_EXPORT(int) 1022setupterm(const char *tname, int Filedes, int *errret) 1023{ 1024 START_TRACE(); 1025 return _nc_setupterm(tname, Filedes, errret, FALSE); 1026} 1027