config: more tweaks
[oweals/busybox.git] / coreutils / stty.c
index 05d91d8c149c1247ae75ac68fb09973361e33a63..6251f2aef5defdb8b7bbfcc0e3a81e8de8f5523a 100644 (file)
@@ -1,27 +1,47 @@
 /* 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"
+ *
+ * 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)
 #if defined(VEOL2) && !defined(CEOL2)
 # define CEOL2 _POSIX_VDISABLE
 #endif
+/* 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
 
 /* 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__)
+#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)
+#if defined(VDSUSP) && !defined(CDSUSP)
 # define CDSUSP Control('y')
 #endif
 #if !defined(VREPRINT) && defined(VRPRNT)       /* Irix 4.0.5 */
 # 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 */
 enum speed_setting {
        input_speed, output_speed, both_speeds
@@ -122,33 +259,21 @@ enum speed_setting {
 
 /* Which member(s) of 'struct termios' a mode uses */
 enum {
-       /* Do NOT change the order or values, as mode_type_flag()
-        * depends on them */
        control, input, output, local, combination
 };
-
-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";
+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                  */
@@ -156,277 +281,536 @@ static const char stty_dec  [] = "dec";
 #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 * const name;      /* Name given on command line           */
-       const unsigned char type;     /* Which structure element to change    */
-       const unsigned char flags;    /* Setting and display options          */
-       /* were using short here, but ppc32 was unhappy: */
+       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            */
 };
 
-/* We can optimize it further by using name[8] instead of char *name */
-/* but beware of "if (info->name == evenp)" checks! */
-/* Need to replace them with "if (info == &mode_info[EVENP_INDX])" */
+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
+};
+
+#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
+# if OXTABS
+       MI_ENTRY("tab3",     output,      SANE_UNSET,        OXTABS,     0 )
+# endif
+#endif
+
+#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
+       ;
 
-#define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B }
+#undef MI_ENTRY
+#define MI_ENTRY(N,T,F,B,M) { T, F, M, B },
 
 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),
+       /* 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
-# 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 ),
+#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
 };
 
 enum {
-       NUM_mode_info = (sizeof(mode_info) / sizeof(mode_info[0]))
+       NUM_mode_info = ARRAY_SIZE(mode_info)
 };
 
-/* Control character settings */
+
+/* Control characters */
 struct control_info {
-       const char * const name;               /* Name given on command line */
-       const unsigned char saneval;          /* Value to set for 'stty sane' */
-       const unsigned char offset;           /* Offset in c_cc */
+       const uint8_t saneval;  /* Value to set for 'stty sane' */
+       const uint8_t offset;   /* Offset in c_cc */
 };
 
-/* Control characters */
+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,
+};
 
-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},
+#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
-#ifdef VSWTCH
-       {stty_swtch, CSWTCH,  VSWTCH},
+#if VSWTCH
+       CI_ENTRY("swtch",    CSWTCH,  VSWTCH  )
 #endif
-       {"start",    CSTART,  VSTART},
-       {"stop",     CSTOP,   VSTOP},
-       {"susp",     CSUSP,   VSUSP},
-#ifdef VDSUSP
-       {"dsusp",    CDSUSP,  VDSUSP},
+       CI_ENTRY("start",    CSTART,  VSTART  )
+       CI_ENTRY("stop",     CSTOP,   VSTOP   )
+       CI_ENTRY("susp",     CSUSP,   VSUSP   )
+#if VDSUSP
+       CI_ENTRY("dsusp",    CDSUSP,  VDSUSP  )
 #endif
-#ifdef VREPRINT
-       {"rprnt",    CRPRNT,  VREPRINT},
+#if VREPRINT
+       CI_ENTRY("rprnt",    CRPRNT,  VREPRINT)
 #endif
-#ifdef VWERASE
-       {"werase",   CWERASE, VWERASE},
+#if VWERASE
+       CI_ENTRY("werase",   CWERASE, VWERASE )
 #endif
-#ifdef VLNEXT
-       {"lnext",    CLNEXT,  VLNEXT},
+#if VLNEXT
+       CI_ENTRY("lnext",    CLNEXT,  VLNEXT  )
 #endif
-#ifdef VFLUSHO
-       {"flush",    CFLUSHO, VFLUSHO},
+#if VFLUSHO
+       CI_ENTRY("flush",    CFLUSHO, VFLUSHO )
 #endif
