1 /****************************************************************************
2 * Copyright (c) 1998-2016,2017 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>
91 #include <tty_settings.h>
93 #if HAVE_GETTTYNAM && HAVE_TTYENT_H
97 char *ttyname(int fd);
100 MODULE_ID("$Id: tset.c,v 1.120 2017/10/08 00:01:29 tom Exp $")
103 extern char **environ;
106 const char *_nc_progname = "tset";
108 #define LOWERCASE(c) ((isalpha(UChar(c)) && isupper(UChar(c))) ? tolower(UChar(c)) : (c))
110 static void exit_error(void) GCC_NORETURN;
113 CaselessCmp(const char *a, const char *b)
114 { /* strcasecmp isn't portable */
116 int cmp = LOWERCASE(*a) - LOWERCASE(*b);
121 return LOWERCASE(*a) - LOWERCASE(*b);
127 restore_tty_settings();
128 (void) fprintf(stderr, "\n");
130 ExitProgram(EXIT_FAILURE);
135 err(const char *fmt,...)
139 (void) fprintf(stderr, "%s: ", _nc_progname);
140 (void) vfprintf(stderr, fmt, ap);
147 failed(const char *msg)
150 size_t len = strlen(_nc_progname) + 2;
152 if ((int) len < (int) sizeof(temp) - 12) {
153 _nc_STRCPY(temp, _nc_progname, sizeof(temp));
154 _nc_STRCAT(temp, ": ", sizeof(temp));
156 _nc_STRCPY(temp, "tset: ", sizeof(temp));
158 _nc_STRNCAT(temp, msg, sizeof(temp), sizeof(temp) - strlen(temp) - 2);
164 /* Prompt the user for a terminal type. */
166 askuser(const char *dflt)
168 static char answer[256];
171 /* We can get recalled; if so, don't continue uselessly. */
173 if (feof(stdin) || ferror(stdin)) {
174 (void) fprintf(stderr, "\n");
180 (void) fprintf(stderr, "Terminal type? [%s] ", dflt);
182 (void) fprintf(stderr, "Terminal type? ");
183 (void) fflush(stderr);
185 if (fgets(answer, sizeof(answer), stdin) == 0) {
193 if ((p = strchr(answer, '\n')) != 0)
202 /**************************************************************************
204 * Mapping logic begins here
206 **************************************************************************/
208 /* Baud rate conditionals for mapping. */
217 struct map *next; /* Linked list of maps. */
218 const char *porttype; /* Port type, or "" for any. */
219 const char *type; /* Terminal type to select. */
220 int conditional; /* Baud rate conditionals bitmask. */
221 int speed; /* Baud rate to compare against. */
224 static MAP *cur, *maplist;
226 #define DATA(name,value) { { name }, value }
228 typedef struct speeds {
229 const char string[7];
233 static const SPEEDS speeds[] =
250 /* sgttyb may define up to this point */
252 DATA("19200", B19200),
255 DATA("38400", B38400),
258 DATA("19200", B19200),
261 DATA("38400", B38400),
264 DATA("19200", B19200),
271 DATA("38400", B38400),
278 DATA("57600", B57600),
281 DATA("76800", B57600),
284 DATA("115200", B115200),
287 DATA("153600", B153600),
290 DATA("230400", B230400),
293 DATA("307200", B307200),
296 DATA("460800", B460800),
299 DATA("500000", B500000),
302 DATA("576000", B576000),
305 DATA("921600", B921600),
308 DATA("1000000", B1000000),
311 DATA("1152000", B1152000),
314 DATA("1500000", B1500000),
317 DATA("2000000", B2000000),
320 DATA("2500000", B2500000),
323 DATA("3000000", B3000000),
326 DATA("3500000", B3500000),
329 DATA("4000000", B4000000),
335 tbaudrate(char *rate)
337 const SPEEDS *sp = 0;
340 /* The baudrate number can be preceded by a 'B', which is ignored. */
344 for (n = 0; n < SIZEOF(speeds); ++n) {
345 if (n > 0 && (speeds[n].speed <= speeds[n - 1].speed)) {
346 /* if the speeds are not increasing, likely a numeric overflow */
349 if (!CaselessCmp(rate, speeds[n].string)) {
355 err("unknown baud rate %s", rate);
361 * [port-type][test baudrate]:terminal-type
362 * The baud rate tests are: >, <, @, =, !
365 add_mapping(const char *port, char *arg)
373 mapp = typeMalloc(MAP, 1);
374 if (copy == 0 || mapp == 0)
382 cur = maplist = mapp;
388 mapp->porttype = arg;
389 mapp->conditional = 0;
391 arg = strpbrk(arg, "><@=!:");
393 if (arg == 0) { /* [?]term */
394 mapp->type = mapp->porttype;
399 if (arg == mapp->porttype) /* [><@=! baud]:term */
400 termp = mapp->porttype = 0;
404 for (;; ++arg) { /* Optional conditionals. */
407 if (mapp->conditional & GT)
409 mapp->conditional |= LT;
412 if (mapp->conditional & LT)
414 mapp->conditional |= GT;
417 case '=': /* Not documented. */
418 mapp->conditional |= EQ;
421 mapp->conditional |= NOT;
430 if (mapp->conditional)
433 } else { /* Optional baudrate. */
434 arg = strchr(p = arg, ':');
438 mapp->speed = tbaudrate(p);
443 /* Terminate porttype, if specified. */
447 /* If a NOT conditional, reverse the test. */
448 if (mapp->conditional & NOT)
449 mapp->conditional = ~mapp->conditional & (EQ | GT | LT);
451 /* If user specified a port with an option flag, set it. */
454 if (mapp->porttype) {
456 err("illegal -m option format: %s", copy);
458 mapp->porttype = port;
462 (void) printf("port: %s\n", mapp->porttype ? mapp->porttype : "ANY");
463 (void) printf("type: %s\n", mapp->type);
464 (void) printf("conditional: ");
466 if (mapp->conditional & GT) {
470 if (mapp->conditional & EQ) {
471 (void) printf("%sEQ", p);
474 if (mapp->conditional & LT)
475 (void) printf("%sLT", p);
476 (void) printf("\nspeed: %d\n", mapp->speed);
481 * Return the type of terminal to use for a port of type 'type', as specified
482 * by the first applicable mapping in 'map'. If no mappings apply, return
486 mapped(const char *type)
491 for (mapp = maplist; mapp; mapp = mapp->next)
492 if (mapp->porttype == 0 || !strcmp(mapp->porttype, type)) {
493 switch (mapp->conditional) {
494 case 0: /* No test specified. */
498 match = ((int) ospeed == mapp->speed);
501 match = ((int) ospeed >= mapp->speed);
504 match = ((int) ospeed > mapp->speed);
507 match = ((int) ospeed <= mapp->speed);
510 match = ((int) ospeed < mapp->speed);
518 /* No match found; return given type. */
522 /**************************************************************************
526 **************************************************************************/
529 * Figure out what kind of terminal we're dealing with, and then read in
533 get_termcap_entry(int fd, char *userarg)
552 /* Try the environment. */
553 if ((ttype = getenv("TERM")) != 0)
556 if ((ttypath = ttyname(fd)) != 0) {
557 p = _nc_basename(ttypath);
560 * We have the 4.3BSD library call getttynam(3); that means
561 * there's an /etc/ttys to look up device-to-type mappings in.
562 * Try ttyname(3); check for dialup or other mapping.
564 if ((t = getttynam(p))) {
569 if ((fp = fopen("/etc/ttytype", "r")) != 0
570 || (fp = fopen("/etc/ttys", "r")) != 0) {
574 while (fgets(buffer, sizeof(buffer) - 1, fp) != 0) {
575 for (s = buffer, t = d = 0; *s; s++) {
576 if (isspace(UChar(*s)))
580 else if (d == 0 && s != buffer && s[-1] == '\0')
583 if (t != 0 && d != 0 && !strcmp(d, p)) {
591 #endif /* HAVE_GETTTYNAM */
594 /* If still undefined, use "unknown". */
597 map:ttype = mapped(ttype);
600 * If not a path, remove TERMCAP from the environment so we get a
601 * real entry from /etc/termcap. This prevents us from being fooled
602 * by out of date stuff in the environment.
605 if ((p = getenv("TERMCAP")) != 0 && !_nc_is_abs_path(p)) {
606 /* 'unsetenv("TERMCAP")' is not portable.
607 * The 'environ' array is better.
610 for (n = 0; environ[n] != 0; n++) {
611 if (!strncmp("TERMCAP=", environ[n], (size_t) 8)) {
612 while ((environ[n] = environ[n + 1]) != 0) {
621 * ttype now contains a pointer to the type of the terminal.
622 * If the first character is '?', ask the user.
624 if (ttype[0] == '?') {
625 if (ttype[1] != '\0')
626 ttype = askuser(ttype + 1);
630 /* Find the terminfo entry. If it doesn't exist, ask the user. */
631 while (setupterm((NCURSES_CONST char *) ttype, fd, &errret)
634 (void) fprintf(stderr, "%s: unknown terminal type %s\n",
635 _nc_progname, ttype);
638 (void) fprintf(stderr,
639 "%s: can't initialize terminal type %s (error %d)\n",
640 _nc_progname, ttype, errret);
643 ttype = askuser(ttype);
646 tgetflag("am"); /* force lib_termcap.o to be linked for 'ospeed' */
651 /**************************************************************************
655 **************************************************************************/
658 * Convert the obsolete argument forms into something that getopt can handle.
659 * This means that -e, -i and -k get default arguments supplied for them.
662 obsolete(char **argv)
664 for (; *argv; ++argv) {
665 char *parm = argv[0];
667 if (parm[0] == '-' && parm[1] == '\0') {
668 argv[0] = strdup("-q");
673 || (argv[1] && argv[1][0] != '-')
674 || (parm[1] != 'e' && parm[1] != 'i' && parm[1] != 'k')
675 || (parm[2] != '\0'))
677 switch (argv[0][1]) {
679 argv[0] = strdup("-e^H");
682 argv[0] = strdup("-i^C");
685 argv[0] = strdup("-k^U");
692 print_shell_commands(const char *ttype)
699 * Figure out what shell we're using. A hack, we look for an
700 * environmental variable SHELL ending in "csh".
702 if ((var = getenv("SHELL")) != 0
703 && ((len = (int) strlen(leaf = _nc_basename(var))) >= 3)
704 && !strcmp(leaf + len - 3, "csh"))
705 p = "set noglob;\nsetenv TERM %s;\nunset noglob;\n";
708 (void) printf(p, ttype);
714 #define SKIP(s) /* nothing */
715 #define KEEP(s) s "\n"
716 static const char msg[] =
720 SKIP(" -a arpanet (obsolete)")
721 KEEP(" -c set control characters")
722 SKIP(" -d dialup (obsolete)")
723 KEEP(" -e ch erase character")
724 KEEP(" -I no initialization strings")
725 KEEP(" -i ch interrupt character")
726 KEEP(" -k ch kill character")
727 KEEP(" -m mapping map identifier to type")
728 SKIP(" -p plugboard (obsolete)")
729 KEEP(" -Q do not output control key settings")
730 KEEP(" -q display term only, do no changes")
731 KEEP(" -r display term on stderr")
732 SKIP(" -S (obsolete)")
733 KEEP(" -s output TERM set command")
734 KEEP(" -V print curses-version")
735 KEEP(" -w set window-size")
737 KEEP("If neither -c/-w are given, both are assumed.")
741 (void) fprintf(stderr, "Usage: %s [options] [terminal]\n", _nc_progname);
743 ExitProgram(EXIT_FAILURE);
750 return (char) ((optarg[0] == '^' && optarg[1] != '\0')
751 ? ((optarg[1] == '?') ? '\177' : CTRL(optarg[1]))
756 main(int argc, char **argv)
758 int ch, noinit, noset, quiet, Sflag, sflag, showterm;
760 int terasechar = -1; /* new erase character */
761 int intrchar = -1; /* new interrupt character */
762 int tkillchar = -1; /* new kill character */
764 bool opt_c = FALSE; /* set control-chars */
765 bool opt_w = FALSE; /* set window-size */
768 my_fd = STDERR_FILENO;
770 noinit = noset = quiet = Sflag = sflag = showterm = 0;
771 while ((ch = getopt(argc, argv, "a:cd:e:Ii:k:m:p:qQrSsVw")) != -1) {
773 case 'c': /* set control-chars */
776 case 'a': /* OBSOLETE: map identifier to type */
777 add_mapping("arpanet", optarg);
779 case 'd': /* OBSOLETE: map identifier to type */
780 add_mapping("dialup", optarg);
782 case 'e': /* erase character */
783 terasechar = arg_to_char();
785 case 'I': /* no initialization strings */
788 case 'i': /* interrupt character */
789 intrchar = arg_to_char();
791 case 'k': /* kill character */
792 tkillchar = arg_to_char();
794 case 'm': /* map identifier to type */
795 add_mapping(0, optarg);
797 case 'p': /* OBSOLETE: map identifier to type */
798 add_mapping("plugboard", optarg);
800 case 'Q': /* don't output control key settings */
803 case 'q': /* display term only */
806 case 'r': /* display term on stderr */
809 case 'S': /* OBSOLETE: output TERM & TERMCAP */
812 case 's': /* output TERM set command */
815 case 'V': /* print curses-version */
816 puts(curses_version());
817 ExitProgram(EXIT_SUCCESS);
818 case 'w': /* set window-size */
827 _nc_progname = _nc_rootname(*argv);
834 if (!opt_c && !opt_w)
835 opt_c = opt_w = TRUE;
837 my_fd = save_tty_settings(&mode, TRUE);
840 ospeed = (NCURSES_OSPEED) cfgetospeed(&mode);
842 ospeed = (NCURSES_OSPEED) mode.sg_ospeed;
845 if (same_program(_nc_progname, PROG_RESET)) {
846 reset_start(stderr, TRUE, FALSE);
847 reset_tty_settings(my_fd, &mode);
849 reset_start(stderr, FALSE, TRUE);
852 ttype = get_termcap_entry(my_fd, *argv);
857 set_window_size(my_fd, &lines, &columns);
861 set_control_chars(&mode, terasechar, intrchar, tkillchar);
862 set_conversions(&mode);
865 if (send_init_strings(my_fd, &oldmode)) {
866 (void) putc('\r', stderr);
867 (void) fflush(stderr);
868 (void) napms(1000); /* Settle the terminal. */
872 update_tty_settings(&oldmode, &mode);
877 (void) printf("%s\n", ttype);
880 (void) fprintf(stderr, "Terminal type is %s.\n", ttype);
882 * If erase, kill and interrupt characters could have been
883 * modified and not -Q, display the changes.
886 print_tty_chars(&oldmode, &mode);
891 err("The -S option is not supported under terminfo.");
894 print_shell_commands(ttype);
897 ExitProgram(EXIT_SUCCESS);