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