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