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