1 /****************************************************************************
2 * Copyright 2020 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.123 2020/08/29 23:32:18 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 void exit_error(void) GCC_NORETURN;
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);
128 restore_tty_settings();
129 (void) fprintf(stderr, "\n");
131 ExitProgram(EXIT_FAILURE);
136 err(const char *fmt, ...)
140 (void) fprintf(stderr, "%s: ", _nc_progname);
141 (void) vfprintf(stderr, fmt, ap);
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];
172 /* We can get recalled; if so, don't continue uselessly. */
174 if (feof(stdin) || ferror(stdin)) {
175 (void) fprintf(stderr, "\n");
181 (void) fprintf(stderr, "Terminal type? [%s] ", dflt);
183 (void) fprintf(stderr, "Terminal type? ");
184 (void) fflush(stderr);
186 if (fgets(answer, sizeof(answer), stdin) == 0) {
194 if ((p = strchr(answer, '\n')) != 0)
203 /**************************************************************************
205 * Mapping logic begins here
207 **************************************************************************/
209 /* Baud rate conditionals for mapping. */
218 struct map *next; /* Linked list of maps. */
219 const char *porttype; /* Port type, or "" for any. */
220 const char *type; /* Terminal type to select. */
221 int conditional; /* Baud rate conditionals bitmask. */
222 int speed; /* Baud rate to compare against. */
225 static MAP *cur, *maplist;
227 #define DATA(name,value) { { name }, value }
229 typedef struct speeds {
230 const char string[8];
234 #if (defined(_WIN32) || defined(_WIN64))
235 static const SPEEDS speeds[] = { { "0", 0 } };
237 static const SPEEDS speeds[] =
254 /* sgttyb may define up to this point */
256 DATA("19200", B19200),
259 DATA("38400", B38400),
262 DATA("19200", B19200),
265 DATA("38400", B38400),
268 DATA("19200", B19200),
275 DATA("38400", B38400),
282 DATA("57600", B57600),
285 DATA("76800", B57600),
288 DATA("115200", B115200),
291 DATA("153600", B153600),
294 DATA("230400", B230400),
297 DATA("307200", B307200),
300 DATA("460800", B460800),
303 DATA("500000", B500000),
306 DATA("576000", B576000),
309 DATA("921600", B921600),
312 DATA("1000000", B1000000),
315 DATA("1152000", B1152000),
318 DATA("1500000", B1500000),
321 DATA("2000000", B2000000),
324 DATA("2500000", B2500000),
327 DATA("3000000", B3000000),
330 DATA("3500000", B3500000),
333 DATA("4000000", B4000000),
340 tbaudrate(char *rate)
342 const SPEEDS *sp = 0;
345 /* The baudrate number can be preceded by a 'B', which is ignored. */
349 for (n = 0; n < SIZEOF(speeds); ++n) {
350 if (n > 0 && (speeds[n].speed <= speeds[n - 1].speed)) {
351 /* if the speeds are not increasing, likely a numeric overflow */
354 if (!CaselessCmp(rate, speeds[n].string)) {
360 err("unknown baud rate %s", rate);
366 * [port-type][test baudrate]:terminal-type
367 * The baud rate tests are: >, <, @, =, !
370 add_mapping(const char *port, char *arg)
378 mapp = typeMalloc(MAP, 1);
379 if (copy == 0 || mapp == 0)
387 cur = maplist = mapp;
393 mapp->porttype = arg;
394 mapp->conditional = 0;
396 arg = strpbrk(arg, "><@=!:");
398 if (arg == 0) { /* [?]term */
399 mapp->type = mapp->porttype;
404 if (arg == mapp->porttype) /* [><@=! baud]:term */
405 termp = mapp->porttype = 0;
409 for (;; ++arg) { /* Optional conditionals. */
412 if (mapp->conditional & GT)
414 mapp->conditional |= LT;
417 if (mapp->conditional & LT)
419 mapp->conditional |= GT;
422 case '=': /* Not documented. */
423 mapp->conditional |= EQ;
426 mapp->conditional |= NOT;
435 if (mapp->conditional)
438 } else { /* Optional baudrate. */
439 arg = strchr(p = arg, ':');
443 mapp->speed = tbaudrate(p);
448 /* Terminate porttype, if specified. */
452 /* If a NOT conditional, reverse the test. */
453 if (mapp->conditional & NOT)
454 mapp->conditional = ~mapp->conditional & (EQ | GT | LT);
456 /* If user specified a port with an option flag, set it. */
459 if (mapp->porttype) {
461 err("illegal -m option format: %s", copy);
463 mapp->porttype = port;
467 (void) printf("port: %s\n", mapp->porttype ? mapp->porttype : "ANY");
468 (void) printf("type: %s\n", mapp->type);
469 (void) printf("conditional: ");
471 if (mapp->conditional & GT) {
475 if (mapp->conditional & EQ) {
476 (void) printf("%sEQ", p);
479 if (mapp->conditional & LT)
480 (void) printf("%sLT", p);
481 (void) printf("\nspeed: %d\n", mapp->speed);
486 * Return the type of terminal to use for a port of type 'type', as specified
487 * by the first applicable mapping in 'map'. If no mappings apply, return
491 mapped(const char *type)
496 for (mapp = maplist; mapp; mapp = mapp->next)
497 if (mapp->porttype == 0 || !strcmp(mapp->porttype, type)) {
498 switch (mapp->conditional) {
499 case 0: /* No test specified. */
503 match = ((int) ospeed == mapp->speed);
506 match = ((int) ospeed >= mapp->speed);
509 match = ((int) ospeed > mapp->speed);
512 match = ((int) ospeed <= mapp->speed);
515 match = ((int) ospeed < mapp->speed);
523 /* No match found; return given type. */
527 /**************************************************************************
531 **************************************************************************/
534 * Figure out what kind of terminal we're dealing with, and then read in
538 get_termcap_entry(int fd, char *userarg)
557 /* Try the environment. */
558 if ((ttype = getenv("TERM")) != 0)
561 if ((ttypath = ttyname(fd)) != 0) {
562 p = _nc_basename(ttypath);
565 * We have the 4.3BSD library call getttynam(3); that means
566 * there's an /etc/ttys to look up device-to-type mappings in.
567 * Try ttyname(3); check for dialup or other mapping.
569 if ((t = getttynam(p))) {
574 if ((fp = fopen("/etc/ttytype", "r")) != 0
575 || (fp = fopen("/etc/ttys", "r")) != 0) {
579 while (fgets(buffer, sizeof(buffer) - 1, fp) != 0) {
580 for (s = buffer, t = d = 0; *s; s++) {
581 if (isspace(UChar(*s)))
585 else if (d == 0 && s != buffer && s[-1] == '\0')
588 if (t != 0 && d != 0 && !strcmp(d, p)) {
596 #endif /* HAVE_GETTTYNAM */
599 /* If still undefined, use "unknown". */
602 map:ttype = mapped(ttype);
605 * If not a path, remove TERMCAP from the environment so we get a
606 * real entry from /etc/termcap. This prevents us from being fooled
607 * by out of date stuff in the environment.
610 if ((p = getenv("TERMCAP")) != 0 && !_nc_is_abs_path(p)) {
611 /* 'unsetenv("TERMCAP")' is not portable.
612 * The 'environ' array is better.
615 for (n = 0; environ[n] != 0; n++) {
616 if (!strncmp("TERMCAP=", environ[n], (size_t) 8)) {
617 while ((environ[n] = environ[n + 1]) != 0) {
626 * ttype now contains a pointer to the type of the terminal.
627 * If the first character is '?', ask the user.
629 if (ttype[0] == '?') {
630 if (ttype[1] != '\0')
631 ttype = askuser(ttype + 1);
635 /* Find the terminfo entry. If it doesn't exist, ask the user. */
636 while (setupterm((NCURSES_CONST char *) ttype, fd, &errret)
639 (void) fprintf(stderr, "%s: unknown terminal type %s\n",
640 _nc_progname, ttype);
643 (void) fprintf(stderr,
644 "%s: can't initialize terminal type %s (error %d)\n",
645 _nc_progname, ttype, errret);
648 ttype = askuser(ttype);
651 tgetflag("am"); /* force lib_termcap.o to be linked for 'ospeed' */
656 /**************************************************************************
660 **************************************************************************/
663 * Convert the obsolete argument forms into something that getopt can handle.
664 * This means that -e, -i and -k get default arguments supplied for them.
667 obsolete(char **argv)
669 for (; *argv; ++argv) {
670 char *parm = argv[0];
672 if (parm[0] == '-' && parm[1] == '\0') {
673 argv[0] = strdup("-q");
678 || (argv[1] && argv[1][0] != '-')
679 || (parm[1] != 'e' && parm[1] != 'i' && parm[1] != 'k')
680 || (parm[2] != '\0'))
682 switch (argv[0][1]) {
684 argv[0] = strdup("-e^H");
687 argv[0] = strdup("-i^C");
690 argv[0] = strdup("-k^U");
697 print_shell_commands(const char *ttype)
704 * Figure out what shell we're using. A hack, we look for an
705 * environmental variable SHELL ending in "csh".
707 if ((var = getenv("SHELL")) != 0
708 && ((len = (int) strlen(leaf = _nc_basename(var))) >= 3)
709 && !strcmp(leaf + len - 3, "csh"))
710 p = "set noglob;\nsetenv TERM %s;\nunset noglob;\n";
713 (void) printf(p, ttype);
719 #define SKIP(s) /* nothing */
720 #define KEEP(s) s "\n"
721 static const char msg[] =
725 SKIP(" -a arpanet (obsolete)")
726 KEEP(" -c set control characters")
727 SKIP(" -d dialup (obsolete)")
728 KEEP(" -e ch erase character")
729 KEEP(" -I no initialization strings")
730 KEEP(" -i ch interrupt character")
731 KEEP(" -k ch kill character")
732 KEEP(" -m mapping map identifier to type")
733 SKIP(" -p plugboard (obsolete)")
734 KEEP(" -Q do not output control key settings")
735 KEEP(" -q display term only, do no changes")
736 KEEP(" -r display term on stderr")
737 SKIP(" -S (obsolete)")
738 KEEP(" -s output TERM set command")
739 KEEP(" -V print curses-version")
740 KEEP(" -w set window-size")
742 KEEP("If neither -c/-w are given, both are assumed.")
746 (void) fprintf(stderr, "Usage: %s [options] [terminal]\n", _nc_progname);
748 ExitProgram(EXIT_FAILURE);
755 return (char) ((optarg[0] == '^' && optarg[1] != '\0')
756 ? ((optarg[1] == '?') ? '\177' : CTRL(optarg[1]))
761 main(int argc, char **argv)
763 int ch, noinit, noset, quiet, Sflag, sflag, showterm;
765 int terasechar = -1; /* new erase character */
766 int intrchar = -1; /* new interrupt character */
767 int tkillchar = -1; /* new kill character */
769 bool opt_c = FALSE; /* set control-chars */
770 bool opt_w = FALSE; /* set window-size */
773 my_fd = STDERR_FILENO;
775 noinit = noset = quiet = Sflag = sflag = showterm = 0;
776 while ((ch = getopt(argc, argv, "a:cd:e:Ii:k:m:p:qQrSsVw")) != -1) {
778 case 'c': /* set control-chars */
781 case 'a': /* OBSOLETE: map identifier to type */
782 add_mapping("arpanet", optarg);
784 case 'd': /* OBSOLETE: map identifier to type */
785 add_mapping("dialup", optarg);
787 case 'e': /* erase character */
788 terasechar = arg_to_char();
790 case 'I': /* no initialization strings */
793 case 'i': /* interrupt character */
794 intrchar = arg_to_char();
796 case 'k': /* kill character */
797 tkillchar = arg_to_char();
799 case 'm': /* map identifier to type */
800 add_mapping(0, optarg);
802 case 'p': /* OBSOLETE: map identifier to type */
803 add_mapping("plugboard", optarg);
805 case 'Q': /* don't output control key settings */
808 case 'q': /* display term only */
811 case 'r': /* display term on stderr */
814 case 'S': /* OBSOLETE: output TERM & TERMCAP */
817 case 's': /* output TERM set command */
820 case 'V': /* print curses-version */
821 puts(curses_version());
822 ExitProgram(EXIT_SUCCESS);
823 case 'w': /* set window-size */
832 _nc_progname = _nc_rootname(*argv);
839 if (!opt_c && !opt_w)
840 opt_c = opt_w = TRUE;
842 my_fd = save_tty_settings(&mode, TRUE);
845 ospeed = (NCURSES_OSPEED) cfgetospeed(&mode);
846 #elif (defined(_WIN32) || defined(_WIN64))
849 ospeed = (NCURSES_OSPEED) mode.sg_ospeed;
852 if (same_program(_nc_progname, PROG_RESET)) {
853 reset_start(stderr, TRUE, FALSE);
854 reset_tty_settings(my_fd, &mode);
856 reset_start(stderr, FALSE, TRUE);
859 ttype = get_termcap_entry(my_fd, *argv);
864 set_window_size(my_fd, &lines, &columns);
868 set_control_chars(&mode, terasechar, intrchar, tkillchar);
869 set_conversions(&mode);
872 if (send_init_strings(my_fd, &oldmode)) {
873 (void) putc('\r', stderr);
874 (void) fflush(stderr);
875 (void) napms(1000); /* Settle the terminal. */
879 update_tty_settings(&oldmode, &mode);
884 (void) printf("%s\n", ttype);
887 (void) fprintf(stderr, "Terminal type is %s.\n", ttype);
889 * If erase, kill and interrupt characters could have been
890 * modified and not -Q, display the changes.
893 print_tty_chars(&oldmode, &mode);
898 err("The -S option is not supported under terminfo.");
901 print_shell_commands(ttype);
904 ExitProgram(EXIT_SUCCESS);