1 /* $OpenBSD: lib_setup.c,v 1.9 2000/03/10 01:35:04 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 defined(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.59 2000/02/13 01:01:26 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 void 100 use_env(bool f) 101 { 102 _use_env = f; 103 } 104 105 int LINES = 0, COLS = 0, TABSIZE = 0; 106 107 static void 108 _nc_get_screensize(int *linep, int *colp) 109 /* Obtain lines/columns values from the environment and/or terminfo entry */ 110 { 111 /* figure out the size of the screen */ 112 T(("screen size: terminfo lines = %d columns = %d", lines, columns)); 113 114 if (!_use_env) { 115 *linep = (int) lines; 116 *colp = (int) columns; 117 } else { /* usually want to query LINES and COLUMNS from environment */ 118 int value; 119 120 *linep = *colp = 0; 121 122 /* first, look for environment variables */ 123 if ((value = _nc_getenv_num("LINES")) > 0) { 124 *linep = value; 125 } 126 if ((value = _nc_getenv_num("COLUMNS")) > 0) { 127 *colp = value; 128 } 129 T(("screen size: environment LINES = %d COLUMNS = %d", *linep, *colp)); 130 131 #ifdef __EMX__ 132 if (*linep <= 0 || *colp <= 0) { 133 int screendata[2]; 134 _scrsize(screendata); 135 *colp = screendata[0]; 136 *linep = screendata[1]; 137 T(("EMX screen size: environment LINES = %d COLUMNS = %d", 138 *linep, *colp)); 139 } 140 #endif 141 #if HAVE_SIZECHANGE 142 /* if that didn't work, maybe we can try asking the OS */ 143 if (*linep <= 0 || *colp <= 0) { 144 if (isatty(cur_term->Filedes)) { 145 STRUCT_WINSIZE size; 146 147 errno = 0; 148 do { 149 if (ioctl(cur_term->Filedes, IOCTL_WINSIZE, &size) < 0 150 && errno != EINTR) 151 goto failure; 152 } while 153 (errno == EINTR); 154 155 /* 156 * Solaris lets users override either dimension with an 157 * environment variable. 158 */ 159 if (*linep <= 0) 160 *linep = WINSIZE_ROWS(size); 161 if (*colp <= 0) 162 *colp = WINSIZE_COLS(size); 163 } 164 /* FALLTHRU */ 165 failure:; 166 } 167 #endif /* HAVE_SIZECHANGE */ 168 169 /* if we can't get dynamic info about the size, use static */ 170 if (*linep <= 0 || *colp <= 0) 171 if (lines > 0 && columns > 0) { 172 *linep = (int) lines; 173 *colp = (int) columns; 174 } 175 176 /* the ultimate fallback, assume fixed 24x80 size */ 177 if (*linep <= 0 || *colp <= 0) { 178 *linep = 24; 179 *colp = 80; 180 } 181 182 /* 183 * Put the derived values back in the screen-size caps, so 184 * tigetnum() and tgetnum() will do the right thing. 185 */ 186 lines = (short) (*linep); 187 columns = (short) (*colp); 188 } 189 190 T(("screen size is %dx%d", *linep, *colp)); 191 192 if (VALID_NUMERIC(init_tabs)) 193 TABSIZE = (int) init_tabs; 194 else 195 TABSIZE = 8; 196 T(("TABSIZE = %d", TABSIZE)); 197 198 } 199 200 #if USE_SIZECHANGE 201 void 202 _nc_update_screensize(void) 203 { 204 int my_lines, my_cols; 205 206 _nc_get_screensize(&my_lines, &my_cols); 207 if (SP != 0 && SP->_resize != 0) 208 SP->_resize(my_lines, my_cols); 209 } 210 #endif 211 212 /**************************************************************************** 213 * 214 * Terminal setup 215 * 216 ****************************************************************************/ 217 218 #define ret_error(code, fmt, arg) if (errret) {\ 219 *errret = code;\ 220 returnCode(ERR);\ 221 } else {\ 222 fprintf(stderr, fmt, arg);\ 223 exit(EXIT_FAILURE);\ 224 } 225 226 #define ret_error0(code, msg) if (errret) {\ 227 *errret = code;\ 228 returnCode(ERR);\ 229 } else {\ 230 fprintf(stderr, msg);\ 231 exit(EXIT_FAILURE);\ 232 } 233 234 #if USE_DATABASE 235 static int 236 grab_entry(const char *const tn, TERMTYPE * const tp) 237 /* return 1 if entry found, 0 if not found, -1 if database not accessible */ 238 { 239 char filename[PATH_MAX]; 240 int status; 241 242 /* 243 * $TERM shouldn't contain pathname delimiters. 244 */ 245 if (strchr(tn, '/')) 246 return 0; 247 248 if ((status = _nc_read_entry(tn, filename, tp)) != 1) { 249 250 #ifndef PURE_TERMINFO 251 /* 252 * Try falling back on the termcap file. 253 * Note: allowing this call links the entire terminfo/termcap 254 * compiler into the startup code. It's preferable to build a 255 * real terminfo database and use that. 256 */ 257 status = _nc_read_termcap_entry(tn, tp); 258 #endif /* PURE_TERMINFO */ 259 260 } 261 262 /* 263 * If we have an entry, force all of the cancelled strings to null 264 * pointers so we don't have to test them in the rest of the library. 265 * (The terminfo compiler bypasses this logic, since it must know if 266 * a string is cancelled, for merging entries). 267 */ 268 if (status == 1) { 269 int n; 270 for_each_boolean(n, tp) 271 if (!VALID_BOOLEAN(tp->Booleans[n])) 272 tp->Booleans[n] = FALSE; 273 for_each_string(n, tp) 274 if (tp->Strings[n] == CANCELLED_STRING) 275 tp->Strings[n] = ABSENT_STRING; 276 } 277 return (status); 278 } 279 #endif 280 281 char ttytype[NAMESIZE] = ""; 282 283 /* 284 * setupterm(termname, Filedes, errret) 285 * 286 * Find and read the appropriate object file for the terminal 287 * Make cur_term point to the structure. 288 * 289 */ 290 291 int 292 setupterm(NCURSES_CONST char *tname, int Filedes, int *errret) 293 { 294 struct term *term_ptr; 295 int status; 296 297 T((T_CALLED("setupterm(%s,%d,%p)"), _nc_visbuf(tname), Filedes, errret)); 298 299 if (tname == 0) { 300 tname = getenv("TERM"); 301 if (tname == 0 || *tname == '\0') { 302 ret_error0(-1, "TERM environment variable not set.\n"); 303 } 304 } 305 if (strlen(tname) > MAX_NAME_SIZE) { 306 ret_error(-1, "TERM environment must be <= %d characters.\n", 307 MAX_NAME_SIZE); 308 } 309 310 T(("your terminal name is %s", tname)); 311 312 term_ptr = typeCalloc(TERMINAL, 1); 313 314 if (term_ptr == 0) { 315 ret_error0(-1, "Not enough memory to create terminal structure.\n"); 316 } 317 #if USE_DATABASE 318 status = grab_entry(tname, &term_ptr->type); 319 #else 320 status = 0; 321 #endif 322 323 /* try fallback list if entry on disk */ 324 if (status != 1) { 325 const TERMTYPE *fallback = _nc_fallback(tname); 326 327 if (fallback) { 328 term_ptr->type = *fallback; 329 status = 1; 330 } 331 } 332 333 if (status == -1) { 334 ret_error0(-1, "terminals database is inaccessible\n"); 335 } else if (status == 0) { 336 ret_error(0, "'%s': unknown terminal type.\n", tname); 337 } 338 339 /* 340 * Improve on SVr4 curses. If an application mixes curses and termcap 341 * calls, it may call both initscr and tgetent. This is not really a 342 * good thing to do, but can happen if someone tries using ncurses with 343 * the readline library. The problem we are fixing is that when 344 * tgetent calls setupterm, the resulting Ottyb struct in cur_term is 345 * zeroed. A subsequent call to endwin uses the zeroed terminal 346 * settings rather than the ones saved in initscr. So we check if 347 * cur_term appears to contain terminal settings for the same output 348 * file as our current call - and copy those terminal settings. (SVr4 349 * curses does not do this, however applications that are working 350 * around the problem will still work properly with this feature). 351 */ 352 if (cur_term != 0) { 353 if (cur_term->Filedes == Filedes) 354 term_ptr->Ottyb = cur_term->Ottyb; 355 } 356 357 set_curterm(term_ptr); 358 359 if (command_character && getenv("CC")) 360 do_prototype(); 361 362 strlcpy(ttytype, cur_term->type.term_names, NAMESIZE); 363 364 /* 365 * Allow output redirection. This is what SVr3 does. 366 * If stdout is directed to a file, screen updates go 367 * to standard error. 368 */ 369 if (Filedes == STDOUT_FILENO && !isatty(Filedes)) 370 Filedes = STDERR_FILENO; 371 cur_term->Filedes = Filedes; 372 373 _nc_get_screensize(&LINES, &COLS); 374 375 if (errret) 376 *errret = 1; 377 378 T((T_CREATE("screen %s %dx%d"), tname, LINES, COLS)); 379 380 if (generic_type) { 381 ret_error(0, "'%s': I need something more specific.\n", tname); 382 } 383 if (hard_copy) { 384 ret_error(1, "'%s': I can't handle hardcopy terminals.\n", tname); 385 } 386 returnCode(OK); 387 } 388 389 /* 390 ** do_prototype() 391 ** 392 ** Take the real command character out of the CC environment variable 393 ** and substitute it in for the prototype given in 'command_character'. 394 ** 395 */ 396 397 static void 398 do_prototype(void) 399 { 400 int i; 401 char CC; 402 char proto; 403 char *tmp; 404 405 tmp = getenv("CC"); 406 CC = *tmp; 407 proto = *command_character; 408 409 for_each_string(i, &(cur_term->type)) { 410 for (tmp = cur_term->type.Strings[i]; *tmp; tmp++) { 411 if (*tmp == proto) 412 *tmp = CC; 413 } 414 } 415 } 416