config: more tweaks
[oweals/busybox.git] / coreutils / stty.c
index a63c1d1faf93f3a5f060bde9bf4d48b2d55a2f32..6251f2aef5defdb8b7bbfcc0e3a81e8de8f5523a 100644 (file)
@@ -1,39 +1,56 @@
 /* vi: set sw=4 ts=4: */
-/* stty -- change and print terminal line settings
-   Copyright (C) 1990-1999 Free Software Foundation, Inc.
-
-   Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
-*/
+/*
+ * stty -- change and print terminal line settings
+ * Copyright (C) 1990-1999 Free Software Foundation, Inc.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
 /* Usage: stty [-ag] [-F device] [setting...]
-
-   Options:
-   -a Write all current settings to stdout in human-readable form.
-   -g Write all current settings to stdout in stty-readable form.
-   -F Open and use the specified device instead of stdin
-
-   If no args are given, write to stdout the baud rate and settings that
-   have been changed from their defaults.  Mode reading and changes
-   are done on the specified device, or stdin if none was specified.
-
-   David MacKenzie <djm@gnu.ai.mit.edu>
-
-   Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
-
-   */
-
-#include "busybox.h"
-
-#define STREQ(a, b) (strcmp ((a), (b)) == 0)
-
+ *
+ * Options:
+ * -a Write all current settings to stdout in human-readable form.
+ * -g Write all current settings to stdout in stty-readable form.
+ * -F Open and use the specified device instead of stdin
+ *
+ * If no args are given, write to stdout the baud rate and settings that
+ * have been changed from their defaults.  Mode reading and changes
+ * are done on the specified device, or stdin if none was specified.
+ *
+ * David MacKenzie <djm@gnu.ai.mit.edu>
+ *
+ * Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
+ */
+//config:config STTY
+//config:      bool "stty (8.9 kb)"
+//config:      default y
+//config:      help
+//config:      stty is used to change and print terminal line settings.
+
+//applet:IF_STTY(APPLET_NOEXEC(stty, stty, BB_DIR_BIN, BB_SUID_DROP, stty))
+
+//kbuild:lib-$(CONFIG_STTY) += stty.o
+
+//usage:#define stty_trivial_usage
+//usage:       "[-a|g] [-F DEVICE] [SETTING]..."
+//usage:#define stty_full_usage "\n\n"
+//usage:       "Without arguments, prints baud rate, line discipline,\n"
+//usage:       "and deviations from stty sane\n"
+//usage:     "\n       -F DEVICE       Open device instead of stdin"
+//usage:     "\n       -a              Print all current settings in human-readable form"
+//usage:     "\n       -g              Print in stty-readable form"
+//usage:     "\n       [SETTING]       See manpage"
+
+#include "libbb.h"
+#include "common_bufsiz.h"
 
 #ifndef _POSIX_VDISABLE
 # define _POSIX_VDISABLE ((unsigned char) 0)
 #endif
 
 #define Control(c) ((c) & 0x1f)
-/* Canonical values for control characters. */
+/* Canonical values for control characters */
 #ifndef CINTR
-# define CINTR Control ('c')
+# define CINTR Control('c')
 #endif
 #ifndef CQUIT
 # define CQUIT 28
 # define CERASE 127
 #endif
 #ifndef CKILL
-# define CKILL Control ('u')
+# define CKILL Control('u')
 #endif
 #ifndef CEOF
-# define CEOF Control ('d')
+# define CEOF Control('d')
 #endif
 #ifndef CEOL
 # define CEOL _POSIX_VDISABLE
 #endif
 #ifndef CSTART
-# define CSTART Control ('q')
+# define CSTART Control('q')
 #endif
 #ifndef CSTOP
-# define CSTOP Control ('s')
+# define CSTOP Control('s')
 #endif
 #ifndef CSUSP
-# define CSUSP Control ('z')
+# define CSUSP Control('z')
 #endif
 #if defined(VEOL2) && !defined(CEOL2)
 # define CEOL2 _POSIX_VDISABLE
 #endif
-/* ISC renamed swtch to susp for termios, but we'll accept either name.  */
+/* glibc-2.12.1 uses only VSWTC name */
+#if defined(VSWTC) && !defined(VSWTCH)
+# define VSWTCH VSWTC
+#endif
+/* ISC renamed swtch to susp for termios, but we'll accept either name */
 #if defined(VSUSP) && !defined(VSWTCH)
 # define VSWTCH VSUSP
 # define CSWTCH CSUSP
 # 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.'  */
-#if defined (__sparc__) && defined (__svr4__)
+/* 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
 #endif
 
-#if defined(VWERSE) && !defined (VWERASE)       /* AIX-3.2.5 */
+#if defined(VWERSE) && !defined(VWERASE)       /* AIX-3.2.5 */
 # define VWERASE VWERSE
 #endif
-#if defined(VDSUSP) && !defined (CDSUSP)
-# define CDSUSP Control ('y')
+#if defined(VDSUSP) && !defined(CDSUSP)
+# define CDSUSP Control('y')
 #endif
 #if !defined(VREPRINT) && defined(VRPRNT)       /* Irix 4.0.5 */
 # define VREPRINT VRPRNT
 #endif
 #if defined(VREPRINT) && !defined(CRPRNT)
-# define CRPRNT Control ('r')
+# define CRPRNT Control('r')
 #endif
 #if defined(VWERASE) && !defined(CWERASE)
-# define CWERASE Control ('w')
+# define CWERASE Control('w')
 #endif
 #if defined(VLNEXT) && !defined(CLNEXT)
-# define CLNEXT Control ('v')
+# define CLNEXT Control('v')
 #endif
 #if defined(VDISCARD) && !defined(VFLUSHO)
 # define VFLUSHO VDISCARD
 # define ECHOKE CRTKIL
 #endif
 #if defined(VFLUSHO) && !defined(CFLUSHO)
-# define CFLUSHO Control ('o')
+# define CFLUSHO Control('o')
 #endif
 #if defined(VSTATUS) && !defined(CSTATUS)
-# define CSTATUS Control ('t')
+# define CSTATUS Control('t')
+#endif
+
+/* Save us from #ifdef forest plague */
+#ifndef BSDLY
+# define BSDLY 0
+#endif
+#ifndef CIBAUD
+# define CIBAUD 0
+#endif
+#ifndef CRDLY
+# define CRDLY 0
+#endif
+#ifndef CMSPAR
+# define CMSPAR 0
+#endif
+#ifndef CRTSCTS
+# define CRTSCTS 0
+#endif
+#ifndef ECHOCTL
+# define ECHOCTL 0
+#endif
+#ifndef ECHOKE
+# define ECHOKE 0
+#endif
+#ifndef ECHOPRT
+# define ECHOPRT 0
+#endif
+#ifndef FFDLY
+# define FFDLY 0
+#endif
+#ifndef IEXTEN
+# define IEXTEN 0
+#endif
+#ifndef IMAXBEL
+# define IMAXBEL 0
+#endif
+#ifndef IUCLC
+# define IUCLC 0
+#endif
+#ifndef IXANY
+# define IXANY 0
+#endif
+#ifndef NLDLY
+# define NLDLY 0
+#endif
+#ifndef OCRNL
+# define OCRNL 0
+#endif
+#ifndef OFDEL
+# define OFDEL 0
+#endif
+#ifndef OFILL
+# define OFILL 0
+#endif
+#ifndef OLCUC
+# define OLCUC 0
+#endif
+#ifndef ONLCR
+# define ONLCR 0
+#endif
+#ifndef ONLRET
+# define ONLRET 0
+#endif
+#ifndef ONOCR
+# define ONOCR 0
+#endif
+#ifndef OXTABS
+# define OXTABS 0
+#endif
+#ifndef TABDLY
+# define TABDLY 0
+#endif
+#ifndef TAB1
+# define TAB1 0
+#endif
+#ifndef TAB2
+# define TAB2 0
+#endif
+#ifndef TOSTOP
+# define TOSTOP 0
+#endif
+#ifndef VDSUSP
+# define VDSUSP 0
+#endif
+#ifndef VEOL2
+# define VEOL2 0
+#endif
+#ifndef VFLUSHO
+# define VFLUSHO 0
+#endif
+#ifndef VLNEXT
+# define VLNEXT 0
+#endif
+#ifndef VREPRINT
+# define VREPRINT 0
+#endif
+#ifndef VSTATUS
+# define VSTATUS 0
+#endif
+#ifndef VSWTCH
+# define VSWTCH 0
+#endif
+#ifndef VTDLY
+# define VTDLY 0
+#endif
+#ifndef VWERASE
+# define VWERASE 0
+#endif
+#ifndef XCASE
+# define XCASE 0
+#endif
+#ifndef IUTF8
+# define IUTF8 0
 #endif
 
-/* Which speeds to set */
+/* Which speeds to set */
 enum speed_setting {
        input_speed, output_speed, both_speeds
 };
 
