ncurses 6.2 - patch 20200829
[ncurses.git] / progs / tset.c
1 /****************************************************************************
2  * Copyright 2020 Thomas E. Dickey                                          *
3  * Copyright 1998-2016,2017 Free Software Foundation, Inc.                  *
4  *                                                                          *
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:                 *
12  *                                                                          *
13  * The above copyright notice and this permission notice shall be included  *
14  * in all copies or substantial portions of the Software.                   *
15  *                                                                          *
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.                               *
23  *                                                                          *
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       *
27  * authorization.                                                           *
28  ****************************************************************************/
29
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  ****************************************************************************/
35
36 /*
37  * Notes:
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
41  * change is the same.
42  *
43  * Comparing with current (2009) source, excluding this comment:
44  * a) 209 lines match identically to the 4.4BSD Lite sources, with 771 lines
45  *    changed/added.
46  * a) Ignoring whitespace, the current version still uses 516 lines from the
47  *    4.4BSD Lite sources, with 402 lines changed/added.
48  *
49  * Raymond's original comment on this follows...
50  */
51
52 /*
53  * tset.c - terminal initialization utility
54  *
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:
59  */
60 /*-
61  * Copyright (c) 1980, 1991, 1993
62  *      The Regents of the University of California.  All rights reserved.
63  *
64  * Redistribution and use in source and binary forms, with or without
65  * modification, are permitted provided that the following conditions
66  * are met:
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.
75  *
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
86  * SUCH DAMAGE.
87  */
88
89 #include <reset_cmd.h>
90 #include <termcap.h>
91 #include <transform.h>
92 #include <tty_settings.h>
93
94 #if HAVE_GETTTYNAM && HAVE_TTYENT_H
95 #include <ttyent.h>
96 #endif
97 #ifdef NeXT
98 char *ttyname(int fd);
99 #endif
100
101 MODULE_ID("$Id: tset.c,v 1.123 2020/08/29 23:32:18 tom Exp $")
102
103 #ifndef environ
104 extern char **environ;
105 #endif
106
107 const char *_nc_progname = "tset";
108
109 #define LOWERCASE(c) ((isalpha(UChar(c)) && isupper(UChar(c))) ? tolower(UChar(c)) : (c))
110
111 static void exit_error(void) GCC_NORETURN;
112
113 static int
114 CaselessCmp(const char *a, const char *b)
115 {                               /* strcasecmp isn't portable */
116     while (*a && *b) {
117         int cmp = LOWERCASE(*a) - LOWERCASE(*b);
118         if (cmp != 0)
119             break;
120         a++, b++;
121     }
122     return LOWERCASE(*a) - LOWERCASE(*b);
123 }
124
125 static void
126 exit_error(void)
127 {
128     restore_tty_settings();
129     (void) fprintf(stderr, "\n");
130     fflush(stderr);
131     ExitProgram(EXIT_FAILURE);
132     /* NOTREACHED */
133 }
134
135 static void
136 err(const char *fmt, ...)
137 {
138     va_list ap;
139     va_start(ap, fmt);
140     (void) fprintf(stderr, "%s: ", _nc_progname);
141     (void) vfprintf(stderr, fmt, ap);
142     va_end(ap);
143     exit_error();
144     /* NOTREACHED */
145 }
146
147 static void
148 failed(const char *msg)
149 {
150     char temp[BUFSIZ];
151     size_t len = strlen(_nc_progname) + 2;
152
153     if ((int) len < (int) sizeof(temp) - 12) {
154         _nc_STRCPY(temp, _nc_progname, sizeof(temp));
155         _nc_STRCAT(temp, ": ", sizeof(temp));
156     } else {
157         _nc_STRCPY(temp, "tset: ", sizeof(temp));
158     }
159     _nc_STRNCAT(temp, msg, sizeof(temp), sizeof(temp) - strlen(temp) - 2);
160     perror(temp);
161     exit_error();
162     /* NOTREACHED */
163 }
164
165 /* Prompt the user for a terminal type. */
166 static const char *
167 askuser(const char *dflt)
168 {
169     static char answer[256];
170     char *p;
171
172     /* We can get recalled; if so, don't continue uselessly. */
173     clearerr(stdin);
174     if (feof(stdin) || ferror(stdin)) {
175         (void) fprintf(stderr, "\n");
176         exit_error();
177         /* NOTREACHED */
178     }
179     for (;;) {
180         if (dflt)
181             (void) fprintf(stderr, "Terminal type? [%s] ", dflt);
182         else
183             (void) fprintf(stderr, "Terminal type? ");
184         (void) fflush(stderr);
185
186         if (fgets(answer, sizeof(answer), stdin) == 0) {
187             if (dflt == 0) {
188                 exit_error();
189                 /* NOTREACHED */
190             }
191             return (dflt);
192         }
193
194         if ((p = strchr(answer, '\n')) != 0)
195             *p = '\0';
196         if (answer[0])
197             return (answer);
198         if (dflt != 0)
199             return (dflt);
200     }
201 }
202
203 /**************************************************************************
204  *
205  * Mapping logic begins here
206  *
207  **************************************************************************/
208
209 /* Baud rate conditionals for mapping. */
210 #define GT              0x01
211 #define EQ              0x02
212 #define LT              0x04
213 #define NOT             0x08
214 #define GE              (GT | EQ)
215 #define LE              (LT | EQ)
216
217 typedef struct map {
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. */
223 } MAP;
224
225 static MAP *cur, *maplist;
226
227 #define DATA(name,value) { { name }, value }
228
229 typedef struct speeds {
230     const char string[8];
231     int speed;
232 } SPEEDS;
233
234 #if (defined(_WIN32) || defined(_WIN64))
235 static const SPEEDS speeds[] = { { "0", 0 } };
236 #else
237 static const SPEEDS speeds[] =
238 {
239     DATA("0", B0),
240     DATA("50", B50),
241     DATA("75", B75),
242     DATA("110", B110),
243     DATA("134", B134),
244     DATA("134.5", B134),
245     DATA("150", B150),
246     DATA("200", B200),
247     DATA("300", B300),
248     DATA("600", B600),
249     DATA("1200", B1200),
250     DATA("1800", B1800),
251     DATA("2400", B2400),
252     DATA("4800", B4800),
253     DATA("9600", B9600),
254     /* sgttyb may define up to this point */
255 #ifdef B19200
256     DATA("19200", B19200),
257 #endif
258 #ifdef B38400
259     DATA("38400", B38400),
260 #endif
261 #ifdef B19200
262     DATA("19200", B19200),
263 #endif
264 #ifdef B38400
265     DATA("38400", B38400),
266 #endif
267 #ifdef B19200
268     DATA("19200", B19200),
269 #else
270 #ifdef EXTA
271     DATA("19200", EXTA),
272 #endif
273 #endif
274 #ifdef B38400
275     DATA("38400", B38400),
276 #else
277 #ifdef EXTB
278     DATA("38400", EXTB),
279 #endif
280 #endif
281 #ifdef B57600
282     DATA("57600", B57600),
283 #endif
284 #ifdef B76800
285     DATA("76800", B57600),
286 #endif
287 #ifdef B115200
288     DATA("115200", B115200),
289 #endif
290 #ifdef B153600
291     DATA("153600", B153600),
292 #endif
293 #ifdef B230400
294     DATA("230400", B230400),
295 #endif
296 #ifdef B307200
297     DATA("307200", B307200),
298 #endif
299 #ifdef B460800
300     DATA("460800", B460800),
301 #endif
302 #ifdef B500000
303     DATA("500000", B500000),
304 #endif
305 #ifdef B576000
306     DATA("576000", B576000),
307 #endif
308 #ifdef B921600
309     DATA("921600", B921600),
310 #endif
311 #ifdef B1000000
312     DATA("1000000", B1000000),
313 #endif
314 #ifdef B1152000
315     DATA("1152000", B1152000),
316 #endif
317 #ifdef B1500000
318     DATA("1500000", B1500000),
319 #endif
320 #ifdef B2000000
321     DATA("2000000", B2000000),
322 #endif
323 #ifdef B2500000
324     DATA("2500000", B2500000),
325 #endif
326 #ifdef B3000000
327     DATA("3000000", B3000000),
328 #endif
329 #ifdef B3500000
330     DATA("3500000", B3500000),
331 #endif
332 #ifdef B4000000
333     DATA("4000000", B4000000),
334 #endif
335 };
336 #undef DATA
337 #endif
338
339 static int
340 tbaudrate(char *rate)
341 {
342     const SPEEDS *sp = 0;
343     size_t n;
344
345     /* The baudrate number can be preceded by a 'B', which is ignored. */
346     if (*rate == 'B')
347         ++rate;
348
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 */
352             break;
353         }
354         if (!CaselessCmp(rate, speeds[n].string)) {
355             sp = speeds + n;
356             break;
357         }
358     }
359     if (sp == 0)
360         err("unknown baud rate %s", rate);
361     return (sp->speed);
362 }
363
364 /*
365  * Syntax for -m:
366  * [port-type][test baudrate]:terminal-type
367  * The baud rate tests are: >, <, @, =, !
368  */
369 static void
370 add_mapping(const char *port, char *arg)
371 {
372     MAP *mapp;
373     char *copy, *p;
374     const char *termp;
375     char *base = 0;
376
377     copy = strdup(arg);
378     mapp = typeMalloc(MAP, 1);
379     if (copy == 0 || mapp == 0)
380         failed("malloc");
381
382     assert(copy != 0);
383     assert(mapp != 0);
384
385     mapp->next = 0;
386     if (maplist == 0)
387         cur = maplist = mapp;
388     else {
389         cur->next = mapp;
390         cur = mapp;
391     }
392
393     mapp->porttype = arg;
394     mapp->conditional = 0;
395
396     arg = strpbrk(arg, "><@=!:");
397
398     if (arg == 0) {             /* [?]term */
399         mapp->type = mapp->porttype;
400         mapp->porttype = 0;
401         goto done;
402     }
403
404     if (arg == mapp->porttype)  /* [><@=! baud]:term */
405         termp = mapp->porttype = 0;
406     else
407         termp = base = arg;
408
409     for (;; ++arg) {            /* Optional conditionals. */
410         switch (*arg) {
411         case '<':
412             if (mapp->conditional & GT)
413                 goto badmopt;
414             mapp->conditional |= LT;
415             break;
416         case '>':
417             if (mapp->conditional & LT)
418                 goto badmopt;
419             mapp->conditional |= GT;
420             break;
421         case '@':
422         case '=':               /* Not documented. */
423             mapp->conditional |= EQ;
424             break;
425         case '!':
426             mapp->conditional |= NOT;
427             break;
428         default:
429             goto next;
430         }
431     }
432
433   next:
434     if (*arg == ':') {
435         if (mapp->conditional)
436             goto badmopt;
437         ++arg;
438     } else {                    /* Optional baudrate. */
439         arg = strchr(p = arg, ':');
440         if (arg == 0)
441             goto badmopt;
442         *arg++ = '\0';
443         mapp->speed = tbaudrate(p);
444     }
445
446     mapp->type = arg;
447
448     /* Terminate porttype, if specified. */
449     if (termp != 0)
450         *base = '\0';
451
452     /* If a NOT conditional, reverse the test. */
453     if (mapp->conditional & NOT)
454         mapp->conditional = ~mapp->conditional & (EQ | GT | LT);
455
456     /* If user specified a port with an option flag, set it. */
457   done:
458     if (port) {
459         if (mapp->porttype) {
460           badmopt:
461             err("illegal -m option format: %s", copy);
462         }
463         mapp->porttype = port;
464     }
465     free(copy);
466 #ifdef MAPDEBUG
467     (void) printf("port: %s\n", mapp->porttype ? mapp->porttype : "ANY");
468     (void) printf("type: %s\n", mapp->type);
469     (void) printf("conditional: ");
470     p = "";
471     if (mapp->conditional & GT) {
472         (void) printf("GT");
473         p = "/";
474     }
475     if (mapp->conditional & EQ) {
476         (void) printf("%sEQ", p);
477         p = "/";
478     }
479     if (mapp->conditional & LT)
480         (void) printf("%sLT", p);
481     (void) printf("\nspeed: %d\n", mapp->speed);
482 #endif
483 }
484
485 /*
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
488  * 'type'.
489  */
490 static const char *
491 mapped(const char *type)
492 {
493     MAP *mapp;
494     int match;
495
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. */
500                 match = TRUE;
501                 break;
502             case EQ:
503                 match = ((int) ospeed == mapp->speed);
504                 break;
505             case GE:
506                 match = ((int) ospeed >= mapp->speed);
507                 break;
508             case GT:
509                 match = ((int) ospeed > mapp->speed);
510                 break;
511             case LE:
512                 match = ((int) ospeed <= mapp->speed);
513                 break;
514             case LT:
515                 match = ((int) ospeed < mapp->speed);
516                 break;
517             default:
518                 match = FALSE;
519             }
520             if (match)
521                 return (mapp->type);
522         }
523     /* No match found; return given type. */
524     return (type);
525 }
526
527 /**************************************************************************
528  *
529  * Entry fetching
530  *
531  **************************************************************************/
532
533 /*
534  * Figure out what kind of terminal we're dealing with, and then read in
535  * its termcap entry.
536  */
537 static const char *
538 get_termcap_entry(int fd, char *userarg)
539 {
540     int errret;
541     char *p;
542     const char *ttype;
543 #if HAVE_GETTTYNAM
544     struct ttyent *t;
545 #else
546     FILE *fp;
547 #endif
548     char *ttypath;
549
550     (void) fd;
551
552     if (userarg) {
553         ttype = userarg;
554         goto found;
555     }
556
557     /* Try the environment. */
558     if ((ttype = getenv("TERM")) != 0)
559         goto map;
560
561     if ((ttypath = ttyname(fd)) != 0) {
562         p = _nc_basename(ttypath);
563 #if HAVE_GETTTYNAM
564         /*
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.
568          */
569         if ((t = getttynam(p))) {
570             ttype = t->ty_type;
571             goto map;
572         }
573 #else
574         if ((fp = fopen("/etc/ttytype", "r")) != 0
575             || (fp = fopen("/etc/ttys", "r")) != 0) {
576             char buffer[BUFSIZ];
577             char *s, *t, *d;
578
579             while (fgets(buffer, sizeof(buffer) - 1, fp) != 0) {
580                 for (s = buffer, t = d = 0; *s; s++) {
581                     if (isspace(UChar(*s)))
582                         *s = '\0';
583                     else if (t == 0)
584                         t = s;
585                     else if (d == 0 && s != buffer && s[-1] == '\0')
586                         d = s;
587                 }
588                 if (t != 0 && d != 0 && !strcmp(d, p)) {
589                     ttype = strdup(t);
590                     fclose(fp);
591                     goto map;
592                 }
593             }
594             fclose(fp);
595         }
596 #endif /* HAVE_GETTTYNAM */
597     }
598
599     /* If still undefined, use "unknown". */
600     ttype = "unknown";
601
602   map:ttype = mapped(ttype);
603
604     /*
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.
608      */
609   found:
610     if ((p = getenv("TERMCAP")) != 0 && !_nc_is_abs_path(p)) {
611         /* 'unsetenv("TERMCAP")' is not portable.
612          * The 'environ' array is better.
613          */
614         int n;
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) {
618                     n++;
619                 }
620                 break;
621             }
622         }
623     }
624
625     /*
626      * ttype now contains a pointer to the type of the terminal.
627      * If the first character is '?', ask the user.
628      */
629     if (ttype[0] == '?') {
630         if (ttype[1] != '\0')
631             ttype = askuser(ttype + 1);
632         else
633             ttype = askuser(0);
634     }
635     /* Find the terminfo entry.  If it doesn't exist, ask the user. */
636     while (setupterm((NCURSES_CONST char *) ttype, fd, &errret)
637            != OK) {
638         if (errret == 0) {
639             (void) fprintf(stderr, "%s: unknown terminal type %s\n",
640                            _nc_progname, ttype);
641             ttype = 0;
642         } else {
643             (void) fprintf(stderr,
644                            "%s: can't initialize terminal type %s (error %d)\n",
645                            _nc_progname, ttype, errret);
646             ttype = 0;
647         }
648         ttype = askuser(ttype);
649     }
650 #if BROKEN_LINKER
651     tgetflag("am");             /* force lib_termcap.o to be linked for 'ospeed' */
652 #endif
653     return (ttype);
654 }
655
656 /**************************************************************************
657  *
658  * Main sequence
659  *
660  **************************************************************************/
661
662 /*
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.
665  */
666 static void
667 obsolete(char **argv)
668 {
669     for (; *argv; ++argv) {
670         char *parm = argv[0];
671
672         if (parm[0] == '-' && parm[1] == '\0') {
673             argv[0] = strdup("-q");
674             continue;
675         }
676
677         if ((parm[0] != '-')
678             || (argv[1] && argv[1][0] != '-')
679             || (parm[1] != 'e' && parm[1] != 'i' && parm[1] != 'k')
680             || (parm[2] != '\0'))
681             continue;
682         switch (argv[0][1]) {
683         case 'e':
684             argv[0] = strdup("-e^H");
685             break;
686         case 'i':
687             argv[0] = strdup("-i^C");
688             break;
689         case 'k':
690             argv[0] = strdup("-k^U");
691             break;
692         }
693     }
694 }
695
696 static void
697 print_shell_commands(const char *ttype)
698 {
699     const char *p;
700     int len;
701     char *var;
702     char *leaf;
703     /*
704      * Figure out what shell we're using.  A hack, we look for an
705      * environmental variable SHELL ending in "csh".
706      */
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";
711     else
712         p = "TERM=%s;\n";
713     (void) printf(p, ttype);
714 }
715
716 static void
717 usage(void)
718 {
719 #define SKIP(s)                 /* nothing */
720 #define KEEP(s) s "\n"
721     static const char msg[] =
722     {
723         KEEP("")
724         KEEP("Options:")
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")
741         KEEP("")
742         KEEP("If neither -c/-w are given, both are assumed.")
743     };
744 #undef KEEP
745 #undef SKIP
746     (void) fprintf(stderr, "Usage: %s [options] [terminal]\n", _nc_progname);
747     fputs(msg, stderr);
748     ExitProgram(EXIT_FAILURE);
749     /* NOTREACHED */
750 }
751
752 static char
753 arg_to_char(void)
754 {
755     return (char) ((optarg[0] == '^' && optarg[1] != '\0')
756                    ? ((optarg[1] == '?') ? '\177' : CTRL(optarg[1]))
757                    : optarg[0]);
758 }
759
760 int
761 main(int argc, char **argv)
762 {
763     int ch, noinit, noset, quiet, Sflag, sflag, showterm;
764     const char *ttype;
765     int terasechar = -1;        /* new erase character */
766     int intrchar = -1;          /* new interrupt character */
767     int tkillchar = -1;         /* new kill character */
768     int my_fd;
769     bool opt_c = FALSE;         /* set control-chars */
770     bool opt_w = FALSE;         /* set window-size */
771     TTY mode, oldmode;
772
773     my_fd = STDERR_FILENO;
774     obsolete(argv);
775     noinit = noset = quiet = Sflag = sflag = showterm = 0;
776     while ((ch = getopt(argc, argv, "a:cd:e:Ii:k:m:p:qQrSsVw")) != -1) {
777         switch (ch) {
778         case 'c':               /* set control-chars */
779             opt_c = TRUE;
780             break;
781         case 'a':               /* OBSOLETE: map identifier to type */
782             add_mapping("arpanet", optarg);
783             break;
784         case 'd':               /* OBSOLETE: map identifier to type */
785             add_mapping("dialup", optarg);
786             break;
787         case 'e':               /* erase character */
788             terasechar = arg_to_char();
789             break;
790         case 'I':               /* no initialization strings */
791             noinit = 1;
792             break;
793         case 'i':               /* interrupt character */
794             intrchar = arg_to_char();
795             break;
796         case 'k':               /* kill character */
797             tkillchar = arg_to_char();
798             break;
799         case 'm':               /* map identifier to type */
800             add_mapping(0, optarg);
801             break;
802         case 'p':               /* OBSOLETE: map identifier to type */
803             add_mapping("plugboard", optarg);
804             break;
805         case 'Q':               /* don't output control key settings */
806             quiet = 1;
807             break;
808         case 'q':               /* display term only */
809             noset = 1;
810             break;
811         case 'r':               /* display term on stderr */
812             showterm = 1;
813             break;
814         case 'S':               /* OBSOLETE: output TERM & TERMCAP */
815             Sflag = 1;
816             break;
817         case 's':               /* output TERM set command */
818             sflag = 1;
819             break;
820         case 'V':               /* print curses-version */
821             puts(curses_version());
822             ExitProgram(EXIT_SUCCESS);
823         case 'w':               /* set window-size */
824             opt_w = TRUE;
825             break;
826         case '?':
827         default:
828             usage();
829         }
830     }
831
832     _nc_progname = _nc_rootname(*argv);
833     argc -= optind;
834     argv += optind;
835
836     if (argc > 1)
837         usage();
838
839     if (!opt_c && !opt_w)
840         opt_c = opt_w = TRUE;
841
842     my_fd = save_tty_settings(&mode, TRUE);
843     oldmode = mode;
844 #ifdef TERMIOS
845     ospeed = (NCURSES_OSPEED) cfgetospeed(&mode);
846 #elif (defined(_WIN32) || defined(_WIN64))
847     ospeed = 0;
848 #else
849     ospeed = (NCURSES_OSPEED) mode.sg_ospeed;
850 #endif
851
852     if (same_program(_nc_progname, PROG_RESET)) {
853         reset_start(stderr, TRUE, FALSE);
854         reset_tty_settings(my_fd, &mode);
855     } else {
856         reset_start(stderr, FALSE, TRUE);
857     }
858
859     ttype = get_termcap_entry(my_fd, *argv);
860
861     if (!noset) {
862 #if HAVE_SIZECHANGE
863         if (opt_w) {
864             set_window_size(my_fd, &lines, &columns);
865         }
866 #endif
867         if (opt_c) {
868             set_control_chars(&mode, terasechar, intrchar, tkillchar);
869             set_conversions(&mode);
870
871             if (!noinit) {
872                 if (send_init_strings(my_fd, &oldmode)) {
873                     (void) putc('\r', stderr);
874                     (void) fflush(stderr);
875                     (void) napms(1000);         /* Settle the terminal. */
876                 }
877             }
878
879             update_tty_settings(&oldmode, &mode);
880         }
881     }
882
883     if (noset) {
884         (void) printf("%s\n", ttype);
885     } else {
886         if (showterm)
887             (void) fprintf(stderr, "Terminal type is %s.\n", ttype);
888         /*
889          * If erase, kill and interrupt characters could have been
890          * modified and not -Q, display the changes.
891          */
892         if (!quiet) {
893             print_tty_chars(&oldmode, &mode);
894         }
895     }
896
897     if (Sflag)
898         err("The -S option is not supported under terminfo.");
899
900     if (sflag) {
901         print_shell_commands(ttype);
902     }
903
904     ExitProgram(EXIT_SUCCESS);
905 }