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