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