aboutsummaryrefslogtreecommitdiff
path: root/progs/tset.c
diff options
context:
space:
mode:
Diffstat (limited to 'progs/tset.c')
-rw-r--r--progs/tset.c1305
1 files changed, 1305 insertions, 0 deletions
diff --git a/progs/tset.c b/progs/tset.c
new file mode 100644
index 000000000000..3d6091224979
--- /dev/null
+++ b/progs/tset.c
@@ -0,0 +1,1305 @@
+/****************************************************************************
+ * Copyright (c) 1998-2006,2007 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 *
+ ****************************************************************************/
+
+/*
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#define USE_LIBTINFO
+#define __INTERNAL_CAPS_VISIBLE /* we need to see has_hardware_tabs */
+#include <progs.priv.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <termcap.h>
+#include <fcntl.h>
+
+#if HAVE_GETTTYNAM && HAVE_TTYENT_H
+#include <ttyent.h>
+#endif
+#ifdef NeXT
+char *ttyname(int fd);
+#endif
+
+/* this is just to stifle a missing-prototype warning */
+#ifdef linux
+# include <sys/ioctl.h>
+#endif
+
+#if NEED_PTEM_H
+/* they neglected to define struct winsize in termios.h -- it's only
+ in termio.h */
+#include <sys/stream.h>
+#include <sys/ptem.h>
+#endif
+
+#include <dump_entry.h>
+#include <transform.h>
+
+MODULE_ID("$Id: tset.c,v 1.70 2007/10/13 22:22:04 tom Exp $")
+
+extern char **environ;
+
+#undef CTRL
+#define CTRL(x) ((x) & 0x1f)
+
+const char *_nc_progname = "tset";
+
+static TTY mode, oldmode, original;
+
+static bool opt_c; /* set control-chars */
+static bool opt_w; /* set window-size */
+
+static bool can_restore = FALSE;
+static bool isreset = FALSE; /* invoked as reset */
+static int terasechar = -1; /* new erase character */
+static int intrchar = -1; /* new interrupt character */
+static int tkillchar = -1; /* new kill character */
+static int tlines, tcolumns; /* window size */
+
+#define LOWERCASE(c) ((isalpha(UChar(c)) && isupper(UChar(c))) ? tolower(UChar(c)) : (c))
+
+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 void
+exit_error(void)
+{
+ if (can_restore)
+ SET_TTY(STDERR_FILENO, &original);
+ (void) fprintf(stderr, "\n");
+ fflush(stderr);
+ ExitProgram(EXIT_FAILURE);
+ /* NOTREACHED */
+}
+
+static 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 void
+failed(const char *msg)
+{
+ char temp[BUFSIZ];
+ unsigned len = strlen(_nc_progname) + 2;
+
+ if (len < sizeof(temp) - 12) {
+ strcpy(temp, _nc_progname);
+ strcat(temp, ": ");
+ } else {
+ strcpy(temp, "tset: ");
+ }
+ perror(strncat(temp, msg, sizeof(temp) - strlen(temp) - 2));
+ exit_error();
+ /* NOTREACHED */
+}
+
+static void
+cat(char *file)
+{
+ FILE *fp;
+ size_t nr;
+ char buf[BUFSIZ];
+
+ if ((fp = fopen(file, "r")) == 0)
+ failed(file);
+
+ while ((nr = fread(buf, sizeof(char), sizeof(buf), fp)) != 0)
+ if (fwrite(buf, sizeof(char), nr, stderr) != nr)
+ failed("write to stderr");
+ fclose(fp);
+}
+
+static int
+outc(int c)
+{
+ return putc(c, stderr);
+}
+
+/* Prompt the user for a terminal type. */
+static const char *
+askuser(const char *dflt)
+{
+ static char answer[256];
+ char *p;
+
+ /* 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 (;;) {
+ 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;
+
+typedef struct speeds {
+ const char *string;
+ int speed;
+} SPEEDS;
+
+static const SPEEDS speeds[] =
+{
+ {"0", B0},
+ {"50", B50},
+ {"75", B75},
+ {"110", B110},
+ {"134", B134},
+ {"134.5", B134},
+ {"150", B150},
+ {"200", B200},
+ {"300", B300},
+ {"600", B600},
+ {"1200", B1200},
+ {"1800", B1800},
+ {"2400", B2400},
+ {"4800", B4800},
+ {"9600", B9600},
+ /* sgttyb may define up to this point */
+#ifdef B19200
+ {"19200", B19200},
+#endif
+#ifdef B38400
+ {"38400", B38400},
+#endif
+#ifdef B19200
+ {"19200", B19200},
+#endif
+#ifdef B38400
+ {"38400", B38400},
+#endif
+#ifdef B19200
+ {"19200", B19200},
+#else
+#ifdef EXTA
+ {"19200", EXTA},
+#endif
+#endif
+#ifdef B38400
+ {"38400", B38400},
+#else
+#ifdef EXTB
+ {"38400", EXTB},
+#endif
+#endif
+#ifdef B57600
+ {"57600", B57600},
+#endif
+#ifdef B115200
+ {"115200", B115200},
+#endif
+#ifdef B230400
+ {"230400", B230400},
+#endif
+#ifdef B460800
+ {"460800", B460800},
+#endif
+ {(char *) 0, 0}
+};
+
+static int
+tbaudrate(char *rate)
+{
+ const SPEEDS *sp;
+ int found = FALSE;
+
+ /* The baudrate number can be preceded by a 'B', which is ignored. */
+ if (*rate == 'B')
+ ++rate;
+
+ for (sp = speeds; sp->string; ++sp) {
+ if (!CaselessCmp(rate, sp->string)) {
+ found = TRUE;
+ break;
+ }
+ }
+ if (!found)
+ 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 = (MAP *) malloc(sizeof(MAP));
+ if (copy == 0 || mapp == 0)
+ failed("malloc");
+ 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);
+ }
+
+ if (arg == (char *) 0) /* Non-optional type. */
+ goto badmopt;
+
+ 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;
+ }
+#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 = (ospeed == mapp->speed);
+ break;
+ case GE:
+ match = (ospeed >= mapp->speed);
+ break;
+ case GT:
+ match = (ospeed > mapp->speed);
+ break;
+ case LE:
+ match = (ospeed <= mapp->speed);
+ break;
+ case LT:
+ match = (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(char *userarg)
+{
+ int errret;
+ char *p;
+ const char *ttype;
+#if HAVE_GETTTYNAM
+ struct ttyent *t;
+#else
+ FILE *fp;
+#endif
+ char *ttypath;
+
+ if (userarg) {
+ ttype = userarg;
+ goto found;
+ }
+
+ /* Try the environment. */
+ if ((ttype = getenv("TERM")) != 0)
+ goto map;
+
+ if ((ttypath = ttyname(STDERR_FILENO)) != 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 */
+ }
+
+ /* 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], 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, STDOUT_FILENO, &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);
+}
+
+/**************************************************************************
+ *
+ * Mode-setting logic
+ *
+ **************************************************************************/
+
+/* some BSD systems have these built in, some systems are missing
+ * one or more definitions. The safest solution is to override unless the
+ * commonly-altered ones are defined.
+ */
+#if !(defined(CERASE) && defined(CINTR) && defined(CKILL) && defined(CQUIT))
+#undef CEOF
+#undef CERASE
+#undef CINTR
+#undef CKILL
+#undef CLNEXT
+#undef CRPRNT
+#undef CQUIT
+#undef CSTART
+#undef CSTOP
+#undef CSUSP
+#endif
+
+/* control-character defaults */
+#ifndef CEOF
+#define CEOF CTRL('D')
+#endif
+#ifndef CERASE
+#define CERASE CTRL('H')
+#endif
+#ifndef CINTR
+#define CINTR 127 /* ^? */
+#endif
+#ifndef CKILL
+#define CKILL CTRL('U')
+#endif
+#ifndef CLNEXT
+#define CLNEXT CTRL('v')
+#endif
+#ifndef CRPRNT
+#define CRPRNT CTRL('r')
+#endif
+#ifndef CQUIT
+#define CQUIT CTRL('\\')
+#endif
+#ifndef CSTART
+#define CSTART CTRL('Q')
+#endif
+#ifndef CSTOP
+#define CSTOP CTRL('S')
+#endif
+#ifndef CSUSP
+#define CSUSP CTRL('Z')
+#endif
+
+#if defined(_POSIX_VDISABLE)
+#define DISABLED(val) (((_POSIX_VDISABLE != -1) \
+ && ((val) == _POSIX_VDISABLE)) \
+ || ((val) <= 0))
+#else
+#define DISABLED(val) ((int)(val) <= 0)
+#endif
+
+#define CHK(val, dft) (DISABLED(val) ? dft : val)
+
+static bool set_tabs(void);
+
+/*
+ * Reset the terminal mode bits to a sensible state. Very useful after
+ * a child program dies in raw mode.
+ */
+static void
+reset_mode(void)
+{
+#ifdef TERMIOS
+ tcgetattr(STDERR_FILENO, &mode);
+#else
+ stty(STDERR_FILENO, &mode);
+#endif
+
+#ifdef TERMIOS
+#if defined(VDISCARD) && defined(CDISCARD)
+ mode.c_cc[VDISCARD] = CHK(mode.c_cc[VDISCARD], CDISCARD);
+#endif
+ mode.c_cc[VEOF] = CHK(mode.c_cc[VEOF], CEOF);
+ mode.c_cc[VERASE] = CHK(mode.c_cc[VERASE], CERASE);
+#if defined(VFLUSH) && defined(CFLUSH)
+ mode.c_cc[VFLUSH] = CHK(mode.c_cc[VFLUSH], CFLUSH);
+#endif
+ mode.c_cc[VINTR] = CHK(mode.c_cc[VINTR], CINTR);
+ mode.c_cc[VKILL] = CHK(mode.c_cc[VKILL], CKILL);
+#if defined(VLNEXT) && defined(CLNEXT)
+ mode.c_cc[VLNEXT] = CHK(mode.c_cc[VLNEXT], CLNEXT);
+#endif
+ mode.c_cc[VQUIT] = CHK(mode.c_cc[VQUIT], CQUIT);
+#if defined(VREPRINT) && defined(CRPRNT)
+ mode.c_cc[VREPRINT] = CHK(mode.c_cc[VREPRINT], CRPRNT);
+#endif
+#if defined(VSTART) && defined(CSTART)
+ mode.c_cc[VSTART] = CHK(mode.c_cc[VSTART], CSTART);
+#endif
+#if defined(VSTOP) && defined(CSTOP)
+ mode.c_cc[VSTOP] = CHK(mode.c_cc[VSTOP], CSTOP);
+#endif
+#if defined(VSUSP) && defined(CSUSP)
+ mode.c_cc[VSUSP] = CHK(mode.c_cc[VSUSP], CSUSP);
+#endif
+#if defined(VWERASE) && defined(CWERASE)
+ mode.c_cc[VWERASE] = CHK(mode.c_cc[VWERASE], CWERASE);
+#endif
+
+ mode.c_iflag &= ~(IGNBRK | PARMRK | INPCK | ISTRIP | INLCR | IGNCR
+#ifdef IUCLC
+ | IUCLC
+#endif
+#ifdef IXANY
+ | IXANY
+#endif
+ | IXOFF);
+
+ mode.c_iflag |= (BRKINT | IGNPAR | ICRNL | IXON
+#ifdef IMAXBEL
+ | IMAXBEL
+#endif
+ );
+
+ mode.c_oflag &= ~(0
+#ifdef OLCUC
+ | OLCUC
+#endif
+#ifdef OCRNL
+ | OCRNL
+#endif
+#ifdef ONOCR
+ | ONOCR
+#endif
+#ifdef ONLRET
+ | ONLRET
+#endif
+#ifdef OFILL
+ | OFILL
+#endif
+#ifdef OFDEL
+ | OFDEL
+#endif
+#ifdef NLDLY
+ | NLDLY
+#endif
+#ifdef CRDLY
+ | CRDLY
+#endif
+#ifdef TABDLY
+ | TABDLY
+#endif
+#ifdef BSDLY
+ | BSDLY
+#endif
+#ifdef VTDLY
+ | VTDLY
+#endif
+#ifdef FFDLY
+ | FFDLY
+#endif
+ );
+
+ mode.c_oflag |= (OPOST
+#ifdef ONLCR
+ | ONLCR
+#endif
+ );
+
+ mode.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD | CLOCAL);
+ mode.c_cflag |= (CS8 | CREAD);
+ mode.c_lflag &= ~(ECHONL | NOFLSH
+#ifdef TOSTOP
+ | TOSTOP
+#endif
+#ifdef ECHOPTR
+ | ECHOPRT
+#endif
+#ifdef XCASE
+ | XCASE
+#endif
+ );
+
+ mode.c_lflag |= (ISIG | ICANON | ECHO | ECHOE | ECHOK
+#ifdef ECHOCTL
+ | ECHOCTL
+#endif
+#ifdef ECHOKE
+ | ECHOKE
+#endif
+ );
+#endif
+
+ SET_TTY(STDERR_FILENO, &mode);
+}
+
+/*
+ * Returns a "good" value for the erase character. This is loosely based on
+ * the BSD4.4 logic.
+ */
+#ifdef TERMIOS
+static int
+default_erase(void)
+{
+ int result;
+
+ if (over_strike
+ && key_backspace != 0
+ && strlen(key_backspace) == 1)
+ result = key_backspace[0];
+ else
+ result = CERASE;
+
+ return result;
+}
+#endif
+
+/*
+ * Update the values of the erase, interrupt, and kill characters in 'mode'.
+ *
+ * SVr4 tset (e.g., Solaris 2.5) only modifies the intr, quit or erase
+ * characters if they're unset, or if we specify them as options. This differs
+ * from BSD 4.4 tset, which always sets erase.
+ */
+static void
+set_control_chars(void)
+{
+#ifdef TERMIOS
+ if (DISABLED(mode.c_cc[VERASE]) || terasechar >= 0)
+ mode.c_cc[VERASE] = terasechar >= 0 ? terasechar : default_erase();
+
+ if (DISABLED(mode.c_cc[VINTR]) || intrchar >= 0)
+ mode.c_cc[VINTR] = intrchar >= 0 ? intrchar : CINTR;
+
+ if (DISABLED(mode.c_cc[VKILL]) || tkillchar >= 0)
+ mode.c_cc[VKILL] = tkillchar >= 0 ? tkillchar : CKILL;
+#endif
+}
+
+/*
+ * Set up various conversions in 'mode', including parity, tabs, returns,
+ * echo, and case, according to the termcap entry. If the program we're
+ * running was named with a leading upper-case character, map external
+ * uppercase to internal lowercase.
+ */
+static void
+set_conversions(void)
+{
+#ifdef __OBSOLETE__
+ /*
+ * Conversion logic for some *really* ancient terminal glitches,
+ * not supported in terminfo. Left here for succeeding generations
+ * to marvel at.
+ */
+ if (tgetflag("UC")) {
+#ifdef IUCLC
+ mode.c_iflag |= IUCLC;
+ mode.c_oflag |= OLCUC;
+#endif
+ } else if (tgetflag("LC")) {
+#ifdef IUCLC
+ mode.c_iflag &= ~IUCLC;
+ mode.c_oflag &= ~OLCUC;
+#endif
+ }
+ mode.c_iflag &= ~(PARMRK | INPCK);
+ mode.c_lflag |= ICANON;
+ if (tgetflag("EP")) {
+ mode.c_cflag |= PARENB;
+ mode.c_cflag &= ~PARODD;
+ }
+ if (tgetflag("OP")) {
+ mode.c_cflag |= PARENB;
+ mode.c_cflag |= PARODD;
+ }
+#endif /* __OBSOLETE__ */
+
+#ifdef TERMIOS
+#ifdef ONLCR
+ mode.c_oflag |= ONLCR;
+#endif
+ mode.c_iflag |= ICRNL;
+ mode.c_lflag |= ECHO;
+#ifdef OXTABS
+ mode.c_oflag |= OXTABS;
+#endif /* OXTABS */
+
+ /* test used to be tgetflag("NL") */
+ if (newline != (char *) 0 && newline[0] == '\n' && !newline[1]) {
+ /* Newline, not linefeed. */
+#ifdef ONLCR
+ mode.c_oflag &= ~ONLCR;
+#endif
+ mode.c_iflag &= ~ICRNL;
+ }
+#ifdef __OBSOLETE__
+ if (tgetflag("HD")) /* Half duplex. */
+ mode.c_lflag &= ~ECHO;
+#endif /* __OBSOLETE__ */
+#ifdef OXTABS
+ /* test used to be tgetflag("pt") */
+ if (has_hardware_tabs) /* Print tabs. */
+ mode.c_oflag &= ~OXTABS;
+#endif /* OXTABS */
+ mode.c_lflag |= (ECHOE | ECHOK);
+#endif
+}
+
+/* Output startup string. */
+static void
+set_init(void)
+{
+ char *p;
+ bool settle;
+
+#ifdef __OBSOLETE__
+ if (pad_char != (char *) 0) /* Get/set pad character. */
+ PC = pad_char[0];
+#endif /* OBSOLETE */
+
+#ifdef TAB3
+ if (oldmode.c_oflag & (TAB3 | ONLCR | OCRNL | ONLRET)) {
+ oldmode.c_oflag &= (TAB3 | ONLCR | OCRNL | ONLRET);
+ SET_TTY(STDERR_FILENO, &oldmode);
+ }
+#endif
+ settle = set_tabs();
+
+ if (isreset) {
+ if ((p = reset_1string) != 0) {
+ tputs(p, 0, outc);
+ settle = TRUE;
+ }
+ if ((p = reset_2string) != 0) {
+ tputs(p, 0, outc);
+ settle = TRUE;
+ }
+ /* What about rf, rs3, as per terminfo man page? */
+ /* also might be nice to send rmacs, rmul, rmm */
+ if ((p = reset_file) != 0
+ || (p = init_file) != 0) {
+ cat(p);
+ settle = TRUE;
+ }
+ }
+
+ if (settle) {
+ (void) putc('\r', stderr);
+ (void) fflush(stderr);
+ (void) napms(1000); /* Settle the terminal. */
+ }
+}
+
+/*
+ * Set the hardware tabs on the terminal, using the ct (clear all tabs),
+ * st (set one tab) and ch (horizontal cursor addressing) capabilities.
+ * This is done before if and is, so they can patch in case we blow this.
+ * Return TRUE if we set any tab stops, FALSE if not.
+ */
+static bool
+set_tabs(void)
+{
+ if (set_tab && clear_all_tabs) {
+ int c;
+
+ (void) putc('\r', stderr); /* Force to left margin. */
+ tputs(clear_all_tabs, 0, outc);
+
+ for (c = 8; c < tcolumns; c += 8) {
+ /* Get to the right column. In BSD tset, this
+ * used to try a bunch of half-clever things
+ * with cup and hpa, for an average saving of
+ * somewhat less than two character times per
+ * tab stop, less than .01 sec at 2400cps. We
+ * lost all this cruft because it seemed to be
+ * introducing some odd bugs.
+ * -----------12345678----------- */
+ (void) fputs(" ", stderr);
+ tputs(set_tab, 0, outc);
+ }
+ putc('\r', stderr);
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+/**************************************************************************
+ *
+ * Main sequence
+ *
+ **************************************************************************/
+
+/*
+ * Tell the user if a control key has been changed from the default value.
+ */
+#ifdef TERMIOS
+static void
+report(const char *name, int which, unsigned def)
+{
+ unsigned older, newer;
+ char *p;
+
+ newer = mode.c_cc[which];
+ older = oldmode.c_cc[which];
+
+ if (older == newer && older == def)
+ return;
+
+ (void) fprintf(stderr, "%s %s ", name, older == newer ? "is" : "set to");
+
+ if (DISABLED(newer))
+ (void) fprintf(stderr, "undef.\n");
+ /*
+ * Check 'delete' before 'backspace', since the key_backspace value
+ * is ambiguous.
+ */
+ else if (newer == 0177)
+ (void) fprintf(stderr, "delete.\n");
+ else if ((p = key_backspace) != 0
+ && newer == (unsigned char) p[0]
+ && p[1] == '\0')
+ (void) fprintf(stderr, "backspace.\n");
+ else if (newer < 040) {
+ newer ^= 0100;
+ (void) fprintf(stderr, "control-%c (^%c).\n", UChar(newer), UChar(newer));
+ } else
+ (void) fprintf(stderr, "%c.\n", UChar(newer));
+}
+#endif
+
+/*
+ * 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
+usage(void)
+{
+ static const char *tbl[] =
+ {
+ ""
+ ,"Options:"
+ ," -c set control characters"
+ ," -e ch erase character"
+ ," -I no initialization strings"
+ ," -i ch interrupt character"
+ ," -k ch kill character"
+ ," -m mapping map identifier to type"
+ ," -Q do not output control key settings"
+ ," -r display term on stderr"
+ ," -s output TERM set command"
+ ," -V print curses-version"
+ ," -w set window-size"
+ };
+ unsigned n;
+ (void) fprintf(stderr, "Usage: %s [options] [terminal]\n", _nc_progname);
+ for (n = 0; n < sizeof(tbl) / sizeof(tbl[0]); ++n)
+ fprintf(stderr, "%s\n", tbl[n]);
+ exit_error();
+ /* NOTREACHED */
+}
+
+static char
+arg_to_char(void)
+{
+ return (optarg[0] == '^' && optarg[1] != '\0')
+ ? ((optarg[1] == '?') ? '\177' : CTRL(optarg[1]))
+ : optarg[0];
+}
+
+int
+main(int argc, char **argv)
+{
+#if defined(TIOCGWINSZ) && defined(TIOCSWINSZ)
+ struct winsize win;
+#endif
+ int ch, noinit, noset, quiet, Sflag, sflag, showterm;
+ const char *p;
+ const char *ttype;
+
+ obsolete(argv);
+ noinit = noset = quiet = Sflag = sflag = showterm = 0;
+ while ((ch = getopt(argc, argv, "a:cd:e:Ii:k:m:np:qQSrsVw")) != -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();
+ }
+ }
+
+ _nc_progname = _nc_rootname(*argv);
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 1)
+ usage();
+
+ if (!opt_c && !opt_w)
+ opt_c = opt_w = TRUE;
+
+ if (GET_TTY(STDERR_FILENO, &mode) < 0)
+ failed("standard error");
+ can_restore = TRUE;
+ original = oldmode = mode;
+#ifdef TERMIOS
+ ospeed = cfgetospeed(&mode);
+#else
+ ospeed = mode.sg_ospeed;
+#endif
+
+ if (!strcmp(_nc_progname, PROG_RESET)) {
+ isreset = TRUE;
+ reset_mode();
+ }
+
+ ttype = get_termcap_entry(*argv);
+
+ if (!noset) {
+ tcolumns = columns;
+ tlines = lines;
+
+#if defined(TIOCGWINSZ) && defined(TIOCSWINSZ)
+ if (opt_w) {
+ /* Set window size */
+ (void) ioctl(STDERR_FILENO, TIOCGWINSZ, &win);
+ if (win.ws_row == 0 && win.ws_col == 0 &&
+ tlines > 0 && tcolumns > 0) {
+ win.ws_row = tlines;
+ win.ws_col = tcolumns;
+ (void) ioctl(STDERR_FILENO, TIOCSWINSZ, &win);
+ }
+ }
+#endif
+ if (opt_c) {
+ set_control_chars();
+ set_conversions();
+
+ if (!noinit)
+ set_init();
+
+ /* Set the modes if they've changed. */
+ if (memcmp(&mode, &oldmode, sizeof(mode))) {
+ SET_TTY(STDERR_FILENO, &mode);
+ }
+ }
+ }
+
+ /* Get the terminal name from the entry. */
+ ttype = _nc_first_name(cur_term->type.term_names);
+
+ 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.
+ */
+#ifdef TERMIOS
+ if (!quiet) {
+ report("Erase", VERASE, CERASE);
+ report("Kill", VKILL, CKILL);
+ report("Interrupt", VINTR, CINTR);
+ }
+#endif
+ }
+
+ if (Sflag)
+ err("The -S option is not supported under terminfo.");
+
+ if (sflag) {
+ 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 = 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);
+ }
+
+ ExitProgram(EXIT_SUCCESS);
+}