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