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