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