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