1 /****************************************************************************
2 * Copyright (c) 1998-2015,2016 Free Software Foundation, Inc. *
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: *
12 * The above copyright notice and this permission notice shall be included *
13 * in all copies or substantial portions of the Software. *
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. *
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 *
27 ****************************************************************************/
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 ****************************************************************************/
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
42 * Comparing with current (2009) source, excluding this comment:
43 * a) 209 lines match identically to the 4.4BSD Lite sources, with 771 lines
45 * a) Ignoring whitespace, the current version still uses 516 lines from the
46 * 4.4BSD Lite sources, with 402 lines changed/added.
48 * Raymond's original comment on this follows...
52 * tset.c - terminal initialization utility
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:
60 * Copyright (c) 1980, 1991, 1993
61 * The Regents of the University of California. All rights reserved.
63 * Redistribution and use in source and binary forms, with or without
64 * modification, are permitted provided that the following conditions
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.
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
88 #include <reset_cmd.h>
90 #include <transform.h>
92 #if HAVE_GETTTYNAM && HAVE_TTYENT_H
96 char *ttyname(int fd);
99 MODULE_ID("$Id: tset.c,v 1.112 2016/09/10 23:33:10 tom Exp $")
102 extern char **environ;
105 const char *_nc_progname = "tset";
107 #define LOWERCASE(c) ((isalpha(UChar(c)) && isupper(UChar(c))) ? tolower(UChar(c)) : (c))
109 static void exit_error(void) GCC_NORETURN;
112 CaselessCmp(const char *a, const char *b)
113 { /* strcasecmp isn't portable */
115 int cmp = LOWERCASE(*a) - LOWERCASE(*b);
120 return LOWERCASE(*a) - LOWERCASE(*b);
126 restore_tty_settings();
127 (void) fprintf(stderr, "\n");
129 ExitProgram(EXIT_FAILURE);
134 err(const char *fmt,...)
138 (void) fprintf(stderr, "%s: ", _nc_progname);
139 (void) vfprintf(stderr, fmt, ap);
146 failed(const char *msg)
149 size_t len = strlen(_nc_progname) + 2;
151 if ((int) len < (int) sizeof(temp) - 12) {
152 _nc_STRCPY(temp, _nc_progname, sizeof(temp));
153 _nc_STRCAT(temp, ": ", sizeof(temp));
155 _nc_STRCPY(temp, "tset: ", sizeof(temp));
157 _nc_STRNCAT(temp, msg, sizeof(temp), sizeof(temp) - strlen(temp) - 2);
163 /* Prompt the user for a terminal type. */
165 askuser(const char *dflt)
167 static char answer[256];
170 /* We can get recalled; if so, don't continue uselessly. */
172 if (feof(stdin) || ferror(stdin)) {
173 (void) fprintf(stderr, "\n");
179 (void) fprintf(stderr, "Terminal type? [%s] ", dflt);
181 (void) fprintf(stderr, "Terminal type? ");
182 (void) fflush(stderr);
184 if (fgets(answer, sizeof(answer), stdin) == 0) {
192 if ((p = strchr(answer, '\n')) != 0)
201 /**************************************************************************
203 * Mapping logic begins here
205 **************************************************************************/
207 /* Baud rate conditionals for mapping. */
216 struct map *next; /* Linked list of maps. */
217 const char *porttype; /* Port type, or "" for any. */
218 const char *type; /* Terminal type to select. */
219 int conditional; /* Baud rate conditionals bitmask. */
220 int speed; /* Baud rate to compare against. */
223 static MAP *cur, *maplist;
225 #define DATA(name,value) { { name }, value }
227 typedef struct speeds {
228 const char string[7];
232 static const SPEEDS speeds[] =
249 /* sgttyb may define up to this point */
251 DATA("19200", B19200),
254 DATA("38400", B38400),
257 DATA("19200", B19200),
260 DATA("38400", B38400),
263 DATA("19200", B19200),
270 DATA("38400", B38400),
277 DATA("57600", B57600),
280 DATA("76800", B57600),
283 DATA("115200", B115200),
286 DATA("153600", B153600),
289 DATA("230400", B230400),
292 DATA("307200", B307200),
295 DATA("460800", B460800),
298 DATA("500000", B500000),
301 DATA("576000", B576000),
304 DATA("921600", B921600),
307 DATA("1000000", B1000000),
310 DATA("1152000", B1152000),
313 DATA("1500000", B1500000),
316 DATA("2000000", B2000000),
319 DATA("2500000", B2500000),
322 DATA("3000000", B3000000),
325 DATA("3500000", B3500000),
328 DATA("4000000", B4000000),
334 tbaudrate(char *rate)
336 const SPEEDS *sp = 0;
339 /* The baudrate number can be preceded by a 'B', which is ignored. */
343 for (n = 0; n < SIZEOF(speeds); ++n) {
344 if (n > 0 && (speeds[n].speed <= speeds[n - 1].speed)) {
345 /* if the speeds are not increasing, likely a numeric overflow */
348 if (!CaselessCmp(rate, speeds[n].string)) {
354 err("unknown baud rate %s", rate);
360 * [port-type][test baudrate]:terminal-type
361 * The baud rate tests are: >, <, @, =, !
364 add_mapping(const char *port, char *arg)
372 mapp = typeMalloc(MAP, 1);
373 if (copy == 0 || mapp == 0)
381 cur = maplist = mapp;
387 mapp->porttype = arg;
388 mapp->conditional = 0;
390 arg = strpbrk(arg, "><@=!:");
392 if (arg == 0) { /* [?]term */
393 mapp->type = mapp->porttype;
398 if (arg == mapp->porttype) /* [><@=! baud]:term */
399 termp = mapp->porttype = 0;
403 for (;; ++arg) { /* Optional conditionals. */
406 if (mapp->conditional & GT)
408 mapp->conditional |= LT;
411 if (mapp->conditional & LT)
413 mapp->conditional |= GT;
416 case '=': /* Not documented. */
417 mapp->conditional |= EQ;
420 mapp->conditional |= NOT;
429 if (mapp->conditional)
432 } else { /* Optional baudrate. */
433 arg = strchr(p = arg, ':');
437 mapp->speed = tbaudrate(p);
442 /* Terminate porttype, if specified. */
446 /* If a NOT conditional, reverse the test. */
447 if (mapp->conditional & NOT)
448 mapp->conditional = ~mapp->conditional & (EQ | GT | LT);
450 /* If user specified a port with an option flag, set it. */
453 if (mapp->porttype) {
455 err("illegal -m option format: %s", copy);
457 mapp->porttype = port;
461 (void) printf("port: %s\n", mapp->porttype ? mapp->porttype : "ANY");
462 (void) printf("type: %s\n", mapp->type);
463 (void) printf("conditional: ");
465 if (mapp->conditional & GT) {
469 if (mapp->conditional & EQ) {
470 (void) printf("%sEQ", p);
473 if (mapp->conditional & LT)
474 (void) printf("%sLT", p);
475 (void) printf("\nspeed: %d\n", mapp->speed);
480 * Return the type of terminal to use for a port of type 'type', as specified
481 * by the first applicable mapping in 'map'. If no mappings apply, return
485 mapped(const char *type)
490 for (mapp = maplist; mapp; mapp = mapp->next)
491 if (mapp->porttype == 0 || !strcmp(mapp->porttype, type)) {
492 switch (mapp->conditional) {
493 case 0: /* No test specified. */
497 match = ((int) ospeed == mapp->speed);
500 match = ((int) ospeed >= mapp->speed);
503 match = ((int) ospeed > mapp->speed);
506 match = ((int) ospeed <= mapp->speed);
509 match = ((int) ospeed < mapp->speed);
517 /* No match found; return given type. */
521 /**************************************************************************
525 **************************************************************************/
528 * Figure out what kind of terminal we're dealing with, and then read in
532 get_termcap_entry(int fd, char *userarg)
551 /* Try the environment. */
552 if ((ttype = getenv("TERM")) != 0)
555 if ((ttypath = ttyname(fd)) != 0) {
556 p = _nc_basename(ttypath);
559 * We have the 4.3BSD library call getttynam(3); that means
560 * there's an /etc/ttys to look up device-to-type mappings in.
561 * Try ttyname(3); check for dialup or other mapping.
563 if ((t = getttynam(p))) {
568 if ((fp = fopen("/etc/ttytype", "r")) != 0
569 || (fp = fopen("/etc/ttys", "r")) != 0) {
573 while (fgets(buffer, sizeof(buffer) - 1, fp) != 0) {
574 for (s = buffer, t = d = 0; *s; s++) {
575 if (isspace(UChar(*s)))
579 else if (d == 0 && s != buffer && s[-1] == '\0')
582 if (t != 0 && d != 0 && !strcmp(d, p)) {
590 #endif /* HAVE_GETTTYNAM */
593 /* If still undefined, use "unknown". */
596 map:ttype = mapped(ttype);
599 * If not a path, remove TERMCAP from the environment so we get a
600 * real entry from /etc/termcap. This prevents us from being fooled
601 * by out of date stuff in the environment.
604 if ((p = getenv("TERMCAP")) != 0 && !_nc_is_abs_path(p)) {
605 /* 'unsetenv("TERMCAP")' is not portable.
606 * The 'environ' array is better.
609 for (n = 0; environ[n] != 0; n++) {
610 if (!strncmp("TERMCAP=", environ[n], (size_t) 8)) {
611 while ((environ[n] = environ[n + 1]) != 0) {
620 * ttype now contains a pointer to the type of the terminal.
621 * If the first character is '?', ask the user.
623 if (ttype[0] == '?') {
624 if (ttype[1] != '\0')
625 ttype = askuser(ttype + 1);
629 /* Find the terminfo entry. If it doesn't exist, ask the user. */
630 while (setupterm((NCURSES_CONST char *) ttype, STDOUT_FILENO, &errret)
633 (void) fprintf(stderr, "%s: unknown terminal type %s\n",
634 _nc_progname, ttype);
637 (void) fprintf(stderr,
638 "%s: can't initialize terminal type %s (error %d)\n",
639 _nc_progname, ttype, errret);
642 ttype = askuser(ttype);
645 tgetflag("am"); /* force lib_termcap.o to be linked for 'ospeed' */
650 /**************************************************************************
654 **************************************************************************/
657 * Convert the obsolete argument forms into something that getopt can handle.
658 * This means that -e, -i and -k get default arguments supplied for them.
661 obsolete(char **argv)
663 for (; *argv; ++argv) {
664 char *parm = argv[0];
666 if (parm[0] == '-' && parm[1] == '\0') {
667 argv[0] = strdup("-q");
672 || (argv[1] && argv[1][0] != '-')
673 || (parm[1] != 'e' && parm[1] != 'i' && parm[1] != 'k')
674 || (parm[2] != '\0'))
676 switch (argv[0][1]) {
678 argv[0] = strdup("-e^H");
681 argv[0] = strdup("-i^C");
684 argv[0] = strdup("-k^U");
691 print_shell_commands(const char *ttype)
698 * Figure out what shell we're using. A hack, we look for an
699 * environmental variable SHELL ending in "csh".
701 if ((var = getenv("SHELL")) != 0
702 && ((len = (int) strlen(leaf = _nc_basename(var))) >= 3)
703 && !strcmp(leaf + len - 3, "csh"))
704 p = "set noglob;\nsetenv TERM %s;\nunset noglob;\n";
707 (void) printf(p, ttype);
713 #define DATA(s) s "\n"
714 static const char msg[] =
718 DATA(" -c set control characters")
719 DATA(" -e ch erase character")
720 DATA(" -I no initialization strings")
721 DATA(" -i ch interrupt character")
722 DATA(" -k ch kill character")
723 DATA(" -m mapping map identifier to type")
724 DATA(" -Q do not output control key settings")
725 DATA(" -r display term on stderr")
726 DATA(" -s output TERM set command")
727 DATA(" -V print curses-version")
728 DATA(" -w set window-size")
731 (void) fprintf(stderr, "Usage: %s [options] [terminal]\n", _nc_progname);
740 return (char) ((optarg[0] == '^' && optarg[1] != '\0')
741 ? ((optarg[1] == '?') ? '\177' : CTRL(optarg[1]))
746 main(int argc, char **argv)
748 int ch, noinit, noset, quiet, Sflag, sflag, showterm;
750 int terasechar = -1; /* new erase character */
751 int intrchar = -1; /* new interrupt character */
752 int tkillchar = -1; /* new kill character */
754 bool opt_c = FALSE; /* set control-chars */
755 bool opt_w = FALSE; /* set window-size */
758 my_fd = STDERR_FILENO;
760 noinit = noset = quiet = Sflag = sflag = showterm = 0;
761 while ((ch = getopt(argc, argv, "a:cd:e:Ii:k:m:p:qQSrsVw")) != -1) {
763 case 'c': /* set control-chars */
766 case 'a': /* OBSOLETE: map identifier to type */
767 add_mapping("arpanet", optarg);
769 case 'd': /* OBSOLETE: map identifier to type */
770 add_mapping("dialup", optarg);
772 case 'e': /* erase character */
773 terasechar = arg_to_char();
775 case 'I': /* no initialization strings */
778 case 'i': /* interrupt character */
779 intrchar = arg_to_char();
781 case 'k': /* kill character */
782 tkillchar = arg_to_char();
784 case 'm': /* map identifier to type */
785 add_mapping(0, optarg);
787 case 'p': /* OBSOLETE: map identifier to type */
788 add_mapping("plugboard", optarg);
790 case 'Q': /* don't output control key settings */
793 case 'q': /* display term only */
796 case 'r': /* display term on stderr */
799 case 'S': /* OBSOLETE: output TERM & TERMCAP */
802 case 's': /* output TERM set command */
805 case 'V': /* print curses-version */
806 puts(curses_version());
807 ExitProgram(EXIT_SUCCESS);
808 case 'w': /* set window-size */
817 _nc_progname = _nc_rootname(*argv);
824 if (!opt_c && !opt_w)
825 opt_c = opt_w = TRUE;
827 my_fd = save_tty_settings(&mode);
830 ospeed = (NCURSES_OSPEED) cfgetospeed(&mode);
832 ospeed = (NCURSES_OSPEED) mode.sg_ospeed;
835 if (same_program(_nc_progname, PROG_RESET)) {
836 reset_start(stderr, TRUE, FALSE);
837 reset_tty_settings(&mode);
839 reset_start(stderr, FALSE, FALSE);
842 ttype = get_termcap_entry(my_fd, *argv);
847 set_window_size(my_fd, lines, columns);
851 set_control_chars(&mode, terasechar, intrchar, tkillchar);
852 set_conversions(&mode);
855 if (send_init_strings(&oldmode)) {
856 (void) putc('\r', stderr);
857 (void) fflush(stderr);
858 (void) napms(1000); /* Settle the terminal. */
862 update_tty_settings(&oldmode, &mode);
867 (void) printf("%s\n", ttype);
870 (void) fprintf(stderr, "Terminal type is %s.\n", ttype);
872 * If erase, kill and interrupt characters could have been
873 * modified and not -Q, display the changes.
876 print_tty_chars(&oldmode, &mode);
881 err("The -S option is not supported under terminfo.");
884 print_shell_commands(ttype);
887 ExitProgram(EXIT_SUCCESS);