rodata cleanup. "unable to" == "cannot". -300 bytes
[oweals/busybox.git] / coreutils / stty.c
index e60c4f5892c03f838493a0db1006dcc3446ecd59..22784a2601200db76cee6545b61380b67a8b030e 100644 (file)
@@ -68,8 +68,8 @@
 # define CSWTCH _POSIX_VDISABLE
 #endif
 
-/* SunOS 5.3 loses (^Z doesn't work) if `swtch' is the same as `susp'.
-   So the default is to disable `swtch.'  */
+/* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'.
+   So the default is to disable 'swtch.'  */
 #if defined (__sparc__) && defined (__svr4__)
 # undef CSWTCH
 # define CSWTCH _POSIX_VDISABLE
@@ -120,8 +120,8 @@ enum speed_setting {
        input_speed, output_speed, both_speeds
 };
 
-/* Which member(s) of `struct termios' a mode uses */
-enum mode_type {
+/* Which member(s) of 'struct termios' a mode uses */
+enum {
        /* Do NOT change the order or values, as mode_type_flag()
         * depends on them */
        control, input, output, local, combination
@@ -150,22 +150,20 @@ static const char stty_LCASE[] = "LCASE";
 static const char stty_crt  [] = "crt";
 static const char stty_dec  [] = "dec";
 
-/* Flags for `struct mode_info' */
-#define SANE_SET 1              /* Set in `sane' mode                  */
-#define SANE_UNSET 2            /* Unset in `sane' mode                */
-#define REV 4                   /* Can be turned off by prepending `-' */
+/* Flags for 'struct mode_info' */
+#define SANE_SET 1              /* Set in 'sane' mode                  */
+#define SANE_UNSET 2            /* Unset in 'sane' mode                */
+#define REV 4                   /* Can be turned off by prepending '-' */
 #define OMIT 8                  /* Don't display value                 */
 
 /* Each mode */
 struct mode_info {
        const char *name;       /* Name given on command line            */
-       /* enum mode_type type; */
        char type;              /* Which structure element to change     */
        char flags;             /* Setting and display options           */
-       unsigned short mask;     /* Other bits to turn off for this mode */
+       unsigned short mask;    /* Other bits to turn off for this mode */
        unsigned long bits;     /* Bits to set for this mode             */
 };
-#define EMT(t) ((enum mode_type)(t))
 
 #define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B }
 
@@ -322,7 +320,7 @@ enum {
 /* Control character settings */
 struct control_info {
        const char *name;                       /* Name given on command line */
-       unsigned char saneval;          /* Value to set for `stty sane' */
+       unsigned char saneval;          /* Value to set for 'stty sane' */
        unsigned char offset;                           /* Offset in c_cc */
 };
 
@@ -372,37 +370,86 @@ enum {
 };
 
 /* The width of the screen, for output wrapping */
-static int max_col;
-
+static unsigned max_col = 80; /* default */
 /* Current position, to know when to wrap */
-static int current_col;
-
-static const char *  visible(unsigned int ch);
-static int           recover_mode(const char *arg, struct termios *mode);
-static int           screen_columns(void);
-static void          set_mode(const struct mode_info *info,
-                                       int reversed, struct termios *mode);
-static speed_t       string_to_baud(const char *arg);
-static tcflag_t*     mode_type_flag(enum mode_type type, const struct termios *mode);
-static void          display_all(const struct termios *mode);
-static void          display_changed(const struct termios *mode);
-static void          display_recoverable(const struct termios *mode);
-static void          display_speed(const struct termios *mode, int fancy);
-static void          display_window_size(int fancy);
-static void          sane_mode(struct termios *mode);
-static void          set_control_char(const struct control_info *info,
-                                       const char *arg, struct termios *mode);
-static void          set_speed(enum speed_setting type,
-                                       const char *arg, struct termios *mode);
-static void          set_window_size(int rows, int cols);
-
+static unsigned current_col;
 static const char *device_name = bb_msg_standard_input;
 
-static ATTRIBUTE_NORETURN void perror_on_device(const char *fmt)
+/* Return a string that is the printable representation of character CH */
+/* Adapted from 'cat' by Torbjorn Granlund */
+static const char *visible(unsigned int ch)
+{
+       static char buf[10];
+       char *bpout = buf;
+
+       if (ch == _POSIX_VDISABLE)
+               return "<undef>";
+
+       if (ch >= 128) {
+               ch -= 128;
+               *bpout++ = 'M';
+               *bpout++ = '-';
+       }
+
+       if (ch < 32) {
+               *bpout++ = '^';
+               *bpout++ = ch + 64;
+       } else if (ch < 127) {
+               *bpout++ = ch;
+       } else {
+               *bpout++ = '^';
+               *bpout++ = '?';
+       }
+
+       *bpout = '\0';
+       return buf;
+}
+
+static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
+{
+       static const unsigned char tcflag_offsets[] = {
+               offsetof(struct termios, c_cflag), /* control */
+               offsetof(struct termios, c_iflag), /* input */
+               offsetof(struct termios, c_oflag), /* output */
+               offsetof(struct termios, c_lflag), /* local */
+       };
+
+       if (type <= local) {
+               return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
+       }
+       return NULL;
+}
+
+static speed_t string_to_baud_or_die(const char *arg)
+{
+       return tty_value_to_baud(xatou(arg));
+}
+
+static void set_speed_or_die(enum speed_setting type, const char *arg,
+                                       struct termios *mode)
+{
+       speed_t baud;
+
+       baud = string_to_baud_or_die(arg);
+
+       if (type != output_speed) {     /* either input or both */
+               cfsetispeed(mode, baud);
+       }
+       if (type != input_speed) {      /* either output or both */
+               cfsetospeed(mode, baud);
+       }
+}
+
+static ATTRIBUTE_NORETURN void perror_on_device_and_die(const char *fmt)
 {
        bb_perror_msg_and_die(fmt, device_name);
 }
 
+static void perror_on_device(const char *fmt)
+{
+       bb_perror_msg(fmt, device_name);
+}
+
 /* No, inline won't be as efficient (gcc 3.4.3) */
 #define streq(a,b) (!strcmp((a),(b)))
 
@@ -423,11 +470,13 @@ static void wrapf(const char *message, ...)
 
        if (current_col > 0) {
                current_col++;
-               if (current_col + buflen >= max_col) {
-                       putchar('\n');
-                       current_col = 0;
-               } else
-                       if (buf[0] != '\n') putchar(' ');
+               if (buf[0] != '\n') {
+                       if (current_col + buflen >= max_col) {
+                               putchar('\n');
+                               current_col = 0;
+                       } else
+                               putchar(' ');
+               }
        }
        fputs(buf, stdout);
        current_col += buflen;
@@ -435,6 +484,101 @@ static void wrapf(const char *message, ...)
                current_col = 0;
 }
 
+#ifdef TIOCGWINSZ
+
+static int get_win_size(int fd, struct winsize *win)
+{
+       return ioctl(fd, TIOCGWINSZ, (char *) win);
+}
+
+static void set_window_size(int rows, int cols)
+{
+       struct winsize win;
+
+       if (get_win_size(STDIN_FILENO, &win)) {
+               if (errno != EINVAL) {
+                       perror_on_device("%s");
+                       return;
+               }
+               memset(&win, 0, sizeof(win));
+       }
+
+       if (rows >= 0)
+               win.ws_row = rows;
+       if (cols >= 0)
+               win.ws_col = cols;
+
+# ifdef TIOCSSIZE
+       /* Alexander Dupuy <dupuy@cs.columbia.edu> wrote:
+          The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel.
+          This comment from sys/ttold.h describes Sun's twisted logic - a better
+          test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0).
+          At any rate, the problem is gone in Solaris 2.x */
+
+       if (win.ws_row == 0 || win.ws_col == 0) {
+               struct ttysize ttysz;
+
+               ttysz.ts_lines = win.ws_row;
+               ttysz.ts_cols = win.ws_col;
+
+               win.ws_row = win.ws_col = 1;
+
+               if ((ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win) != 0)
+               || (ioctl(STDIN_FILENO, TIOCSSIZE, (char *) &ttysz) != 0)) {
+                       perror_on_device("%s");
+               }
+               return;
+       }
+# endif
+
+       if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
+               perror_on_device("%s");
+}
+
+static void display_window_size(int fancy)
+{
+       const char *fmt_str = "%s\0%s: no size information for this device";
+       struct winsize win;
+
+       if (get_win_size(STDIN_FILENO, &win)) {
+               if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
+                       perror_on_device(fmt_str);
+               }
+       } else {
+               wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
+                               win.ws_row, win.ws_col);
+       }
+}
+
+#else /* !TIOCGWINSZ */
+
+static inline void display_window_size(int fancy) {}
+
+#endif /* !TIOCGWINSZ */
+
+static int screen_columns_or_die(void)
+{
+       const char *s;
+
+#ifdef TIOCGWINSZ
+       struct winsize win;
+
+       /* With Solaris 2.[123], this ioctl fails and errno is set to
+          EINVAL for telnet (but not rlogin) sessions.
+          On ISC 3.0, it fails for the console and the serial port
+          (but it works for ptys).
+          It can also fail on any system when stdout isn't a tty.
+          In case of any failure, just use the default */
+       if (get_win_size(STDOUT_FILENO, &win) == 0 && win.ws_col > 0)
+               return win.ws_col;
+#endif
+
+       s = getenv("COLUMNS");
+       if (s)
+               return xatoi_u(s);
+       return 80;
+}
+
 static const struct suffix_mult stty_suffixes[] = {
        {"b",  512 },
        {"k",  1024},
@@ -466,9 +610,9 @@ enum {
        param_rows   = 2 | 0x80,
        param_cols   = 3 | 0x80,
        param_size   = 4,
-       param_ispeed = 5 | 0x80,
-       param_ospeed = 6 | 0x80,
-       param_speed  = 7,
+       param_speed  = 5,
+       param_ispeed = 6 | 0x80,
+       param_ospeed = 7 | 0x80,
 };
 
 static int find_param(const char *name)
@@ -482,13 +626,24 @@ static int find_param(const char *name)
        if (streq(name, "columns")) return param_cols;
        if (streq(name, "size")) return param_size;
 #endif
+       if (streq(name, "speed")) return param_speed;
        if (streq(name, "ispeed")) return param_ispeed;
        if (streq(name, "ospeed")) return param_ospeed;
-       if (streq(name, "speed")) return param_speed;
        return 0;
 }
 
 
