1/*	$OpenBSD: tset.c,v 1.45 2023/10/17 09:52:11 nicm Exp $	*/
2
3/****************************************************************************
4 * Copyright 2020,2021 Thomas E. Dickey                                     *
5 * Copyright 1998-2016,2017 Free Software Foundation, Inc.                  *
6 *                                                                          *
7 * Permission is hereby granted, free of charge, to any person obtaining a  *
8 * copy of this software and associated documentation files (the            *
9 * "Software"), to deal in the Software without restriction, including      *
10 * without limitation the rights to use, copy, modify, merge, publish,      *
11 * distribute, distribute with modifications, sublicense, and/or sell       *
12 * copies of the Software, and to permit persons to whom the Software is    *
13 * furnished to do so, subject to the following conditions:                 *
14 *                                                                          *
15 * The above copyright notice and this permission notice shall be included  *
16 * in all copies or substantial portions of the Software.                   *
17 *                                                                          *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
21 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
24 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
25 *                                                                          *
26 * Except as contained in this notice, the name(s) of the above copyright   *
27 * holders shall not be used in advertising or otherwise to promote the     *
28 * sale, use or other dealings in this Software without prior written       *
29 * authorization.                                                           *
30 ****************************************************************************/
31
32/****************************************************************************
33 *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
34 *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
35 *     and: Thomas E. Dickey                        1996-on                 *
36 ****************************************************************************/
37
38/*
39 * Notes:
40 * The initial adaptation from 4.4BSD Lite sources in September 1995 used 686
41 * lines from that version, and made changes/additions for 150 lines.  There
42 * was no reformatting, so with/without ignoring whitespace, the amount of
43 * change is the same.
44 *
45 * Comparing with current (2009) source, excluding this comment:
46 * a) 209 lines match identically to the 4.4BSD Lite sources, with 771 lines
47 *    changed/added.
48 * a) Ignoring whitespace, the current version still uses 516 lines from the
49 *    4.4BSD Lite sources, with 402 lines changed/added.
50 *
51 * Raymond's original comment on this follows...
52 */
53
54/*
55 * tset.c - terminal initialization utility
56 *
57 * This code was mostly swiped from 4.4BSD tset, with some obsolescent
58 * cruft removed and substantial portions rewritten.  A Regents of the
59 * University of California copyright applies to some portions of the
60 * code, and is reproduced below:
61 */
62/*-
63 * Copyright (c) 1980, 1991, 1993
64 *	The Regents of the University of California.  All rights reserved.
65 *
66 * Redistribution and use in source and binary forms, with or without
67 * modification, are permitted provided that the following conditions
68 * are met:
69 * 1. Redistributions of source code must retain the above copyright
70 *    notice, this list of conditions and the following disclaimer.
71 * 2. Redistributions in binary form must reproduce the above copyright
72 *    notice, this list of conditions and the following disclaimer in the
73 *    documentation and/or other materials provided with the distribution.
74 * 3. Neither the name of the University nor the names of its contributors
75 *    may be used to endorse or promote products derived from this software
76 *    without specific prior written permission.
77 *
78 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
79 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
80 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
81 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
82 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
83 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
84 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
85 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
86 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
87 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
88 * SUCH DAMAGE.
89 */
90
91#include <reset_cmd.h>
92#include <termcap.h>
93#include <transform.h>
94#include <tty_settings.h>
95
96#if HAVE_GETTTYNAM
97#include <ttyent.h>
98#endif
99#ifdef NeXT
100char *ttyname(int fd);
101#endif
102
103
104
105#ifndef environ
106extern char **environ;
107#endif
108
109const char *_nc_progname = "tset";
110
111#define LOWERCASE(c) ((isalpha(UChar(c)) && isupper(UChar(c))) ? tolower(UChar(c)) : (c))
112
113static GCC_NORETURN void exit_error(void);
114
115static int
116CaselessCmp(const char *a, const char *b)
117{				/* strcasecmp isn't portable */
118    while (*a && *b) {
119	int cmp = LOWERCASE(*a) - LOWERCASE(*b);
120	if (cmp != 0)
121	    break;
122	a++, b++;
123    }
124    return LOWERCASE(*a) - LOWERCASE(*b);
125}
126
127static GCC_NORETURN void
128exit_error(void)
129{
130    restore_tty_settings();
131    (void) fprintf(stderr, "\n");
132    fflush(stderr);
133    ExitProgram(EXIT_FAILURE);
134    /* NOTREACHED */
135}
136
137static GCC_NORETURN void
138err(const char *fmt, ...)
139{
140    va_list ap;
141    va_start(ap, fmt);
142    (void) fprintf(stderr, "%s: ", _nc_progname);
143    (void) vfprintf(stderr, fmt, ap);
144    va_end(ap);
145    exit_error();
146    /* NOTREACHED */
147}
148
149static GCC_NORETURN void
150failed(const char *msg)
151{
152    char temp[BUFSIZ];
153    size_t len = strlen(_nc_progname) + 2;
154
155    if ((int) len < (int) sizeof(temp) - 12) {
156	_nc_STRCPY(temp, _nc_progname, sizeof(temp));
157	_nc_STRCAT(temp, ": ", sizeof(temp));
158    } else {
159	_nc_STRCPY(temp, "tset: ", sizeof(temp));
160    }
161    _nc_STRNCAT(temp, msg, sizeof(temp), sizeof(temp) - strlen(temp) - 2);
162    perror(temp);
163    exit_error();
164    /* NOTREACHED */
165}
166
167/* Prompt the user for a terminal type. */
168static const char *
169askuser(const char *dflt)
170{
171    static char answer[256];
172
173    /* We can get recalled; if so, don't continue uselessly. */
174    clearerr(stdin);
175    if (feof(stdin) || ferror(stdin)) {
176	(void) fprintf(stderr, "\n");
177	exit_error();
178	/* NOTREACHED */
179    }
180
181    for (;;) {
182	char *p;
183
184	if (dflt)
185	    (void) fprintf(stderr, "Terminal type? [%s] ", dflt);
186	else
187	    (void) fprintf(stderr, "Terminal type? ");
188	(void) fflush(stderr);
189
190	if (fgets(answer, sizeof(answer), stdin) == 0) {
191	    if (dflt == 0) {
192		exit_error();
193		/* NOTREACHED */
194	    }
195	    return (dflt);
196	}
197
198	if ((p = strchr(answer, '\n')) != 0)
199	    *p = '\0';
200	if (answer[0])
201	    return (answer);
202	if (dflt != 0)
203	    return (dflt);
204    }
205}
206
207/**************************************************************************
208 *
209 * Mapping logic begins here
210 *
211 **************************************************************************/
212
213/* Baud rate conditionals for mapping. */
214#define	GT		0x01
215#define	EQ		0x02
216#define	LT		0x04
217#define	NOT		0x08
218#define	GE		(GT | EQ)
219#define	LE		(LT | EQ)
220
221typedef struct map {
222    struct map *next;		/* Linked list of maps. */
223    const char *porttype;	/* Port type, or "" for any. */
224    const char *type;		/* Terminal type to select. */
225    int conditional;		/* Baud rate conditionals bitmask. */
226    int speed;			/* Baud rate to compare against. */
227} MAP;
228
229static MAP *cur, *maplist;
230
231#define DATA(name,value) { { name }, value }
232
233typedef struct speeds {
234    const char string[8];
235    int speed;
236} SPEEDS;
237
238#if defined(EXP_WIN32_DRIVER)
239static const SPEEDS speeds[] =
240{
241    {"0", 0}
242};
243#else
244static const SPEEDS speeds[] =
245{
246    DATA("0", B0),
247    DATA("50", B50),
248    DATA("75", B75),
249    DATA("110", B110),
250    DATA("134", B134),
251    DATA("134.5", B134),
252    DATA("150", B150),
253    DATA("200", B200),
254    DATA("300", B300),
255    DATA("600", B600),
256    DATA("1200", B1200),
257    DATA("1800", B1800),
258    DATA("2400", B2400),
259    DATA("4800", B4800),
260    DATA("9600", B9600),
261    /* sgttyb may define up to this point */
262#ifdef B19200
263    DATA("19200", B19200),
264#endif
265#ifdef B38400
266    DATA("38400", B38400),
267#endif
268#ifdef B19200
269    DATA("19200", B19200),
270#endif
271#ifdef B38400
272    DATA("38400", B38400),
273#endif
274#ifdef B19200
275    DATA("19200", B19200),
276#else
277#ifdef EXTA
278    DATA("19200", EXTA),
279#endif
280#endif
281#ifdef B38400
282    DATA("38400", B38400),
283#else
284#ifdef EXTB
285    DATA("38400", EXTB),
286#endif
287#endif
288#ifdef B57600
289    DATA("57600", B57600),
290#endif
291#ifdef B76800
292    DATA("76800", B57600),
293#endif
294#ifdef B115200
295    DATA("115200", B115200),
296#endif
297#ifdef B153600
298    DATA("153600", B153600),
299#endif
300#ifdef B230400
301    DATA("230400", B230400),
302#endif
303#ifdef B307200
304    DATA("307200", B307200),
305#endif
306#ifdef B460800
307    DATA("460800", B460800),
308#endif
309#ifdef B500000
310    DATA("500000", B500000),
311#endif
312#ifdef B576000
313    DATA("576000", B576000),
314#endif
315#ifdef B921600
316    DATA("921600", B921600),
317#endif
318#ifdef B1000000
319    DATA("1000000", B1000000),
320#endif
321#ifdef B1152000
322    DATA("1152000", B1152000),
323#endif
324#ifdef B1500000
325    DATA("1500000", B1500000),
326#endif
327#ifdef B2000000
328    DATA("2000000", B2000000),
329#endif
330#ifdef B2500000
331    DATA("2500000", B2500000),
332#endif
333#ifdef B3000000
334    DATA("3000000", B3000000),
335#endif
336#ifdef B3500000
337    DATA("3500000", B3500000),
338#endif
339#ifdef B4000000
340    DATA("4000000", B4000000),
341#endif
342};
343#undef DATA
344#endif
345
346static int
347tbaudrate(char *rate)
348{
349    const SPEEDS *sp = 0;
350    size_t n;
351
352    /* The baudrate number can be preceded by a 'B', which is ignored. */
353    if (*rate == 'B')
354	++rate;
355
356    for (n = 0; n < SIZEOF(speeds); ++n) {
357	if (n > 0 && (speeds[n].speed <= speeds[n - 1].speed)) {
358	    /* if the speeds are not increasing, likely a numeric overflow */
359	    break;
360	}
361	if (!CaselessCmp(rate, speeds[n].string)) {
362	    sp = speeds + n;
363	    break;
364	}
365    }
366    if (sp == 0)
367	err("unknown baud rate %s", rate);
368    return (sp->speed);
369}
370
371/*
372 * Syntax for -m:
373 * [port-type][test baudrate]:terminal-type
374 * The baud rate tests are: >, <, @, =, !
375 */
376static void
377add_mapping(const char *port, char *arg)
378{
379    MAP *mapp;
380    char *copy, *p;
381    const char *termp;
382    char *base = 0;
383
384    copy = strdup(arg);
385    mapp = typeMalloc(MAP, 1);
386    if (copy == 0 || mapp == 0)
387	failed("malloc");
388
389    assert(copy != 0);
390    assert(mapp != 0);
391
392    mapp->next = 0;
393    if (maplist == 0)
394	cur = maplist = mapp;
395    else {
396	cur->next = mapp;
397	cur = mapp;
398    }
399
400    mapp->porttype = arg;
401    mapp->conditional = 0;
402
403    arg = strpbrk(arg, "><@=!:");
404
405    if (arg == 0) {		/* [?]term */
406	mapp->type = mapp->porttype;
407	mapp->porttype = 0;
408	goto done;
409    }
410
411    if (arg == mapp->porttype)	/* [><@=! baud]:term */
412	termp = mapp->porttype = 0;
413    else
414	termp = base = arg;
415
416    for (;; ++arg) {		/* Optional conditionals. */
417	switch (*arg) {
418	case '<':
419	    if (mapp->conditional & GT)
420		goto badmopt;
421	    mapp->conditional |= LT;
422	    break;
423	case '>':
424	    if (mapp->conditional & LT)
425		goto badmopt;
426	    mapp->conditional |= GT;
427	    break;
428	case '@':
429	case '=':		/* Not documented. */
430	    mapp->conditional |= EQ;
431	    break;
432	case '!':
433	    mapp->conditional |= NOT;
434	    break;
435	default:
436	    goto next;
437	}
438    }
439
440  next:
441    if (*arg == ':') {
442	if (mapp->conditional)
443	    goto badmopt;
444	++arg;
445    } else {			/* Optional baudrate. */
446	arg = strchr(p = arg, ':');
447	if (arg == 0)
448	    goto badmopt;
449	*arg++ = '\0';
450	mapp->speed = tbaudrate(p);
451    }
452
453    mapp->type = arg;
454
455    /* Terminate porttype, if specified. */
456    if (termp != 0)
457	*base = '\0';
458
459    /* If a NOT conditional, reverse the test. */
460    if (mapp->conditional & NOT)
461	mapp->conditional = ~mapp->conditional & (EQ | GT | LT);
462
463    /* If user specified a port with an option flag, set it. */
464  done:
465    if (port) {
466	if (mapp->porttype) {
467	  badmopt:
468	    err("illegal -m option format: %s", copy);
469	}
470	mapp->porttype = port;
471    }
472    free(copy);
473#ifdef MAPDEBUG
474    (void) printf("port: %s\n", mapp->porttype ? mapp->porttype : "ANY");
475    (void) printf("type: %s\n", mapp->type);
476    (void) printf("conditional: ");
477    p = "";
478    if (mapp->conditional & GT) {
479	(void) printf("GT");
480	p = "/";
481    }
482    if (mapp->conditional & EQ) {
483	(void) printf("%sEQ", p);
484	p = "/";
485    }
486    if (mapp->conditional & LT)
487	(void) printf("%sLT", p);
488    (void) printf("\nspeed: %d\n", mapp->speed);
489#endif
490}
491
492/*
493 * Return the type of terminal to use for a port of type 'type', as specified
494 * by the first applicable mapping in 'map'.  If no mappings apply, return
495 * 'type'.
496 */
497static const char *
498mapped(const char *type)
499{
500    MAP *mapp;
501    int match;
502
503    for (mapp = maplist; mapp; mapp = mapp->next)
504	if (mapp->porttype == 0 || !strcmp(mapp->porttype, type)) {
505	    switch (mapp->conditional) {
506	    case 0:		/* No test specified. */
507		match = TRUE;
508		break;
509	    case EQ:
510		match = ((int) ospeed == mapp->speed);
511		break;
512	    case GE:
513		match = ((int) ospeed >= mapp->speed);
514		break;
515	    case GT:
516		match = ((int) ospeed > mapp->speed);
517		break;
518	    case LE:
519		match = ((int) ospeed <= mapp->speed);
520		break;
521	    case LT:
522		match = ((int) ospeed < mapp->speed);
523		break;
524	    default:
525		match = FALSE;
526	    }
527	    if (match)
528		return (mapp->type);
529	}
530    /* No match found; return given type. */
531    return (type);
532}
533
534/**************************************************************************
535 *
536 * Entry fetching
537 *
538 **************************************************************************/
539
540/*
541 * Figure out what kind of terminal we're dealing with, and then read in
542 * its termcap entry.
543 */
544static const char *
545get_termcap_entry(int fd, char *userarg)
546{
547    int errret;
548    char *p;
549    const char *ttype;
550#if HAVE_PATH_TTYS
551#if HAVE_GETTTYNAM
552    struct ttyent *t;
553#else
554    FILE *fp;
555#endif
556    char *ttypath;
557#endif /* HAVE_PATH_TTYS */
558
559    (void) fd;
560
561    if (userarg) {
562	ttype = userarg;
563	goto found;
564    }
565
566    /* Try the environment. */
567    if ((ttype = getenv("TERM")) != 0)
568	goto map;
569
570#if HAVE_PATH_TTYS
571    if ((ttypath = ttyname(fd)) != 0) {
572	p = _nc_basename(ttypath);
573#if HAVE_GETTTYNAM
574	/*
575	 * We have the 4.3BSD library call getttynam(3); that means
576	 * there's an /etc/ttys to look up device-to-type mappings in.
577	 * Try ttyname(3); check for dialup or other mapping.
578	 */
579	if ((t = getttynam(p))) {
580	    ttype = t->ty_type;
581	    goto map;
582	}
583#else
584	if ((fp = fopen("/etc/ttytype", "r")) != 0
585	    || (fp = fopen("/etc/ttys", "r")) != 0) {
586	    char buffer[BUFSIZ];
587	    char *s, *t, *d;
588
589	    while (fgets(buffer, sizeof(buffer) - 1, fp) != 0) {
590		for (s = buffer, t = d = 0; *s; s++) {
591		    if (isspace(UChar(*s)))
592			*s = '\0';
593		    else if (t == 0)
594			t = s;
595		    else if (d == 0 && s != buffer && s[-1] == '\0')
596			d = s;
597		}
598		if (t != 0 && d != 0 && !strcmp(d, p)) {
599		    ttype = strdup(t);
600		    fclose(fp);
601		    goto map;
602		}
603	    }
604	    fclose(fp);
605	}
606#endif /* HAVE_GETTTYNAM */
607    }
608#endif /* HAVE_PATH_TTYS */
609
610    /* If still undefined, use "unknown". */
611    ttype = "unknown";
612
613  map:ttype = mapped(ttype);
614
615    /*
616     * If not a path, remove TERMCAP from the environment so we get a
617     * real entry from /etc/termcap.  This prevents us from being fooled
618     * by out of date stuff in the environment.
619     */
620  found:
621    if ((p = getenv("TERMCAP")) != 0 && !_nc_is_abs_path(p)) {
622	/* 'unsetenv("TERMCAP")' is not portable.
623	 * The 'environ' array is better.
624	 */
625	int n;
626	for (n = 0; environ[n] != 0; n++) {
627	    if (!strncmp("TERMCAP=", environ[n], (size_t) 8)) {
628		while ((environ[n] = environ[n + 1]) != 0) {
629		    n++;
630		}
631		break;
632	    }
633	}
634    }
635
636    /*
637     * ttype now contains a pointer to the type of the terminal.
638     * If the first character is '?', ask the user.
639     */
640    if (ttype[0] == '?') {
641	if (ttype[1] != '\0')
642	    ttype = askuser(ttype + 1);
643	else
644	    ttype = askuser(0);
645    }
646    /* Find the terminfo entry.  If it doesn't exist, ask the user. */
647    while (setupterm((NCURSES_CONST char *) ttype, fd, &errret)
648	   != OK) {
649	if (errret == 0) {
650	    (void) fprintf(stderr, "%s: unknown terminal type %s\n",
651			   _nc_progname, ttype);
652	    ttype = 0;
653	} else {
654	    (void) fprintf(stderr,
655			   "%s: can't initialize terminal type %s (error %d)\n",
656			   _nc_progname, ttype, errret);
657	    ttype = 0;
658	}
659	ttype = askuser(ttype);
660    }
661#if BROKEN_LINKER
662    tgetflag("am");		/* force lib_termcap.o to be linked for 'ospeed' */
663#endif
664    return (ttype);
665}
666
667/**************************************************************************
668 *
669 * Main sequence
670 *
671 **************************************************************************/
672
673/*
674 * Convert the obsolete argument forms into something that getopt can handle.
675 * This means that -e, -i and -k get default arguments supplied for them.
676 */
677static void
678obsolete(char **argv)
679{
680    for (; *argv; ++argv) {
681	char *parm = argv[0];
682
683	if (parm[0] == '-' && parm[1] == '\0') {
684	    argv[0] = strdup("-q");
685	    continue;
686	}
687
688	if ((parm[0] != '-')
689	    || (argv[1] && argv[1][0] != '-')
690	    || (parm[1] != 'e' && parm[1] != 'i' && parm[1] != 'k')
691	    || (parm[2] != '\0'))
692	    continue;
693	switch (argv[0][1]) {
694	case 'e':
695	    argv[0] = strdup("-e^H");
696	    break;
697	case 'i':
698	    argv[0] = strdup("-i^C");
699	    break;
700	case 'k':
701	    argv[0] = strdup("-k^U");
702	    break;
703	}
704    }
705}
706
707static void
708print_shell_commands(const char *ttype)
709{
710    const char *p;
711    int len;
712    char *var;
713    char *leaf;
714    /*
715     * Figure out what shell we're using.  A hack, we look for an
716     * environmental variable SHELL ending in "csh".
717     */
718    if ((var = getenv("SHELL")) != 0
719	&& ((len = (int) strlen(leaf = _nc_basename(var))) >= 3)
720	&& !strcmp(leaf + len - 3, "csh"))
721	p = "set noglob;\nsetenv TERM %s;\nunset noglob;\n";
722    else
723	p = "TERM=%s;\n";
724    (void) printf(p, ttype);
725}
726
727static void
728usage(void)
729{
730#define SKIP(s)			/* nothing */
731#define KEEP(s) s "\n"
732    static const char msg[] =
733    {
734	KEEP("")
735	KEEP("Options:")
736	SKIP("  -a arpanet  (obsolete)")
737	KEEP("  -c          set control characters")
738	SKIP("  -d dialup   (obsolete)")
739	KEEP("  -e ch       erase character")
740	KEEP("  -I          no initialization strings")
741	KEEP("  -i ch       interrupt character")
742	KEEP("  -k ch       kill character")
743	KEEP("  -m mapping  map identifier to type")
744	SKIP("  -p plugboard (obsolete)")
745	KEEP("  -Q          do not output control key settings")
746	KEEP("  -q          display term only, do no changes")
747	KEEP("  -r          display term on stderr")
748	SKIP("  -S          (obsolete)")
749	KEEP("  -s          output TERM set command")
750	KEEP("  -V          print curses-version")
751	KEEP("  -w          set window-size")
752	KEEP("")
753	KEEP("If neither -c/-w are given, both are assumed.")
754    };
755#undef KEEP
756#undef SKIP
757    (void) fprintf(stderr, "Usage: %s [options] [terminal]\n", _nc_progname);
758    fputs(msg, stderr);
759    ExitProgram(EXIT_FAILURE);
760    /* NOTREACHED */
761}
762
763static char
764arg_to_char(void)
765{
766    return (char) ((optarg[0] == '^' && optarg[1] != '\0')
767		   ? ((optarg[1] == '?') ? '\177' : CTRL(optarg[1]))
768		   : optarg[0]);
769}
770
771int
772main(int argc, char **argv)
773{
774    int ch, noinit, noset, quiet, Sflag, sflag, showterm;
775    const char *ttype;
776    int terasechar = -1;	/* new erase character */
777    int intrchar = -1;		/* new interrupt character */
778    int tkillchar = -1;		/* new kill character */
779    int my_fd;
780    bool opt_c = FALSE;		/* set control-chars */
781    bool opt_w = FALSE;		/* set window-size */
782    TTY mode, oldmode;
783
784    _nc_progname = _nc_rootname(*argv);
785
786    if (pledge("stdio rpath wpath tty", NULL) == -1)
787	err("pledge: %s", strerror(errno));
788
789    obsolete(argv);
790    noinit = noset = quiet = Sflag = sflag = showterm = 0;
791    while ((ch = getopt(argc, argv, "a:cd:e:Ii:k:m:np:qQrSsVw")) != -1) {
792	switch (ch) {
793	case 'c':		/* set control-chars */
794	    opt_c = TRUE;
795	    break;
796	case 'a':		/* OBSOLETE: map identifier to type */
797	    add_mapping("arpanet", optarg);
798	    break;
799	case 'd':		/* OBSOLETE: map identifier to type */
800	    add_mapping("dialup", optarg);
801	    break;
802	case 'e':		/* erase character */
803	    terasechar = arg_to_char();
804	    break;
805	case 'I':		/* no initialization strings */
806	    noinit = 1;
807	    break;
808	case 'i':		/* interrupt character */
809	    intrchar = arg_to_char();
810	    break;
811	case 'k':		/* kill character */
812	    tkillchar = arg_to_char();
813	    break;
814	case 'm':		/* map identifier to type */
815	    add_mapping(0, optarg);
816	    break;
817	case 'n':		/* OBSOLETE: set new tty driver */
818	    break;
819	case 'p':		/* OBSOLETE: map identifier to type */
820	    add_mapping("plugboard", optarg);
821	    break;
822	case 'Q':		/* don't output control key settings */
823	    quiet = 1;
824	    break;
825	case 'q':		/* display term only */
826	    noset = 1;
827	    break;
828	case 'r':		/* display term on stderr */
829	    showterm = 1;
830	    break;
831	case 'S':		/* OBSOLETE: output TERM & TERMCAP */
832	    Sflag = 1;
833	    break;
834	case 's':		/* output TERM set command */
835	    sflag = 1;
836	    break;
837	case 'V':		/* print curses-version */
838	    puts(curses_version());
839	    ExitProgram(EXIT_SUCCESS);
840	case 'w':		/* set window-size */
841	    opt_w = TRUE;
842	    break;
843	case '?':
844	default:
845	    usage();
846	}
847    }
848
849    argc -= optind;
850    argv += optind;
851
852    if (argc > 1)
853	usage();
854
855    if (!opt_c && !opt_w)
856	opt_c = opt_w = TRUE;
857
858    my_fd = save_tty_settings(&mode, TRUE);
859    oldmode = mode;
860#ifdef TERMIOS
861    ospeed = (NCURSES_OSPEED) cfgetospeed(&mode);
862#elif defined(EXP_WIN32_DRIVER)
863    ospeed = 0;
864#else
865    ospeed = (NCURSES_OSPEED) mode.sg_ospeed;
866#endif
867
868    if (same_program(_nc_progname, PROG_RESET)) {
869	reset_start(stderr, TRUE, FALSE);
870	reset_tty_settings(my_fd, &mode, noset);
871    } else {
872	reset_start(stderr, FALSE, TRUE);
873    }
874
875    ttype = get_termcap_entry(my_fd, *argv);
876
877    if (!noset) {
878#if HAVE_SIZECHANGE
879	if (opt_w) {
880	    set_window_size(my_fd, &lines, &columns);
881	}
882#endif
883	if (opt_c) {
884	    set_control_chars(&mode, terasechar, intrchar, tkillchar);
885	    set_conversions(&mode);
886
887	    if (!noinit) {
888		if (send_init_strings(my_fd, &oldmode)) {
889		    (void) putc('\r', stderr);
890		    (void) fflush(stderr);
891		    (void) napms(1000);		/* Settle the terminal. */
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