ncurses 5.9 - patch 20131012
[ncurses.git] / progs / tset.c
index 392aac0ef55184f1b2db2f5b76fe36884a9da629..21203e3de07468618ababe2e4d1f38ef961d1a18 100644 (file)
@@ -1,5 +1,5 @@
 /****************************************************************************
- * Copyright (c) 1998,1999,2000 Free Software Foundation, Inc.              *
+ * Copyright (c) 1998-2012,2013 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            *
 /****************************************************************************
  *  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
  *
  * 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
+ * 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.
  *
@@ -72,6 +85,7 @@
  * SUCH DAMAGE.
  */
 
+#define USE_LIBTINFO
 #define __INTERNAL_CAPS_VISIBLE        /* we need to see has_hardware_tabs */
 #include <progs.priv.h>
 
 char *ttyname(int fd);
 #endif
 
-/* this is just to stifle a missing-prototype warning */
-#ifdef linux
-# include <sys/ioctl.h>
+#if HAVE_SIZECHANGE
+# if !defined(sun) || !TERMIOS
+#  if HAVE_SYS_IOCTL_H
+#   include <sys/ioctl.h>
+#  endif
+# endif
 #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>
+#include <sys/stream.h>
+#include <sys/ptem.h>
 #endif
 
-#include <curses.h>            /* for bool typedef */
 #include <dump_entry.h>
+#include <transform.h>
 
-MODULE_ID("$Id: tset.c,v 0.41 2000/03/12 00:03:00 tom Exp $")
+MODULE_ID("$Id: tset.c,v 1.92 2013/10/12 22:17:20 tom Exp $")
 
+/*
+ * SCO defines TIOCGSIZE and the corresponding struct.  Other systems (SunOS,
+ * Solaris, IRIX) define TIOCGWINSZ and struct winsize.
+ */
+#ifdef TIOCGSIZE
+# define IOCTL_GET_WINSIZE TIOCGSIZE
+# define IOCTL_SET_WINSIZE TIOCSSIZE
+# define STRUCT_WINSIZE struct ttysize
+# define WINSIZE_ROWS(n) n.ts_lines
+# define WINSIZE_COLS(n) n.ts_cols
+#else
+# ifdef TIOCGWINSZ
+#  define IOCTL_GET_WINSIZE TIOCGWINSZ
+#  define IOCTL_SET_WINSIZE TIOCSWINSZ
+#  define STRUCT_WINSIZE struct winsize
+#  define WINSIZE_ROWS(n) n.ws_row
+#  define WINSIZE_COLS(n) n.ws_col
+# endif
+#endif
+
+#ifndef environ
 extern char **environ;
+#endif
 
 #undef CTRL
 #define CTRL(x)        ((x) & 0x1f)
 
+static void failed(const char *) GCC_NORETURN;
+static void exit_error(void) GCC_NORETURN;
+static void err(const char *,...) GCC_NORETURN;
+
 const char *_nc_progname = "tset";
 
-static TTY mode, oldmode;
+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 isreset;            /* invoked as reset */
 static int tkillchar = -1;     /* new kill character */
+
+#if HAVE_SIZECHANGE
 static int tlines, tcolumns;   /* window size */
+#endif
 
-#define LOWERCASE(c) ((isalpha(c) && isupper(c)) ? tolower(c) : (c))
+#define LOWERCASE(c) ((isalpha(UChar(c)) && isupper(UChar(c))) ? tolower(UChar(c)) : (c))
 
 static int
 CaselessCmp(const char *a, const char *b)
@@ -133,16 +183,26 @@ CaselessCmp(const char *a, const char *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, "tset: ");
+    (void) fprintf(stderr, "%s: ", _nc_progname);
     (void) vfprintf(stderr, fmt, ap);
     va_end(ap);
-    (void) fprintf(stderr, "\n");
-    exit(EXIT_FAILURE);
+    exit_error();
     /* NOTREACHED */
 }
 