+static int recover_mode(const char *arg, struct termios *mode);
+static void set_mode(const struct mode_info *info,
+                               int reversed, struct termios *mode);
+static void display_all(const struct termios *mode);
+static void display_changed(const struct termios *mode);
+static void display_recoverable(const struct termios *mode);
+static void display_speed(const struct termios *mode, int fancy);
+static void sane_mode(struct termios *mode);
+static void set_control_char_or_die(const struct control_info *info,
+                               const char *arg, struct termios *mode);
+
 int stty_main(int argc, char **argv)
 {
        struct termios mode;
@@ -549,7 +704,7 @@ int stty_main(int argc, char **argv)
                                                        bb_error_msg_and_die(bb_msg_requires_arg, "-F");
                                                /* remove -F param from arg[vc] */
                                                --argc;
-                                               while (argv[p+1]) { argv[p] = argv[p+1]; ++p; }
+                                               while (argv[p]) { argv[p] = argv[p+1]; ++p; }
                                        }
                                        goto end_option;
                                default:
@@ -570,6 +725,8 @@ end_option:
                if (cp) {
                        if (!argnext)
                                bb_error_msg_and_die(bb_msg_requires_arg, arg);
+                       /* called for the side effect of xfunc death only */
+                       set_control_char_or_die(cp, argnext, &mode);
                        noargs = 0;
                        ++k;
                        continue;
@@ -586,24 +743,30 @@ end_option:
 #ifdef HAVE_C_LINE
                case param_line:
 # ifndef TIOCGWINSZ
-                       bb_xparse_number(argnext, stty_suffixes);
+                       xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
                        break;
 # endif /* else fall-through */
 #endif
 #ifdef TIOCGWINSZ
                case param_rows:
                case param_cols:
-                       bb_xparse_number(argnext, stty_suffixes);
+                       xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
                        break;
                case param_size:
 #endif
+               case param_speed:
+                       break;
                case param_ispeed:
+                       /* called for the side effect of xfunc death only */
+                       set_speed_or_die(input_speed, argnext, &mode);
+                       break;
                case param_ospeed:
-               case param_speed:
+                       /* called for the side effect of xfunc death only */
+                       set_speed_or_die(output_speed, argnext, &mode);
                        break;
                default:
                        if (recover_mode(arg, &mode) == 1) break;
-                       if (string_to_baud(arg) != (speed_t) -1) break;
+                       if (string_to_baud_or_die(arg) != (speed_t) -1) break;
                        bb_error_msg_and_die("invalid argument '%s'", arg);
                }
                noargs = 0;