-#ifdef VSTATUS
-       {"status",   CSTATUS, VSTATUS},
+#if VSTATUS
+       CI_ENTRY("status",   CSTATUS, VSTATUS )
 #endif
        /* These must be last because of the display routines */
-       {stty_min,   1,       VMIN},
-       {stty_time,  0,       VTIME},
+       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(control_info[0]))
+       NUM_control_info = ARRAY_SIZE(control_info)
 };
 
-/* The width of the screen, for output wrapping */
-static unsigned max_col = 80; /* default */
-/* Current position, to know when to wrap */
-static unsigned current_col;
-static const char *device_name = bb_msg_standard_input;
 
-/* Return a string that is the printable representation of character CH */
-/* Adapted from 'cat' by Torbjorn Granlund */
-static const char *visible(unsigned int ch)
-{
-       static char buf[10];
-       char *bpout = buf;
-
-       if (ch == _POSIX_VDISABLE)
-               return "<undef>";
-
-       if (ch >= 128) {
-               ch -= 128;
-               *bpout++ = 'M';
-               *bpout++ = '-';
-       }
-
-       if (ch < 32) {
-               *bpout++ = '^';
-               *bpout++ = ch + 64;
-       } else if (ch < 127) {
-               *bpout++ = ch;
-       } else {
-               *bpout++ = '^';
-               *bpout++ = '?';
-       }
-
-       *bpout = '\0';
-       return buf;
-}
-
-static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
-{
-       static const unsigned char tcflag_offsets[] = {
-               offsetof(struct termios, c_cflag), /* control */
-               offsetof(struct termios, c_iflag), /* input */
-               offsetof(struct termios, c_oflag), /* output */
-               offsetof(struct termios, c_lflag), /* local */
-       };
-
-       if (type <= local) {
-               return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
-       }
-       return NULL;
-}
-
-static void set_speed_or_die(enum speed_setting type, const char * const arg,
-                                       struct termios * const mode)
+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)
 {
        speed_t baud;
 
@@ -440,19 +824,16 @@ static void set_speed_or_die(enum speed_setting type, const char * const arg,
        }
 }
 
-static ATTRIBUTE_NORETURN void perror_on_device_and_die(const char *fmt)
+static NORETURN void perror_on_device_and_die(const char *fmt)
 {
-       bb_perror_msg_and_die(fmt, device_name);
+       bb_perror_msg_and_die(fmt, G.device_name);
 }
 
 static void perror_on_device(const char *fmt)
 {
-       bb_perror_msg(fmt, device_name);
+       bb_perror_msg(fmt, G.device_name);
 }
 
-/* No, inline won't be as efficient (gcc 3.4.3) */
-#define streq(a,b) (!strcmp((a),(b)))
-
 /* 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 */
