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