@@ -621,24 +784,23 @@ end_option:
                int fd, fdflags;
                device_name = file_name;
                fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
-               if (fd != 0) {
-                       dup2(fd, 0);
+               if (fd != STDIN_FILENO) {
+                       dup2(fd, STDIN_FILENO);
                        close(fd);
                }
                fdflags = fcntl(STDIN_FILENO, F_GETFL);
                if (fdflags == -1 || fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
-                       perror_on_device("%s: couldn't reset non-blocking mode");
+                       perror_on_device_and_die("%s: cannot reset non-blocking mode");
        }
 
        /* Initialize to all zeroes so there is no risk memcmp will report a
           spurious difference in an uninitialized portion of the structure */
        memset(&mode, 0, sizeof(mode));
        if (tcgetattr(STDIN_FILENO, &mode))
-               perror_on_device("%s");
+               perror_on_device_and_die("%s");
 
        if (verbose_output || recoverable_output || noargs) {
-               max_col = screen_columns();
-               current_col = 0;
+               max_col = screen_columns_or_die();
                output_func(&mode);
                return EXIT_SUCCESS;
        }
@@ -670,7 +832,7 @@ end_option:
                cp = find_control(arg);
                if (cp) {
                        ++k;
-                       set_control_char(cp, argnext, &mode);
+                       set_control_char_or_die(cp, argnext, &mode);
                        continue;
                }
 
@@ -682,42 +844,39 @@ end_option:
                switch (param) {
 #ifdef HAVE_C_LINE
                case param_line:
-                       mode.c_line = bb_xparse_number(argnext, stty_suffixes);
+                       mode.c_line = xatoul_sfx(argnext, stty_suffixes);
                        require_set_attr = 1;
                        break;
 #endif
 #ifdef TIOCGWINSZ
                case param_cols:
-                       set_window_size(-1, (int) bb_xparse_number(argnext, stty_suffixes));
+                       set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
                        break;
                case param_size:
-                       max_col = screen_columns();
-                       current_col = 0;
                        display_window_size(0);
                        break;
                case param_rows:
-                       set_window_size((int) bb_xparse_number(argnext, stty_suffixes), -1);
+                       set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
                        break;
 #endif
+               case param_speed:
+                       display_speed(&mode, 0);
+                       break;
                case param_ispeed:
-                       set_speed(input_speed, argnext, &mode);
+                       set_speed_or_die(input_speed, argnext, &mode);
                        speed_was_set = 1;
                        require_set_attr = 1;
                        break;
                case param_ospeed:
-                       set_speed(output_speed, argnext, &mode);
+                       set_speed_or_die(output_speed, argnext, &mode);
                        speed_was_set = 1;
                        require_set_attr = 1;
                        break;
-               case param_speed:
-                       max_col = screen_columns();
-                       display_speed(&mode, 0);
-                       break;
                default:
                        if (recover_mode(arg, &mode) == 1)
                                require_set_attr = 1;
-                       else /* true: if (string_to_baud(arg) != (speed_t) -1) */ {
-                               set_speed(both_speeds, arg, &mode);
+                       else /* true: if (string_to_baud_or_die(arg) != (speed_t) -1) */ {
+                               set_speed_or_die(both_speeds, arg, &mode);
                                speed_was_set = 1;
                                require_set_attr = 1;
                        } /* else - impossible (caught in the first pass):
@@ -729,11 +888,11 @@ end_option:
                struct termios new_mode;
 
                if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
-                       perror_on_device("%s");
+                       perror_on_device_and_die("%s");
 
                /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
                   it performs *any* of the requested operations.  This means it
-                  can report `success' when it has actually failed to perform
+                  can report 'success' when it has actually failed to perform
                   some proper subset of the requested operations.  To detect
                   this partial failure, get the current terminal attributes and
                   compare them to the requested ones */
@@ -742,7 +901,7 @@ end_option:
                   spurious difference in an uninitialized portion of the structure */
                memset(&new_mode, 0, sizeof(new_mode));
                if (tcgetattr(STDIN_FILENO, &new_mode))
-                       perror_on_device("%s");
+                       perror_on_device_and_die("%s");
 
                if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
 #ifdef CIBAUD
@@ -758,7 +917,7 @@ end_option:
                        new_mode.c_cflag &= (~CIBAUD);
                        if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
 #endif
-                               perror_on_device ("%s: unable to perform all requested operations");
+                               perror_on_device_and_die("%s: cannot perform all requested operations");
                }
        }
 
@@ -800,279 +959,159 @@ end_option:
 #define ECHOKE 0
 #endif
 
-static void
-set_mode(const struct mode_info *info, int reversed, struct termios *mode)
+static void set_mode(const struct mode_info *info, int reversed,
+                                       struct termios *mode)
 {
        tcflag_t *bitsp;
 
-       bitsp = mode_type_flag(EMT(info->type), mode);
-
-       if (bitsp == NULL) {
-               /* Combination mode */
-               if (info->name == evenp || info->name == parity) {
-                       if (reversed)
-                               mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
-                       else
-                               mode->c_cflag =
-                                       (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
-               } else if (info->name == stty_oddp) {
-                       if (reversed)
-                               mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
-                       else
-                               mode->c_cflag =
-                                       (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
-               } else if (info->name == stty_nl) {
-                       if (reversed) {
-                               mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
-                               mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
-                       } else {
-                               mode->c_iflag = mode->c_iflag & ~ICRNL;
-                               if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
-                       }
-               } else if (info->name == stty_ek) {
-                       mode->c_cc[VERASE] = CERASE;
-                       mode->c_cc[VKILL] = CKILL;
-               } else if (info->name == stty_sane)
-                       sane_mode(mode);
-               else if (info->name == cbreak) {
-                       if (reversed)
-                               mode->c_lflag |= ICANON;
-                       else
-                               mode->c_lflag &= ~ICANON;
-               } else if (info->name == stty_pass8) {
-                       if (reversed) {
-                               mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
-                               mode->c_iflag |= ISTRIP;
-                       } else {
-                               mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
-                               mode->c_iflag &= ~ISTRIP;
-                       }
-               } else if (info->name == litout) {
-                       if (reversed) {
-                               mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
-                               mode->c_iflag |= ISTRIP;
-                               mode->c_oflag |= OPOST;
-                       } else {
-                               mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
-                               mode->c_iflag &= ~ISTRIP;
-                               mode->c_oflag &= ~OPOST;
-                       }
-               } else if (info->name == raw || info->name == cooked) {
-                       if ((info->name[0] == 'r' && reversed)
-                               || (info->name[0] == 'c' && !reversed)) {
-                               /* Cooked mode */
-                               mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
-                               mode->c_oflag |= OPOST;
-                               mode->c_lflag |= ISIG | ICANON;
-#if VMIN == VEOF
-                               mode->c_cc[VEOF] = CEOF;
-#endif
-#if VTIME == VEOL
-                               mode->c_cc[VEOL] = CEOL;
-#endif
-                       } else {
-                               /* Raw mode */
-                               mode->c_iflag = 0;
-                               mode->c_oflag &= ~OPOST;
-                               mode->c_lflag &= ~(ISIG | ICANON | XCASE);
-                               mode->c_cc[VMIN] = 1;
-                               mode->c_cc[VTIME] = 0;
-                       }
-               }
-               else if (IXANY && info->name == decctlq) {
-                       if (reversed)
-                               mode->c_iflag |= IXANY;
-                       else
-                               mode->c_iflag &= ~IXANY;
+       bitsp = mode_type_flag(info->type, mode);
+
+       if (bitsp) {
+               if (reversed)
+                       *bitsp = *bitsp & ~((unsigned long)info->mask) & ~info->bits;
+               else
+                       *bitsp = (*bitsp & ~((unsigned long)info->mask)) | info->bits;
+               return;
+       }
+
+       /* Combination mode */
+       if (info->name == evenp || info->name == parity) {
+               if (reversed)
+                       mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+               else
+                       mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
+       } else if (info->name == stty_oddp) {
+               if (reversed)
+                       mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+               else
+                       mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
+       } else if (info->name == stty_nl) {
+               if (reversed) {
+                       mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
+                       mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
+               } else {
+                       mode->c_iflag = mode->c_iflag & ~ICRNL;
+                       if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
                }
-               else if (TABDLY && info->name == stty_tabs) {
-                       if (reversed)
-                               mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
-                       else
-                               mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
+       } else if (info->name == stty_ek) {
+               mode->c_cc[VERASE] = CERASE;
+               mode->c_cc[VKILL] = CKILL;
+       } else if (info->name == stty_sane) {
+               sane_mode(mode);
+       }
+       else if (info->name == cbreak) {
+               if (reversed)
+                       mode->c_lflag |= ICANON;
+               else
+                       mode->c_lflag &= ~ICANON;
+       } else if (info->name == stty_pass8) {
+               if (reversed) {
+                       mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
+                       mode->c_iflag |= ISTRIP;
+               } else {
+                       mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+                       mode->c_iflag &= ~ISTRIP;
                }
-               else if (OXTABS && info->name == stty_tabs) {
-                       if (reversed)
-                               mode->c_oflag = mode->c_oflag | OXTABS;
-                       else
-                               mode->c_oflag = mode->c_oflag & ~OXTABS;
+       } else if (info->name == litout) {
+               if (reversed) {
+                       mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
+                       mode->c_iflag |= ISTRIP;
+                       mode->c_oflag |= OPOST;
+               } else {
+                       mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+                       mode->c_iflag &= ~ISTRIP;
+                       mode->c_oflag &= ~OPOST;
                }
-               else if (XCASE && IUCLC && OLCUC
-               && (info->name == stty_lcase || info->name == stty_LCASE)) {
-                       if (reversed) {
-                               mode->c_lflag &= ~XCASE;
-                               mode->c_iflag &= ~IUCLC;
-                               mode->c_oflag &= ~OLCUC;
-                       } else {
-                               mode->c_lflag |= XCASE;
-                               mode->c_iflag |= IUCLC;
-                               mode->c_oflag |= OLCUC;
-                       }
+       } else if (info->name == raw || info->name == cooked) {
+               if ((info->name[0] == 'r' && reversed)
+                       || (info->name[0] == 'c' && !reversed)) {
+                       /* Cooked mode */
+                       mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
+                       mode->c_oflag |= OPOST;
+                       mode->c_lflag |= ISIG | ICANON;
+#if VMIN == VEOF
+                       mode->c_cc[VEOF] = CEOF;
+#endif
+#if VTIME == VEOL
+                       mode->c_cc[VEOL] = CEOL;
+#endif
+               } else {
+                       /* Raw mode */
+                       mode->c_iflag = 0;
+                       mode->c_oflag &= ~OPOST;
+                       mode->c_lflag &= ~(ISIG | ICANON | XCASE);
+                       mode->c_cc[VMIN] = 1;
+                       mode->c_cc[VTIME] = 0;
                }
-               else if (info->name == stty_crt)
-                       mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
-               else if (info->name == stty_dec) {
-                       mode->c_cc[VINTR] = 3;  /* ^C */
-                       mode->c_cc[VERASE] = 127;       /* DEL */
-                       mode->c_cc[VKILL] = 21; /* ^U */
-                       mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
-                       if (IXANY) mode->c_iflag &= ~IXANY;
+       }
+       else if (IXANY && info->name == decctlq) {
+               if (reversed)
+                       mode->c_iflag |= IXANY;
+               else
+                       mode->c_iflag &= ~IXANY;
+       }
+       else if (TABDLY && info->name == stty_tabs) {
+               if (reversed)
+                       mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
+               else
+                       mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
+       }
+       else if (OXTABS && info->name == stty_tabs) {
+               if (reversed)
+                       mode->c_oflag |= OXTABS;
+               else
+                       mode->c_oflag &= ~OXTABS;
+       }
+       else if (XCASE && IUCLC && OLCUC
+       && (info->name == stty_lcase || info->name == stty_LCASE)) {
+               if (reversed) {
+                       mode->c_lflag &= ~XCASE;
+                       mode->c_iflag &= ~IUCLC;
+                       mode->c_oflag &= ~OLCUC;
+               } else {
+                       mode->c_lflag |= XCASE;
+                       mode->c_iflag |= IUCLC;
+                       mode->c_oflag |= OLCUC;
                }
-       } else if (reversed)
-               *bitsp = *bitsp & ~((unsigned long)info->mask) & ~info->bits;
-       else
-               *bitsp = (*bitsp & ~((unsigned long)info->mask)) | info->bits;
+       }
+       else if (info->name == stty_crt) {
+               mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
+       }
+       else if (info->name == stty_dec) {
+               mode->c_cc[VINTR] = 3; /* ^C */
+               mode->c_cc[VERASE] = 127; /* DEL */
+               mode->c_cc[VKILL] = 21; /* ^U */
+               mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
+               if (IXANY) mode->c_iflag &= ~IXANY;
+       }
 }
 
-static void
-set_control_char(const struct control_info *info, const char *arg,
-                                struct termios *mode)
+static void set_control_char_or_die(const struct control_info *info,
+                       const char *arg, struct termios *mode)
 {
        unsigned char value;
 
        if (info->name == stty_min || info->name == stty_time)
-               value = bb_xparse_number(arg, stty_suffixes);
+               value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
        else if (arg[0] == '\0' || arg[1] == '\0')
                value = arg[0];
        else if (streq(arg, "^-") || streq(arg, "undef"))
                value = _POSIX_VDISABLE;
-       else if (arg[0] == '^' && arg[1] != '\0') {     /* Ignore any trailing junk */
+       else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
+               value = arg[1] & 0x1f; /* Non-letters get weird results */
                if (arg[1] == '?')
                        value = 127;
-               else
-                       value = arg[1] & ~0140; /* Non-letters get weird results */
        } else
-               value = bb_xparse_number(arg, stty_suffixes);
+               value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
        mode->c_cc[info->offset] = value;
 }
 
-static void
-set_speed(enum speed_setting type, const char *arg, struct termios *mode)
-{
-       speed_t baud;
-
-       baud = string_to_baud(arg);
-
-       if (type != output_speed) {     /* either input or both */
-               cfsetispeed(mode, baud);
-       }
-       if (type != input_speed) {      /* either output or both */
-               cfsetospeed(mode, baud);
-       }
-}
-
-#ifdef TIOCGWINSZ
-
-static int get_win_size(int fd, struct winsize *win)
-{
-       return ioctl(fd, TIOCGWINSZ, (char *) win);
-}
-
-static void
-set_window_size(int rows, int cols)
-{
-       struct winsize win;
-
-       if (get_win_size(STDIN_FILENO, &win)) {
-               if (errno != EINVAL)
-                       perror_on_device("%s");
-               memset(&win, 0, sizeof(win));
-       }
-
-       if (rows >= 0)
-               win.ws_row = rows;
-       if (cols >= 0)
-               win.ws_col = cols;
-
-# ifdef TIOCSSIZE
-       /* Alexander Dupuy <dupuy@cs.columbia.edu> wrote:
-          The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel.
-          This comment from sys/ttold.h describes Sun's twisted logic - a better
-          test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0).
-          At any rate, the problem is gone in Solaris 2.x */
-
-       if (win.ws_row == 0 || win.ws_col == 0) {
-               struct ttysize ttysz;
-
-               ttysz.ts_lines = win.ws_row;
-               ttysz.ts_cols = win.ws_col;
-
-               win.ws_row = win.ws_col = 1;
-
-               if ((ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win) != 0)
-                       || (ioctl(STDIN_FILENO, TIOCSSIZE, (char *) &ttysz) != 0)) {
-                       perror_on_device("%s");
-               }
-               return;
-       }
-# endif
-
-       if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
-               perror_on_device("%s");
-}
-
-static void display_window_size(int fancy)
-{
-       const char *fmt_str = "%s" "\0" "%s: no size information for this device";
-       struct winsize win;
-
-       if (get_win_size(STDIN_FILENO, &win)) {
-               if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
-                       perror_on_device(fmt_str);
-               }
-       } else {
-               wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
-                         win.ws_row, win.ws_col);
-       }
-}
-#endif
-
-static int screen_columns(void)
-{
-       int columns;
-       const char *s;
-
-#ifdef TIOCGWINSZ
-       struct winsize win;
-
-       /* With Solaris 2.[123], this ioctl fails and errno is set to
-          EINVAL for telnet (but not rlogin) sessions.
-          On ISC 3.0, it fails for the console and the serial port
-          (but it works for ptys).
-          It can also fail on any system when stdout isn't a tty.
-          In case of any failure, just use the default */
-       if (get_win_size(STDOUT_FILENO, &win) == 0 && win.ws_col > 0)
-               return win.ws_col;
-#endif
-
-       columns = 80;
-       if ((s = getenv("COLUMNS"))) {
-               columns = atoi(s);
-       }
-       return columns;
-}
-
-static tcflag_t *mode_type_flag(enum mode_type type, const struct termios *mode)
-{
-       static const unsigned char tcflag_offsets[] = {
-               offsetof(struct termios, c_cflag), /* control */
-               offsetof(struct termios, c_iflag), /* input */
-               offsetof(struct termios, c_oflag), /* output */
-               offsetof(struct termios, c_lflag) /* local */
-       };
-
-       if (((unsigned int) type) <= local) {
-               return (tcflag_t *)(((char *) mode) + tcflag_offsets[(int)type]);
-       }
-       return NULL;
-}
-
 static void display_changed(const struct termios *mode)
 {
        int i;
        tcflag_t *bitsp;
        unsigned long mask;
-       enum mode_type prev_type = control;
+       int prev_type = control;
 
        display_speed(mode, 1);
 #ifdef HAVE_C_LINE
@@ -1107,12 +1146,12 @@ static void display_changed(const struct termios *mode)
        for (i = 0; i < NUM_mode_info; ++i) {
                if (mode_info[i].flags & OMIT)
                        continue;
-               if (EMT(mode_info[i].type) != prev_type) {
+               if (mode_info[i].type != prev_type) {
                        if (current_col) wrapf("\n");
-                       prev_type = EMT(mode_info[i].type);
+                       prev_type = mode_info[i].type;
                }
 
-               bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
+               bitsp = mode_type_flag(mode_info[i].type, mode);
                mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
                if ((*bitsp & mask) == mode_info[i].bits) {
                        if (mode_info[i].flags & SANE_UNSET) {
@@ -1125,18 +1164,15 @@ static void display_changed(const struct termios *mode)
        if (current_col) wrapf("\n");
 }
 
-static void
-display_all(const struct termios *mode)
+static void display_all(const struct termios *mode)
 {
        int i;
        tcflag_t *bitsp;
        unsigned long mask;
-       enum mode_type prev_type = control;
+       int prev_type = control;
 
        display_speed(mode, 1);
-#ifdef TIOCGWINSZ
        display_window_size(1);
-#endif
 #ifdef HAVE_C_LINE
        wrapf("line = %d;\n", mode->c_line);
 #else
@@ -1167,13 +1203,12 @@ display_all(const struct termios *mode)
        for (i = 0; i < NUM_mode_info; ++i) {
                if (mode_info[i].flags & OMIT)
                        continue;
-               if (EMT(mode_info[i].type) != prev_type) {
-                       putchar('\n');
-                       current_col = 0;
-                       prev_type = EMT(mode_info[i].type);
+               if (mode_info[i].type != prev_type) {
+                       wrapf("\n");
+                       prev_type = mode_info[i].type;
                }
 
-               bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
+               bitsp = mode_type_flag(mode_info[i].type, mode);
                mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
                if ((*bitsp & mask) == mode_info[i].bits)
                        wrapf("%s", mode_info[i].name);
@@ -1185,14 +1220,14 @@ display_all(const struct termios *mode)
 
 static void display_speed(const struct termios *mode, int fancy)
 {
-                            //12345678 9 10
+                            //01234567 8 9
        const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
        unsigned long ispeed, ospeed;
 
        ospeed = ispeed = cfgetispeed(mode);
        if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
                ispeed = ospeed;                /* in case ispeed was 0 */
-                        //1234 5 6 7 8 9 10
+                        //0123 4 5 6 7 8 9
                fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
        }
        if (fancy) fmt_str += 9;
@@ -1202,7 +1237,6 @@ static void display_speed(const struct termios *mode, int fancy)
 static void display_recoverable(const struct termios *mode)
 {
        int i;
-
        printf("%lx:%lx:%lx:%lx",
                   (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
                   (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
@@ -1218,7 +1252,7 @@ static int recover_mode(const char *arg, struct termios *mode)
        unsigned long iflag, oflag, cflag, lflag;
 
        /* Scan into temporaries since it is too much trouble to figure out
-          the right format for `tcflag_t' */
+          the right format for 'tcflag_t' */
        if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
                           &iflag, &oflag, &cflag, &lflag, &n) != 4)
                return 0;
@@ -1241,11 +1275,6 @@ static int recover_mode(const char *arg, struct termios *mode)
        return 1;
 }
 
-static speed_t string_to_baud(const char *arg)
-{
-       return tty_value_to_baud(bb_xparse_number(arg, 0));
-}
-
 static void sane_mode(struct termios *mode)
 {
        int i;
@@ -1261,45 +1290,13 @@ static void sane_mode(struct termios *mode)
 
        for (i = 0; i < NUM_mode_info; ++i) {
                if (mode_info[i].flags & SANE_SET) {
-                       bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
+                       bitsp = mode_type_flag(mode_info[i].type, mode);
                        *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
                                | mode_info[i].bits;
                } else if (mode_info[i].flags & SANE_UNSET) {
-                       bitsp = mode_type_flag(EMT(mode_info[i].type), mode);
+                       bitsp = mode_type_flag(mode_info[i].type, mode);
                        *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
                                & ~mode_info[i].bits;
                }
        }
 }
-
-/* Return a string that is the printable representation of character CH */
-/* Adapted from `cat' by Torbjorn Granlund */
-
-static const char *visible(unsigned int ch)
-{
-       static char buf[10];
-       char *bpout = buf;
-
-       if (ch == _POSIX_VDISABLE) {
-               return "<undef>";
-       }
-
-       if (ch >= 128) {
-               ch -= 128;
-               *bpout++ = 'M';
-               *bpout++ = '-';
-       }
-
-       if (ch < 32) {
-               *bpout++ = '^';
-               *bpout++ = ch + 64;
-       } else if (ch < 127) {
-               *bpout++ = ch;
-       } else {
-               *bpout++ = '^';
-               *bpout++ = '?';
-       }
-
-       *bpout = '\0';
-       return buf;
-}