1 /**************************************************************************** 2 * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc. * 3 * * 4 * Permission is hereby granted, free of charge, to any person obtaining a * 5 * copy of this software and associated documentation files (the * 6 * "Software"), to deal in the Software without restriction, including * 7 * without limitation the rights to use, copy, modify, merge, publish, * 8 * distribute, distribute with modifications, sublicense, and/or sell * 9 * copies of the Software, and to permit persons to whom the Software is * 10 * furnished to do so, subject to the following conditions: * 11 * * 12 * The above copyright notice and this permission notice shall be included * 13 * in all copies or substantial portions of the Software. * 14 * * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 18 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 21 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 22 * * 23 * Except as contained in this notice, the name(s) of the above copyright * 24 * holders shall not be used in advertising or otherwise to promote the * 25 * sale, use or other dealings in this Software without prior written * 26 * authorization. * 27 ****************************************************************************/ 28 29 /**************************************************************************** 30 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 31 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 32 * and: Thomas E. Dickey 1996-on * 33 ****************************************************************************/ 34 35 /* 36 * tset.c - terminal initialization utility 37 * 38 * This code was mostly swiped from 4.4BSD tset, with some obsolescent 39 * cruft removed and substantial portions rewritten. A Regents of the 40 * University of California copyright applies to some portions of the 41 * code, and is reproduced below: 42 */ 43 /*- 44 * Copyright (c) 1980, 1991, 1993 45 * The Regents of the University of California. All rights reserved. 46 * 47 * Redistribution and use in source and binary forms, with or without 48 * modification, are permitted provided that the following conditions 49 * are met: 50 * 1. Redistributions of source code must retain the above copyright 51 * notice, this list of conditions and the following disclaimer. 52 * 2. Redistributions in binary form must reproduce the above copyright 53 * notice, this list of conditions and the following disclaimer in the 54 * documentation and/or other materials provided with the distribution. 55 * 3. All advertising materials mentioning features or use of this software 56 * must display the following acknowledgement: 57 * This product includes software developed by the University of 58 * California, Berkeley and its contributors. 59 * 4. Neither the name of the University nor the names of its contributors 60 * may be used to endorse or promote products derived from this software 61 * without specific prior written permission. 62 * 63 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 64 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 65 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 66 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 67 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 68 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 69 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 70 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 71 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 72 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 73 * SUCH DAMAGE. 74 */ 75 76 #define USE_LIBTINFO 77 #define __INTERNAL_CAPS_VISIBLE /* we need to see has_hardware_tabs */ 78 #include <progs.priv.h> 79 80 #include <errno.h> 81 #include <stdio.h> 82 #include <termcap.h> 83 #include <fcntl.h> 84 85 #if HAVE_GETTTYNAM && HAVE_TTYENT_H 86 #include <ttyent.h> 87 #endif 88 #ifdef NeXT 89 char *ttyname(int fd); 90 #endif 91 92 #if HAVE_SIZECHANGE 93 # if !defined(sun) || !TERMIOS 94 # if HAVE_SYS_IOCTL_H 95 # include <sys/ioctl.h> 96 # endif 97 # endif 98 #endif 99 100 #if NEED_PTEM_H 101 /* they neglected to define struct winsize in termios.h -- it's only 102 in termio.h */ 103 #include <sys/stream.h> 104 #include <sys/ptem.h> 105 #endif 106 107 #include <dump_entry.h> 108 #include <transform.h> 109 110 MODULE_ID("$Id: tset.c,v 1.76 2008/10/11 19:26:19 tom Exp $") 111 112 /* 113 * SCO defines TIOCGSIZE and the corresponding struct. Other systems (SunOS, 114 * Solaris, IRIX) define TIOCGWINSZ and struct winsize. 115 */ 116 #ifdef TIOCGSIZE 117 # define IOCTL_GET_WINSIZE TIOCGSIZE 118 # define IOCTL_SET_WINSIZE TIOCSSIZE 119 # define STRUCT_WINSIZE struct ttysize 120 # define WINSIZE_ROWS(n) n.ts_lines 121 # define WINSIZE_COLS(n) n.ts_cols 122 #else 123 # ifdef TIOCGWINSZ 124 # define IOCTL_GET_WINSIZE TIOCGWINSZ 125 # define IOCTL_SET_WINSIZE TIOCSWINSZ 126 # define STRUCT_WINSIZE struct winsize 127 # define WINSIZE_ROWS(n) n.ws_row 128 # define WINSIZE_COLS(n) n.ws_col 129 # endif 130 #endif 131 132 extern char **environ; 133 134 #undef CTRL 135 #define CTRL(x) ((x) & 0x1f) 136 137 const char *_nc_progname = "tset"; 138 139 static TTY mode, oldmode, original; 140 141 static bool opt_c; /* set control-chars */ 142 static bool opt_w; /* set window-size */ 143 144 static bool can_restore = FALSE; 145 static bool isreset = FALSE; /* invoked as reset */ 146 static int terasechar = -1; /* new erase character */ 147 static int intrchar = -1; /* new interrupt character */ 148 static int tkillchar = -1; /* new kill character */ 149 static int tlines, tcolumns; /* window size */ 150 151 #define LOWERCASE(c) ((isalpha(UChar(c)) && isupper(UChar(c))) ? tolower(UChar(c)) : (c)) 152 153 static int 154 CaselessCmp(const char *a, const char *b) 155 { /* strcasecmp isn't portable */ 156 while (*a && *b) { 157 int cmp = LOWERCASE(*a) - LOWERCASE(*b); 158 if (cmp != 0) 159 break; 160 a++, b++; 161 } 162 return LOWERCASE(*a) - LOWERCASE(*b); 163 } 164 165 static void 166 exit_error(void) 167 { 168 if (can_restore) 169 SET_TTY(STDERR_FILENO, &original); 170 (void) fprintf(stderr, "\n"); 171 fflush(stderr); 172 ExitProgram(EXIT_FAILURE); 173 /* NOTREACHED */ 174 } 175 176 static void 177 err(const char *fmt,...) 178 { 179 va_list ap; 180 va_start(ap, fmt); 181 (void) fprintf(stderr, "%s: ", _nc_progname); 182 (void) vfprintf(stderr, fmt, ap); 183 va_end(ap); 184 exit_error(); 185 /* NOTREACHED */ 186 } 187 188 static void 189 failed(const char *msg) 190 { 191 char temp[BUFSIZ]; 192 unsigned len = strlen(_nc_progname) + 2; 193 194 if ((int) len < (int) sizeof(temp) - 12) { 195 strcpy(temp, _nc_progname); 196 strcat(temp, ": "); 197 } else { 198 strcpy(temp, "tset: "); 199 } 200 perror(strncat(temp, msg, sizeof(temp) - strlen(temp) - 2)); 201 exit_error(); 202 /* NOTREACHED */ 203 } 204 205 static void 206 cat(char *file) 207 { 208 FILE *fp; 209 size_t nr; 210 char buf[BUFSIZ]; 211 212 if ((fp = fopen(file, "r")) == 0) 213 failed(file); 214 215 while ((nr = fread(buf, sizeof(char), sizeof(buf), fp)) != 0) 216 if (fwrite(buf, sizeof(char), nr, stderr) != nr) 217 failed("write to stderr"); 218 fclose(fp); 219 } 220 221 static int 222 outc(int c) 223 { 224 return putc(c, stderr); 225 } 226 227 /* Prompt the user for a terminal type. */ 228 static const char * 229 askuser(const char *dflt) 230 { 231 static char answer[256]; 232 char *p; 233 234 /* We can get recalled; if so, don't continue uselessly. */ 235 clearerr(stdin); 236 if (feof(stdin) || ferror(stdin)) { 237 (void) fprintf(stderr, "\n"); 238 exit_error(); 239 /* NOTREACHED */ 240 } 241 for (;;) { 242 if (dflt) 243 (void) fprintf(stderr, "Terminal type? [%s] ", dflt); 244 else 245 (void) fprintf(stderr, "Terminal type? "); 246 (void) fflush(stderr); 247 248 if (fgets(answer, sizeof(answer), stdin) == 0) { 249 if (dflt == 0) { 250 exit_error(); 251 /* NOTREACHED */ 252 } 253 return (dflt); 254 } 255 256 if ((p = strchr(answer, '\n')) != 0) 257 *p = '\0'; 258 if (answer[0]) 259 return (answer); 260 if (dflt != 0) 261 return (dflt); 262 } 263 } 264 265 /************************************************************************** 266 * 267 * Mapping logic begins here 268 * 269 **************************************************************************/ 270 271 /* Baud rate conditionals for mapping. */ 272 #define GT 0x01 273 #define EQ 0x02 274 #define LT 0x04 275 #define NOT 0x08 276 #define GE (GT | EQ) 277 #define LE (LT | EQ) 278 279 typedef struct map { 280 struct map *next; /* Linked list of maps. */ 281 const char *porttype; /* Port type, or "" for any. */ 282 const char *type; /* Terminal type to select. */ 283 int conditional; /* Baud rate conditionals bitmask. */ 284 int speed; /* Baud rate to compare against. */ 285 } MAP; 286 287 static MAP *cur, *maplist; 288 289 typedef struct speeds { 290 const char *string; 291 int speed; 292 } SPEEDS; 293 294 static const SPEEDS speeds[] = 295 { 296 {"0", B0}, 297 {"50", B50}, 298 {"75", B75}, 299 {"110", B110}, 300 {"134", B134}, 301 {"134.5", B134}, 302 {"150", B150}, 303 {"200", B200}, 304 {"300", B300}, 305 {"600", B600}, 306 {"1200", B1200}, 307 {"1800", B1800}, 308 {"2400", B2400}, 309 {"4800", B4800}, 310 {"9600", B9600}, 311 /* sgttyb may define up to this point */ 312 #ifdef B19200 313 {"19200", B19200}, 314 #endif 315 #ifdef B38400 316 {"38400", B38400}, 317 #endif 318 #ifdef B19200 319 {"19200", B19200}, 320 #endif 321 #ifdef B38400 322 {"38400", B38400}, 323 #endif 324 #ifdef B19200 325 {"19200", B19200}, 326 #else 327 #ifdef EXTA 328 {"19200", EXTA}, 329 #endif 330 #endif 331 #ifdef B38400 332 {"38400", B38400}, 333 #else 334 #ifdef EXTB 335 {"38400", EXTB}, 336 #endif 337 #endif 338 #ifdef B57600 339 {"57600", B57600}, 340 #endif 341 #ifdef B115200 342 {"115200", B115200}, 343 #endif 344 #ifdef B230400 345 {"230400", B230400}, 346 #endif 347 #ifdef B460800 348 {"460800", B460800}, 349 #endif 350 {(char *) 0, 0} 351 }; 352 353 static int 354 tbaudrate(char *rate) 355 { 356 const SPEEDS *sp; 357 int found = FALSE; 358 359 /* The baudrate number can be preceded by a 'B', which is ignored. */ 360 if (*rate == 'B') 361 ++rate; 362 363 for (sp = speeds; sp->string; ++sp) { 364 if (!CaselessCmp(rate, sp->string)) { 365 found = TRUE; 366 break; 367 } 368 } 369 if (!found) 370 err("unknown baud rate %s", rate); 371 return (sp->speed); 372 } 373 374 /* 375 * Syntax for -m: 376 * [port-type][test baudrate]:terminal-type 377 * The baud rate tests are: >, <, @, =, ! 378 */ 379 static void 380 add_mapping(const char *port, char *arg) 381 { 382 MAP *mapp; 383 char *copy, *p; 384 const char *termp; 385 char *base = 0; 386 387 copy = strdup(arg); 388 mapp = (MAP *) malloc(sizeof(MAP)); 389 if (copy == 0 || mapp == 0) 390 failed("malloc"); 391 mapp->next = 0; 392 if (maplist == 0) 393 cur = maplist = mapp; 394 else { 395 cur->next = mapp; 396 cur = mapp; 397 } 398 399 mapp->porttype = arg; 400 mapp->conditional = 0; 401 402 arg = strpbrk(arg, "><@=!:"); 403 404 if (arg == 0) { /* [?]term */ 405 mapp->type = mapp->porttype; 406 mapp->porttype = 0; 407 goto done; 408 } 409 410 if (arg == mapp->porttype) /* [><@=! baud]:term */ 411 termp = mapp->porttype = 0; 412 else 413 termp = base = arg; 414 415 for (;; ++arg) { /* Optional conditionals. */ 416 switch (*arg) { 417 case '<': 418 if (mapp->conditional & GT) 419 goto badmopt; 420 mapp->conditional |= LT; 421 break; 422 case '>': 423 if (mapp->conditional & LT) 424 goto badmopt; 425 mapp->conditional |= GT; 426 break; 427 case '@': 428 case '=': /* Not documented. */ 429 mapp->conditional |= EQ; 430 break; 431 case '!': 432 mapp->conditional |= NOT; 433 break; 434 default: 435 goto next; 436 } 437 } 438 439 next: 440 if (*arg == ':') { 441 if (mapp->conditional) 442 goto badmopt; 443 ++arg; 444 } else { /* Optional baudrate. */ 445 arg = strchr(p = arg, ':'); 446 if (arg == 0) 447 goto badmopt; 448 *arg++ = '\0'; 449 mapp->speed = tbaudrate(p); 450 } 451 452 if (arg == (char *) 0) /* Non-optional type. */ 453 goto badmopt; 454 455 mapp->type = arg; 456 457 /* Terminate porttype, if specified. */ 458 if (termp != 0) 459 *base = '\0'; 460 461 /* If a NOT conditional, reverse the test. */ 462 if (mapp->conditional & NOT) 463 mapp->conditional = ~mapp->conditional & (EQ | GT | LT); 464 465 /* If user specified a port with an option flag, set it. */ 466 done: 467 if (port) { 468 if (mapp->porttype) { 469 badmopt: 470 err("illegal -m option format: %s", copy); 471 } 472 mapp->porttype = port; 473 } 474 free(copy); 475 #ifdef MAPDEBUG 476 (void) printf("port: %s\n", mapp->porttype ? mapp->porttype : "ANY"); 477 (void) printf("type: %s\n", mapp->type); 478 (void) printf("conditional: "); 479 p = ""; 480 if (mapp->conditional & GT) { 481 (void) printf("GT"); 482 p = "/"; 483 } 484 if (mapp->conditional & EQ) { 485 (void) printf("%sEQ", p); 486 p = "/"; 487 } 488 if (mapp->conditional & LT) 489 (void) printf("%sLT", p); 490 (void) printf("\nspeed: %d\n", mapp->speed); 491 #endif 492 } 493 494 /* 495 * Return the type of terminal to use for a port of type 'type', as specified 496 * by the first applicable mapping in 'map'. If no mappings apply, return 497 * 'type'. 498 */ 499 static const char * 500 mapped(const char *type) 501 { 502 MAP *mapp; 503 int match; 504 505 for (mapp = maplist; mapp; mapp = mapp->next) 506 if (mapp->porttype == 0 || !strcmp(mapp->porttype, type)) { 507 switch (mapp->conditional) { 508 case 0: /* No test specified. */ 509 match = TRUE; 510 break; 511 case EQ: 512 match = (ospeed == mapp->speed); 513 break; 514 case GE: 515 match = (ospeed >= mapp->speed); 516 break; 517 case GT: 518 match = (ospeed > mapp->speed); 519 break; 520 case LE: 521 match = (ospeed <= mapp->speed); 522 break; 523 case LT: 524 match = (ospeed < mapp->speed); 525 break; 526 default: 527 match = FALSE; 528 } 529 if (match) 530 return (mapp->type); 531 } 532 /* No match found; return given type. */ 533 return (type); 534 } 535 536 /************************************************************************** 537 * 538 * Entry fetching 539 * 540 **************************************************************************/ 541 542 /* 543 * Figure out what kind of terminal we're dealing with, and then read in 544 * its termcap entry. 545 */ 546 static const char * 547 get_termcap_entry(char *userarg) 548 { 549 int errret; 550 char *p; 551 const char *ttype; 552 #if HAVE_GETTTYNAM 553 struct ttyent *t; 554 #else 555 FILE *fp; 556 #endif 557 char *ttypath; 558 559 if (userarg) { 560 ttype = userarg; 561 goto found; 562 } 563 564 /* Try the environment. */ 565 if ((ttype = getenv("TERM")) != 0) 566 goto map; 567 568 if ((ttypath = ttyname(STDERR_FILENO)) != 0) { 569 p = _nc_basename(ttypath); 570 #if HAVE_GETTTYNAM 571 /* 572 * We have the 4.3BSD library call getttynam(3); that means 573 * there's an /etc/ttys to look up device-to-type mappings in. 574 * Try ttyname(3); check for dialup or other mapping. 575 */ 576 if ((t = getttynam(p))) { 577 ttype = t->ty_type; 578 goto map; 579 } 580 #else 581 if ((fp = fopen("/etc/ttytype", "r")) != 0 582 || (fp = fopen("/etc/ttys", "r")) != 0) { 583 char buffer[BUFSIZ]; 584 char *s, *t, *d; 585 586 while (fgets(buffer, sizeof(buffer) - 1, fp) != 0) { 587 for (s = buffer, t = d = 0; *s; s++) { 588 if (isspace(UChar(*s))) 589 *s = '\0'; 590 else if (t == 0) 591 t = s; 592 else if (d == 0 && s != buffer && s[-1] == '\0') 593 d = s; 594 } 595 if (t != 0 && d != 0 && !strcmp(d, p)) { 596 ttype = strdup(t); 597 fclose(fp); 598 goto map; 599 } 600 } 601 fclose(fp); 602 } 603 #endif /* HAVE_GETTTYNAM */ 604 } 605 606 /* If still undefined, use "unknown". */ 607 ttype = "unknown"; 608 609 map:ttype = mapped(ttype); 610 611 /* 612 * If not a path, remove TERMCAP from the environment so we get a 613 * real entry from /etc/termcap. This prevents us from being fooled 614 * by out of date stuff in the environment. 615 */ 616 found:if ((p = getenv("TERMCAP")) != 0 && !_nc_is_abs_path(p)) { 617 /* 'unsetenv("TERMCAP")' is not portable. 618 * The 'environ' array is better. 619 */ 620 int n; 621 for (n = 0; environ[n] != 0; n++) { 622 if (!strncmp("TERMCAP=", environ[n], 8)) { 623 while ((environ[n] = environ[n + 1]) != 0) { 624 n++; 625 } 626 break; 627 } 628 } 629 } 630 631 /* 632 * ttype now contains a pointer to the type of the terminal. 633 * If the first character is '?', ask the user. 634 */ 635 if (ttype[0] == '?') { 636 if (ttype[1] != '\0') 637 ttype = askuser(ttype + 1); 638 else 639 ttype = askuser(0); 640 } 641 /* Find the terminfo entry. If it doesn't exist, ask the user. */ 642 while (setupterm((NCURSES_CONST char *) ttype, STDOUT_FILENO, &errret) 643 != OK) { 644 if (errret == 0) { 645 (void) fprintf(stderr, "%s: unknown terminal type %s\n", 646 _nc_progname, ttype); 647 ttype = 0; 648 } else { 649 (void) fprintf(stderr, 650 "%s: can't initialize terminal type %s (error %d)\n", 651 _nc_progname, ttype, errret); 652 ttype = 0; 653 } 654 ttype = askuser(ttype); 655 } 656 #if BROKEN_LINKER 657 tgetflag("am"); /* force lib_termcap.o to be linked for 'ospeed' */ 658 #endif 659 return (ttype); 660 } 661 662 /************************************************************************** 663 * 664 * Mode-setting logic 665 * 666 **************************************************************************/ 667 668 /* some BSD systems have these built in, some systems are missing 669 * one or more definitions. The safest solution is to override unless the 670 * commonly-altered ones are defined. 671 */ 672 #if !(defined(CERASE) && defined(CINTR) && defined(CKILL) && defined(CQUIT)) 673 #undef CEOF 674 #undef CERASE 675 #undef CINTR 676 #undef CKILL 677 #undef CLNEXT 678 #undef CRPRNT 679 #undef CQUIT 680 #undef CSTART 681 #undef CSTOP 682 #undef CSUSP 683 #endif 684 685 /* control-character defaults */ 686 #ifndef CEOF 687 #define CEOF CTRL('D') 688 #endif 689 #ifndef CERASE 690 #define CERASE CTRL('H') 691 #endif 692 #ifndef CINTR 693 #define CINTR 127 /* ^? */ 694 #endif 695 #ifndef CKILL 696 #define CKILL CTRL('U') 697 #endif 698 #ifndef CLNEXT 699 #define CLNEXT CTRL('v') 700 #endif 701 #ifndef CRPRNT 702 #define CRPRNT CTRL('r') 703 #endif 704 #ifndef CQUIT 705 #define CQUIT CTRL('\\') 706 #endif 707 #ifndef CSTART 708 #define CSTART CTRL('Q') 709 #endif 710 #ifndef CSTOP 711 #define CSTOP CTRL('S') 712 #endif 713 #ifndef CSUSP 714 #define CSUSP CTRL('Z') 715 #endif 716 717 #if defined(_POSIX_VDISABLE) 718 #define DISABLED(val) (((_POSIX_VDISABLE != -1) \ 719 && ((val) == _POSIX_VDISABLE)) \ 720 || ((val) <= 0)) 721 #else 722 #define DISABLED(val) ((int)(val) <= 0) 723 #endif 724 725 #define CHK(val, dft) (DISABLED(val) ? dft : val) 726 727 static bool set_tabs(void); 728 729 /* 730 * Reset the terminal mode bits to a sensible state. Very useful after 731 * a child program dies in raw mode. 732 */ 733 static void 734 reset_mode(void) 735 { 736 #ifdef TERMIOS 737 tcgetattr(STDERR_FILENO, &mode); 738 #else 739 stty(STDERR_FILENO, &mode); 740 #endif 741 742 #ifdef TERMIOS 743 #if defined(VDISCARD) && defined(CDISCARD) 744 mode.c_cc[VDISCARD] = CHK(mode.c_cc[VDISCARD], CDISCARD); 745 #endif 746 mode.c_cc[VEOF] = CHK(mode.c_cc[VEOF], CEOF); 747 mode.c_cc[VERASE] = CHK(mode.c_cc[VERASE], CERASE); 748 #if defined(VFLUSH) && defined(CFLUSH) 749 mode.c_cc[VFLUSH] = CHK(mode.c_cc[VFLUSH], CFLUSH); 750 #endif 751 mode.c_cc[VINTR] = CHK(mode.c_cc[VINTR], CINTR); 752 mode.c_cc[VKILL] = CHK(mode.c_cc[VKILL], CKILL); 753 #if defined(VLNEXT) && defined(CLNEXT) 754 mode.c_cc[VLNEXT] = CHK(mode.c_cc[VLNEXT], CLNEXT); 755 #endif 756 mode.c_cc[VQUIT] = CHK(mode.c_cc[VQUIT], CQUIT); 757 #if defined(VREPRINT) && defined(CRPRNT) 758 mode.c_cc[VREPRINT] = CHK(mode.c_cc[VREPRINT], CRPRNT); 759 #endif 760 #if defined(VSTART) && defined(CSTART) 761 mode.c_cc[VSTART] = CHK(mode.c_cc[VSTART], CSTART); 762 #endif 763 #if defined(VSTOP) && defined(CSTOP) 764 mode.c_cc[VSTOP] = CHK(mode.c_cc[VSTOP], CSTOP); 765 #endif 766 #if defined(VSUSP) && defined(CSUSP) 767 mode.c_cc[VSUSP] = CHK(mode.c_cc[VSUSP], CSUSP); 768 #endif 769 #if defined(VWERASE) && defined(CWERASE) 770 mode.c_cc[VWERASE] = CHK(mode.c_cc[VWERASE], CWERASE); 771 #endif 772 773 mode.c_iflag &= ~(IGNBRK | PARMRK | INPCK | ISTRIP | INLCR | IGNCR 774 #ifdef IUCLC 775 | IUCLC 776 #endif 777 #ifdef IXANY 778 | IXANY 779 #endif 780 | IXOFF); 781 782 mode.c_iflag |= (BRKINT | IGNPAR | ICRNL | IXON 783 #ifdef IMAXBEL 784 | IMAXBEL 785 #endif 786 ); 787 788 mode.c_oflag &= ~(0 789 #ifdef OLCUC 790 | OLCUC 791 #endif 792 #ifdef OCRNL 793 | OCRNL 794 #endif 795 #ifdef ONOCR 796 | ONOCR 797 #endif 798 #ifdef ONLRET 799 | ONLRET 800 #endif 801 #ifdef OFILL 802 | OFILL 803 #endif 804 #ifdef OFDEL 805 | OFDEL 806 #endif 807 #ifdef NLDLY 808 | NLDLY 809 #endif 810 #ifdef CRDLY 811 | CRDLY 812 #endif 813 #ifdef TABDLY 814 | TABDLY 815 #endif 816 #ifdef BSDLY 817 | BSDLY 818 #endif 819 #ifdef VTDLY 820 | VTDLY 821 #endif 822 #ifdef FFDLY 823 | FFDLY 824 #endif 825 ); 826 827 mode.c_oflag |= (OPOST 828 #ifdef ONLCR 829 | ONLCR 830 #endif 831 ); 832 833 mode.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD | CLOCAL); 834 mode.c_cflag |= (CS8 | CREAD); 835 mode.c_lflag &= ~(ECHONL | NOFLSH 836 #ifdef TOSTOP 837 | TOSTOP 838 #endif 839 #ifdef ECHOPTR 840 | ECHOPRT 841 #endif 842 #ifdef XCASE 843 | XCASE 844 #endif 845 ); 846 847 mode.c_lflag |= (ISIG | ICANON | ECHO | ECHOE | ECHOK 848 #ifdef ECHOCTL 849 | ECHOCTL 850 #endif 851 #ifdef ECHOKE 852 | ECHOKE 853 #endif 854 ); 855 #endif 856 857 SET_TTY(STDERR_FILENO, &mode); 858 } 859 860 /* 861 * Returns a "good" value for the erase character. This is loosely based on 862 * the BSD4.4 logic. 863 */ 864 #ifdef TERMIOS 865 static int 866 default_erase(void) 867 { 868 int result; 869 870 if (over_strike 871 && key_backspace != 0 872 && strlen(key_backspace) == 1) 873 result = key_backspace[0]; 874 else 875 result = CERASE; 876 877 return result; 878 } 879 #endif 880 881 /* 882 * Update the values of the erase, interrupt, and kill characters in 'mode'. 883 * 884 * SVr4 tset (e.g., Solaris 2.5) only modifies the intr, quit or erase 885 * characters if they're unset, or if we specify them as options. This differs 886 * from BSD 4.4 tset, which always sets erase. 887 */ 888 static void 889 set_control_chars(void) 890 { 891 #ifdef TERMIOS 892 if (DISABLED(mode.c_cc[VERASE]) || terasechar >= 0) 893 mode.c_cc[VERASE] = (terasechar >= 0) ? terasechar : default_erase(); 894 895 if (DISABLED(mode.c_cc[VINTR]) || intrchar >= 0) 896 mode.c_cc[VINTR] = (intrchar >= 0) ? intrchar : CINTR; 897 898 if (DISABLED(mode.c_cc[VKILL]) || tkillchar >= 0) 899 mode.c_cc[VKILL] = (tkillchar >= 0) ? tkillchar : CKILL; 900 #endif 901 } 902 903 /* 904 * Set up various conversions in 'mode', including parity, tabs, returns, 905 * echo, and case, according to the termcap entry. If the program we're 906 * running was named with a leading upper-case character, map external 907 * uppercase to internal lowercase. 908 */ 909 static void 910 set_conversions(void) 911 { 912 #ifdef __OBSOLETE__ 913 /* 914 * Conversion logic for some *really* ancient terminal glitches, 915 * not supported in terminfo. Left here for succeeding generations 916 * to marvel at. 917 */ 918 if (tgetflag("UC")) { 919 #ifdef IUCLC 920 mode.c_iflag |= IUCLC; 921 mode.c_oflag |= OLCUC; 922 #endif 923 } else if (tgetflag("LC")) { 924 #ifdef IUCLC 925 mode.c_iflag &= ~IUCLC; 926 mode.c_oflag &= ~OLCUC; 927 #endif 928 } 929 mode.c_iflag &= ~(PARMRK | INPCK); 930 mode.c_lflag |= ICANON; 931 if (tgetflag("EP")) { 932 mode.c_cflag |= PARENB; 933 mode.c_cflag &= ~PARODD; 934 } 935 if (tgetflag("OP")) { 936 mode.c_cflag |= PARENB; 937 mode.c_cflag |= PARODD; 938 } 939 #endif /* __OBSOLETE__ */ 940 941 #ifdef TERMIOS 942 #ifdef ONLCR 943 mode.c_oflag |= ONLCR; 944 #endif 945 mode.c_iflag |= ICRNL; 946 mode.c_lflag |= ECHO; 947 #ifdef OXTABS 948 mode.c_oflag |= OXTABS; 949 #endif /* OXTABS */ 950 951 /* test used to be tgetflag("NL") */ 952 if (newline != (char *) 0 && newline[0] == '\n' && !newline[1]) { 953 /* Newline, not linefeed. */ 954 #ifdef ONLCR 955 mode.c_oflag &= ~ONLCR; 956 #endif 957 mode.c_iflag &= ~ICRNL; 958 } 959 #ifdef __OBSOLETE__ 960 if (tgetflag("HD")) /* Half duplex. */ 961 mode.c_lflag &= ~ECHO; 962 #endif /* __OBSOLETE__ */ 963 #ifdef OXTABS 964 /* test used to be tgetflag("pt") */ 965 if (has_hardware_tabs) /* Print tabs. */ 966 mode.c_oflag &= ~OXTABS; 967 #endif /* OXTABS */ 968 mode.c_lflag |= (ECHOE | ECHOK); 969 #endif 970 } 971 972 /* Output startup string. */ 973 static void 974 set_init(void) 975 { 976 char *p; 977 bool settle; 978 979 #ifdef __OBSOLETE__ 980 if (pad_char != (char *) 0) /* Get/set pad character. */ 981 PC = pad_char[0]; 982 #endif /* OBSOLETE */ 983 984 #ifdef TAB3 985 if (oldmode.c_oflag & (TAB3 | ONLCR | OCRNL | ONLRET)) { 986 oldmode.c_oflag &= (TAB3 | ONLCR | OCRNL | ONLRET); 987 SET_TTY(STDERR_FILENO, &oldmode); 988 } 989 #endif 990 settle = set_tabs(); 991 992 if (isreset) { 993 if ((p = reset_1string) != 0) { 994 tputs(p, 0, outc); 995 settle = TRUE; 996 } 997 if ((p = reset_2string) != 0) { 998 tputs(p, 0, outc); 999 settle = TRUE; 1000 } 1001 /* What about rf, rs3, as per terminfo man page? */ 1002 /* also might be nice to send rmacs, rmul, rmm */ 1003 if ((p = reset_file) != 0 1004 || (p = init_file) != 0) { 1005 cat(p); 1006 settle = TRUE; 1007 } 1008 } 1009 1010 if (settle) { 1011 (void) putc('\r', stderr); 1012 (void) fflush(stderr); 1013 (void) napms(1000); /* Settle the terminal. */ 1014 } 1015 } 1016 1017 /* 1018 * Set the hardware tabs on the terminal, using the ct (clear all tabs), 1019 * st (set one tab) and ch (horizontal cursor addressing) capabilities. 1020 * This is done before if and is, so they can patch in case we blow this. 1021 * Return TRUE if we set any tab stops, FALSE if not. 1022 */ 1023 static bool 1024 set_tabs(void) 1025 { 1026 if (set_tab && clear_all_tabs) { 1027 int c; 1028 1029 (void) putc('\r', stderr); /* Force to left margin. */ 1030 tputs(clear_all_tabs, 0, outc); 1031 1032 for (c = 8; c < tcolumns; c += 8) { 1033 /* Get to the right column. In BSD tset, this 1034 * used to try a bunch of half-clever things 1035 * with cup and hpa, for an average saving of 1036 * somewhat less than two character times per 1037 * tab stop, less than .01 sec at 2400cps. We 1038 * lost all this cruft because it seemed to be 1039 * introducing some odd bugs. 1040 * -----------12345678----------- */ 1041 (void) fputs(" ", stderr); 1042 tputs(set_tab, 0, outc); 1043 } 1044 putc('\r', stderr); 1045 return (TRUE); 1046 } 1047 return (FALSE); 1048 } 1049 1050 /************************************************************************** 1051 * 1052 * Main sequence 1053 * 1054 **************************************************************************/ 1055 1056 /* 1057 * Tell the user if a control key has been changed from the default value. 1058 */ 1059 #ifdef TERMIOS 1060 static void 1061 report(const char *name, int which, unsigned def) 1062 { 1063 unsigned older, newer; 1064 char *p; 1065 1066 newer = mode.c_cc[which]; 1067 older = oldmode.c_cc[which]; 1068 1069 if (older == newer && older == def) 1070 return; 1071 1072 (void) fprintf(stderr, "%s %s ", name, older == newer ? "is" : "set to"); 1073 1074 if (DISABLED(newer)) 1075 (void) fprintf(stderr, "undef.\n"); 1076 /* 1077 * Check 'delete' before 'backspace', since the key_backspace value 1078 * is ambiguous. 1079 */ 1080 else if (newer == 0177) 1081 (void) fprintf(stderr, "delete.\n"); 1082 else if ((p = key_backspace) != 0 1083 && newer == (unsigned char) p[0] 1084 && p[1] == '\0') 1085 (void) fprintf(stderr, "backspace.\n"); 1086 else if (newer < 040) { 1087 newer ^= 0100; 1088 (void) fprintf(stderr, "control-%c (^%c).\n", UChar(newer), UChar(newer)); 1089 } else 1090 (void) fprintf(stderr, "%c.\n", UChar(newer)); 1091 } 1092 #endif 1093 1094 /* 1095 * Convert the obsolete argument forms into something that getopt can handle. 1096 * This means that -e, -i and -k get default arguments supplied for them. 1097 */ 1098 static void 1099 obsolete(char **argv) 1100 { 1101 for (; *argv; ++argv) { 1102 char *parm = argv[0]; 1103 1104 if (parm[0] == '-' && parm[1] == '\0') { 1105 argv[0] = strdup("-q"); 1106 continue; 1107 } 1108 1109 if ((parm[0] != '-') 1110 || (argv[1] && argv[1][0] != '-') 1111 || (parm[1] != 'e' && parm[1] != 'i' && parm[1] != 'k') 1112 || (parm[2] != '\0')) 1113 continue; 1114 switch (argv[0][1]) { 1115 case 'e': 1116 argv[0] = strdup("-e^H"); 1117 break; 1118 case 'i': 1119 argv[0] = strdup("-i^C"); 1120 break; 1121 case 'k': 1122 argv[0] = strdup("-k^U"); 1123 break; 1124 } 1125 } 1126 } 1127 1128 static void 1129 usage(void) 1130 { 1131 static const char *tbl[] = 1132 { 1133 "" 1134 ,"Options:" 1135 ," -c set control characters" 1136 ," -e ch erase character" 1137 ," -I no initialization strings" 1138 ," -i ch interrupt character" 1139 ," -k ch kill character" 1140 ," -m mapping map identifier to type" 1141 ," -Q do not output control key settings" 1142 ," -r display term on stderr" 1143 ," -s output TERM set command" 1144 ," -V print curses-version" 1145 ," -w set window-size" 1146 }; 1147 unsigned n; 1148 (void) fprintf(stderr, "Usage: %s [options] [terminal]\n", _nc_progname); 1149 for (n = 0; n < sizeof(tbl) / sizeof(tbl[0]); ++n) 1150 fprintf(stderr, "%s\n", tbl[n]); 1151 exit_error(); 1152 /* NOTREACHED */ 1153 } 1154 1155 static char 1156 arg_to_char(void) 1157 { 1158 return (char) ((optarg[0] == '^' && optarg[1] != '\0') 1159 ? ((optarg[1] == '?') ? '\177' : CTRL(optarg[1])) 1160 : optarg[0]); 1161 } 1162 1163 int 1164 main(int argc, char **argv) 1165 { 1166 int ch, noinit, noset, quiet, Sflag, sflag, showterm; 1167 const char *p; 1168 const char *ttype; 1169 1170 obsolete(argv); 1171 noinit = noset = quiet = Sflag = sflag = showterm = 0; 1172 while ((ch = getopt(argc, argv, "a:cd:e:Ii:k:m:np:qQSrsVw")) != -1) { 1173 switch (ch) { 1174 case 'c': /* set control-chars */ 1175 opt_c = TRUE; 1176 break; 1177 case 'a': /* OBSOLETE: map identifier to type */ 1178 add_mapping("arpanet", optarg); 1179 break; 1180 case 'd': /* OBSOLETE: map identifier to type */ 1181 add_mapping("dialup", optarg); 1182 break; 1183 case 'e': /* erase character */ 1184 terasechar = arg_to_char(); 1185 break; 1186 case 'I': /* no initialization strings */ 1187 noinit = 1; 1188 break; 1189 case 'i': /* interrupt character */ 1190 intrchar = arg_to_char(); 1191 break; 1192 case 'k': /* kill character */ 1193 tkillchar = arg_to_char(); 1194 break; 1195 case 'm': /* map identifier to type */ 1196 add_mapping(0, optarg); 1197 break; 1198 case 'n': /* OBSOLETE: set new tty driver */ 1199 break; 1200 case 'p': /* OBSOLETE: map identifier to type */ 1201 add_mapping("plugboard", optarg); 1202 break; 1203 case 'Q': /* don't output control key settings */ 1204 quiet = 1; 1205 break; 1206 case 'q': /* display term only */ 1207 noset = 1; 1208 break; 1209 case 'r': /* display term on stderr */ 1210 showterm = 1; 1211 break; 1212 case 'S': /* OBSOLETE: output TERM & TERMCAP */ 1213 Sflag = 1; 1214 break; 1215 case 's': /* output TERM set command */ 1216 sflag = 1; 1217 break; 1218 case 'V': /* print curses-version */ 1219 puts(curses_version()); 1220 ExitProgram(EXIT_SUCCESS); 1221 case 'w': /* set window-size */ 1222 opt_w = TRUE; 1223 break; 1224 case '?': 1225 default: 1226 usage(); 1227 } 1228 } 1229 1230 _nc_progname = _nc_rootname(*argv); 1231 argc -= optind; 1232 argv += optind; 1233 1234 if (argc > 1) 1235 usage(); 1236 1237 if (!opt_c && !opt_w) 1238 opt_c = opt_w = TRUE; 1239 1240 if (GET_TTY(STDERR_FILENO, &mode) < 0) 1241 failed("standard error"); 1242 can_restore = TRUE; 1243 original = oldmode = mode; 1244 #ifdef TERMIOS 1245 ospeed = (NCURSES_OSPEED) cfgetospeed(&mode); 1246 #else 1247 ospeed = (NCURSES_OSPEED) mode.sg_ospeed; 1248 #endif 1249 1250 if (!strcmp(_nc_progname, PROG_RESET)) { 1251 isreset = TRUE; 1252 reset_mode(); 1253 } 1254 1255 ttype = get_termcap_entry(*argv); 1256 1257 if (!noset) { 1258 tcolumns = columns; 1259 tlines = lines; 1260 1261 #if HAVE_SIZECHANGE 1262 if (opt_w) { 1263 STRUCT_WINSIZE win; 1264 /* Set window size if not set already */ 1265 (void) ioctl(STDERR_FILENO, IOCTL_GET_WINSIZE, &win); 1266 if (WINSIZE_ROWS(win) == 0 && 1267 WINSIZE_COLS(win) == 0 && 1268 tlines > 0 && tcolumns > 0) { 1269 WINSIZE_ROWS(win) = tlines; 1270 WINSIZE_COLS(win) = tcolumns; 1271 (void) ioctl(STDERR_FILENO, IOCTL_SET_WINSIZE, &win); 1272 } 1273 } 1274 #endif 1275 if (opt_c) { 1276 set_control_chars(); 1277 set_conversions(); 1278 1279 if (!noinit) 1280 set_init(); 1281 1282 /* Set the modes if they've changed. */ 1283 if (memcmp(&mode, &oldmode, sizeof(mode))) { 1284 SET_TTY(STDERR_FILENO, &mode); 1285 } 1286 } 1287 } 1288 1289 /* Get the terminal name from the entry. */ 1290 ttype = _nc_first_name(cur_term->type.term_names); 1291 1292 if (noset) 1293 (void) printf("%s\n", ttype); 1294 else { 1295 if (showterm) 1296 (void) fprintf(stderr, "Terminal type is %s.\n", ttype); 1297 /* 1298 * If erase, kill and interrupt characters could have been 1299 * modified and not -Q, display the changes. 1300 */ 1301 #ifdef TERMIOS 1302 if (!quiet) { 1303 report("Erase", VERASE, CERASE); 1304 report("Kill", VKILL, CKILL); 1305 report("Interrupt", VINTR, CINTR); 1306 } 1307 #endif 1308 } 1309 1310 if (Sflag) 1311 err("The -S option is not supported under terminfo."); 1312 1313 if (sflag) { 1314 int len; 1315 char *var; 1316 char *leaf; 1317 /* 1318 * Figure out what shell we're using. A hack, we look for an 1319 * environmental variable SHELL ending in "csh". 1320 */ 1321 if ((var = getenv("SHELL")) != 0 1322 && ((len = (int) strlen(leaf = _nc_basename(var))) >= 3) 1323 && !strcmp(leaf + len - 3, "csh")) 1324 p = "set noglob;\nsetenv TERM %s;\nunset noglob;\n"; 1325 else 1326 p = "TERM=%s;\n"; 1327 (void) printf(p, ttype); 1328 } 1329 1330 ExitProgram(EXIT_SUCCESS); 1331 } 1332