[BACK]Return to tset.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tset

File: [local] / src / usr.bin / tset / tset.c (download)

Revision 1.45, Tue Oct 17 09:52:11 2023 UTC (7 months ago) by nicm
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, HEAD
Changes since 1.44: +248 -611 lines

Update ncurses and associated libraries (form, panel, menu) to
6.4-20230826 (from 5.7-20081102).

Based on result from Thomas Dickey's ncu2openbsd script and then
modified. Switches to the upstream tput. Major bump for the ncurses
libraries and for libedit and libreadline.

Help from tb, millert.

ok deraadt sthen

/*	$OpenBSD: tset.c,v 1.45 2023/10/17 09:52:11 nicm Exp $	*/

/****************************************************************************
 * Copyright 2020,2021 Thomas E. Dickey                                     *
 * Copyright 1998-2016,2017 Free Software Foundation, Inc.                  *
 *                                                                          *
 * Permission is hereby granted, free of charge, to any person obtaining a  *
 * copy of this software and associated documentation files (the            *
 * "Software"), to deal in the Software without restriction, including      *
 * without limitation the rights to use, copy, modify, merge, publish,      *
 * distribute, distribute with modifications, sublicense, and/or sell       *
 * copies of the Software, and to permit persons to whom the Software is    *
 * furnished to do so, subject to the following conditions:                 *
 *                                                                          *
 * The above copyright notice and this permission notice shall be included  *
 * in all copies or substantial portions of the Software.                   *
 *                                                                          *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
 *                                                                          *
 * Except as contained in this notice, the name(s) of the above copyright   *
 * holders shall not be used in advertising or otherwise to promote the     *
 * sale, use or other dealings in this Software without prior written       *
 * authorization.                                                           *
 ****************************************************************************/

/****************************************************************************
 *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
 *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
 *     and: Thomas E. Dickey                        1996-on                 *
 ****************************************************************************/

/*
 * Notes:
 * The initial adaptation from 4.4BSD Lite sources in September 1995 used 686
 * lines from that version, and made changes/additions for 150 lines.  There
 * was no reformatting, so with/without ignoring whitespace, the amount of
 * change is the same.
 *
 * Comparing with current (2009) source, excluding this comment:
 * a) 209 lines match identically to the 4.4BSD Lite sources, with 771 lines
 *    changed/added.
 * a) Ignoring whitespace, the current version still uses 516 lines from the
 *    4.4BSD Lite sources, with 402 lines changed/added.
 *
 * Raymond's original comment on this follows...
 */

/*
 * tset.c - terminal initialization utility
 *
 * This code was mostly swiped from 4.4BSD tset, with some obsolescent
 * cruft removed and substantial portions rewritten.  A Regents of the
 * University of California copyright applies to some portions of the
 * code, and is reproduced below:
 */
/*-
 * Copyright (c) 1980, 1991, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <reset_cmd.h>
#include <termcap.h>
#include <transform.h>
#include <tty_settings.h>

#if HAVE_GETTTYNAM
#include <ttyent.h>
#endif
#ifdef NeXT
char *ttyname(int fd);
#endif



#ifndef environ
extern char **environ;
#endif

const char *_nc_progname = "tset";

#define LOWERCASE(c) ((isalpha(UChar(c)) && isupper(UChar(c))) ? tolower(UChar(c)) : (c))

static GCC_NORETURN void exit_error(void);

static int
CaselessCmp(const char *a, const char *b)
{				/* strcasecmp isn't portable */
    while (*a && *b) {
	int cmp = LOWERCASE(*a) - LOWERCASE(*b);
	if (cmp != 0)
	    break;
	a++, b++;
    }
    return LOWERCASE(*a) - LOWERCASE(*b);
}

static GCC_NORETURN void
exit_error(void)
{
    restore_tty_settings();
    (void) fprintf(stderr, "\n");
    fflush(stderr);
    ExitProgram(EXIT_FAILURE);
    /* NOTREACHED */
}

static GCC_NORETURN void
err(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    (void) fprintf(stderr, "%s: ", _nc_progname);
    (void) vfprintf(stderr, fmt, ap);
    va_end(ap);
    exit_error();
    /* NOTREACHED */
}

