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