-/* Which member(s) of `struct termios' a mode uses.  */
-enum mode_type {
-       /* Do NOT change the order or values, as mode_type_flag()
-        * depends on them. */
+/* Which member(s) of 'struct termios' a mode uses */
+enum {
        control, input, output, local, combination
 };
+static tcflag_t *get_ptr_to_tcflag(unsigned type, const struct termios *mode)
+{
+       static const uint8_t tcflag_offsets[] ALIGN1 = {
+               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;
+}
+
+/* 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                 */
 
 
-static const char evenp     [] = "evenp";
-static const char raw       [] = "raw";
-static const char stty_min  [] = "min";
-static const char stty_time [] = "time";
-static const char stty_swtch[] = "swtch";
-static const char stty_eol  [] = "eol";
-static const char stty_eof  [] = "eof";
-static const char parity    [] = "parity";
-static const char stty_oddp [] = "oddp";
-static const char stty_nl   [] = "nl";
-static const char stty_ek   [] = "ek";
-static const char stty_sane [] = "sane";
-static const char cbreak    [] = "cbreak";
-static const char stty_pass8[] = "pass8";
-static const char litout    [] = "litout";
-static const char cooked    [] = "cooked";
-static const char decctlq   [] = "decctlq";
-static const char stty_tabs [] = "tabs";
-static const char stty_lcase[] = "lcase";
-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 `-'. */
-#define OMIT 8                  /* Don't display value.                 */
-
-/* Each mode.  */
+/* Each mode.
+ * This structure should be kept as small as humanly possible.
+ */
 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 long bits;     /* Bits to set for this mode.            */
+       const uint8_t type;           /* Which structure element to change    */
+       const uint8_t flags;          /* Setting and display options          */
+       /* only these values are ever used, so... */
+#if   (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x100
+       const uint8_t mask;
+#elif (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x10000
+       const uint16_t mask;
+#else
+       const tcflag_t mask;          /* Other bits to turn off for this mode */
+#endif
+       /* was using short here, but ppc32 was unhappy */
+       const tcflag_t bits;          /* Bits to set for this mode            */
 };
 
-#define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B }
+enum {
+       /* Must match mode_name[] and mode_info[] order! */
+       IDX_evenp = 0,
+       IDX_parity,
+       IDX_oddp,
+       IDX_nl,
+       IDX_ek,
+       IDX_sane,
+       IDX_cooked,
+       IDX_raw,
+       IDX_pass8,
+       IDX_litout,
+       IDX_cbreak,
+       IDX_crt,
+       IDX_dec,
+#if IXANY
+       IDX_decctlq,
+#endif
+#if TABDLY || OXTABS
+       IDX_tabs,
+#endif
+#if XCASE && IUCLC && OLCUC
+       IDX_lcase,
+       IDX_LCASE,
+#endif
+};
 
-static const struct mode_info mode_info[] = {
-       MI_ENTRY("parenb",   control,     REV,               PARENB,     0 ),
-       MI_ENTRY("parodd",   control,     REV,               PARODD,     0 ),
-       MI_ENTRY("cs5",      control,     0,                 CS5,     CSIZE),
-       MI_ENTRY("cs6",      control,     0,                 CS6,     CSIZE),
-       MI_ENTRY("cs7",      control,     0,                 CS7,     CSIZE),
-       MI_ENTRY("cs8",      control,     0,                 CS8,     CSIZE),
-       MI_ENTRY("hupcl",    control,     REV,               HUPCL,      0 ),
-       MI_ENTRY("hup",      control,     REV        | OMIT, HUPCL,      0 ),
-       MI_ENTRY("cstopb",   control,     REV,               CSTOPB,     0 ),
-       MI_ENTRY("cread",    control,     SANE_SET   | REV,  CREAD,      0 ),
-       MI_ENTRY("clocal",   control,     REV,               CLOCAL,     0 ),
-#ifdef CRTSCTS
-       MI_ENTRY("crtscts",  control,     REV,               CRTSCTS,    0 ),
-#endif
-       MI_ENTRY("ignbrk",   input,       SANE_UNSET | REV,  IGNBRK,     0 ),
-       MI_ENTRY("brkint",   input,       SANE_SET   | REV,  BRKINT,     0 ),
-       MI_ENTRY("ignpar",   input,       REV,               IGNPAR,     0 ),
-       MI_ENTRY("parmrk",   input,       REV,               PARMRK,     0 ),
-       MI_ENTRY("inpck",    input,       REV,               INPCK,      0 ),
-       MI_ENTRY("istrip",   input,       REV,               ISTRIP,     0 ),
-       MI_ENTRY("inlcr",    input,       SANE_UNSET | REV,  INLCR,      0 ),
-       MI_ENTRY("igncr",    input,       SANE_UNSET | REV,  IGNCR,      0 ),
-       MI_ENTRY("icrnl",    input,       SANE_SET   | REV,  ICRNL,      0 ),
-       MI_ENTRY("ixon",     input,       REV,               IXON,       0 ),
-       MI_ENTRY("ixoff",    input,       SANE_UNSET | REV,  IXOFF,      0 ),
-       MI_ENTRY("tandem",   input,       REV        | OMIT, IXOFF,      0 ),
-#ifdef IUCLC
-       MI_ENTRY("iuclc",    input,       SANE_UNSET | REV,  IUCLC,      0 ),
-#endif
-#ifdef IXANY
-       MI_ENTRY("ixany",    input,       SANE_UNSET | REV,  IXANY,      0 ),
-#endif
-#ifdef IMAXBEL
-       MI_ENTRY("imaxbel",  input,       SANE_SET   | REV,  IMAXBEL,    0 ),
-#endif
-       MI_ENTRY("opost",    output,      SANE_SET   | REV,  OPOST,      0 ),
-#ifdef OLCUC
-       MI_ENTRY("olcuc",    output,      SANE_UNSET | REV,  OLCUC,      0 ),
-#endif
-#ifdef OCRNL
-       MI_ENTRY("ocrnl",    output,      SANE_UNSET | REV,  OCRNL,      0 ),
-#endif
-#ifdef ONLCR
-       MI_ENTRY("onlcr",    output,      SANE_SET   | REV,  ONLCR,      0 ),
-#endif
-#ifdef ONOCR
-       MI_ENTRY("onocr",    output,      SANE_UNSET | REV,  ONOCR,      0 ),
-#endif
-#ifdef ONLRET
-       MI_ENTRY("onlret",   output,      SANE_UNSET | REV,  ONLRET,     0 ),
-#endif
-#ifdef OFILL
-       MI_ENTRY("ofill",    output,      SANE_UNSET | REV,  OFILL,      0 ),
-#endif
-#ifdef OFDEL
-       MI_ENTRY("ofdel",    output,      SANE_UNSET | REV,  OFDEL,      0 ),
-#endif
-#ifdef NLDLY
-       MI_ENTRY("nl1",      output,      SANE_UNSET,        NL1,     NLDLY),
-       MI_ENTRY("nl0",      output,      SANE_SET,          NL0,     NLDLY),
-#endif
-#ifdef CRDLY
-       MI_ENTRY("cr3",      output,      SANE_UNSET,        CR3,     CRDLY),
-       MI_ENTRY("cr2",      output,      SANE_UNSET,        CR2,     CRDLY),
-       MI_ENTRY("cr1",      output,      SANE_UNSET,        CR1,     CRDLY),
-       MI_ENTRY("cr0",      output,      SANE_SET,          CR0,     CRDLY),
-#endif
-
-#ifdef TABDLY
-       MI_ENTRY("tab3",     output,      SANE_UNSET,        TAB3,   TABDLY),
-       MI_ENTRY("tab2",     output,      SANE_UNSET,        TAB2,   TABDLY),
-       MI_ENTRY("tab1",     output,      SANE_UNSET,        TAB1,   TABDLY),
-       MI_ENTRY("tab0",     output,      SANE_SET,          TAB0,   TABDLY),
+#define MI_ENTRY(N,T,F,B,M) N "\0"
+
+/* Mode names given on command line */
+static const char mode_name[] ALIGN1 =
+       MI_ENTRY("evenp",    combination, REV        | OMIT, 0,          0 )
+       MI_ENTRY("parity",   combination, REV        | OMIT, 0,          0 )
+       MI_ENTRY("oddp",     combination, REV        | OMIT, 0,          0 )
+       MI_ENTRY("nl",       combination, REV        | OMIT, 0,          0 )
+       MI_ENTRY("ek",       combination, OMIT,              0,          0 )
+       MI_ENTRY("sane",     combination, OMIT,              0,          0 )
+       MI_ENTRY("cooked",   combination, REV        | OMIT, 0,          0 )
+       MI_ENTRY("raw",      combination, REV        | OMIT, 0,          0 )
+       MI_ENTRY("pass8",    combination, REV        | OMIT, 0,          0 )
+       MI_ENTRY("litout",   combination, REV        | OMIT, 0,          0 )
+       MI_ENTRY("cbreak",   combination, REV        | OMIT, 0,          0 )
+       MI_ENTRY("crt",      combination, OMIT,              0,          0 )
+       MI_ENTRY("dec",      combination, OMIT,              0,          0 )
+#if IXANY
+       MI_ENTRY("decctlq",  combination, REV        | OMIT, 0,          0 )
+#endif
+#if TABDLY || OXTABS
+       MI_ENTRY("tabs",     combination, REV        | OMIT, 0,          0 )
+#endif
+#if XCASE && IUCLC && OLCUC
+       MI_ENTRY("lcase",    combination, REV        | OMIT, 0,          0 )
+       MI_ENTRY("LCASE",    combination, REV        | OMIT, 0,          0 )
+#endif
+       MI_ENTRY("parenb",   control,     REV,               PARENB,     0 )
+       MI_ENTRY("parodd",   control,     REV,               PARODD,     0 )
+#if CMSPAR
+       MI_ENTRY("cmspar",   control,     REV,               CMSPAR,     0 )
+#endif
+       MI_ENTRY("cs5",      control,     0,                 CS5,     CSIZE)
+       MI_ENTRY("cs6",      control,     0,                 CS6,     CSIZE)
+       MI_ENTRY("cs7",      control,     0,                 CS7,     CSIZE)
+       MI_ENTRY("cs8",      control,     0,                 CS8,     CSIZE)
+       MI_ENTRY("hupcl",    control,     REV,               HUPCL,      0 )
+       MI_ENTRY("hup",      control,     REV        | OMIT, HUPCL,      0 )
+       MI_ENTRY("cstopb",   control,     REV,               CSTOPB,     0 )
+       MI_ENTRY("cread",    control,     SANE_SET   | REV,  CREAD,      0 )
+       MI_ENTRY("clocal",   control,     REV,               CLOCAL,     0 )
+#if CRTSCTS
+       MI_ENTRY("crtscts",  control,     REV,               CRTSCTS,    0 )
+#endif
+       MI_ENTRY("ignbrk",   input,       SANE_UNSET | REV,  IGNBRK,     0 )
+       MI_ENTRY("brkint",   input,       SANE_SET   | REV,  BRKINT,     0 )
+       MI_ENTRY("ignpar",   input,       REV,               IGNPAR,     0 )
+       MI_ENTRY("parmrk",   input,       REV,               PARMRK,     0 )
+       MI_ENTRY("inpck",    input,       REV,               INPCK,      0 )
+       MI_ENTRY("istrip",   input,       REV,               ISTRIP,     0 )
+       MI_ENTRY("inlcr",    input,       SANE_UNSET | REV,  INLCR,      0 )
+       MI_ENTRY("igncr",    input,       SANE_UNSET | REV,  IGNCR,      0 )
+       MI_ENTRY("icrnl",    input,       SANE_SET   | REV,  ICRNL,      0 )
+       MI_ENTRY("ixon",     input,       REV,               IXON,       0 )
+       MI_ENTRY("ixoff",    input,       SANE_UNSET | REV,  IXOFF,      0 )
+       MI_ENTRY("tandem",   input,       OMIT       | REV,  IXOFF,      0 )
+#if IUCLC
+       MI_ENTRY("iuclc",    input,       SANE_UNSET | REV,  IUCLC,      0 )
+#endif
+#if IXANY
+       MI_ENTRY("ixany",    input,       SANE_UNSET | REV,  IXANY,      0 )
+#endif
+#if IMAXBEL
+       MI_ENTRY("imaxbel",  input,       SANE_SET   | REV,  IMAXBEL,    0 )
+#endif
+#if IUTF8
+       MI_ENTRY("iutf8",    input,       SANE_UNSET | REV,  IUTF8,      0 )
+#endif
+       MI_ENTRY("opost",    output,      SANE_SET   | REV,  OPOST,      0 )
+#if OLCUC
+       MI_ENTRY("olcuc",    output,      SANE_UNSET | REV,  OLCUC,      0 )
+#endif
+#if OCRNL
+       MI_ENTRY("ocrnl",    output,      SANE_UNSET | REV,  OCRNL,      0 )
+#endif
+#if ONLCR
+       MI_ENTRY("onlcr",    output,      SANE_SET   | REV,  ONLCR,      0 )
+#endif
+#if ONOCR
+       MI_ENTRY("onocr",    output,      SANE_UNSET | REV,  ONOCR,      0 )
+#endif
+#if ONLRET
+       MI_ENTRY("onlret",   output,      SANE_UNSET | REV,  ONLRET,     0 )
+#endif
+#if OFILL
+       MI_ENTRY("ofill",    output,      SANE_UNSET | REV,  OFILL,      0 )
+#endif
+#if OFDEL
+       MI_ENTRY("ofdel",    output,      SANE_UNSET | REV,  OFDEL,      0 )
+#endif
+#if NLDLY
+       MI_ENTRY("nl1",      output,      SANE_UNSET,        NL1,     NLDLY)
+       MI_ENTRY("nl0",      output,      SANE_SET,          NL0,     NLDLY)
+#endif
+#if CRDLY
+       MI_ENTRY("cr3",      output,      SANE_UNSET,        CR3,     CRDLY)
+       MI_ENTRY("cr2",      output,      SANE_UNSET,        CR2,     CRDLY)
+       MI_ENTRY("cr1",      output,      SANE_UNSET,        CR1,     CRDLY)
+       MI_ENTRY("cr0",      output,      SANE_SET,          CR0,     CRDLY)
+#endif
+
+#if TABDLY
+       MI_ENTRY("tab3",     output,      SANE_UNSET,        TAB3,   TABDLY)
+# if TAB2
+       MI_ENTRY("tab2",     output,      SANE_UNSET,        TAB2,   TABDLY)
+# endif
+# if TAB1
+       MI_ENTRY("tab1",     output,      SANE_UNSET,        TAB1,   TABDLY)
+# endif
+       MI_ENTRY("tab0",     output,      SANE_SET,          TAB0,   TABDLY)
 #else
-# ifdef OXTABS
-       MI_ENTRY("tab3",     output,      SANE_UNSET,        OXTABS,     0 ),
+# if OXTABS
+       MI_ENTRY("tab3",     output,      SANE_UNSET,        OXTABS,     0 )
 # endif
 #endif
 
-#ifdef BSDLY
-       MI_ENTRY("bs1",      output,      SANE_UNSET,        BS1,     BSDLY),
-       MI_ENTRY("bs0",      output,      SANE_SET,          BS0,     BSDLY),
-#endif
-#ifdef VTDLY
-       MI_ENTRY("vt1",      output,      SANE_UNSET,        VT1,     VTDLY),
-       MI_ENTRY("vt0",      output,      SANE_SET,          VT0,     VTDLY),
-#endif
-#ifdef FFDLY
-       MI_ENTRY("ff1",      output,      SANE_UNSET,        FF1,     FFDLY),
-       MI_ENTRY("ff0",      output,      SANE_SET,          FF0,     FFDLY),
-#endif
-       MI_ENTRY("isig",     local,       SANE_SET   | REV,  ISIG,       0 ),
-       MI_ENTRY("icanon",   local,       SANE_SET   | REV,  ICANON,     0 ),
-#ifdef IEXTEN
-       MI_ENTRY("iexten",   local,       SANE_SET   | REV,  IEXTEN,     0 ),
-#endif
-       MI_ENTRY("echo",     local,       SANE_SET   | REV,  ECHO,       0 ),
-       MI_ENTRY("echoe",    local,       SANE_SET   | REV,  ECHOE,      0 ),
-       MI_ENTRY("crterase", local,       REV        | OMIT, ECHOE,      0 ),
-       MI_ENTRY("echok",    local,       SANE_SET   | REV,  ECHOK,      0 ),
-       MI_ENTRY("echonl",   local,       SANE_UNSET | REV,  ECHONL,     0 ),
-       MI_ENTRY("noflsh",   local,       SANE_UNSET | REV,  NOFLSH,     0 ),
-#ifdef XCASE
-       MI_ENTRY("xcase",    local,       SANE_UNSET | REV,  XCASE,      0 ),
-#endif
-#ifdef TOSTOP
-       MI_ENTRY("tostop",   local,       SANE_UNSET | REV,  TOSTOP,     0 ),
-#endif
-#ifdef ECHOPRT
-       MI_ENTRY("echoprt",  local,       SANE_UNSET | REV,  ECHOPRT,    0 ),
-       MI_ENTRY("prterase", local,       REV | OMIT,        ECHOPRT,    0 ),
-#endif
-#ifdef ECHOCTL
-       MI_ENTRY("echoctl",  local,       SANE_SET   | REV,  ECHOCTL,    0 ),
-       MI_ENTRY("ctlecho",  local,       REV        | OMIT, ECHOCTL,    0 ),
-#endif
-#ifdef ECHOKE
-       MI_ENTRY("echoke",   local,       SANE_SET   | REV,  ECHOKE,     0 ),
-       MI_ENTRY("crtkill",  local,       REV        | OMIT, ECHOKE,     0 ),
-#endif
-       MI_ENTRY(evenp,      combination, REV        | OMIT, 0,          0 ),
-       MI_ENTRY(parity,     combination, REV        | OMIT, 0,          0 ),
-       MI_ENTRY(stty_oddp,  combination, REV        | OMIT, 0,          0 ),
-       MI_ENTRY(stty_nl,    combination, REV        | OMIT, 0,          0 ),
-       MI_ENTRY(stty_ek,    combination, OMIT,              0,          0 ),
-       MI_ENTRY(stty_sane,  combination, OMIT,              0,          0 ),
-       MI_ENTRY(cooked,     combination, REV        | OMIT, 0,          0 ),
-       MI_ENTRY(raw,        combination, REV        | OMIT, 0,          0 ),
-       MI_ENTRY(stty_pass8, combination, REV        | OMIT, 0,          0 ),
-       MI_ENTRY(litout,     combination, REV        | OMIT, 0,          0 ),
-       MI_ENTRY(cbreak,     combination, REV        | OMIT, 0,          0 ),
-#ifdef IXANY
-       MI_ENTRY(decctlq,    combination, REV        | OMIT, 0,          0 ),
-#endif
-#if defined (TABDLY) || defined (OXTABS)
-       MI_ENTRY(stty_tabs,  combination, REV        | OMIT, 0,          0 ),
-#endif
-#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
-       MI_ENTRY(stty_lcase, combination, REV        | OMIT, 0,          0 ),
-       MI_ENTRY(stty_LCASE, combination, REV        | OMIT, 0,          0 ),
-#endif
-       MI_ENTRY(stty_crt,   combination, OMIT,              0,          0 ),
-       MI_ENTRY(stty_dec,   combination, OMIT,              0,          0 ),
-};
-
-enum {
-       NUM_mode_info =
-       (sizeof(mode_info) / sizeof(struct mode_info))
-};
+#if BSDLY
+       MI_ENTRY("bs1",      output,      SANE_UNSET,        BS1,     BSDLY)
+       MI_ENTRY("bs0",      output,      SANE_SET,          BS0,     BSDLY)
+#endif
+#if VTDLY
+       MI_ENTRY("vt1",      output,      SANE_UNSET,        VT1,     VTDLY)
+       MI_ENTRY("vt0",      output,      SANE_SET,          VT0,     VTDLY)
+#endif
+#if FFDLY
+       MI_ENTRY("ff1",      output,      SANE_UNSET,        FF1,     FFDLY)
+       MI_ENTRY("ff0",      output,      SANE_SET,          FF0,     FFDLY)
+#endif
+       MI_ENTRY("isig",     local,       SANE_SET   | REV,  ISIG,       0 )
+       MI_ENTRY("icanon",   local,       SANE_SET   | REV,  ICANON,     0 )
+#if IEXTEN
+       MI_ENTRY("iexten",   local,       SANE_SET   | REV,  IEXTEN,     0 )
+#endif
+       MI_ENTRY("echo",     local,       SANE_SET   | REV,  ECHO,       0 )
+       MI_ENTRY("echoe",    local,       SANE_SET   | REV,  ECHOE,      0 )
+       MI_ENTRY("crterase", local,       OMIT       | REV,  ECHOE,      0 )
+       MI_ENTRY("echok",    local,       SANE_SET   | REV,  ECHOK,      0 )
+       MI_ENTRY("echonl",   local,       SANE_UNSET | REV,  ECHONL,     0 )
+       MI_ENTRY("noflsh",   local,       SANE_UNSET | REV,  NOFLSH,     0 )
+#if XCASE
+       MI_ENTRY("xcase",    local,       SANE_UNSET | REV,  XCASE,      0 )
+#endif
+#if TOSTOP
+       MI_ENTRY("tostop",   local,       SANE_UNSET | REV,  TOSTOP,     0 )
+#endif
+#if ECHOPRT
+       MI_ENTRY("echoprt",  local,       SANE_UNSET | REV,  ECHOPRT,    0 )
+       MI_ENTRY("prterase", local,       OMIT       | REV,  ECHOPRT,    0 )
+#endif
+#if ECHOCTL
+       MI_ENTRY("echoctl",  local,       SANE_SET   | REV,  ECHOCTL,    0 )
+       MI_ENTRY("ctlecho",  local,       OMIT       | REV,  ECHOCTL,    0 )
+#endif
+#if ECHOKE
+       MI_ENTRY("echoke",   local,       SANE_SET   | REV,  ECHOKE,     0 )
+       MI_ENTRY("crtkill",  local,       OMIT       | REV,  ECHOKE,     0 )
+#endif
+       MI_ENTRY("flusho",   local,       SANE_UNSET | REV,  FLUSHO,     0 )
+#ifdef EXTPROC
+       MI_ENTRY("extproc",  local,       SANE_UNSET | REV,  EXTPROC,    0 )
+#endif
+       ;
 
-/* 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 offset;                           /* Offset in c_cc.  */
-};
+#undef MI_ENTRY
+#define MI_ENTRY(N,T,F,B,M) { T, F, M, B },
 
-/* Control characters. */
+static const struct mode_info mode_info[] = {
+       /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */
+       MI_ENTRY("evenp",    combination, REV        | OMIT, 0,          0 )
+       MI_ENTRY("parity",   combination, REV        | OMIT, 0,          0 )
+       MI_ENTRY("oddp",     combination, REV        | OMIT, 0,          0 )
+       MI_ENTRY("nl",       combination, REV        | OMIT, 0,          0 )
+       MI_ENTRY("ek",       combination, OMIT,              0,          0 )
+       MI_ENTRY("sane",     combination, OMIT,              0,          0 )
+       MI_ENTRY("cooked",   combination, REV        | OMIT, 0,          0 )
+       MI_ENTRY("raw",      combination, REV        | OMIT, 0,          0 )
+       MI_ENTRY("pass8",    combination, REV        | OMIT, 0,          0 )
+       MI_ENTRY("litout",   combination, REV        | OMIT, 0,          0 )
+       MI_ENTRY("cbreak",   combination, REV        | OMIT, 0,          0 )
+       MI_ENTRY("crt",      combination, OMIT,              0,          0 )
+       MI_ENTRY("dec",      combination, OMIT,              0,          0 )
+#if IXANY
+       MI_ENTRY("decctlq",  combination, REV        | OMIT, 0,          0 )
+#endif
+#if TABDLY || OXTABS
+       MI_ENTRY("tabs",     combination, REV        | OMIT, 0,          0 )
+#endif
+#if XCASE && IUCLC && OLCUC
+       MI_ENTRY("lcase",    combination, REV        | OMIT, 0,          0 )
+       MI_ENTRY("LCASE",    combination, REV        | OMIT, 0,          0 )
+#endif
+       MI_ENTRY("parenb",   control,     REV,               PARENB,     0 )
+       MI_ENTRY("parodd",   control,     REV,               PARODD,     0 )
+#if CMSPAR
+       MI_ENTRY("cmspar",   control,     REV,               CMSPAR,     0 )
+#endif
+       MI_ENTRY("cs5",      control,     0,                 CS5,     CSIZE)
+       MI_ENTRY("cs6",      control,     0,                 CS6,     CSIZE)
+       MI_ENTRY("cs7",      control,     0,                 CS7,     CSIZE)
+       MI_ENTRY("cs8",      control,     0,                 CS8,     CSIZE)
+       MI_ENTRY("hupcl",    control,     REV,               HUPCL,      0 )
+       MI_ENTRY("hup",      control,     REV        | OMIT, HUPCL,      0 )
+       MI_ENTRY("cstopb",   control,     REV,               CSTOPB,     0 )
+       MI_ENTRY("cread",    control,     SANE_SET   | REV,  CREAD,      0 )
+       MI_ENTRY("clocal",   control,     REV,               CLOCAL,     0 )
+#if CRTSCTS
+       MI_ENTRY("crtscts",  control,     REV,               CRTSCTS,    0 )
+#endif
+       MI_ENTRY("ignbrk",   input,       SANE_UNSET | REV,  IGNBRK,     0 )
+       MI_ENTRY("brkint",   input,       SANE_SET   | REV,  BRKINT,     0 )
+       MI_ENTRY("ignpar",   input,       REV,               IGNPAR,     0 )
+       MI_ENTRY("parmrk",   input,       REV,               PARMRK,     0 )
+       MI_ENTRY("inpck",    input,       REV,               INPCK,      0 )
+       MI_ENTRY("istrip",   input,       REV,               ISTRIP,     0 )
+       MI_ENTRY("inlcr",    input,       SANE_UNSET | REV,  INLCR,      0 )
+       MI_ENTRY("igncr",    input,       SANE_UNSET | REV,  IGNCR,      0 )
+       MI_ENTRY("icrnl",    input,       SANE_SET   | REV,  ICRNL,      0 )
+       MI_ENTRY("ixon",     input,       REV,               IXON,       0 )
+       MI_ENTRY("ixoff",    input,       SANE_UNSET | REV,  IXOFF,      0 )
+       MI_ENTRY("tandem",   input,       OMIT       | REV,  IXOFF,      0 )
+#if IUCLC
+       MI_ENTRY("iuclc",    input,       SANE_UNSET | REV,  IUCLC,      0 )
+#endif
+#if IXANY
+       MI_ENTRY("ixany",    input,       SANE_UNSET | REV,  IXANY,      0 )
+#endif
+#if IMAXBEL
+       MI_ENTRY("imaxbel",  input,       SANE_SET   | REV,  IMAXBEL,    0 )
+#endif
+#if IUTF8
+       MI_ENTRY("iutf8",    input,       SANE_UNSET | REV,  IUTF8,      0 )
+#endif
+       MI_ENTRY("opost",    output,      SANE_SET   | REV,  OPOST,      0 )
+#if OLCUC
+       MI_ENTRY("olcuc",    output,      SANE_UNSET | REV,  OLCUC,      0 )
+#endif
+#if OCRNL
+       MI_ENTRY("ocrnl",    output,      SANE_UNSET | REV,  OCRNL,      0 )
+#endif
+#if ONLCR
+       MI_ENTRY("onlcr",    output,      SANE_SET   | REV,  ONLCR,      0 )
+#endif
+#if ONOCR
+       MI_ENTRY("onocr",    output,      SANE_UNSET | REV,  ONOCR,      0 )
+#endif
+#if ONLRET
+       MI_ENTRY("onlret",   output,      SANE_UNSET | REV,  ONLRET,     0 )
+#endif
+#if OFILL
+       MI_ENTRY("ofill",    output,      SANE_UNSET | REV,  OFILL,      0 )
+#endif
+#if OFDEL
+       MI_ENTRY("ofdel",    output,      SANE_UNSET | REV,  OFDEL,      0 )
+#endif
+#if NLDLY
+       MI_ENTRY("nl1",      output,      SANE_UNSET,        NL1,     NLDLY)
+       MI_ENTRY("nl0",      output,      SANE_SET,          NL0,     NLDLY)
+#endif
+#if CRDLY
+       MI_ENTRY("cr3",      output,      SANE_UNSET,        CR3,     CRDLY)
+       MI_ENTRY("cr2",      output,      SANE_UNSET,        CR2,     CRDLY)
+       MI_ENTRY("cr1",      output,      SANE_UNSET,        CR1,     CRDLY)
+       MI_ENTRY("cr0",      output,      SANE_SET,          CR0,     CRDLY)
+#endif
+
+#if TABDLY
+       MI_ENTRY("tab3",     output,      SANE_UNSET,        TAB3,   TABDLY)
+# if TAB2
+       MI_ENTRY("tab2",     output,      SANE_UNSET,        TAB2,   TABDLY)
+# endif
+# if TAB1
+       MI_ENTRY("tab1",     output,      SANE_UNSET,        TAB1,   TABDLY)
+# endif
+       MI_ENTRY("tab0",     output,      SANE_SET,          TAB0,   TABDLY)
+#else
+# if OXTABS
+       MI_ENTRY("tab3",     output,      SANE_UNSET,        OXTABS,     0 )
+# endif
+#endif
 
-static const struct control_info control_info[] = {
-       {"intr",     CINTR,   VINTR},
-       {"quit",     CQUIT,   VQUIT},
-       {"erase",    CERASE,  VERASE},
-       {"kill",     CKILL,   VKILL},
-       {stty_eof,   CEOF,    VEOF},
-       {stty_eol,   CEOL,    VEOL},
-#ifdef VEOL2
-       {"eol2",     CEOL2,   VEOL2},
+#if BSDLY
+       MI_ENTRY("bs1",      output,      SANE_UNSET,        BS1,     BSDLY)
+       MI_ENTRY("bs0",      output,      SANE_SET,          BS0,     BSDLY)
 #endif
-#ifdef VSWTCH
-       {stty_swtch, CSWTCH,  VSWTCH},
+#if VTDLY
+       MI_ENTRY("vt1",      output,      SANE_UNSET,        VT1,     VTDLY)
+       MI_ENTRY("vt0",      output,      SANE_SET,          VT0,     VTDLY)
 #endif
-       {"start",    CSTART,  VSTART},
-       {"stop",     CSTOP,   VSTOP},
-       {"susp",     CSUSP,   VSUSP},
-#ifdef VDSUSP
-       {"dsusp",    CDSUSP,  VDSUSP},
+#if FFDLY
+       MI_ENTRY("ff1",      output,      SANE_UNSET,        FF1,     FFDLY)
+       MI_ENTRY("ff0",      output,      SANE_SET,          FF0,     FFDLY)
 #endif
-#ifdef VREPRINT
-       {"rprnt",    CRPRNT,  VREPRINT},
+       MI_ENTRY("isig",     local,       SANE_SET   | REV,  ISIG,       0 )
+       MI_ENTRY("icanon",   local,       SANE_SET   | REV,  ICANON,     0 )
+#if IEXTEN
+       MI_ENTRY("iexten",   local,       SANE_SET   | REV,  IEXTEN,     0 )
 #endif
-#ifdef VWERASE
-       {"werase",   CWERASE, VWERASE},
+       MI_ENTRY("echo",     local,       SANE_SET   | REV,  ECHO,       0 )
+       MI_ENTRY("echoe",    local,       SANE_SET   | REV,  ECHOE,      0 )
+       MI_ENTRY("crterase", local,       OMIT       | REV,  ECHOE,      0 )
+       MI_ENTRY("echok",    local,       SANE_SET   | REV,  ECHOK,      0 )
+       MI_ENTRY("echonl",   local,       SANE_UNSET | REV,  ECHONL,     0 )
+       MI_ENTRY("noflsh",   local,       SANE_UNSET | REV,  NOFLSH,     0 )
+#if XCASE
+       MI_ENTRY("xcase",    local,       SANE_UNSET | REV,  XCASE,      0 )
 #endif
-#ifdef VLNEXT
-       {"lnext",    CLNEXT,  VLNEXT},
+#if TOSTOP
+       MI_ENTRY("tostop",   local,       SANE_UNSET | REV,  TOSTOP,     0 )
 #endif
-#ifdef VFLUSHO
-       {"flush",    CFLUSHO, VFLUSHO},
+#if ECHOPRT
+       MI_ENTRY("echoprt",  local,       SANE_UNSET | REV,  ECHOPRT,    0 )
+       MI_ENTRY("prterase", local,       OMIT       | REV,  ECHOPRT,    0 )
 #endif
-#ifdef VSTATUS
-       {"status",   CSTATUS, VSTATUS},
+#if ECHOCTL
+       MI_ENTRY("echoctl",  local,       SANE_SET   | REV,  ECHOCTL,    0 )
+       MI_ENTRY("ctlecho",  local,       OMIT       | REV,  ECHOCTL,    0 )
 #endif
-       /* These must be last because of the display routines. */
-       {stty_min,   1,       VMIN},
-       {stty_time,  0,       VTIME},
+#if ECHOKE
+       MI_ENTRY("echoke",   local,       SANE_SET   | REV,  ECHOKE,     0 )
+       MI_ENTRY("crtkill",  local,       OMIT       | REV,  ECHOKE,     0 )
+#endif
+       MI_ENTRY("flusho",   local,       SANE_UNSET | REV,  FLUSHO,     0 )
+#ifdef EXTPROC
+       MI_ENTRY("extproc",  local,       SANE_UNSET | REV,  EXTPROC,    0 )
+#endif
+};
+
+enum {
+       NUM_mode_info = ARRAY_SIZE(mode_info)
+};
+
+
+/* Control characters */
+struct control_info {
+       const uint8_t saneval;  /* Value to set for 'stty sane' */
+       const uint8_t offset;   /* Offset in c_cc */
+};
+
+enum {
+       /* Must match control_name[] and control_info[] order! */
+       CIDX_intr = 0,
+       CIDX_quit,
+       CIDX_erase,
+       CIDX_kill,
+       CIDX_eof,
+       CIDX_eol,
+#if VEOL2
+       CIDX_eol2,
+#endif
+#if VSWTCH
+       CIDX_swtch,
+#endif
+       CIDX_start,
+       CIDX_stop,
+       CIDX_susp,
+#if VDSUSP
+       CIDX_dsusp,
+#endif
+#if VREPRINT
+       CIDX_rprnt,
+#endif
+#if VWERASE
+       CIDX_werase,
+#endif
+#if VLNEXT
+       CIDX_lnext,
+#endif
+#if VFLUSHO
+       CIDX_flush,
+#endif
+#if VSTATUS
+       CIDX_status,
+#endif
+       CIDX_min,
+       CIDX_time,
+};
+
+#define CI_ENTRY(n,s,o) n "\0"
+
+/* Name given on command line */
+static const char control_name[] ALIGN1 =
+       CI_ENTRY("intr",     CINTR,   VINTR   )
+       CI_ENTRY("quit",     CQUIT,   VQUIT   )
+       CI_ENTRY("erase",    CERASE,  VERASE  )
+       CI_ENTRY("kill",     CKILL,   VKILL   )
+       CI_ENTRY("eof",      CEOF,    VEOF    )
+       CI_ENTRY("eol",      CEOL,    VEOL    )
+#if VEOL2
+       CI_ENTRY("eol2",     CEOL2,   VEOL2   )
+#endif
+#if VSWTCH
+       CI_ENTRY("swtch",    CSWTCH,  VSWTCH  )
+#endif
+       CI_ENTRY("start",    CSTART,  VSTART  )
+       CI_ENTRY("stop",     CSTOP,   VSTOP   )
+       CI_ENTRY("susp",     CSUSP,   VSUSP   )
+#if VDSUSP
+       CI_ENTRY("dsusp",    CDSUSP,  VDSUSP  )
+#endif
+#if VREPRINT
+       CI_ENTRY("rprnt",    CRPRNT,  VREPRINT)
+#endif
+#if VWERASE
+       CI_ENTRY("werase",   CWERASE, VWERASE )
+#endif
+#if VLNEXT
+       CI_ENTRY("lnext",    CLNEXT,  VLNEXT  )
+#endif
+#if VFLUSHO
+       CI_ENTRY("flush",    CFLUSHO, VFLUSHO )
+#endif
+#if VSTATUS
+       CI_ENTRY("status",   CSTATUS, VSTATUS )
+#endif
+       /* These must be last because of the display routines */
+       CI_ENTRY("min",      1,       VMIN    )
+       CI_ENTRY("time",     0,       VTIME   )
+       ;
+
+#undef CI_ENTRY
+#define CI_ENTRY(n,s,o) { s, o },
+
+static const struct control_info control_info[] ALIGN2 = {
+       /* This should be verbatim cut-n-paste copy of the above CI_ENTRYs */
+       CI_ENTRY("intr",     CINTR,   VINTR   )
+       CI_ENTRY("quit",     CQUIT,   VQUIT   )
+       CI_ENTRY("erase",    CERASE,  VERASE  )
+       CI_ENTRY("kill",     CKILL,   VKILL   )
+       CI_ENTRY("eof",      CEOF,    VEOF    )
+       CI_ENTRY("eol",      CEOL,    VEOL    )
+#if VEOL2
+       CI_ENTRY("eol2",     CEOL2,   VEOL2   )
+#endif
+#if VSWTCH
+       CI_ENTRY("swtch",    CSWTCH,  VSWTCH  )
+#endif
+       CI_ENTRY("start",    CSTART,  VSTART  )
+       CI_ENTRY("stop",     CSTOP,   VSTOP   )
+       CI_ENTRY("susp",     CSUSP,   VSUSP   )
+#if VDSUSP
+       CI_ENTRY("dsusp",    CDSUSP,  VDSUSP  )
+#endif
+#if VREPRINT
+       CI_ENTRY("rprnt",    CRPRNT,  VREPRINT)
+#endif
+#if VWERASE
+       CI_ENTRY("werase",   CWERASE, VWERASE )
+#endif
+#if VLNEXT
+       CI_ENTRY("lnext",    CLNEXT,  VLNEXT  )
+#endif
+#if VFLUSHO
+       CI_ENTRY("flush",    CFLUSHO, VFLUSHO )
+#endif
+#if VSTATUS
+       CI_ENTRY("status",   CSTATUS, VSTATUS )
+#endif
+       /* These must be last because of the display routines */
+       CI_ENTRY("min",      1,       VMIN    )
+       CI_ENTRY("time",     0,       VTIME   )
 };
 
 enum {
-       NUM_control_info =
-       (sizeof(control_info) / sizeof(struct control_info))
+       NUM_control_info = ARRAY_SIZE(control_info)
 };
 
-#define EMT(t) ((enum mode_type)(t))
-
-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, struct termios *mode);
-static void          display_all(struct termios *mode);
-static void          display_changed(struct termios *mode);
-static void          display_recoverable(struct termios *mode);
-static void          display_speed(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 const char *device_name = bb_msg_standard_input;
-
-static ATTRIBUTE_NORETURN void perror_on_device(const char *fmt)
+
+struct globals {
+       const char *device_name;
+       /* The width of the screen, for output wrapping */
+       unsigned max_col;
+       /* Current position, to know when to wrap */
+       unsigned current_col;
+} FIX_ALIASING;
+#define G (*(struct globals*)bb_common_bufsiz1)
+#define INIT_G() do { \
+       setup_common_bufsiz(); \
+       G.device_name = bb_msg_standard_input; \
+       G.max_col = 80; \
+       G.current_col = 0; /* we are noexec, must clear */ \
+} while (0)
+
+static void set_speed_or_die(enum speed_setting type, const char *arg,
+                                       struct termios *mode)
 {
-       bb_perror_msg_and_die(fmt, device_name);
-}
+       speed_t baud;
+
+       baud = tty_value_to_baud(xatou(arg));
 
+       if (type != output_speed) {     /* either input or both */
+               cfsetispeed(mode, baud);
+       }
+       if (type != input_speed) {      /* either output or both */
+               cfsetospeed(mode, baud);
+       }
+}
 
-/* The width of the screen, for output wrapping. */
-static int max_col;
+static NORETURN void perror_on_device_and_die(const char *fmt)
+{
+       bb_perror_msg_and_die(fmt, G.device_name);
+}
 
-/* Current position, to know when to wrap. */
-static int current_col;
+static void perror_on_device(const char *fmt)
+{
+       bb_perror_msg(fmt, G.device_name);
+}
 
 /* Print format string MESSAGE and optional args.
    Wrap to next line first if it won't fit.
-   Print a space first unless MESSAGE will start a new line. */
-
+   Print a space first unless MESSAGE will start a new line */
 static void wrapf(const char *message, ...)
 {
+       char buf[128];
        va_list args;
-       char buf[1024];                 /* Plenty long for our needs. */
-       int buflen;
+       unsigned buflen;
 
        va_start(args, message);
-       vsprintf(buf, message, args);
+       buflen = vsnprintf(buf, sizeof(buf), message, args);
        va_end(args);
-       buflen = strlen(buf);
-       if (current_col + (current_col > 0) + buflen >= max_col) {
-               putchar('\n');
-               current_col = 0;
-       }
-       if (current_col > 0) {
-               putchar(' ');
-               current_col++;
+       /* We seem to be called only with suitable lengths, but check if
+          somebody failed to adhere to this assumption just to be sure.  */
+       if (!buflen || buflen >= sizeof(buf)) return;
+
+       if (G.current_col > 0) {
+               G.current_col++;
+               if (buf[0] != '\n') {
+                       if (G.current_col + buflen >= G.max_col) {
+                               G.current_col = 0;
+                               bb_putchar('\n');
+                       } else {
+                               bb_putchar(' ');
+                       }
+               }
        }
        fputs(buf, stdout);
-       current_col += buflen;
+       G.current_col += buflen;
+       if (buf[buflen-1] == '\n')
+               G.current_col = 0;
+}
+
+static void newline(void)
+{
+       if (G.current_col != 0)
+               wrapf("\n");
+}
+
+#ifdef TIOCGWINSZ
+static void set_window_size(int rows, int cols)
+{
+       struct winsize win = { 0, 0, 0, 0 };
+
+       if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
+               if (errno != EINVAL) {
+                       goto bail;
+               }
+               memset(&win, 0, sizeof(win));
+       }
+
+       if (rows >= 0)
+               win.ws_row = rows;
+       if (cols >= 0)
+               win.ws_col = cols;
+
+       if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
+bail:
+               perror_on_device("%s");
+}
+#endif
+
+static void display_window_size(int fancy)
+{
+       const char *fmt_str = "%s\0%s: no size information for this device";
+       unsigned width, height;
+
+       if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
+               if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
+                       perror_on_device(fmt_str);
+               }
+       } else {
+               wrapf(fancy ? "rows %u; columns %u;" : "%u %u\n",
+                               height, width);
+       }
 }
 
 static const struct suffix_mult stty_suffixes[] = {
-       {"b",  512 },
-       {"k",  1024},
-       {"B",  1024},
-       {NULL, 0   }
+       { "b",  512 },
+       { "k", 1024 },
+       { "B", 1024 },
+       { "", 0 }
 };
 
 static const struct mode_info *find_mode(const char *name)
 {
-       int i;
-       for (i = 0; i < NUM_mode_info; ++i)
-               if (STREQ(name, mode_info[i].name))
-                       return &mode_info[i];
-       return 0;
+       int i = index_in_strings(mode_name, name);
+       return i >= 0 ? &mode_info[i] : NULL;
 }
 
 static const struct control_info *find_control(const char *name)
 {
-       int i;
-       for (i = 0; i < NUM_control_info; ++i)
-               if (STREQ(name, control_info[i].name))
-                       return &control_info[i];
-       return 0;
+       int i = index_in_strings(control_name, name);
+       return i >= 0 ? &control_info[i] : NULL;
 }
 
 enum {
        param_need_arg = 0x80,
-       param_line   = 1 | 0x80,
-       param_rows   = 2 | 0x80,
-       param_cols   = 3 | 0x80,
-       param_size   = 4,
-       param_ispeed = 5 | 0x80,
-       param_ospeed = 6 | 0x80,
-       param_speed  = 7,
+       param_line    = 1 | 0x80,
+       param_rows    = 2 | 0x80,
+       param_cols    = 3 | 0x80,
+       param_columns = 4 | 0x80,
+       param_size    = 5,
+       param_speed   = 6,
+       param_ispeed  = 7 | 0x80,
+       param_ospeed  = 8 | 0x80,
 };
 
 static int find_param(const char *name)
 {
-#ifdef HAVE_C_LINE
-       if (STREQ(name, "line")) return param_line;
-#endif
-#ifdef TIOCGWINSZ
-       if (STREQ(name, "rows")) return param_rows;
-       if (STREQ(name, "cols")) return param_cols;
-       if (STREQ(name, "columns")) return param_cols;
-       if (STREQ(name, "size")) return param_size;
-#endif
-       if (STREQ(name, "ispeed")) return param_ispeed;
-       if (STREQ(name, "ospeed")) return param_ospeed;
-       if (STREQ(name, "speed")) return param_speed;
-       return 0;
+       static const char params[] ALIGN1 =
+               "line\0"    /* 1 */
+               "rows\0"    /* 2 */
+               "cols\0"    /* 3 */
+               "columns\0" /* 4 */
+               "size\0"    /* 5 */
+               "speed\0"   /* 6 */
+               "ispeed\0"
+               "ospeed\0";
+       int i = index_in_strings(params, name) + 1;
+       if (i == 0)
+               return 0;
+       if (i != 5 && i != 6)
+               i |= 0x80;
+       return i;
 }
 
-
-int stty_main(int argc, char **argv)
+static int recover_mode(const char *arg, struct termios *mode)
 {
-       struct termios mode;
-       void (*output_func)(struct termios *);
-       const char *file_name = NULL;
-       int require_set_attr;
-       int speed_was_set;
-       int verbose_output;
-       int recoverable_output;
-       int noargs;
-       int k;
+       int i, n;
+       unsigned chr;
+       unsigned long iflag, oflag, cflag, lflag;
 
-       output_func = display_changed;
-       noargs = 1;
-       speed_was_set = 0;
-       require_set_attr = 0;
-       verbose_output = 0;
-       recoverable_output = 0;
+       /* Scan into temporaries since it is too much trouble to figure out
+          the right format for 'tcflag_t' */
+       if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
+                          &iflag, &oflag, &cflag, &lflag, &n) != 4)
+               return 0;
+       mode->c_iflag = iflag;
+       mode->c_oflag = oflag;
+       mode->c_cflag = cflag;
+       mode->c_lflag = lflag;
+       arg += n;
+       for (i = 0; i < NCCS; ++i) {
+               if (sscanf(arg, ":%x%n", &chr, &n) != 1)
+                       return 0;
+               mode->c_cc[i] = chr;
+               arg += n;
+       }
 
-       /* First pass: only parse/verify command line params */
-       k = 0;
-       while (argv[++k]) {
-               const struct mode_info *mp;
-               const struct control_info *cp;
-               const char *arg = argv[k];
-               const char *argnext = argv[k+1];
-               int param;
+       /* Fail if there are too many fields */
+       if (*arg != '\0')
+               return 0;
 
-               if (arg[0] == '-') {
-                       int i;
-                       mp = find_mode(arg+1);
-                       if (mp) {
-                               if (!(mp->flags & REV))
-                                       bb_error_msg_and_die("invalid argument '%s'", arg);
-                               noargs = 0;
-                               continue;
-                       }
-                       /* It is an option - parse it */
-                       i = 0;
-                       while (arg[++i]) {
-                               switch (arg[i]) {
-                               case 'a':
-                                       verbose_output = 1;
-                                       output_func = display_all;
-                                       break;
-                               case 'g':
-                                       recoverable_output = 1;
-                                       output_func = display_recoverable;
-                                       break;
-                               case 'F':
-                                       if (file_name)
-                                               bb_error_msg_and_die("only one device may be specified");
-                                       file_name = &arg[i+1]; /* "-Fdevice" ? */
-                                       if (!file_name[0]) { /* nope, "-F device" */
-                                               int p = k+1; /* argv[p] is argnext */
-                                               file_name = argnext;
-                                               if (!file_name)
-                                                       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; }
-                                       }
-                                       goto end_option;
-                               default:
-                                       bb_error_msg_and_die("invalid argument '%s'", arg);
-                               }
-                       }
-end_option:
-                       continue;
-               }
+       return 1;
+}
 
-               mp = find_mode(arg);
-               if (mp) {
-                       noargs = 0;
-                       continue;
-               }
+static void display_recoverable(const struct termios *mode,
+                               int UNUSED_PARAM dummy)
+{
+       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);
+       for (i = 0; i < NCCS; ++i)
+               printf(":%x", (unsigned int) mode->c_cc[i]);
+       bb_putchar('\n');
+}
 
-               cp = find_control(arg);
-               if (cp) {
-                       if (!argnext)
-                               bb_error_msg_and_die(bb_msg_requires_arg, arg);
-                       noargs = 0;
-                       ++k;
-                       continue;
-               }
+static void display_speed(const struct termios *mode, int fancy)
+{
+       //____________________ 01234567 8 9
+       const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
+       unsigned long ispeed, ospeed;
 
-               param = find_param(arg);
-               if (param & param_need_arg) {
-                       if (!argnext)
-                               bb_error_msg_and_die(bb_msg_requires_arg, arg);
-                       ++k;
-               }
+       ispeed = cfgetispeed(mode);
+       ospeed = cfgetospeed(mode);
+       if (ispeed == 0 || ispeed == ospeed) {
+               ispeed = ospeed;                /* in case ispeed was 0 */
+               //________ 0123 4 5 6 7 8 9
+               fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
+       }
+       if (fancy) fmt_str += 9;
+       wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
+}
 
-               switch (param) {
-#ifdef HAVE_C_LINE
+static void do_display(const struct termios *mode, int all)
+{
+       int i;
+       tcflag_t *bitsp;
+       unsigned long mask;
+       int prev_type = control;
+
+       display_speed(mode, 1);
+       if (all)
+               display_window_size(1);
+#ifdef __linux__
+       wrapf("line = %u;\n", mode->c_line);
+#else
+       newline();
+#endif
+
+       for (i = 0; i != CIDX_min; ++i) {
+               char ch;
+               char buf10[10];
+
+               /* If swtch is the same as susp, don't print both */
+#if VSWTCH == VSUSP
+               if (i == CIDX_swtch)
+                       continue;
+#endif
+               /* If eof uses the same slot as min, only print whichever applies */
+#if VEOF == VMIN
+               if (!(mode->c_lflag & ICANON)
+                && (i == CIDX_eof || i == CIDX_eol)
+               ) {
+                       continue;
+               }
+#endif
+               ch = mode->c_cc[control_info[i].offset];
+               if (ch == _POSIX_VDISABLE)
+                       strcpy(buf10, "<undef>");
+               else
+                       visible(ch, buf10, 0);
+               wrapf("%s = %s;", nth_string(control_name, i), buf10);
+       }
+#if VEOF == VMIN
+       if ((mode->c_lflag & ICANON) == 0)
+#endif
+               wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
+       newline();
+
+       for (i = 0; i < NUM_mode_info; ++i) {
+               if (mode_info[i].flags & OMIT)
+                       continue;
+               if (mode_info[i].type != prev_type) {
+                       newline();
+                       prev_type = mode_info[i].type;
+               }
+
+               bitsp = get_ptr_to_tcflag(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 (all || (mode_info[i].flags & SANE_UNSET))
+                               wrapf("-%s"+1, nth_string(mode_name, i));
+               } else {
+                       if ((all && mode_info[i].flags & REV)
+                        || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
+                       ) {
+                               wrapf("-%s", nth_string(mode_name, i));
+                       }
+               }
+       }
+       newline();
+}
+
+static void sane_mode(struct termios *mode)
+{
+       int i;
+
+       for (i = 0; i < NUM_control_info; ++i) {
+#if VMIN == VEOF
+               if (i == CIDX_min)
+                       break;
+#endif
+               mode->c_cc[control_info[i].offset] = control_info[i].saneval;
+       }
+
+       for (i = 0; i < NUM_mode_info; ++i) {
+               tcflag_t val;
+               tcflag_t *bitsp = get_ptr_to_tcflag(mode_info[i].type, mode);
+
+               if (!bitsp)
+                       continue;
+               val = *bitsp & ~((unsigned long)mode_info[i].mask);
+               if (mode_info[i].flags & SANE_SET) {
+                       *bitsp = val | mode_info[i].bits;
+               } else
+               if (mode_info[i].flags & SANE_UNSET) {
+                       *bitsp = val & ~mode_info[i].bits;
+               }
+       }
+}
+
+static void set_mode(const struct mode_info *info, int reversed,
+                                       struct termios *mode)
+{
+       tcflag_t *bitsp;
+
+       bitsp = get_ptr_to_tcflag(info->type, mode);
+
+       if (bitsp) {
+               tcflag_t val = *bitsp & ~info->mask;
+               if (reversed)
+                       *bitsp = val & ~info->bits;
+               else
+                       *bitsp = val | info->bits;
+               return;
+       }
+
+       /* !bitsp - it's a "combination" mode */
+       if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_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 == &mode_info[IDX_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 == &mode_info[IDX_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 == &mode_info[IDX_ek]) {
+               mode->c_cc[VERASE] = CERASE;
+               mode->c_cc[VKILL] = CKILL;
+       } else if (info == &mode_info[IDX_sane]) {
+               sane_mode(mode);
+       } else if (info == &mode_info[IDX_cbreak]) {
+               if (reversed)
+                       mode->c_lflag |= ICANON;
+               else
+                       mode->c_lflag &= ~ICANON;
+       } else if (info == &mode_info[IDX_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 == &mode_info[IDX_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 == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
+               if ((info == &mode_info[IDX_raw] && reversed)
+                || (info == &mode_info[IDX_cooked] && !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;
+               }
+       }
+#if IXANY
+       else if (info == &mode_info[IDX_decctlq]) {
+               if (reversed)
+                       mode->c_iflag |= IXANY;
+               else
+                       mode->c_iflag &= ~IXANY;
+       }
+#endif
+#if TABDLY
+       else if (info == &mode_info[IDX_tabs]) {
+               if (reversed)
+                       mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
+               else
+                       mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
+       }
+#endif
+#if OXTABS
+       else if (info == &mode_info[IDX_tabs]) {
+               if (reversed)
+                       mode->c_oflag |= OXTABS;
+               else
+                       mode->c_oflag &= ~OXTABS;
+       }
+#endif
+#if XCASE && IUCLC && OLCUC
+       else if (info==&mode_info[IDX_lcase] || info==&mode_info[IDX_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;
+               }
+       }
+#endif
+       else if (info == &mode_info[IDX_crt]) {
+               mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
+       } else if (info == &mode_info[IDX_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_or_die(const struct control_info *info,
+                       const char *arg, struct termios *mode)
+{
+       unsigned char value;
+
+       if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time])
+               value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
+       else if (arg[0] == '\0' || arg[1] == '\0')
+               value = arg[0];
+       else if (strcmp(arg, "^-") == 0 || strcmp(arg, "undef") == 0)
+               value = _POSIX_VDISABLE;
+       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 = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
+       mode->c_cc[info->offset] = value;
+}
+
+#define STTY_require_set_attr   (1 << 0)
+#define STTY_speed_was_set      (1 << 1)
+#define STTY_verbose_output     (1 << 2)
+#define STTY_recoverable_output (1 << 3)
+#define STTY_noargs             (1 << 4)
+
+int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int stty_main(int argc UNUSED_PARAM, char **argv)
+{
+       struct termios mode;
+       void (*output_func)(const struct termios *, int);
+       const char *file_name = NULL;
+       int display_all = 0;
+       int stty_state;
+       int k;
+
+       INIT_G();
+
+       stty_state = STTY_noargs;
+       output_func = do_display;
+
+       /* First pass: only parse/verify command line params */
+       k = 0;
+       while (argv[++k]) {
+               const struct mode_info *mp;
+               const struct control_info *cp;
+               const char *arg = argv[k];
+               const char *argnext = argv[k+1];
+               int param;
+
+               if (arg[0] == '-') {
+                       int i;
+                       mp = find_mode(arg+1);
+                       if (mp) {
+                               if (!(mp->flags & REV))
+                                       goto invalid_argument;
+                               stty_state &= ~STTY_noargs;
+                               continue;
+                       }
+                       /* It is an option - parse it */
+                       i = 0;
+                       while (arg[++i]) {
+                               switch (arg[i]) {
+                               case 'a':
+                                       stty_state |= STTY_verbose_output;
+                                       output_func = do_display;
+                                       display_all = 1;
+                                       break;
+                               case 'g':
+                                       stty_state |= STTY_recoverable_output;
+                                       output_func = display_recoverable;
+                                       break;
+                               case 'F':
+                                       if (file_name)
+                                               bb_error_msg_and_die("only one device may be specified");
+                                       file_name = &arg[i+1]; /* "-Fdevice" ? */
+                                       if (!file_name[0]) { /* nope, "-F device" */
+                                               int p = k+1; /* argv[p] is argnext */
+                                               file_name = argnext;
+                                               if (!file_name)
+                                                       bb_error_msg_and_die(bb_msg_requires_arg, "-F");
+                                               /* remove -F param from arg[vc] */
+                                               while (argv[p]) {
+                                                       argv[p] = argv[p+1];
+                                                       ++p;
+                                               }
+                                       }
+                                       goto end_option;
+                               default:
+                                       goto invalid_argument;
+                               }
+                       }
+ end_option:
+                       continue;
+               }
+
+               mp = find_mode(arg);
+               if (mp) {
+                       stty_state &= ~STTY_noargs;
+                       continue;
+               }
+
+               cp = find_control(arg);
+               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);
+                       stty_state &= ~STTY_noargs;
+                       ++k;
+                       continue;
+               }
+
+               param = find_param(arg);
+               if (param & param_need_arg) {
+                       if (!argnext)
+                               bb_error_msg_and_die(bb_msg_requires_arg, arg);
+                       ++k;
+               }
+
+               switch (param) {
+#ifdef __linux__
                case param_line:
-                       bb_xparse_number(argnext, stty_suffixes);
+# ifndef TIOCGWINSZ
+                       xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
                        break;
+# endif /* else fall-through */
 #endif
 #ifdef TIOCGWINSZ
                case param_rows:
-                       bb_xparse_number(argnext, stty_suffixes);
-                       break;
                case param_cols:
-                       bb_xparse_number(argnext, stty_suffixes);
+               case param_columns:
+                       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 (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
+ invalid_argument:
                        bb_error_msg_and_die("invalid argument '%s'", arg);
                }
-               noargs = 0;
+               stty_state &= ~STTY_noargs;
        }
 
        /* Specifying both -a and -g is an error */
-       if (verbose_output && recoverable_output)
-               bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
+       if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
+               (STTY_verbose_output | STTY_recoverable_output)
+       ) {
+               bb_error_msg_and_die("-a and -g are mutually exclusive");
+       }
        /* Specifying -a or -g with non-options is an error */
-       if (!noargs && (verbose_output || recoverable_output))
-               bb_error_msg_and_die("modes may not be set when specifying an output style");
+       if ((stty_state & (STTY_verbose_output | STTY_recoverable_output))
+        && !(stty_state & STTY_noargs)
+       ) {
+               bb_error_msg_and_die("modes may not be set when -a or -g is used");
+       }
 
        /* Now it is safe to start doing things */
        if (file_name) {
-               int fd, fdflags;
-               device_name = file_name;
-               fd = xopen(device_name, O_RDONLY | O_NONBLOCK);
-               if (fd != 0) {
-                       dup2(fd, 0);
-                       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");
+               G.device_name = file_name;
+               xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO);
+               ndelay_off(STDIN_FILENO);
        }
 
        /* Initialize to all zeroes so there is no risk memcmp will report a
-          spurious difference in an uninitialized portion of the structure */
+          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;
-               output_func(&mode);
+       if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
+               G.max_col = get_terminal_width(STDOUT_FILENO);
+               output_func(&mode, display_all);
                return EXIT_SUCCESS;
        }
 
@@ -659,6 +1452,7 @@ end_option:
                        mp = find_mode(arg+1);
                        if (mp) {
                                set_mode(mp, 1 /* reversed */, &mode);
+                               stty_state |= STTY_require_set_attr;
                        }
                        /* It is an option - already parsed. Skip it */
                        continue;
@@ -667,13 +1461,15 @@ end_option:
                mp = find_mode(arg);
                if (mp) {
                        set_mode(mp, 0 /* non-reversed */, &mode);
+                       stty_state |= STTY_require_set_attr;
                        continue;
                }
 
                cp = find_control(arg);
                if (cp) {
                        ++k;
-                       set_control_char(cp, argnext, &mode);
+                       set_control_char_or_die(cp, argnext, &mode);
+                       stty_state |= STTY_require_set_attr;
                        continue;
                }
 
@@ -683,72 +1479,72 @@ end_option:
                }
 
                switch (param) {
-#ifdef HAVE_C_LINE
+#ifdef __linux__
                case param_line:
-                       mode.c_line = bb_xparse_number(argnext, stty_suffixes);
-                       require_set_attr = 1;
+                       mode.c_line = xatoul_sfx(argnext, stty_suffixes);
+                       stty_state |= STTY_require_set_attr;
                        break;
 #endif
 #ifdef TIOCGWINSZ
                case param_cols:
-                       set_window_size(-1, (int) bb_xparse_number(argnext, stty_suffixes));
+               case param_columns:
+                       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);
-                       speed_was_set = 1;
-                       require_set_attr = 1;
+                       set_speed_or_die(input_speed, argnext, &mode);
+                       stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
                        break;
                case param_ospeed:
-                       set_speed(output_speed, argnext, &mode);
-                       speed_was_set = 1;
-                       require_set_attr = 1;
-                       break;
-               case param_speed:
-                       max_col = screen_columns();
-                       display_speed(&mode, 0);
+                       set_speed_or_die(output_speed, argnext, &mode);
+                       stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
                        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);
-                               speed_was_set = 1;
-                               require_set_attr = 1;
+                               stty_state |= STTY_require_set_attr;
+                       else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
+                               set_speed_or_die(both_speeds, arg, &mode);
+                               stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
                        } /* else - impossible (caught in the first pass):
                                bb_error_msg_and_die("invalid argument '%s'", arg); */
                }
        }
 
-       if (require_set_attr) {
+       if (stty_state & STTY_require_set_attr) {
                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 */
+                  compare them to the requested ones */
 
                /* Initialize to all zeroes so there is no risk memcmp will report a
-                  spurious difference in an uninitialized portion of the structure */
+                  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
+/*
+ * I think the below chunk is not necessary on Linux.
+ * If you are deleting it, also delete STTY_speed_was_set bit -
+ * it is only ever checked here.
+ */
+#if 0 /* was "if CIBAUD" */
                        /* SunOS 4.1.3 (at least) has the problem that after this sequence,
                           tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
                           sometimes (m1 != m2).  The only difference is in the four bits
@@ -756,588 +1552,15 @@ end_option:
                           Sun users a little confusion, don't report an error if this
                           happens.  But suppress the error only if we haven't tried to
                           set the baud rate explicitly -- otherwise we'd never give an
-                          error for a true failure to set the baud rate */
+                          error for a true failure to set the baud rate */
 
                        new_mode.c_cflag &= (~CIBAUD);
-                       if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
+                       if ((stty_state & STTY_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");
                }
        }
 
        return EXIT_SUCCESS;
 }
-
-/* Return 0 if not applied because not reversible; otherwise return 1.  */
-
-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
-#ifdef ONLCR
-                                                                | ONLCR
-#endif
-                                       )
-#ifdef OCRNL
-                                       & ~OCRNL
-#endif
-#ifdef ONLRET
-                                       & ~ONLRET
-#endif
-                                       ;
-                       } else {
-                               mode->c_iflag = mode->c_iflag & ~ICRNL;
-#ifdef ONLCR
-                               mode->c_oflag = mode->c_oflag & ~ONLCR;
-#endif
-                       }
-               } 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
-#ifdef XCASE
-                                                                  | XCASE
-#endif
-                                       );
-                               mode->c_cc[VMIN] = 1;
-                               mode->c_cc[VTIME] = 0;
-                       }
-               }
-#ifdef IXANY
-               else if (info->name == decctlq) {
-                       if (reversed)
-                               mode->c_iflag |= IXANY;
-                       else
-                               mode->c_iflag &= ~IXANY;
-               }
-#endif
-#ifdef TABDLY
-               else if (info->name == stty_tabs) {
-                       if (reversed)
-                               mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
-                       else
-                               mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
-               }
-#else
-# ifdef OXTABS
-               else if (info->name == stty_tabs) {
-                       if (reversed)
-                               mode->c_oflag = mode->c_oflag | OXTABS;
-                       else
-                               mode->c_oflag = mode->c_oflag & ~OXTABS;
-               }
-# endif
-#endif
-#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
-               else if (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;
-                       }
-               }
-#endif
-               else if (info->name == stty_crt)
-                       mode->c_lflag |= ECHOE
-#ifdef ECHOCTL
-                               | ECHOCTL
-#endif
-#ifdef ECHOKE
-                               | ECHOKE
-#endif
-                               ;
-               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
-#ifdef ECHOCTL
-                               | ECHOCTL
-#endif
-#ifdef ECHOKE
-                               | ECHOKE
-#endif
-                               ;
-#ifdef IXANY
-                       mode->c_iflag &= ~IXANY;
-#endif
-               }
-       } else if (reversed)
-               *bitsp = *bitsp & ~((unsigned long)info->mask) & ~info->bits;
-       else
-               *bitsp = (*bitsp & ~((unsigned long)info->mask)) | info->bits;
-}
-
-static void
-set_control_char(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);
-       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. */
-               if (arg[1] == '?')
-                       value = 127;
-               else
-                       value = arg[1] & ~0140; /* Non-letters get weird results. */
-       } else
-               value = bb_xparse_number(arg, 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)
-{
-       int err = ioctl(fd, TIOCGWINSZ, (char *) win);
-
-       return err;
-}
-
-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);
-               if (!fancy)
-                       current_col = 0;
-       }
-}
-#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, 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(struct termios *mode)
-{
-       int i;
-       int empty_line;
-       tcflag_t *bitsp;
-       unsigned long mask;
-       enum mode_type prev_type = control;
-
-       display_speed(mode, 1);
-#ifdef HAVE_C_LINE
-       wrapf("line = %d;", mode->c_line);
-#endif
-       putchar('\n');
-       current_col = 0;
-
-       empty_line = 1;
-       for (i = 0; control_info[i].name != stty_min; ++i) {
-               if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
-                       continue;
-               /* If swtch is the same as susp, don't print both.  */
-#if VSWTCH == VSUSP
-               if (control_info[i].name == stty_swtch)
-                       continue;
-#endif
-               /* If eof uses the same slot as min, only print whichever applies.  */
-#if VEOF == VMIN
-               if ((mode->c_lflag & ICANON) == 0
-                       && (control_info[i].name == stty_eof
-                               || control_info[i].name == stty_eol)) continue;
-#endif
-
-               empty_line = 0;
-               wrapf("%s = %s;", control_info[i].name,
-                         visible(mode->c_cc[control_info[i].offset]));
-       }
-       if ((mode->c_lflag & ICANON) == 0) {
-               wrapf("min = %d; time = %d;\n", (int) mode->c_cc[VMIN],
-                         (int) mode->c_cc[VTIME]);
-       } else if (empty_line == 0)
-               putchar('\n');
-       current_col = 0;
-
-       empty_line = 1;
-       for (i = 0; i < NUM_mode_info; ++i) {
-               if (mode_info[i].flags & OMIT)
-                       continue;
-               if (EMT(mode_info[i].type) != prev_type) {
-                       if (empty_line == 0) {
-                               putchar('\n');
-                               current_col = 0;
-                               empty_line = 1;
-                       }
-                       prev_type = EMT(mode_info[i].type);
-               }
-
-               bitsp = mode_type_flag(EMT(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) {
-                               wrapf("%s", mode_info[i].name);
-                               empty_line = 0;
-                       }
-               }
-                       else if ((mode_info[i].flags & (SANE_SET | REV)) ==
-                                        (SANE_SET | REV)) {
-                       wrapf("-%s", mode_info[i].name);
-                       empty_line = 0;
-               }
-       }
-       if (empty_line == 0)
-               putchar('\n');
-       current_col = 0;
-}
-
-static void
-display_all(struct termios *mode)
-{
-       int i;
-       tcflag_t *bitsp;
-       unsigned long mask;
-       enum mode_type prev_type = control;
-
-       display_speed(mode, 1);
-#ifdef TIOCGWINSZ
-       display_window_size(1);
-#endif
-#ifdef HAVE_C_LINE
-       wrapf("line = %d;", mode->c_line);
-#endif
-       putchar('\n');
-       current_col = 0;
-
-       for (i = 0; control_info[i].name != stty_min; ++i) {
-               /* If swtch is the same as susp, don't print both.  */
-#if VSWTCH == VSUSP
-               if (control_info[i].name == stty_swtch)
-                       continue;
-#endif
-               /* If eof uses the same slot as min, only print whichever applies.  */
-#if VEOF == VMIN
-               if ((mode->c_lflag & ICANON) == 0
-                       && (control_info[i].name == stty_eof
-                               || control_info[i].name == stty_eol)) continue;
-#endif
-               wrapf("%s = %s;", control_info[i].name,
-                         visible(mode->c_cc[control_info[i].offset]));
-       }
-#if VEOF == VMIN
-       if ((mode->c_lflag & ICANON) == 0)
-#endif
-               wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
-       if (current_col != 0)
-               putchar('\n');
-       current_col = 0;
-
-       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);
-               }
-
-               bitsp = mode_type_flag(EMT(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);
-               else if (mode_info[i].flags & REV)
-                       wrapf("-%s", mode_info[i].name);
-       }
-       putchar('\n');
-       current_col = 0;
-}
-
-static void display_speed(struct termios *mode, int fancy)
-{
-       unsigned long ispeed, ospeed;
-       const char *fmt_str =
-               "%lu %lu\n\0"        "ispeed %lu baud; ospeed %lu baud;\0"
-               "%lu\n\0" "\0\0\0\0" "speed %lu baud;";
-
-       ospeed = ispeed = cfgetispeed(mode);
-       if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
-               ispeed = ospeed;                /* in case ispeed was 0 */
-               fmt_str += 43;
-       }
-       if (fancy) {
-               fmt_str += 9;
-       }
-       wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
-       if (!fancy)
-               current_col = 0;
-}
-
-static void display_recoverable(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);
-       for (i = 0; i < NCCS; ++i)
-               printf(":%x", (unsigned int) mode->c_cc[i]);
-       putchar('\n');
-}
-
-static int recover_mode(const char *arg, struct termios *mode)
-{
-       int i, n;
-       unsigned int chr;
-       unsigned long iflag, oflag, cflag, lflag;
-
-       /* Scan into temporaries since it is too much trouble to figure out
-          the right format for `tcflag_t'.  */
-       if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
-                          &iflag, &oflag, &cflag, &lflag, &n) != 4)
-               return 0;
-       mode->c_iflag = iflag;
-       mode->c_oflag = oflag;
-       mode->c_cflag = cflag;
-       mode->c_lflag = lflag;
-       arg += n;
-       for (i = 0; i < NCCS; ++i) {
-               if (sscanf(arg, ":%x%n", &chr, &n) != 1)
-                       return 0;
-               mode->c_cc[i] = chr;
-               arg += n;
-       }
-
-       /* Fail if there are too many fields.  */
-       if (*arg != '\0')
-               return 0;
-
-       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;
-       tcflag_t *bitsp;
-
-       for (i = 0; i < NUM_control_info; ++i) {
-#if VMIN == VEOF
-               if (control_info[i].name == stty_min)
-                       break;
-#endif
-               mode->c_cc[control_info[i].offset] = control_info[i].saneval;
-       }
-
-       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 = (*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 = *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;
-}