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