1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 1995-1999 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 /* LINTLIBRARY */ 28 29 /* 30 * setupterm.c 31 * 32 * XCurses Library 33 * 34 * Copyright 1990, 1995 by Mortice Kern Systems Inc. All rights reserved. 35 * 36 */ 37 38 #ifdef M_RCSID 39 #ifndef lint 40 static char const rcsID[] = 41 "$Header: /team/ps/sun_xcurses/archive/local_changes/xcurses/src/lib/" 42 "libxcurses/src/libc/xcurses/rcs/setup.c 1.16 1998/06/05 14:35:33 " 43 "cbates Exp $"; 44 #endif 45 #endif 46 47 #include <private.h> 48 #include <sys/types.h> 49 #ifdef TIOCGWINSZ 50 #include <sys/ioctl.h> 51 #endif 52 #include <fcntl.h> 53 #include <limits.h> 54 #include <stdlib.h> 55 #include <string.h> 56 #include <unistd.h> 57 58 TERMINAL *cur_term; 59 60 /* 61 * Any version number should be placed in this file, since setupterm() 62 * must be called in order to initialize Curses or Terminfo. 63 */ 64 char __m_curses_version[] = M_CURSES_VERSION; 65 66 /* 67 * True if __m_setupterm() should use either the window settings from 68 * ioctl(), or the environment variables LINES and COLUMNS to override 69 * the terminfo database entries for 'lines' and 'columns'. 70 * 71 * Call use_env(flag) before either setupterm(), newterm(), or initscr(). 72 */ 73 static bool use_environment = TRUE; 74 75 static const char e_terminal[] = 76 "No memory for TERMINAL structure.\n"; 77 static const char e_unknown[] = 78 "\"%s\": Unknown terminal type.\n"; 79 static const char e_pathmax[] = 80 "\"%s\": terminfo database path too long.\n"; 81 82 83 /* 84 * These globals are used so that macro arguments are evaluated 85 * exactly once 86 */ 87 /* The downside is that it is not really thread-safe. Oh well... */ 88 WINDOW *__w1; 89 chtype __cht1; 90 chtype __cht2; 91 cchar_t *__pcht1; 92 cchar_t *__pcht2; 93 94 /* 95 * Take the real command character out of the CC environment variable 96 * and substitute it in for the prototype given in 'command_character'. 97 */ 98 static void 99 do_prototype(void) 100 { 101 int i, j; 102 char proto; 103 char *CC; 104 105 CC = getenv("CC"); 106 proto = *command_character; 107 108 for (i = 0; i < __COUNT_STR; i++) 109 for (j = 0; cur_term->_str[i][j]; ++j) 110 if (cur_term->_str[i][j] == proto) 111 cur_term->_str[i][j] = *CC; 112 } 113 114 #define min(a, b) ((a) < (b) ? (a) : (b)) 115 116 /* 117 * Return a number from a terminfo file. Numbers in a terminfo 118 * file are stored as two bytes with low-high ordering. 119 */ 120 static short 121 getnum(int fd) 122 { 123 unsigned char bytes[2]; 124 125 if (read(fd, bytes, 2) != 2) 126 return (SHRT_MIN); 127 128 return ((short) (bytes[0] + bytes[1] * 256)); 129 } 130 131 /* 132 * MKS Header format for terminfo database files. 133 * 134 * The header consists of six short integers, stored using VAX/PDP style 135 * byte swapping (least-significant byte first). The integers are 136 * 137 * 1) magic number (octal 0432); 138 * 2) the size, in bytes, of the names sections; 139 * 3) the number of bytes in the boolean section; 140 * 4) the number of short integers in the numbers section; 141 * 5) the number of offsets (short integers) in the strings section; 142 * 6) the size, in bytes, of the string table. 143 * 144 * Between the boolean and number sections, a null byte is inserted, if 145 * necessary, to ensure that the number section begins on an even byte 146 * offset. All short integers are aligned on a short word boundary. 147 */ 148 149 #define __TERMINFO_MAGIC 0432 150 151 typedef struct { 152 short magic; 153 short name_size; 154 short bool_count; 155 short num_count; 156 short str_count; 157 short str_size; 158 } terminfo_header_t; 159 160 /* 161 * Read the compiled terminfo entry in the given file into the 162 * structure pointed to by ptr, allocating space for the string 163 * table and placing its address in ptr->str_table. 164 */ 165 int 166 __m_read_terminfo(const char *filename, TERMINAL *tp) 167 { 168 int fd; 169 short offset; 170 size_t i, len; 171 terminfo_header_t header; 172 unsigned char ch; 173 174 /* Get compiled terminfo file header. */ 175 if ((fd = open(filename, 0)) < 0) { 176 goto error_1; 177 } 178 179 if ((header.magic = getnum(fd)) != __TERMINFO_MAGIC || 180 (header.name_size = getnum(fd)) < 0 || 181 (header.bool_count = getnum(fd)) < 0 || 182 (header.num_count = getnum(fd)) < 0 || 183 (header.str_count = getnum(fd)) < 0 || 184 (header.str_size = getnum(fd)) < 0) { 185 goto error_2; 186 } 187 188 189 /* Allocate and fetch terminal names. */ 190 len = min(127, header.name_size); 191 if ((tp->_names = (char *) malloc(len + 1)) == NULL) 192 goto error_2; 193 if (read(fd, tp->_names, len) != len) 194 goto error_3; 195 tp->_names[len] = '\0'; 196 197 if (127 < header.name_size) 198 (void) lseek(fd, (off_t) (header.name_size - 127), SEEK_CUR); 199 200 /* Fetch booleans. */ 201 len = min(__COUNT_BOOL, header.bool_count); 202 if (read(fd, tp->_bool, len) != len) 203 goto error_3; 204 205 if (__COUNT_BOOL < header.bool_count) { 206 (void) lseek(fd, (off_t) (header.bool_count - __COUNT_BOOL), 207 SEEK_CUR); 208 } else { 209 for (len = header.bool_count; len < __COUNT_BOOL; ++len) 210 tp->_bool[len] = 0; 211 } 212 213 /* Eat padding byte. */ 214 if ((header.name_size + header.bool_count) % 2 != 0) 215 (void) read(fd, &ch, sizeof (ch)); 216 217 /* Fetch numbers. */ 218 len = min(__COUNT_NUM, header.num_count); 219 for (i = 0; i < len; ++i) 220 tp->_num[i] = getnum(fd); 221 222 if (__COUNT_NUM < header.num_count) { 223 (void) lseek(fd, (off_t) (2 * 224 (header.num_count - __COUNT_NUM)), SEEK_CUR); 225 } else { 226 for (len = header.num_count; len < __COUNT_NUM; ++len) 227 tp->_num[len] = -1; 228 } 229 230 /* Allocate and fetch strings. */ 231 if ((tp->_str_table = (char *) malloc(header.str_size)) == NULL) 232 goto error_3; 233 234 /* Read in string offset section setting pointers to strings. */ 235 len = min(__COUNT_STR, header.str_count); 236 for (i = 0; i < len; ++i) { 237 if ((offset = getnum(fd)) == SHRT_MIN) 238 goto error_4; 239 240 if (offset < 0) 241 tp->_str[i] = NULL; 242 else 243 tp->_str[i] = tp->_str_table + offset; 244 } 245 246 if (__COUNT_STR < header.str_count) { 247 (void) lseek(fd, (off_t) (2 * 248 (header.str_count - __COUNT_STR)), SEEK_CUR); 249 } else { 250 for (; i < __COUNT_STR; ++i) 251 tp->_str[i] = NULL; 252 } 253 254 if (read(fd, tp->_str_table, header.str_size) != header.str_size) 255 goto error_4; 256 (void) close(fd); 257 258 return (0); 259 error_4: 260 free(tp->_str_table); 261 error_3: 262 free(tp->_names); 263 error_2: 264 (void) close(fd); 265 error_1: 266 return (-1); 267 } 268 269 void 270 use_env(bool bf) 271 { 272 use_environment = bf; 273 } 274 275 /* 276 * Set up terminal. 277 * 278 * Reads in the terminfo database pointed to by $TERMINFO env. var. 279 * for the given terminal, but does not set up the output virtualization 280 * structues used by CURSES. If the terminal name pointer is NULL, 281 * the $TERM env. var. is used for the terminal. All output is to 282 * the given file descriptor which is initialized for output. 283 * 284 * On error, if errret != NULL then setupterm() returns OK 285 * or ERR and stores a status value in the integer pointed to by 286 * errret. A status of 1 is normal, 0 means the terminal could 287 * not be found, and -1 means the terminfo database could not be 288 * found. If errret == NULL then setupterm() prints an error 289 * message upon and exit(). 290 * 291 * On success, cur_term set to a terminfo structure and OK returned. 292 */ 293 int 294 __m_setupterm(char *termname, int ifd, int ofd, int *err_return) 295 { 296 int err_code = 1; 297 TERMINAL *old_term; 298 const char *err_msg; 299 300 /* 301 * It is possible to call setupterm() for multiple terminals, 302 * in which case we have to be able to restore cur_term in 303 * case of error. 304 */ 305 old_term = cur_term; 306 307 cur_term = (TERMINAL *) calloc(1, sizeof (*cur_term)); 308 if (cur_term == NULL) { 309 err_code = -1; 310 goto error; 311 } 312 313 if (isatty(cur_term->_ifd = ifd)) 314 cur_term->_flags |= __TERM_ISATTY_IN; 315 if (isatty(cur_term->_ofd = ofd)) 316 cur_term->_flags |= __TERM_ISATTY_OUT; 317 318 cur_term->_shell = (void *) calloc(1, sizeof (struct termios)); 319 cur_term->_prog = (void *) calloc(1, sizeof (struct termios)); 320 cur_term->_save = (void *) calloc(1, sizeof (struct termios)); 321 cur_term->_actual = (void *) calloc(1, sizeof (struct termios)); 322 cur_term->_term = NULL; 323 cur_term->_names = NULL; 324 cur_term->_str_table = NULL; 325 (void) def_shell_mode(); 326 (void) def_prog_mode(); 327 (void) __m_tty_get(PTERMIOS(_actual)); /* Synch cached value */ 328 329 #ifdef ONLCR 330 if ((PTERMIOS(_prog)->c_oflag & (OPOST | ONLCR)) == (OPOST | ONLCR)) 331 #else 332 if (PTERMIOS(_prog)->c_oflag & OPOST) 333 #endif 334 cur_term->_flags |= __TERM_NL_IS_CRLF; 335 336 (void) restartterm(termname, ofd, &err_code); 337 error: 338 switch (err_code) { 339 case -1: 340 err_msg = e_terminal; 341 break; 342 case 0: 343 err_msg = e_unknown; 344 break; 345 case 1: 346 break; 347 case 2: 348 err_msg = e_pathmax; 349 err_code = -1; 350 break; 351 } 352 353 if (err_return != NULL) { 354 *err_return = err_code; 355 356 if (err_code == 1) { 357 err_code = OK; 358 } else { 359 err_code = ERR; 360 free(cur_term); 361 cur_term = old_term; 362 } 363 } else if (err_code != 1) { 364 (void) fprintf(stderr, err_msg, termname); 365 exit(1); 366 } 367 368 return (err_code); 369 } 370 371 int 372 setupterm(char *term, int out, int *err) 373 { 374 int code; 375 376 code = __m_setupterm(term, STDIN_FILENO, out, err); 377 378 return (code); 379 } 380 381 int 382 del_curterm(TERMINAL *tp) 383 { 384 if (tp != NULL) { 385 if (cur_term == tp) 386 cur_term = NULL; 387 if (tp->_str_table != NULL) 388 free(tp->_str_table); 389 if (tp->_names != NULL) 390 free(tp->_names); 391 if (tp->_term != NULL) 392 free(tp->_term); 393 if (tp->_pair != (short (*)[2]) 0) 394 free(tp->_pair); 395 if (tp->_color != (short (*)[3]) 0) 396 free(tp->_color); 397 if (tp->_shell) 398 free(tp->_shell); 399 if (tp->_prog) 400 free(tp->_prog); 401 if (tp->_save) 402 free(tp->_save); 403 if (tp->_actual) 404 free(tp->_actual); 405 free(tp); 406 } 407 408 return (OK); 409 } 410 411 TERMINAL * 412 set_curterm(TERMINAL *tp) 413 { 414 TERMINAL *old; 415 416 old = cur_term; 417 cur_term = tp; 418 419 return (old); 420 } 421 422 int 423 restartterm(char *tm, int fd, int *err_return) 424 { 425 size_t len; 426 int err_code; 427 const char *err_msg, *terminfo; 428 char *old_names, *old_strings, *old_term, *filename; 429 static const char def_termname[] = M_TERM_NAME; 430 static const char def_terminfo[] = M_TERMINFO_DIR; 431 432 err_code = 1; 433 filename = NULL; 434 old_term = cur_term->_term; 435 old_names = cur_term->_names; 436 old_strings = cur_term->_str_table; 437 438 terminfo = getenv("TERMINFO"); 439 if (terminfo == NULL || terminfo[0] == '\0') { 440 terminfo = def_terminfo; 441 } else { 442 terminfo = (const char *) strdup((char *) terminfo); 443 if (terminfo == NULL) { 444 /* Not really true... */ 445 err_msg = e_terminal; 446 err_code = 2; 447 goto error; 448 } 449 } 450 451 if ((tm == NULL) && 452 (((tm = getenv("TERM")) == NULL) || (*tm == '\0'))) { 453 tm = (char *)def_termname; 454 } 455 456 /* Remember the terminal name being loaded. */ 457 cur_term->_term = strdup(tm); 458 if (cur_term->_term == NULL) { 459 err_msg = e_terminal; 460 err_code = 2; 461 goto error; 462 } 463 464 /* Length of path we're going to construct. */ 465 len = strlen(terminfo) + 3 + strlen(tm); 466 467 if ((len > PATH_MAX) || 468 ((filename = (char *)malloc(PATH_MAX + 1)) == NULL)) { 469 err_msg = e_pathmax; 470 err_code = 2; 471 goto error; 472 } 473 474 /* Construct terminfo filename. */ 475 (void) sprintf(filename, "%s/%c/%s", terminfo, tm[0], tm); 476 477 /* Go looking for compiled terminal definition. */ 478 if (__m_read_terminfo(filename, cur_term) < 0) { 479 /* Length of default terminfo path. */ 480 len = strlen(def_terminfo) + 3 + strlen(tm); 481 482 if (len > PATH_MAX) { 483 err_msg = e_pathmax; 484 err_code = 2; 485 goto error; 486 } 487 488 (void) sprintf(filename, "%s/%c/%s", def_terminfo, tm[0], tm); 489 490 if (__m_read_terminfo(filename, cur_term) < 0) { 491 err_msg = e_unknown; 492 err_code = 0; 493 goto error; 494 } 495 } 496 497 if (use_environment) { 498 char *env; 499 #ifdef TIOCGWINSZ 500 /* 501 * Use ioctl(TIOCGWINSZ) to get row and column values. These 502 * values may override the default terminfo settings. 503 */ 504 { 505 struct winsize wininfo; 506 if (ioctl(fd, TIOCGWINSZ, &wininfo) != -1) { 507 if (0 < wininfo.ws_col) 508 columns = wininfo.ws_col; 509 if (0 < wininfo.ws_row) 510 lines = wininfo.ws_row; 511 } 512 } 513 #endif /* TIOCGWINSZ */ 514 515 /* Check to see is the user wants a particular size terminal. */ 516 if ((env = getenv("LINES")) != NULL) { 517 int nlines = (int) strtol(env, (char **) 0, 10); 518 if (0 < nlines) 519 lines = nlines; 520 } 521 if ((env = getenv("COLUMNS")) != NULL) { 522 int ncolumns = (int) strtol(env, (char **) 0, 10); 523 if (0 < ncolumns) 524 columns = ncolumns; 525 } 526 } 527 528 if (command_character != NULL && getenv("CC") != NULL) 529 do_prototype(); 530 531 /* 532 * If no_color_video is disabled, then assign it a value that 533 * permits all attributes in combination with colour. 534 */ 535 if (no_color_video == -1) 536 no_color_video = 0; 537 538 __m_mvcur_cost(); 539 error: 540 if (filename != NULL) 541 free(filename); 542 543 if (terminfo != def_terminfo) 544 free((void *) terminfo); 545 546 if (err_return != NULL) { 547 *err_return = err_code; 548 549 if (err_code == 1) { 550 err_code = OK; 551 } else { 552 err_code = ERR; 553 cur_term->_term = old_term; 554 cur_term->_names = old_names; 555 cur_term->_str_table = old_strings; 556 } 557 } else if (err_code != 1) { 558 (void) fprintf(stderr, err_msg, tm); 559 exit(1); 560 } 561 562 if (err_code == OK) { 563 if (old_names != NULL) 564 free(old_names); 565 if (old_strings != NULL) 566 free(old_strings); 567 if (old_term != NULL) 568 free(old_term); 569 } 570 571 return (err_code); 572 } 573 574 /* 575 * Get the termios setting for the terminal. Check the input 576 * file descriptor first, else the output file descriptor. If 577 * both input and output are both terminals, it is assumed that 578 * they refer to the same terminal. 579 */ 580 int 581 __m_tty_get(struct termios *tp) 582 { 583 if (tcgetattr(cur_term->_ifd, tp) != 0) { 584 /* 585 * Input was not a terminal, possibly redirected. 586 * Check output instead. 587 */ 588 if (tcgetattr(cur_term->_ofd, tp) != 0) 589 return (ERR); 590 } 591 592 return (OK); 593 } 594 595 int 596 __m_tty_set_prog_mode(void) 597 { 598 return (__m_tty_set(PTERMIOS(_prog))); 599 } 600 601 /* 602 * Restore the termios settings. 603 */ 604 int 605 __m_tty_set(struct termios *tp) 606 { 607 int fd; 608 int rval; 609 610 if (cur_term->_flags & __TERM_ISATTY_OUT) { 611 fd = cur_term->_ofd; 612 } else if (cur_term->_flags & __TERM_ISATTY_IN) { 613 fd = cur_term->_ifd; 614 } else { 615 return (OK); 616 } 617 if (memcmp(tp, &cur_term->_actual, sizeof (struct termios)) == 0) 618 return (OK); 619 620 *PTERMIOS(_actual) = *tp; 621 622 rval = tcsetattr(fd, TCSADRAIN, tp) == 0 ? OK : ERR; 623 624 return (rval); 625 } 626 627 int 628 def_shell_mode(void) 629 { 630 return (__m_tty_get(PTERMIOS(_shell))); 631 } 632 633 int 634 def_prog_mode(void) 635 { 636 return (__m_tty_get(PTERMIOS(_prog))); 637 } 638 639 int 640 reset_shell_mode(void) 641 { 642 return (__m_tty_set(PTERMIOS(_shell))); 643 } 644 645 int 646 reset_prog_mode(void) 647 { 648 return (__m_tty_set_prog_mode()); 649 } 650