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