@@ -150,26 +210,33 @@ static void
 failed(const char *msg)
 {
     char temp[BUFSIZ];
-    perror(strncat(strcpy(temp, "tset: "), msg, sizeof(temp) - 10));
-    exit(EXIT_FAILURE);
+    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));
+    }
+    perror(strncat(temp, msg, sizeof(temp) - strlen(temp) - 2));
+    exit_error();
     /* NOTREACHED */
 }
 
 static void
 cat(char *file)
 {
-    register int fd, nr, nw;
+    FILE *fp;
+    size_t nr;
     char buf[BUFSIZ];
 
-    if ((fd = open(file, O_RDONLY, 0)) < 0)
+    if ((fp = fopen(file, "r")) == 0)
        failed(file);
 
-    while ((nr = read(fd, buf, sizeof(buf))) > 0)
-       if ((nw = write(STDERR_FILENO, buf, (size_t) nr)) == -1)
-           failed("write to stderr");
-    if (nr != 0)
-       failed(file);
-    (void) close(fd);
+    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
@@ -186,9 +253,11 @@ askuser(const char *dflt)
     char *p;
 
     /* We can get recalled; if so, don't continue uselessly. */
+    clearerr(stdin);
     if (feof(stdin) || ferror(stdin)) {
        (void) fprintf(stderr, "\n");
-       exit(EXIT_FAILURE);
+       exit_error();
+       /* NOTREACHED */
     }
     for (;;) {
        if (dflt)
@@ -199,8 +268,8 @@ askuser(const char *dflt)
 
        if (fgets(answer, sizeof(answer), stdin) == 0) {
            if (dflt == 0) {
-               (void) fprintf(stderr, "\n");
-               exit(EXIT_FAILURE);
+               exit_error();
+               /* NOTREACHED */
            }
            return (dflt);
        }
@@ -233,7 +302,7 @@ typedef struct map {
     const char *porttype;      /* Port type, or "" for any. */
     const char *type;          /* Terminal type to select. */
     int conditional;           /* Baud rate conditionals bitmask. */
-    speed_t speed;             /* Baud rate to compare against. */
+    int speed;                 /* Baud rate to compare against. */
 } MAP;
 
 static MAP *cur, *maplist;
@@ -260,10 +329,19 @@ static const SPEEDS speeds[] =
     {"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
@@ -328,9 +406,13 @@ add_mapping(const char *port, char *arg)
     char *base = 0;
 
     copy = strdup(arg);
-    mapp = malloc(sizeof(MAP));
+    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;
@@ -392,9 +474,6 @@ add_mapping(const char *port, char *arg)
        mapp->speed = tbaudrate(p);
     }
 
-    if (arg == (char *) 0)     /* Non-optional type. */
-       goto badmopt;
-
     mapp->type = arg;
 
     /* Terminate porttype, if specified. */
@@ -406,11 +485,15 @@ add_mapping(const char *port, char *arg)
        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);
+  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);
@@ -448,19 +531,19 @@ mapped(const char *type)
                match = TRUE;
                break;
            case EQ:
-               match = (ospeed == mapp->speed);
+               match = ((int) ospeed == mapp->speed);
                break;
            case GE:
-               match = (ospeed >= mapp->speed);
+               match = ((int) ospeed >= mapp->speed);
                break;
            case GT:
-               match = (ospeed > mapp->speed);
+               match = ((int) ospeed > mapp->speed);
                break;
            case LE:
-               match = (ospeed <= mapp->speed);
+               match = ((int) ospeed <= mapp->speed);
                break;
            case LT:
-               match = (ospeed < mapp->speed);
+               match = ((int) ospeed < mapp->speed);
                break;
            default:
                match = FALSE;
