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