@@ -460,33 +841,42 @@ static void wrapf(const char *message, ...)
 {
        char buf[128];
        va_list args;
-       int buflen;
+       unsigned buflen;
 
        va_start(args, message);
-       vsnprintf(buf, sizeof(buf), message, args);
+       buflen = vsnprintf(buf, sizeof(buf), message, args);
        va_end(args);
-       buflen = strlen(buf);
-       if (!buflen) return;
+       /* 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 (current_col > 0) {
-               current_col++;
+       if (G.current_col > 0) {
+               G.current_col++;
                if (buf[0] != '\n') {
-                       if (current_col + buflen >= max_col) {
-                               putchar('\n');
-                               current_col = 0;
-                       } else
-                               putchar(' ');
+                       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')
-               current_col = 0;
+               G.current_col = 0;
 }
 
-static void set_window_size(const int rows, const int cols)
+static void newline(void)
 {
-       struct winsize win = { 0, 0, 0, 0};
+       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) {
@@ -504,8 +894,9 @@ static void set_window_size(const int rows, const int cols)
 bail:
                perror_on_device("%s");
 }
+#endif
 
-static void display_window_size(const int fancy)
+static void display_window_size(int fancy)
 {
        const char *fmt_str = "%s\0%s: no size information for this device";
        unsigned width, height;
@@ -515,88 +906,65 @@ static void display_window_size(const int fancy)
                        perror_on_device(fmt_str);
                }
        } else {
-               wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
+               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_speed  = 5,
-       param_ispeed = 6 | 0x80,
-       param_ospeed = 7 | 0x80,
+       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 * const name)
+static int find_param(const char *name)
 {
-#if 0
-#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, "speed")) return param_speed;
-       if (streq(name, "ispeed")) return param_ispeed;
-       if (streq(name, "ospeed")) return param_ospeed;
-       return 0;
-#else
-       const char * const params[] = {
-               "line",
-               "rows",
-               "cols",
-               "columns",
-               "size",
-               "speed",
-               "ispeed",
-               "ospeed",
-               NULL
-       };
-       int i = index_in_str_array(params, name);
-       if (i) {
-               if (!(i == 4 || i == 5))
-                       i |= 0x80;
-       }
+       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;
-#endif
 }
 
 static int recover_mode(const char *arg, struct termios *mode)
 {
        int i, n;
-       unsigned int chr;
+       unsigned chr;
        unsigned long iflag, oflag, cflag, lflag;
 
        /* Scan into temporaries since it is too much trouble to figure out
@@ -623,7 +991,8 @@ static int recover_mode(const char *arg, struct termios *mode)
        return 1;
 }
 
-static void display_recoverable(const struct termios *mode)
+static void display_recoverable(const struct termios *mode,
+                               int UNUSED_PARAM dummy)
 {
        int i;
        printf("%lx:%lx:%lx:%lx",
@@ -631,26 +1000,27 @@ static void display_recoverable(const struct termios *mode)
                   (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');
+       bb_putchar('\n');
 }
 
 static void display_speed(const struct termios *mode, int fancy)
 {
-                            //01234567 8 9
+       //____________________ 01234567 8 9
        const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
        unsigned long ispeed, ospeed;
 
-       ospeed = ispeed = cfgetispeed(mode);
-       if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
+       ispeed = cfgetispeed(mode);
+       ospeed = cfgetospeed(mode);
+       if (ispeed == 0 || ispeed == ospeed) {
                ispeed = ospeed;                /* in case ispeed was 0 */
-                        //0123 4 5 6 7 8 9
+               //________ 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));
 }
 
-static void display_all(const struct termios *mode)
+static void do_display(const struct termios *mode, int all)
 {
        int i;
        tcflag_t *bitsp;
@@ -658,159 +1028,142 @@ static void display_all(const struct termios *mode)
        int prev_type = control;
 
        display_speed(mode, 1);
-       display_window_size(1);
-#ifdef HAVE_C_LINE
-       wrapf("line = %d;\n", mode->c_line);
+       if (all)
+               display_window_size(1);
+#ifdef __linux__
+       wrapf("line = %u;\n", mode->c_line);
 #else
-       wrapf("\n");
+       newline();
 #endif
 
-       for (i = 0; control_info[i].name != stty_min; ++i) {
+       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 (control_info[i].name == stty_swtch)
+               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) == 0
-                       && (control_info[i].name == stty_eof
-                               || control_info[i].name == stty_eol)) continue;
+               if (!(mode->c_lflag & ICANON)
+                && (i == CIDX_eof || i == CIDX_eol)
+               ) {
+                       continue;
+               }
 #endif
-               wrapf("%s = %s;", control_info[i].name,
-                         visible(mode->c_cc[control_info[i].offset]));
+               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 = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
-       if (current_col) wrapf("\n");
+               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) {
-                       wrapf("\n");
+                       newline();
                        prev_type = mode_info[i].type;
                }
 
-               bitsp = mode_type_flag(mode_info[i].type, mode);
+               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)
-                       wrapf("%s", mode_info[i].name);
-               else if (mode_info[i].flags & REV)
-                       wrapf("-%s", mode_info[i].name);
+               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));
+                       }
+               }
        }
-       if (current_col) wrapf("\n");
+       newline();
 }
 
 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)
+               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 = mode_type_flag(mode_info[i].type, mode);
-                       *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
-                               | mode_info[i].bits;
-               } else if (mode_info[i].flags & SANE_UNSET) {
-                       bitsp = mode_type_flag(mode_info[i].type, mode);
-                       *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
-                               & ~mode_info[i].bits;
+                       *bitsp = val | mode_info[i].bits;
+               } else
+               if (mode_info[i].flags & SANE_UNSET) {
+                       *bitsp = val & ~mode_info[i].bits;
                }
        }
 }
 
