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