@@ -485,7 +568,7 @@ mapped(const char *type)
 static const char *
 get_termcap_entry(char *userarg)
 {
-    int rval, errret;
+    int errret;
     char *p;
     const char *ttype;
 #if HAVE_GETTTYNAM
@@ -505,10 +588,7 @@ get_termcap_entry(char *userarg)
        goto map;
 
     if ((ttypath = ttyname(STDERR_FILENO)) != 0) {
-       if ((p = strrchr(ttypath, '/')) != 0)
-           ++p;
-       else
-           p = ttypath;
+       p = _nc_basename(ttypath);
 #if HAVE_GETTTYNAM
        /*
         * We have the 4.3BSD library call getttynam(3); that means
@@ -527,7 +607,7 @@ get_termcap_entry(char *userarg)
 
            while (fgets(buffer, sizeof(buffer) - 1, fp) != 0) {
                for (s = buffer, t = d = 0; *s; s++) {
-                   if (isspace(*s))
+                   if (isspace(UChar(*s)))
                        *s = '\0';
                    else if (t == 0)
                        t = s;
@@ -555,7 +635,7 @@ get_termcap_entry(char *userarg)
      * 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 && *p != '/') {
+  found:if ((p = getenv("TERMCAP")) != 0 && !_nc_is_abs_path(p)) {
        /* 'unsetenv("TERMCAP")' is not portable.
         * The 'environ' array is better.
         */
@@ -581,16 +661,16 @@ get_termcap_entry(char *userarg)
            ttype = askuser(0);
     }
     /* Find the terminfo entry.  If it doesn't exist, ask the user. */
-    while ((rval = setupterm((NCURSES_CONST char *) ttype, STDOUT_FILENO,
-               &errret)) != OK) {
+    while (setupterm((NCURSES_CONST char *) ttype, STDOUT_FILENO, &errret)
+          != OK) {
        if (errret == 0) {
-           (void) fprintf(stderr, "tset: unknown terminal type %s\n",
-               ttype);
+           (void) fprintf(stderr, "%s: unknown terminal type %s\n",
+                          _nc_progname, ttype);
            ttype = 0;
        } else {
            (void) fprintf(stderr,
-               "tset: can't initialize terminal type %s (error %d)\n",
-               ttype, errret);
+                          "%s: can't initialize terminal type %s (error %d)\n",
+                          _nc_progname, ttype, errret);
            ttype = 0;
        }
        ttype = askuser(ttype);
@@ -608,8 +688,10 @@ get_termcap_entry(char *userarg)
  **************************************************************************/
 
 /* some BSD systems have these built in, some systems are missing
- * one or more definitions. The safest solution is to override.
+ * 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
@@ -620,20 +702,49 @@ get_termcap_entry(char *userarg)
 #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)   ((int)val <= 0 ? dft : val)
+#define CHK(val, dft)   (DISABLED(val) ? dft : val)
 
 static bool set_tabs(void);
 
@@ -681,86 +792,98 @@ reset_mode(void)
     mode.c_cc[VWERASE] = CHK(mode.c_cc[VWERASE], CWERASE);
 #endif
 
-    mode.c_iflag &= ~(IGNBRK | PARMRK | INPCK | ISTRIP | INLCR | IGNCR
+    mode.c_iflag &= ~((unsigned) (IGNBRK | PARMRK | INPCK | ISTRIP | INLCR | IGNCR
 #ifdef IUCLC
-       | IUCLC
+                                 | IUCLC
 #endif
 #ifdef IXANY
-       | IXANY
+                                 | IXANY
 #endif
-       | IXOFF);
+                                 | IXOFF));
 
     mode.c_iflag |= (BRKINT | IGNPAR | ICRNL | IXON
 #ifdef IMAXBEL
-       | IMAXBEL
+                    | IMAXBEL
 #endif
        );
 
-    mode.c_oflag &= ~(0
+    mode.c_oflag &= ~((unsigned) (0
 #ifdef OLCUC
-       | OLCUC
+                                 | OLCUC
 #endif
 #ifdef OCRNL
-       | OCRNL
+                                 | OCRNL
 #endif
 #ifdef ONOCR
-       | ONOCR
+                                 | ONOCR
 #endif
 #ifdef ONLRET
-       | ONLRET
+                                 | ONLRET
 #endif
 #ifdef OFILL
-       | OFILL
+                                 | OFILL
 #endif
 #ifdef OFDEL
-       | OFDEL
+                                 | OFDEL
 #endif
 #ifdef NLDLY
-       | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY
+                                 | 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
+                    | ONLCR
 #endif
        );
 
-    mode.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD | CLOCAL);
+    mode.c_cflag &= ~((unsigned) (CSIZE | CSTOPB | PARENB | PARODD | CLOCAL));
     mode.c_cflag |= (CS8 | CREAD);
-    mode.c_lflag &= ~(ECHONL | NOFLSH
+    mode.c_lflag &= ~((unsigned) (ECHONL | NOFLSH
 #ifdef TOSTOP
-       | TOSTOP
+                                 | TOSTOP
 #endif
 #ifdef ECHOPTR
-       | ECHOPRT
+                                 | ECHOPRT
 #endif
 #ifdef XCASE
-       | XCASE
+                                 | XCASE
 #endif
-       );
+                     ));
 
     mode.c_lflag |= (ISIG | ICANON | ECHO | ECHOE | ECHOK
 #ifdef ECHOCTL
-       | ECHOCTL
+                    | ECHOCTL
 #endif
 #ifdef ECHOKE
-       | ECHOKE
+                    | ECHOKE
 #endif
        );
 #endif
 
-#ifdef TERMIOS
-    tcsetattr(STDERR_FILENO, TCSADRAIN, &mode);
-#else
-    stty(STDERR_FILENO, &mode);
-#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)
 {
@@ -775,6 +898,7 @@ default_erase(void)
 
     return result;
 }
+#endif
 
 /*
  * Update the values of the erase, interrupt, and kill characters in 'mode'.
@@ -787,14 +911,23 @@ static void
 set_control_chars(void)
 {
 #ifdef TERMIOS
-    if (mode.c_cc[VERASE] == 0 || terasechar >= 0)
-       mode.c_cc[VERASE] = terasechar >= 0 ? terasechar : default_erase();
+    if (DISABLED(mode.c_cc[VERASE]) || terasechar >= 0) {
+       mode.c_cc[VERASE] = UChar((terasechar >= 0)
+                                 ? terasechar
+                                 : default_erase());
+    }
 
-    if (mode.c_cc[VINTR] == 0 || intrchar >= 0)
-       mode.c_cc[VINTR] = intrchar >= 0 ? intrchar : CINTR;
+    if (DISABLED(mode.c_cc[VINTR]) || intrchar >= 0) {
+       mode.c_cc[VINTR] = UChar((intrchar >= 0)
+                                ? intrchar
+                                : CINTR);
+    }
 
-    if (mode.c_cc[VKILL] == 0 || tkillchar >= 0)
-       mode.c_cc[VKILL] = tkillchar >= 0 ? tkillchar : CKILL;
+    if (DISABLED(mode.c_cc[VKILL]) || tkillchar >= 0) {
+       mode.c_cc[VKILL] = UChar((tkillchar >= 0)
+                                ? tkillchar
+                                : CKILL);
+    }
 #endif
 }
 
@@ -850,9 +983,9 @@ set_conversions(void)
     if (newline != (char *) 0 && newline[0] == '\n' && !newline[1]) {
        /* Newline, not linefeed. */
 #ifdef ONLCR
-       mode.c_oflag &= ~ONLCR;
+       mode.c_oflag &= ~((unsigned) ONLCR);
 #endif
-       mode.c_iflag &= ~ICRNL;
+       mode.c_iflag &= ~((unsigned) ICRNL);
     }
 #ifdef __OBSOLETE__
     if (tgetflag("HD"))                /* Half duplex. */
@@ -882,7 +1015,7 @@ set_init(void)
 #ifdef TAB3
     if (oldmode.c_oflag & (TAB3 | ONLCR | OCRNL | ONLRET)) {
        oldmode.c_oflag &= (TAB3 | ONLCR | OCRNL | ONLRET);
-       tcsetattr(STDERR_FILENO, TCSADRAIN, &oldmode);
+       SET_TTY(STDERR_FILENO, &oldmode);
     }
 #endif
     settle = set_tabs();
@@ -919,23 +1052,30 @@ set_init(void)
  * Return TRUE if we set any tab stops, FALSE if not.
  */
 static bool
-set_tabs()
+set_tabs(void)
 {
     if (set_tab && clear_all_tabs) {
        int c;
+       int lim =
+#if HAVE_SIZECHANGE
+       tcolumns
+#else
+       columns
+#endif
+        ;
 
        (void) putc('\r', stderr);      /* Force to left margin. */
        tputs(clear_all_tabs, 0, outc);
 
-       for (c = 8; c < tcolumns; c += 8) {
+       for (c = 8; c < lim; 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 that .01 sec at 2400cps. We
+            * tab stop, less than .01 sec at 2400cps. We
             * lost all this cruft because it seemed to be
             * introducing some odd bugs.
-            * ----------12345678----------- */
+            * -----------12345678----------- */
            (void) fputs("        ", stderr);
            tputs(set_tab, 0, outc);
        }
@@ -954,10 +1094,10 @@ set_tabs()
 /*
  * 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)
 {
-#ifdef TERMIOS
     unsigned older, newer;
     char *p;
 
@@ -969,23 +1109,25 @@ report(const char *name, int which, unsigned def)
 
     (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.
      */
-    if (newer == 0177)
+    else if (newer == 0177)
        (void) fprintf(stderr, "delete.\n");
     else if ((p = key_backspace) != 0
-           && newer == (unsigned char) p[0]
-       && p[1] == '\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", newer, newer);
+       (void) fprintf(stderr, "control-%c (^%c).\n", UChar(newer), UChar(newer));
     } else
-       (void) fprintf(stderr, "%c.\n", newer);
-#endif
+       (void) fprintf(stderr, "%c.\n", UChar(newer));
 }
+#endif
 
 /*
  * Convert the obsolete argument forms into something that getopt can handle.
@@ -1022,60 +1164,53 @@ obsolete(char **argv)
 }
 
 static void
-usage(const char *pname)
+usage(void)
 {
-    (void) fprintf(stderr,
-       "usage: %s [-IQrs] [-] [-e ch] [-i ch] [-k ch] [-m mapping] [terminal]\n", pname);
-    exit(EXIT_FAILURE);
+    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];
+    return (char) ((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;
 
-#ifdef TERMIOS
-    if (tcgetattr(STDERR_FILENO, &mode) < 0)
-       failed("standard error");
-
-    oldmode = mode;
-    ospeed = cfgetospeed(&mode);
-#else
-    if (gtty(STDERR_FILENO, &mode) < 0)
-       failed("standard error");
-
-    oldmode = mode;
-    ospeed = mode.sg_ospeed;
-#endif
-
-    if ((p = strrchr(*argv, '/')) != 0)
-       ++p;
-    else
-       p = *argv;
-    if (!CaselessCmp(p, "reset")) {
-       isreset = 1;
-       reset_mode();
-    }
-
     obsolete(argv);
     noinit = noset = quiet = Sflag = sflag = showterm = 0;
-    while ((ch = getopt(argc, argv, "a:d:e:Ii:k:m:np:qQSrs")) != EOF) {
+    while ((ch = getopt(argc, argv, "a:cd:e:Ii:k:m:np:qQSrsVw")) != -1) {
        switch (ch) {
-       case 'q':               /* display term only */
-           noset = 1;
+       case 'c':               /* set control-chars */
+           opt_c = TRUE;
            break;
        case 'a':               /* OBSOLETE: map identifier to type */
            add_mapping("arpanet", optarg);
@@ -1106,55 +1241,86 @@ main(int argc, char **argv)
        case 'Q':               /* don't output control key settings */
            quiet = 1;
            break;
-       case 'S':               /* OBSOLETE: output TERM & TERMCAP */
-           Sflag = 1;
+       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(*argv);
+           usage();
        }
     }
+
+    _nc_progname = _nc_rootname(*argv);
     argc -= optind;
     argv += optind;
 
     if (argc > 1)
-       usage(*argv);
+       usage();
 
-    ttype = get_termcap_entry(*argv);
+    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 = (NCURSES_OSPEED) cfgetospeed(&mode);
+#else
+    ospeed = (NCURSES_OSPEED) mode.sg_ospeed;
+#endif
+
+    if (same_program(_nc_progname, PROG_RESET)) {
+       isreset = TRUE;
+       reset_mode();
+    }
+
+    (void) get_termcap_entry(*argv);
 
     if (!noset) {
+#if HAVE_SIZECHANGE
        tcolumns = columns;
        tlines = lines;
 
-#if defined(TIOCGWINSZ) && defined(TIOCSWINSZ)
-       /* 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);
+       if (opt_w) {
+           STRUCT_WINSIZE win;
+           /* Set window size if not set already */
+           (void) ioctl(STDERR_FILENO, IOCTL_GET_WINSIZE, &win);
+           if (WINSIZE_ROWS(win) == 0 &&
+               WINSIZE_COLS(win) == 0 &&
+               tlines > 0 && tcolumns > 0) {
+               WINSIZE_ROWS(win) = tlines;
+               WINSIZE_COLS(win) = tcolumns;
+               (void) ioctl(STDERR_FILENO, IOCTL_SET_WINSIZE, &win);
+           }
        }
 #endif
-       set_control_chars();
-       set_conversions();
+       if (opt_c) {
+           set_control_chars();
+           set_conversions();
 
-       if (!noinit)
-           set_init();
+           if (!noinit)
+               set_init();
 
-       /* Set the modes if they've changed. */
-       if (memcmp(&mode, &oldmode, sizeof(mode))) {
-#ifdef TERMIOS
-           tcsetattr(STDERR_FILENO, TCSADRAIN, &mode);
-#else
-           stty(STDERR_FILENO, &mode);
-#endif
+           /* Set the modes if they've changed. */
+           if (memcmp(&mode, &oldmode, sizeof(mode))) {
+               SET_TTY(STDERR_FILENO, &mode);
+           }
        }
     }
 
@@ -1170,30 +1336,34 @@ main(int argc, char **argv)
         * 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, CINTR);
-           report("Interrupt", VINTR, CKILL);
+           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 ((p = getenv("SHELL")) != 0
-           && !strcmp(p + strlen(p) - 3, "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);
     }
 
-    return EXIT_SUCCESS;
+    ExitProgram(EXIT_SUCCESS);
 }
-
-/* tset.c ends here */