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