static GCC_NORETURN void
failed(const char *msg)
{
    char temp[BUFSIZ];
    size_t len = strlen(_nc_progname) + 2;

    if ((int) len < (int) sizeof(temp) - 12) {
	_nc_STRCPY(temp, _nc_progname, sizeof(temp));
	_nc_STRCAT(temp, ": ", sizeof(temp));
    } else {
	_nc_STRCPY(temp, "tset: ", sizeof(temp));
    }
    _nc_STRNCAT(temp, msg, sizeof(temp), sizeof(temp) - strlen(temp) - 2);
    perror(temp);
    exit_error();
    /* NOTREACHED */
}

/* Prompt the user for a terminal type. */
static const char *
askuser(const char *dflt)
{
    static char answer[256];

    /* We can get recalled; if so, don't continue uselessly. */
    clearerr(stdin);
    if (feof(stdin) || ferror(stdin)) {
	(void) fprintf(stderr, "\n");
	exit_error();
	/* NOTREACHED */
    }

    for (;;) {
	char *p;

	if (dflt)
	    (void) fprintf(stderr, "Terminal type? [%s] ", dflt);
	else
	    (void) fprintf(stderr, "Terminal type? ");
	(void) fflush(stderr);

	if (fgets(answer, sizeof(answer), stdin) == 0) {
	    if (dflt == 0) {
		exit_error();
		/* NOTREACHED */
	    }
	    return (dflt);
	}

	if ((p = strchr(answer, '\n')) != 0)
	    *p = '\0';
	if (answer[0])
	    return (answer);
	if (dflt != 0)
	    return (dflt);
    }
}

/**************************************************************************
 *
 * Mapping logic begins here
 *
 **************************************************************************/

/* Baud rate conditionals for mapping. */
#define	GT		0x01
#define	EQ		0x02
#define	LT		0x04
#define	NOT		0x08
#define	GE		(GT | EQ)
#define	LE		(LT | EQ)

typedef struct map {
    struct map *next;		/* Linked list of maps. */
    const char *porttype;	/* Port type, or "" for any. */
    const char *type;		/* Terminal type to select. */
    int conditional;		/* Baud rate conditionals bitmask. */
    int speed;			/* Baud rate to compare against. */
} MAP;

static MAP *cur, *maplist;

#define DATA(name,value) { { name }, value }

typedef struct speeds {
    const char string[8];
    int speed;
} SPEEDS;

#if defined(EXP_WIN32_DRIVER)
static const SPEEDS speeds[] =
{
    {"0", 0}
};
#else
static const SPEEDS speeds[] =
{
    DATA("0", B0),
    DATA("50", B50),
    DATA("75", B75),
    DATA("110", B110),
    DATA("134", B134),
    DATA("134.5", B134),
    DATA("150", B150),
    DATA("200", B200),
    DATA("300", B300),
    DATA("600", B600),
    DATA("1200", B1200),
    DATA("1800", B1800),
    DATA("2400", B2400),
    DATA("4800", B4800),
    DATA("9600", B9600),
    /* sgttyb may define up to this point */
#ifdef B19200
    DATA("19200", B19200),
#endif
#ifdef B38400
    DATA("38400", B38400),
#endif
#ifdef B19200
    DATA("19200", B19200),
#endif
#ifdef B38400
    DATA("38400", B38400),
#endif
#ifdef B19200
    DATA("19200", B19200),
#else
#ifdef EXTA
    DATA("19200", EXTA),
#endif
#endif
#ifdef B38400
    DATA("38400", B38400),
#else
#ifdef EXTB
    DATA("38400", EXTB),
#endif
#endif
#ifdef B57600
    DATA("57600", B57600),
#endif
#ifdef B76800
    DATA("76800", B57600),
#endif
#ifdef B115200
    DATA("115200", B115200),
#endif
#ifdef B153600
    DATA("153600", B153600),
#endif
#ifdef B230400
    DATA("230400", B230400),
#endif
#ifdef B307200
    DATA("307200", B307200),
#endif
#ifdef B460800
    DATA("460800", B460800),
#endif
#ifdef B500000
    DATA("500000", B500000),
#endif
#ifdef B576000
    DATA("576000", B576000),
#endif
#ifdef B921600
    DATA("921600", B921600),
#endif
#ifdef B1000000
    DATA("1000000", B1000000),
#endif
#ifdef B1152000
    DATA("1152000", B1152000),
#endif
#ifdef B1500000
    DATA("1500000", B1500000),
#endif
#ifdef B2000000
    DATA("2000000", B2000000),
#endif
#ifdef B2500000
    DATA("2500000", B2500000),
#endif
#ifdef B3000000
    DATA("3000000", B3000000),
#endif
#ifdef B3500000
    DATA("3500000", B3500000),
#endif
#ifdef B4000000
    DATA("4000000", B4000000),
#endif
};
#undef DATA
#endif

