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