1 /****************************************************************************
2 * Copyright 2020,2021 Thomas E. Dickey *
3 * Copyright 1998-2016,2017 Free Software Foundation, Inc. *
5 * Permission is hereby granted, free of charge, to any person obtaining a *
6 * copy of this software and associated documentation files (the *
7 * "Software"), to deal in the Software without restriction, including *
8 * without limitation the rights to use, copy, modify, merge, publish, *
9 * distribute, distribute with modifications, sublicense, and/or sell *
10 * copies of the Software, and to permit persons to whom the Software is *
11 * furnished to do so, subject to the following conditions: *
13 * The above copyright notice and this permission notice shall be included *
14 * in all copies or substantial portions of the Software. *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
19 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
22 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
24 * Except as contained in this notice, the name(s) of the above copyright *
25 * holders shall not be used in advertising or otherwise to promote the *
26 * sale, use or other dealings in this Software without prior written *
28 ****************************************************************************/
30 /****************************************************************************
31 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 *
32 * and: Eric S. Raymond <esr@snark.thyrsus.com> *
33 * and: Thomas E. Dickey 1996-on *
34 ****************************************************************************/
38 * The initial adaptation from 4.4BSD Lite sources in September 1995 used 686
39 * lines from that version, and made changes/additions for 150 lines. There
40 * was no reformatting, so with/without ignoring whitespace, the amount of
43 * Comparing with current (2009) source, excluding this comment:
44 * a) 209 lines match identically to the 4.4BSD Lite sources, with 771 lines
46 * a) Ignoring whitespace, the current version still uses 516 lines from the
47 * 4.4BSD Lite sources, with 402 lines changed/added.
49 * Raymond's original comment on this follows...
53 * tset.c - terminal initialization utility
55 * This code was mostly swiped from 4.4BSD tset, with some obsolescent
56 * cruft removed and substantial portions rewritten. A Regents of the
57 * University of California copyright applies to some portions of the
58 * code, and is reproduced below:
61 * Copyright (c) 1980, 1991, 1993
62 * The Regents of the University of California. All rights reserved.
64 * Redistribution and use in source and binary forms, with or without
65 * modification, are permitted provided that the following conditions
67 * 1. Redistributions of source code must retain the above copyright
68 * notice, this list of conditions and the following disclaimer.
69 * 2. Redistributions in binary form must reproduce the above copyright
70 * notice, this list of conditions and the following disclaimer in the
71 * documentation and/or other materials provided with the distribution.
72 * 3. Neither the name of the University nor the names of its contributors
73 * may be used to endorse or promote products derived from this software
74 * without specific prior written permission.
76 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
77 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
78 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
79 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
80 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
81 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
82 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
83 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
84 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
85 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
89 #include <reset_cmd.h>
91 #include <transform.h>
92 #include <tty_settings.h>
94 #if HAVE_GETTTYNAM && HAVE_TTYENT_H
98 char *ttyname(int fd);
101 MODULE_ID("$Id: tset.c,v 1.128 2021/04/03 23:03:48 tom Exp $")
104 extern char **environ;
107 const char *_nc_progname = "tset";
109 #define LOWERCASE(c) ((isalpha(UChar(c)) && isupper(UChar(c))) ? tolower(UChar(c)) : (c))
111 static GCC_NORETURN void exit_error(void);
114 CaselessCmp(const char *a, const char *b)
115 { /* strcasecmp isn't portable */
117 int cmp = LOWERCASE(*a) - LOWERCASE(*b);
122 return LOWERCASE(*a) - LOWERCASE(*b);
125 static GCC_NORETURN void
128 restore_tty_settings();
129 (void) fprintf(stderr, "\n");
131 ExitProgram(EXIT_FAILURE);
135 static GCC_NORETURN void
136 err(const char *fmt, ...)
140 (void) fprintf(stderr, "%s: ", _nc_progname);
141 (void) vfprintf(stderr, fmt, ap);
147 static GCC_NORETURN void
148 failed(const char *msg)
151 size_t len = strlen(_nc_progname) + 2;
153 if ((int) len < (int) sizeof(temp) - 12) {
154 _nc_STRCPY(temp, _nc_progname, sizeof(temp));
155 _nc_STRCAT(temp, ": ", sizeof(temp));
157 _nc_STRCPY(temp, "tset: ", sizeof(temp));
159 _nc_STRNCAT(temp, msg, sizeof(temp), sizeof(temp) - strlen(temp) - 2);
165 /* Prompt the user for a terminal type. */
167 askuser(const char *dflt)
169 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");
183 (void) fprintf(stderr, "Terminal type? [%s] ", dflt);
185 (void) fprintf(stderr, "Terminal type? ");
186 (void) fflush(stderr);
188 if (fgets(answer, sizeof(answer), stdin) == 0) {
196 if ((p = strchr(answer, '\n')) != 0)
205 /**************************************************************************
207 * Mapping logic begins here
209 **************************************************************************/
211 /* Baud rate conditionals for mapping. */
220 struct map *next; /* Linked list of maps. */
221 const char *porttype; /* Port type, or "" for any. */
222 const char *type; /* Terminal type to select. */
223 int conditional; /* Baud rate conditionals bitmask. */
224 int speed; /* Baud rate to compare against. */
227 static MAP *cur, *maplist;
229 #define DATA(name,value) { { name }, value }
231 typedef struct speeds {
232 const char string[8];
236 #if defined(EXP_WIN32_DRIVER)
237 static const SPEEDS speeds[] =
242 static const SPEEDS speeds[] =
259 /* sgttyb may define up to this point */
261 DATA("19200", B19200),
264 DATA("38400", B38400),
267 DATA("19200", B19200),
270 DATA("38400", B38400),
273 DATA("19200", B19200),
280 DATA("38400", B38400),
287 DATA("57600", B57600),
290 DATA("76800", B57600),
293 DATA("115200", B115200),
296 DATA("153600", B153600),
299 DATA("230400", B230400),
302 DATA("307200", B307200),
305 DATA("460800", B460800),
308 DATA("500000", B500000),
311 DATA("576000", B576000),
314 DATA("921600", B921600),
317 DATA("1000000", B1000000),
320 DATA("1152000", B1152000),
323 DATA("1500000", B1500000),
326 DATA("2000000", B2000000),
329 DATA("2500000", B2500000),
332 DATA("3000000", B3000000),
335 DATA("3500000", B3500000),
338 DATA("4000000", B4000000),
345 tbaudrate(char *rate)
347 const SPEEDS *sp = 0;
350 /* The baudrate number can be preceded by a 'B', which is ignored. */
354 for (n = 0; n < SIZEOF(speeds); ++n) {
355 if (n > 0 && (speeds[n].speed <= speeds[n - 1].speed)) {
356 /* if the speeds are not increasing, likely a numeric overflow */
359 if (!CaselessCmp(rate, speeds[n].string)) {
365 err("unknown baud rate %s", rate);
371 * [port-type][test baudrate]:terminal-type
372 * The baud rate tests are: >, <, @, =, !
375 add_mapping(const char *port, char *arg)
383 mapp = typeMalloc(MAP, 1);
384 if (copy == 0 || mapp == 0)
392 cur = maplist = mapp;
398 mapp->porttype = arg;
399 mapp->conditional = 0;
401 arg = strpbrk(arg, "><@=!:");
403 if (arg == 0) { /* [?]term */
404 mapp->type = mapp->porttype;
409 if (arg == mapp->porttype) /* [><@=! baud]:term */
410 termp = mapp->porttype = 0;
414 for (;; ++arg) { /* Optional conditionals. */
417 if (mapp->conditional & GT)
419 mapp->conditional |= LT;
422 if (mapp->conditional & LT)
424 mapp->conditional |= GT;
427 case '=': /* Not documented. */
428 mapp->conditional |= EQ;
431 mapp->conditional |= NOT;
440 if (mapp->conditional)
443 } else { /* Optional baudrate. */
444 arg = strchr(p = arg, ':');
448 mapp->speed = tbaudrate(p);
453 /* Terminate porttype, if specified. */
457 /* If a NOT conditional, reverse the test. */
458 if (mapp->conditional & NOT)
459 mapp->conditional = ~mapp->conditional & (EQ | GT | LT);
461 /* If user specified a port with an option flag, set it. */
464 if (mapp->porttype) {
466 err("illegal -m option format: %s", copy);
468 mapp->porttype = port;
472 (void) printf("port: %s\n", mapp->porttype ? mapp->porttype : "ANY");
473 (void) printf("type: %s\n", mapp->type);
474 (void) printf("conditional: ");
476 if (mapp->conditional & GT) {
480 if (mapp->conditional & EQ) {
481 (void) printf("%sEQ", p);
484 if (mapp->conditional & LT)
485 (void) printf("%sLT", p);
486 (void) printf("\nspeed: %d\n", mapp->speed);
491 * Return the type of terminal to use for a port of type 'type', as specified
492 * by the first applicable mapping in 'map'. If no mappings apply, return
496 mapped(const char *type)
501 for (mapp = maplist; mapp; mapp = mapp->next)
502 if (mapp->porttype == 0 || !strcmp(mapp->porttype, type)) {
503 switch (mapp->conditional) {
504 case 0: /* No test specified. */
508 match = ((int) ospeed == mapp->speed);
511 match = ((int) ospeed >= mapp->speed);
514 match = ((int) ospeed > mapp->speed);
517 match = ((int) ospeed <= mapp->speed);
520 match = ((int) ospeed < mapp->speed);
528 /* No match found; return given type. */
532 /**************************************************************************
536 **************************************************************************/
539 * Figure out what kind of terminal we're dealing with, and then read in
543 get_termcap_entry(int fd, char *userarg)
562 /* Try the environment. */
563 if ((ttype = getenv("TERM")) != 0)
566 if ((ttypath = ttyname(fd)) != 0) {
567 p = _nc_basename(ttypath);
570 * We have the 4.3BSD library call getttynam(3); that means
571 * there's an /etc/ttys to look up device-to-type mappings in.
572 * Try ttyname(3); check for dialup or other mapping.
574 if ((t = getttynam(p))) {
579 if ((fp = fopen("/etc/ttytype", "r")) != 0
580 || (fp = fopen("/etc/ttys", "r")) != 0) {
584 while (fgets(buffer, sizeof(buffer) - 1, fp) != 0) {
585 for (s = buffer, t = d = 0; *s; s++) {
586 if (isspace(UChar(*s)))
590 else if (d == 0 && s != buffer && s[-1] == '\0')
593 if (t != 0 && d != 0 && !strcmp(d, p)) {
601 #endif /* HAVE_GETTTYNAM */
604 /* If still undefined, use "unknown". */
607 map:ttype = mapped(ttype);
610 * If not a path, remove TERMCAP from the environment so we get a
611 * real entry from /etc/termcap. This prevents us from being fooled
612 * by out of date stuff in the environment.
615 if ((p = getenv("TERMCAP")) != 0 && !_nc_is_abs_path(p)) {
616 /* 'unsetenv("TERMCAP")' is not portable.
617 * The 'environ' array is better.
620 for (n = 0; environ[n] != 0; n++) {
621 if (!strncmp("TERMCAP=", environ[n], (size_t) 8)) {
622 while ((environ[n] = environ[n + 1]) != 0) {
631 * ttype now contains a pointer to the type of the terminal.
632 * If the first character is '?', ask the user.
634 if (ttype[0] == '?') {
635 if (ttype[1] != '\0')
636 ttype = askuser(ttype + 1);
640 /* Find the terminfo entry. If it doesn't exist, ask the user. */
641 while (setupterm((NCURSES_CONST char *) ttype, fd, &errret)
644 (void) fprintf(stderr, "%s: unknown terminal type %s\n",
645 _nc_progname, ttype);
648 (void) fprintf(stderr,
649 "%s: can't initialize terminal type %s (error %d)\n",
650 _nc_progname, ttype, errret);
653 ttype = askuser(ttype);
656 tgetflag("am"); /* force lib_termcap.o to be linked for 'ospeed' */
661 /**************************************************************************
665 **************************************************************************/
668 * Convert the obsolete argument forms into something that getopt can handle.
669 * This means that -e, -i and -k get default arguments supplied for them.
672 obsolete(char **argv)
674 for (; *argv; ++argv) {
675 char *parm = argv[0];
677 if (parm[0] == '-' && parm[1] == '\0') {
678 argv[0] = strdup("-q");
683 || (argv[1] && argv[1][0] != '-')
684 || (parm[1] != 'e' && parm[1] != 'i' && parm[1] != 'k')
685 || (parm[2] != '\0'))
687 switch (argv[0][1]) {
689 argv[0] = strdup("-e^H");
692 argv[0] = strdup("-i^C");
695 argv[0] = strdup("-k^U");
702 print_shell_commands(const char *ttype)
709 * Figure out what shell we're using. A hack, we look for an
710 * environmental variable SHELL ending in "csh".
712 if ((var = getenv("SHELL")) != 0
713 && ((len = (int) strlen(leaf = _nc_basename(var))) >= 3)
714 && !strcmp(leaf + len - 3, "csh"))
715 p = "set noglob;\nsetenv TERM %s;\nunset noglob;\n";
718 (void) printf(p, ttype);
724 #define SKIP(s) /* nothing */
725 #define KEEP(s) s "\n"
726 static const char msg[] =
730 SKIP(" -a arpanet (obsolete)")
731 KEEP(" -c set control characters")
732 SKIP(" -d dialup (obsolete)")
733 KEEP(" -e ch erase character")
734 KEEP(" -I no initialization strings")
735 KEEP(" -i ch interrupt character")
736 KEEP(" -k ch kill character")
737 KEEP(" -m mapping map identifier to type")
738 SKIP(" -p plugboard (obsolete)")
739 KEEP(" -Q do not output control key settings")
740 KEEP(" -q display term only, do no changes")
741 KEEP(" -r display term on stderr")
742 SKIP(" -S (obsolete)")
743 KEEP(" -s output TERM set command")
744 KEEP(" -V print curses-version")
745 KEEP(" -w set window-size")
747 KEEP("If neither -c/-w are given, both are assumed.")
751 (void) fprintf(stderr, "Usage: %s [options] [terminal]\n", _nc_progname);
753 ExitProgram(EXIT_FAILURE);
760 return (char) ((optarg[0] == '^' && optarg[1] != '\0')
761 ? ((optarg[1] == '?') ? '\177' : CTRL(optarg[1]))
766 main(int argc, char **argv)
768 int ch, noinit, noset, quiet, Sflag, sflag, showterm;
770 int terasechar = -1; /* new erase character */
771 int intrchar = -1; /* new interrupt character */
772 int tkillchar = -1; /* new kill character */
774 bool opt_c = FALSE; /* set control-chars */
775 bool opt_w = FALSE; /* set window-size */
779 noinit = noset = quiet = Sflag = sflag = showterm = 0;
780 while ((ch = getopt(argc, argv, "a:cd:e:Ii:k:m:p:qQrSsVw")) != -1) {
782 case 'c': /* set control-chars */
785 case 'a': /* OBSOLETE: map identifier to type */
786 add_mapping("arpanet", optarg);
788 case 'd': /* OBSOLETE: map identifier to type */
789 add_mapping("dialup", optarg);
791 case 'e': /* erase character */
792 terasechar = arg_to_char();
794 case 'I': /* no initialization strings */
797 case 'i': /* interrupt character */
798 intrchar = arg_to_char();
800 case 'k': /* kill character */
801 tkillchar = arg_to_char();
803 case 'm': /* map identifier to type */
804 add_mapping(0, optarg);
806 case 'p': /* OBSOLETE: map identifier to type */
807 add_mapping("plugboard", optarg);
809 case 'Q': /* don't output control key settings */
812 case 'q': /* display term only */
815 case 'r': /* display term on stderr */
818 case 'S': /* OBSOLETE: output TERM & TERMCAP */
821 case 's': /* output TERM set command */
824 case 'V': /* print curses-version */
825 puts(curses_version());
826 ExitProgram(EXIT_SUCCESS);
827 case 'w': /* set window-size */
836 _nc_progname = _nc_rootname(*argv);
843 if (!opt_c && !opt_w)
844 opt_c = opt_w = TRUE;
846 my_fd = save_tty_settings(&mode, TRUE);
849 ospeed = (NCURSES_OSPEED) cfgetospeed(&mode);
850 #elif defined(EXP_WIN32_DRIVER)
853 ospeed = (NCURSES_OSPEED) mode.sg_ospeed;
856 if (same_program(_nc_progname, PROG_RESET)) {
857 reset_start(stderr, TRUE, FALSE);
858 reset_tty_settings(my_fd, &mode);
860 reset_start(stderr, FALSE, TRUE);
863 ttype = get_termcap_entry(my_fd, *argv);
868 set_window_size(my_fd, &lines, &columns);
872 set_control_chars(&mode, terasechar, intrchar, tkillchar);
873 set_conversions(&mode);
876 if (send_init_strings(my_fd, &oldmode)) {
877 (void) putc('\r', stderr);
878 (void) fflush(stderr);
879 (void) napms(1000); /* Settle the terminal. */
883 update_tty_settings(&oldmode, &mode);
888 (void) printf("%s\n", ttype);
891 (void) fprintf(stderr, "Terminal type is %s.\n", ttype);
893 * If erase, kill and interrupt characters could have been
894 * modified and not -Q, display the changes.
897 print_tty_chars(&oldmode, &mode);
902 err("The -S option is not supported under terminfo.");
905 print_shell_commands(ttype);
908 ExitProgram(EXIT_SUCCESS);