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
98char *ttyname(int fd);
99#endif
100
101MODULE_ID("$Id: tset.c,v 1.121 2020/02/02 23:34:34 tom Exp $")
102
103#ifndef environ
104extern char **environ;
105#endif
106
107const char *_nc_progname = "tset";
108
109#define LOWERCASE(c) ((isalpha(UChar(c)) && isupper(UChar(c))) ? tolower(UChar(c)) : (c))
110
111static void exit_error(void) GCC_NORETURN;
112
113static int
114CaselessCmp(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
125static void
126exit_error(void)
127{
128    restore_tty_settings();
129    (void) fprintf(stderr, "\n");
130    fflush(stderr);
131    ExitProgram(EXIT_FAILURE);
132    /* NOTREACHED */
133}
134
135static void
136err(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
147static void
148failed(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. */
166static const char *
167askuser(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
217typedef 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
225static MAP *cur, *maplist;
226
227#define DATA(name,value) { { name }, value }
228
229typedef struct speeds {
230    const char string[7];
231    int speed;
232} SPEEDS;
233
234static 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
335static int
336tbaudrate(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 */
365static void
366add_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 */
486static const char *
487mapped(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 */
533static const char *
534get_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 */
662static void
663obsolete(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
692static void
693print_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
712static void
713usage(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
748static char
749arg_to_char(void)
750{
751    return (char) ((optarg[0] == '^' && optarg[1] != '\0')
752		   ? ((optarg[1] == '?') ? '\177' : CTRL(optarg[1]))
753		   : optarg[0]);
754}
755
756int
757main(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}
900