static int
tbaudrate(char *rate)
{
    const SPEEDS *sp = 0;
    size_t n;

    /* The baudrate number can be preceded by a 'B', which is ignored. */
    if (*rate == 'B')
	++rate;

    for (n = 0; n < SIZEOF(speeds); ++n) {
	if (n > 0 && (speeds[n].speed <= speeds[n - 1].speed)) {
	    /* if the speeds are not increasing, likely a numeric overflow */
	    break;
	}
	if (!CaselessCmp(rate, speeds[n].string)) {
	    sp = speeds + n;
	    break;
	}
    }
    if (sp == 0)
	err("unknown baud rate %s", rate);
    return (sp->speed);
}

/*
 * Syntax for -m:
 * [port-type][test baudrate]:terminal-type
 * The baud rate tests are: >, <, @, =, !
 */
static void
add_mapping(const char *port, char *arg)
{
    MAP *mapp;
    char *copy, *p;
    const char *termp;
    char *base = 0;

    copy = strdup(arg);
    mapp = typeMalloc(MAP, 1);
    if (copy == 0 || mapp == 0)
	failed("malloc");

    assert(copy != 0);
    assert(mapp != 0);

    mapp->next = 0;
    if (maplist == 0)
	cur = maplist = mapp;
    else {
	cur->next = mapp;
	cur = mapp;
    }

    mapp->porttype = arg;
    mapp->conditional = 0;

    arg = strpbrk(arg, "><@=!:");

    if (arg == 0) {		/* [?]term */
	mapp->type = mapp->porttype;
	mapp->porttype = 0;
	goto done;
    }

    if (arg == mapp->porttype)	/* [><@=! baud]:term */
	termp = mapp->porttype = 0;
    else
	termp = base = arg;

    for (;; ++arg) {		/* Optional conditionals. */
	switch (*arg) {
	case '<':
	    if (mapp->conditional & GT)
		goto badmopt;
	    mapp->conditional |= LT;
	    break;
	case '>':
	    if (mapp->conditional & LT)
		goto badmopt;
	    mapp->conditional |= GT;
	    break;
	case '@':
	case '=':		/* Not documented. */
	    mapp->conditional |= EQ;
	    break;
	case '!':
	    mapp->conditional |= NOT;
	    break;
	default:
	    goto next;
	}
    }

  next:
    if (*arg == ':') {
	if (mapp->conditional)
	    goto badmopt;
	++arg;
    } else {			/* Optional baudrate. */
	arg = strchr(p = arg, ':');
	if (arg == 0)
	    goto badmopt;
	*arg++ = '\0';
	mapp->speed = tbaudrate(p);
    }

    mapp->type = arg;

    /* Terminate porttype, if specified. */
    if (termp != 0)
	*base = '\0';

    /* If a NOT conditional, reverse the test. */
    if (mapp->conditional & NOT)
	mapp->conditional = ~mapp->conditional & (EQ | GT | LT);

    /* If user specified a port with an option flag, set it. */
  done:
    if (port) {
	if (mapp->porttype) {
	  badmopt:
	    err("illegal -m option format: %s", copy);
	}
	mapp->porttype = port;
    }
    free(copy);
#ifdef MAPDEBUG
    (void) printf("port: %s\n", mapp->porttype ? mapp->porttype : "ANY");
    (void) printf("type: %s\n", mapp->type);
    (void) printf("conditional: ");
    p = "";
    if (mapp->conditional & GT) {
	(void) printf("GT");
	p = "/";
    }
    if (mapp->conditional & EQ) {
	(void) printf("%sEQ", p);
	p = "/";
    }
    if (mapp->conditional & LT)
	(void) printf("%sLT", p);
    (void) printf("\nspeed: %d\n", mapp->speed);
#endif
}

