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