busybox/coreutils/stty.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/* stty -- change and print terminal line settings
   3   Copyright (C) 1990-1999 Free Software Foundation, Inc.
   4
   5   Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
   6*/
   7/* Usage: stty [-ag] [-F device] [setting...]
   8
   9   Options:
  10   -a Write all current settings to stdout in human-readable form.
  11   -g Write all current settings to stdout in stty-readable form.
  12   -F Open and use the specified device instead of stdin
  13
  14   If no args are given, write to stdout the baud rate and settings that
  15   have been changed from their defaults.  Mode reading and changes
  16   are done on the specified device, or stdin if none was specified.
  17
  18   David MacKenzie <djm@gnu.ai.mit.edu>
  19
  20   Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
  21
  22   */
  23
  24#include "libbb.h"
  25
  26#ifndef _POSIX_VDISABLE
  27# define _POSIX_VDISABLE ((unsigned char) 0)
  28#endif
  29
  30#define Control(c) ((c) & 0x1f)
  31/* Canonical values for control characters */
  32#ifndef CINTR
  33# define CINTR Control('c')
  34#endif
  35#ifndef CQUIT
  36# define CQUIT 28
  37#endif
  38#ifndef CERASE
  39# define CERASE 127
  40#endif
  41#ifndef CKILL
  42# define CKILL Control('u')
  43#endif
  44#ifndef CEOF
  45# define CEOF Control('d')
  46#endif
  47#ifndef CEOL
  48# define CEOL _POSIX_VDISABLE
  49#endif
  50#ifndef CSTART
  51# define CSTART Control('q')
  52#endif
  53#ifndef CSTOP
  54# define CSTOP Control('s')
  55#endif
  56#ifndef CSUSP
  57# define CSUSP Control('z')
  58#endif
  59#if defined(VEOL2) && !defined(CEOL2)
  60# define CEOL2 _POSIX_VDISABLE
  61#endif
  62/* ISC renamed swtch to susp for termios, but we'll accept either name */
  63#if defined(VSUSP) && !defined(VSWTCH)
  64# define VSWTCH VSUSP
  65# define CSWTCH CSUSP
  66#endif
  67#if defined(VSWTCH) && !defined(CSWTCH)
  68# define CSWTCH _POSIX_VDISABLE
  69#endif
  70
  71/* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'.
  72   So the default is to disable 'swtch.'  */
  73#if defined(__sparc__) && defined(__svr4__)
  74# undef CSWTCH
  75# define CSWTCH _POSIX_VDISABLE
  76#endif
  77
  78#if defined(VWERSE) && !defined(VWERASE)       /* AIX-3.2.5 */
  79# define VWERASE VWERSE
  80#endif
  81#if defined(VDSUSP) && !defined(CDSUSP)
  82# define CDSUSP Control('y')
  83#endif
  84#if !defined(VREPRINT) && defined(VRPRNT)       /* Irix 4.0.5 */
  85# define VREPRINT VRPRNT
  86#endif
  87#if defined(VREPRINT) && !defined(CRPRNT)
  88# define CRPRNT Control('r')
  89#endif
  90#if defined(VWERASE) && !defined(CWERASE)
  91# define CWERASE Control('w')
  92#endif
  93#if defined(VLNEXT) && !defined(CLNEXT)
  94# define CLNEXT Control('v')
  95#endif
  96#if defined(VDISCARD) && !defined(VFLUSHO)
  97# define VFLUSHO VDISCARD
  98#endif
  99#if defined(VFLUSH) && !defined(VFLUSHO)        /* Ultrix 4.2 */
 100# define VFLUSHO VFLUSH
 101#endif
 102#if defined(CTLECH) && !defined(ECHOCTL)        /* Ultrix 4.3 */
 103# define ECHOCTL CTLECH
 104#endif
 105#if defined(TCTLECH) && !defined(ECHOCTL)       /* Ultrix 4.2 */
 106# define ECHOCTL TCTLECH
 107#endif
 108#if defined(CRTKIL) && !defined(ECHOKE)         /* Ultrix 4.2 and 4.3 */
 109# define ECHOKE CRTKIL
 110#endif
 111#if defined(VFLUSHO) && !defined(CFLUSHO)
 112# define CFLUSHO Control('o')
 113#endif
 114#if defined(VSTATUS) && !defined(CSTATUS)
 115# define CSTATUS Control('t')
 116#endif
 117
 118/* Which speeds to set */
 119enum speed_setting {
 120        input_speed, output_speed, both_speeds
 121};
 122
 123/* Which member(s) of 'struct termios' a mode uses */
 124enum {
 125        /* Do NOT change the order or values, as mode_type_flag()
 126         * depends on them */
 127        control, input, output, local, combination
 128};
 129
 130/* Flags for 'struct mode_info' */
 131#define SANE_SET 1              /* Set in 'sane' mode                  */
 132#define SANE_UNSET 2            /* Unset in 'sane' mode                */
 133#define REV 4                   /* Can be turned off by prepending '-' */
 134#define OMIT 8                  /* Don't display value                 */
 135
 136
 137/* Each mode.
 138 * This structure should be kept as small as humanly possible.
 139 */
 140struct mode_info {
 141        const uint8_t type;           /* Which structure element to change    */
 142        const uint8_t flags;          /* Setting and display options          */
 143        /* only these values are ever used, so... */
 144#if   (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x100
 145        const uint8_t mask;
 146#elif (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x10000
 147        const uint16_t mask;
 148#else
 149        const tcflag_t mask;          /* Other bits to turn off for this mode */
 150#endif
 151        /* was using short here, but ppc32 was unhappy */
 152        const tcflag_t bits;          /* Bits to set for this mode            */
 153};
 154
 155enum {
 156        /* Must match mode_name[] and mode_info[] order! */
 157        IDX_evenp = 0,
 158        IDX_parity,
 159        IDX_oddp,
 160        IDX_nl,
 161        IDX_ek,
 162        IDX_sane,
 163        IDX_cooked,
 164        IDX_raw,
 165        IDX_pass8,
 166        IDX_litout,
 167        IDX_cbreak,
 168        IDX_crt,
 169        IDX_dec,
 170#ifdef IXANY
 171        IDX_decctlq,
 172#endif
 173#if defined(TABDLY) || defined(OXTABS)
 174        IDX_tabs,
 175#endif
 176#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
 177        IDX_lcase,
 178        IDX_LCASE,
 179#endif
 180};
 181
 182#define MI_ENTRY(N,T,F,B,M) N "\0"
 183
 184/* Mode names given on command line */
 185static const char mode_name[] =
 186        MI_ENTRY("evenp",    combination, REV        | OMIT, 0,          0 )
 187        MI_ENTRY("parity",   combination, REV        | OMIT, 0,          0 )
 188        MI_ENTRY("oddp",     combination, REV        | OMIT, 0,          0 )
 189        MI_ENTRY("nl",       combination, REV        | OMIT, 0,          0 )
 190        MI_ENTRY("ek",       combination, OMIT,              0,          0 )
 191        MI_ENTRY("sane",     combination, OMIT,              0,          0 )
 192        MI_ENTRY("cooked",   combination, REV        | OMIT, 0,          0 )
 193        MI_ENTRY("raw",      combination, REV        | OMIT, 0,          0 )
 194        MI_ENTRY("pass8",    combination, REV        | OMIT, 0,          0 )
 195        MI_ENTRY("litout",   combination, REV        | OMIT, 0,          0 )
 196        MI_ENTRY("cbreak",   combination, REV        | OMIT, 0,          0 )
 197        MI_ENTRY("crt",      combination, OMIT,              0,          0 )
 198        MI_ENTRY("dec",      combination, OMIT,              0,          0 )
 199#ifdef IXANY
 200        MI_ENTRY("decctlq",  combination, REV        | OMIT, 0,          0 )
 201#endif
 202#if defined(TABDLY) || defined(OXTABS)
 203        MI_ENTRY("tabs",     combination, REV        | OMIT, 0,          0 )
 204#endif
 205#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
 206        MI_ENTRY("lcase",    combination, REV        | OMIT, 0,          0 )
 207        MI_ENTRY("LCASE",    combination, REV        | OMIT, 0,          0 )
 208#endif
 209        MI_ENTRY("parenb",   control,     REV,               PARENB,     0 )
 210        MI_ENTRY("parodd",   control,     REV,               PARODD,     0 )
 211        MI_ENTRY("cs5",      control,     0,                 CS5,     CSIZE)
 212        MI_ENTRY("cs6",      control,     0,                 CS6,     CSIZE)
 213        MI_ENTRY("cs7",      control,     0,                 CS7,     CSIZE)
 214        MI_ENTRY("cs8",      control,     0,                 CS8,     CSIZE)
 215        MI_ENTRY("hupcl",    control,     REV,               HUPCL,      0 )
 216        MI_ENTRY("hup",      control,     REV        | OMIT, HUPCL,      0 )
 217        MI_ENTRY("cstopb",   control,     REV,               CSTOPB,     0 )
 218        MI_ENTRY("cread",    control,     SANE_SET   | REV,  CREAD,      0 )
 219        MI_ENTRY("clocal",   control,     REV,               CLOCAL,     0 )
 220#ifdef CRTSCTS
 221        MI_ENTRY("crtscts",  control,     REV,               CRTSCTS,    0 )
 222#endif
 223        MI_ENTRY("ignbrk",   input,       SANE_UNSET | REV,  IGNBRK,     0 )
 224        MI_ENTRY("brkint",   input,       SANE_SET   | REV,  BRKINT,     0 )
 225        MI_ENTRY("ignpar",   input,       REV,               IGNPAR,     0 )
 226        MI_ENTRY("parmrk",   input,       REV,               PARMRK,     0 )
 227        MI_ENTRY("inpck",    input,       REV,               INPCK,      0 )
 228        MI_ENTRY("istrip",   input,       REV,               ISTRIP,     0 )
 229        MI_ENTRY("inlcr",    input,       SANE_UNSET | REV,  INLCR,      0 )
 230        MI_ENTRY("igncr",    input,       SANE_UNSET | REV,  IGNCR,      0 )
 231        MI_ENTRY("icrnl",    input,       SANE_SET   | REV,  ICRNL,      0 )
 232        MI_ENTRY("ixon",     input,       REV,               IXON,       0 )
 233        MI_ENTRY("ixoff",    input,       SANE_UNSET | REV,  IXOFF,      0 )
 234        MI_ENTRY("tandem",   input,       REV        | OMIT, IXOFF,      0 )
 235#ifdef IUCLC
 236        MI_ENTRY("iuclc",    input,       SANE_UNSET | REV,  IUCLC,      0 )
 237#endif
 238#ifdef IXANY
 239        MI_ENTRY("ixany",    input,       SANE_UNSET | REV,  IXANY,      0 )
 240#endif
 241#ifdef IMAXBEL
 242        MI_ENTRY("imaxbel",  input,       SANE_SET   | REV,  IMAXBEL,    0 )
 243#endif
 244        MI_ENTRY("opost",    output,      SANE_SET   | REV,  OPOST,      0 )
 245#ifdef OLCUC
 246        MI_ENTRY("olcuc",    output,      SANE_UNSET | REV,  OLCUC,      0 )
 247#endif
 248#ifdef OCRNL
 249        MI_ENTRY("ocrnl",    output,      SANE_UNSET | REV,  OCRNL,      0 )
 250#endif
 251#ifdef ONLCR
 252        MI_ENTRY("onlcr",    output,      SANE_SET   | REV,  ONLCR,      0 )
 253#endif
 254#ifdef ONOCR
 255        MI_ENTRY("onocr",    output,      SANE_UNSET | REV,  ONOCR,      0 )
 256#endif
 257#ifdef ONLRET
 258        MI_ENTRY("onlret",   output,      SANE_UNSET | REV,  ONLRET,     0 )
 259#endif
 260#ifdef OFILL
 261        MI_ENTRY("ofill",    output,      SANE_UNSET | REV,  OFILL,      0 )
 262#endif
 263#ifdef OFDEL
 264        MI_ENTRY("ofdel",    output,      SANE_UNSET | REV,  OFDEL,      0 )
 265#endif
 266#ifdef NLDLY
 267        MI_ENTRY("nl1",      output,      SANE_UNSET,        NL1,     NLDLY)
 268        MI_ENTRY("nl0",      output,      SANE_SET,          NL0,     NLDLY)
 269#endif
 270#ifdef CRDLY
 271        MI_ENTRY("cr3",      output,      SANE_UNSET,        CR3,     CRDLY)
 272        MI_ENTRY("cr2",      output,      SANE_UNSET,        CR2,     CRDLY)
 273        MI_ENTRY("cr1",      output,      SANE_UNSET,        CR1,     CRDLY)
 274        MI_ENTRY("cr0",      output,      SANE_SET,          CR0,     CRDLY)
 275#endif
 276
 277#ifdef TABDLY
 278        MI_ENTRY("tab3",     output,      SANE_UNSET,        TAB3,   TABDLY)
 279        MI_ENTRY("tab2",     output,      SANE_UNSET,        TAB2,   TABDLY)
 280        MI_ENTRY("tab1",     output,      SANE_UNSET,        TAB1,   TABDLY)
 281        MI_ENTRY("tab0",     output,      SANE_SET,          TAB0,   TABDLY)
 282#else
 283# ifdef OXTABS
 284        MI_ENTRY("tab3",     output,      SANE_UNSET,        OXTABS,     0 )
 285# endif
 286#endif
 287
 288#ifdef BSDLY
 289        MI_ENTRY("bs1",      output,      SANE_UNSET,        BS1,     BSDLY)
 290        MI_ENTRY("bs0",      output,      SANE_SET,          BS0,     BSDLY)
 291#endif
 292#ifdef VTDLY
 293        MI_ENTRY("vt1",      output,      SANE_UNSET,        VT1,     VTDLY)
 294        MI_ENTRY("vt0",      output,      SANE_SET,          VT0,     VTDLY)
 295#endif
 296#ifdef FFDLY
 297        MI_ENTRY("ff1",      output,      SANE_UNSET,        FF1,     FFDLY)
 298        MI_ENTRY("ff0",      output,      SANE_SET,          FF0,     FFDLY)
 299#endif
 300        MI_ENTRY("isig",     local,       SANE_SET   | REV,  ISIG,       0 )
 301        MI_ENTRY("icanon",   local,       SANE_SET   | REV,  ICANON,     0 )
 302#ifdef IEXTEN
 303        MI_ENTRY("iexten",   local,       SANE_SET   | REV,  IEXTEN,     0 )
 304#endif
 305        MI_ENTRY("echo",     local,       SANE_SET   | REV,  ECHO,       0 )
 306        MI_ENTRY("echoe",    local,       SANE_SET   | REV,  ECHOE,      0 )
 307        MI_ENTRY("crterase", local,       REV        | OMIT, ECHOE,      0 )
 308        MI_ENTRY("echok",    local,       SANE_SET   | REV,  ECHOK,      0 )
 309        MI_ENTRY("echonl",   local,       SANE_UNSET | REV,  ECHONL,     0 )
 310        MI_ENTRY("noflsh",   local,       SANE_UNSET | REV,  NOFLSH,     0 )
 311#ifdef XCASE
 312        MI_ENTRY("xcase",    local,       SANE_UNSET | REV,  XCASE,      0 )
 313#endif
 314#ifdef TOSTOP
 315        MI_ENTRY("tostop",   local,       SANE_UNSET | REV,  TOSTOP,     0 )
 316#endif
 317#ifdef ECHOPRT
 318        MI_ENTRY("echoprt",  local,       SANE_UNSET | REV,  ECHOPRT,    0 )
 319        MI_ENTRY("prterase", local,       REV | OMIT,        ECHOPRT,    0 )
 320#endif
 321#ifdef ECHOCTL
 322        MI_ENTRY("echoctl",  local,       SANE_SET   | REV,  ECHOCTL,    0 )
 323        MI_ENTRY("ctlecho",  local,       REV        | OMIT, ECHOCTL,    0 )
 324#endif
 325#ifdef ECHOKE
 326        MI_ENTRY("echoke",   local,       SANE_SET   | REV,  ECHOKE,     0 )
 327        MI_ENTRY("crtkill",  local,       REV        | OMIT, ECHOKE,     0 )
 328#endif
 329        ;
 330
 331#undef MI_ENTRY
 332#define MI_ENTRY(N,T,F,B,M) { T, F, M, B },
 333
 334static const struct mode_info mode_info[] = {
 335        /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */
 336        MI_ENTRY("evenp",    combination, REV        | OMIT, 0,          0 )
 337        MI_ENTRY("parity",   combination, REV        | OMIT, 0,          0 )
 338        MI_ENTRY("oddp",     combination, REV        | OMIT, 0,          0 )
 339        MI_ENTRY("nl",       combination, REV        | OMIT, 0,          0 )
 340        MI_ENTRY("ek",       combination, OMIT,              0,          0 )
 341        MI_ENTRY("sane",     combination, OMIT,              0,          0 )
 342        MI_ENTRY("cooked",   combination, REV        | OMIT, 0,          0 )
 343        MI_ENTRY("raw",      combination, REV        | OMIT, 0,          0 )
 344        MI_ENTRY("pass8",    combination, REV        | OMIT, 0,          0 )
 345        MI_ENTRY("litout",   combination, REV        | OMIT, 0,          0 )
 346        MI_ENTRY("cbreak",   combination, REV        | OMIT, 0,          0 )
 347        MI_ENTRY("crt",      combination, OMIT,              0,          0 )
 348        MI_ENTRY("dec",      combination, OMIT,              0,          0 )
 349#ifdef IXANY
 350        MI_ENTRY("decctlq",  combination, REV        | OMIT, 0,          0 )
 351#endif
 352#if defined(TABDLY) || defined(OXTABS)
 353        MI_ENTRY("tabs",     combination, REV        | OMIT, 0,          0 )
 354#endif
 355#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
 356        MI_ENTRY("lcase",    combination, REV        | OMIT, 0,          0 )
 357        MI_ENTRY("LCASE",    combination, REV        | OMIT, 0,          0 )
 358#endif
 359        MI_ENTRY("parenb",   control,     REV,               PARENB,     0 )
 360        MI_ENTRY("parodd",   control,     REV,               PARODD,     0 )
 361        MI_ENTRY("cs5",      control,     0,                 CS5,     CSIZE)
 362        MI_ENTRY("cs6",      control,     0,                 CS6,     CSIZE)
 363        MI_ENTRY("cs7",      control,     0,                 CS7,     CSIZE)
 364        MI_ENTRY("cs8",      control,     0,                 CS8,     CSIZE)
 365        MI_ENTRY("hupcl",    control,     REV,               HUPCL,      0 )
 366        MI_ENTRY("hup",      control,     REV        | OMIT, HUPCL,      0 )
 367        MI_ENTRY("cstopb",   control,     REV,               CSTOPB,     0 )
 368        MI_ENTRY("cread",    control,     SANE_SET   | REV,  CREAD,      0 )
 369        MI_ENTRY("clocal",   control,     REV,               CLOCAL,     0 )
 370#ifdef CRTSCTS
 371        MI_ENTRY("crtscts",  control,     REV,               CRTSCTS,    0 )
 372#endif
 373        MI_ENTRY("ignbrk",   input,       SANE_UNSET | REV,  IGNBRK,     0 )
 374        MI_ENTRY("brkint",   input,       SANE_SET   | REV,  BRKINT,     0 )
 375        MI_ENTRY("ignpar",   input,       REV,               IGNPAR,     0 )
 376        MI_ENTRY("parmrk",   input,       REV,               PARMRK,     0 )
 377        MI_ENTRY("inpck",    input,       REV,               INPCK,      0 )
 378        MI_ENTRY("istrip",   input,       REV,               ISTRIP,     0 )
 379        MI_ENTRY("inlcr",    input,       SANE_UNSET | REV,  INLCR,      0 )
 380        MI_ENTRY("igncr",    input,       SANE_UNSET | REV,  IGNCR,      0 )
 381        MI_ENTRY("icrnl",    input,       SANE_SET   | REV,  ICRNL,      0 )
 382        MI_ENTRY("ixon",     input,       REV,               IXON,       0 )
 383        MI_ENTRY("ixoff",    input,       SANE_UNSET | REV,  IXOFF,      0 )
 384        MI_ENTRY("tandem",   input,       REV        | OMIT, IXOFF,      0 )
 385#ifdef IUCLC
 386        MI_ENTRY("iuclc",    input,       SANE_UNSET | REV,  IUCLC,      0 )
 387#endif
 388#ifdef IXANY
 389        MI_ENTRY("ixany",    input,       SANE_UNSET | REV,  IXANY,      0 )
 390#endif
 391#ifdef IMAXBEL
 392        MI_ENTRY("imaxbel",  input,       SANE_SET   | REV,  IMAXBEL,    0 )
 393#endif
 394        MI_ENTRY("opost",    output,      SANE_SET   | REV,  OPOST,      0 )
 395#ifdef OLCUC
 396        MI_ENTRY("olcuc",    output,      SANE_UNSET | REV,  OLCUC,      0 )
 397#endif
 398#ifdef OCRNL
 399        MI_ENTRY("ocrnl",    output,      SANE_UNSET | REV,  OCRNL,      0 )
 400#endif
 401#ifdef ONLCR
 402        MI_ENTRY("onlcr",    output,      SANE_SET   | REV,  ONLCR,      0 )
 403#endif
 404#ifdef ONOCR
 405        MI_ENTRY("onocr",    output,      SANE_UNSET | REV,  ONOCR,      0 )
 406#endif
 407#ifdef ONLRET
 408        MI_ENTRY("onlret",   output,      SANE_UNSET | REV,  ONLRET,     0 )
 409#endif
 410#ifdef OFILL
 411        MI_ENTRY("ofill",    output,      SANE_UNSET | REV,  OFILL,      0 )
 412#endif
 413#ifdef OFDEL
 414        MI_ENTRY("ofdel",    output,      SANE_UNSET | REV,  OFDEL,      0 )
 415#endif
 416#ifdef NLDLY
 417        MI_ENTRY("nl1",      output,      SANE_UNSET,        NL1,     NLDLY)
 418        MI_ENTRY("nl0",      output,      SANE_SET,          NL0,     NLDLY)
 419#endif
 420#ifdef CRDLY
 421        MI_ENTRY("cr3",      output,      SANE_UNSET,        CR3,     CRDLY)
 422        MI_ENTRY("cr2",      output,      SANE_UNSET,        CR2,     CRDLY)
 423        MI_ENTRY("cr1",      output,      SANE_UNSET,        CR1,     CRDLY)
 424        MI_ENTRY("cr0",      output,      SANE_SET,          CR0,     CRDLY)
 425#endif
 426
 427#ifdef TABDLY
 428        MI_ENTRY("tab3",     output,      SANE_UNSET,        TAB3,   TABDLY)
 429        MI_ENTRY("tab2",     output,      SANE_UNSET,        TAB2,   TABDLY)
 430        MI_ENTRY("tab1",     output,      SANE_UNSET,        TAB1,   TABDLY)
 431        MI_ENTRY("tab0",     output,      SANE_SET,          TAB0,   TABDLY)
 432#else
 433# ifdef OXTABS
 434        MI_ENTRY("tab3",     output,      SANE_UNSET,        OXTABS,     0 )
 435# endif
 436#endif
 437
 438#ifdef BSDLY
 439        MI_ENTRY("bs1",      output,      SANE_UNSET,        BS1,     BSDLY)
 440        MI_ENTRY("bs0",      output,      SANE_SET,          BS0,     BSDLY)
 441#endif
 442#ifdef VTDLY
 443        MI_ENTRY("vt1",      output,      SANE_UNSET,        VT1,     VTDLY)
 444        MI_ENTRY("vt0",      output,      SANE_SET,          VT0,     VTDLY)
 445#endif
 446#ifdef FFDLY
 447        MI_ENTRY("ff1",      output,      SANE_UNSET,        FF1,     FFDLY)
 448        MI_ENTRY("ff0",      output,      SANE_SET,          FF0,     FFDLY)
 449#endif
 450        MI_ENTRY("isig",     local,       SANE_SET   | REV,  ISIG,       0 )
 451        MI_ENTRY("icanon",   local,       SANE_SET   | REV,  ICANON,     0 )
 452#ifdef IEXTEN
 453        MI_ENTRY("iexten",   local,       SANE_SET   | REV,  IEXTEN,     0 )
 454#endif
 455        MI_ENTRY("echo",     local,       SANE_SET   | REV,  ECHO,       0 )
 456        MI_ENTRY("echoe",    local,       SANE_SET   | REV,  ECHOE,      0 )
 457        MI_ENTRY("crterase", local,       REV        | OMIT, ECHOE,      0 )
 458        MI_ENTRY("echok",    local,       SANE_SET   | REV,  ECHOK,      0 )
 459        MI_ENTRY("echonl",   local,       SANE_UNSET | REV,  ECHONL,     0 )
 460        MI_ENTRY("noflsh",   local,       SANE_UNSET | REV,  NOFLSH,     0 )
 461#ifdef XCASE
 462        MI_ENTRY("xcase",    local,       SANE_UNSET | REV,  XCASE,      0 )
 463#endif
 464#ifdef TOSTOP
 465        MI_ENTRY("tostop",   local,       SANE_UNSET | REV,  TOSTOP,     0 )
 466#endif
 467#ifdef ECHOPRT
 468        MI_ENTRY("echoprt",  local,       SANE_UNSET | REV,  ECHOPRT,    0 )
 469        MI_ENTRY("prterase", local,       REV | OMIT,        ECHOPRT,    0 )
 470#endif
 471#ifdef ECHOCTL
 472        MI_ENTRY("echoctl",  local,       SANE_SET   | REV,  ECHOCTL,    0 )
 473        MI_ENTRY("ctlecho",  local,       REV        | OMIT, ECHOCTL,    0 )
 474#endif
 475#ifdef ECHOKE
 476        MI_ENTRY("echoke",   local,       SANE_SET   | REV,  ECHOKE,     0 )
 477        MI_ENTRY("crtkill",  local,       REV        | OMIT, ECHOKE,     0 )
 478#endif
 479};
 480
 481enum {
 482        NUM_mode_info = ARRAY_SIZE(mode_info)
 483};
 484
 485
 486/* Control characters */
 487struct control_info {
 488        const uint8_t saneval;  /* Value to set for 'stty sane' */
 489        const uint8_t offset;   /* Offset in c_cc */
 490};
 491
 492enum {
 493        /* Must match control_name[] and control_info[] order! */
 494        CIDX_intr = 0,
 495        CIDX_quit,
 496        CIDX_erase,
 497        CIDX_kill,
 498        CIDX_eof,
 499        CIDX_eol,
 500#ifdef VEOL2
 501        CIDX_eol2,
 502#endif
 503#ifdef VSWTCH
 504        CIDX_swtch,
 505#endif
 506        CIDX_start,
 507        CIDX_stop,
 508        CIDX_susp,
 509#ifdef VDSUSP
 510        CIDX_dsusp,
 511#endif
 512#ifdef VREPRINT
 513        CIDX_rprnt,
 514#endif
 515#ifdef VWERASE
 516        CIDX_werase,
 517#endif
 518#ifdef VLNEXT
 519        CIDX_lnext,
 520#endif
 521#ifdef VFLUSHO
 522        CIDX_flush,
 523#endif
 524#ifdef VSTATUS
 525        CIDX_status,
 526#endif
 527        CIDX_min,
 528        CIDX_time,
 529};
 530
 531#define CI_ENTRY(n,s,o) n "\0"
 532
 533/* Name given on command line */
 534static const char control_name[] =
 535        CI_ENTRY("intr",     CINTR,   VINTR   )
 536        CI_ENTRY("quit",     CQUIT,   VQUIT   )
 537        CI_ENTRY("erase",    CERASE,  VERASE  )
 538        CI_ENTRY("kill",     CKILL,   VKILL   )
 539        CI_ENTRY("eof",      CEOF,    VEOF    )
 540        CI_ENTRY("eol",      CEOL,    VEOL    )
 541#ifdef VEOL2
 542        CI_ENTRY("eol2",     CEOL2,   VEOL2   )
 543#endif
 544#ifdef VSWTCH
 545        CI_ENTRY("swtch",    CSWTCH,  VSWTCH  )
 546#endif
 547        CI_ENTRY("start",    CSTART,  VSTART  )
 548        CI_ENTRY("stop",     CSTOP,   VSTOP   )
 549        CI_ENTRY("susp",     CSUSP,   VSUSP   )
 550#ifdef VDSUSP
 551        CI_ENTRY("dsusp",    CDSUSP,  VDSUSP  )
 552#endif
 553#ifdef VREPRINT
 554        CI_ENTRY("rprnt",    CRPRNT,  VREPRINT)
 555#endif
 556#ifdef VWERASE
 557        CI_ENTRY("werase",   CWERASE, VWERASE )
 558#endif
 559#ifdef VLNEXT
 560        CI_ENTRY("lnext",    CLNEXT,  VLNEXT  )
 561#endif
 562#ifdef VFLUSHO
 563        CI_ENTRY("flush",    CFLUSHO, VFLUSHO )
 564#endif
 565#ifdef VSTATUS
 566        CI_ENTRY("status",   CSTATUS, VSTATUS )
 567#endif
 568        /* These must be last because of the display routines */
 569        CI_ENTRY("min",      1,       VMIN    )
 570        CI_ENTRY("time",     0,       VTIME   )
 571        ;
 572
 573#undef CI_ENTRY
 574#define CI_ENTRY(n,s,o) { s, o },
 575
 576static const struct control_info control_info[] = {
 577        /* This should be verbatim cut-n-paste copy of the above CI_ENTRYs */
 578        CI_ENTRY("intr",     CINTR,   VINTR   )
 579        CI_ENTRY("quit",     CQUIT,   VQUIT   )
 580        CI_ENTRY("erase",    CERASE,  VERASE  )
 581        CI_ENTRY("kill",     CKILL,   VKILL   )
 582        CI_ENTRY("eof",      CEOF,    VEOF    )
 583        CI_ENTRY("eol",      CEOL,    VEOL    )
 584#ifdef VEOL2
 585        CI_ENTRY("eol2",     CEOL2,   VEOL2   )
 586#endif
 587#ifdef VSWTCH
 588        CI_ENTRY("swtch",    CSWTCH,  VSWTCH  )
 589#endif
 590        CI_ENTRY("start",    CSTART,  VSTART  )
 591        CI_ENTRY("stop",     CSTOP,   VSTOP   )
 592        CI_ENTRY("susp",     CSUSP,   VSUSP   )
 593#ifdef VDSUSP
 594        CI_ENTRY("dsusp",    CDSUSP,  VDSUSP  )
 595#endif
 596#ifdef VREPRINT
 597        CI_ENTRY("rprnt",    CRPRNT,  VREPRINT)
 598#endif
 599#ifdef VWERASE
 600        CI_ENTRY("werase",   CWERASE, VWERASE )
 601#endif
 602#ifdef VLNEXT
 603        CI_ENTRY("lnext",    CLNEXT,  VLNEXT  )
 604#endif
 605#ifdef VFLUSHO
 606        CI_ENTRY("flush",    CFLUSHO, VFLUSHO )
 607#endif
 608#ifdef VSTATUS
 609        CI_ENTRY("status",   CSTATUS, VSTATUS )
 610#endif
 611        /* These must be last because of the display routines */
 612        CI_ENTRY("min",      1,       VMIN    )
 613        CI_ENTRY("time",     0,       VTIME   )
 614};
 615
 616enum {
 617        NUM_control_info = ARRAY_SIZE(control_info)
 618};
 619
 620
 621struct globals {
 622        const char *device_name;
 623        /* The width of the screen, for output wrapping */
 624        unsigned max_col;
 625        /* Current position, to know when to wrap */
 626        unsigned current_col;
 627        char buf[10];
 628};
 629#define G (*(struct globals*)&bb_common_bufsiz1)
 630#define INIT_G() do { \
 631        G.device_name = bb_msg_standard_input; \
 632        G.max_col = 80; \
 633} while (0)
 634
 635
 636/* Return a string that is the printable representation of character CH */
 637/* Adapted from 'cat' by Torbjorn Granlund */
 638static const char *visible(unsigned ch)
 639{
 640        char *bpout = G.buf;
 641
 642        if (ch == _POSIX_VDISABLE)
 643                return "<undef>";
 644
 645        if (ch >= 128) {
 646                ch -= 128;
 647                *bpout++ = 'M';
 648                *bpout++ = '-';
 649        }
 650
 651        if (ch < 32) {
 652                *bpout++ = '^';
 653                *bpout++ = ch + 64;
 654        } else if (ch < 127) {
 655                *bpout++ = ch;
 656        } else {
 657                *bpout++ = '^';
 658                *bpout++ = '?';
 659        }
 660
 661        *bpout = '\0';
 662        return G.buf;
 663}
 664
 665static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode)
 666{
 667        static const uint8_t tcflag_offsets[] ALIGN1 = {
 668                offsetof(struct termios, c_cflag), /* control */
 669                offsetof(struct termios, c_iflag), /* input */
 670                offsetof(struct termios, c_oflag), /* output */
 671                offsetof(struct termios, c_lflag)  /* local */
 672        };
 673
 674        if (type <= local) {
 675                return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]);
 676        }
 677        return NULL;
 678}
 679
 680static void set_speed_or_die(enum speed_setting type, const char *arg,
 681                                        struct termios *mode)
 682{
 683        speed_t baud;
 684
 685        baud = tty_value_to_baud(xatou(arg));
 686
 687        if (type != output_speed) {     /* either input or both */
 688                cfsetispeed(mode, baud);
 689        }
 690        if (type != input_speed) {      /* either output or both */
 691                cfsetospeed(mode, baud);
 692        }
 693}
 694
 695static NORETURN void perror_on_device_and_die(const char *fmt)
 696{
 697        bb_perror_msg_and_die(fmt, G.device_name);
 698}
 699
 700static void perror_on_device(const char *fmt)
 701{
 702        bb_perror_msg(fmt, G.device_name);
 703}
 704
 705/* Print format string MESSAGE and optional args.
 706   Wrap to next line first if it won't fit.
 707   Print a space first unless MESSAGE will start a new line */
 708static void wrapf(const char *message, ...)
 709{
 710        char buf[128];
 711        va_list args;
 712        unsigned buflen;
 713
 714        va_start(args, message);
 715        buflen = vsnprintf(buf, sizeof(buf), message, args);
 716        va_end(args);
 717        /* We seem to be called only with suitable lengths, but check if
 718           somebody failed to adhere to this assumption just to be sure.  */
 719        if (!buflen || buflen >= sizeof(buf)) return;
 720
 721        if (G.current_col > 0) {
 722                G.current_col++;
 723                if (buf[0] != '\n') {
 724                        if (G.current_col + buflen >= G.max_col) {
 725                                bb_putchar('\n');
 726                                G.current_col = 0;
 727                        } else
 728                                bb_putchar(' ');
 729                }
 730        }
 731        fputs(buf, stdout);
 732        G.current_col += buflen;
 733        if (buf[buflen-1] == '\n')
 734                G.current_col = 0;
 735}
 736
 737static void newline(void)
 738{
 739        if (G.current_col != 0)
 740                wrapf("\n");
 741}
 742
 743static void set_window_size(int rows, int cols)
 744{
 745        struct winsize win = { 0, 0, 0, 0 };
 746
 747        if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
 748                if (errno != EINVAL) {
 749                        goto bail;
 750                }
 751                memset(&win, 0, sizeof(win));
 752        }
 753
 754        if (rows >= 0)
 755                win.ws_row = rows;
 756        if (cols >= 0)
 757                win.ws_col = cols;
 758
 759        if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
 760bail:
 761                perror_on_device("%s");
 762}
 763
 764static void display_window_size(int fancy)
 765{
 766        const char *fmt_str = "%s\0%s: no size information for this device";
 767        unsigned width, height;
 768
 769        if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
 770                if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
 771                        perror_on_device(fmt_str);
 772                }
 773        } else {
 774                wrapf(fancy ? "rows %u; columns %u;" : "%u %u\n",
 775                                height, width);
 776        }
 777}
 778
 779static const struct suffix_mult stty_suffixes[] = {
 780        { "b",  512 },
 781        { "k", 1024 },
 782        { "B", 1024 },
 783        { "", 0 }
 784};
 785
 786static const struct mode_info *find_mode(const char *name)
 787{
 788        int i = index_in_strings(mode_name, name);
 789        return i >= 0 ? &mode_info[i] : NULL;
 790}
 791
 792static const struct control_info *find_control(const char *name)
 793{
 794        int i = index_in_strings(control_name, name);
 795        return i >= 0 ? &control_info[i] : NULL;
 796}
 797
 798enum {
 799        param_need_arg = 0x80,
 800        param_line    = 1 | 0x80,
 801        param_rows    = 2 | 0x80,
 802        param_cols    = 3 | 0x80,
 803        param_columns = 4 | 0x80,
 804        param_size    = 5,
 805        param_speed   = 6,
 806        param_ispeed  = 7 | 0x80,
 807        param_ospeed  = 8 | 0x80,
 808};
 809
 810static int find_param(const char *name)
 811{
 812        static const char params[] ALIGN1 =
 813                "line\0"    /* 1 */
 814                "rows\0"    /* 2 */
 815                "cols\0"    /* 3 */
 816                "columns\0" /* 4 */
 817                "size\0"    /* 5 */
 818                "speed\0"   /* 6 */
 819                "ispeed\0"
 820                "ospeed\0";
 821        int i = index_in_strings(params, name) + 1;
 822        if (i == 0)
 823                return 0;
 824        if (i != 5 && i != 6)
 825                i |= 0x80;
 826        return i;
 827}
 828
 829static int recover_mode(const char *arg, struct termios *mode)
 830{
 831        int i, n;
 832        unsigned chr;
 833        unsigned long iflag, oflag, cflag, lflag;
 834
 835        /* Scan into temporaries since it is too much trouble to figure out
 836           the right format for 'tcflag_t' */
 837        if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
 838                           &iflag, &oflag, &cflag, &lflag, &n) != 4)
 839                return 0;
 840        mode->c_iflag = iflag;
 841        mode->c_oflag = oflag;
 842        mode->c_cflag = cflag;
 843        mode->c_lflag = lflag;
 844        arg += n;
 845        for (i = 0; i < NCCS; ++i) {
 846                if (sscanf(arg, ":%x%n", &chr, &n) != 1)
 847                        return 0;
 848                mode->c_cc[i] = chr;
 849                arg += n;
 850        }
 851
 852        /* Fail if there are too many fields */
 853        if (*arg != '\0')
 854                return 0;
 855
 856        return 1;
 857}
 858
 859static void display_recoverable(const struct termios *mode,
 860                                int UNUSED_PARAM dummy)
 861{
 862        int i;
 863        printf("%lx:%lx:%lx:%lx",
 864                   (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
 865                   (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
 866        for (i = 0; i < NCCS; ++i)
 867                printf(":%x", (unsigned int) mode->c_cc[i]);
 868        bb_putchar('\n');
 869}
 870
 871static void display_speed(const struct termios *mode, int fancy)
 872{
 873        //____________________ 01234567 8 9
 874        const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
 875        unsigned long ispeed, ospeed;
 876
 877        ospeed = ispeed = cfgetispeed(mode);
 878        if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
 879                ispeed = ospeed;                /* in case ispeed was 0 */
 880                //________ 0123 4 5 6 7 8 9
 881                fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;";
 882        }
 883        if (fancy) fmt_str += 9;
 884        wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
 885}
 886
 887static void do_display(const struct termios *mode, int all)
 888{
 889        int i;
 890        tcflag_t *bitsp;
 891        unsigned long mask;
 892        int prev_type = control;
 893
 894        display_speed(mode, 1);
 895        if (all)
 896                display_window_size(1);
 897#ifdef HAVE_C_LINE
 898        wrapf("line = %u;\n", mode->c_line);
 899#else
 900        newline();
 901#endif
 902
 903        for (i = 0; i != CIDX_min; ++i) {
 904                /* If swtch is the same as susp, don't print both */
 905#if VSWTCH == VSUSP
 906                if (i == CIDX_swtch)
 907                        continue;
 908#endif
 909                /* If eof uses the same slot as min, only print whichever applies */
 910#if VEOF == VMIN
 911                if (!(mode->c_lflag & ICANON)
 912                 && (i == CIDX_eof || i == CIDX_eol)
 913                ) {
 914                        continue;
 915                }
 916#endif
 917                wrapf("%s = %s;", nth_string(control_name, i),
 918                          visible(mode->c_cc[control_info[i].offset]));
 919        }
 920#if VEOF == VMIN
 921        if ((mode->c_lflag & ICANON) == 0)
 922#endif
 923                wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
 924        newline();
 925
 926        for (i = 0; i < NUM_mode_info; ++i) {
 927                if (mode_info[i].flags & OMIT)
 928                        continue;
 929                if (mode_info[i].type != prev_type) {
 930                        newline();
 931                        prev_type = mode_info[i].type;
 932                }
 933
 934                bitsp = mode_type_flag(mode_info[i].type, mode);
 935                mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
 936                if ((*bitsp & mask) == mode_info[i].bits) {
 937                        if (all || (mode_info[i].flags & SANE_UNSET))
 938                                wrapf("-%s"+1, nth_string(mode_name, i));
 939                } else {
 940                        if ((all && mode_info[i].flags & REV)
 941                         || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
 942                        ) {
 943                                wrapf("-%s", nth_string(mode_name, i));
 944                        }
 945                }
 946        }
 947        newline();
 948}
 949
 950static void sane_mode(struct termios *mode)
 951{
 952        int i;
 953        tcflag_t *bitsp;
 954
 955        for (i = 0; i < NUM_control_info; ++i) {
 956#if VMIN == VEOF
 957                if (i == CIDX_min)
 958                        break;
 959#endif
 960                mode->c_cc[control_info[i].offset] = control_info[i].saneval;
 961        }
 962
 963        for (i = 0; i < NUM_mode_info; ++i) {
 964                if (mode_info[i].flags & SANE_SET) {
 965                        bitsp = mode_type_flag(mode_info[i].type, mode);
 966                        *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
 967                                | mode_info[i].bits;
 968                } else if (mode_info[i].flags & SANE_UNSET) {
 969                        bitsp = mode_type_flag(mode_info[i].type, mode);
 970                        *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
 971                                & ~mode_info[i].bits;
 972                }
 973        }
 974}
 975
 976/* Save set_mode from #ifdef forest plague */
 977#ifndef ONLCR
 978#define ONLCR 0
 979#endif
 980#ifndef OCRNL
 981#define OCRNL 0
 982#endif
 983#ifndef ONLRET
 984#define ONLRET 0
 985#endif
 986#ifndef XCASE
 987#define XCASE 0
 988#endif
 989#ifndef IXANY
 990#define IXANY 0
 991#endif
 992#ifndef TABDLY
 993#define TABDLY 0
 994#endif
 995#ifndef OXTABS
 996#define OXTABS 0
 997#endif
 998#ifndef IUCLC
 999#define IUCLC 0
1000#endif
1001#ifndef OLCUC
1002#define OLCUC 0
1003#endif
1004#ifndef ECHOCTL
1005#define ECHOCTL 0
1006#endif
1007#ifndef ECHOKE
1008#define ECHOKE 0
1009#endif
1010
1011static void set_mode(const struct mode_info *info, int reversed,
1012                                        struct termios *mode)
1013{
1014        tcflag_t *bitsp;
1015
1016        bitsp = mode_type_flag(info->type, mode);
1017
1018        if (bitsp) {
1019                if (reversed)
1020                        *bitsp = *bitsp & ~info->mask & ~info->bits;
1021                else
1022                        *bitsp = (*bitsp & ~info->mask) | info->bits;
1023                return;
1024        }
1025
1026        /* Combination mode */
1027        if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) {
1028                if (reversed)
1029                        mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1030                else
1031                        mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
1032        } else if (info == &mode_info[IDX_oddp]) {
1033                if (reversed)
1034                        mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1035                else
1036                        mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
1037        } else if (info == &mode_info[IDX_nl]) {
1038                if (reversed) {
1039                        mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
1040                        mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET;
1041                } else {
1042                        mode->c_iflag = mode->c_iflag & ~ICRNL;
1043                        if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
1044                }
1045        } else if (info == &mode_info[IDX_ek]) {
1046                mode->c_cc[VERASE] = CERASE;
1047                mode->c_cc[VKILL] = CKILL;
1048        } else if (info == &mode_info[IDX_sane]) {
1049                sane_mode(mode);
1050        } else if (info == &mode_info[IDX_cbreak]) {
1051                if (reversed)
1052                        mode->c_lflag |= ICANON;
1053                else
1054                        mode->c_lflag &= ~ICANON;
1055        } else if (info == &mode_info[IDX_pass8]) {
1056                if (reversed) {
1057                        mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1058                        mode->c_iflag |= ISTRIP;
1059                } else {
1060                        mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1061                        mode->c_iflag &= ~ISTRIP;
1062                }
1063        } else if (info == &mode_info[IDX_litout]) {
1064                if (reversed) {
1065                        mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
1066                        mode->c_iflag |= ISTRIP;
1067                        mode->c_oflag |= OPOST;
1068                } else {
1069                        mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
1070                        mode->c_iflag &= ~ISTRIP;
1071                        mode->c_oflag &= ~OPOST;
1072                }
1073        } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
1074                if ((info == &mode_info[IDX_raw] && reversed)
1075                 || (info == &mode_info[IDX_cooked] && !reversed)
1076                ) {
1077                        /* Cooked mode */
1078                        mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
1079                        mode->c_oflag |= OPOST;
1080                        mode->c_lflag |= ISIG | ICANON;
1081#if VMIN == VEOF
1082                        mode->c_cc[VEOF] = CEOF;
1083#endif
1084#if VTIME == VEOL
1085                        mode->c_cc[VEOL] = CEOL;
1086#endif
1087                } else {
1088                        /* Raw mode */
1089                        mode->c_iflag = 0;
1090                        mode->c_oflag &= ~OPOST;
1091                        mode->c_lflag &= ~(ISIG | ICANON | XCASE);
1092                        mode->c_cc[VMIN] = 1;
1093                        mode->c_cc[VTIME] = 0;
1094                }
1095        }
1096        else if (IXANY && info == &mode_info[IDX_decctlq]) {
1097                if (reversed)
1098                        mode->c_iflag |= IXANY;
1099                else
1100                        mode->c_iflag &= ~IXANY;
1101        }
1102        else if (TABDLY && info == &mode_info[IDX_tabs]) {
1103                if (reversed)
1104                        mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
1105                else
1106                        mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
1107        }
1108        else if (OXTABS && info == &mode_info[IDX_tabs]) {
1109                if (reversed)
1110                        mode->c_oflag |= OXTABS;
1111                else
1112                        mode->c_oflag &= ~OXTABS;
1113        } else
1114        if (XCASE && IUCLC && OLCUC
1115         && (info == &mode_info[IDX_lcase] || info == &mode_info[IDX_LCASE])
1116        ) {
1117                if (reversed) {
1118                        mode->c_lflag &= ~XCASE;
1119                        mode->c_iflag &= ~IUCLC;
1120                        mode->c_oflag &= ~OLCUC;
1121                } else {
1122                        mode->c_lflag |= XCASE;
1123                        mode->c_iflag |= IUCLC;
1124                        mode->c_oflag |= OLCUC;
1125                }
1126        } else if (info == &mode_info[IDX_crt]) {
1127                mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1128        } else if (info == &mode_info[IDX_dec]) {
1129                mode->c_cc[VINTR] = 3; /* ^C */
1130                mode->c_cc[VERASE] = 127; /* DEL */
1131                mode->c_cc[VKILL] = 21; /* ^U */
1132                mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
1133                if (IXANY) mode->c_iflag &= ~IXANY;
1134        }
1135}
1136
1137static void set_control_char_or_die(const struct control_info *info,
1138                        const char *arg, struct termios *mode)
1139{
1140        unsigned char value;
1141
1142        if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time])
1143                value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1144        else if (arg[0] == '\0' || arg[1] == '\0')
1145                value = arg[0];
1146        else if (strcmp(arg, "^-") == 0 || strcmp(arg, "undef") == 0)
1147                value = _POSIX_VDISABLE;
1148        else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
1149                value = arg[1] & 0x1f; /* Non-letters get weird results */
1150                if (arg[1] == '?')
1151                        value = 127;
1152        } else
1153                value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
1154        mode->c_cc[info->offset] = value;
1155}
1156
1157#define STTY_require_set_attr   (1 << 0)
1158#define STTY_speed_was_set      (1 << 1)
1159#define STTY_verbose_output     (1 << 2)
1160#define STTY_recoverable_output (1 << 3)
1161#define STTY_noargs             (1 << 4)
1162
1163int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1164int stty_main(int argc UNUSED_PARAM, char **argv)
1165{
1166        struct termios mode;
1167        void (*output_func)(const struct termios *, int);
1168        const char *file_name = NULL;
1169        int display_all = 0;
1170        int stty_state;
1171        int k;
1172
1173        INIT_G();
1174
1175        stty_state = STTY_noargs;
1176        output_func = do_display;
1177
1178        /* First pass: only parse/verify command line params */
1179        k = 0;
1180        while (argv[++k]) {
1181                const struct mode_info *mp;
1182                const struct control_info *cp;
1183                const char *arg = argv[k];
1184                const char *argnext = argv[k+1];
1185                int param;
1186
1187                if (arg[0] == '-') {
1188                        int i;
1189                        mp = find_mode(arg+1);
1190                        if (mp) {
1191                                if (!(mp->flags & REV))
1192                                        goto invalid_argument;
1193                                stty_state &= ~STTY_noargs;
1194                                continue;
1195                        }
1196                        /* It is an option - parse it */
1197                        i = 0;
1198                        while (arg[++i]) {
1199                                switch (arg[i]) {
1200                                case 'a':
1201                                        stty_state |= STTY_verbose_output;
1202                                        output_func = do_display;
1203                                        display_all = 1;
1204                                        break;
1205                                case 'g':
1206                                        stty_state |= STTY_recoverable_output;
1207                                        output_func = display_recoverable;
1208                                        break;
1209                                case 'F':
1210                                        if (file_name)
1211                                                bb_error_msg_and_die("only one device may be specified");
1212                                        file_name = &arg[i+1]; /* "-Fdevice" ? */
1213                                        if (!file_name[0]) { /* nope, "-F device" */
1214                                                int p = k+1; /* argv[p] is argnext */
1215                                                file_name = argnext;
1216                                                if (!file_name)
1217                                                        bb_error_msg_and_die(bb_msg_requires_arg, "-F");
1218                                                /* remove -F param from arg[vc] */
1219                                                while (argv[p]) {
1220                                                        argv[p] = argv[p+1];
1221                                                        ++p;
1222                                                }
1223                                        }
1224                                        goto end_option;
1225                                default:
1226                                        goto invalid_argument;
1227                                }
1228                        }
1229 end_option:
1230                        continue;
1231                }
1232
1233                mp = find_mode(arg);
1234                if (mp) {
1235                        stty_state &= ~STTY_noargs;
1236                        continue;
1237                }
1238
1239                cp = find_control(arg);
1240                if (cp) {
1241                        if (!argnext)
1242                                bb_error_msg_and_die(bb_msg_requires_arg, arg);
1243                        /* called for the side effect of xfunc death only */
1244                        set_control_char_or_die(cp, argnext, &mode);
1245                        stty_state &= ~STTY_noargs;
1246                        ++k;
1247                        continue;
1248                }
1249
1250                param = find_param(arg);
1251                if (param & param_need_arg) {
1252                        if (!argnext)
1253                                bb_error_msg_and_die(bb_msg_requires_arg, arg);
1254                        ++k;
1255                }
1256
1257                switch (param) {
1258#ifdef HAVE_C_LINE
1259                case param_line:
1260# ifndef TIOCGWINSZ
1261                        xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1262                        break;
1263# endif /* else fall-through */
1264#endif
1265#ifdef TIOCGWINSZ
1266                case param_rows:
1267                case param_cols:
1268                case param_columns:
1269                        xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
1270                        break;
1271                case param_size:
1272#endif
1273                case param_speed:
1274                        break;
1275                case param_ispeed:
1276                        /* called for the side effect of xfunc death only */
1277                        set_speed_or_die(input_speed, argnext, &mode);
1278                        break;
1279                case param_ospeed:
1280                        /* called for the side effect of xfunc death only */
1281                        set_speed_or_die(output_speed, argnext, &mode);
1282                        break;
1283                default:
1284                        if (recover_mode(arg, &mode) == 1) break;
1285                        if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
1286 invalid_argument:
1287                        bb_error_msg_and_die("invalid argument '%s'", arg);
1288                }
1289                stty_state &= ~STTY_noargs;
1290        }
1291
1292        /* Specifying both -a and -g is an error */
1293        if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) ==
1294                (STTY_verbose_output | STTY_recoverable_output))
1295                bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive");
1296        /* Specifying -a or -g with non-options is an error */
1297        if (!(stty_state & STTY_noargs)
1298         && (stty_state & (STTY_verbose_output | STTY_recoverable_output))
1299        ) {
1300                bb_error_msg_and_die("modes may not be set when specifying an output style");
1301        }
1302
1303        /* Now it is safe to start doing things */
1304        if (file_name) {
1305                G.device_name = file_name;
1306                xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO);
1307                ndelay_off(STDIN_FILENO);
1308        }
1309
1310        /* Initialize to all zeroes so there is no risk memcmp will report a
1311           spurious difference in an uninitialized portion of the structure */
1312        memset(&mode, 0, sizeof(mode));
1313        if (tcgetattr(STDIN_FILENO, &mode))
1314                perror_on_device_and_die("%s");
1315
1316        if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
1317                get_terminal_width_height(STDOUT_FILENO, &G.max_col, NULL);
1318                output_func(&mode, display_all);
1319                return EXIT_SUCCESS;
1320        }
1321
1322        /* Second pass: perform actions */
1323        k = 0;
1324        while (argv[++k]) {
1325                const struct mode_info *mp;
1326                const struct control_info *cp;
1327                const char *arg = argv[k];
1328                const char *argnext = argv[k+1];
1329                int param;
1330
1331                if (arg[0] == '-') {
1332                        mp = find_mode(arg+1);
1333                        if (mp) {
1334                                set_mode(mp, 1 /* reversed */, &mode);
1335                                stty_state |= STTY_require_set_attr;
1336                        }
1337                        /* It is an option - already parsed. Skip it */
1338                        continue;
1339                }
1340
1341                mp = find_mode(arg);
1342                if (mp) {
1343                        set_mode(mp, 0 /* non-reversed */, &mode);
1344                        stty_state |= STTY_require_set_attr;
1345                        continue;
1346                }
1347
1348                cp = find_control(arg);
1349                if (cp) {
1350                        ++k;
1351                        set_control_char_or_die(cp, argnext, &mode);
1352                        stty_state |= STTY_require_set_attr;
1353                        continue;
1354                }
1355
1356                param = find_param(arg);
1357                if (param & param_need_arg) {
1358                        ++k;
1359                }
1360
1361                switch (param) {
1362#ifdef HAVE_C_LINE
1363                case param_line:
1364                        mode.c_line = xatoul_sfx(argnext, stty_suffixes);
1365                        stty_state |= STTY_require_set_attr;
1366                        break;
1367#endif
1368#ifdef TIOCGWINSZ
1369                case param_cols:
1370                case param_columns:
1371                        set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
1372                        break;
1373                case param_size:
1374                        display_window_size(0);
1375                        break;
1376                case param_rows:
1377                        set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
1378                        break;
1379#endif
1380                case param_speed:
1381                        display_speed(&mode, 0);
1382                        break;
1383                case param_ispeed:
1384                        set_speed_or_die(input_speed, argnext, &mode);
1385                        stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1386                        break;
1387                case param_ospeed:
1388                        set_speed_or_die(output_speed, argnext, &mode);
1389                        stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1390                        break;
1391                default:
1392                        if (recover_mode(arg, &mode) == 1)
1393                                stty_state |= STTY_require_set_attr;
1394                        else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
1395                                set_speed_or_die(both_speeds, arg, &mode);
1396                                stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
1397                        } /* else - impossible (caught in the first pass):
1398                                bb_error_msg_and_die("invalid argument '%s'", arg); */
1399                }
1400        }
1401
1402        if (stty_state & STTY_require_set_attr) {
1403                struct termios new_mode;
1404
1405                if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
1406                        perror_on_device_and_die("%s");
1407
1408                /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
1409                   it performs *any* of the requested operations.  This means it
1410                   can report 'success' when it has actually failed to perform
1411                   some proper subset of the requested operations.  To detect
1412                   this partial failure, get the current terminal attributes and
1413                   compare them to the requested ones */
1414
1415                /* Initialize to all zeroes so there is no risk memcmp will report a
1416                   spurious difference in an uninitialized portion of the structure */
1417                memset(&new_mode, 0, sizeof(new_mode));
1418                if (tcgetattr(STDIN_FILENO, &new_mode))
1419                        perror_on_device_and_die("%s");
1420
1421                if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
1422#ifdef CIBAUD
1423                        /* SunOS 4.1.3 (at least) has the problem that after this sequence,
1424                           tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
1425                           sometimes (m1 != m2).  The only difference is in the four bits
1426                           of the c_cflag field corresponding to the baud rate.  To save
1427                           Sun users a little confusion, don't report an error if this
1428                           happens.  But suppress the error only if we haven't tried to
1429                           set the baud rate explicitly -- otherwise we'd never give an
1430                           error for a true failure to set the baud rate */
1431
1432                        new_mode.c_cflag &= (~CIBAUD);
1433                        if ((stty_state & STTY_speed_was_set)
1434                         || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
1435#endif
1436                                perror_on_device_and_die("%s: cannot perform all requested operations");
1437                }
1438        }
1439
1440        return EXIT_SUCCESS;
1441}
1442