/*
 * Return the type of terminal to use for a port of type 'type', as specified
 * by the first applicable mapping in 'map'.  If no mappings apply, return
 * 'type'.
 */
static const char *
mapped(const char *type)
{
    MAP *mapp;
    int match;

    for (mapp = maplist; mapp; mapp = mapp->next)
	if (mapp->porttype == 0 || !strcmp(mapp->porttype, type)) {
	    switch (mapp->conditional) {
	    case 0:		/* No test specified. */
		match = TRUE;
		break;
	    case EQ:
		match = ((int) ospeed == mapp->speed);
		break;
	    case GE:
		match = ((int) ospeed >= mapp->speed);
		break;
	    case GT:
		match = ((int) ospeed > mapp->speed);
		break;
	    case LE:
		match = ((int) ospeed <= mapp->speed);
		break;
	    case LT:
		match = ((int) ospeed < mapp->speed);
		break;
	    default:
		match = FALSE;
	    }
	    if (match)
		return (mapp->type);
	}
    /* No match found; return given type. */
    return (type);
}

/**************************************************************************
 *
 * Entry fetching
 *
 **************************************************************************/

/*
 * Figure out what kind of terminal we're dealing with, and then read in
 * its termcap entry.
 */
static const char *
get_termcap_entry(int fd, char *userarg)
{
    int errret;
    char *p;
    const char *ttype;
#if HAVE_PATH_TTYS
#if HAVE_GETTTYNAM
    struct ttyent *t;
#else
    FILE *fp;
#endif
    char *ttypath;
#endif /* HAVE_PATH_TTYS */

    (void) fd;

    if (userarg) {
	ttype = userarg;
	goto found;
    }

    /* Try the environment. */
    if ((ttype = getenv("TERM")) != 0)
	goto map;

#if HAVE_PATH_TTYS
    if ((ttypath = ttyname(fd)) != 0) {
	p = _nc_basename(ttypath);
#if HAVE_GETTTYNAM
	/*
	 * We have the 4.3BSD library call getttynam(3); that means
	 * there's an /etc/ttys to look up device-to-type mappings in.
	 * Try ttyname(3); check for dialup or other mapping.
	 */
	if ((t = getttynam(p))) {
	    ttype = t->ty_type;
	    goto map;
	}
#else
	if ((fp = fopen("/etc/ttytype", "r")) != 0
	    || (fp = fopen("/etc/ttys", "r")) != 0) {
	    char buffer[BUFSIZ];
	    char *s, *t, *d;

	    while (fgets(buffer, sizeof(buffer) - 1, fp) != 0) {
		for (s = buffer, t = d = 0; *s; s++) {
		    if (isspace(UChar(*s)))
			*s = '\0';
		    else if (t == 0)
			t = s;
		    else if (d == 0 && s != buffer && s[-1] == '\0')
			d = s;
		}
		if (t != 0 && d != 0 && !strcmp(d, p)) {
		    ttype = strdup(t);
		    fclose(fp);
		    goto map;
		}
	    }
	    fclose(fp);
	}
#endif /* HAVE_GETTTYNAM */
    }
#endif /* HAVE_PATH_TTYS */

    /* If still undefined, use "unknown". */
    ttype = "unknown";

  map:ttype = mapped(ttype);

    /*
     * If not a path, remove TERMCAP from the environment so we get a
     * real entry from /etc/termcap.  This prevents us from being fooled
     * by out of date stuff in the environment.
     */
  found:
    if ((p = getenv("TERMCAP")) != 0 && !_nc_is_abs_path(p)) {
	/* 'unsetenv("TERMCAP")' is not portable.
	 * The 'environ' array is better.
	 */
	int n;
	for (n = 0; environ[n] != 0; n++) {
	    if (!strncmp("TERMCAP=", environ[n], (size_t) 8)) {
		while ((environ[n] = environ[n + 1]) != 0) {
		    n++;
		}
		break;
	    }
	}
    }

    /*
     * ttype now contains a pointer to the type of the terminal.
     * If the first character is '?', ask the user.
     */
    if (ttype[0] == '?') {
	if (ttype[1] != '\0')
	    ttype = askuser(ttype + 1);
	else
	    ttype = askuser(0);
    }
    /* Find the terminfo entry.  If it doesn't exist, ask the user. */
    while (setupterm((NCURSES_CONST char *) ttype, fd, &errret)
	   != OK) {
	if (errret == 0) {
	    (void) fprintf(stderr, "%s: unknown terminal type %s\n",
			   _nc_progname, ttype);
	    ttype = 0;
	} else {
	    (void) fprintf(stderr,
			   "%s: can't initialize terminal type %s (error %d)\n",
			   _nc_progname, ttype, errret);
	    ttype = 0;
	}
	ttype = askuser(ttype);
    }
#if BROKEN_LINKER
    tgetflag("am");		/* force lib_termcap.o to be linked for 'ospeed' */
#endif
    return (ttype);
}

