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