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.107 2016/08/06 20:54:22 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))
110 CaselessCmp(const char *a, const char *b)
111 { /* strcasecmp isn't portable */
113 int cmp = LOWERCASE(*a) - LOWERCASE(*b);
118 return LOWERCASE(*a) - LOWERCASE(*b);
124 restore_tty_settings();
125 (void) fprintf(stderr, "\n");
127 ExitProgram(EXIT_FAILURE);
132 err(const char *fmt,...)
136 (void) fprintf(stderr, "%s: ", _nc_progname);
137 (void) vfprintf(stderr, fmt, ap);
144 failed(const char *msg)
147 size_t len = strlen(_nc_progname) + 2;
149 if ((int) len < (int) sizeof(temp) - 12) {
150 _nc_STRCPY(temp, _nc_progname, sizeof(temp));
151 _nc_STRCAT(temp, ": ", sizeof(temp));
153 _nc_STRCPY(temp, "tset: ", sizeof(temp));
155 perror(strncat(temp, msg, sizeof(temp) - strlen(temp) - 2));
160 /* Prompt the user for a terminal type. */
162 askuser(const char *dflt)
164 static char answer[256];
167 /* We can get recalled; if so, don't continue uselessly. */
169 if (feof(stdin) || ferror(stdin)) {
170 (void) fprintf(stderr, "\n");
176 (void) fprintf(stderr, "Terminal type? [%s] ", dflt);
178 (void) fprintf(stderr, "Terminal type? ");
179 (void) fflush(stderr);
181 if (fgets(answer, sizeof(answer), stdin) == 0) {
189 if ((p = strchr(answer, '\n')) != 0)
198 /**************************************************************************
200 * Mapping logic begins here
202 **************************************************************************/
204 /* Baud rate conditionals for mapping. */
213 struct map *next; /* Linked list of maps. */
214 const char *porttype; /* Port type, or "" for any. */
215 const char *type; /* Terminal type to select. */
216 int conditional; /* Baud rate conditionals bitmask. */
217 int speed; /* Baud rate to compare against. */
220 static MAP *cur, *maplist;
222 #define DATA(name,value) { { name }, value }
224 typedef struct speeds {
225 const char string[7];
229 static const SPEEDS speeds[] =
246 /* sgttyb may define up to this point */
248 DATA("19200", B19200),
251 DATA("38400", B38400),
254 DATA("19200", B19200),
257 DATA("38400", B38400),
260 DATA("19200", B19200),
267 DATA("38400", B38400),
274 DATA("57600", B57600),
277 DATA("115200", B115200),
280 DATA("230400", B230400),
283 DATA("460800", B460800),
289 tbaudrate(char *rate)
291 const SPEEDS *sp = 0;
294 /* The baudrate number can be preceded by a 'B', which is ignored. */
298 for (n = 0; n < SIZEOF(speeds); ++n) {
299 if (!CaselessCmp(rate, speeds[n].string)) {
305 err("unknown baud rate %s", rate);
311 * [port-type][test baudrate]:terminal-type
312 * The baud rate tests are: >, <, @, =, !
315 add_mapping(const char *port, char *arg)
323 mapp = typeMalloc(MAP, 1);
324 if (copy == 0 || mapp == 0)
332 cur = maplist = mapp;
338 mapp->porttype = arg;
339 mapp->conditional = 0;
341 arg = strpbrk(arg, "><@=!:");
343 if (arg == 0) { /* [?]term */
344 mapp->type = mapp->porttype;
349 if (arg == mapp->porttype) /* [><@=! baud]:term */
350 termp = mapp->porttype = 0;
354 for (;; ++arg) { /* Optional conditionals. */
357 if (mapp->conditional & GT)
359 mapp->conditional |= LT;
362 if (mapp->conditional & LT)
364 mapp->conditional |= GT;
367 case '=': /* Not documented. */
368 mapp->conditional |= EQ;
371 mapp->conditional |= NOT;
380 if (mapp->conditional)
383 } else { /* Optional baudrate. */
384 arg = strchr(p = arg, ':');
388 mapp->speed = tbaudrate(p);
393 /* Terminate porttype, if specified. */
397 /* If a NOT conditional, reverse the test. */
398 if (mapp->conditional & NOT)
399 mapp->conditional = ~mapp->conditional & (EQ | GT | LT);
401 /* If user specified a port with an option flag, set it. */
404 if (mapp->porttype) {
406 err("illegal -m option format: %s", copy);
408 mapp->porttype = port;
412 (void) printf("port: %s\n", mapp->porttype ? mapp->porttype : "ANY");
413 (void) printf("type: %s\n", mapp->type);
414 (void) printf("conditional: ");
416 if (mapp->conditional & GT) {
420 if (mapp->conditional & EQ) {
421 (void) printf("%sEQ", p);
424 if (mapp->conditional & LT)
425 (void) printf("%sLT", p);
426 (void) printf("\nspeed: %d\n", mapp->speed);
431 * Return the type of terminal to use for a port of type 'type', as specified
432 * by the first applicable mapping in 'map'. If no mappings apply, return
436 mapped(const char *type)
441 for (mapp = maplist; mapp; mapp = mapp->next)
442 if (mapp->porttype == 0 || !strcmp(mapp->porttype, type)) {
443 switch (mapp->conditional) {
444 case 0: /* No test specified. */
448 match = ((int) ospeed == mapp->speed);
451 match = ((int) ospeed >= mapp->speed);
454 match = ((int) ospeed > mapp->speed);
457 match = ((int) ospeed <= mapp->speed);
460 match = ((int) ospeed < mapp->speed);
468 /* No match found; return given type. */
472 /**************************************************************************
476 **************************************************************************/
479 * Figure out what kind of terminal we're dealing with, and then read in
483 get_termcap_entry(int fd, char *userarg)
500 /* Try the environment. */
501 if ((ttype = getenv("TERM")) != 0)
504 if ((ttypath = ttyname(fd)) != 0) {
505 p = _nc_basename(ttypath);
508 * We have the 4.3BSD library call getttynam(3); that means
509 * there's an /etc/ttys to look up device-to-type mappings in.
510 * Try ttyname(3); check for dialup or other mapping.
512 if ((t = getttynam(p))) {
517 if ((fp = fopen("/etc/ttytype", "r")) != 0
518 || (fp = fopen("/etc/ttys", "r")) != 0) {
522 while (fgets(buffer, sizeof(buffer) - 1, fp) != 0) {
523 for (s = buffer, t = d = 0; *s; s++) {
524 if (isspace(UChar(*s)))
528 else if (d == 0 && s != buffer && s[-1] == '\0')
531 if (t != 0 && d != 0 && !strcmp(d, p)) {
539 #endif /* HAVE_GETTTYNAM */
542 /* If still undefined, use "unknown". */
545 map:ttype = mapped(ttype);
548 * If not a path, remove TERMCAP from the environment so we get a
549 * real entry from /etc/termcap. This prevents us from being fooled
550 * by out of date stuff in the environment.
553 if ((p = getenv("TERMCAP")) != 0 && !_nc_is_abs_path(p)) {
554 /* 'unsetenv("TERMCAP")' is not portable.
555 * The 'environ' array is better.
558 for (n = 0; environ[n] != 0; n++) {
559 if (!strncmp("TERMCAP=", environ[n], (size_t) 8)) {
560 while ((environ[n] = environ[n + 1]) != 0) {
569 * ttype now contains a pointer to the type of the terminal.
570 * If the first character is '?', ask the user.
572 if (ttype[0] == '?') {
573 if (ttype[1] != '\0')
574 ttype = askuser(ttype + 1);
578 /* Find the terminfo entry. If it doesn't exist, ask the user. */
579 while (setupterm((NCURSES_CONST char *) ttype, STDOUT_FILENO, &errret)
582 (void) fprintf(stderr, "%s: unknown terminal type %s\n",
583 _nc_progname, ttype);
586 (void) fprintf(stderr,
587 "%s: can't initialize terminal type %s (error %d)\n",
588 _nc_progname, ttype, errret);
591 ttype = askuser(ttype);
594 tgetflag("am"); /* force lib_termcap.o to be linked for 'ospeed' */
599 /**************************************************************************
603 **************************************************************************/
606 * Convert the obsolete argument forms into something that getopt can handle.
607 * This means that -e, -i and -k get default arguments supplied for them.
610 obsolete(char **argv)
612 for (; *argv; ++argv) {
613 char *parm = argv[0];
615 if (parm[0] == '-' && parm[1] == '\0') {
616 argv[0] = strdup("-q");
621 || (argv[1] && argv[1][0] != '-')
622 || (parm[1] != 'e' && parm[1] != 'i' && parm[1] != 'k')
623 || (parm[2] != '\0'))
625 switch (argv[0][1]) {
627 argv[0] = strdup("-e^H");
630 argv[0] = strdup("-i^C");
633 argv[0] = strdup("-k^U");
640 print_shell_commands(const char *ttype)
647 * Figure out what shell we're using. A hack, we look for an
648 * environmental variable SHELL ending in "csh".
650 if ((var = getenv("SHELL")) != 0
651 && ((len = (int) strlen(leaf = _nc_basename(var))) >= 3)
652 && !strcmp(leaf + len - 3, "csh"))
653 p = "set noglob;\nsetenv TERM %s;\nunset noglob;\n";
656 (void) printf(p, ttype);
662 #define DATA(s) s "\n"
663 static const char msg[] =
667 DATA(" -c set control characters")
668 DATA(" -e ch erase character")
669 DATA(" -I no initialization strings")
670 DATA(" -i ch interrupt character")
671 DATA(" -k ch kill character")
672 DATA(" -m mapping map identifier to type")
673 DATA(" -Q do not output control key settings")
674 DATA(" -r display term on stderr")
675 DATA(" -s output TERM set command")
676 DATA(" -V print curses-version")
677 DATA(" -w set window-size")
680 (void) fprintf(stderr, "Usage: %s [options] [terminal]\n", _nc_progname);
689 return (char) ((optarg[0] == '^' && optarg[1] != '\0')
690 ? ((optarg[1] == '?') ? '\177' : CTRL(optarg[1]))
695 main(int argc, char **argv)
697 int ch, noinit, noset, quiet, Sflag, sflag, showterm;
699 int terasechar = -1; /* new erase character */
700 int intrchar = -1; /* new interrupt character */
701 int tkillchar = -1; /* new kill character */
703 bool opt_c = FALSE; /* set control-chars */
704 bool opt_w = FALSE; /* set window-size */
707 my_fd = STDERR_FILENO;
709 noinit = noset = quiet = Sflag = sflag = showterm = 0;
710 while ((ch = getopt(argc, argv, "a:cd:e:Ii:k:m:p:qQSrsVw")) != -1) {
712 case 'c': /* set control-chars */
715 case 'a': /* OBSOLETE: map identifier to type */
716 add_mapping("arpanet", optarg);
718 case 'd': /* OBSOLETE: map identifier to type */
719 add_mapping("dialup", optarg);
721 case 'e': /* erase character */
722 terasechar = arg_to_char();
724 case 'I': /* no initialization strings */
727 case 'i': /* interrupt character */
728 intrchar = arg_to_char();
730 case 'k': /* kill character */
731 tkillchar = arg_to_char();
733 case 'm': /* map identifier to type */
734 add_mapping(0, optarg);
736 case 'p': /* OBSOLETE: map identifier to type */
737 add_mapping("plugboard", optarg);
739 case 'Q': /* don't output control key settings */
742 case 'q': /* display term only */
745 case 'r': /* display term on stderr */
748 case 'S': /* OBSOLETE: output TERM & TERMCAP */
751 case 's': /* output TERM set command */
754 case 'V': /* print curses-version */
755 puts(curses_version());
756 ExitProgram(EXIT_SUCCESS);
757 case 'w': /* set window-size */
766 _nc_progname = _nc_rootname(*argv);
773 if (!opt_c && !opt_w)
774 opt_c = opt_w = TRUE;
776 my_fd = save_tty_settings(&mode);
779 ospeed = (NCURSES_OSPEED) cfgetospeed(&mode);
781 ospeed = (NCURSES_OSPEED) mode.sg_ospeed;
784 if (same_program(_nc_progname, PROG_RESET)) {
785 reset_start(stderr, TRUE, FALSE);
786 reset_tty_settings(&mode);
788 reset_start(stderr, FALSE, FALSE);
791 ttype = get_termcap_entry(my_fd, *argv);
796 set_window_size(my_fd, lines, columns);
800 set_control_chars(&mode, terasechar, intrchar, tkillchar);
801 set_conversions(&mode);
804 if (send_init_strings(&oldmode)) {
805 (void) putc('\r', stderr);
806 (void) fflush(stderr);
807 (void) napms(1000); /* Settle the terminal. */
811 update_tty_settings(&oldmode, &mode);
816 (void) printf("%s\n", ttype);
819 (void) fprintf(stderr, "Terminal type is %s.\n", ttype);
821 * If erase, kill and interrupt characters could have been
822 * modified and not -Q, display the changes.
825 print_tty_chars(&oldmode, &mode);
830 err("The -S option is not supported under terminfo.");
833 print_shell_commands(ttype);
836 ExitProgram(EXIT_SUCCESS);