/**************************************************************************
 *
 * Main sequence
 *
 **************************************************************************/

/*
 * Convert the obsolete argument forms into something that getopt can handle.
 * This means that -e, -i and -k get default arguments supplied for them.
 */
static void
obsolete(char **argv)
{
    for (; *argv; ++argv) {
	char *parm = argv[0];

	if (parm[0] == '-' && parm[1] == '\0') {
	    argv[0] = strdup("-q");
	    continue;
	}

	if ((parm[0] != '-')
	    || (argv[1] && argv[1][0] != '-')
	    || (parm[1] != 'e' && parm[1] != 'i' && parm[1] != 'k')
	    || (parm[2] != '\0'))
	    continue;
	switch (argv[0][1]) {
	case 'e':
	    argv[0] = strdup("-e^H");
	    break;
	case 'i':
	    argv[0] = strdup("-i^C");
	    break;
	case 'k':
	    argv[0] = strdup("-k^U");
	    break;
	}
    }
}

static void
print_shell_commands(const char *ttype)
{
    const char *p;
    int len;
    char *var;
    char *leaf;
    /*
     * Figure out what shell we're using.  A hack, we look for an
     * environmental variable SHELL ending in "csh".
     */
    if ((var = getenv("SHELL")) != 0
	&& ((len = (int) strlen(leaf = _nc_basename(var))) >= 3)
	&& !strcmp(leaf + len - 3, "csh"))
	p = "set noglob;\nsetenv TERM %s;\nunset noglob;\n";
    else
	p = "TERM=%s;\n";
    (void) printf(p, ttype);
}

static void
usage(void)
{
#define SKIP(s)			/* nothing */
#define KEEP(s) s "\n"
    static const char msg[] =
    {
	KEEP("")
	KEEP("Options:")
	SKIP("  -a arpanet  (obsolete)")
	KEEP("  -c          set control characters")
	SKIP("  -d dialup   (obsolete)")
	KEEP("  -e ch       erase character")
	KEEP("  -I          no initialization strings")
	KEEP("  -i ch       interrupt character")
	KEEP("  -k ch       kill character")
	KEEP("  -m mapping  map identifier to type")
	SKIP("  -p plugboard (obsolete)")
	KEEP("  -Q          do not output control key settings")
	KEEP("  -q          display term only, do no changes")
	KEEP("  -r          display term on stderr")
	SKIP("  -S          (obsolete)")
	KEEP("  -s          output TERM set command")
	KEEP("  -V          print curses-version")
	KEEP("  -w          set window-size")
	KEEP("")
	KEEP("If neither -c/-w are given, both are assumed.")
    };
#undef KEEP
#undef SKIP
    (void) fprintf(stderr, "Usage: %s [options] [terminal]\n", _nc_progname);
    fputs(msg, stderr);
    ExitProgram(EXIT_FAILURE);
    /* NOTREACHED */
}

static char
arg_to_char(void)
{
    return (char) ((optarg[0] == '^' && optarg[1] != '\0')
		   ? ((optarg[1] == '?') ? '\177' : CTRL(optarg[1]))
		   : optarg[0]);
}

