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