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