int
main(int argc, char **argv)
{
    int ch, noinit, noset, quiet, Sflag, sflag, showterm;
    const char *ttype;
    int terasechar = -1;	/* new erase character */
    int intrchar = -1;		/* new interrupt character */
    int tkillchar = -1;		/* new kill character */
    int my_fd;
    bool opt_c = FALSE;		/* set control-chars */
    bool opt_w = FALSE;		/* set window-size */
    TTY mode, oldmode;

    _nc_progname = _nc_rootname(*argv);

    if (pledge("stdio rpath wpath tty", NULL) == -1)
	err("pledge: %s", strerror(errno));

    obsolete(argv);
    noinit = noset = quiet = Sflag = sflag = showterm = 0;
    while ((ch = getopt(argc, argv, "a:cd:e:Ii:k:m:np:qQrSsVw")) != -1) {
	switch (ch) {
	case 'c':		/* set control-chars */
	    opt_c = TRUE;
	    break;
	case 'a':		/* OBSOLETE: map identifier to type */
	    add_mapping("arpanet", optarg);
	    break;
	case 'd':		/* OBSOLETE: map identifier to type */
	    add_mapping("dialup", optarg);
	    break;
	case 'e':		/* erase character */
	    terasechar = arg_to_char();
	    break;
	case 'I':		/* no initialization strings */
	    noinit = 1;
	    break;
	case 'i':		/* interrupt character */
	    intrchar = arg_to_char();
	    break;
	case 'k':		/* kill character */
	    tkillchar = arg_to_char();
	    break;
	case 'm':		/* map identifier to type */
	    add_mapping(0, optarg);
	    break;
	case 'n':		/* OBSOLETE: set new tty driver */
	    break;
	case 'p':		/* OBSOLETE: map identifier to type */
	    add_mapping("plugboard", optarg);
	    break;
	case 'Q':		/* don't output control key settings */
	    quiet = 1;
	    break;
	case 'q':		/* display term only */
	    noset = 1;
	    break;
	case 'r':		/* display term on stderr */
	    showterm = 1;
	    break;
	case 'S':		/* OBSOLETE: output TERM & TERMCAP */
	    Sflag = 1;
	    break;
	case 's':		/* output TERM set command */
	    sflag = 1;
	    break;
	case 'V':		/* print curses-version */
	    puts(curses_version());
	    ExitProgram(EXIT_SUCCESS);
	case 'w':		/* set window-size */
	    opt_w = TRUE;
	    break;
	case '?':
	default:
	    usage();
	}
    }

    argc -= optind;
    argv += optind;

    if (argc > 1)
	usage();

    if (!opt_c && !opt_w)
	opt_c = opt_w = TRUE;

    my_fd = save_tty_settings(&mode, TRUE);
    oldmode = mode;
#ifdef TERMIOS
    ospeed = (NCURSES_OSPEED) cfgetospeed(&mode);
#elif defined(EXP_WIN32_DRIVER)
    ospeed = 0;
#else
    ospeed = (NCURSES_OSPEED) mode.sg_ospeed;
#endif

    if (same_program(_nc_progname, PROG_RESET)) {
	reset_start(stderr, TRUE, FALSE);
	reset_tty_settings(my_fd, &mode, noset);
    } else {
	reset_start(stderr, FALSE, TRUE);
    }

    ttype = get_termcap_entry(my_fd, *argv);

    if (!noset) {
#if HAVE_SIZECHANGE
	if (opt_w) {
	    set_window_size(my_fd, &lines, &columns);
	}
#endif
	if (opt_c) {
	    set_control_chars(&mode, terasechar, intrchar, tkillchar);
	    set_conversions(&mode);

	    if (!noinit) {
		if (send_init_strings(my_fd, &oldmode)) {
		    (void) putc('\r', stderr);
		    (void) fflush(stderr);
		    (void) napms(1000);		/* Settle the terminal. */
		}
	    }

	    update_tty_settings(&oldmode, &mode);
	}
    }

    if (noset) {
	(void) printf("%s\n", ttype);
    } else {
	if (showterm)
	    (void) fprintf(stderr, "Terminal type is %s.\n", ttype);
	/*
	 * If erase, kill and interrupt characters could have been
	 * modified and not -Q, display the changes.
	 */
	if (!quiet) {
	    print_tty_chars(&oldmode, &mode);
	}
    }

    if (Sflag)
	err("The -S option is not supported under terminfo.");

    if (sflag) {
	print_shell_commands(ttype);
    }

    ExitProgram(EXIT_SUCCESS);
}