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