-/* Save set_mode from #ifdef forest plague */
-#ifndef ONLCR
-#define ONLCR 0
-#endif
-#ifndef OCRNL
-#define OCRNL 0
-#endif
-#ifndef ONLRET
-#define ONLRET 0
-#endif
-#ifndef XCASE
-#define XCASE 0
-#endif
-#ifndef IXANY
-#define IXANY 0
-#endif
-#ifndef TABDLY
-#define TABDLY 0
-#endif
-#ifndef OXTABS
-#define OXTABS 0
-#endif
-#ifndef IUCLC
-#define IUCLC 0
-#endif
-#ifndef OLCUC
-#define OLCUC 0
-#endif
-#ifndef ECHOCTL
-#define ECHOCTL 0
-#endif
-#ifndef ECHOKE
-#define ECHOKE 0
-#endif
-
 static void set_mode(const struct mode_info *info, int reversed,
                                        struct termios *mode)
 {
        tcflag_t *bitsp;
 
-       bitsp = mode_type_flag(info->type, mode);
+       bitsp = get_ptr_to_tcflag(info->type, mode);
 
        if (bitsp) {
+               tcflag_t val = *bitsp & ~info->mask;
                if (reversed)
-                       *bitsp = *bitsp & ~info->mask & ~info->bits;
+                       *bitsp = val & ~info->bits;
                else
-                       *bitsp = (*bitsp & ~info->mask) | info->bits;
+                       *bitsp = val | info->bits;
                return;
        }
 
-       /* Combination mode */
-       if (info->name == evenp || info->name == parity) {
+       /* !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->name == stty_oddp) {
+                       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->name == stty_nl) {
+                       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;
+                       mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
                } else {
                        mode->c_iflag = mode->c_iflag & ~ICRNL;
                        if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
                }
-       } else if (info->name == stty_ek) {
+       } else if (info == &mode_info[IDX_ek]) {
                mode->c_cc[VERASE] = CERASE;
                mode->c_cc[VKILL] = CKILL;
-       } else if (info->name == stty_sane) {
+       } else if (info == &mode_info[IDX_sane]) {
                sane_mode(mode);
-       }
-       else if (info->name == cbreak) {
+       } else if (info == &mode_info[IDX_cbreak]) {
                if (reversed)
                        mode->c_lflag |= ICANON;
                else
                        mode->c_lflag &= ~ICANON;
-       } else if (info->name == stty_pass8) {
+       } else if (info == &mode_info[IDX_pass8]) {
                if (reversed) {
                        mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
                        mode->c_iflag |= ISTRIP;
@@ -818,7 +1171,7 @@ static void set_mode(const struct mode_info *info, int reversed,
                        mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
                        mode->c_iflag &= ~ISTRIP;
                }
-       } else if (info->name == litout) {
+       } else if (info == &mode_info[IDX_litout]) {
                if (reversed) {
                        mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
                        mode->c_iflag |= ISTRIP;
@@ -828,9 +1181,10 @@ static void set_mode(const struct mode_info *info, int reversed,
                        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)) {
+       } 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;
@@ -850,26 +1204,32 @@ static void set_mode(const struct mode_info *info, int reversed,
                        mode->c_cc[VTIME] = 0;
                }
        }
-       else if (IXANY && info->name == decctlq) {
+#if IXANY
+       else if (info == &mode_info[IDX_decctlq]) {
                if (reversed)
                        mode->c_iflag |= IXANY;
                else
                        mode->c_iflag &= ~IXANY;
        }
-       else if (TABDLY && info->name == stty_tabs) {
+#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;
        }
-       else if (OXTABS && info->name == stty_tabs) {
+#endif
+#if OXTABS
+       else if (info == &mode_info[IDX_tabs]) {
                if (reversed)
                        mode->c_oflag |= OXTABS;
                else
                        mode->c_oflag &= ~OXTABS;
        }
-       else if (XCASE && IUCLC && OLCUC
-       && (info->name == stty_lcase || info->name == stty_LCASE)) {
+#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;
@@ -880,10 +1240,10 @@ static void set_mode(const struct mode_info *info, int reversed,
                        mode->c_oflag |= OLCUC;
                }
        }
-       else if (info->name == stty_crt) {
+#endif
+       else if (info == &mode_info[IDX_crt]) {
                mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
-       }
-       else if (info->name == stty_dec) {
+       } 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 */
@@ -892,73 +1252,16 @@ static void set_mode(const struct mode_info *info, int reversed,
        }
 }
 
-static void display_changed(const struct termios *mode)
-{
-       int i;
-       tcflag_t *bitsp;
-       unsigned long mask;
-       int prev_type = control;
-
-       display_speed(mode, 1);
-#ifdef HAVE_C_LINE
-       wrapf("line = %d;\n", mode->c_line);
-#else
-       wrapf("\n");
-#endif
-
-       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
-               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;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
-
-       if (current_col) wrapf("\n");
-
-       for (i = 0; i < NUM_mode_info; ++i) {
-               if (mode_info[i].flags & OMIT)
-                       continue;
-               if (mode_info[i].type != prev_type) {
-                       if (current_col) wrapf("\n");
-                       prev_type = mode_info[i].type;
-               }
-
-               bitsp = mode_type_flag(mode_info[i].type, mode);
-               mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
-               if ((*bitsp & mask) == mode_info[i].bits) {
-                       if (mode_info[i].flags & SANE_UNSET) {
-                               wrapf("%s", mode_info[i].name);
-                       }
-               } else if ((mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)) {
-                       wrapf("-%s", mode_info[i].name);
-               }
-       }
-       if (current_col) wrapf("\n");
-}
-
 static void set_control_char_or_die(const struct control_info *info,
                        const char *arg, struct termios *mode)
 {
        unsigned char value;
 
-       if (info->name == stty_min || info->name == stty_time)
+       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 (streq(arg, "^-") || streq(arg, "undef"))
+       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 */
@@ -969,19 +1272,26 @@ static void set_control_char_or_die(const struct control_info *info,
        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)
+#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 *);
+       void (*output_func)(const struct termios *, int);
        const char *file_name = NULL;
+       int display_all = 0;
+       int stty_state;
        int k;
-       option_mask32 = STTY_noargs;
-       output_func = display_changed;
+
+       INIT_G();
+
+       stty_state = STTY_noargs;
+       output_func = do_display;
 
        /* First pass: only parse/verify command line params */
        k = 0;
@@ -998,7 +1308,7 @@ int stty_main(int argc, char **argv)
                        if (mp) {
                                if (!(mp->flags & REV))
                                        goto invalid_argument;
-                               option_mask32 &= ~STTY_noargs;
+                               stty_state &= ~STTY_noargs;
                                continue;
                        }
                        /* It is an option - parse it */
@@ -1006,11 +1316,12 @@ int stty_main(int argc, char **argv)
                        while (arg[++i]) {
                                switch (arg[i]) {
                                case 'a':
-                                       option_mask32 |= STTY_verbose_output;
-                                       output_func = display_all;
+                                       stty_state |= STTY_verbose_output;
+                                       output_func = do_display;
+                                       display_all = 1;
                                        break;
                                case 'g':
-                                       option_mask32 |= STTY_recoverable_output;
+                                       stty_state |= STTY_recoverable_output;
                                        output_func = display_recoverable;
                                        break;
                                case 'F':
@@ -1023,21 +1334,23 @@ int stty_main(int argc, char **argv)
                                                if (!file_name)
                                                        bb_error_msg_and_die(bb_msg_requires_arg, "-F");
                                                /* remove -F param from arg[vc] */
-                                               --argc;
-                                               while (argv[p]) { argv[p] = argv[p+1]; ++p; }
+                                               while (argv[p]) {
+                                                       argv[p] = argv[p+1];
+                                                       ++p;
+                                               }
                                        }
                                        goto end_option;
                                default:
                                        goto invalid_argument;
                                }
                        }
-end_option:
+ end_option:
                        continue;
                }
 
                mp = find_mode(arg);
                if (mp) {
-                       option_mask32 &= ~STTY_noargs;
+                       stty_state &= ~STTY_noargs;
                        continue;
                }
 
