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