1/**************************************************************************** 2 * Copyright (c) 1998-2004,2005 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.60 2005/09/25 00:43:52 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 && *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#define CHK(val, dft) ((int)val <= 0 ? dft : val) 690 691static bool set_tabs(void); 692 693/* 694 * Reset the terminal mode bits to a sensible state. Very useful after 695 * a child program dies in raw mode. 696 */ 697static void 698reset_mode(void) 699{ 700#ifdef TERMIOS 701 tcgetattr(STDERR_FILENO, &mode); 702#else 703 stty(STDERR_FILENO, &mode); 704#endif 705 706#ifdef TERMIOS 707#if defined(VDISCARD) && defined(CDISCARD) 708 mode.c_cc[VDISCARD] = CHK(mode.c_cc[VDISCARD], CDISCARD); 709#endif 710 mode.c_cc[VEOF] = CHK(mode.c_cc[VEOF], CEOF); 711 mode.c_cc[VERASE] = CHK(mode.c_cc[VERASE], CERASE); 712#if defined(VFLUSH) && defined(CFLUSH) 713 mode.c_cc[VFLUSH] = CHK(mode.c_cc[VFLUSH], CFLUSH); 714#endif 715 mode.c_cc[VINTR] = CHK(mode.c_cc[VINTR], CINTR); 716 mode.c_cc[VKILL] = CHK(mode.c_cc[VKILL], CKILL); 717#if defined(VLNEXT) && defined(CLNEXT) 718 mode.c_cc[VLNEXT] = CHK(mode.c_cc[VLNEXT], CLNEXT); 719#endif 720 mode.c_cc[VQUIT] = CHK(mode.c_cc[VQUIT], CQUIT); 721#if defined(VREPRINT) && defined(CRPRNT) 722 mode.c_cc[VREPRINT] = CHK(mode.c_cc[VREPRINT], CRPRNT); 723#endif 724#if defined(VSTART) && defined(CSTART) 725 mode.c_cc[VSTART] = CHK(mode.c_cc[VSTART], CSTART); 726#endif 727#if defined(VSTOP) && defined(CSTOP) 728 mode.c_cc[VSTOP] = CHK(mode.c_cc[VSTOP], CSTOP); 729#endif 730#if defined(VSUSP) && defined(CSUSP) 731 mode.c_cc[VSUSP] = CHK(mode.c_cc[VSUSP], CSUSP); 732#endif 733#if defined(VWERASE) && defined(CWERASE) 734 mode.c_cc[VWERASE] = CHK(mode.c_cc[VWERASE], CWERASE); 735#endif 736 737 mode.c_iflag &= ~(IGNBRK | PARMRK | INPCK | ISTRIP | INLCR | IGNCR 738#ifdef IUCLC 739 | IUCLC 740#endif 741#ifdef IXANY 742 | IXANY 743#endif 744 | IXOFF); 745 746 mode.c_iflag |= (BRKINT | IGNPAR | ICRNL | IXON 747#ifdef IMAXBEL 748 | IMAXBEL 749#endif 750 ); 751 752 mode.c_oflag &= ~(0 753#ifdef OLCUC 754 | OLCUC 755#endif 756#ifdef OCRNL 757 | OCRNL 758#endif 759#ifdef ONOCR 760 | ONOCR 761#endif 762#ifdef ONLRET 763 | ONLRET 764#endif 765#ifdef OFILL 766 | OFILL 767#endif 768#ifdef OFDEL 769 | OFDEL 770#endif 771#ifdef NLDLY 772 | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY 773#endif 774 ); 775 776 mode.c_oflag |= (OPOST 777#ifdef ONLCR 778 | ONLCR 779#endif 780 ); 781 782 mode.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD | CLOCAL); 783 mode.c_cflag |= (CS8 | CREAD); 784 mode.c_lflag &= ~(ECHONL | NOFLSH 785#ifdef TOSTOP 786 | TOSTOP 787#endif 788#ifdef ECHOPTR 789 | ECHOPRT 790#endif 791#ifdef XCASE 792 | XCASE 793#endif 794 ); 795 796 mode.c_lflag |= (ISIG | ICANON | ECHO | ECHOE | ECHOK 797#ifdef ECHOCTL 798 | ECHOCTL 799#endif 800#ifdef ECHOKE 801 | ECHOKE 802#endif 803 ); 804#endif 805 806 SET_TTY(STDERR_FILENO, &mode); 807} 808 809/* 810 * Returns a "good" value for the erase character. This is loosely based on 811 * the BSD4.4 logic. 812 */ 813#ifdef TERMIOS 814static int 815default_erase(void) 816{ 817 int result; 818 819 if (over_strike 820 && key_backspace != 0 821 && strlen(key_backspace) == 1) 822 result = key_backspace[0]; 823 else 824 result = CERASE; 825 826 return result; 827} 828#endif 829 830/* 831 * Update the values of the erase, interrupt, and kill characters in 'mode'. 832 * 833 * SVr4 tset (e.g., Solaris 2.5) only modifies the intr, quit or erase 834 * characters if they're unset, or if we specify them as options. This differs 835 * from BSD 4.4 tset, which always sets erase. 836 */ 837static void 838set_control_chars(void) 839{ 840#ifdef TERMIOS 841 if (mode.c_cc[VERASE] == 0 || terasechar >= 0) 842 mode.c_cc[VERASE] = terasechar >= 0 ? terasechar : default_erase(); 843 844 if (mode.c_cc[VINTR] == 0 || intrchar >= 0) 845 mode.c_cc[VINTR] = intrchar >= 0 ? intrchar : CINTR; 846 847 if (mode.c_cc[VKILL] == 0 || tkillchar >= 0) 848 mode.c_cc[VKILL] = tkillchar >= 0 ? tkillchar : CKILL; 849#endif 850} 851 852/* 853 * Set up various conversions in 'mode', including parity, tabs, returns, 854 * echo, and case, according to the termcap entry. If the program we're 855 * running was named with a leading upper-case character, map external 856 * uppercase to internal lowercase. 857 */ 858static void 859set_conversions(void) 860{ 861#ifdef __OBSOLETE__ 862 /* 863 * Conversion logic for some *really* ancient terminal glitches, 864 * not supported in terminfo. Left here for succeeding generations 865 * to marvel at. 866 */ 867 if (tgetflag("UC")) { 868#ifdef IUCLC 869 mode.c_iflag |= IUCLC; 870 mode.c_oflag |= OLCUC; 871#endif 872 } else if (tgetflag("LC")) { 873#ifdef IUCLC 874 mode.c_iflag &= ~IUCLC; 875 mode.c_oflag &= ~OLCUC; 876#endif 877 } 878 mode.c_iflag &= ~(PARMRK | INPCK); 879 mode.c_lflag |= ICANON; 880 if (tgetflag("EP")) { 881 mode.c_cflag |= PARENB; 882 mode.c_cflag &= ~PARODD; 883 } 884 if (tgetflag("OP")) { 885 mode.c_cflag |= PARENB; 886 mode.c_cflag |= PARODD; 887 } 888#endif /* __OBSOLETE__ */ 889 890#ifdef TERMIOS 891#ifdef ONLCR 892 mode.c_oflag |= ONLCR; 893#endif 894 mode.c_iflag |= ICRNL; 895 mode.c_lflag |= ECHO; 896#ifdef OXTABS 897 mode.c_oflag |= OXTABS; 898#endif /* OXTABS */ 899 900 /* test used to be tgetflag("NL") */ 901 if (newline != (char *) 0 && newline[0] == '\n' && !newline[1]) { 902 /* Newline, not linefeed. */ 903#ifdef ONLCR 904 mode.c_oflag &= ~ONLCR; 905#endif 906 mode.c_iflag &= ~ICRNL; 907 } 908#ifdef __OBSOLETE__ 909 if (tgetflag("HD")) /* Half duplex. */ 910 mode.c_lflag &= ~ECHO; 911#endif /* __OBSOLETE__ */ 912#ifdef OXTABS 913 /* test used to be tgetflag("pt") */ 914 if (has_hardware_tabs) /* Print tabs. */ 915 mode.c_oflag &= ~OXTABS; 916#endif /* OXTABS */ 917 mode.c_lflag |= (ECHOE | ECHOK); 918#endif 919} 920 921/* Output startup string. */ 922static void 923set_init(void) 924{ 925 char *p; 926 bool settle; 927 928#ifdef __OBSOLETE__ 929 if (pad_char != (char *) 0) /* Get/set pad character. */ 930 PC = pad_char[0]; 931#endif /* OBSOLETE */ 932 933#ifdef TAB3 934 if (oldmode.c_oflag & (TAB3 | ONLCR | OCRNL | ONLRET)) { 935 oldmode.c_oflag &= (TAB3 | ONLCR | OCRNL | ONLRET); 936 SET_TTY(STDERR_FILENO, &oldmode); 937 } 938#endif 939 settle = set_tabs(); 940 941 if (isreset) { 942 if ((p = reset_1string) != 0) { 943 tputs(p, 0, outc); 944 settle = TRUE; 945 } 946 if ((p = reset_2string) != 0) { 947 tputs(p, 0, outc); 948 settle = TRUE; 949 } 950 /* What about rf, rs3, as per terminfo man page? */ 951 /* also might be nice to send rmacs, rmul, rmm */ 952 if ((p = reset_file) != 0 953 || (p = init_file) != 0) { 954 cat(p); 955 settle = TRUE; 956 } 957 } 958 959 if (settle) { 960 (void) putc('\r', stderr); 961 (void) fflush(stderr); 962 (void) napms(1000); /* Settle the terminal. */ 963 } 964} 965 966/* 967 * Set the hardware tabs on the terminal, using the ct (clear all tabs), 968 * st (set one tab) and ch (horizontal cursor addressing) capabilities. 969 * This is done before if and is, so they can patch in case we blow this. 970 * Return TRUE if we set any tab stops, FALSE if not. 971 */ 972static bool 973set_tabs() 974{ 975 if (set_tab && clear_all_tabs) { 976 int c; 977 978 (void) putc('\r', stderr); /* Force to left margin. */ 979 tputs(clear_all_tabs, 0, outc); 980 981 for (c = 8; c < tcolumns; c += 8) { 982 /* Get to the right column. In BSD tset, this 983 * used to try a bunch of half-clever things 984 * with cup and hpa, for an average saving of 985 * somewhat less than two character times per 986 * tab stop, less than .01 sec at 2400cps. We 987 * lost all this cruft because it seemed to be 988 * introducing some odd bugs. 989 * -----------12345678----------- */ 990 (void) fputs(" ", stderr); 991 tputs(set_tab, 0, outc); 992 } 993 putc('\r', stderr); 994 return (TRUE); 995 } 996 return (FALSE); 997} 998 999/************************************************************************** 1000 * 1001 * Main sequence 1002 * 1003 **************************************************************************/ 1004 1005/* 1006 * Tell the user if a control key has been changed from the default value. 1007 */ 1008#ifdef TERMIOS 1009static void 1010report(const char *name, int which, unsigned def) 1011{ 1012 unsigned older, newer; 1013 char *p; 1014 1015 newer = mode.c_cc[which]; 1016 older = oldmode.c_cc[which]; 1017 1018 if (older == newer && older == def) 1019 return; 1020 1021 (void) fprintf(stderr, "%s %s ", name, older == newer ? "is" : "set to"); 1022 1023 /* 1024 * Check 'delete' before 'backspace', since the key_backspace value 1025 * is ambiguous. 1026 */ 1027 if (newer == 0177) 1028 (void) fprintf(stderr, "delete.\n"); 1029 else if ((p = key_backspace) != 0 1030 && newer == (unsigned char) p[0] 1031 && p[1] == '\0') 1032 (void) fprintf(stderr, "backspace.\n"); 1033 else if (newer < 040) { 1034 newer ^= 0100; 1035 (void) fprintf(stderr, "control-%c (^%c).\n", UChar(newer), UChar(newer)); 1036 } else 1037 (void) fprintf(stderr, "%c.\n", UChar(newer)); 1038} 1039#endif 1040 1041/* 1042 * Convert the obsolete argument forms into something that getopt can handle. 1043 * This means that -e, -i and -k get default arguments supplied for them. 1044 */ 1045static void 1046obsolete(char **argv) 1047{ 1048 for (; *argv; ++argv) { 1049 char *parm = argv[0]; 1050 1051 if (parm[0] == '-' && parm[1] == '\0') { 1052 argv[0] = strdup("-q"); 1053 continue; 1054 } 1055 1056 if ((parm[0] != '-') 1057 || (argv[1] && argv[1][0] != '-') 1058 || (parm[1] != 'e' && parm[1] != 'i' && parm[1] != 'k') 1059 || (parm[2] != '\0')) 1060 continue; 1061 switch (argv[0][1]) { 1062 case 'e': 1063 argv[0] = strdup("-e^H"); 1064 break; 1065 case 'i': 1066 argv[0] = strdup("-i^C"); 1067 break; 1068 case 'k': 1069 argv[0] = strdup("-k^U"); 1070 break; 1071 } 1072 } 1073} 1074 1075static void 1076usage(void) 1077{ 1078 static const char *tbl[] = 1079 { 1080 "" 1081 ,"Options:" 1082 ," -c set control characters" 1083 ," -e ch erase character" 1084 ," -I no initialization strings" 1085 ," -i ch interrupt character" 1086 ," -k ch kill character" 1087 ," -m mapping map identifier to type" 1088 ," -Q do not output control key settings" 1089 ," -r display term on stderr" 1090 ," -s output TERM set command" 1091 ," -V print curses-version" 1092 ," -w set window-size" 1093 }; 1094 unsigned n; 1095 (void) fprintf(stderr, "Usage: %s [options] [terminal]\n", _nc_progname); 1096 for (n = 0; n < sizeof(tbl) / sizeof(tbl[0]); ++n) 1097 fprintf(stderr, "%s\n", tbl[n]); 1098 exit_error(); 1099 /* NOTREACHED */ 1100} 1101 1102static char 1103arg_to_char(void) 1104{ 1105 return (optarg[0] == '^' && optarg[1] != '\0') 1106 ? ((optarg[1] == '?') ? '\177' : CTRL(optarg[1])) 1107 : optarg[0]; 1108} 1109 1110int 1111main(int argc, char **argv) 1112{ 1113#if defined(TIOCGWINSZ) && defined(TIOCSWINSZ) 1114 struct winsize win; 1115#endif 1116 int ch, noinit, noset, quiet, Sflag, sflag, showterm; 1117 const char *p; 1118 const char *ttype; 1119 1120 obsolete(argv); 1121 noinit = noset = quiet = Sflag = sflag = showterm = 0; 1122 while ((ch = getopt(argc, argv, "a:cd:e:Ii:k:m:np:qQSrsVw")) != EOF) { 1123 switch (ch) { 1124 case 'c': /* set control-chars */ 1125 opt_c = TRUE; 1126 break; 1127 case 'a': /* OBSOLETE: map identifier to type */ 1128 add_mapping("arpanet", optarg); 1129 break; 1130 case 'd': /* OBSOLETE: map identifier to type */ 1131 add_mapping("dialup", optarg); 1132 break; 1133 case 'e': /* erase character */ 1134 terasechar = arg_to_char(); 1135 break; 1136 case 'I': /* no initialization strings */ 1137 noinit = 1; 1138 break; 1139 case 'i': /* interrupt character */ 1140 intrchar = arg_to_char(); 1141 break; 1142 case 'k': /* kill character */ 1143 tkillchar = arg_to_char(); 1144 break; 1145 case 'm': /* map identifier to type */ 1146 add_mapping(0, optarg); 1147 break; 1148 case 'n': /* OBSOLETE: set new tty driver */ 1149 break; 1150 case 'p': /* OBSOLETE: map identifier to type */ 1151 add_mapping("plugboard", optarg); 1152 break; 1153 case 'Q': /* don't output control key settings */ 1154 quiet = 1; 1155 break; 1156 case 'q': /* display term only */ 1157 noset = 1; 1158 break; 1159 case 'r': /* display term on stderr */ 1160 showterm = 1; 1161 break; 1162 case 'S': /* OBSOLETE: output TERM & TERMCAP */ 1163 Sflag = 1; 1164 break; 1165 case 's': /* output TERM set command */ 1166 sflag = 1; 1167 break; 1168 case 'V': /* print curses-version */ 1169 puts(curses_version()); 1170 return EXIT_SUCCESS; 1171 case 'w': /* set window-size */ 1172 opt_w = TRUE; 1173 break; 1174 case '?': 1175 default: 1176 usage(); 1177 } 1178 } 1179 1180 _nc_progname = _nc_rootname(*argv); 1181 argc -= optind; 1182 argv += optind; 1183 1184 if (argc > 1) 1185 usage(); 1186 1187 if (!opt_c && !opt_w) 1188 opt_c = opt_w = TRUE; 1189 1190 if (GET_TTY(STDERR_FILENO, &mode) < 0) 1191 failed("standard error"); 1192 can_restore = TRUE; 1193 original = oldmode = mode; 1194#ifdef TERMIOS 1195 ospeed = cfgetospeed(&mode); 1196#else 1197 ospeed = mode.sg_ospeed; 1198#endif 1199 1200 if (!strcmp(_nc_progname, PROG_RESET)) { 1201 isreset = TRUE; 1202 reset_mode(); 1203 } 1204 1205 ttype = get_termcap_entry(*argv); 1206 1207 if (!noset) { 1208 tcolumns = columns; 1209 tlines = lines; 1210 1211#if defined(TIOCGWINSZ) && defined(TIOCSWINSZ) 1212 if (opt_w) { 1213 /* Set window size */ 1214 (void) ioctl(STDERR_FILENO, TIOCGWINSZ, &win); 1215 if (win.ws_row == 0 && win.ws_col == 0 && 1216 tlines > 0 && tcolumns > 0) { 1217 win.ws_row = tlines; 1218 win.ws_col = tcolumns; 1219 (void) ioctl(STDERR_FILENO, TIOCSWINSZ, &win); 1220 } 1221 } 1222#endif 1223 if (opt_c) { 1224 set_control_chars(); 1225 set_conversions(); 1226 1227 if (!noinit) 1228 set_init(); 1229 1230 /* Set the modes if they've changed. */ 1231 if (memcmp(&mode, &oldmode, sizeof(mode))) { 1232 SET_TTY(STDERR_FILENO, &mode); 1233 } 1234 } 1235 } 1236 1237 /* Get the terminal name from the entry. */ 1238 ttype = _nc_first_name(cur_term->type.term_names); 1239 1240 if (noset) 1241 (void) printf("%s\n", ttype); 1242 else { 1243 if (showterm) 1244 (void) fprintf(stderr, "Terminal type is %s.\n", ttype); 1245 /* 1246 * If erase, kill and interrupt characters could have been 1247 * modified and not -Q, display the changes. 1248 */ 1249#ifdef TERMIOS 1250 if (!quiet) { 1251 report("Erase", VERASE, CERASE); 1252 report("Kill", VKILL, CKILL); 1253 report("Interrupt", VINTR, CINTR); 1254 } 1255#endif 1256 } 1257 1258 if (Sflag) 1259 err("The -S option is not supported under terminfo."); 1260 1261 if (sflag) { 1262 int len; 1263 /* 1264 * Figure out what shell we're using. A hack, we look for an 1265 * environmental variable SHELL ending in "csh". 1266 */ 1267 if ((p = getenv("SHELL")) != 0 1268 && (len = strlen(p)) >= 3 1269 && !strcmp(p + len - 3, "csh")) 1270 p = "set noglob;\nsetenv TERM %s;\nunset noglob;\n"; 1271 else 1272 p = "TERM=%s;\n"; 1273 (void) printf(p, ttype); 1274 } 1275 1276 return EXIT_SUCCESS; 1277} 1278