1/****************************************************************************
2 * Copyright 2020-2021,2024 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
98char *ttyname(int fd);
99#endif
100
101MODULE_ID("$Id: tset.c,v 1.135 2024/04/20 22:20:41 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 GCC_NORETURN void exit_error(void);
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 GCC_NORETURN 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 GCC_NORETURN 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 GCC_NORETURN 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
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
219typedef 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
227static MAP *cur, *maplist;
228
229#define DATA(name,value) { { name }, value }
230
231typedef struct speeds {
232    const char string[8];
233    int speed;
234} SPEEDS;
235
236#if defined(EXP_WIN32_DRIVER)
237static const SPEEDS speeds[] =
238{
239    {"0", 0}
240};
241#else
242static 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
344static int
345tbaudrate(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 */
374static void
375add_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 */
495static const char *
496mapped(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 */
542static const char *
543get_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 */
675static void
676obsolete(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
705static void
706print_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
725static void
726usage(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
761static char
762arg_to_char(void)
763{
764    return (char) ((optarg[0] == '^' && optarg[1] != '\0')
765		   ? ((optarg[1] == '?') ? '\177' : CTRL(optarg[1]))
766		   : optarg[0]);
767}
768
769int
770main(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	    NCURSES_INT2 my_rows = lines;
873	    NCURSES_INT2 my_cols = columns;
874	    set_window_size(my_fd, &my_rows, &my_cols);
875	    lines = my_rows;
876	    columns = my_cols;
877	}
878#endif
879	if (opt_c) {
880	    set_control_chars(&mode, terasechar, intrchar, tkillchar);
881	    set_conversions(&mode);
882
883	    if (!noinit) {
884		if (send_init_strings(my_fd, &oldmode)) {
885		    const char *name;
886
887		    (void) putc('\r', stderr);
888		    (void) fflush(stderr);
889		    if (IsRealTty(my_fd, name)) {
890			(void) napms(1000);	/* Settle the terminal. */
891		    }
892		}
893	    }
894
895	    update_tty_settings(&oldmode, &mode);
896	}
897    }
898
899    if (noset) {
900	(void) printf("%s\n", ttype);
901    } else {
902	if (showterm)
903	    (void) fprintf(stderr, "Terminal type is %s.\n", ttype);
904	/*
905	 * If erase, kill and interrupt characters could have been
906	 * modified and not -Q, display the changes.
907	 */
908	if (!quiet) {
909	    print_tty_chars(&oldmode, &mode);
910	}
911    }
912
913    if (Sflag)
914	err("The -S option is not supported under terminfo.");
915
916    if (sflag) {
917	print_shell_commands(ttype);
918    }
919
920    ExitProgram(EXIT_SUCCESS);
921}
922