1 /* $OpenBSD: lib_setup.c,v 1.11 2001/01/22 18:01:53 millert Exp $ */ 2 3 /**************************************************************************** 4 * Copyright (c) 1998,1999,2000 Free Software Foundation, Inc. * 5 * * 6 * Permission is hereby granted, free of charge, to any person obtaining a * 7 * copy of this software and associated documentation files (the * 8 * "Software"), to deal in the Software without restriction, including * 9 * without limitation the rights to use, copy, modify, merge, publish, * 10 * distribute, distribute with modifications, sublicense, and/or sell * 11 * copies of the Software, and to permit persons to whom the Software is * 12 * furnished to do so, subject to the following conditions: * 13 * * 14 * The above copyright notice and this permission notice shall be included * 15 * in all copies or substantial portions of the Software. * 16 * * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 20 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 23 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 24 * * 25 * Except as contained in this notice, the name(s) of the above copyright * 26 * holders shall not be used in advertising or otherwise to promote the * 27 * sale, use or other dealings in this Software without prior written * 28 * authorization. * 29 ****************************************************************************/ 30 31 /**************************************************************************** 32 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 33 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 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 #include <term_entry.h> 46 47 #if SVR4_TERMIO && !defined(_POSIX_SOURCE) 48 #define _POSIX_SOURCE 49 #endif 50 51 #include <term.h> /* lines, columns, cur_term */ 52 53 MODULE_ID("$From: lib_setup.c,v 1.64 2000/12/10 02:55:07 tom Exp $") 54 55 /**************************************************************************** 56 * 57 * Terminal size computation 58 * 59 ****************************************************************************/ 60 61 #if HAVE_SIZECHANGE 62 # if !defined(sun) || !TERMIOS 63 # if HAVE_SYS_IOCTL_H 64 # include <sys/ioctl.h> 65 # endif 66 # endif 67 #endif 68 69 #if NEED_PTEM_H 70 /* On SCO, they neglected to define struct winsize in termios.h -- it's only 71 * in termio.h and ptem.h (the former conflicts with other definitions). 72 */ 73 # include <sys/stream.h> 74 # include <sys/ptem.h> 75 #endif 76 77 /* 78 * SCO defines TIOCGSIZE and the corresponding struct. Other systems (SunOS, 79 * Solaris, IRIX) define TIOCGWINSZ and struct winsize. 80 */ 81 #ifdef TIOCGSIZE 82 # define IOCTL_WINSIZE TIOCGSIZE 83 # define STRUCT_WINSIZE struct ttysize 84 # define WINSIZE_ROWS(n) (int)n.ts_lines 85 # define WINSIZE_COLS(n) (int)n.ts_cols 86 #else 87 # ifdef TIOCGWINSZ 88 # define IOCTL_WINSIZE TIOCGWINSZ 89 # define STRUCT_WINSIZE struct winsize 90 # define WINSIZE_ROWS(n) (int)n.ws_row 91 # define WINSIZE_COLS(n) (int)n.ws_col 92 # endif 93 #endif 94 95 static int _use_env = TRUE; 96 97 static void do_prototype(void); 98 99 NCURSES_EXPORT(void) 100 use_env(bool f) 101 { 102 _use_env = f; 103 } 104 105 NCURSES_EXPORT_VAR(int) 106 LINES = 0; 107 NCURSES_EXPORT_VAR(int) 108 COLS = 0; 109 NCURSES_EXPORT_VAR(int) 110 TABSIZE = 0; 111 112 static void 113 _nc_get_screensize(int *linep, int *colp) 114 /* Obtain lines/columns values from the environment and/or terminfo entry */ 115 { 116 /* figure out the size of the screen */ 117 T(("screen size: terminfo lines = %d columns = %d", lines, columns)); 118 119 if (!_use_env) { 120 *linep = (int) lines; 121 *colp = (int) columns; 122 } else { /* usually want to query LINES and COLUMNS from environment */ 123 int value; 124 125 *linep = *colp = 0; 126 127 /* first, look for environment variables */ 128 if ((value = _nc_getenv_num("LINES")) > 0) { 129 *linep = value; 130 } 131 if ((value = _nc_getenv_num("COLUMNS")) > 0) { 132 *colp = value; 133 } 134 T(("screen size: environment LINES = %d COLUMNS = %d", *linep, *colp)); 135 136 #ifdef __EMX__ 137 if (*linep <= 0 || *colp <= 0) { 138 int screendata[2]; 139 _scrsize(screendata); 140 *colp = screendata[0]; 141 *linep = screendata[1]; 142 T(("EMX screen size: environment LINES = %d COLUMNS = %d", 143 *linep, *colp)); 144 } 145 #endif 146 #if HAVE_SIZECHANGE 147 /* if that didn't work, maybe we can try asking the OS */ 148 if (*linep <= 0 || *colp <= 0) { 149 if (isatty(cur_term->Filedes)) { 150 STRUCT_WINSIZE size; 151 152 errno = 0; 153 do { 154 if (ioctl(cur_term->Filedes, IOCTL_WINSIZE, &size) < 0 155 && errno != EINTR) 156 goto failure; 157 } while 158 (errno == EINTR); 159 160 /* 161 * Solaris lets users override either dimension with an 162 * environment variable. 163 */ 164 if (*linep <= 0) 165 *linep = WINSIZE_ROWS(size); 166 if (*colp <= 0) 167 *colp = WINSIZE_COLS(size); 168 } 169 /* FALLTHRU */ 170 failure:; 171 } 172 #endif /* HAVE_SIZECHANGE */ 173 174 /* if we can't get dynamic info about the size, use static */ 175 if (*linep <= 0) { 176 *linep = (int) lines; 177 } 178 if (*colp <= 0) { 179 *colp = (int) columns; 180 } 181 182 /* the ultimate fallback, assume fixed 24x80 size */ 183 if (*linep <= 0 || *colp <= 0) { 184 *linep = 24; 185 *colp = 80; 186 } 187 188 /* 189 * Put the derived values back in the screen-size caps, so 190 * tigetnum() and tgetnum() will do the right thing. 191 */ 192 lines = (short) (*linep); 193 columns = (short) (*colp); 194 } 195 196 T(("screen size is %dx%d", *linep, *colp)); 197 198 if (VALID_NUMERIC(init_tabs)) 199 TABSIZE = (int) init_tabs; 200 else 201 TABSIZE = 8; 202 T(("TABSIZE = %d", TABSIZE)); 203 204 } 205 206 #if USE_SIZECHANGE 207 NCURSES_EXPORT(void) 208 _nc_update_screensize(void) 209 { 210 int my_lines, my_cols; 211 212 _nc_get_screensize(&my_lines, &my_cols); 213 if (SP != 0 && SP->_resize != 0) 214 SP->_resize(my_lines, my_cols); 215 } 216 #endif 217 218 /**************************************************************************** 219 * 220 * Terminal setup 221 * 222 ****************************************************************************/ 223 224 #define ret_error(code, fmt, arg) if (errret) {\ 225 *errret = code;\ 226 returnCode(ERR);\ 227 } else {\ 228 fprintf(stderr, fmt, arg);\ 229 exit(EXIT_FAILURE);\ 230 } 231 232 #define ret_error0(code, msg) if (errret) {\ 233 *errret = code;\ 234 returnCode(ERR);\ 235 } else {\ 236 fprintf(stderr, msg);\ 237 exit(EXIT_FAILURE);\ 238 } 239 240 #if USE_DATABASE 241 static int 242 grab_entry(const char *const tn, TERMTYPE * const tp) 243 /* return 1 if entry found, 0 if not found, -1 if database not accessible */ 244 { 245 char filename[PATH_MAX]; 246 int status; 247 248 /* 249 * $TERM shouldn't contain pathname delimiters. 250 */ 251 if (strchr(tn, '/')) 252 return 0; 253 254 if ((status = _nc_read_entry(tn, filename, tp)) != 1) { 255 256 #if !PURE_TERMINFO 257 /* 258 * Try falling back on the termcap file. 259 * Note: allowing this call links the entire terminfo/termcap 260 * compiler into the startup code. It's preferable to build a 261 * real terminfo database and use that. 262 */ 263 status = _nc_read_termcap_entry(tn, tp); 264 #endif /* PURE_TERMINFO */ 265 266 } 267 268 /* 269 * If we have an entry, force all of the cancelled strings to null 270 * pointers so we don't have to test them in the rest of the library. 271 * (The terminfo compiler bypasses this logic, since it must know if 272 * a string is cancelled, for merging entries). 273 */ 274 if (status == 1) { 275 int n; 276 for_each_boolean(n, tp) { 277 if (!VALID_BOOLEAN(tp->Booleans[n])) 278 tp->Booleans[n] = FALSE; 279 } 280 for_each_string(n, tp) { 281 if (tp->Strings[n] == CANCELLED_STRING) 282 tp->Strings[n] = ABSENT_STRING; 283 } 284 } 285 return (status); 286 } 287 #endif 288 289 NCURSES_EXPORT_VAR(char) ttytype[NAMESIZE] = ""; 290 291 /* 292 * setupterm(termname, Filedes, errret) 293 * 294 * Find and read the appropriate object file for the terminal 295 * Make cur_term point to the structure. 296 * 297 */ 298 299 NCURSES_EXPORT(int) 300 setupterm 301 (NCURSES_CONST char *tname, int Filedes, int *errret) 302 { 303 struct term *term_ptr; 304 int status; 305 306 T((T_CALLED("setupterm(%s,%d,%p)"), _nc_visbuf(tname), Filedes, errret)); 307 308 if (tname == 0) { 309 tname = getenv("TERM"); 310 if (tname == 0 || *tname == '\0') { 311 ret_error0(-1, "TERM environment variable not set.\n"); 312 } 313 } 314 if (strlen(tname) > MAX_NAME_SIZE) { 315 ret_error(-1, "TERM environment must be <= %d characters.\n", 316 MAX_NAME_SIZE); 317 } 318 319 T(("your terminal name is %s", tname)); 320 321 term_ptr = typeCalloc(TERMINAL, 1); 322 323 if (term_ptr == 0) { 324 ret_error0(-1, "Not enough memory to create terminal structure.\n"); 325 } 326 #if USE_DATABASE 327 status = grab_entry(tname, &term_ptr->type); 328 #else 329 status = 0; 330 #endif 331 332 /* try fallback list if entry on disk */ 333 if (status != 1) { 334 const TERMTYPE *fallback = _nc_fallback(tname); 335 336 if (fallback) { 337 term_ptr->type = *fallback; 338 status = 1; 339 } 340 } 341 342 if (status == -1) { 343 ret_error0(-1, "terminals database is inaccessible\n"); 344 } else if (status == 0) { 345 ret_error(0, "'%s': unknown terminal type.\n", tname); 346 } 347 348 /* 349 * Improve on SVr4 curses. If an application mixes curses and termcap 350 * calls, it may call both initscr and tgetent. This is not really a 351 * good thing to do, but can happen if someone tries using ncurses with 352 * the readline library. The problem we are fixing is that when 353 * tgetent calls setupterm, the resulting Ottyb struct in cur_term is 354 * zeroed. A subsequent call to endwin uses the zeroed terminal 355 * settings rather than the ones saved in initscr. So we check if 356 * cur_term appears to contain terminal settings for the same output 357 * file as our current call - and copy those terminal settings. (SVr4 358 * curses does not do this, however applications that are working 359 * around the problem will still work properly with this feature). 360 */ 361 if (cur_term != 0) { 362 if (cur_term->Filedes == Filedes) 363 term_ptr->Ottyb = cur_term->Ottyb; 364 } 365 366 set_curterm(term_ptr); 367 368 if (command_character && getenv("CC")) 369 do_prototype(); 370 371 strlcpy(ttytype, cur_term->type.term_names, NAMESIZE); 372 373 /* 374 * Allow output redirection. This is what SVr3 does. 375 * If stdout is directed to a file, screen updates go 376 * to standard error. 377 */ 378 if (Filedes == STDOUT_FILENO && !isatty(Filedes)) 379 Filedes = STDERR_FILENO; 380 cur_term->Filedes = Filedes; 381 382 _nc_get_screensize(&LINES, &COLS); 383 384 if (errret) 385 *errret = 1; 386 387 T((T_CREATE("screen %s %dx%d"), tname, LINES, COLS)); 388 389 if (generic_type) { 390 ret_error(0, "'%s': I need something more specific.\n", tname); 391 } 392 if (hard_copy) { 393 ret_error(1, "'%s': I can't handle hardcopy terminals.\n", tname); 394 } 395 returnCode(OK); 396 } 397 398 /* 399 ** do_prototype() 400 ** 401 ** Take the real command character out of the CC environment variable 402 ** and substitute it in for the prototype given in 'command_character'. 403 ** 404 */ 405 406 static void 407 do_prototype(void) 408 { 409 int i; 410 char CC; 411 char proto; 412 char *tmp; 413 414 tmp = getenv("CC"); 415 CC = *tmp; 416 proto = *command_character; 417 418 for_each_string(i, &(cur_term->type)) { 419 for (tmp = cur_term->type.Strings[i]; *tmp; tmp++) { 420 if (*tmp == proto) 421 *tmp = CC; 422 } 423 } 424 } 425