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