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