@@ -1047,7 +1360,7 @@ end_option:
                                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);
-                       option_mask32 &= ~STTY_noargs;
+                       stty_state &= ~STTY_noargs;
                        ++k;
                        continue;
                }
@@ -1060,7 +1373,7 @@ end_option:
                }
 
                switch (param) {
-#ifdef HAVE_C_LINE
+#ifdef __linux__
                case param_line:
 # ifndef TIOCGWINSZ
                        xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
@@ -1070,6 +1383,7 @@ end_option:
 #ifdef TIOCGWINSZ
                case param_rows:
                case param_cols:
+               case param_columns:
                        xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
                        break;
                case param_size:
@@ -1087,34 +1401,30 @@ end_option:
                default:
                        if (recover_mode(arg, &mode) == 1) break;
                        if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
-invalid_argument:
+ invalid_argument:
                        bb_error_msg_and_die("invalid argument '%s'", arg);
                }
-               option_mask32 &= ~STTY_noargs;
+               stty_state &= ~STTY_noargs;
        }
 
        /* Specifying both -a and -g is an error */
-       if ((option_mask32 & (STTY_verbose_output | STTY_recoverable_output)) ==
-               (STTY_verbose_output | STTY_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 (!(option_mask32 & STTY_noargs) &&
-               (option_mask32 & (STTY_verbose_output | STTY_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 != STDIN_FILENO) {
-                       dup2(fd, STDIN_FILENO);
-                       close(fd);
-               }
-               fdflags = fcntl(STDIN_FILENO, F_GETFL);
-               if (fdflags < 0 ||
-                       fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
-                       perror_on_device_and_die("%s: cannot 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
@@ -1123,9 +1433,9 @@ invalid_argument:
        if (tcgetattr(STDIN_FILENO, &mode))
                perror_on_device_and_die("%s");
 
-       if (option_mask32 & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
-               get_terminal_width_height(STDOUT_FILENO, &max_col, NULL);
-               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;
        }
 
@@ -1142,7 +1452,7 @@ invalid_argument:
                        mp = find_mode(arg+1);
                        if (mp) {
                                set_mode(mp, 1 /* reversed */, &mode);
-                               option_mask32 |= STTY_require_set_attr;
+                               stty_state |= STTY_require_set_attr;
                        }
                        /* It is an option - already parsed. Skip it */
                        continue;
@@ -1151,7 +1461,7 @@ invalid_argument:
                mp = find_mode(arg);
                if (mp) {
                        set_mode(mp, 0 /* non-reversed */, &mode);
-                       option_mask32 |= STTY_require_set_attr;
+                       stty_state |= STTY_require_set_attr;
                        continue;
                }
 
@@ -1159,7 +1469,7 @@ invalid_argument:
                if (cp) {
                        ++k;
                        set_control_char_or_die(cp, argnext, &mode);
-                       option_mask32 |= STTY_require_set_attr;
+                       stty_state |= STTY_require_set_attr;
                        continue;
                }
 
@@ -1169,14 +1479,15 @@ invalid_argument:
                }
 
                switch (param) {
-#ifdef HAVE_C_LINE
+#ifdef __linux__
                case param_line:
                        mode.c_line = xatoul_sfx(argnext, stty_suffixes);
-                       option_mask32 |= STTY_require_set_attr;
+                       stty_state |= STTY_require_set_attr;
                        break;
 #endif
 #ifdef TIOCGWINSZ
                case param_cols:
+               case param_columns:
                        set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
                        break;
                case param_size:
@@ -1191,24 +1502,24 @@ invalid_argument:
                        break;
                case param_ispeed:
                        set_speed_or_die(input_speed, argnext, &mode);
-                       option_mask32 |= (STTY_require_set_attr | STTY_speed_was_set);
+                       stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
                        break;
                case param_ospeed:
                        set_speed_or_die(output_speed, argnext, &mode);
-                       option_mask32 |= (STTY_require_set_attr | STTY_speed_was_set);
+                       stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
                        break;
                default:
                        if (recover_mode(arg, &mode) == 1)
-                               option_mask32 |= STTY_require_set_attr;
+                               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);
-                               option_mask32 |= (STTY_require_set_attr | STTY_speed_was_set);
+                               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 (option_mask32 & STTY_require_set_attr) {
+       if (stty_state & STTY_require_set_attr) {
                struct termios new_mode;
 
                if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
@@ -1228,7 +1539,12 @@ invalid_argument:
                        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
@@ -1239,8 +1555,8 @@ invalid_argument:
                           error for a true failure to set the baud rate */
 
                        new_mode.c_cflag &= (~CIBAUD);
-                       if (option_mask32 & STTY_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_and_die("%s: cannot perform all requested operations");
                }