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